Hi — sharing a finding from reverse-engineering the AIV troop-spot behaviour, in case it's useful for the reimplementation. All base-Stronghold Crusader.exe addresses below are against the SHA256 your SARIF targets (3BB0A8C1…AC5A); I verified the same bug exists in Crusader Extreme and shipped a UCP3 fix for it.
Symptom
Start troops placed at AIV unit positions (section 2012) never walk to their spots for three unit types, while ranged units / knights / assassins do. Most visibly the Arabic lords' swordsmen just idle at the keep. Firefly fixed this for the Definitive Edition; the original engine never was.
Mechanism
The loop that decodes AIV section 2012 into the per-AI spot arrays contains an explicit skip of three rows. In base SHC at 0x4EF4B0:
0x4EF4B0 cmp dword [esp+0x1C], 9 ; row index
0x4EF4B5 mov eax, [esp+0x38]
0x4EF4B9 mov dword [eax], 0 ; zero this row's count
0x4EF4BF je 0x4EF7F7 ; -> loop increment, row not loaded
0x4EF4C5 cmp dword [esp+0x1C], 0xB
0x4EF4CA je 0x4EF7F7
0x4EF4D0 cmp dword [esp+0x1C], 0x12
0x4EF4D5 je 0x4EF7F7
0x4EF4DB ... ; normal position decoding
The row index is the AIVUnitType (your enum). Skipped: 9 = Pikeman, 11 (0xB) = Swordsman, 18 (0x12) = Arabian Swordsman — three melee infantry types. Their positions are discarded, so the downstream assignment (AICState::sendWallPatrolUnitTribesToAIVLocations in your naming) finds no spot for them and they never get a move order.
Why it looks like a bug, not intent
- Stock AIVs place real troops in exactly these rows (e.g. every Arabic lord uses row 18; Wolf uses row 9; Phillip uses row 11).
- These three types otherwise go through the same "walk to spot" path as knights (row 12) and assassins (row 15), which do walk — only their spot data is missing because of the skip.
- Removing the three
je (NOP) makes them march to their positions, matching DE behaviour. Verified live.
Crusader Extreme
Same code, shifted addresses (Extreme has extra code; base and Extreme addresses do not coincide). In Stronghold_Crusader_Extreme.exe the skip block is at 0x4EF840. I can provide the Extreme↔base mapping for the surrounding AI functions if that helps your cross-version work — happy to contribute.
Thanks for OpenSHC; the named symbols (sendWallPatrolUnitTribesToAIVLocations, getDefensiveTribeForUnit, aiGiveOuterPatrolCommand, the AIVUnitType enum) made confirming all of this much faster.
Hi — sharing a finding from reverse-engineering the AIV troop-spot behaviour, in case it's useful for the reimplementation. All base-
Stronghold Crusader.exeaddresses below are against the SHA256 your SARIF targets (3BB0A8C1…AC5A); I verified the same bug exists in Crusader Extreme and shipped a UCP3 fix for it.Symptom
Start troops placed at AIV unit positions (section 2012) never walk to their spots for three unit types, while ranged units / knights / assassins do. Most visibly the Arabic lords' swordsmen just idle at the keep. Firefly fixed this for the Definitive Edition; the original engine never was.
Mechanism
The loop that decodes AIV section 2012 into the per-AI spot arrays contains an explicit skip of three rows. In base SHC at
0x4EF4B0:The row index is the
AIVUnitType(your enum). Skipped: 9 = Pikeman, 11 (0xB) = Swordsman, 18 (0x12) = Arabian Swordsman — three melee infantry types. Their positions are discarded, so the downstream assignment (AICState::sendWallPatrolUnitTribesToAIVLocationsin your naming) finds no spot for them and they never get a move order.Why it looks like a bug, not intent
je(NOP) makes them march to their positions, matching DE behaviour. Verified live.Crusader Extreme
Same code, shifted addresses (Extreme has extra code; base and Extreme addresses do not coincide). In
Stronghold_Crusader_Extreme.exethe skip block is at0x4EF840. I can provide the Extreme↔base mapping for the surrounding AI functions if that helps your cross-version work — happy to contribute.Thanks for OpenSHC; the named symbols (
sendWallPatrolUnitTribesToAIVLocations,getDefensiveTribeForUnit,aiGiveOuterPatrolCommand, theAIVUnitTypeenum) made confirming all of this much faster.