Commit graph

7 commits

Author SHA1 Message Date
WiseDev
f0f739c02c drop the commentary and run a workspace-wide format pass 2026-08-28 16:27:34 +03:00
WiseDev
07f0ee5427 converge combat timers, projectile references, pending damage, and the spawn ring
- replay the shooter's combat update a second time when it launches a
  projectile, matching the client's own component-pass re-entry
- carry the shooter's damage effect on every projectile and null a
  shot's target/source when either leaves the board
- derive pending physical damage from the shots actually in flight
  instead of an incremental total, so it can never go negative
- split the spawn ring's two angle registers so three- and five-unit
  cards land where the client puts them
- attribute a combat target left at NONE to the exact site that did it,
  gated to report each object once
- number and annotate every checksum field so a client-reported
  mismatch resolves straight to a name
2026-08-28 16:27:07 +03:00
WiseDev
0451fcdf43 grant achievement progress and roll exp into levels
two separate breakages behind "no achievement, no level up", both
checked against the client in IDA.

level up: LogicClientAvatar::xpGainHelper adds a gain to exp_points -
which is progress WITHIN the level, not a running total - then rolls it
into levels: while exp_points >= ExpToNextLevel(level) it subtracts that
threshold and increments the level, calling levelUp for the deck slots
and diamond reward. our add_exp only grew exp_points, so the bar filled
past full and the level never moved. it now rolls over the same way,
against the exp_levels table.

achievement: the client only offers a claim when progress >= ActionCount
(LogicClientAvatar::isAchievementCompleted reads commodity 2), and the
claim command re-reads that same commodity - and build_avatar populated
none of it, so every claim came back "not completed". build_avatar now
serves real progress: findcard from the count of distinct cards owned,
reacharena from the arena reached. donate / jointeam / watchtv need
social features we do not have and stay at zero.

tests cover both and fail without the fixes: 25 exp reaches level 2 with
5 carried, and a completed findcard tier claims once, grants exp, and is
rejected the second time.
2026-08-24 09:15:22 +03:00
WiseDev
81d6fe3922 take the checksum from before the closing checkpoint
LogicGameMode::encode reads its return value out of getCheckSum() and
only then writes it:

  v15 = ChecksumEncoder::getCheckSum(a2);
  (...vptr+88)(a2, v15);        // the checkpoint vint
  return v15;

we were reading ours after that write, so the closing checkpoint was
folded into the number we compared. the two could never match, whatever
the simulation did - which is why tick 41 disagreed with six untouched
towers on the field.

write() now hands back the value it wrote, the way encode() does.
2026-08-23 15:04:51 +03:00
WiseDev
438e2c6491 compare the server checksum against the client's
LogicGameMode::calculateChecksum runs the whole game mode through a
ChecksumEncoder - in a battle there is no client home, so it is the same
encode as the snapshot with the command manager left out. we compute the
same number now and log it beside the one the client sends in every turn
message, with whether they agree.

that comparison only means something because of what turned up while
reading the encoder: the two vints the decoder throws away are not
padding. the client writes getCheckSum() into both, once after the
server tick and once after the tutorial manager. we were writing zero,
which parses fine - the decoder discards them either way - but poisons
the running checksum, so the numbers could never have matched. they
carry the real running value now.

the two will not agree yet. the point is to see how far apart they are
and where, since the gap is what stands between this and battle type 0.
2026-08-23 13:44:51 +03:00
WiseDev
774e7c60cf build and send the battle sector state
npc missions used to get a ServerErrorMessage back. now StartMission builds a
LogicGameMode snapshot off the arena tilemap and answers 21903.

towers come from assets/locations/*.csv the way initDefaultSector does it: tile
coordinates times 500, leader index decided by which half of the map the tower
sits in. the two king towers must be there, the client dereferences them without
a null check. they live in buildings.csv, not characters.csv.

LogicCharacter puts the base object fields fourth, not first. the buff component
writes a fixed array sized by the character_buffs row count even with no buffs.

training_arena parses to 2 kings, 4 princess towers, 36x64 subtiles. snapshot is
602 bytes over 6 objects. the real client has not seen it yet.
2026-08-23 10:24:20 +03:00
WiseDev
d25a6de423 move all crates into crates/, glob members
pure move, no code touched. readme and protocol.md paths fixed up.
2026-08-23 08:15:45 +03:00