diff options
author | metamuffin <metamuffin@disroot.org> | 2024-07-06 12:39:57 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-07-06 12:39:57 +0200 |
commit | c56431eb8fcb1ec6758a5aa3cdbba3892989085f (patch) | |
tree | ef3d7b1e5420b231e85f89cb8437c9581bb81494 | |
parent | 8360741499e11767aa3cbbec0fae43ddaab706f9 (diff) | |
download | hurrycurry-c56431eb8fcb1ec6758a5aa3cdbba3892989085f.tar hurrycurry-c56431eb8fcb1ec6758a5aa3cdbba3892989085f.tar.bz2 hurrycurry-c56431eb8fcb1ec6758a5aa3cdbba3892989085f.tar.zst |
conveyors
-rw-r--r-- | data/index.yaml | 1 | ||||
-rw-r--r-- | data/maps/test.yaml | 91 | ||||
-rw-r--r-- | server/src/data.rs | 20 | ||||
-rw-r--r-- | server/src/entity/conveyor.rs | 54 | ||||
-rw-r--r-- | server/src/entity/mod.rs | 42 | ||||
-rw-r--r-- | server/src/game.rs | 27 | ||||
-rw-r--r-- | server/src/lib.rs | 1 | ||||
-rw-r--r-- | test-client/main.ts | 6 |
8 files changed, 229 insertions, 13 deletions
diff --git a/data/index.yaml b/data/index.yaml index 535a43ea..d0398327 100644 --- a/data/index.yaml +++ b/data/index.yaml @@ -9,6 +9,7 @@ maps: - tiny - small - big + - test recipes: - none diff --git a/data/maps/test.yaml b/data/maps/test.yaml new file mode 100644 index 00000000..09f0b8a7 --- /dev/null +++ b/data/maps/test.yaml @@ -0,0 +1,91 @@ +# Undercooked - 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/>. +# +map: + - "*''''*'''*'''''*'''*'''*'''*''*'" + - "'''*''''*'*'**'''*''**''**''*'''" + - "''██▒██▒██▒███▒███▒████▒██▒███*'" + - "''█ctc.ctc.ctc.ctc.ctc█s....#█**" + - "''█.....c.............█s.~..⌷█''" + - "'*▒c...c...████www██d██⌷⌷.⌷.⌷█*'" + - "*'█tc.ctc..█#.....CC.>>>>.⌷.L█''" + - "''▒c...c...w..⌷⌷.....<<<<...R█'*" + - "*'█c.......w..⌷⌷⌷⌷⌷⌷⌷⌷█ff.⌷.T█*'" + - "'*▒tc......w..........d...⌷.F█''" + - "''█c.....ct█⌷SSS⌷f⌷oo⌷█⌷⌷...X█*'" + - "*'████dd██████████▒███████▒███'*" + - "'''*''__''''''''''''''''''''''*'" + - "*'''*'__________________________" + - "'*'*''_______________________!__" + - "*''*''''''''''''''''''''''''''''" + +tiles: + "⌷": counter + "f": counter + "<": counter + "#": counter + ">": counter + "t": table + "w": counter-window + "s": sink + "o": oven + "S": stove + "C": cuttingboard + "R": raw-steak-crate + "T": tomato-crate + "F": flour-crate + "L": leek-crate + "X": trash + + "c": chair + "~": floor + ".": floor + "'": grass + "*": tree + "!": path + "_": path + "d": door + "█": wall + "▒": wall-window + +items: + "S": pot + "w": plate + "f": foodprocessor + +entities: + - !conveyor { from: [22, 7], to: [21, 7] } + - !conveyor { from: [23, 7], to: [22, 7] } + - !conveyor { from: [24, 7], to: [23, 7] } + - !conveyor { from: [21, 6], to: [22, 6] } + - !conveyor { from: [22, 6], to: [23, 6] } + - !conveyor { from: [23, 6], to: [24, 6] } + - !conveyor { from: [23, 6], to: [24, 6] } + + - !conveyor { from: [28, 3], to: [12, 6] } + +chef_spawn: "~" +customer_spawn: "!" + +walkable: + - door + - floor + - chair + - grass + - path + +collider: + - wall + - tree diff --git a/server/src/data.rs b/server/src/data.rs index 46f7ed28..62a7f5d8 100644 --- a/server/src/data.rs +++ b/server/src/data.rs @@ -16,6 +16,7 @@ */ use crate::{ + entity::EntityDecl, interaction::Recipe, protocol::{DemandIndex, ItemIndex, RecipeIndex, TileIndex}, }; @@ -64,11 +65,14 @@ pub struct RecipeDecl { pub struct InitialMap { map: Vec<String>, tiles: HashMap<char, String>, + #[serde(default)] items: HashMap<char, String>, collider: Vec<String>, walkable: Vec<String>, chef_spawn: char, customer_spawn: char, + #[serde(default)] + entities: Vec<EntityDecl>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -88,19 +92,18 @@ pub struct Demand { } #[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[rustfmt::skip] pub struct Gamedata { - #[serde(skip)] - pub recipes: Vec<Recipe>, - #[serde(skip)] - pub demands: Vec<Demand>, pub item_names: Vec<String>, pub tile_names: Vec<String>, pub tile_collide: Vec<bool>, pub tile_interact: Vec<bool>, - #[serde(skip)] - pub initial_map: HashMap<IVec2, (TileIndex, Option<ItemIndex>)>, - pub chef_spawn: Vec2, - pub customer_spawn: Vec2, + #[serde(skip)] pub recipes: Vec<Recipe>, + #[serde(skip)] pub demands: Vec<Demand>, + #[serde(skip)] pub initial_map: HashMap<IVec2, (TileIndex, Option<ItemIndex>)>, + #[serde(skip)] pub chef_spawn: Vec2, + #[serde(skip)] pub customer_spawn: Vec2, + #[serde(skip)] pub entities: Vec<EntityDecl>, } #[derive(Debug, Deserialize, Default)] @@ -258,6 +261,7 @@ impl Gamedata { recipes, initial_map, item_names, + entities: map_in.entities, tile_names, chef_spawn, customer_spawn, diff --git a/server/src/entity/conveyor.rs b/server/src/entity/conveyor.rs new file mode 100644 index 00000000..b74c03ef --- /dev/null +++ b/server/src/entity/conveyor.rs @@ -0,0 +1,54 @@ +use super::Entity; +use crate::{ + data::Gamedata, + game::{interact_effect, Tile}, + protocol::{ItemLocation, PacketC}, +}; +use anyhow::{anyhow, Result}; +use glam::IVec2; +use std::collections::{HashMap, VecDeque}; + +#[derive(Debug, Default)] +pub struct Conveyor { + pub(super) from: IVec2, + pub(super) to: IVec2, + pub(super) cooldown: f32, + pub(super) max_cooldown: f32, +} + +impl Entity for Conveyor { + fn tick( + &mut self, + data: &Gamedata, + points: &mut i64, + packet_out: &mut VecDeque<PacketC>, + tiles: &mut HashMap<IVec2, Tile>, + dt: f32, + ) -> Result<()> { + let [from, to] = tiles + .get_many_mut([&self.from, &self.to]) + .ok_or(anyhow!("conveyor does ends in itself"))?; + + if from.item.is_some() { + self.cooldown += dt; + if self.cooldown < self.max_cooldown { + return Ok(()); + } + self.cooldown = 0.; + + interact_effect( + data, + true, + &mut to.item, + ItemLocation::Tile(self.to), + &mut from.item, + ItemLocation::Tile(self.from), + Some(to.kind), + packet_out, + points, + ); + } + + Ok(()) + } +} diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs new file mode 100644 index 00000000..089c60a5 --- /dev/null +++ b/server/src/entity/mod.rs @@ -0,0 +1,42 @@ +pub mod conveyor; + +use crate::{data::Gamedata, game::Tile, protocol::PacketC}; +use anyhow::Result; +use conveyor::Conveyor; +use glam::IVec2; +use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, VecDeque}; + +pub type DynEntity = Box<dyn Entity + Send + Sync + 'static>; + +pub trait Entity { + fn tick( + &mut self, + data: &Gamedata, + points: &mut i64, + packet_out: &mut VecDeque<PacketC>, + tiles: &mut HashMap<IVec2, Tile>, + dt: f32, + ) -> Result<()>; +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum EntityDecl { + Conveyor { + from: IVec2, + to: IVec2, + speed: Option<f32>, + }, +} + +pub fn construct_entity(decl: &EntityDecl) -> DynEntity { + match decl.to_owned() { + EntityDecl::Conveyor { from, to, speed } => Box::new(Conveyor { + from, + to, + max_cooldown: 1. / speed.unwrap_or(2.), + ..Default::default() + }), + } +} diff --git a/server/src/game.rs b/server/src/game.rs index 838a71e4..13ee5410 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -18,6 +18,7 @@ use crate::{ customer::DemandState, data::Gamedata, + entity::{construct_entity, DynEntity}, interaction::{interact, tick_slot, InteractEffect, TickEffect}, protocol::{ ItemIndex, ItemLocation, Message, PacketC, PacketS, PlayerID, RecipeIndex, TileIndex, @@ -68,6 +69,7 @@ pub struct Game { packet_out: VecDeque<PacketC>, demand: Option<DemandState>, pub points: i64, + entities: Vec<DynEntity>, end: Option<Instant>, } @@ -76,10 +78,11 @@ impl Game { Self { data: Gamedata::default().into(), packet_out: Default::default(), - players: Default::default(), - tiles: Default::default(), + players: HashMap::new(), + tiles: HashMap::new(), demand: None, end: None, + entities: vec![], points: 0, } } @@ -112,6 +115,12 @@ impl Game { self.data = gamedata.into(); self.points = 0; self.end = timer.map(|dur| Instant::now() + dur); + self.entities = self + .data + .entities + .iter() + .map(|decl| construct_entity(decl)) + .collect(); for (&p, (tile, item)) in &self.data.initial_map { self.tiles.insert( @@ -476,6 +485,18 @@ impl Game { } } + for entity in &mut self.entities { + if let Err(e) = entity.tick( + &self.data, + &mut self.points, + &mut self.packet_out, + &mut self.tiles, + dt, + ) { + warn!("entity tick failed: {e}") + } + } + return self.end.map(|t| t < Instant::now()).unwrap_or_default(); } } @@ -486,7 +507,7 @@ impl From<TileIndex> for Tile { } } -fn interact_effect( +pub fn interact_effect( data: &Gamedata, edge: bool, this: &mut Option<Item>, diff --git a/server/src/lib.rs b/server/src/lib.rs index b8929bd6..890e5148 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -22,3 +22,4 @@ pub mod game; pub mod interaction; pub mod protocol; pub mod state; +pub mod entity; diff --git a/test-client/main.ts b/test-client/main.ts index c7d6cf1c..0cd5c4cb 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -240,8 +240,10 @@ function keyboard(ev: KeyboardEvent, down: boolean) { if (HANDLED_KEYS.includes(ev.code)) ev.preventDefault() if (!keys_down.has(KEY_INTERACT) && ev.code == KEY_INTERACT && down) set_interact(true) if (keys_down.has(KEY_INTERACT) && ev.code == KEY_INTERACT && !down) set_interact(false) - if (down && ev.code == "Numpad1") send({ type: "communicate", message: { text: "/start default-small-default" }, persist: false }) - if (down && ev.code == "Numpad2") send({ type: "communicate", message: { text: "/start default-big-default" }, persist: false }) + if (down && ev.code == "Numpad1") send({ type: "communicate", message: { text: "/start tiny" }, persist: false }) + if (down && ev.code == "Numpad2") send({ type: "communicate", message: { text: "/start small" }, persist: false }) + if (down && ev.code == "Numpad3") send({ type: "communicate", message: { text: "/start big" }, persist: false }) + if (down && ev.code == "Numpad4") send({ type: "communicate", message: { text: "/start test" }, persist: false }) if (down && ev.code == "Numpad0") send({ type: "communicate", message: { text: "/end" }, persist: false }) if (down) keys_down.add(ev.code) else keys_down.delete(ev.code) |