Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
873c34a
reimplement: AICState methods that are less than 100%
gynt Oct 14, 2026
8251a29
add status file
gynt Oct 14, 2026
fa386a1
AICState: match aiShouldBuild* parameter type to the generated header
gynt Oct 16, 2026
a013994
reimplement: SHC_3BB0A8C1_0x004CCD20 80.13% Register allocation differs
gynt Oct 16, 2026
25fd3eb
reimplement: SHC_3BB0A8C1_0x004CF2E0 100.00% Reimplemented
gynt Oct 16, 2026
be606de
reimplement: SHC_3BB0A8C1_0x004D28B0 100.00% Reimplemented
gynt Oct 16, 2026
84a65b0
reimplement: SHC_3BB0A8C1_0x004CDB20 65.96% Loop register allocation
gynt Oct 16, 2026
177b341
reimplement: SHC_3BB0A8C1_0x004CBA50 90.32% Flag store order/slots
gynt Oct 16, 2026
34a0b9a
reimplement: SHC_3BB0A8C1_0x004CD2A0 73.17% tribeID/unitID regs swapped
gynt Oct 16, 2026
237721b
reimplement: SHC_3BB0A8C1_0x004D4340 77.19% playerID not kept in ebp
gynt Oct 16, 2026
818c2ce
reimplement: SHC_3BB0A8C1_0x004CC990 96.15% Init store before guard
gynt Oct 16, 2026
0253742
reimplement: SHC_3BB0A8C1_0x004D49E0 81.73% Pools 1 instead of 0 in ebp
gynt Oct 16, 2026
6a93c2c
reimplement: SHC_3BB0A8C1_0x004D1A00 68.80% targetPlayer/zero regs sw…
gynt Oct 16, 2026
910a28b
reimplement: SHC_3BB0A8C1_0x004D4680 100.00% Reimplemented
gynt Oct 16, 2026
5eb6821
reimplement: SHC_3BB0A8C1_0x004D2A70 58.57% Tile copies merged differ…
gynt Oct 16, 2026
8a1fe37
reimplement: SHC_3BB0A8C1_0x004CE200 100.00% Reimplemented
gynt Oct 16, 2026
4ce195c
reimplement: SHC_3BB0A8C1_0x004D2000 97.54% Register naming, alignmen…
gynt Oct 16, 2026
bd9a65e
reimplement: SHC_3BB0A8C1_0x004D3AE0 47.27% Loop register allocation
gynt Oct 16, 2026
2d0d4e5
reimplement: SHC_3BB0A8C1_0x004D2000 98.46% Register naming, alignmen…
gynt Oct 16, 2026
0cb54ab
reimplement: SHC_3BB0A8C1_0x004D2000 99.69% Loop latch reload order
gynt Oct 16, 2026
e4f2b69
reimplement: SHC_3BB0A8C1_0x004D48F0 100.00% Reimplemented
gynt Oct 16, 2026
a229c21
status: refresh three AICState entries measured with data resolvers e…
gynt Oct 16, 2026
6eb9b82
reimplement: SHC_3BB0A8C1_0x004CDCF0 83.25% Pools 1 in a register
gynt Oct 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/OpenSHC/AI/AICState/addEngineersToTribe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "../AICState.func.hpp"

#include "OpenSHC/Map/Units/UnitLogicState.hpp"
#include "OpenSHC/Map/Units/UnitType.hpp"

#include "OpenSHC/Globals/DAT_UnitsState.hpp"

namespace OpenSHC {
namespace AI {

// FUNCTION: STRONGHOLDCRUSADER 0x004D4040
void AICState::addEngineersToTribe(int playerID)
{
for (int unitID = 1; unitID < (int)DAT_UnitsState::instance.maxUnitCount; unitID++) {
if (DAT_UnitsState::instance.units[unitID].logicalState != Map::Units::ULS_NORMAL)
continue;
if (DAT_UnitsState::instance.units[unitID].owner != playerID)
continue;
if (DAT_UnitsState::instance.units[unitID].dying != 0)
continue;
if (DAT_UnitsState::instance.units[unitID].unitType != Map::Units::UT_E_ENGINEER)
continue;
if (DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType != 10)
continue;
if (DAT_UnitsState::instance.units[unitID].tribeID != 0)
continue;

MACRO_CALL_MEMBER(AICState_Func::addUnitToItsTribe, this)(unitID, 10);
}
}

}
}
57 changes: 57 additions & 0 deletions src/OpenSHC/AI/AICState/addRallyPointForTribe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include "../AICState.func.hpp"

#include "OpenSHC/Game/GameStateStructures.func.hpp"
#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp"
#include "OpenSHC/Map/Units/UnitLogicState.hpp"
#include "OpenSHC/Map/Units/UnitType.hpp"
#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp"

#include "OpenSHC/Globals/DAT_GameState.hpp"
#include "OpenSHC/Globals/DAT_PathFindingState.hpp"
#include "OpenSHC/Globals/DAT_TribesState.hpp"
#include "OpenSHC/Globals/DAT_UnitsState.hpp"

namespace OpenSHC {
namespace AI {

// FUNCTION: STRONGHOLDCRUSADER 0x004CFA10
void AICState::addRallyPointForTribe(int tribeID)
{
DAT_TribesState::instance.tribes[tribeID].rallyPointCount = 0;
for (int unitID = 1; unitID < 2500; unitID++) {
if (DAT_UnitsState::instance.units[unitID].logicalState == OpenSHC::Map::Units::ULS_INVISIBLE)
continue;
if (MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::getTeamsDifferent, DAT_GameState::ptr)(
DAT_TribesState::instance.tribes[tribeID].owner, DAT_UnitsState::instance.units[unitID].owner)
!= FALSE)
continue;
if (DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_S_CATAPULT
&& DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_S_TREBUCHET
&& DAT_UnitsState::instance.units[unitID].unitType != OpenSHC::Map::Units::UT_S_FBALLISTA)
continue;
if (DAT_UnitsState::instance.units[unitID].unknownSiegeTentRelated02 != 3)
continue;

MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::computeNextRallyPointDestination,
DAT_PathFindingState::ptr)(
-1, DAT_UnitsState::instance.units[unitID].x, DAT_UnitsState::instance.units[unitID].y);
DAT_TribesState::instance.tribes[tribeID]
.rallyPointArray[DAT_TribesState::instance.tribes[tribeID].rallyPointCount][0]
= DAT_PathFindingState::instance.ALG_ResultX;
DAT_TribesState::instance.tribes[tribeID]
.rallyPointArray[DAT_TribesState::instance.tribes[tribeID].rallyPointCount][1]
= DAT_PathFindingState::instance.ALG_ResultY;
DAT_TribesState::instance.tribes[tribeID].rallyPointCount++;
if (DAT_TribesState::instance.tribes[tribeID].rallyPointCount >= 9)
break;
}

if (DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex < 0)
DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex = 0;
if (DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex
>= DAT_TribesState::instance.tribes[tribeID].rallyPointCount)
DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex = 0;
}

}
}
27 changes: 27 additions & 0 deletions src/OpenSHC/AI/AICState/addUnitToItsTribe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "../AICState.func.hpp"

#include "OpenSHC/Map/Units/TribesState.func.hpp"
#include "OpenSHC/AI/AITypeA.hpp"

#include "OpenSHC/Globals/DAT_GameState.hpp"
#include "OpenSHC/Globals/DAT_TribesState.hpp"
#include "OpenSHC/Globals/DAT_UnitsState.hpp"

namespace OpenSHC {
namespace AI {

// FUNCTION: STRONGHOLDCRUSADER 0x004D27E0
void AICState::addUnitToItsTribe(int unitID, int aiUnitBehaviourType)
{
int owner = DAT_UnitsState::instance.units[unitID].owner;
if (DAT_GameState::instance.playerDataArray[owner].aiType == AITA_NULL)
return;

DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = aiUnitBehaviourType;
int tribeID = MACRO_CALL_MEMBER(AICState_Func::addUnitToSmallestBehaviourTypeTribe, this)(
owner, unitID, aiUnitBehaviourType);
if (tribeID != 0)
MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, tribeID);
}
}
}
81 changes: 81 additions & 0 deletions src/OpenSHC/AI/AICState/addUnitToSmallestBehaviourTypeTribe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "OpenSHC/Map/Units/TribesState.func.hpp"
#include "OpenSHC/AI/AICState.hpp"
#include "OpenSHC/AI/AITypeA.hpp"

#include "OpenSHC/Globals/DAT_GameState.hpp"
#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp"
#include "OpenSHC/Globals/DAT_TribesState.hpp"

namespace OpenSHC {
namespace AI {

// FUNCTION: STRONGHOLDCRUSADER 0x004CCD20
int AICState::addUnitToSmallestBehaviourTypeTribe(
undefined4 playerID, undefined4 unitID, undefined4 aiUnitBehaviourType)
{
int aiType = DAT_GameState::instance.playerDataArray[playerID].aiType;
if (aiType == AITA_NULL)
return 0;

int aicIndex = aiType - 1;
int firstSlot = DAT_SkirmishDefinedData::instance.AIUnitBehaviourTypeMapping[aiUnitBehaviourType - 10];
int selectedTribeID = 0;
int selectedSlot = 0;
int smallestSize = 1000;
int groupsCount;
switch (firstSlot) {
case 15:
groupsCount = this->aics[aicIndex].AttUnitPatrolGroupsCount;
break;
case 186:
groupsCount = this->aics[aicIndex].AttUnitBackupGroupsCount;
break;
case 190:
groupsCount = this->aics[aicIndex].AttUnitSiegeDefGroupsCount;
break;
case 192:
groupsCount = this->aics[aicIndex].AttMainGroupsCount;
break;
default:
groupsCount = 1;
break;
}
if (groupsCount < 1)
groupsCount = 1;

if (groupsCount == 1) {
int tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[firstSlot];
if (tribeID != 0
&& DAT_TribesState::instance.tribes[tribeID].uid
== DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[firstSlot])
return tribeID;
selectedTribeID
= MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID);
selectedSlot = firstSlot;
} else {
for (int slot = firstSlot; slot < firstSlot + groupsCount; slot++) {
int tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[slot];
if (tribeID == 0
|| DAT_TribesState::instance.tribes[tribeID].uid
!= DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[slot]) {
selectedTribeID = MACRO_CALL_MEMBER(
Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID);
selectedSlot = slot;
break;
}
if (DAT_TribesState::instance.tribes[tribeID].size < smallestSize) {
smallestSize = DAT_TribesState::instance.tribes[tribeID].size;
selectedTribeID = tribeID;
selectedSlot = slot;
}
}
}
if (selectedTribeID <= 0)
return 0;
DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[selectedSlot] = selectedTribeID;
DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[selectedSlot]
= DAT_TribesState::instance.tribes[selectedTribeID].uid;
return selectedTribeID;
}
}
}
35 changes: 35 additions & 0 deletions src/OpenSHC/AI/AICState/addUnitsToTribe166.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include "OpenSHC/Map/Units/TribesState.func.hpp"
#include "OpenSHC/AI/AICState.hpp"
#include "OpenSHC/Map/Units/UnitLogicState.hpp"

#include "OpenSHC/Globals/DAT_GameState.hpp"
#include "OpenSHC/Globals/DAT_TribesState.hpp"
#include "OpenSHC/Globals/DAT_UnitsState.hpp"

namespace OpenSHC {
namespace AI {

// FUNCTION: STRONGHOLDCRUSADER 0x004CC720
void AICState::addUnitsToTribe166(int playerID)
{
for (int unitID = 1; unitID < (int)DAT_UnitsState::instance.maxUnitCount; unitID++) {
if (DAT_UnitsState::instance.units[unitID].owner != playerID)
continue;
if (DAT_UnitsState::instance.units[unitID].logicalState != Map::Units::ULS_NORMAL)
continue;
if (DAT_UnitsState::instance.units[unitID].dying != 0)
continue;
if (DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType != 6)
continue;

int tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[166];
if (DAT_UnitsState::instance.units[unitID].tribeID == tribeID)
continue;

MACRO_CALL_MEMBER(Map::Units::TribesState_Func::removeUnitFromTribe, DAT_TribesState::ptr)(
unitID, DAT_UnitsState::instance.units[unitID].tribeID);
MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, tribeID);
}
}
}
}
31 changes: 31 additions & 0 deletions src/OpenSHC/AI/AICState/aiAddUnitToMoatDiggerTribe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "OpenSHC/Map/Units/TribesState.func.hpp"
#include "OpenSHC/AI/AICState.hpp"
#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp"

#include "OpenSHC/Globals/DAT_GameState.hpp"
#include "OpenSHC/Globals/DAT_TribesState.hpp"
#include "OpenSHC/Globals/DAT_UnitsState.hpp"

namespace OpenSHC {
namespace AI {

// FUNCTION: STRONGHOLDCRUSADER 0x004CC840
void AICState::aiAddUnitToMoatDiggerTribe(int unitID)
{
int owner = DAT_UnitsState::instance.units[unitID].owner;
DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = 5;
int tribeID = DAT_GameState::instance.playerDataArray[owner].aiTribeIDs[10];
if (tribeID == 0
|| DAT_TribesState::instance.tribes[tribeID].uid
!= DAT_GameState::instance.playerDataArray[owner].aiTribeUIDs[10]) {
tribeID
= MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(owner);
DAT_GameState::instance.playerDataArray[owner].aiTribeIDs[10] = tribeID;
DAT_GameState::instance.playerDataArray[owner].aiTribeUIDs[10]
= DAT_TribesState::instance.tribes[tribeID].uid;
}
MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, tribeID);
DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_DEFENSIVE;
}
}
}
60 changes: 60 additions & 0 deletions src/OpenSHC/AI/AICState/aiAssignMoatDiggers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "../AICState.func.hpp"

#include "OpenSHC/AI/AITypeA.hpp"
#include "OpenSHC/Map/Units/UnitLogicState.hpp"
#include "OpenSHC/Map/Units/UnitType.hpp"

#include "OpenSHC/Globals/DAT_GameState.hpp"
#include "OpenSHC/Globals/DAT_UnitsState.hpp"

namespace OpenSHC {
namespace AI {

using namespace OpenSHC::Map::Units;

// FUNCTION: STRONGHOLDCRUSADER 0x004D3F20
void AICState::aiAssignMoatDiggers(int playerID)
{
int aiType = DAT_GameState::instance.playerDataArray[playerID].aiType;
if (aiType == AITA_NULL)
return;
int aicIndex = aiType - 1;
if (DAT_GameState::instance.playerDataArray[playerID].totalTroopsType0 <= 0)
return;

for (int unitID = 1; unitID < (int)DAT_UnitsState::instance.maxUnitCount; unitID++) {
if (DAT_UnitsState::instance.units[unitID].logicalState == ULS_INVISIBLE)
continue;
if (DAT_UnitsState::instance.units[unitID].owner != playerID)
continue;
if (DAT_UnitsState::instance.units[unitID].isSelectable_OR_matchTime == 0)
continue;
if (DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType != 0)
continue;
if (DAT_UnitsState::instance.units[unitID].dying != 0)
continue;

if (DAT_UnitsState::instance.units[unitID].unitType == UT_LORD
|| DAT_UnitsState::instance.units[unitID].unitType == UT_E_ENGINEER
|| DAT_UnitsState::instance.units[unitID].unitType == UT_TUNNELER
|| DAT_UnitsState::instance.units[unitID].unitType == UT_S_CATAPULT
|| DAT_UnitsState::instance.units[unitID].unitType == UT_S_TREBUCHET
|| DAT_UnitsState::instance.units[unitID].unitType == UT_S_MANGONEL
|| DAT_UnitsState::instance.units[unitID].unitType == UT_S_TOWER
|| DAT_UnitsState::instance.units[unitID].unitType == UT_S_BATTERINGRAM
|| DAT_UnitsState::instance.units[unitID].unitType == UT_S_SHIELD
|| DAT_UnitsState::instance.units[unitID].unitType == UT_S_BALLISTA
|| DAT_UnitsState::instance.units[unitID].unitType == UT_S_FBALLISTA)
continue;

if (this->aics[aicIndex].DefDiggingUnitMax == 0
|| DAT_UnitsState::instance.units[unitID].unitType == UT_E_ARCHER
|| DAT_UnitsState::instance.units[unitID].unitType == UT_E_XBOW
|| DAT_UnitsState::instance.units[unitID].someUnitStat4 == 0)
MACRO_CALL_MEMBER(AICState_Func::assignUnitToATribe, this)(unitID);
else
MACRO_CALL_MEMBER(AICState_Func::aiAddUnitToMoatDiggerTribe, this)(unitID);
}
}
}
}
26 changes: 26 additions & 0 deletions src/OpenSHC/AI/AICState/aiAssignUnitToDefensiveTribe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "../AICState.func.hpp"

#include "OpenSHC/Map/Units/TribesState.func.hpp"
#include "OpenSHC/AI/AITypeA.hpp"

#include "OpenSHC/Globals/DAT_GameState.hpp"
#include "OpenSHC/Globals/DAT_TribesState.hpp"
#include "OpenSHC/Globals/DAT_UnitsState.hpp"

namespace OpenSHC {
namespace AI {

// FUNCTION: STRONGHOLDCRUSADER 0x004D2790
void AICState::aiAssignUnitToDefensiveTribe(int unitID)
{
int owner = DAT_UnitsState::instance.units[unitID].owner;
if (DAT_GameState::instance.playerDataArray[owner].aiType == AITA_NULL)
return;

DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = 2;
int tribeID = MACRO_CALL_MEMBER(AICState_Func::getDefensiveTribeForUnit, this)(owner, unitID);
if (tribeID != 0)
MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, tribeID);
}
}
}
28 changes: 28 additions & 0 deletions src/OpenSHC/AI/AICState/aiBuyAndSellGoods.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "../AICState.func.hpp"

#include "OpenSHC/Globals/DAT_GameState.hpp"

namespace OpenSHC {
namespace AI {

// FUNCTION: STRONGHOLDCRUSADER 0x004D48F0
void AICState::aiBuyAndSellGoods(int playerID)
{
int marketID = DAT_GameState::instance.playerDataArray[playerID].marketplace.id;
DAT_GameState::instance.playerDataArray[playerID].hasMarketUnk = 0;
if (marketID != 0)
DAT_GameState::instance.playerDataArray[playerID].hasMarketUnk = 1;
if (DAT_GameState::instance.playerDataArray[playerID].hasMarketUnk == 0)
return;

DAT_GameState::instance.playerDataArray[playerID].willBuy
= DAT_GameState::instance.playerDataArray[playerID].willBuy ^ TRUE;
if (DAT_GameState::instance.playerDataArray[playerID].willBuy == FALSE) {
MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sellExcessGoods, this)(playerID);
return;
}
MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::buyRequiredGoods, this)(playerID);
}

}
}
Loading