diff options
Diffstat (limited to 'server/src')
| -rw-r--r-- | server/src/commands.rs | 26 | ||||
| -rw-r--r-- | server/src/entity/pedestrians.rs | 3 | ||||
| -rw-r--r-- | server/src/entity/tutorial.rs | 120 | ||||
| -rw-r--r-- | server/src/server.rs | 7 |
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 |