/* Hurry Curry! - 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 . */ use super::movement::MovementBase; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, PacketS, }; use log::debug; use std::{ cmp::Ordering, collections::{BinaryHeap, HashMap, HashSet}, }; pub struct Path(Vec); impl Path { pub fn execute_tick( &mut self, player: &mut MovementBase, walkable: &HashSet, dt: f32, ) -> PacketS { if let Some(next) = self.0.last().copied() { debug!("next {next}"); if next.distance(player.position) < if self.0.len() == 1 { 0.1 } else { 0.6 } { self.0.pop(); } player.update( &walkable, (next - player.position).normalize_or_zero() * 0.5, false, dt, ) } else { player.update(&walkable, Vec2::ZERO, false, dt) } } pub fn is_done(&self) -> bool { self.0.is_empty() } } pub fn find_path(walkable: &HashSet, from: IVec2, to: IVec2) -> Option { #[derive(Debug, PartialEq, Eq)] struct Open(i32, IVec2, IVec2); impl PartialOrd for Open { fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(&other.0) } } impl Ord for Open { fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } } let mut visited = HashMap::new(); let mut open = BinaryHeap::new(); open.push(Open(1, from, from)); loop { let Some(Open(_, p, f)) = open.pop() else { return None; }; if visited.contains_key(&p) { continue; } visited.insert(p, f); if p == to { break; } for d in [IVec2::NEG_X, IVec2::NEG_Y, IVec2::X, IVec2::Y] { let n = p + d; if walkable.contains(&n) { open.push(Open(-d.distance_squared(to), n, p)); } } } let mut path = Vec::new(); let mut c = to; loop { path.push(c.as_vec2() + 0.5); let cn = visited[&c]; if cn == c { break; } c = cn } Some(Path(path)) }