diff options
author | metamuffin <metamuffin@disroot.org> | 2024-06-19 23:21:45 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-06-23 19:21:49 +0200 |
commit | 7be7848b65bd16139569389961541fcc52c905d2 (patch) | |
tree | b2b5a26f9e98d82bc4b54fdf7f76d9dfd583aa0f /server/src/customer/mod.rs | |
parent | 6ca76cc0568f3d60b280f11ae07a34303c317f34 (diff) | |
download | hurrycurry-7be7848b65bd16139569389961541fcc52c905d2.tar hurrycurry-7be7848b65bd16139569389961541fcc52c905d2.tar.bz2 hurrycurry-7be7848b65bd16139569389961541fcc52c905d2.tar.zst |
split up customer code. customer can enter, order and leave but not take meal
Diffstat (limited to 'server/src/customer/mod.rs')
-rw-r--r-- | server/src/customer/mod.rs | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/server/src/customer/mod.rs b/server/src/customer/mod.rs new file mode 100644 index 00000000..ab3accdd --- /dev/null +++ b/server/src/customer/mod.rs @@ -0,0 +1,201 @@ +pub mod movement; +mod pathfinding; + +use crate::{ + data::Gamedata, + game::Game, + protocol::{ItemIndex, Message, PacketC, PacketS, PlayerID}, +}; +use glam::{IVec2, Vec2}; +use log::{debug, error}; +use movement::MovementBase; +use pathfinding::{find_path, Path}; +use rand::thread_rng; +use std::{ + collections::{HashMap, HashSet}, + sync::Arc, + time::Duration, +}; +use tokio::{ + sync::{broadcast, RwLock}, + time::interval, +}; + +struct DemandState { + data: Gamedata, + walkable: HashSet<IVec2>, + chairs: HashMap<IVec2, bool>, + items: HashMap<IVec2, ItemIndex>, + customers: HashMap<PlayerID, Customer>, +} + +enum CustomerState { + WalkingToChair { path: Path, chair: IVec2 }, + Waiting { chair: IVec2, demand: ItemIndex }, + Exiting { path: Path }, +} + +struct Customer { + movement: MovementBase, + state: CustomerState, +} + +pub async fn customer(game: Arc<RwLock<Game>>, mut grx: broadcast::Receiver<PacketC>) { + let mut state = DemandState { + walkable: Default::default(), + chairs: Default::default(), + items: Default::default(), + customers: Default::default(), + data: Gamedata::default(), + }; + let initial = game.write().await.prime_client(-1); + for p in initial { + match p { + PacketC::Init { data, .. } => { + state.data = data; + } + PacketC::UpdateMap { pos, tile, .. } => { + let tilename = &state.data.tile_names[tile]; + if tilename == "floor" || tilename == "door" || tilename == "chair" { + state.walkable.insert(pos); + } + if tilename == "chair" { + state.chairs.insert(pos, true); + } + } + _ => (), + } + } + + let mut interval = interval(Duration::from_millis(40)); + let mut packets_out = Vec::new(); + loop { + tokio::select! { + packet = grx.recv() => { + match packet.unwrap() { + PacketC::PutItem { .. } + | PacketC::TakeItem { .. } + | PacketC::ProduceItem { .. } + | PacketC::ConsumeItem { .. } => { + let g = game.read().await; + update_items(&mut state, &g) + }, + _ => () + } + } + _ = interval.tick() => { + state.tick(&mut packets_out, 0.04); + for (player,packet) in packets_out.drain(..) { + if let Err(e) = game.write().await.packet_in(player, packet) { + error!("customer misbehaved: {e}") + } + } + } + } + } +} + +fn update_items(state: &mut DemandState, game: &Game) { + state.items.clear(); + for (&pos, tile) in game.tiles() { + if let Some(item) = &tile.item { + state.items.insert(pos, item.kind); + } + } +} + +impl DemandState { + pub fn tick(&mut self, packets_out: &mut Vec<(PlayerID, PacketS)>, dt: f32) { + if self.customers.is_empty() { + let id = -1; + packets_out.push(( + id, + PacketS::Join { + name: "George".to_string(), + character: 0, + }, + )); + let chair = select_chair(&mut self.chairs); + let path = find_path(&self.walkable, self.data.customer_spawn.as_ivec2(), chair) + .expect("no path"); + self.customers.insert( + id, + Customer { + movement: MovementBase { + position: self.data.customer_spawn, + facing: Vec2::X, + vel: Vec2::ZERO, + }, + state: CustomerState::WalkingToChair { path, chair }, + }, + ); + } + let mut customers_to_remove = Vec::new(); + for (&id, p) in &mut self.customers { + match &mut p.state { + CustomerState::WalkingToChair { path, chair } => { + packets_out.push((id, path.execute_tick(&mut p.movement, &self.walkable, dt))); + if path.is_done() { + let demand = self.data.get_item("tomato").unwrap(); + packets_out.push(( + id, + PacketS::Communicate { + message: Some(Message::Item(demand)), + }, + )); + p.state = CustomerState::Waiting { + chair: *chair, + demand, + }; + } + } + CustomerState::Waiting { chair, demand } => { + let demand_pos = [IVec2::NEG_X, IVec2::NEG_Y, IVec2::X, IVec2::Y] + .into_iter() + .find_map(|off| { + let pos = *chair + off; + if self.items.get(&pos) == Some(demand) { + Some(pos) + } else { + None + } + }); + if let Some(pos) = demand_pos { + if self.items.get(&pos) == Some(demand) { + packets_out.push((id, PacketS::Communicate { message: None })); + let path = find_path( + &self.walkable, + p.movement.position.as_ivec2(), + self.data.customer_spawn.as_ivec2(), + ) + .expect("no path to exit"); + p.state = CustomerState::Exiting { path } + } + } + debug!("waiting") + } + CustomerState::Exiting { path } => { + packets_out.push((id, path.execute_tick(&mut p.movement, &self.walkable, dt))); + if path.is_done() { + packets_out.push((id, PacketS::Leave)); + customers_to_remove.push(id); + } + } + } + } + for c in customers_to_remove { + self.customers.remove(&c).unwrap(); + } + } +} + +pub fn select_chair(chairs: &mut HashMap<IVec2, bool>) -> IVec2 { + use rand::seq::IteratorRandom; + let (chosen, free) = chairs + .iter_mut() + .filter(|(_p, free)| **free) + .choose(&mut thread_rng()) + .unwrap(); + *free = false; + *chosen +} |