diff --git a/crates/game-service/src/battle_session.rs b/crates/game-service/src/battle_session.rs index 09c2d34..e565d52 100644 --- a/crates/game-service/src/battle_session.rs +++ b/crates/game-service/src/battle_session.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::sync::Arc; pub const SNAPSHOT_INTERVAL_TICKS: i32 = 20; +pub const MAX_CATCH_UP_TICKS: i32 = 40; use std::time::Instant; use logic::battle::{ LogicGameMode, LogicGameObjectEntry, LogicVector2, BATTLE_TICKS_PER_SECOND, BATTLE_TYPE_PVP, @@ -105,6 +106,12 @@ impl BattleSession { fn snapshot_message(&mut self) -> Option { let snapshot = self.mode.snapshot().ok()?; let report = verify_snapshot(&snapshot); + tracing::debug!( + tick = self.tick, + bytes = snapshot.len(), + ok = report.is_ok(), + "built a battle snapshot" + ); if !report.is_ok() { tracing::error!( bytes = snapshot.len(), @@ -174,6 +181,7 @@ impl BattleSession { self.queued.push((at_tick.max(self.tick + 1), entries)); } pub fn advance_to(&mut self, tick: i32) { + let tick = tick.min(self.tick + MAX_CATCH_UP_TICKS); while self.tick < tick { self.tick += 1; self.mode.time.tick = self.tick; diff --git a/crates/game-service/src/service.rs b/crates/game-service/src/service.rs index b5468e8..69d4f7a 100644 --- a/crates/game-service/src/service.rs +++ b/crates/game-service/src/service.rs @@ -456,12 +456,17 @@ impl GameApi for GameService { Matched::Waiting => Ok(Vec::new()), }; } - Ok(self + let messages = self .running_battles .tick(account, |card, position, owner, instance| { self.battles.summon(card, position, owner, 0, instance) }) - .await) + .await; + if !messages.is_empty() { + let kinds: Vec = messages.iter().map(|message| message.message_type).collect(); + tracing::debug!(%account, sent = ?kinds, "battle tick"); + } + Ok(messages) } async fn battle_event(&self, account: AccountRef, payload: Vec) -> RpcResult<()> { let Ok(sent) = SendBattleEventMessage::from_bytes(&payload) else { diff --git a/crates/logic/src/battle/logic_component.rs b/crates/logic/src/battle/logic_component.rs index 960949d..3783ef5 100644 --- a/crates/logic/src/battle/logic_component.rs +++ b/crates/logic/src/battle/logic_component.rs @@ -65,6 +65,8 @@ pub struct LogicMovementComponent { pub pushback_speed: i32, pub move_timer: i32, pub jump_distance: i32, + pub goal: Option<(i32, i32)>, + pub route: Vec<(i32, i32)>, } impl Default for LogicMovementComponent { fn default() -> Self { @@ -89,6 +91,8 @@ impl Default for LogicMovementComponent { pushback_speed: 0, move_timer: 0, jump_distance: 0, + goal: None, + route: Vec::new(), } } } diff --git a/crates/logic/src/battle/logic_simulation.rs b/crates/logic/src/battle/logic_simulation.rs index 298f5db..3618164 100644 --- a/crates/logic/src/battle/logic_simulation.rs +++ b/crates/logic/src/battle/logic_simulation.rs @@ -23,6 +23,7 @@ pub const COLUMN_TARGET_ONLY_BUILDINGS: &str = "TargetOnlyBuildings"; pub const COLUMN_FLYING_HEIGHT: &str = "FlyingHeight"; pub const COLUMN_PROJECTILE: &str = "Projectile"; pub const DISTANCE_SATURATION: i32 = 46340; +pub const WAYPOINT_REACHED: i32 = 250; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct LogicCharacterStats { pub speed: i32, @@ -130,6 +131,23 @@ impl LogicGameObjectEntry { component.hitpoints = (component.hitpoints - amount.max(0)).max(0); } } + pub fn movement(&self) -> Option<&crate::battle::logic_component::LogicMovementComponent> { + match self.components.get(crate::battle::logic_component::COMPONENT_MOVEMENT)? { + Some(LogicComponent::Movement(component)) => Some(component), + _ => None, + } + } + pub fn movement_mut( + &mut self, + ) -> Option<&mut crate::battle::logic_component::LogicMovementComponent> { + match self + .components + .get_mut(crate::battle::logic_component::COMPONENT_MOVEMENT)? + { + Some(LogicComponent::Movement(component)) => Some(component), + _ => None, + } + } pub fn combat_mut(&mut self) -> Option<&mut crate::battle::logic_component::LogicCombatComponent> { match self.components.get_mut(COMPONENT_COMBAT)? { Some(LogicComponent::Combat(component)) => Some(component), @@ -239,6 +257,57 @@ impl LogicBattle { } best.map(|(_, other)| other) } + fn next_waypoint( + &mut self, + index: usize, + from: (i32, i32), + goal: (i32, i32), + flying: bool, + ) -> (i32, i32) { + let stale = match self.objects.objects[index].movement() { + Some(movement) => movement.goal != Some(goal) || movement.path.is_empty(), + None => return goal, + }; + if stale { + let path = self + .tilemap + .as_ref() + .map(|tilemap| crate::battle::find_path(tilemap, from, goal, flying)) + .unwrap_or_default(); + if let Some(movement) = self.objects.objects[index].movement_mut() { + movement.goal = Some(goal); + movement.route = path; + } + } + let reached = self + .objects + .objects + .get(index) + .and_then(LogicGameObjectEntry::movement) + .and_then(|movement| movement.route.first().copied()) + .map(|node| { + let close = distance_squared(from, node) <= WAYPOINT_REACHED * WAYPOINT_REACHED; + (node, close) + }); + match reached { + Some((node, true)) => { + if let Some(movement) = self.objects.objects[index].movement_mut() { + if !movement.route.is_empty() { + movement.route.remove(0); + } + } + let _ = node; + self.objects + .objects + .get(index) + .and_then(LogicGameObjectEntry::movement) + .and_then(|movement| movement.route.first().copied()) + .unwrap_or(goal) + } + Some((node, false)) => node, + None => goal, + } + } fn move_objects(&mut self) { let positions: Vec<(i32, i32)> = self .objects @@ -272,12 +341,7 @@ impl LogicBattle { if straight <= stand_off as i64 || straight == 0 { continue; } - let waypoint = self - .tilemap - .as_ref() - .map(|tilemap| crate::battle::find_path(tilemap, from, goal, stats.flying)) - .and_then(|path| path.first().copied()) - .unwrap_or(goal); + let waypoint = self.next_waypoint(index, from, goal, stats.flying); let dx = (waypoint.0 - from.0) as i64; let dy = (waypoint.1 - from.1) as i64; let leg = integer_sqrt(dx * dx + dy * dy);