count a spawned character up to its deploy time

first, a correction: the towers do have models. the king tower shows its
cannon and the princess towers carry health bars, and health bars are
built inside Character::Character. so objects from the opening state get
their models after all, and the listener theory that sent me around the
houses was wrong. only troops are missing.

the difference is one field. LogicCharacter::getDeployT is

  clamp(DeployTime - this[47], 0, DeployTime)

and field 47 is our deploy_timer, which we set to zero and never moved.
so every troop reads as still coming down, for the whole battle: it
keeps the spawn effect and the sound the user could hear, and never
gets a body. towers are unaffected - their deploy time is zero.

the counter now climbs a tick at a time, the way the client's own
simulation would, and a moving character starts in state 1 as
LogicCharacter::setDefaultState leaves it.

widening the push interval to ten seconds changed nothing, so the
tear-down theory is out too.
This commit is contained in:
WiseDev 2026-08-23 16:40:47 +03:00
parent f072c16244
commit b8c12cd00b
3 changed files with 26 additions and 1 deletions

View file

@ -133,6 +133,11 @@ impl BattleBuilder {
DIRECTION_BOTTOM
},
),
state: if moves {
logic::battle::CHARACTER_STATE_MOVING
} else {
0
},
..LogicCharacter::default()
};
let components = self.components_for(hitpoints, moves);

View file

@ -5,6 +5,8 @@ use crate::battle::logic_battle::BATTLE_TICKS_PER_SECOND;
use crate::battle::logic_tilemap::SUBTILE_UNITS;
use crate::data::LogicDataRef;
pub const TICK_MILLISECONDS: i32 = 50;
pub const CHARACTER_DEPLOY_TIME_COLUMN: &str = "DeployTime";
pub const CHARACTER_STATE_MOVING: i32 = 1;
pub const MANA_ACCUMULATOR_STEP: i32 = 5000;
pub const MANA_RATE_SCALE: i32 = 100;
pub const GLOBAL_MAX_MANA: &str = "MAX_MANA";
@ -189,6 +191,7 @@ impl LogicBattle {
}
}
pub fn tick(&mut self, tick: i32) {
self.advance_deploy();
self.regenerate_mana(tick);
self.retarget();
self.resolve_collisions();
@ -196,6 +199,21 @@ impl LogicBattle {
self.resolve_attacks();
self.remove_dead();
}
fn advance_deploy(&mut self) {
for entry in self.objects.objects.iter_mut() {
let deploy_time = entry
.data
.data()
.map(|row| row.int(CHARACTER_DEPLOY_TIME_COLUMN))
.unwrap_or(0);
let LogicObjectBody::Character(character) = &mut entry.body else {
continue;
};
if character.deploy_timer < deploy_time {
character.deploy_timer += 1;
}
}
}
fn retarget(&mut self) {
let snapshot: Vec<(i32, (i32, i32), LogicCharacterStats, bool)> = self
.objects

View file

@ -33,7 +33,9 @@ pub use logic_game_object::{
pub use logic_game_object_manager::LogicGameObjectManager;
pub use logic_game_object_ref::LogicGameObjectRef;
pub use logic_pathfinder::find_path;
pub use logic_simulation::{LogicCharacterStats, TICK_MILLISECONDS};
pub use logic_simulation::{
LogicCharacterStats, CHARACTER_DEPLOY_TIME_COLUMN, CHARACTER_STATE_MOVING, TICK_MILLISECONDS,
};
pub use logic_tilemap::{LogicTilemap, OBJECT_KING_TOWER, OBJECT_PRINCESS_TOWER, SUBTILE_UNITS};
pub use logic_time::LogicTime;
pub use logic_tutorial_manager::LogicTutorialManager;