/* Hurry Curry! - a game about cooking Copyright (C) 2025 Hurry Curry! Contributors 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 . */ pub mod book; pub mod bot; pub mod campaign; pub mod conveyor; pub mod customers; pub mod environment_effect; pub mod item_portal; pub mod pedestrians; pub mod player_portal; pub mod tram; pub mod tutorial; use crate::{entity::pedestrians::Pedestrians, scoreboard::ScoreboardStore}; use anyhow::Result; use book::Book; use campaign::{Gate, Map}; use conveyor::Conveyor; use customers::Customers; use environment_effect::{EnvironmentController, EnvironmentEffectController}; use hurrycurry_client_lib::Game; use hurrycurry_data::{entities::EntityDecl, Serverdata}; use hurrycurry_locale::TrError; use hurrycurry_protocol::{glam::IVec2, Character, Gamedata, PacketC, PacketS, PlayerID}; use item_portal::ItemPortal; use player_portal::PlayerPortal; use std::{ any::Any, collections::{HashMap, VecDeque}, }; use tram::Tram; pub type DynEntity = Box; pub type Entities = Vec; pub struct EntityContext<'a> { pub game: &'a mut Game, pub serverdata: &'a Serverdata, pub packet_out: &'a mut VecDeque, pub packet_in: &'a mut VecDeque, pub score_changed: &'a mut bool, pub load_map: &'a mut Option, pub scoreboard: &'a ScoreboardStore, pub replies: Option<&'a mut Vec>, pub dt: f32, } pub trait Entity: Any { fn tick(&mut self, _c: EntityContext<'_>) -> Result<()> { Ok(()) } fn finished(&self) -> bool { false } fn constructor(&mut self, _c: EntityContext<'_>) {} fn destructor(&mut self, _c: EntityContext<'_>) {} fn interact( &mut self, _c: EntityContext<'_>, _pos: Option, _player: PlayerID, ) -> Result { Ok(false) } } pub fn construct_entity(decl: &EntityDecl, data: &Gamedata) -> DynEntity { match decl.to_owned() { EntityDecl::Book { pos } => Box::new(Book(pos)), EntityDecl::ItemPortal { from, to } => Box::new(ItemPortal { from, to }), EntityDecl::PlayerPortal { from, to } => Box::new(PlayerPortal { from, to }), EntityDecl::Conveyor { from, to, speed } => Box::new(Conveyor { from, to, max_cooldown: 1. / speed.unwrap_or(2.), cooldown: 0., }), EntityDecl::Map { name, pos } => Box::new(Map { pos, name }), EntityDecl::Gate { condition, pos } => Box::new(Gate { condition, unlocked: false, pos, blocker_tile: data .get_tile_by_name("fence") .expect("asserted earlier (tm)"), active: true, }), EntityDecl::Customers { scaling_factor } => { Box::new(Customers::new(scaling_factor.unwrap_or(0.5))) } EntityDecl::EnvironmentEffect(config) => Box::new(EnvironmentEffectController::new(config)), EntityDecl::Environment(names) => Box::new(EnvironmentController(names)), EntityDecl::Tram { length, color, points, smoothing, spacing, } => Box::new(Tram { length, character: Character { color: color.unwrap_or_default(), hairstyle: 0, headwear: 0, }, ids: Vec::new(), points, progress: 0., spacing, smoothing, }), EntityDecl::Pedestrians { spawn_delay, spawn_delay_stdev, points, speed, } => Box::new(Pedestrians { players: HashMap::new(), points, spawn_delay_distr: rand_distr::Normal::new( spawn_delay, spawn_delay_stdev.unwrap_or(0.), ) .unwrap(), cooldown: 0., speed: speed.unwrap_or(0.6), }), } }