Skip to content

AIV troop-spot loader skips 3 unit types (pikemen / swordsmen / Arabian swordsmen) — placed troops never walk to their AIV positions #186

Description

@DDanielDragon

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions