/* 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 . */ use crate::{ PacketSink, pathfinding::{Path, find_path_to_neighbour}, }; use hurrycurry_game_core::Game; use hurrycurry_protocol::{ Hand, ItemIndex, ItemLocation, PacketS, PlayerID, glam::{IVec2, Vec2}, }; pub struct StepState { path: Path, interact_position: IVec2, item_expected: Option, item_current: Option, hand: Hand, wait_timer: f32, interact_start_pending: bool, interact_stop_pending: bool, me: PlayerID, } impl StepState { pub fn new_idle(me: PlayerID) -> Self { Self::new_wait(me, 0.) } pub fn new_wait(me: PlayerID, timer: f32) -> Self { Self { me, path: Path::EMPTY, hand: Hand(0), wait_timer: timer, interact_start_pending: false, interact_stop_pending: false, interact_position: IVec2::ZERO, item_expected: None, item_current: None, } } pub fn new_segment( game: &Game, me: PlayerID, hand: Hand, pos: IVec2, interact: f32, ) -> Option { let own_pos = game.players.get(&me)?.movement.position.as_ivec2(); let path = find_path_to_neighbour(game, own_pos, pos)?; let destination_item = game .tiles .get(&pos) .and_then(|t| t.item.as_ref().map(|i| i.kind)); Some(Self { me, path, hand, wait_timer: interact, interact_start_pending: true, interact_stop_pending: true, interact_position: pos, item_expected: destination_item, item_current: destination_item, }) } pub fn is_busy(&self) -> bool { (self.wait_timer > 0. || self.interact_stop_pending) && !self.path.is_stuck(3.) && (self.path.is_done() || self.item_current == self.item_expected) } pub fn tick(&mut self, out: &mut PacketSink, game: &Game, dt: f32) { #[cfg(feature = "debug_events")] { use crate::debug_player_color; use hurrycurry_protocol::{DebugEvent, DebugEventDisplay, glam::Vec2}; out.push(PacketS::Debug(DebugEvent { key: format!("step-{}", self.me), color: debug_player_color(self.me), display: DebugEventDisplay::Label { pos: self.interact_position.as_vec2() + Vec2::splat(0.5), text: format!( "D={} E={} T={:.01}", self.path.is_done() as u8, (self.item_expected == self.item_current) as u8, self.wait_timer ), }, timeout: 1., })); } if self.path.is_done() { if self.interact_start_pending { self.interact_start_pending = false; out.push(PacketS::Interact { player: self.me, hand: self.hand, target: Some(ItemLocation::Tile(self.interact_position)), }); out.push(PacketS::Movement { player: self.me, dir: Vec2::ZERO, boost: false, pos: None, }); } else if self.wait_timer > 0. { self.wait_timer -= dt; } else if self.interact_stop_pending { self.interact_stop_pending = false; out.push(PacketS::Interact { player: self.me, hand: self.hand, target: None, }); } } else { self.item_current = game .tiles .get(&self.interact_position) .and_then(|t| t.item.as_ref().map(|i| i.kind)); let position = game .players .get(&self.me) .as_ref() .map(|p| p.movement.position) .unwrap_or_default(); let dir = self.path.next_direction(position, dt); #[cfg(feature = "debug_events")] out.push(PacketS::Debug(self.path.debug(self.me))); out.push(PacketS::Movement { player: self.me, dir, boost: self.path.is_stuck(0.5), pos: None, }); } } }