aboutsummaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/commands.rs26
-rw-r--r--server/src/entity/pedestrians.rs3
-rw-r--r--server/src/entity/tutorial.rs120
-rw-r--r--server/src/server.rs7
4 files changed, 129 insertions, 27 deletions
diff --git a/server/src/commands.rs b/server/src/commands.rs
index 98ca10c6..8b7f8c26 100644
--- a/server/src/commands.rs
+++ b/server/src/commands.rs
@@ -314,18 +314,32 @@ impl Server {
});
}
Command::StartTutorial { item } => {
- let item = self
- .game
- .data
- .get_item_by_name(&item)
- .ok_or(tre!("s.error.item_not_found", s = item))?;
if self.entities.iter().any(|e| {
<dyn std::any::Any>::downcast_ref::<Tutorial>(e.as_ref())
.is_some_and(|t| t.player == player)
}) {
return Err(tre!("s.error.tutorial_already_running"));
}
- self.entities.push(Box::new(Tutorial::new(player, item)));
+ if matches!(
+ item.as_str(),
+ "book" | "map-selector" | "button:reject" | "button:accept"
+ ) {
+ let tile = self
+ .game
+ .data
+ .get_tile_by_name(&item)
+ .ok_or(tre!("s.error.tile_not_found", s = item))?;
+ self.entities
+ .push(Box::new(Tutorial::new_button(player, tile)));
+ } else {
+ let item = self
+ .game
+ .data
+ .get_item_by_name(&item)
+ .ok_or(tre!("s.error.item_not_found", s = item))?;
+ self.entities
+ .push(Box::new(Tutorial::new_item(player, item)));
+ }
}
Command::EndTutorial => {
if let Some(tutorial) = self
diff --git a/server/src/entity/pedestrians.rs b/server/src/entity/pedestrians.rs
index 4fe91464..40ec721d 100644
--- a/server/src/entity/pedestrians.rs
+++ b/server/src/entity/pedestrians.rs
@@ -1,5 +1,3 @@
-use crate::random_gauss;
-
/*
Hurry Curry! - a game about cooking
Copyright (C) 2025 Hurry Curry! Contributors
@@ -18,6 +16,7 @@ use crate::random_gauss;
*/
use super::{Entity, EntityContext};
+use crate::random_gauss;
use anyhow::Result;
use hurrycurry_locale::TrError;
use hurrycurry_protocol::{Character, PacketS, PlayerClass, PlayerID, glam::Vec2};
diff --git a/server/src/entity/tutorial.rs b/server/src/entity/tutorial.rs
index 87896a09..47ec2aff 100644
--- a/server/src/entity/tutorial.rs
+++ b/server/src/entity/tutorial.rs
@@ -19,34 +19,60 @@ use super::{Entity, EntityContext};
use anyhow::Result;
use hurrycurry_locale::{TrError, trm};
use hurrycurry_protocol::{
- ItemIndex, Message, PacketC, PlayerID, Recipe, RecipeIndex, TileIndex, glam::IVec2,
+ ItemIndex, ItemLocation, Message, PacketC, PlayerID, Recipe, RecipeIndex, TileIndex,
+ glam::IVec2,
};
use log::{debug, warn};
pub struct Tutorial {
pub player: PlayerID,
- target: ItemIndex,
-
+ state: TutorialState,
next_update_due: f32,
- had_aquired_target: bool,
-
current_hint: Option<(Option<IVec2>, Message)>,
delete_timer: f32,
pub ended: bool,
}
+pub enum TutorialState {
+ Item {
+ item: ItemIndex,
+ had_aquired_target: bool,
+ },
+ Button {
+ tile: TileIndex,
+ pressed: bool,
+ },
+}
+
impl Tutorial {
- pub fn new(player: PlayerID, item: ItemIndex) -> Self {
+ fn new(player: PlayerID, target: TutorialState) -> Self {
Self {
ended: false,
player,
next_update_due: 0.,
- target: item,
+ state: target,
current_hint: None,
delete_timer: 1.5,
- had_aquired_target: false,
}
}
+ pub fn new_item(player: PlayerID, item: ItemIndex) -> Self {
+ Self::new(
+ player,
+ TutorialState::Item {
+ item,
+ had_aquired_target: false,
+ },
+ )
+ }
+ pub fn new_button(player: PlayerID, tile: TileIndex) -> Self {
+ Self::new(
+ player,
+ TutorialState::Button {
+ tile,
+ pressed: false,
+ },
+ )
+ }
}
impl Entity for Tutorial {
@@ -63,10 +89,34 @@ impl Entity for Tutorial {
}
c.packet_out.push_back(PacketC::TutorialEnded {
player: self.player,
- item: self.target,
+ item: match &self.state {
+ TutorialState::Item { item, .. } => *item,
+ _ => ItemIndex(0), // TODO
+ },
success: false,
});
}
+ fn interact(
+ &mut self,
+ c: EntityContext<'_>,
+ target: Option<ItemLocation>,
+ player: PlayerID,
+ ) -> Result<bool, TrError> {
+ if player == self.player {
+ match (&mut self.state, target) {
+ (TutorialState::Button { tile, pressed }, Some(ItemLocation::Tile(pos))) => {
+ if let Some(t) = c.game.tiles.get(&pos) {
+ if t.parts.contains(tile) {
+ *pressed = true;
+ }
+ }
+ }
+ _ => (),
+ };
+ }
+ Ok(false)
+ }
+
fn tick(&mut self, c: EntityContext<'_>) -> Result<(), TrError> {
if self.ended {
return Ok(());
@@ -78,21 +128,30 @@ impl Entity for Tutorial {
}
self.next_update_due += TARGET_DT;
- let mut hint = StepContext {
- ent: &c,
- had_aquired_target: &mut self.had_aquired_target,
- player: self.player,
- recursion_abort: 0,
- }
- .fulfil_demand(self.target)
- .err();
+ let mut hint = match &mut self.state {
+ TutorialState::Item {
+ item,
+ had_aquired_target,
+ } => StepContext {
+ ent: &c,
+ had_aquired_target,
+ player: self.player,
+ recursion_abort: 0,
+ }
+ .fulfil_demand(*item)
+ .err(),
+ TutorialState::Button { tile, pressed } => find_button(&c, *tile, *pressed),
+ };
if hint.is_none() {
self.delete_timer -= TARGET_DT;
if self.delete_timer <= 0. {
self.ended = true;
hint = None;
c.packet_out.push_back(PacketC::TutorialEnded {
- item: self.target,
+ item: match &self.state {
+ TutorialState::Item { item, .. } => *item,
+ _ => ItemIndex(0),
+ },
player: self.player,
success: true,
});
@@ -123,6 +182,31 @@ impl Entity for Tutorial {
}
}
+fn find_button(
+ ent: &EntityContext,
+ tile: TileIndex,
+ pressed: bool,
+) -> Option<(Option<IVec2>, Message)> {
+ if pressed {
+ return None;
+ }
+ ent.game
+ .tile_index
+ .get(&tile)
+ .map(|pos| pos.iter().next())
+ .flatten()
+ .map(|p| {
+ (
+ Some(*p),
+ match ent.game.data.tile_name(tile) {
+ "book" => trm!("s.tutorial.button.book"),
+ "map-selector" => trm!("s.tutorial.button.map_selector"),
+ _ => trm!("s.tutorial.interact"),
+ },
+ )
+ })
+}
+
struct StepContext<'a> {
ent: &'a EntityContext<'a>,
had_aquired_target: &'a mut bool,
diff --git a/server/src/server.rs b/server/src/server.rs
index f3d78176..96cf547d 100644
--- a/server/src/server.rs
+++ b/server/src/server.rs
@@ -395,6 +395,7 @@ impl Server {
player,
hand,
} => {
+ let mut entity_handled = false;
for e in &mut self.entities {
if e.interact(
EntityContext {
@@ -411,9 +412,13 @@ impl Server {
target,
player,
)? {
- return Ok(());
+ // multiple entities might handle this in case of a button tutorial
+ entity_handled = true;
}
}
+ if entity_handled {
+ return Ok(());
+ }
let pid = player;
let player = self