use crate::{ interaction::{interact, tick_tile, Out}, protocol::{ItemID, ItemIndex, PacketC, PacketS, PlayerID, RecipeIndex, TileIndex}, recipes::Gamedata, }; use anyhow::{anyhow, bail, Result}; use glam::IVec2; use log::info; use std::{ collections::{HashMap, VecDeque}, ops::Deref, sync::Arc, }; pub struct ActiveRecipe { pub recipe: RecipeIndex, pub progress: f32, pub working: usize, } pub struct Tile { kind: TileIndex, items: Vec, active: Option, } struct Player { name: String, interacting: bool, hand: Option, } pub struct Game { data: Arc, item_id_counter: ItemID, tiles: HashMap, items: HashMap, players: HashMap, packet_out: VecDeque, } impl Game { pub fn new(gamedata: Arc) -> Self { let mut g = Self { data: gamedata.clone(), item_id_counter: 0, items: Default::default(), packet_out: Default::default(), players: Default::default(), tiles: Default::default(), }; for x in -5..5 { for y in -5..5 { g.tiles .insert(IVec2 { x, y }, gamedata.get_tile("floor").unwrap().into()); } } for x in -5..5 { g.tiles.insert( IVec2 { x, y: -5 }, gamedata.get_tile("counter").unwrap().into(), ); g.tiles.insert( IVec2 { x, y: 4 }, gamedata.get_tile("table").unwrap().into(), ); } for y in -5..5 { g.tiles.insert( IVec2 { x: -5, y }, gamedata.get_tile("table").unwrap().into(), ); g.tiles.insert( IVec2 { x: 4, y }, gamedata.get_tile("table").unwrap().into(), ); } g.tiles.extend( [ ([1, 4], "pan"), ([2, 4], "pan"), ([-1, 4], "oven"), ([-2, 4], "oven"), ([-5, 2], "sink"), ([-5, 3], "dirty-plate-spawn"), ([4, 0], "flour-spawn"), ([4, 1], "tomato-spawn"), ([4, 2], "raw-steak-spawn"), ([4, -4], "trash"), ] .map(|(k, v)| (IVec2::from_array(k), gamedata.get_tile(v).unwrap().into())), ); g } pub fn packet_out(&mut self) -> Option { self.packet_out.pop_front() } pub fn prime_client(&self, id: PlayerID) -> Vec { let mut out = Vec::new(); for (&id, player) in &self.players { out.push(PacketC::AddPlayer { id, name: player.name.clone(), hand: player.hand.map(|i| (i, self.items[&i].clone())), }) } for (&pos, tdata) in &self.tiles { out.push(PacketC::UpdateMap { pos, tile: tdata.kind.clone(), }); for &id in &tdata.items { out.push(PacketC::ProduceItem { id, pos, kind: self.items[&id].clone(), }) } } out.push(PacketC::Joined { id, data: self.data.deref().to_owned(), }); out } pub fn packet_in(&mut self, player: PlayerID, packet: PacketS) -> Result<()> { match packet { PacketS::Join { name } => { self.players.insert( player, Player { hand: None, interacting: false, name: name.clone(), }, ); self.packet_out.push_back(PacketC::AddPlayer { id: player, name, hand: None, }); } PacketS::Leave => { let p = self .players .remove(&player) .ok_or(anyhow!("player does not exist"))?; if let Some(id) = p.hand { self.items.remove(&id).expect("hand item lost"); } self.packet_out .push_back(PacketC::RemovePlayer { id: player }) } PacketS::Position { pos, rot } => { self.packet_out .push_back(PacketC::Position { player, pos, rot }); } PacketS::Interact { pos, edge } => { info!("interact {pos:?} edge={edge}"); let pid = player; let player = self .players .get_mut(&player) .ok_or(anyhow!("player does not exist"))?; let tile = self .tiles .get_mut(&pos) .ok_or(anyhow!("tile does not exist"))?; if edge == player.interacting { bail!("already (not) interacting") } let items = tile.items.iter().map(|e| self.items[e]).collect::>(); let tilekind = tile.kind; let hand = player.hand.map(|e| self.items[&e]); interact( &self.data, edge, tilekind, &mut tile.active, items, hand, |out| match out { Out::Take(index) => { info!("take"); let item = tile.items.remove(index); player.hand = Some(item); self.packet_out .push_back(PacketC::TakeItem { item, player: pid }) } Out::Put => { info!("put"); let hand = player.hand.take().unwrap(); tile.items.push(hand); self.packet_out .push_back(PacketC::PutItem { item: hand, pos }) } Out::Produce(kind) => { info!("produce"); let id = self.item_id_counter; self.item_id_counter += 1; self.items.insert(id, kind); tile.items.push(id); self.packet_out .push_back(PacketC::ProduceItem { id, pos, kind }); } Out::Consume(index) => { info!("consume"); let id = tile.items.remove(index); info!("left {:?}", tile.items); self.packet_out.push_back(PacketC::ConsumeItem { id, pos }); } Out::SetActive(progress) => { self.packet_out.push_back(PacketC::SetActive { tile: pos, progress, }); } }, ); player.interacting = edge; } } Ok(()) } pub fn tick(&mut self, dt: f32) { for (&pos, tile) in &mut self.tiles { let items = tile.items.iter().map(|e| self.items[e]).collect::>(); tick_tile( dt, &self.data, tile.kind, &mut tile.active, items, |out| match out { Out::Take(_) | Out::Put => { unreachable!() } Out::Produce(kind) => { info!("produce"); let id = self.item_id_counter; self.item_id_counter += 1; self.items.insert(id, kind); tile.items.push(id); self.packet_out .push_back(PacketC::ProduceItem { id, pos, kind }); } Out::Consume(index) => { info!("consume"); let id = tile.items.remove(index); info!("left {:?}", tile.items); self.packet_out.push_back(PacketC::ConsumeItem { id, pos }); } Out::SetActive(progress) => { self.packet_out.push_back(PacketC::SetActive { tile: pos, progress, }); } }, ); } } } impl From for Tile { fn from(kind: TileIndex) -> Self { Self { kind, items: vec![], active: None, } } }