From 6c5295bc0750bf0c8f85dde5ffd711288a581803 Mon Sep 17 00:00:00 2001 From: WiseDev <83840010+wisedevik@users.noreply.github.com> Date: Sun, 23 Aug 2026 11:17:10 +0300 Subject: [PATCH] fix the LogicGameObject field mapping the base object writes owner index, component mask, position and z, in that order. we had the first field unnamed and were writing the owner into the z slot, so every object belonged to player 0, and the mask slot carried a zero. the mask matters: getHitpointComponent tests bit 2 of it before touching components[2], so a zero mask made every tower report no hitpoints, LogicCharacter::isAlive fell through to the z field, shouldDestruct went true and the towers were destroyed on the first tick - which then hit "cant find summoner tower" in LogicBattle::removeGameObjectReferences. entries are built through LogicGameObjectEntry::new now so the mask is derived from the component array and cannot drift from it. also rename the first character flag after what sets it: kamikaze death and morph both raise it right before the object is removed. --- crates/game-service/src/battle.rs | 8 ++--- crates/logic/src/battle/logic_character.rs | 6 ++-- crates/logic/src/battle/logic_game_object.rs | 32 ++++++++++++++++++-- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/crates/game-service/src/battle.rs b/crates/game-service/src/battle.rs index fedaae9..3dac3ae 100644 --- a/crates/game-service/src/battle.rs +++ b/crates/game-service/src/battle.rs @@ -110,11 +110,11 @@ impl BattleBuilder { } else { LogicObjectBody::Character(Box::new(character)) }; - LogicGameObjectEntry { + LogicGameObjectEntry::new( data, - global_id: LogicGameObjectRef::of(CHARACTER_OBJECT_TYPE + 1, instance), + LogicGameObjectRef::of(CHARACTER_OBJECT_TYPE + 1, instance), body, - components: [ + [ Some(LogicComponent::Combat(LogicCombatComponent::default())), None, Some(LogicComponent::Hitpoint(LogicHitpointComponent::healthy( @@ -124,7 +124,7 @@ impl BattleBuilder { self.buff_type_count, ))), ], - } + ) } pub fn build( &self, diff --git a/crates/logic/src/battle/logic_character.rs b/crates/logic/src/battle/logic_character.rs index 15634d5..186d446 100644 --- a/crates/logic/src/battle/logic_character.rs +++ b/crates/logic/src/battle/logic_character.rs @@ -6,7 +6,7 @@ pub const DEFAULT_SIZE: i32 = 100; #[derive(Debug, Clone, PartialEq, Eq)] pub struct LogicCharacter { pub level_index: i32, - pub flag_160: bool, + pub is_destroyed: bool, pub is_leader: bool, pub base: LogicGameObject, pub direction: LogicVector2, @@ -29,7 +29,7 @@ impl Default for LogicCharacter { fn default() -> Self { Self { level_index: 0, - flag_160: false, + is_destroyed: false, is_leader: false, base: LogicGameObject::default(), direction: LogicVector2::new(0, DIRECTION_BOTTOM), @@ -53,7 +53,7 @@ impl Default for LogicCharacter { impl LogicCharacter { pub fn encode(&self, writer: &mut ByteStreamWriter) -> Result<()> { writer.write_vint(self.level_index); - writer.write_boolean(self.flag_160); + writer.write_boolean(self.is_destroyed); writer.write_boolean(self.is_leader); self.base.encode_base(writer)?; writer.write_vint(self.direction.x); diff --git a/crates/logic/src/battle/logic_game_object.rs b/crates/logic/src/battle/logic_game_object.rs index c61b4ab..41e6876 100644 --- a/crates/logic/src/battle/logic_game_object.rs +++ b/crates/logic/src/battle/logic_game_object.rs @@ -19,12 +19,12 @@ impl LogicVector2 { #[derive(Debug, Default, Clone, PartialEq, Eq, Payload)] pub struct LogicGameObject { #[codec(vint)] - pub field_28: i32, + pub owner_index: i32, #[codec(vint)] - pub field_56: i32, + pub component_mask: i32, pub position: LogicVector2, #[codec(vint)] - pub owner_index: i32, + pub z: i32, } impl LogicGameObject { pub fn encode_base(&self, writer: &mut titan::ByteStreamWriter) -> titan::Result<()> { @@ -37,6 +37,12 @@ pub enum LogicObjectBody { Summoner(Box), } impl LogicObjectBody { + pub fn base_mut(&mut self) -> &mut LogicGameObject { + match self { + LogicObjectBody::Character(character) => &mut character.base, + LogicObjectBody::Summoner(summoner) => &mut summoner.character.base, + } + } pub fn encode(&self, writer: &mut titan::ByteStreamWriter) -> titan::Result<()> { match self { LogicObjectBody::Character(character) => character.encode(writer), @@ -52,6 +58,26 @@ pub struct LogicGameObjectEntry { pub components: [Option; COMPONENT_PASSES], } impl LogicGameObjectEntry { + pub fn new( + data: LogicDataRef, + global_id: LogicGameObjectRef, + mut body: LogicObjectBody, + components: [Option; COMPONENT_PASSES], + ) -> Self { + let mut component_mask = 0; + for (index, component) in components.iter().enumerate() { + if component.is_some() { + component_mask |= 1 << index; + } + } + body.base_mut().component_mask = component_mask; + Self { + data, + global_id, + body, + components, + } + } pub fn object_type(&self) -> i32 { self.global_id.0.map(|id| id.class_id - 1).unwrap_or(-1) }