aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-10-15 16:30:20 +0200
committermetamuffin <metamuffin@disroot.org>2025-10-15 16:30:20 +0200
commita47a61ec1ffb0d597783870a053cbe0760544301 (patch)
treed2516f94445eb1f27614bf5618cbc5e4298eee7b
parentf9be1a87bbdfdfbeb316cc335b7a2dbbf42b06f2 (diff)
downloadhurrycurry-a47a61ec1ffb0d597783870a053cbe0760544301.tar
hurrycurry-a47a61ec1ffb0d597783870a053cbe0760544301.tar.bz2
hurrycurry-a47a61ec1ffb0d597783870a053cbe0760544301.tar.zst
Add tag minigame (hidden map + entity)
-rw-r--r--Cargo.toml12
-rw-r--r--data/maps/tag.yaml60
-rw-r--r--server/data/src/entities.rs4
-rw-r--r--server/data/src/lib.rs10
-rw-r--r--server/data/src/registry.rs6
-rw-r--r--server/src/entity/mod.rs7
-rw-r--r--server/src/entity/tag_minigame.rs96
7 files changed, 185 insertions, 10 deletions
diff --git a/Cargo.toml b/Cargo.toml
index f3221e74..62bd0ef2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,16 +1,16 @@
[workspace]
members = [
"server",
- "server/protocol",
+ "server/book-export",
"server/bot",
"server/client-lib",
- "server/replaytool",
- "server/registry",
- "server/discover",
"server/data",
+ "server/discover",
"server/editor",
- "server/tools",
"server/locale-export",
- "server/book-export",
+ "server/protocol",
+ "server/registry",
+ "server/replaytool",
+ "server/tools",
]
resolver = "2"
diff --git a/data/maps/tag.yaml b/data/maps/tag.yaml
new file mode 100644
index 00000000..1ad5b83b
--- /dev/null
+++ b/data/maps/tag.yaml
@@ -0,0 +1,60 @@
+# 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 <https://www.gnu.org/licenses/>.
+#
+score_baseline: 100000
+default_timer: 120
+recipes: none
+map:
+ - "BBBBBBB......^o^..^."
+ - "B.....e...^^^.^..^o^"
+ - "B.....e..^ooo^..^.^."
+ - "B..Ø..d...^^^..^o^.."
+ - "B.....d......^..^..^"
+ - "B.......^o^.^o^....o"
+ - "B............^.....^"
+ - "CC.CC...xccxx..CC.CC"
+ - "C...C..........C...C"
+ - "C.x.C.xaa.~....C...C"
+ - "C.x.C......bbx.Cxx.C"
+ - "C...C..........C...C"
+ - "CC.CC..xccxx...CC.CC"
+ - ".....^^............A"
+ - "....^oo^..^^.......A"
+ - "..^..^^..^oo^......A"
+ - ".^o^..^...^^....ø..A"
+ - "..^..^o^.^.........A"
+ - "......^.^o^........A"
+ - "..^o^....^...AAAAAAA"
+
+tiles:
+ "x": counter -c
+ "a": cutting-board -c
+ "b": rolling-board -c
+ "c": stove -c
+ "d": oven -c
+ "e": freezer -c
+ "Ø": black-hole -w
+ "ø": white-hole -c
+ "~": floor -w --chef-spawn --customer-spawn
+ ".": floor -w
+ "A": wall:blue -c
+ "B": wall:red -c
+ "C": wall:green -c
+ "o": table -c
+ "^": chair -w
+
+entities:
+ - !player_portal { from: [3.5, 3.5], to: [16.5, 16.5] }
+ - !tag_minigame
diff --git a/server/data/src/entities.rs b/server/data/src/entities.rs
index 04d4e1c5..fdd451a0 100644
--- a/server/data/src/entities.rs
+++ b/server/data/src/entities.rs
@@ -70,6 +70,10 @@ pub enum EntityDecl {
speed: Option<f32>,
points: Vec<Vec2>,
},
+ TagMinigame {
+ #[serde(default = "default_item")]
+ tag_item: ItemIndex,
+ },
}
#[derive(Debug, Clone, Deserialize, Serialize)]
diff --git a/server/data/src/lib.rs b/server/data/src/lib.rs
index 78ec21b3..445e59b9 100644
--- a/server/data/src/lib.rs
+++ b/server/data/src/lib.rs
@@ -214,8 +214,14 @@ fn build_data(
}
for mut e in map_in.entities.clone() {
- if let EntityDecl::Customers { unknown_order, .. } = &mut e {
- *unknown_order = reg.register_item("unknown-order".to_owned())
+ match &mut e {
+ EntityDecl::Customers { unknown_order, .. } => {
+ *unknown_order = reg.register_item("unknown-order".to_owned())
+ }
+ EntityDecl::TagMinigame { tag_item } => {
+ *tag_item = reg.register_item("lettuce".to_owned())
+ }
+ _ => (),
}
entities.push(e);
}
diff --git a/server/data/src/registry.rs b/server/data/src/registry.rs
index e7dc7f77..9d513c90 100644
--- a/server/data/src/registry.rs
+++ b/server/data/src/registry.rs
@@ -80,8 +80,12 @@ pub(crate) fn filter_unused_tiles_and_items(data: &mut Gamedata, serverdata: &mu
}
for e in &serverdata.entity_decls {
match e {
- EntityDecl::Gate { blocker_tile: blocker, .. } => used_tiles.insert(*blocker),
+ EntityDecl::Gate {
+ blocker_tile: blocker,
+ ..
+ } => used_tiles.insert(*blocker),
EntityDecl::Customers { unknown_order, .. } => used_items.insert(*unknown_order),
+ EntityDecl::TagMinigame { tag_item, .. } => used_items.insert(*tag_item),
_ => false,
};
}
diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs
index 6418d0c7..01cb9462 100644
--- a/server/src/entity/mod.rs
+++ b/server/src/entity/mod.rs
@@ -24,10 +24,14 @@ pub mod environment_effect;
pub mod item_portal;
pub mod pedestrians;
pub mod player_portal;
+pub mod tag_minigame;
pub mod tram;
pub mod tutorial;
-use crate::{entity::pedestrians::Pedestrians, scoreboard::ScoreboardStore};
+use crate::{
+ entity::{pedestrians::Pedestrians, tag_minigame::TagMinigame},
+ scoreboard::ScoreboardStore,
+};
use anyhow::Result;
use book::Book;
use campaign::{Gate, Map};
@@ -82,6 +86,7 @@ pub trait Entity: Any {
pub fn construct_entity(decl: &EntityDecl) -> DynEntity {
match decl.to_owned() {
+ EntityDecl::TagMinigame { tag_item } => Box::new(TagMinigame::new(tag_item)),
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 }),
diff --git a/server/src/entity/tag_minigame.rs b/server/src/entity/tag_minigame.rs
new file mode 100644
index 00000000..02c3726f
--- /dev/null
+++ b/server/src/entity/tag_minigame.rs
@@ -0,0 +1,96 @@
+/*
+ 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 <https://www.gnu.org/licenses/>.
+
+*/
+use super::{Entity, EntityContext};
+use anyhow::Result;
+use hurrycurry_client_lib::Item;
+use hurrycurry_protocol::{Hand, ItemIndex, ItemLocation, Message, PacketC, PlayerID};
+use std::{collections::HashMap, fmt::Write};
+
+#[derive(Debug, Clone)]
+pub struct TagMinigame {
+ scores: HashMap<PlayerID, f32>,
+ report_cooldown: f32,
+ init_done: bool,
+ tag_item: ItemIndex,
+}
+impl TagMinigame {
+ pub fn new(tag_item: ItemIndex) -> Self {
+ Self {
+ init_done: false,
+ report_cooldown: 0.,
+ scores: HashMap::new(),
+ tag_item,
+ }
+ }
+}
+impl Entity for TagMinigame {
+ fn tick(&mut self, c: EntityContext) -> Result<()> {
+ if !self.init_done {
+ self.init_done = true;
+ // Hand out item every but one player (not random yet)
+ for (id, player) in c.game.players.iter_mut().skip(1) {
+ if let Some(slot) = player.items.get_mut(0) {
+ *slot = Some(Item {
+ active: None,
+ kind: self.tag_item,
+ });
+ c.packet_out.push_back(PacketC::SetItem {
+ location: ItemLocation::Player(*id, Hand(0)),
+ item: Some(self.tag_item),
+ });
+ }
+ }
+ }
+
+ // Award points to players with the item
+ for (&id, player) in &c.game.players {
+ if let Some(slot) = player.items.get(0)
+ && let Some(item) = slot
+ && item.kind == self.tag_item
+ {
+ *self.scores.entry(id).or_default() += c.dt
+ }
+ }
+
+ // Send out a scoreboard message every 0.1s
+ self.report_cooldown -= c.dt;
+ if self.report_cooldown < 0. {
+ self.report_cooldown = 0.1;
+ let mut scores = self
+ .scores
+ .iter()
+ .map(|(x, y)| ((y * 10.) as i64, *x))
+ .collect::<Vec<_>>();
+ scores.sort_by_key(|(s, _)| -s);
+
+ let mut o = String::new();
+ writeln!(o, "Tag Minigame — Try to not have your lettuce stolen!").unwrap();
+ for ((score, player), rank) in scores.into_iter().zip(1..) {
+ let name = c.game.players.get(&player).map_or("unknown", |p| &p.name);
+ writeln!(o, "{rank:>2} {score:>4} {name}").unwrap();
+ }
+ o.pop(); // newline
+ c.packet_out.push_back(PacketC::ServerMessage {
+ error: false,
+ message: Message::Text(o),
+ });
+ }
+
+ Ok(())
+ }
+}