diff options
-rw-r--r-- | data/demands/default.yaml | 35 | ||||
-rw-r--r-- | data/recipes/default.ts | 5 | ||||
-rw-r--r-- | server/src/bin/graph.rs | 22 | ||||
-rw-r--r-- | server/src/customer/mod.rs | 16 | ||||
-rw-r--r-- | server/src/data.rs | 19 | ||||
-rw-r--r-- | server/src/game.rs | 44 | ||||
-rw-r--r-- | server/src/interaction.rs | 8 | ||||
-rw-r--r-- | server/src/protocol.rs | 1 | ||||
-rw-r--r-- | test-client/main.ts | 2 | ||||
-rw-r--r-- | test-client/protocol.ts | 2 | ||||
-rw-r--r-- | test-client/visual.ts | 8 |
11 files changed, 116 insertions, 46 deletions
diff --git a/data/demands/default.yaml b/data/demands/default.yaml index f6be0911..26bbac09 100644 --- a/data/demands/default.yaml +++ b/data/demands/default.yaml @@ -1,26 +1,37 @@ # 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/>. -# -- { from: bread-slice-plate, to: dirty-plate, duration: 10 } -- { from: steak-plate, to: dirty-plate, duration: 10 } -- { from: sliced-tomato-plate, to: dirty-plate, duration: 10 } +# +- { from: sliced-tomato-plate, to: dirty-plate, duration: 10, points: 2 } +- { from: bread-slice-plate, to: dirty-plate, duration: 10, points: 3 } +- { from: steak-plate, to: dirty-plate, duration: 10, points: 4 } -- { from: bread-slice-steak-plate, to: dirty-plate, duration: 15 } -- { from: bread-slice-sliced-tomato-plate, to: dirty-plate, duration: 15 } -- { from: sliced-tomato-steak-plate, to: dirty-plate, duration: 15 } +- { from: bread-slice-steak-plate, to: dirty-plate, duration: 15, points: 6 } +- { + from: bread-slice-sliced-tomato-plate, + to: dirty-plate, + duration: 15, + points: 5, + } +- { from: sliced-tomato-steak-plate, to: dirty-plate, duration: 15, points: 5 } -- { from: bread-slice-sliced-tomato-steak-plate, to: dirty-plate, duration: 20 } +- { + from: bread-slice-sliced-tomato-steak-plate, + to: dirty-plate, + duration: 20, + points: 10, + } -- { from: tomato-soup-plate, to: dirty-plate, duration: 20 } +- { from: tomato-soup-plate, to: dirty-plate, duration: 10, points: 5 } +- { from: bread, duration: 0, points: 4 } diff --git a/data/recipes/default.ts b/data/recipes/default.ts index dd662081..db106e54 100644 --- a/data/recipes/default.ts +++ b/data/recipes/default.ts @@ -25,7 +25,8 @@ export interface Recipe { action: "instant" | "passive" | "active" | "demand" duration?: number revert_duration?: number, - warn?: boolean + warn?: boolean, + points?: number, } export const all_items = new Set<string>() @@ -76,7 +77,7 @@ export function bake(from: string, to?: string) { out({ action: "passive", duration: 15, revert_duration: 20, tile: "oven", inputs: [o], outputs: ["burned"], warn: true }) } export function crate(item: string) { - out({ action: "instant", tile: item + "-crate", inputs: [], outputs: [item], }) + out({ action: "instant", tile: item + "-crate", inputs: [], outputs: [item], points: -1 }) } export function get_container(ifull: string): [string, string | null] { diff --git a/server/src/bin/graph.rs b/server/src/bin/graph.rs index deae2034..024512c6 100644 --- a/server/src/bin/graph.rs +++ b/server/src/bin/graph.rs @@ -17,7 +17,7 @@ */ use anyhow::{anyhow, Result}; use undercooked::{ - data::DataIndex, + data::{DataIndex, Demand}, interaction::Recipe, protocol::{ItemIndex, RecipeIndex}, }; @@ -59,14 +59,24 @@ fn main() -> Result<()> { } } - for (di, d) in data.demands.iter().enumerate() { + for ( + di, + Demand { + duration, + from: ItemIndex(from), + to, + points, + }, + ) in data.demands.iter().enumerate() + { let color = "#c4422b"; println!( - "d{di} [label=\"Demand\\ntakes {}s\" shape=box color={color:?} fillcolor={color:?} style=filled]", - d.duration + "d{di} [label=\"Demand\\ntakes {duration}s\\n{points} points\" shape=box color={color:?} fillcolor={color:?} style=filled]", ); - println!("i{} -> d{di}", d.from.0); - println!("d{di} -> i{}", d.to.0); + println!("i{from} -> d{di}"); + if let Some(ItemIndex(to)) = to { + println!("d{di} -> i{to}"); + } } println!("}}"); diff --git a/server/src/customer/mod.rs b/server/src/customer/mod.rs index 9e474b8f..d1f49655 100644 --- a/server/src/customer/mod.rs +++ b/server/src/customer/mod.rs @@ -104,6 +104,7 @@ impl DemandState { tiles: &mut HashMap<IVec2, Tile>, data: &Gamedata, dt: f32, + points: &mut i64, ) -> Result<()> { if self.customers.len() < 5 { self.customer_id_counter.0 -= 1; @@ -165,6 +166,7 @@ impl DemandState { .expect("no path to exit"); *self.chairs.get_mut(&chair).unwrap() = true; self.failed += 1; + *points -= 1; self.score_changed = true; p.state = CustomerState::Exiting { path } } else { @@ -212,14 +214,11 @@ impl DemandState { let demand = data.demand(*demand); *progress += dt / demand.duration; if *progress >= 1. { - packets_out.push(( - id, - PacketS::ReplaceHand { - item: Some(demand.to), - }, - )); - for edge in [true, false] { - packets_out.push((id, PacketS::Interact { pos: *target, edge })) + packets_out.push((id, PacketS::ReplaceHand { item: demand.to })); + if demand.to.is_some() { + for edge in [true, false] { + packets_out.push((id, PacketS::Interact { pos: *target, edge })) + } } let path = find_path( &self.walkable, @@ -229,6 +228,7 @@ impl DemandState { .ok_or(anyhow!("no path to exit"))?; *self.chairs.get_mut(&chair).unwrap() = true; self.completed += 1; + *points += demand.points; self.score_changed = true; p.state = CustomerState::Exiting { path } } diff --git a/server/src/data.rs b/server/src/data.rs index 763a67bc..46f7ed28 100644 --- a/server/src/data.rs +++ b/server/src/data.rs @@ -56,6 +56,8 @@ pub struct RecipeDecl { revert_duration: Option<f32>, #[serde(default)] duration: Option<f32>, + #[serde(default)] + points: Option<i64>, } #[derive(Debug, Clone, Deserialize)] @@ -72,15 +74,17 @@ pub struct InitialMap { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DemandDecl { from: String, - to: String, + to: Option<String>, duration: f32, + points: i64, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Demand { pub from: ItemIndex, - pub to: ItemIndex, + pub to: Option<ItemIndex>, pub duration: f32, + pub points: i64, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] @@ -161,7 +165,7 @@ impl Gamedata { let mut recipes = Vec::new(); let mut demands = Vec::new(); - for r in recipes_in { + for mut r in recipes_in { let r2 = r.clone(); let mut inputs = r .inputs @@ -190,21 +194,24 @@ impl Gamedata { }), Action::Instant => { recipes.push(Recipe::Instant { + points: r.points.take().unwrap_or(0), tile, inputs: [inputs.next(), inputs.next()], outputs: [outputs.next(), outputs.next()], }); } } - assert_eq!(inputs.next(), None, "{r2:?}"); - assert_eq!(outputs.next(), None, "{r2:?}"); + assert_eq!(inputs.next(), None, "{r2:?} inputs left over"); + assert_eq!(outputs.next(), None, "{r2:?} outputs left over"); + assert_eq!(r.points, None, "points specified where not possible") } for d in demands_in { demands.push(Demand { from: ItemIndex(register(&item_names, d.from)), - to: ItemIndex(register(&item_names, d.to)), + to: d.to.map(|to| ItemIndex(register(&item_names, to))), duration: d.duration, + points: d.points, }) } diff --git a/server/src/game.rs b/server/src/game.rs index 23900559..72f653c4 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -65,6 +65,7 @@ pub struct Game { players: HashMap<PlayerID, Player>, packet_out: VecDeque<PacketC>, demand: Option<DemandState>, + points: i64, } impl Game { @@ -75,11 +76,13 @@ impl Game { players: Default::default(), tiles: Default::default(), demand: None, + points: 0, } } fn unload(&mut self) { - self.packet_out.push_back(PacketC::SetIngame { state: false }); + self.packet_out + .push_back(PacketC::SetIngame { state: false }); for (id, _) in self.players.drain() { self.packet_out.push_back(PacketC::RemovePlayer { id }) } @@ -189,18 +192,22 @@ impl Game { }) } } - out.push(PacketC::Score { + out.push(self.score()); + out.push(PacketC::SetIngame { state: true }); + out + } + + pub fn score(&self) -> PacketC { + PacketC::Score { + points: self.points, demands_failed: self.demand.as_ref().map(|d| d.failed).unwrap_or_default(), demands_completed: self .demand .as_ref() .map(|d| d.completed) .unwrap_or_default(), - }); - out.push(PacketC::SetIngame { state: true }); - out + } } - pub fn packet_in(&mut self, player: PlayerID, packet: PacketS) -> Result<()> { match packet { PacketS::Join { name, character } => { @@ -315,6 +322,7 @@ impl Game { tile.kind, &mut tile.item, &mut player.item, + &mut self.points, ) { match effect { InteractEffect::Put => self.packet_out.push_back(PacketC::PutItem { @@ -363,6 +371,21 @@ impl Game { item: Some(i.kind), }); } + self.packet_out.push_back({ + PacketC::Score { + points: self.points, + demands_failed: self + .demand + .as_ref() + .map(|d| d.failed) + .unwrap_or_default(), + demands_completed: self + .demand + .as_ref() + .map(|d| d.completed) + .unwrap_or_default(), + } + }) } } } @@ -396,11 +419,18 @@ impl Game { pub fn tick(&mut self, dt: f32) { if let Some(demand) = &mut self.demand { let mut packet_out = Vec::new(); - if let Err(err) = demand.tick(&mut packet_out, &mut self.tiles, &self.data, dt) { + if let Err(err) = demand.tick( + &mut packet_out, + &mut self.tiles, + &self.data, + dt, + &mut self.points, + ) { warn!("demand tick {err}"); } if demand.score_changed { self.packet_out.push_back(PacketC::Score { + points: self.points, demands_failed: demand.failed, demands_completed: demand.completed, }); diff --git a/server/src/interaction.rs b/server/src/interaction.rs index e3dccfba..69fc9e70 100644 --- a/server/src/interaction.rs +++ b/server/src/interaction.rs @@ -43,6 +43,7 @@ pub enum Recipe { tile: Option<TileIndex>, inputs: [Option<ItemIndex>; 2], outputs: [Option<ItemIndex>; 2], + points: i64, }, } @@ -120,6 +121,7 @@ pub fn interact( tile_kind: TileIndex, this: &mut Option<Item>, other: &mut Option<Item>, + points: &mut i64, ) -> Option<InteractEffect> { let interactable = data.is_tile_interactable(tile_kind); if interactable && other.is_none() { @@ -188,7 +190,10 @@ pub fn interact( } } Recipe::Instant { - inputs, outputs, .. + inputs, + outputs, + points: pd, + .. } => { let on_tile = this.as_ref().map(|i| i.kind); let in_hand = other.as_ref().map(|i| i.kind); @@ -199,6 +204,7 @@ pub fn interact( let ok_rev = ok_rev as usize; *other = outputs[1 - ok_rev].map(|kind| Item { kind, active: None }); *this = outputs[ok_rev].map(|kind| Item { kind, active: None }); + *points += pd; return Some(InteractEffect::Produce); } } diff --git a/server/src/protocol.rs b/server/src/protocol.rs index 16c379b9..9355f90a 100644 --- a/server/src/protocol.rs +++ b/server/src/protocol.rs @@ -131,6 +131,7 @@ pub enum PacketC { message: Option<Message>, }, Score { + points: i64, demands_failed: usize, demands_completed: usize, }, diff --git a/test-client/main.ts b/test-client/main.ts index 4d1e9c53..72e88c90 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -99,6 +99,7 @@ export const items_removed = new Set<ItemData>() export let data: Gamedata = { item_names: [], tile_names: [], spawn: [0, 0], tile_collide: [], tile_interact: [] } export let my_id: PlayerID = -1 +export let points = 0 export let demands_completed = 0 export let demands_failed = 0 export const camera: V2 = { x: 0, y: 0 } @@ -197,6 +198,7 @@ function packet(p: PacketC) { case "score": demands_completed = p.demands_completed demands_failed = p.demands_failed + points = p.points break; case "error": console.warn(p.message) diff --git a/test-client/protocol.ts b/test-client/protocol.ts index ba6a630c..28ed6d11 100644 --- a/test-client/protocol.ts +++ b/test-client/protocol.ts @@ -48,7 +48,7 @@ export type PacketC = | { type: "set_active", tile: Vec2, progress?: number, warn: boolean } // A tile is doing something. progress goes from 0 to 1, then null when finished | { type: "update_map", tile: Vec2, kind: TileIndex | null, neighbors: [TileIndex | null] } // A map tile was changed | { type: "communicate", player: PlayerID, message?: Message } // A player wants to communicate something, message is null when cleared - | { type: "score", demands_failed: number, demands_completed: number, } // Supplies information for score OSD + | { type: "score", points: number, demands_failed: number, demands_completed: number, } // Supplies information for score OSD | { type: "set_ingame", state: boolean } // Set to false when entering the game or switching maps | { type: "error", message?: Message } // Your client did something wrong. diff --git a/test-client/visual.ts b/test-client/visual.ts index 1b4363e8..19866710 100644 --- a/test-client/visual.ts +++ b/test-client/visual.ts @@ -15,7 +15,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import { ItemData, MessageData, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, demands_completed, demands_failed, get_interact_target, interact_active_anim, interact_possible_anim, interact_target_anim, items_removed, keys_down, my_id, players, tiles } from "./main.ts"; +import { ItemData, MessageData, PlayerData, TileData, camera, camera_scale, canvas, ctx, data, demands_completed, demands_failed, get_interact_target, interact_active_anim, interact_possible_anim, interact_target_anim, items_removed, keys_down, my_id, players, points, tiles } from "./main.ts"; import { PLAYER_SIZE } from "./movement.ts"; import { FALLBACK_TILE, ITEMS, TILES, FALLBACK_ITEM } from "./tiles.ts"; import { V2, ceil_v2, floor_v2 } from "./util.ts"; @@ -77,9 +77,11 @@ export function draw_ingame() { ctx.fillStyle = "white" ctx.textAlign = "left" ctx.textBaseline = "bottom" + ctx.font = "30px sans-serif" + ctx.fillText(`Points: ${points}`, 10, canvas.height - 60) ctx.font = "20px sans-serif" - ctx.fillText(`Completed: ${demands_completed}`, 10, canvas.height - 10) - ctx.fillText(`Failed: ${demands_failed}`, 10, canvas.height - 30) + ctx.fillText(`Completed: ${demands_completed}`, 10, canvas.height - 30) + ctx.fillText(`Failed: ${demands_failed}`, 10, canvas.height - 10) if (keys_down.has("KeyP")) { draw_debug() |