diff options
Diffstat (limited to 'server/src/customer/mod.rs')
-rw-r--r-- | server/src/customer/mod.rs | 290 |
1 files changed, 0 insertions, 290 deletions
diff --git a/server/src/customer/mod.rs b/server/src/customer/mod.rs deleted file mode 100644 index bf385927..00000000 --- a/server/src/customer/mod.rs +++ /dev/null @@ -1,290 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright 2024 metamuffin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - -*/ -mod pathfinding; - -use crate::{data::Gamedata, game::Tile}; -use anyhow::{anyhow, Result}; -use fake::{faker, Fake}; -use hurrycurry_protocol::{ - glam::IVec2, movement::MovementBase, DemandIndex, Message, PacketS, PlayerID, -}; -use log::info; -use pathfinding::{find_path, Path}; -use rand::{random, thread_rng}; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, -}; - -pub struct DemandState { - data: Arc<Gamedata>, - walkable: HashSet<IVec2>, - chairs: HashMap<IVec2, bool>, - customer_id_counter: PlayerID, - customers: HashMap<PlayerID, Customer>, - spawn_cooldown: f32, - - pub completed: usize, - pub failed: usize, - pub score_changed: bool, -} - -enum CustomerState { - Entering { - path: Path, - chair: IVec2, - }, - Waiting { - demand: DemandIndex, - chair: IVec2, - timeout: f32, - }, - Eating { - demand: DemandIndex, - target: IVec2, - progress: f32, - chair: IVec2, - }, - Exiting { - path: Path, - }, -} - -pub struct Customer { - movement: MovementBase, - state: CustomerState, -} - -impl DemandState { - pub fn new(data: Arc<Gamedata>, map: &HashMap<IVec2, Tile>) -> Self { - let chair = data.get_tile_by_name("chair"); - Self { - score_changed: true, - completed: 0, - failed: 0, - walkable: map - .iter() - .filter(|(_, v)| !data.is_tile_colliding(v.kind)) - .map(|(e, _)| *e) - .collect(), - chairs: map - .iter() - .filter(|(_, v)| Some(v.kind) == chair) - .map(|(e, _)| (*e, true)) - .collect(), - customer_id_counter: PlayerID(0), - customers: Default::default(), - data, - spawn_cooldown: 0., - } - } -} - -impl DemandState { - pub fn tick( - &mut self, - packets_out: &mut Vec<(PlayerID, PacketS)>, - tiles: &mut HashMap<IVec2, Tile>, - data: &Gamedata, - dt: f32, - points: &mut i64, - ) -> Result<()> { - self.spawn_cooldown -= dt; - self.spawn_cooldown = self.spawn_cooldown.max(0.); - if self.customers.len() < 5 && self.spawn_cooldown <= 0. { - self.spawn_cooldown = 10. + random::<f32>() * 10.; - self.customer_id_counter.0 -= 1; - let id = self.customer_id_counter; - packets_out.push(( - id, - PacketS::Join { - name: faker::name::fr_fr::Name().fake(), - character: -1 - (random::<u16>() as i32), - }, - )); - let chair = self.select_chair().ok_or(anyhow!("no free chair found"))?; - let from = data.customer_spawn.as_ivec2(); - let path = find_path(&self.walkable, from, chair) - .ok_or(anyhow!("no path from {from} to {chair}"))?; - info!("{id:?} -> entering"); - self.customers.insert( - id, - Customer { - movement: MovementBase::new(data.customer_spawn), - state: CustomerState::Entering { path, chair }, - }, - ); - } - let mut customers_to_remove = Vec::new(); - for (&id, p) in &mut self.customers { - match &mut p.state { - CustomerState::Entering { path, chair } => { - packets_out.push((id, path.execute_tick(&mut p.movement, &self.walkable, dt))); - if path.is_done() { - let demand = DemandIndex(random::<usize>() % self.data.demands.len()); - packets_out.push(( - id, - PacketS::Communicate { - message: Some(Message::Item(data.demand(demand).from)), - persist: true, - }, - )); - info!("{id:?} -> waiting"); - p.state = CustomerState::Waiting { - chair: *chair, - timeout: 90. + random::<f32>() * 60., - demand, - }; - } - } - CustomerState::Waiting { - chair, - demand, - timeout, - } => { - *timeout -= dt; - if *timeout <= 0. { - packets_out.push(( - id, - PacketS::Communicate { - message: None, - persist: true, - }, - )); - packets_out.push(( - id, - PacketS::Communicate { - message: Some(Message::Effect("angry".to_string())), - persist: false, - }, - )); - let path = find_path( - &self.walkable, - p.movement.position.as_ivec2(), - data.customer_spawn.as_ivec2(), - ) - .expect("no path to exit"); - *self.chairs.get_mut(&chair).unwrap() = true; - self.failed += 1; - *points -= 1; - self.score_changed = true; - info!("{id:?} -> exiting"); - p.state = CustomerState::Exiting { path } - } else { - let demand_data = &data.demand(*demand); - let demand_pos = [IVec2::NEG_X, IVec2::NEG_Y, IVec2::X, IVec2::Y] - .into_iter() - .find_map(|off| { - let pos = *chair + off; - if tiles - .get(&pos) - .map(|t| { - t.item - .as_ref() - .map(|i| i.kind == demand_data.from) - .unwrap_or_default() - }) - .unwrap_or_default() - { - Some(pos) - } else { - None - } - }); - if let Some(pos) = demand_pos { - packets_out.push(( - id, - PacketS::Communicate { - persist: true, - message: None, - }, - )); - packets_out.push(( - id, - PacketS::Communicate { - message: Some(Message::Effect("satisfied".to_string())), - persist: false, - }, - )); - packets_out.push((id, PacketS::Interact { pos: Some(pos) })); - packets_out.push((id, PacketS::Interact { pos: None })); - info!("{id:?} -> eating"); - p.state = CustomerState::Eating { - demand: *demand, - target: pos, - progress: 0., - chair: *chair, - } - } - } - } - CustomerState::Eating { - demand, - target, - progress, - chair, - } => { - let demand = data.demand(*demand); - *progress += dt / demand.duration; - if *progress >= 1. { - packets_out.push((id, PacketS::ReplaceHand { item: demand.to })); - if demand.to.is_some() { - packets_out.push((id, PacketS::Interact { pos: Some(*target) })); - packets_out.push((id, PacketS::Interact { pos: None })); - } - let path = find_path( - &self.walkable, - p.movement.position.as_ivec2(), - data.customer_spawn.as_ivec2(), - ) - .ok_or(anyhow!("no path to exit"))?; - *self.chairs.get_mut(&chair).unwrap() = true; - self.completed += 1; - *points += demand.points; - self.score_changed = true; - info!("{id:?} -> exiting"); - p.state = CustomerState::Exiting { path } - } - } - CustomerState::Exiting { path } => { - packets_out.push((id, path.execute_tick(&mut p.movement, &self.walkable, dt))); - if path.is_done() { - info!("{id:?} -> leave"); - packets_out.push((id, PacketS::Leave)); - customers_to_remove.push(id); - } - } - } - } - for c in customers_to_remove { - self.customers.remove(&c).unwrap(); - } - Ok(()) - } - - fn select_chair(&mut self) -> Option<IVec2> { - use rand::seq::IteratorRandom; - let (chosen, free) = self - .chairs - .iter_mut() - .filter(|(_p, free)| **free) - .choose(&mut thread_rng())?; - *free = false; - Some(*chosen) - } -} |