From d47a286e0c503a6a4808fcf03fed7bfac4097f35 Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 22:52:34 +0200 Subject: [PATCH 01/85] reimplement: SHC_3BB0A8C1_0x004F15C0 63.13% Register allocation --- .../AI/AIVState/aiDecideOnNewBuildings.cpp | 112 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp diff --git a/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp b/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp new file mode 100644 index 000000000..5dbc385a3 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp @@ -0,0 +1,112 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/AI/AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004F15C0 + void AIVState::aiDecideOnNewBuildings(PlayerID playerID) + { + int const aivID = DAT_GameState::instance.playerDataArray[playerID].aivID; + int stepIncrement = 1; + int isPausing = 0; + if (aivID <= 0) { + return; + } + + if (DAT_GameState::instance.playerDataArray[playerID].resourceRebuildDelay != 0) { + ++DAT_GameState::instance.playerDataArray[playerID].resourceRebuildDelay; + if (DAT_GameState::instance.playerDataArray[playerID].resourceRebuildDelay + < MACRO_CALL_MEMBER(AICState_Func::getResourceRebuildDelay, DAT_AICState::ptr)(playerID)) { + return; + } + DAT_GameState::instance.playerDataArray[playerID].resourceRebuildDelay = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] > 16000) { + // rich + stepIncrement = 2; + } else if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] > 5000) { + stepIncrement = 1; + } else { + // poor + ++this->aivs[aivID].aivPoorCounter; + if (this->aivs[aivID].aivPoorCounter < this->aivs[aivID].aivPoorLimit_OR_AIC_buildInterval) { + return; + } + } + this->aivs[aivID].aivPoorCounter = 0; + + ++DAT_GameState::instance.playerDataArray[playerID].aiResourceBuildingFocusIndicator; + if (DAT_GameState::instance.playerDataArray[playerID].aiResourceBuildingFocusIndicator >= 5) { + DAT_GameState::instance.playerDataArray[playerID].aiResourceBuildingFocusIndicator = 0; + } + if (DAT_GameState::instance.playerDataArray[playerID].aiResourceBuildingFocusIndicator == 0) { + if (!MACRO_CALL_MEMBER(AICState_Func::shouldDestroyWoodCutterUnk, DAT_AICState::ptr)(playerID) + && MACRO_CALL_MEMBER(AICState_Func::aiShouldBuildWoodcutter, DAT_AICState::ptr)(playerID)) { + MACRO_CALL_MEMBER(AIVState_Func::aiPlaceWoodcuttershut, this)(playerID); + } + } else if (DAT_GameState::instance.playerDataArray[playerID].aiResourceBuildingFocusIndicator == 1) { + if (MACRO_CALL_MEMBER(AICState_Func::aiShouldBuildFarm, DAT_AICState::ptr)(playerID) + && MACRO_CALL_MEMBER(AIVState_Func::aiPlaceFarm, this)(playerID, + (MappersEnum)MACRO_CALL_MEMBER(AICState_Func::aiFarmChoice, DAT_AICState::ptr)(playerID))) { + MACRO_CALL_MEMBER(AICState_Func::setNextFarmTypeToBuild, DAT_AICState::ptr)(playerID); + } + } else if (DAT_GameState::instance.playerDataArray[playerID].aiResourceBuildingFocusIndicator == 2) { + if (MACRO_CALL_MEMBER(AICState_Func::aiShouldBuildQuarry, DAT_AICState::ptr)(playerID)) { + MACRO_CALL_MEMBER(AIVState_Func::aiPlaceQuarryAndOxtether, this)(playerID); + } + if (MACRO_CALL_MEMBER(AICState_Func::aiRequiresExtraOxtethers, DAT_AICState::ptr)(playerID)) { + MACRO_CALL_MEMBER(AIVState_Func::aiPlaceAdditionalOxtether, this)(playerID); + } + } else if (DAT_GameState::instance.playerDataArray[playerID].aiResourceBuildingFocusIndicator == 3) { + if (MACRO_CALL_MEMBER(AICState_Func::aiShouldBuildIronMine, DAT_AICState::ptr)(playerID)) { + MACRO_CALL_MEMBER(AIVState_Func::aiPlaceIronMine, this)(playerID); + } + } else if (DAT_GameState::instance.playerDataArray[playerID].aiResourceBuildingFocusIndicator == 4) { + if (MACRO_CALL_MEMBER(AICState_Func::aiShouldBuildPitchrig, DAT_AICState::ptr)(playerID)) { + MACRO_CALL_MEMBER(AIVState_Func::aiPlacePitchrig, this)(playerID); + } + } + + if (DAT_GameState::instance.playerDataArray[playerID].aivCurrentPause != 0) { + if (stepIncrement > 1) { + DAT_GameState::instance.playerDataArray[playerID].aivCurrentPause = 0; + isPausing = 1; + } else { + --DAT_GameState::instance.playerDataArray[playerID].aivCurrentPause; + isPausing = 1; + } + } else { + int const pauseStep = DAT_GameState::instance.playerDataArray[playerID] + .aivPauses[DAT_GameState::instance.playerDataArray[playerID].aivCurrentPauseIndex]; + if (pauseStep > 0 && this->aivs[aivID].currentStepGoal >= pauseStep) { + DAT_GameState::instance.playerDataArray[playerID].aivCurrentPause + = DAT_GameState::instance.playerDataArray[playerID].aivPauseDelay; + ++DAT_GameState::instance.playerDataArray[playerID].aivCurrentPauseIndex; + return; + } + // the step goal can advance by 2 when the player is rich + this->aivs[aivID].currentStepGoal += stepIncrement; + } + + if (this->aivs[aivID].aivSubType == 0) { + MACRO_CALL_MEMBER(AIVState_Func::executeDefaultCastleAIV, this)(playerID, isPausing); + } else if (!MACRO_CALL_MEMBER(AICState_Func::destroyHouse, DAT_AICState::ptr)(playerID)) { + for (int step = 1; step <= this->aivs[aivID].totalSteps && step <= this->aivs[aivID].currentStepGoal; + ++step) { + if (MACRO_CALL_MEMBER(AIVState_Func::aiPlaceAIVBuilding, this)(playerID, step, isPausing)) { + break; + } + } + } + MACRO_CALL_MEMBER(AIVState_Func::aiPlaceFlag, this)(playerID); + MACRO_CALL_MEMBER(AIVState_Func::aiPlaceBrazier, this)(playerID); + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index b80b8635f..44f231720 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38005,7 +38005,7 @@ SHC_3BB0A8C1_0x004F13F0 | 0.0% | Pending SHC_3BB0A8C1_0x004F14F0 | 0.0% | Pending -SHC_3BB0A8C1_0x004F15C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F15C0 | 63.1% | Register allocation SHC_3BB0A8C1_0x004F1860 | 0.0% | Pending From 297345475f19569da970f77842450bce5a383e2c Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 22:57:52 +0200 Subject: [PATCH 02/85] reimplement: SHC_3BB0A8C1_0x004EFC30 96.00% Reimplemented --- .../AI/AIVState/aiPlaceAdditionalOxtether.cpp | 53 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlaceAdditionalOxtether.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlaceAdditionalOxtether.cpp b/src/OpenSHC/AI/AIVState/aiPlaceAdditionalOxtether.cpp new file mode 100644 index 000000000..724ef65e8 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlaceAdditionalOxtether.cpp @@ -0,0 +1,53 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EFC30 + void AIVState::aiPlaceAdditionalOxtether(PlayerID playerID) + { + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(Commands::M_MAPPER_OXENBASE, playerID, FALSE) + || DAT_BuildingsState::instance.unknownCountdown01 < 20) { + return; + } + + int const quarryID = DAT_GameState::instance.playerDataArray[playerID].highestLoadedQuarryUnk; + MACRO_CALL_MEMBER(AIVState_Func::findSuitableGridLocationFittingCriteria, this)( + (short)DAT_BuildingsState::instance.buildings[quarryID].x / 5 + + DAT_AIVDefinedData::instance.OffsetsForFiveByFiveAreaSpotCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9].x, + (short)DAT_BuildingsState::instance.buildings[quarryID].y / 5 + + DAT_AIVDefinedData::instance.OffsetsForFiveByFiveAreaSpotCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9].y); + if (this->buildingApproriateGridXPosition == -1) { + return; + } + + ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, + this->buildingApproriateGridXPosition * 5, this->buildingAppropriateGridYPosition * 5, + Commands::M_MAPPER_OXENBASE, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + Commands::M_MAPPER_OXENBASE), + 15); + + // link the new ox tether to the first free slot of the quarry + int const oxTetherID = DAT_TileMapState::instance.placedBuildingID; + for (int tetherIndex = 0; tetherIndex < 3; ++tetherIndex) { + if (DAT_BuildingsState::instance.buildings[quarryID].quarryLinkedOxTethers[tetherIndex] == 0) { + DAT_BuildingsState::instance.buildings[quarryID].quarryLinkedOxTethers[tetherIndex] = (short)oxTetherID; + break; + } + } + DAT_BuildingsState::instance.buildings[oxTetherID].oxtetherLinkedQuarryID = (short)quarryID; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 44f231720..c1b01e6bd 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37983,7 +37983,7 @@ SHC_3BB0A8C1_0x004EFAB0 | 0.0% | Pending SHC_3BB0A8C1_0x004EFB90 | 0.0% | Pending -SHC_3BB0A8C1_0x004EFC30 | 0.0% | Pending +SHC_3BB0A8C1_0x004EFC30 | 96.0% | Reimplemented SHC_3BB0A8C1_0x004EFD90 | 0.0% | Pending From e6c3472c4329709e3ae7d739226bf8fcab352238 Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 23:07:21 +0200 Subject: [PATCH 03/85] reimplement: SHC_3BB0A8C1_0x004ED410 85.29% Stack slot assignment --- .../AI/AIVState/aiPlaceAIVBuilding.cpp | 311 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp new file mode 100644 index 000000000..5874764ad --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp @@ -0,0 +1,311 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ED410 + BOOLEnum AIVState::aiPlaceAIVBuilding(PlayerID playerID, int step, int isPausing) + { + int const aivID = DAT_GameState::instance.playerDataArray[playerID].aivID; + MappersEnum const mapper = (MappersEnum)this->aivs[aivID].aivBuildingSteps[step].buildingType; + + // when pausing, only hovels are built + if (isPausing && mapper != Commands::M_MAPPER_HOVEL) { + return FALSE; + } + // disabled steps and steps without room are never built + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_disabled) { + return FALSE; + } + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_insufficient_roomUnk) { + return FALSE; + } + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_insufficient_resources + && this->aivs[aivID].aivBuildingSteps[step].wait > 0) { + // wait for more resources + --this->aivs[aivID].aivBuildingSteps[step].wait; + return FALSE; + } + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built) { + if (DAT_GameState::instance.playerDataArray[playerID].closestEnemyDistanceToTheLord < 120) { + return FALSE; + } + } else if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(mapper, playerID, FALSE)) { + return FALSE; + } + if (DAT_BuildingsState::instance.unknownCountdown01 < 20) { + return FALSE; + } + if (MACRO_CALL_MEMBER(AICState_Func::mapperIsWoodCuttersHutWhileNoWoodAvailable, DAT_AICState::ptr)( + playerID, mapper)) { + return FALSE; + } + + if (mapper == Commands::M_MAPPER_MOAT) { + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built + && this->aivs[aivID].aivBuildingSteps[step].wait > 0) { + --this->aivs[aivID].aivBuildingSteps[step].wait; + return FALSE; + } + for (int i = 0; i < this->aivs[aivID].aivBuildingSteps[step].quantity; ++i) { + int const tile + = this->aivs[aivID].locationsArray[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + i]; + MACRO_CALL_MEMBER(Map::TileMapState_Func::planMoat, DAT_TileMapState::ptr)( + playerID, tile, DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]); + } + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; + this->aivs[aivID].aivBuildingSteps[step].wait = 50; + return FALSE; + } + + if (mapper == Commands::M_MAPPER_PITCH_DITCH) { + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built) { + if (this->aivs[aivID].aivBuildingSteps[step].wait > 0) { + --this->aivs[aivID].aivBuildingSteps[step].wait; + return FALSE; + } + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(Commands::M_MAPPER_PITCH_DITCH, playerID, FALSE)) { + return FALSE; + } + } + for (int i = 0; i < this->aivs[aivID].aivBuildingSteps[step].quantity; ++i) { + int const tile + = this->aivs[aivID].locationsArray[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + i]; + int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, x, y, + Commands::M_MAPPER_PITCH_DITCH, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, + DAT_TileMapState::ptr)(Commands::M_MAPPER_PITCH_DITCH), + 15); + } + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; + this->aivs[aivID].aivBuildingSteps[step].wait = 200; + return FALSE; + } + + // walls + if (mapper == Commands::M_MAPPER_WALL || mapper == Commands::M_MAPPER_WOODWALL + || mapper == Commands::M_MAPPER_CRENAL || mapper == Commands::M_MAPPER_CRENAL2) { + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built) { + if (this->aivs[aivID].aivBuildingSteps[step].wait > 0) { + --this->aivs[aivID].aivBuildingSteps[step].wait; + return FALSE; + } + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_STONE] <= 0) { + // no stone, request some + if (DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_STONE] + > 0) { + return FALSE; + } + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_STONE] = 5; + return FALSE; + } + } + for (int i = 0; i < this->aivs[aivID].aivBuildingSteps[step].quantity; ++i) { + int const tile + = this->aivs[aivID].locationsArray[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + i]; + uint const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + uint const x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, x, y, mapper, 1); + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeDefensiveStructureTile, DAT_TileMapState::ptr)( + playerID, x, y, mapper); + } + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; + this->aivs[aivID].aivBuildingSteps[step].wait = 20; + if (!DAT_TileMapState::instance.illegalBuild) { + return TRUE; + } + return FALSE; + } + + // stairs + if (mapper == Commands::M_MAPPER_STAIR1 || mapper == Commands::M_MAPPER_STAIR2 + || mapper == Commands::M_MAPPER_STAIR3 || mapper == Commands::M_MAPPER_STAIR4 + || mapper == Commands::M_MAPPER_STAIR5 || mapper == Commands::M_MAPPER_STAIR6) { + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built + && this->aivs[aivID].aivBuildingSteps[step].wait > 0) { + --this->aivs[aivID].aivBuildingSteps[step].wait; + return FALSE; + } + uint const y = DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; + uint const x = this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, x, y, mapper, 1); + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeDefensiveStructureTile, DAT_TileMapState::ptr)( + playerID, x, y, mapper); + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; + this->aivs[aivID].aivBuildingSteps[step].wait = 1; + if (!DAT_TileMapState::instance.illegalBuild) { + return TRUE; + } + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_insufficient_resources; + this->aivs[aivID].aivBuildingSteps[step].wait = 4; + return FALSE; + } + + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingGranary, DAT_AICState::ptr)(playerID, mapper)) { + return FALSE; + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingArmoury, DAT_AICState::ptr)(playerID, mapper)) { + return FALSE; + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingHovel, DAT_AICState::ptr)(playerID, mapper)) { + return FALSE; + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildMarketArmoryGranaryBarracksMercenarypost, + DAT_AICState::ptr)(playerID, mapper)) { + return FALSE; + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildKillingPits, DAT_AICState::ptr)(playerID, mapper)) { + return FALSE; + } + + int const tile = this->aivs[aivID].aivBuildingSteps[step].location.tile.tile; + int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + int orientation = 15; + if (mapper == Commands::M_MAPPER_BARRACKS_ARAB || mapper == Commands::M_MAPPER_BARRACKS_EURO + || mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD + || mapper == Commands::M_MAPPER_OIL_SMELTER) { + orientation = this->aivs[aivID].keepOrientation; + } else if (mapper == Commands::M_MAPPER_GATE_STONE1A || mapper == Commands::M_MAPPER_GATE_STONE2A) { + if (this->aivs[aivID].keepOrientation == 0 || this->aivs[aivID].keepOrientation == 4) { + orientation = 81; + } else { + orientation = 80; + } + } else if (mapper == Commands::M_MAPPER_GATE_STONE1B || mapper == Commands::M_MAPPER_GATE_STONE2B) { + if (this->aivs[aivID].keepOrientation == 2 || this->aivs[aivID].keepOrientation == 6) { + orientation = 81; + } else { + orientation = 80; + } + } else if (mapper == Commands::M_MAPPER_POLETURNER || mapper == Commands::M_MAPPER_BLACKSMITH + || mapper == Commands::M_MAPPER_ARMOURER || mapper == Commands::M_MAPPER_TANNER + || mapper == Commands::M_MAPPER_FLETCHER || mapper == Commands::M_MAPPER_BAKER + || mapper == Commands::M_MAPPER_BREWER) { + orientation = 8; + } else if (mapper == Commands::M_MAPPER_DRAWBRIDGE) { + DAT_TileMapState::instance.buildingPlacementFail = FALSE; + MACRO_CALL_MEMBER(Map::TileMapState_Func::checkDrawbridgePlacement, DAT_TileMapState::ptr)(x, y); + if (DAT_TileMapState::instance.buildingPlacementFail) { + return FALSE; + } + orientation = DAT_TileMapState::instance.uiBuildingRotation; + } + + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built) { + // rebuild a destroyed building + int const buildingID = (short)DAT_TileMapState::instance.BuildingLayer[tile]; + if ((short)DAT_BuildingsState::instance.buildings[buildingID].buildingType + == MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(mapper)) { + return FALSE; + } + if (DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker == 0 + && DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker == 0 + && this->aivs[aivID].aivBuildingSteps[step].wait > 0) { + --this->aivs[aivID].aivBuildingSteps[step].wait; + return FALSE; + } + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(mapper, playerID, FALSE)) { + return FALSE; + } + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldWaitWithBuildingAPositiveFearFactor, DAT_AICState::ptr)( + playerID, mapper)) { + return FALSE; + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldWaitWithBuildingNegativeFearFactor, DAT_AICState::ptr)( + playerID, mapper)) { + return FALSE; + } + + if (orientation == 15) { + DAT_TileMapState::instance.DAT_TempBuildingRotation = 0; + } else { + DAT_TileMapState::instance.DAT_TempBuildingRotation = orientation / 2; + } + int const buildingSize = MACRO_CALL_MEMBER( + Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)(mapper); + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, x, y, mapper, buildingSize); + // keep orientation is 0, 2, 4 or 6, halving it gives the index of the part offsets + if (mapper == Commands::M_MAPPER_BARRACKS_EURO || mapper == Commands::M_MAPPER_BARRACKS_ARAB) { + // the three fields in front of the barracks + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].x + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].y + y, + mapper, buildingSize); + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, + mapper, buildingSize); + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].x + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].y + y, + mapper, buildingSize); + + } else if (mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD) { + // the guild field + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, + mapper, buildingSize); + } else if (mapper == Commands::M_MAPPER_OIL_SMELTER) { + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].x + x, + DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].y + y, mapper, + buildingSize); + } + + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, x, y, mapper, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + mapper), + orientation); + int const placedID = DAT_TileMapState::instance.placedBuildingID; + if (!DAT_TileMapState::instance.buildingPlacementFail) { + DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].field245_0x2c8 = 1; + MACRO_CALL_MEMBER(AICState_Func::setupWorkshopProductionType, DAT_AICState::ptr)(playerID, placedID); + } + if (!DAT_TileMapState::instance.buildingPlacementFail) { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; + } else if (mapper == Commands::M_MAPPER_WHEATFARM || mapper == Commands::M_MAPPER_HOPSFARM + || mapper == Commands::M_MAPPER_APPLEFARM || mapper == Commands::M_MAPPER_CATTLEFARM) { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_insufficient_roomUnk; + } else { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_insufficient_resources; + } + this->aivs[aivID].aivBuildingSteps[step].wait + = this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built ? 10 : 16; + if (DAT_TileMapState::instance.buildingPlacementFail) { + return FALSE; + } + return TRUE; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index c1b01e6bd..0b9aec30f 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37931,7 +37931,7 @@ SHC_3BB0A8C1_0x004ED300 | 0.0% | Pending SHC_3BB0A8C1_0x004ED3B0 | 0.0% | Pending -SHC_3BB0A8C1_0x004ED410 | 0.0% | Pending +SHC_3BB0A8C1_0x004ED410 | 85.3% | Stack slot assignment SHC_3BB0A8C1_0x004EDC90 | 0.0% | Pending From 2e806f9a4a519c859d58de3d9f6e05680b48f199 Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 23:10:23 +0200 Subject: [PATCH 04/85] reimplement: SHC_3BB0A8C1_0x004ED300 96.23% Reimplemented --- src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp | 48 ++++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp b/src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp new file mode 100644 index 000000000..d31f27d48 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp @@ -0,0 +1,48 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ED300 + void AIVState::aiPlaceBrazier(PlayerID playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[AIVUT_BRAZIER] <= 0) { + return; + } + for (int i = 0; i < 10; ++i) { + int const tile = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlots[AIVUT_BRAZIER][i]; + if (tile <= 0) { + continue; + } + int const entityID = DAT_TileMapState::instance.EntityLayer[tile]; + if (entityID != 0 && DAT_EntityState::instance.entityArray[entityID].entityType == Map::Entities::ET_BRAZIER) { + continue; + } + uint const height + = MACRO_CALL_MEMBER(Map::TileMapState_Func::getTotalHeightAtTile, DAT_TileMapState::ptr)(tile); + if ((int)height <= 0) { + continue; + } + MACRO_CALL_MEMBER(Map::Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)(0, + playerID, playerID, + (tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) + * 8, + DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] * 8, height, 0, 0, 0, + Map::Entities::ET_BRAZIER, 0); + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 0b9aec30f..7c0d74421 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37927,7 +37927,7 @@ SHC_3BB0A8C1_0x004ED0B0 | 0.0% | Pending SHC_3BB0A8C1_0x004ED240 | 0.0% | Pending -SHC_3BB0A8C1_0x004ED300 | 0.0% | Pending +SHC_3BB0A8C1_0x004ED300 | 96.2% | Reimplemented SHC_3BB0A8C1_0x004ED3B0 | 0.0% | Pending From 31d0aac3f89235e5cd03e58349e592752ebeca07 Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 23:10:23 +0200 Subject: [PATCH 05/85] reimplement: SHC_3BB0A8C1_0x004ED240 94.74% Reimplemented --- src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp | 53 +++++++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp b/src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp new file mode 100644 index 000000000..d445e4f07 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp @@ -0,0 +1,53 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ED240 + void AIVState::aiPlaceFlag(PlayerID playerID) + { + Map::Entities::EntityType const flagType = (Map::Entities::EntityType)DAT_AICState::instance + .aics[DAT_GameState::instance.playerDataArray[playerID].aiType - 1] + .flagType; + if (DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[AIVUT_FLAG] <= 0) { + return; + } + for (int i = 0; i < 10; ++i) { + int const tile = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlots[AIVUT_FLAG][i]; + if (tile <= 0) { + continue; + } + int const entityID = DAT_TileMapState::instance.EntityLayer[tile]; + if (entityID != 0 && DAT_EntityState::instance.entityArray[entityID].entityType == flagType) { + continue; + } + int const height + = MACRO_CALL_MEMBER(Map::TileMapState_Func::getTotalHeightAt, DAT_TileMapState::ptr)(tile, playerID); + if (height <= 0) { + continue; + } + MACRO_CALL_MEMBER(Map::Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)(0, + playerID, playerID, + (tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) + * 8 + + 4, + DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] * 8 + 4, height, 0, 0, 0, + flagType, 0); + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 7c0d74421..4e50e9cff 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37925,7 +37925,7 @@ SHC_3BB0A8C1_0x004ED0A8 | 0.0% | Pending SHC_3BB0A8C1_0x004ED0B0 | 0.0% | Pending -SHC_3BB0A8C1_0x004ED240 | 0.0% | Pending +SHC_3BB0A8C1_0x004ED240 | 94.7% | Reimplemented SHC_3BB0A8C1_0x004ED300 | 96.2% | Reimplemented From 59e32188e2bd91e75a7dba50892040dfa771fadc Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 23:17:23 +0200 Subject: [PATCH 06/85] reimplement: SHC_3BB0A8C1_0x004EFA60 91.89% Reimplemented --- src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp | 30 +++++++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp b/src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp new file mode 100644 index 000000000..0b9894fca --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp @@ -0,0 +1,30 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EFA60 + BOOLEnum AIVState::aiPlaceFarm(int playerID, MappersEnum buildingType) + { + // despite its declared type, buildingType holds a BuildingType + if (buildingType <= 0) { + return FALSE; + } + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)( + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::convertBuildingTypeToCommandBuildingType, + DAT_BuildingsState::ptr)((Map::Buildings::BuildingType)buildingType), + playerID, FALSE)) { + return FALSE; + } + return MACRO_CALL_MEMBER(AIVState_Func::findSuitableFarmLocationAndPlaceFarm, this)(playerID, buildingType) != 0; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 4e50e9cff..14570f3e9 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37977,7 +37977,7 @@ SHC_3BB0A8C1_0x004EF900 | 0.0% | Pending SHC_3BB0A8C1_0x004EF920 | 0.0% | Pending -SHC_3BB0A8C1_0x004EFA60 | 0.0% | Pending +SHC_3BB0A8C1_0x004EFA60 | 91.9% | Reimplemented SHC_3BB0A8C1_0x004EFAB0 | 0.0% | Pending From cec75f7bbabc709612f5ff72308978d81281b67f Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 23:17:23 +0200 Subject: [PATCH 07/85] reimplement: SHC_3BB0A8C1_0x004EFE30 91.11% Reimplemented --- src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp | 34 +++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp b/src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp new file mode 100644 index 000000000..442153b1a --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp @@ -0,0 +1,34 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EFE30 + void AIVState::aiPlaceIronMine(int playerID) + { + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(Commands::M_MAPPER_IRON_MINE, playerID, FALSE) + || DAT_BuildingsState::instance.unknownCountdown01 < 20) { + return; + } + MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForResourceTypeBuilding, this)(playerID, AIGCRT_IRON); + if (this->buildingApproriateGridXPosition == -1) { + return; + } + ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, + this->buildingApproriateGridXPosition * 5, this->buildingAppropriateGridYPosition * 5, Commands::M_MAPPER_IRON_MINE, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + Commands::M_MAPPER_IRON_MINE), + 15); + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 14570f3e9..3ed80a9e5 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37987,7 +37987,7 @@ SHC_3BB0A8C1_0x004EFC30 | 96.0% | Reimplemented SHC_3BB0A8C1_0x004EFD90 | 0.0% | Pending -SHC_3BB0A8C1_0x004EFE30 | 0.0% | Pending +SHC_3BB0A8C1_0x004EFE30 | 91.1% | Reimplemented SHC_3BB0A8C1_0x004EFED0 | 0.0% | Pending From 093c0c1e0a7ee5f155abd5c4f022fccdcb166088 Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 23:17:23 +0200 Subject: [PATCH 08/85] reimplement: SHC_3BB0A8C1_0x004EFD90 91.11% Reimplemented --- src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp | 34 +++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp b/src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp new file mode 100644 index 000000000..8e4f12822 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp @@ -0,0 +1,34 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EFD90 + void AIVState::aiPlacePitchrig(int playerID) + { + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(Commands::M_MAPPER_PITCH_WORKINGS, playerID, FALSE) + || DAT_BuildingsState::instance.unknownCountdown01 < 20) { + return; + } + MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForResourceTypeBuilding, this)(playerID, AIGCRT_OIL); + if (this->buildingApproriateGridXPosition == -1) { + return; + } + ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, + this->buildingApproriateGridXPosition * 5, this->buildingAppropriateGridYPosition * 5, Commands::M_MAPPER_PITCH_WORKINGS, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + Commands::M_MAPPER_PITCH_WORKINGS), + 15); + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 3ed80a9e5..f927fddf5 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37985,7 +37985,7 @@ SHC_3BB0A8C1_0x004EFB90 | 0.0% | Pending SHC_3BB0A8C1_0x004EFC30 | 96.0% | Reimplemented -SHC_3BB0A8C1_0x004EFD90 | 0.0% | Pending +SHC_3BB0A8C1_0x004EFD90 | 91.1% | Reimplemented SHC_3BB0A8C1_0x004EFE30 | 91.1% | Reimplemented From 7f4a7f5c5414e2445e63cba107ec06740b897f04 Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 23:17:24 +0200 Subject: [PATCH 09/85] reimplement: SHC_3BB0A8C1_0x004EFED0 93.81% Reimplemented --- .../AI/AIVState/aiPlaceQuarryAndOxtether.cpp | 71 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlaceQuarryAndOxtether.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlaceQuarryAndOxtether.cpp b/src/OpenSHC/AI/AIVState/aiPlaceQuarryAndOxtether.cpp new file mode 100644 index 000000000..c177dd6a5 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlaceQuarryAndOxtether.cpp @@ -0,0 +1,71 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EFED0 + void AIVState::aiPlaceQuarryAndOxtether(int playerID) + { + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(Commands::M_MAPPER_QUARRY, playerID, FALSE) + || DAT_BuildingsState::instance.unknownCountdown01 < 20) { + return; + } + MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForResourceTypeBuilding, this)( + playerID, AIGCRT_BOULDER); + if (this->buildingApproriateGridXPosition == -1) { + return; + } + + ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; + int const quarryXOffset = DAT_AIVDefinedData::instance + .OffsetsForClockwiseThreeByThreeAreaCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9] + .x; + int const quarryYOffset = DAT_AIVDefinedData::instance + .OffsetsForClockwiseThreeByThreeAreaCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9] + .y; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, + quarryXOffset + this->buildingApproriateGridXPosition * 5, + quarryYOffset + this->buildingAppropriateGridYPosition * 5, Commands::M_MAPPER_QUARRY, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + Commands::M_MAPPER_QUARRY), + 15); + if (DAT_TileMapState::instance.buildingPlacementFail) { + return; + } + int const quarryID = DAT_TileMapState::instance.placedBuildingID; + MACRO_CALL_MEMBER(AIVState_Func::findSuitableGridLocationFittingCriteria, this)( + this->buildingApproriateGridXPosition, this->buildingAppropriateGridYPosition); + if (this->buildingApproriateGridXPosition == -1) { + return; + } + + // place an ox tether on the first free side and link it to the quarry + for (int side = 0; side < 4; ++side) { + int const xOffset = DAT_AIVDefinedData::instance.OffsetsForSpotCheckThreeByThreeAreaOnTheSide[side].x; + int const yOffset = DAT_AIVDefinedData::instance.OffsetsForSpotCheckThreeByThreeAreaOnTheSide[side].y; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, + xOffset + this->buildingApproriateGridXPosition * 5, yOffset + this->buildingAppropriateGridYPosition * 5, + Commands::M_MAPPER_OXENBASE, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + Commands::M_MAPPER_OXENBASE), + 15); + if (!DAT_TileMapState::instance.buildingPlacementFail) { + int const oxTetherID = DAT_TileMapState::instance.placedBuildingID; + DAT_BuildingsState::instance.buildings[quarryID].quarryLinkedOxTethers[0] = (short)oxTetherID; + DAT_BuildingsState::instance.buildings[oxTetherID].oxtetherLinkedQuarryID = (short)quarryID; + return; + } + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index f927fddf5..d4b719df6 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37989,7 +37989,7 @@ SHC_3BB0A8C1_0x004EFD90 | 91.1% | Reimplemented SHC_3BB0A8C1_0x004EFE30 | 91.1% | Reimplemented -SHC_3BB0A8C1_0x004EFED0 | 0.0% | Pending +SHC_3BB0A8C1_0x004EFED0 | 93.8% | Reimplemented SHC_3BB0A8C1_0x004F0060 | 0.0% | Pending From 257b2abaafcd27f7f9735ad5d24f3cbb5b1c5e72 Mon Sep 17 00:00:00 2001 From: Gynt Date: Tue, 22 Sep 2026 23:17:24 +0200 Subject: [PATCH 10/85] reimplement: SHC_3BB0A8C1_0x004EFAB0 82.76% Register reuse after search --- .../AI/AIVState/aiPlaceWoodcuttershut.cpp | 43 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/aiPlaceWoodcuttershut.cpp diff --git a/src/OpenSHC/AI/AIVState/aiPlaceWoodcuttershut.cpp b/src/OpenSHC/AI/AIVState/aiPlaceWoodcuttershut.cpp new file mode 100644 index 000000000..b115a429c --- /dev/null +++ b/src/OpenSHC/AI/AIVState/aiPlaceWoodcuttershut.cpp @@ -0,0 +1,43 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EFAB0 + void AIVState::aiPlaceWoodcuttershut(int playerID) + { + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(Commands::M_MAPPER_WOODSMAN, playerID, FALSE) + || DAT_BuildingsState::instance.unknownCountdown01 < 20) { + return; + } + MACRO_CALL_MEMBER(AIVState_Func::findAppropriateWoodCutterGridLocation, this)(playerID); + if (this->buildingApproriateGridXPosition == -1) { + return; + } + this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition] + .woodRelatedCountdownTo0 = 4; + // search around the result of the woodcutter search + MACRO_CALL_MEMBER(AIVState_Func::findSuitableGridLocationFittingCriteria, this)( + this->buildingApproriateGridXPosition, this->buildingAppropriateGridYPosition); + if (this->buildingApproriateGridXPosition == -1) { + return; + } + ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, + this->buildingApproriateGridXPosition * 5, this->buildingAppropriateGridYPosition * 5, + Commands::M_MAPPER_WOODSMAN, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + Commands::M_MAPPER_WOODSMAN), + 15); + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index d4b719df6..8964ffa20 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37979,7 +37979,7 @@ SHC_3BB0A8C1_0x004EF920 | 0.0% | Pending SHC_3BB0A8C1_0x004EFA60 | 91.9% | Reimplemented -SHC_3BB0A8C1_0x004EFAB0 | 0.0% | Pending +SHC_3BB0A8C1_0x004EFAB0 | 82.8% | Register reuse after search SHC_3BB0A8C1_0x004EFB90 | 0.0% | Pending From 2651b7543bc997b268b9dbc2c3973993327233f2 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 06:39:48 +0200 Subject: [PATCH 11/85] reimplement: SHC_3BB0A8C1_0x004EF0D0 30.96% Register alloc; 74% normalized --- src/OpenSHC/AI/AIVState/applyAIV.cpp | 188 +++++++++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/applyAIV.cpp diff --git a/src/OpenSHC/AI/AIVState/applyAIV.cpp b/src/OpenSHC/AI/AIVState/applyAIV.cpp new file mode 100644 index 000000000..a575b0b62 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/applyAIV.cpp @@ -0,0 +1,188 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/IO/FilePackager.func.hpp" +#include "OpenSHC/Rendering/ViewportRenderState.func.hpp" + +#include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/FilePackagerObj.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EF0D0 + void AIVState::applyAIV(int aivID, int playerID) + { + BOOLEnum keepDefined = FALSE; + MACRO_CALL_MEMBER(IO::FilePackager_Func::unpackAIVFile, FilePackagerObj::ptr)( + DAT_AIVDefinedData::instance.AIVSectionAddressArray, + DAT_AIVDefinedData::instance.AIVFileNames[this->aivs[aivID].aiType - 2][this->aivs[aivID].castleID]); + MACRO_CALL_MEMBER(AIVState_Func::rotateAIV, this)(this->aivs[aivID].keepOrientation); + + // single buildings and the keep + for (int y = 0; y < 100; ++y) { + for (int x = 0; x < 100; ++x) { + int const index = y * 100 + x; + if (this->isHandled[index]) { + continue; + } + int const type = this->constructions[index]; + if (type == 0) { + this->isHandled[index] = 1; + } else if (type == 2) { + this->isHandled[index] = 1; + } else if (type == AIV::AIVBT_KEEP2) { + if (!keepDefined) { + this->keepX = this->aivs[aivID].keepXOffset + x; + this->keepY = this->aivs[aivID].keepYOffset + y; + keepDefined = TRUE; + } + this->isHandled[index] = 1; + } else { + MappersEnum const mapper = MACRO_CALL_MEMBER(AIVState_Func::convertAIVBuildingTypeToCommandBuildingType, + this)((AIV::AIVBuildingType2)type); + int const step = this->steps[y][x]; + // walls, moats and pitch ditches are collected below + if (mapper == Commands::M_MAPPER_WALL || mapper == Commands::M_MAPPER_WOODWALL + || mapper == Commands::M_MAPPER_CRENAL || mapper == Commands::M_MAPPER_CRENAL2 + || mapper == Commands::M_MAPPER_MOAT || mapper == Commands::M_MAPPER_PITCH_DITCH) { + continue; + } + this->isHandled[index] = 1; + if (this->aivs[aivID].aivBuildingSteps[step].quantity > 0) { + continue; + } + int const realY = this->aivs[aivID].keepYOffset + y; + if (!MACRO_CALL_MEMBER(Rendering::ViewportRenderState_Func::xyAreValid, DAT_ViewportRenderState::ptr)( + this->aivs[aivID].keepXOffset + x, realY)) { + continue; + } + this->aivs[aivID].aivBuildingSteps[step].buildingType = mapper; + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_unbuilt; + this->aivs[aivID].aivBuildingSteps[step].quantity = 1; + this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + = DAT_ViewportRenderState::instance.translationMatrix[realY].addXgetTile + + this->aivs[aivID].keepXOffset + x; + if (step > this->aivs[aivID].totalSteps) { + this->aivs[aivID].totalSteps = step; + } + } + } + } + + // collect the tiles of each wall, moat and pitch ditch step, one step per pass + int startY = 0; + BOOLEnum selected; + do { + selected = FALSE; + if (startY >= 100) { + break; + } + int step; + MappersEnum mapper; + for (int y = startY; y < 100; ++y) { + for (int x = 0; x < 100; ++x) { + int const index = y * 100 + x; + if (this->isHandled[index]) { + continue; + } + if (selected) { + if (this->steps[y][x] != step) { + continue; + } + } else { + mapper = MACRO_CALL_MEMBER(AIVState_Func::convertAIVBuildingTypeToCommandBuildingType, this)( + (AIV::AIVBuildingType2)this->constructions[index]); + step = this->steps[y][x]; + this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + = this->aivs[aivID].wallLocationsArrayIndex; + selected = TRUE; + if (y > startY) { + startY = y; + } + } + uint const realX = this->aivs[aivID].keepXOffset + x; + uint const realY = this->aivs[aivID].keepYOffset + y; + if (realX <= 399 && realY <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[realY * 400 + realX]) { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_unbuilt; + this->aivs[aivID].aivBuildingSteps[step].buildingType = mapper; + ++this->aivs[aivID].aivBuildingSteps[step].quantity; + this->aivs[aivID].locationsArray[this->aivs[aivID].wallLocationsArrayIndex] + = DAT_ViewportRenderState::instance.translationMatrix[realY].addXgetTile + + this->aivs[aivID].keepXOffset + x; + ++this->aivs[aivID].wallLocationsArrayIndex; + if (step > this->aivs[aivID].totalSteps) { + this->aivs[aivID].totalSteps = step; + } + } + this->isHandled[index] = 1; + } + } + } while (selected); + + // rotate the unit locations and copy them to the player + int y; + int x; + for (int unitType = 0; unitType < 22; ++unitType) { + DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[unitType] = 0; + if (unitType == AIVUT_PIKEMAN || unitType == AIVUT_SWORDSMAN || unitType == AIVUT_ARABIAN_SWORDSMAN) { + continue; + } + for (int i = 0; i < 10; ++i) { + int const location = DAT_AIVState::instance.unitLocations[unitType][i]; + if (location <= 0) { + continue; + } + int const relativeY = location / 100; + int const relativeX = location % 100; + if (this->aivs[aivID].keepOrientation == 0) { + x = relativeX; + y = relativeY; + } else if (this->aivs[aivID].keepOrientation == 4) { + x = 99 - relativeX; + y = 99 - relativeY; + } else if (this->aivs[aivID].keepOrientation == 6) { + x = 99 - relativeY; + y = relativeX; + } else if (this->aivs[aivID].keepOrientation == 2) { + y = 99 - relativeX; + x = relativeY; + } + int const tile = DAT_ViewportRenderState::instance.translationMatrix[this->aivs[aivID].keepYOffset + y] + .addXgetTile + + this->aivs[aivID].keepXOffset + x; + DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlots[unitType][i] = tile; + if (tile > 0) { + ++DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[unitType]; + } + } + } + + for (int i = 0; i < 20; ++i) { + DAT_GameState::instance.playerDataArray[playerID].aivPauses[i] = (short)DAT_AIVState::instance.pauses[i]; + } + DAT_GameState::instance.playerDataArray[playerID].aivCurrentPauseIndex = 1; + if (DAT_AIVState::instance.pauseDelay <= 0) { + DAT_AIVState::instance.pauseDelay = 100; + } + DAT_GameState::instance.playerDataArray[playerID].aivPauseDelay = DAT_AIVState::instance.pauseDelay; + + // steps that can not be built + for (int step = 1; step <= this->aivs[aivID].totalSteps; ++step) { + if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_NULL + || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_KEEP3 + || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POND1 + || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POND2_SMALL + || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POND3_LARGE1 + || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POND4_LARGE2) { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_disabled; + } + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 8964ffa20..5e05a9883 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37963,7 +37963,7 @@ SHC_3BB0A8C1_0x004EEF20 | 0.0% | Pending SHC_3BB0A8C1_0x004EF090 | 0.0% | Pending -SHC_3BB0A8C1_0x004EF0D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004EF0D0 | 31.0% | Register alloc; 74% normalized SHC_3BB0A8C1_0x004EF130 | 0.0% | Pending From d48a918ba86e89ca674ae4a8737f8ea7011200bf Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 06:41:22 +0200 Subject: [PATCH 12/85] reimplement: SHC_3BB0A8C1_0x004EDC90 85.71% Reimplemented --- src/OpenSHC/AI/AIVState/clearTheHeatmaps.cpp | 18 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/clearTheHeatmaps.cpp diff --git a/src/OpenSHC/AI/AIVState/clearTheHeatmaps.cpp b/src/OpenSHC/AI/AIVState/clearTheHeatmaps.cpp new file mode 100644 index 000000000..32dd8cff3 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/clearTheHeatmaps.cpp @@ -0,0 +1,18 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EDC90 + void AIVState::clearTheHeatmaps() + { + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(this->heatMaps), 0, this->heatMaps); + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 5e05a9883..79ae1eff0 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37933,7 +37933,7 @@ SHC_3BB0A8C1_0x004ED3B0 | 0.0% | Pending SHC_3BB0A8C1_0x004ED410 | 85.3% | Stack slot assignment -SHC_3BB0A8C1_0x004EDC90 | 0.0% | Pending +SHC_3BB0A8C1_0x004EDC90 | 85.7% | Reimplemented SHC_3BB0A8C1_0x004EDCB0 | 0.0% | Pending From 82b3055db0ad8c397ce1a0d1dd7b7349b93ac48f Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 06:41:22 +0200 Subject: [PATCH 13/85] reimplement: SHC_3BB0A8C1_0x004F13E0 83.33% Reimplemented --- src/OpenSHC/AI/AIVState/Constructor_AIVState.cpp | 14 ++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/Constructor_AIVState.cpp diff --git a/src/OpenSHC/AI/AIVState/Constructor_AIVState.cpp b/src/OpenSHC/AI/AIVState/Constructor_AIVState.cpp new file mode 100644 index 000000000..6e2dd4d6c --- /dev/null +++ b/src/OpenSHC/AI/AIVState/Constructor_AIVState.cpp @@ -0,0 +1,14 @@ +#include "../AIVState.func.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004F13E0 + AIVState* AIVState::Constructor_AIVState() + { + MACRO_CALL_MEMBER(AIVState_Func::wipeAIVsAndHeatMaps, this)(); + return this; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 79ae1eff0..db315b03d 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37999,7 +37999,7 @@ SHC_3BB0A8C1_0x004F0E80 | 0.0% | Pending SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending -SHC_3BB0A8C1_0x004F13E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F13E0 | 83.3% | Reimplemented SHC_3BB0A8C1_0x004F13F0 | 0.0% | Pending From 091cef204bfe0d1eee3b6f9516a3bde99453834d Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 06:41:23 +0200 Subject: [PATCH 14/85] reimplement: SHC_3BB0A8C1_0x004ECFE0 100.00% Reimplemented --- ...rtAIVBuildingTypeToCommandBuildingType.cpp | 48 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/convertAIVBuildingTypeToCommandBuildingType.cpp diff --git a/src/OpenSHC/AI/AIVState/convertAIVBuildingTypeToCommandBuildingType.cpp b/src/OpenSHC/AI/AIVState/convertAIVBuildingTypeToCommandBuildingType.cpp new file mode 100644 index 000000000..5daa3b42d --- /dev/null +++ b/src/OpenSHC/AI/AIVState/convertAIVBuildingTypeToCommandBuildingType.cpp @@ -0,0 +1,48 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ECFE0 + MappersEnum AIVState::convertAIVBuildingTypeToCommandBuildingType(AIVBuildingType2 aivBuildingType) + { + switch (aivBuildingType) { + case AIV::AIVBT_HIGH_WALL: + return Commands::M_MAPPER_WALL; + case AIV::AIVBT_LOW_WALL: + return Commands::M_MAPPER_WOODWALL; + case AIV::AIVBT_CRENEL: + return Commands::M_MAPPER_CRENAL; + case AIV::AIVBT_LOW_CRENEL: + return Commands::M_MAPPER_CRENAL2; + case AIV::AIVBT_STAIRS_1: + return Commands::M_MAPPER_STAIR1; + case AIV::AIVBT_STAIRS_2: + return Commands::M_MAPPER_STAIR2; + case AIV::AIVBT_STAIRS_3: + return Commands::M_MAPPER_STAIR3; + case AIV::AIVBT_STAIRS_4: + return Commands::M_MAPPER_STAIR4; + case AIV::AIVBT_STAIRS_5: + return Commands::M_MAPPER_STAIR5; + case AIV::AIVBT_STAIRS_6Unk: + return Commands::M_MAPPER_STAIR6; + case AIV::AIVBT_MOAT_A: + case AIV::AIVBT_MOAT_B: + case AIV::AIVBT_MOAT_C: + case AIV::AIVBT_MOAT_D: + return Commands::M_MAPPER_MOAT; + case AIV::AIVBT_PITCHDITCH_2: + return Commands::M_MAPPER_PITCH_DITCH; + } + // all other buildings are looked up in a table starting at the first tower + if (aivBuildingType < AIV::AIVBT_TOWER1 || aivBuildingType > AIV::AIVBT_TOWER1 + 79) { + return Commands::M_MAPPER_GENERIC; + } + return DAT_AIVDefinedData::instance.field21_0xf4[aivBuildingType - AIV::AIVBT_TOWER1]; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index db315b03d..19af1b58a 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37891,7 +37891,7 @@ SHC_3BB0A8C1_0x004ECEF0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECF70 | 0.0% | Pending -SHC_3BB0A8C1_0x004ECFE0 | 0.0% | Pending +SHC_3BB0A8C1_0x004ECFE0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ED070 | 0.0% | Pending From dcea1236485eeab93d239b9c27f6347d4fef57d9 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 06:47:25 +0200 Subject: [PATCH 15/85] reimplement: SHC_3BB0A8C1_0x004EF8C0 60.19% Register alloc; 93% normalized --- .../AI/AIVState/computeAIVPlacementFit.cpp | 69 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp diff --git a/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp b/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp new file mode 100644 index 000000000..9189251d3 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp @@ -0,0 +1,69 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + // returns the lowest AIV step (minus one) that can not be placed, 999999 is a perfect fit + // FUNCTION: STRONGHOLDCRUSADER 0x004EF8C0 + int AIVState::computeAIVPlacementFit(int playerID) + { + int minSteps = 1000000; + DAT_TileMapState::instance.buildingPlacementProperty_7 = 0; + DAT_TileMapState::instance.buildingPlacementProperty_6 = 0; + DAT_TileMapState::instance.buildingPlacementProperty_5 = 0; + DAT_TileMapState::instance.buildingPlacementProperty_3 = 0; + int index = 0; + for (int y = 0; y < 100; ++y) { + for (int x = 0; x < 100; ++x, ++index) { + int const type = this->constructions[index]; + if (type == 0) { + continue; + } + if (type == 1) { + this->isHandled[index] = 1; + continue; + } + MappersEnum const mapper = MACRO_CALL_MEMBER( + AIVState_Func::convertAIVBuildingTypeToCommandBuildingType, this)((AIV::AIVBuildingType2)type); + DAT_TileMapState::instance.buildingHeightLimit + = DAT_BuildingDefinedData::instance.BuildingPlacement_HeightLimit[MACRO_CALL_MEMBER( + Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(mapper)]; + DAT_TileMapState::instance.buildingMaxHeightDifference + = DAT_BuildingDefinedData::instance.BuildingPlacement_MaxHeightDifference[MACRO_CALL_MEMBER( + Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(mapper)]; + ++this->buildingCounter; + uint const realY = this->aivs[playerID].keepYOffset + y; + uint const realX = this->aivs[playerID].keepXOffset + x; + if (realX > 399 || realY > 399 + || !DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[realY * 400 + realX]) { + continue; + } + int const rowTile = DAT_ViewportRenderState::instance.translationMatrix[realY].addXgetTile; + MACRO_CALL_MEMBER(Map::TileMapState_Func::storeMinAndMaxHeightOfArea, DAT_TileMapState::ptr)( + realX, realY, 1); + if (MACRO_CALL_MEMBER(Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, DAT_TileMapState::ptr)( + rowTile + realX, 0, mapper, 0)) { + this->isHandled[index] = 1; + // steps is accessed as a flat 100 * 100 array + if (this->steps[0][index] < minSteps) { + minSteps = this->steps[0][index]; + } + ++this->invalidTileCounter; + } + } + } + return minSteps - 1; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 19af1b58a..7255808c1 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37971,7 +37971,7 @@ SHC_3BB0A8C1_0x004EF150 | 0.0% | Pending SHC_3BB0A8C1_0x004EF450 | 0.0% | Pending -SHC_3BB0A8C1_0x004EF8C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004EF8C0 | 60.2% | Register alloc; 93% normalized SHC_3BB0A8C1_0x004EF900 | 0.0% | Pending From bb97bdd4885de5badaaf9bbcc27a2712207bb9e0 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 06:47:26 +0200 Subject: [PATCH 16/85] reimplement: SHC_3BB0A8C1_0x004EDD00 97.56% Reimplemented --- .../AIVState/computeLargestSeparateArea.cpp | 36 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/computeLargestSeparateArea.cpp diff --git a/src/OpenSHC/AI/AIVState/computeLargestSeparateArea.cpp b/src/OpenSHC/AI/AIVState/computeLargestSeparateArea.cpp new file mode 100644 index 000000000..04d2df38a --- /dev/null +++ b/src/OpenSHC/AI/AIVState/computeLargestSeparateArea.cpp @@ -0,0 +1,36 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EDD00 + int AIVState::computeLargestSeparateArea() + { + int areaSizes[10000]; + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(areaSizes), 0, areaSizes); + for (int tile = 0; tile < 80400; ++tile) { + int const areaID = (short)DAT_TileMapState::instance.PathConnectionLayer[tile]; + if (areaID > 0) { + ++areaSizes[areaID]; + } + } + + int highestSize = 0; + int largestAreaID = 0; + for (int areaID = 0; areaID < 10000; ++areaID) { + if (areaSizes[areaID] > highestSize) { + highestSize = areaSizes[areaID]; + largestAreaID = areaID; + } + } + return largestAreaID; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 7255808c1..48da53bee 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37937,7 +37937,7 @@ SHC_3BB0A8C1_0x004EDC90 | 85.7% | Reimplemented SHC_3BB0A8C1_0x004EDCB0 | 0.0% | Pending -SHC_3BB0A8C1_0x004EDD00 | 0.0% | Pending +SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented SHC_3BB0A8C1_0x004EDDF0 | 0.0% | Pending From 83267fd1cdfb916163b27ebfdbc3aa2e3042f84e Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 06:49:06 +0200 Subject: [PATCH 17/85] reimplement: SHC_3BB0A8C1_0x004EF0D0 36.88% Stack slots; 77% normalized --- src/OpenSHC/AI/AIVState/applyAIV.cpp | 26 ++++++++++++-------------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/applyAIV.cpp b/src/OpenSHC/AI/AIVState/applyAIV.cpp index a575b0b62..52dcdf95c 100644 --- a/src/OpenSHC/AI/AIVState/applyAIV.cpp +++ b/src/OpenSHC/AI/AIVState/applyAIV.cpp @@ -22,10 +22,10 @@ namespace AI { DAT_AIVDefinedData::instance.AIVFileNames[this->aivs[aivID].aiType - 2][this->aivs[aivID].castleID]); MACRO_CALL_MEMBER(AIVState_Func::rotateAIV, this)(this->aivs[aivID].keepOrientation); - // single buildings and the keep + // single buildings and the keep, steps is accessed as a flat 100 * 100 array + int index = 0; for (int y = 0; y < 100; ++y) { - for (int x = 0; x < 100; ++x) { - int const index = y * 100 + x; + for (int x = 0; x < 100; ++x, ++index) { if (this->isHandled[index]) { continue; } @@ -44,7 +44,7 @@ namespace AI { } else { MappersEnum const mapper = MACRO_CALL_MEMBER(AIVState_Func::convertAIVBuildingTypeToCommandBuildingType, this)((AIV::AIVBuildingType2)type); - int const step = this->steps[y][x]; + int const step = this->steps[0][index]; // walls, moats and pitch ditches are collected below if (mapper == Commands::M_MAPPER_WALL || mapper == Commands::M_MAPPER_WOODWALL || mapper == Commands::M_MAPPER_CRENAL || mapper == Commands::M_MAPPER_CRENAL2 @@ -83,20 +83,20 @@ namespace AI { } int step; MappersEnum mapper; + int index = startY * 100; for (int y = startY; y < 100; ++y) { - for (int x = 0; x < 100; ++x) { - int const index = y * 100 + x; + for (int x = 0; x < 100; ++x, ++index) { if (this->isHandled[index]) { continue; } if (selected) { - if (this->steps[y][x] != step) { + if (this->steps[0][index] != step) { continue; } } else { mapper = MACRO_CALL_MEMBER(AIVState_Func::convertAIVBuildingTypeToCommandBuildingType, this)( (AIV::AIVBuildingType2)this->constructions[index]); - step = this->steps[y][x]; + step = this->steps[0][index]; this->aivs[aivID].aivBuildingSteps[step].location.tile.tile = this->aivs[aivID].wallLocationsArrayIndex; selected = TRUE; @@ -173,12 +173,10 @@ namespace AI { // steps that can not be built for (int step = 1; step <= this->aivs[aivID].totalSteps; ++step) { - if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_NULL - || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_KEEP3 - || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POND1 - || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POND2_SMALL - || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POND3_LARGE1 - || this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POND4_LARGE2) { + int const buildingType = this->aivs[aivID].aivBuildingSteps[step].buildingType; + if (buildingType == Commands::M_MAPPER_NULL || buildingType == Commands::M_MAPPER_KEEP3 + || buildingType == Commands::M_MAPPER_POND1 || buildingType == Commands::M_MAPPER_POND2_SMALL + || buildingType == Commands::M_MAPPER_POND3_LARGE1 || buildingType == Commands::M_MAPPER_POND4_LARGE2) { this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_disabled; } } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 48da53bee..dda06085b 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37963,7 +37963,7 @@ SHC_3BB0A8C1_0x004EEF20 | 0.0% | Pending SHC_3BB0A8C1_0x004EF090 | 0.0% | Pending -SHC_3BB0A8C1_0x004EF0D0 | 31.0% | Register alloc; 74% normalized +SHC_3BB0A8C1_0x004EF0D0 | 36.9% | Stack slots; 77% normalized SHC_3BB0A8C1_0x004EF130 | 0.0% | Pending From 0d893c3222361a752761081a657bc7b9e9577c47 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 06:56:02 +0200 Subject: [PATCH 18/85] reimplement: SHC_3BB0A8C1_0x004F0060 36.43% Store scheduling; 77% normalized --- .../AI/AIVState/executeDefaultCastleAIV.cpp | 621 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 622 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp diff --git a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp new file mode 100644 index 000000000..4a3df1af6 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp @@ -0,0 +1,621 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + // builds a generic castle for AIs without an AIV, the number of each building depends on the AI character + // FUNCTION: STRONGHOLDCRUSADER 0x004F0060 + BOOLEnum AIVState::executeDefaultCastleAIV(PlayerID playerID, int isPausing) + { + int const aivID = DAT_GameState::instance.playerDataArray[playerID].aivID; + int stockpiles = 0; + int hovels = 0; + int granaries = 1; + int marketplaces = 1; + int armories = 0; + int barracks = 0; + int mercenaryPosts = 0; + int cathedrals = 0; + int chapels = 0; + int inns = 0; + int breweries = 0; + int bakeries = 0; + int mills = 0; + int poleturners = 0; + int fletchers = 0; + int tanners = 0; + int blacksmiths = 0; + int armourers = 0; + int engineersGuilds = 0; + int tunnelersGuilds = 0; + int stables = 0; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_RAT) { + stockpiles = 0; + hovels = 3; + armories = 1; + barracks = 1; + mercenaryPosts = 0; + inns = 0; + breweries = 0; + bakeries = 0; + mills = 0; + poleturners = 1; + fletchers = 1; + tanners = 0; + blacksmiths = 0; + engineersGuilds = 0; + chapels = 0; + armourers = 0; + cathedrals = 0; + stables = 0; + tunnelersGuilds = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_SNAKE) { + mercenaryPosts = 1; + tanners = 0; + blacksmiths = 0; + poleturners = 2; + stockpiles = 2; + inns = 0; + mills = 0; + bakeries = 0; + breweries = 0; + armories = 1; + barracks = 1; + hovels = 4; + engineersGuilds = 1; + fletchers = 2; + chapels = 0; + armourers = 0; + cathedrals = 0; + stables = 0; + tunnelersGuilds = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_PIG) { + mercenaryPosts = 0; + poleturners = 0; + tanners = 2; + blacksmiths = 2; + stockpiles = 2; + inns = 0; + mills = 0; + bakeries = 0; + breweries = 0; + armories = 1; + barracks = 1; + hovels = 4; + engineersGuilds = 1; + fletchers = 2; + chapels = 0; + armourers = 0; + cathedrals = 0; + stables = 0; + tunnelersGuilds = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_WOLF) { + stockpiles = 3; + hovels = 5; + armories = 1; + barracks = 1; + mercenaryPosts = 1; + chapels = 2; + inns = 2; + breweries = 3; + bakeries = 0; + mills = 0; + poleturners = 2; + fletchers = 2; + tanners = 1; + blacksmiths = 2; + armourers = 1; + engineersGuilds = 1; + cathedrals = 0; + stables = 0; + tunnelersGuilds = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_SALADIN) { + stockpiles = 3; + hovels = 8; + inns = 0; + breweries = 0; + bakeries = 4; + mills = 2; + mercenaryPosts = 1; + barracks = 0; + armories = 0; + poleturners = 0; + fletchers = 0; + tanners = 0; + blacksmiths = 0; + engineersGuilds = 1; + chapels = 0; + armourers = 0; + cathedrals = 0; + stables = 0; + tunnelersGuilds = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_CALIPH) { + stockpiles = 2; + inns = 2; + breweries = 5; + bakeries = 0; + mills = 0; + hovels = 6; + mercenaryPosts = 1; + barracks = 0; + armories = 0; + poleturners = 0; + fletchers = 0; + tanners = 0; + blacksmiths = 0; + engineersGuilds = 1; + chapels = 0; + armourers = 0; + cathedrals = 0; + stables = 0; + tunnelersGuilds = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_SULTAN) { + stockpiles = 0; + hovels = 3; + armories = 0; + barracks = 0; + mercenaryPosts = 1; + cathedrals = 0; + chapels = 0; + inns = 0; + breweries = 0; + bakeries = 0; + mills = 0; + poleturners = 0; + fletchers = 0; + tanners = 0; + blacksmiths = 0; + armourers = 0; + engineersGuilds = 1; + tunnelersGuilds = 0; + stables = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_RICHARD) { + stockpiles = 2; + hovels = 6; + armories = 1; + barracks = 1; + mercenaryPosts = 0; + cathedrals = 0; + chapels = 2; + inns = 0; + breweries = 0; + bakeries = 0; + mills = 0; + poleturners = 2; + fletchers = 2; + tanners = 0; + blacksmiths = 2; + armourers = 2; + engineersGuilds = 1; + tunnelersGuilds = 1; + stables = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_FREDERICK) { + hovels = 7; + stockpiles = 3; + armories = 1; + barracks = 1; + mercenaryPosts = 0; + cathedrals = 1; + chapels = 0; + inns = 2; + breweries = 2; + bakeries = 0; + mills = 0; + poleturners = 0; + fletchers = 2; + tanners = 2; + blacksmiths = 2; + armourers = 2; + engineersGuilds = 1; + tunnelersGuilds = 0; + stables = 2; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_PHILIPP) { + stockpiles = 2; + hovels = 5; + armories = 1; + barracks = 1; + mercenaryPosts = 0; + cathedrals = 0; + chapels = 2; + inns = 0; + breweries = 0; + bakeries = 0; + mills = 0; + poleturners = 3; + fletchers = 2; + tanners = 0; + blacksmiths = 2; + armourers = 2; + engineersGuilds = 1; + tunnelersGuilds = 0; + stables = 5; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_WAZIR) { + hovels = 6; + armories = 0; + barracks = 0; + inns = 0; + breweries = 0; + bakeries = 3; + fletchers = 0; + mercenaryPosts = 1; + stockpiles = 2; + poleturners = 0; + chapels = 0; + cathedrals = 0; + tanners = 0; + blacksmiths = 0; + armourers = 0; + tunnelersGuilds = 0; + stables = 0; + mills = 1; + engineersGuilds = 1; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_EMIR) { + hovels = 7; + armories = 1; + barracks = 1; + inns = 1; + breweries = 2; + bakeries = 2; + fletchers = 1; + mercenaryPosts = 1; + stockpiles = 3; + poleturners = 0; + chapels = 0; + cathedrals = 0; + tanners = 0; + blacksmiths = 0; + armourers = 0; + tunnelersGuilds = 0; + stables = 0; + mills = 1; + engineersGuilds = 1; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_NIZAR) { + hovels = 8; + armories = 0; + barracks = 0; + mercenaryPosts = 1; + cathedrals = 0; + chapels = 0; + inns = 1; + breweries = 3; + mills = 1; + fletchers = 0; + stockpiles = 2; + poleturners = 0; + bakeries = 4; + tanners = 0; + blacksmiths = 0; + armourers = 0; + engineersGuilds = 1; + tunnelersGuilds = 0; + stables = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_SHERIFF) { + stockpiles = 3; + hovels = 6; + armories = 1; + barracks = 1; + mercenaryPosts = 1; + cathedrals = 1; + chapels = 2; + inns = 2; + breweries = 2; + bakeries = 0; + mills = 0; + poleturners = 0; + fletchers = 2; + tanners = 2; + blacksmiths = 2; + armourers = 0; + engineersGuilds = 1; + tunnelersGuilds = 0; + stables = 0; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_MARSHAL) { + stockpiles = 2; + hovels = 5; + armories = 1; + barracks = 1; + mercenaryPosts = 0; + cathedrals = 0; + chapels = 0; + inns = 2; + breweries = 3; + bakeries = 0; + mills = 0; + poleturners = 0; + fletchers = 2; + tanners = 0; + blacksmiths = 2; + armourers = 2; + engineersGuilds = 1; + tunnelersGuilds = 0; + stables = 4; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_ABBOT) { + mills = 2; + chapels = 5; + hovels = 6; + armories = 1; + barracks = 1; + mercenaryPosts = 0; + cathedrals = 1; + inns = 3; + breweries = 6; + fletchers = 3; + stockpiles = 2; + poleturners = 0; + bakeries = 4; + tanners = 0; + blacksmiths = 0; + armourers = 0; + engineersGuilds = 1; + tunnelersGuilds = 0; + stables = 0; + } + + for (int step = 0; step <= 50; ++step) { + if (this->aivs[aivID].currentStepGoal < step) { + return FALSE; + } + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_disabled) { + // use the step for the next building that is still missing + if (stockpiles > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_STORES; + } else if (hovels > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_HOVEL; + } else if (granaries > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_GRANARY; + } else if (marketplaces > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_TRADEPOST; + } else if (barracks > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_BARRACKS_ARAB; + } else if (mercenaryPosts > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_BARRACKS_EURO; + } else if (armories > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_ARMOURY; + } else if (breweries > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_BREWER; + } else if (bakeries > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_BAKER; + } else if (mills > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_MILL; + } else if (poleturners > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_POLETURNER; + } else if (fletchers > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_FLETCHER; + } else if (tanners > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_TANNER; + } else if (blacksmiths > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_BLACKSMITH; + } else if (armourers > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_ARMOURER; + } else if (chapels > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_CHURCH1; + } else if (inns > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_INN; + } else if (engineersGuilds > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_ENGINEERS_GUILD; + } else if (tunnelersGuilds > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_TUNNELERS_GUILD; + } else if (stables > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_STABLES; + } else if (cathedrals > 0) { + this->aivs[aivID].aivBuildingSteps[step].buildingType = Commands::M_MAPPER_CHURCH3; + } else { + continue; + } + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_unbuilt; + } + + AIVBuildingStepStatusByte const status = this->aivs[aivID].aivBuildingSteps[step].buildStatus; + if (status != AIVBSS_disabled) { + if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_HOVEL) { + --hovels; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_STORES) { + --stockpiles; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_GRANARY) { + --granaries; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_TRADEPOST) { + --marketplaces; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_BARRACKS_ARAB) { + --barracks; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_BARRACKS_EURO) { + --mercenaryPosts; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_ARMOURY) { + --armories; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_BREWER) { + --breweries; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_BAKER) { + --bakeries; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_MILL) { + --mills; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_POLETURNER) { + --poleturners; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_FLETCHER) { + --fletchers; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_TANNER) { + --tanners; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_BLACKSMITH) { + --blacksmiths; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_ARMOURER) { + --armourers; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_CHURCH1) { + --chapels; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_INN) { + --inns; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_ENGINEERS_GUILD) { + --engineersGuilds; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_TUNNELERS_GUILD) { + --tunnelersGuilds; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_STABLES) { + --stables; + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_CHURCH3) { + --cathedrals; + } + } + + MappersEnum const mapper = (MappersEnum)this->aivs[aivID].aivBuildingSteps[step].buildingType; + if (isPausing && mapper != Commands::M_MAPPER_HOVEL) { + continue; + } + if (status == AIVBSS_built) { + // only rebuild buildings that were destroyed + short const buildingID = DAT_TileMapState::instance.BuildingLayer[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; + if (buildingID != 0 + && DAT_BuildingsState::instance.buildings[buildingID].buildingType + == MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(mapper)) { + continue; + } + } + if (DAT_GameState::instance.playerDataArray[playerID].closestEnemyDistanceToTheLord <= 120) { + continue; + } + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(mapper, playerID, FALSE) + || DAT_BuildingsState::instance.unknownCountdown01 < 20) { + continue; + } + if (mapper == Commands::M_MAPPER_WOODSMAN) { + if (DAT_AIVState::instance.mapExtraInfo.totalWoodAvailable <= 0) { + continue; + } + } else if (mapper == Commands::M_MAPPER_GRANARY + && !DAT_GameState::instance.playerDataArray[playerID].granaryIsAlmostFilledUp) { + continue; + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingArmoury, DAT_AICState::ptr)(playerID, mapper) + || MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingHovel, DAT_AICState::ptr)(playerID, mapper) + || MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildMarketArmoryGranaryBarracksMercenarypost, + DAT_AICState::ptr)(playerID, mapper)) { + continue; + } + + int orientation = 15; + if (mapper == Commands::M_MAPPER_BARRACKS_ARAB || mapper == Commands::M_MAPPER_BARRACKS_EURO + || mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD + || mapper == Commands::M_MAPPER_OIL_SMELTER) { + orientation = this->aivs[aivID].keepOrientation; + } else if (mapper == Commands::M_MAPPER_POLETURNER || mapper == Commands::M_MAPPER_BLACKSMITH + || mapper == Commands::M_MAPPER_ARMOURER || mapper == Commands::M_MAPPER_TANNER + || mapper == Commands::M_MAPPER_FLETCHER || mapper == Commands::M_MAPPER_BAKER + || mapper == Commands::M_MAPPER_BREWER) { + orientation = 8; + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldWaitWithBuildingAPositiveFearFactor, DAT_AICState::ptr)( + playerID, mapper) + || MACRO_CALL_MEMBER(AICState_Func::shouldWaitWithBuildingNegativeFearFactor, DAT_AICState::ptr)( + playerID, mapper)) { + continue; + } + if (orientation == 15) { + DAT_TileMapState::instance.DAT_TempBuildingRotation = 0; + } else { + DAT_TileMapState::instance.DAT_TempBuildingRotation = orientation / 2; + } + uint const buildingSize = MACRO_CALL_MEMBER( + Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)(mapper); + + uint x; + uint y; + if (mapper == Commands::M_MAPPER_STORES) { + // stockpiles are placed next to each other at the campground + if (stockpiles == 0) { + y = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry - 11; + x = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry + 9; + } else if (stockpiles == 1) { + y = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry - 11; + x = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry + 4; + } else if (stockpiles == 2) { + y = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry - 6; + x = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry + 9; + } + } else { + if (mapper == Commands::M_MAPPER_BARRACKS_ARAB || mapper == Commands::M_MAPPER_BARRACKS_EURO + || mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD + || mapper == Commands::M_MAPPER_CHURCH3) { + MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForBuilding, this)( + DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5, + DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5); + } else { + MACRO_CALL_MEMBER(AIVState_Func::findSuitableGridLocationFittingCriteria, this)( + DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5, + DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5); + } + if (this->buildingApproriateGridXPosition == -1 || this->buildingAppropriateGridYPosition == -1) { + continue; + } + x = this->buildingApproriateGridXPosition * 5; + y = this->buildingAppropriateGridYPosition * 5; + } + + int const rowTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, x, y, mapper, buildingSize); + // keep orientation is 0, 2, 4 or 6, halving it gives the index of the part offsets + if (mapper == Commands::M_MAPPER_BARRACKS_EURO || mapper == Commands::M_MAPPER_BARRACKS_ARAB) { + // the three fields in front of the barracks + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].x + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].y + y, + mapper, buildingSize); + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, + mapper, buildingSize); + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].x + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].y + y, + mapper, buildingSize); + } else if (mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD) { + // the guild field + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, + mapper, buildingSize); + } else if (mapper == Commands::M_MAPPER_OIL_SMELTER) { + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].x + x, + DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].y + y, mapper, + buildingSize); + } + + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, x, y, mapper, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + mapper), + orientation); + int const placedID = DAT_TileMapState::instance.placedBuildingID; + if (!DAT_TileMapState::instance.buildingPlacementFail) { + DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].field245_0x2c8 = 1; + MACRO_CALL_MEMBER(AICState_Func::setupWorkshopProductionType, DAT_AICState::ptr)(playerID, placedID); + this->aivs[aivID].aivBuildingSteps[step].location.tile.tile = rowTile + x; + if (!DAT_TileMapState::instance.buildingPlacementFail) { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; + } + } + this->aivs[aivID].aivBuildingSteps[step].wait = this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built ? 10 : 16; + if (!DAT_TileMapState::instance.buildingPlacementFail) { + return TRUE; + } + } + return FALSE; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index dda06085b..b5c8ecd0f 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37991,7 +37991,7 @@ SHC_3BB0A8C1_0x004EFE30 | 91.1% | Reimplemented SHC_3BB0A8C1_0x004EFED0 | 93.8% | Reimplemented -SHC_3BB0A8C1_0x004F0060 | 0.0% | Pending +SHC_3BB0A8C1_0x004F0060 | 36.4% | Store scheduling; 77% normalized SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending From 2e8ce2bb3fcf22066e418d84d863a8f2add4e94d Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:00:16 +0200 Subject: [PATCH 19/85] reimplement: SHC_3BB0A8C1_0x004EEE10 86.69% Register allocation --- ...findAppropriateGridLocationForBuilding.cpp | 88 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp diff --git a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp new file mode 100644 index 000000000..416a2841c --- /dev/null +++ b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp @@ -0,0 +1,88 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + // breadth first search for a grid cell whose eight neighbours are all free + // FUNCTION: STRONGHOLDCRUSADER 0x004EEE10 + void AIVState::findAppropriateGridLocationForBuilding(uint gridX, uint gridY) + { + this->visitCount = 1; + this->algAIndex = 0; + this->algBIndex = 1; + this->heatMapXArray[0] = gridX; + this->heatMapYArray[0] = gridY; + this->buildingApproriateGridXPosition = -1; + this->buildingAppropriateGridYPosition = -1; + if (gridX > 79 || gridY > 79 || this->heatMaps[gridX][gridY].isNotNearEdgeUnk) { + return; + } + ++this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount; + + if (this->algAIndex == this->algBIndex) { + return; + } + uint candidateX; + uint candidateY; + for (;;) { + int const x = this->heatMapXArray[this->algAIndex]; + int const y = this->heatMapYArray[this->algAIndex]; + int freeCount = 0; + if (!this->heatMaps[x][y].tilesNotPartOfLargestAreaCount) { + freeCount = 1; + } + this->visitCount = (char)this->heatMaps[x][y].algorithmVisitCountUnk; + for (int direction = 0; direction < 8; ++direction) { + candidateX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + candidateY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + if (this->heatMaps[x][y].isNotNearEdgeUnk && (candidateX > 79 || candidateY > 79)) { + continue; + } + int const notInLargestArea = (char)this->heatMaps[candidateX][candidateY].tilesNotPartOfLargestAreaCount; + if (!this->heatMaps[candidateX][candidateY].terrainDiffHigherThan12 + && !this->heatMaps[candidateX][candidateY].structureCount + && !this->heatMaps[candidateX][candidateY].isNotNearEdgeUnk + && !this->heatMaps[candidateX][candidateY].destructionBasedPlacementCooldown + && !this->heatMaps[candidateX][candidateY].treeCount + && (char)this->heatMaps[candidateX][candidateY].bouldersCount < 0 && !notInLargestArea) { + ++freeCount; + } + if (this->heatMaps[candidateX][candidateY].algorithmIterationNumber + == this->mapExtraInfo.algorithmIterationNumber) { + continue; + } + this->heatMaps[candidateX][candidateY].algorithmIterationNumber + = this->mapExtraInfo.algorithmIterationNumber; + if (notInLargestArea >= 15) { + continue; + } + this->heatMaps[candidateX][candidateY].algorithmVisitCountUnk = this->visitCount + 1; + this->heatMapXArray[this->algBIndex] = candidateX; + this->heatMapYArray[this->algBIndex] = candidateY; + ++this->algBIndex; + if ((int)this->algBIndex >= 6400) { + this->algBIndex = 0; + } + } + if (freeCount == 9) { + break; + } + ++this->algAIndex; + if ((int)this->algAIndex >= 6400) { + this->algAIndex = 0; + } + if (this->algAIndex == this->algBIndex) { + return; + } + } + // the result is the last visited neighbour + this->buildingAppropriateGridYPosition = candidateY; + this->buildingApproriateGridXPosition = candidateX; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index b5c8ecd0f..56090a8cc 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37955,7 +37955,7 @@ SHC_3BB0A8C1_0x004EEBD0 | 0.0% | Pending SHC_3BB0A8C1_0x004EECC0 | 0.0% | Pending -SHC_3BB0A8C1_0x004EEE10 | 0.0% | Pending +SHC_3BB0A8C1_0x004EEE10 | 86.7% | Register allocation SHC_3BB0A8C1_0x004EEEC0 | 0.0% | Pending From 03eef1409ba5a26b49cc7a23c7b6fda8595d660e Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:00:17 +0200 Subject: [PATCH 20/85] reimplement: SHC_3BB0A8C1_0x004EEBD0 94.57% Register allocation --- ...indSuitableGridLocationFittingCriteria.cpp | 74 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp diff --git a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp new file mode 100644 index 000000000..72a1f5ce9 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp @@ -0,0 +1,74 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + // breadth first search for the closest free grid cell + // FUNCTION: STRONGHOLDCRUSADER 0x004EEBD0 + void AIVState::findSuitableGridLocationFittingCriteria(uint gridX, uint gridY) + { + this->visitCount = 1; + this->algAIndex = 0; + this->algBIndex = 1; + this->heatMapXArray[0] = gridX; + this->heatMapYArray[0] = gridY; + this->buildingApproriateGridXPosition = -1; + this->buildingAppropriateGridYPosition = -1; + if (gridX >= 79 || gridY >= 79 || this->heatMaps[gridX][gridY].isNotNearEdgeUnk) { + return; + } + ++this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount; + + while (this->algAIndex != this->algBIndex) { + int const x = this->heatMapXArray[this->algAIndex]; + int const y = this->heatMapYArray[this->algAIndex]; + this->visitCount = (char)this->heatMaps[x][y].algorithmVisitCountUnk; + for (int direction = 0; direction < 8; ++direction) { + uint const candidateX + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const candidateY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + if (this->heatMaps[x][y].isNotNearEdgeUnk && (candidateX >= 79 || candidateY >= 79)) { + continue; + } + int const notInLargestArea = (char)this->heatMaps[candidateX][candidateY].tilesNotPartOfLargestAreaCount; + if (this->heatMaps[candidateX][candidateY].algorithmIterationNumber + == this->mapExtraInfo.algorithmIterationNumber) { + continue; + } + this->heatMaps[candidateX][candidateY].algorithmIterationNumber + = this->mapExtraInfo.algorithmIterationNumber; + if (notInLargestArea >= 15) { + continue; + } + if (!this->heatMaps[candidateX][candidateY].terrainDiffHigherThan12 + && !this->heatMaps[candidateX][candidateY].structureCount + && !this->heatMaps[candidateX][candidateY].isNotNearEdgeUnk + && !this->heatMaps[candidateX][candidateY].destructionBasedPlacementCooldown + && !this->heatMaps[candidateX][candidateY].treeCount + && (char)this->heatMaps[candidateX][candidateY].bouldersCount < 0 && !notInLargestArea) { + this->buildingApproriateGridXPosition = candidateX; + this->buildingAppropriateGridYPosition = candidateY; + return; + } + this->heatMaps[candidateX][candidateY].algorithmVisitCountUnk = this->visitCount + 1; + this->heatMapXArray[this->algBIndex] = candidateX; + this->heatMapYArray[this->algBIndex] = candidateY; + ++this->algBIndex; + if (this->algBIndex > 6400) { + this->algBIndex = 0; + } + } + ++this->algAIndex; + if (this->algAIndex > 6400) { + this->algAIndex = 0; + } + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 56090a8cc..5d6dc5a7e 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37951,7 +37951,7 @@ SHC_3BB0A8C1_0x004EE830 | 0.0% | Pending SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending -SHC_3BB0A8C1_0x004EEBD0 | 0.0% | Pending +SHC_3BB0A8C1_0x004EEBD0 | 94.6% | Register allocation SHC_3BB0A8C1_0x004EECC0 | 0.0% | Pending From 3a0a11aadc6bcdd5d2d700205365a59f6bf1da72 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:02:44 +0200 Subject: [PATCH 21/85] reimplement: SHC_3BB0A8C1_0x004EE140 59.30% Register alloc; 79% normalized --- .../findAppropriateWoodCutterGridLocation.cpp | 100 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp diff --git a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp new file mode 100644 index 000000000..3925139ff --- /dev/null +++ b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp @@ -0,0 +1,100 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + // breadth first search from the campground for the grid cell with the best tree score + // FUNCTION: STRONGHOLDCRUSADER 0x004EE140 + void AIVState::findAppropriateWoodCutterGridLocation(int playerID) + { + this->algAIndex = 0; + int foundCount = 0; + this->visitCount = 1; + this->algBIndex = 1; + int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; + int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; + this->buildingApproriateGridXPosition = -1; + this->buildingAppropriateGridYPosition = -1; + int bestScore = -100; + uint bestX = -1; + uint bestY = -1; + this->heatMapXArray[0] = campX; + this->heatMapYArray[0] = campY; + if (DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk > 0) { + --DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk; + return; + } + ++this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[campX][campY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[campX][campY].algorithmVisitCountUnk = this->visitCount; + + while (this->algAIndex != this->algBIndex) { + int const x = this->heatMapXArray[this->algAIndex]; + int const y = this->heatMapYArray[this->algAIndex]; + this->visitCount = (char)this->heatMaps[x][y].algorithmVisitCountUnk; + if (this->visitCount > 40) { + break; + } + // only the four cardinal directions + for (int direction = 0; direction < 8; direction += 2) { + uint const gridY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { + continue; + } + if (this->heatMaps[gridX][gridY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { + continue; + } + this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + int const notInLargestArea = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; + if (notInLargestArea >= 16 || this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown) { + continue; + } + int const treeCount = (char)this->heatMaps[gridX][gridY].treeCount; + if (notInLargestArea < 6 && treeCount > 0) { + ++foundCount; + int score = treeCount * 5 - this->visitCount * 3; + // prefer cells that were not used for a woodcutter recently + if (this->heatMaps[gridX][gridY].woodRelatedCountdownTo0) { + if (score <= 0) { + score *= 2; + } else { + score /= 2; + } + } + if (score > bestScore) { + bestScore = score; + bestX = gridX; + bestY = gridY; + } + if (foundCount > 10 || (foundCount > 5 && this->visitCount > 20) + || (foundCount > 0 && this->visitCount > 30)) { + break; + } + } + this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount + 1; + this->heatMapXArray[this->algBIndex] = gridX; + this->heatMapYArray[this->algBIndex] = gridY; + ++this->algBIndex; + if ((int)this->algBIndex >= 6400) { + this->algBIndex = 0; + } + } + ++this->algAIndex; + if ((int)this->algAIndex >= 6400) { + this->algAIndex = 0; + } + } + if (foundCount > 0) { + this->buildingApproriateGridXPosition = bestX; + this->buildingAppropriateGridYPosition = bestY; + return; + } + DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk = 5; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 5d6dc5a7e..8329b7087 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37941,7 +37941,7 @@ SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented SHC_3BB0A8C1_0x004EDDF0 | 0.0% | Pending -SHC_3BB0A8C1_0x004EE140 | 0.0% | Pending +SHC_3BB0A8C1_0x004EE140 | 59.3% | Register alloc; 79% normalized SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending From 8b5b0d969bc54b285ac25d1b1b9fc354e297750f Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:05:38 +0200 Subject: [PATCH 22/85] reimplement: SHC_3BB0A8C1_0x004EE430 49.90% Register alloc; 84% normalized --- ...ateGridLocationForResourceTypeBuilding.cpp | 149 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp diff --git a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp new file mode 100644 index 000000000..7a9d484b9 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp @@ -0,0 +1,149 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + // breadth first search from the campground for a grid cell with enough of the given resource + // FUNCTION: STRONGHOLDCRUSADER 0x004EE430 + void AIVState::findAppropriateGridLocationForResourceTypeBuilding( + int playerID, AIGridComputationResourceType resourceType) + { + this->visitCount = 1; + this->algAIndex = 0; + this->algBIndex = 1; + this->buildingApproriateGridXPosition = -1; + this->buildingAppropriateGridYPosition = -1; + if (resourceType == AIGCRT_OIL) { + if (DAT_GameState::instance.playerDataArray[playerID].availableOilGridTiles + <= DAT_GameState::instance.playerDataArray[playerID].countPitchRigs) { + this->visitCount = 1; + this->algAIndex = 0; + this->algBIndex = 1; + this->buildingApproriateGridXPosition = -1; + this->buildingAppropriateGridYPosition = -1; + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].algoOilCooldown > 0) { + --DAT_GameState::instance.playerDataArray[playerID].algoOilCooldown; + return; + } + } else if (resourceType == AIGCRT_IRON) { + if (DAT_GameState::instance.playerDataArray[playerID].availableIronGridTiles + <= DAT_GameState::instance.playerDataArray[playerID].countIronMines) { + this->visitCount = 1; + this->algAIndex = 0; + this->algBIndex = 1; + this->buildingApproriateGridXPosition = -1; + this->buildingAppropriateGridYPosition = -1; + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].algoIronCooldown > 0) { + --DAT_GameState::instance.playerDataArray[playerID].algoIronCooldown; + return; + } + } else if (resourceType == AIGCRT_BOULDER) { + if (DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles + <= DAT_GameState::instance.playerDataArray[playerID].countStoneQuarries) { + this->visitCount = 1; + this->algAIndex = 0; + this->algBIndex = 1; + this->buildingApproriateGridXPosition = -1; + this->buildingAppropriateGridYPosition = -1; + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].algoBoulderCooldown > 0) { + --DAT_GameState::instance.playerDataArray[playerID].algoBoulderCooldown; + return; + } + } + + int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; + ++this->mapExtraInfo.algorithmIterationNumber; + int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; + this->heatMapXArray[0] = campX; + this->heatMapYArray[0] = campY; + this->heatMaps[campX][campY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[campX][campY].algorithmVisitCountUnk = this->visitCount; + + while (this->algAIndex != this->algBIndex) { + int const x = this->heatMapXArray[this->algAIndex]; + int const y = this->heatMapYArray[this->algAIndex]; + this->visitCount = (char)this->heatMaps[x][y].algorithmVisitCountUnk; + if (this->visitCount > 40) { + break; + } + // only the four cardinal directions + for (int direction = 0; direction < 8; direction += 2) { + uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const gridY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { + continue; + } + if (this->heatMaps[gridX][gridY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { + continue; + } + this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + int const blockedCount = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount - (char)this->heatMaps[gridX][gridY].impassableCount; + if (blockedCount >= 16) { + continue; + } + int const owner = (char)this->heatMaps[gridX][gridY].tileOwnership; + if ((blockedCount == 0 || resourceType == AIGCRT_IRON) && blockedCount <= 4 && !this->heatMaps[gridX][gridY].structureCount + && !this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown + && (owner == 0 + || DAT_GameState::instance.mapAndTime.playerTeams[playerID] + == DAT_GameState::instance.mapAndTime.playerTeams[owner])) { + if (resourceType == AIGCRT_OIL) { + if ((char)this->heatMaps[gridX][gridY].oilCount > 2 && (char)this->heatMaps[gridX][gridY].marshCount >= 10 + && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + this->buildingApproriateGridXPosition = gridX; + this->buildingAppropriateGridYPosition = gridY; + return; + } + } else if (resourceType == AIGCRT_IRON) { + if ((char)this->heatMaps[gridX][gridY].ironCount > 6 && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + this->buildingApproriateGridXPosition = gridX; + this->buildingAppropriateGridYPosition = gridY; + return; + } + } else if (resourceType == AIGCRT_BOULDER) { + if ((char)this->heatMaps[gridX][gridY].bouldersCount >= 8 && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 40) { + this->buildingApproriateGridXPosition = gridX; + this->buildingAppropriateGridYPosition = gridY; + return; + } + } + } + this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount + 1; + this->heatMapXArray[this->algBIndex] = gridX; + this->heatMapYArray[this->algBIndex] = gridY; + ++this->algBIndex; + if ((int)this->algBIndex >= 6400) { + this->algBIndex = 0; + } + } + ++this->algAIndex; + if ((int)this->algAIndex >= 6400) { + this->algAIndex = 0; + } + } + + // nothing found, wait before searching again + if (resourceType == AIGCRT_OIL) { + DAT_GameState::instance.playerDataArray[playerID].algoOilCooldown = 5; + return; + } + if (resourceType == AIGCRT_IRON) { + DAT_GameState::instance.playerDataArray[playerID].algoIronCooldown = 5; + return; + } + if (resourceType == AIGCRT_BOULDER) { + DAT_GameState::instance.playerDataArray[playerID].algoBoulderCooldown = 5; + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 8329b7087..d1fc138c4 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37945,7 +37945,7 @@ SHC_3BB0A8C1_0x004EE140 | 59.3% | Register alloc; 79% normalized SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending -SHC_3BB0A8C1_0x004EE430 | 0.0% | Pending +SHC_3BB0A8C1_0x004EE430 | 49.9% | Register alloc; 84% normalized SHC_3BB0A8C1_0x004EE830 | 0.0% | Pending From b0cd6da55797b29fc7108d8b37d0851de01c1605 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:08:42 +0200 Subject: [PATCH 23/85] reimplement: SHC_3BB0A8C1_0x004EFB90 97.73% Reimplemented --- .../AIVState/findSpotNearEngineersGuild.cpp | 30 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/findSpotNearEngineersGuild.cpp diff --git a/src/OpenSHC/AI/AIVState/findSpotNearEngineersGuild.cpp b/src/OpenSHC/AI/AIVState/findSpotNearEngineersGuild.cpp new file mode 100644 index 000000000..e59f92932 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/findSpotNearEngineersGuild.cpp @@ -0,0 +1,30 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EFB90 + BOOLEnum AIVState::findSpotNearEngineersGuild(int playerID) + { + int const guildID = DAT_GameState::instance.playerDataArray[playerID].engineersGuild.id; + if (guildID <= 0) { + return FALSE; + } + if (DAT_BuildingsState::instance.buildings[guildID].buildingType != Map::Buildings::BT_ENGINEERSGUILD) { + return FALSE; + } + MACRO_CALL_MEMBER(AIVState_Func::findSuitableGridLocationFittingCriteria, this)( + (short)DAT_BuildingsState::instance.buildings[guildID].x / 5, + (short)DAT_BuildingsState::instance.buildings[guildID].y / 5); + if (this->buildingApproriateGridXPosition == -1) { + return FALSE; + } + ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; + return TRUE; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index d1fc138c4..44330eb0b 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37981,7 +37981,7 @@ SHC_3BB0A8C1_0x004EFA60 | 91.9% | Reimplemented SHC_3BB0A8C1_0x004EFAB0 | 82.8% | Register reuse after search -SHC_3BB0A8C1_0x004EFB90 | 0.0% | Pending +SHC_3BB0A8C1_0x004EFB90 | 97.7% | Reimplemented SHC_3BB0A8C1_0x004EFC30 | 96.0% | Reimplemented From 04e9a9709f5704bec5acbb8800acb02b6f881cc6 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:08:43 +0200 Subject: [PATCH 24/85] reimplement: SHC_3BB0A8C1_0x004EDDF0 83.01% Loop pointer anchor, registers --- .../findSuitableFarmLocationAndPlaceFarm.cpp | 109 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp diff --git a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp new file mode 100644 index 000000000..b3443ebc9 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp @@ -0,0 +1,109 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace AI { + + // breadth first search from the campground for an oasis grid cell and places the farm there + // FUNCTION: STRONGHOLDCRUSADER 0x004EDDF0 + int AIVState::findSuitableFarmLocationAndPlaceFarm(PlayerID playerID, MappersEnum farmType) + { + if (DAT_BuildingsState::instance.unknownCountdown01 <= 20 + || DAT_GameState::instance.playerDataArray[playerID].countFarms + >= DAT_GameState::instance.playerDataArray[playerID].availableOasisGridTiles) { + return 0; + } + if (DAT_GameState::instance.playerDataArray[playerID].algoOasisCooldownUnk > 0) { + --DAT_GameState::instance.playerDataArray[playerID].algoOasisCooldownUnk; + return 0; + } + + this->visitCount = 1; + this->algAIndex = 0; + this->algBIndex = 1; + // despite its declared type, farmType holds the BuildingType of the farm + MappersEnum mapper = farmType; + if (farmType == Map::Buildings::BT_WHEATFARM) { + mapper = Commands::M_MAPPER_WHEATFARM; + } else if (farmType == Map::Buildings::BT_HOPFARM) { + mapper = Commands::M_MAPPER_HOPSFARM; + } else if (farmType == Map::Buildings::BT_APPLEFARM) { + mapper = Commands::M_MAPPER_APPLEFARM; + } else if (farmType == Map::Buildings::BT_DAIRYFARM) { + mapper = Commands::M_MAPPER_CATTLEFARM; + } + int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; + ++this->mapExtraInfo.algorithmIterationNumber; + int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; + this->heatMapXArray[0] = campX; + this->heatMapYArray[0] = campY; + this->heatMaps[campX][campY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[campX][campY].algorithmVisitCountUnk = this->visitCount; + + while (this->algAIndex != this->algBIndex) { + int const x = this->heatMapXArray[this->algAIndex]; + int const y = this->heatMapYArray[this->algAIndex]; + this->visitCount = (char)this->heatMaps[x][y].algorithmVisitCountUnk; + if (this->visitCount > 40) { + break; + } + // only the four cardinal directions + for (int direction = 0; direction < 8; direction += 2) { + uint const gridY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { + continue; + } + if (this->heatMaps[gridX][gridY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { + continue; + } + int const notInLargestArea = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; + this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + if (notInLargestArea >= 16) { + continue; + } + this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount + 1; + this->heatMapXArray[this->algBIndex] = gridX; + this->heatMapYArray[this->algBIndex] = gridY; + ++this->algBIndex; + if (this->algBIndex > 6400) { + this->algBIndex = 0; + } + if (this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount || this->heatMaps[gridX][gridY].structureCount || this->heatMaps[gridX][gridY].treeCount + || this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown || (char)this->heatMaps[gridX][gridY].oasisScrubCount <= 25 + || (char)this->heatMaps[gridX][gridY].oasisThickScrubCount <= 14) { + continue; + } + int const xOffset + = DAT_AIVDefinedData::instance.TilesSelfAndRightThree[this->mapExtraInfo.algBuildingPlacementNudge].x; + int const yOffset + = DAT_AIVDefinedData::instance.TilesSelfAndRightThree[this->mapExtraInfo.algBuildingPlacementNudge].y; + ++this->counter; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, + xOffset + gridX * 5, yOffset + gridY * 5, mapper, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, + DAT_TileMapState::ptr)(mapper), + 15); + if (!DAT_TileMapState::instance.buildingPlacementFail) { + this->heatMaps[gridX][gridY].structureCount = 1; + return 1; + } + } + ++this->algAIndex; + if ((int)this->algAIndex >= 6400) { + this->algAIndex = 0; + } + } + DAT_GameState::instance.playerDataArray[playerID].algoOasisCooldownUnk = 5; + return 0; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 44330eb0b..e06b4dc95 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37939,7 +37939,7 @@ SHC_3BB0A8C1_0x004EDCB0 | 0.0% | Pending SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented -SHC_3BB0A8C1_0x004EDDF0 | 0.0% | Pending +SHC_3BB0A8C1_0x004EDDF0 | 83.0% | Loop pointer anchor, registers SHC_3BB0A8C1_0x004EE140 | 59.3% | Register alloc; 79% normalized From 71621a673b6590b7bbb2cb0e58e6cc60a40e3de1 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:08:43 +0200 Subject: [PATCH 25/85] reimplement: SHC_3BB0A8C1_0x004ED3B0 100.00% Reimplemented --- .../incrementStructureHeatMapTile.cpp | 19 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/incrementStructureHeatMapTile.cpp diff --git a/src/OpenSHC/AI/AIVState/incrementStructureHeatMapTile.cpp b/src/OpenSHC/AI/AIVState/incrementStructureHeatMapTile.cpp new file mode 100644 index 000000000..ab597b462 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/incrementStructureHeatMapTile.cpp @@ -0,0 +1,19 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + // records a structure in the 5x5 tile heat map cell of the given tile coordinates + // FUNCTION: STRONGHOLDCRUSADER 0x004ED3B0 + void AIVState::incrementStructureHeatMapTile(int x, int y) + { + if (DAT_GameSynchronyState::instance.currentGameMode == Game::GM_SOLITARY) { + return; + } + ++this->heatMaps[x / 5][y / 5].structureCount; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index e06b4dc95..638c9c6ff 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37929,7 +37929,7 @@ SHC_3BB0A8C1_0x004ED240 | 94.7% | Reimplemented SHC_3BB0A8C1_0x004ED300 | 96.2% | Reimplemented -SHC_3BB0A8C1_0x004ED3B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004ED3B0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ED410 | 85.3% | Stack slot assignment From 673f7e8e0a6c6ea598d1bf6b063ff2b6aba9c18f Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:14:02 +0200 Subject: [PATCH 26/85] reimplement: SHC_3BB0A8C1_0x004ECBC0 64.71% Register allocation --- .../hostChecksLobbyAIVAvailability.cpp | 118 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp diff --git a/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp b/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp new file mode 100644 index 000000000..1a0128c43 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp @@ -0,0 +1,118 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Synchrony/Actions.func.hpp" +#include "OpenSHC/Synchrony/GameSynchronyState.func.hpp" +#include "OpenSHC/UI/Helpers.func.hpp" +#include "OpenSHC/UI/MenuModalComposition.func.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_MenuModalComposition1.hpp" +#include "OpenSHC/Globals/DAT_WindowAndDirectDraw.hpp" + +namespace OpenSHC { +namespace AI { + + // lists the AIs with an AIV that every player has, and removes other AIs from the lobby + // FUNCTION: STRONGHOLDCRUSADER 0x004ECBC0 + void AIVState::hostChecksLobbyAIVAvailability() + { + if (DAT_GameSynchronyState::instance.currentGameMode != Game::GM_SOLITARY + && DAT_GameSynchronyState::instance.currentGameMode != Game::GM_SKIRMISH_SINGLE_PLAYER) { + if (!DAT_GameSynchronyState::instance.isHost) { + return; + } + int const localPlayer = DAT_GameSynchronyState::instance.currentPlayerSlotID; + DAT_GameCore::instance.numOfAIsWithCastleUnk = 0; + for (int aiIndex = 0; aiIndex < 16; ++aiIndex) { + bool available = true; + for (int player = 1; player < 9; ++player) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1) { + continue; + } + int count; + if (DAT_GameSynchronyState::instance.DAT_ReceivedAIVFileAvailabilityPerAIArray[player][0] == -1) { + // nothing received yet + if (player != localPlayer) { + continue; + } + count = this->aivFileAvailabilityPerAIArray[aiIndex]; + } else if (player == localPlayer) { + count = this->aivFileAvailabilityPerAIArray[aiIndex]; + } else { + count = DAT_GameSynchronyState::instance.DAT_ReceivedAIVFileAvailabilityPerAIArray[player][aiIndex]; + } + if (count == 0) { + available = false; + break; + } + } + if (available) { + DAT_GameCore::instance.arrayOfLordIdsWithAIVsUnk[DAT_GameCore::instance.numOfAIsWithCastleUnk] + = aiIndex + 1; + ++DAT_GameCore::instance.numOfAIsWithCastleUnk; + } + } + + BOOLEnum removedPlayer = FALSE; + for (int player = 1; player < 9; ++player) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] != -1 + || DAT_GameSynchronyState::instance.currentAIArray[player] == 0) { + continue; + } + bool hasAIV = false; + for (int i = 0; i < DAT_GameCore::instance.numOfAIsWithCastleUnk; ++i) { + if (DAT_GameSynchronyState::instance.currentAIArray[player] + == DAT_GameCore::instance.arrayOfLordIdsWithAIVsUnk[i]) { + hasAIV = true; + break; + } + } + if (hasAIV) { + continue; + } + DAT_GameSynchronyState::instance.currentAIArray[player] = 0; + MACRO_CALL(Synchrony::Actions_Func::RemovePositionOfPlayer)(player); + removedPlayer = TRUE; + DAT_GameSynchronyState::instance.DAT_GameCommandParam0 = player; + MACRO_CALL_MEMBER(Synchrony::GameSynchronyState_Func::queueCommand, DAT_GameSynchronyState::ptr)( + Commands::GCT_HOST_REMOVE_PLAYER_BY_SLOT); + } + if (removedPlayer) { + MACRO_CALL_MEMBER(Synchrony::GameSynchronyState_Func::reorderTeamsAndPositions, DAT_GameSynchronyState::ptr)(); + DAT_GameSynchronyState::instance.reparseMaps = TRUE; + } + + // switch between the small and the extended AI selection + if (DAT_MenuModalComposition1::instance.activeModalDialogID == UI::Enums::MMT_BASIC_AI_LORD_SELECT) { + if (DAT_GameCore::instance.numOfAIsWithCastleUnk > 8) { + MACRO_CALL(UI::Helpers_Func::ClearLobbyHoveredAI)(); + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::setExtraActiveModalDialog, + DAT_MenuModalComposition1::ptr)(UI::Enums::MMT_EXTENDED_AI_LORD_SELECT, + DAT_WindowAndDirectDraw::instance.mainMenuBorderWidth + 4, + DAT_WindowAndDirectDraw::instance.mainMenuBorderHeight + 205); + } + } else if (DAT_MenuModalComposition1::instance.activeModalDialogID == UI::Enums::MMT_EXTENDED_AI_LORD_SELECT + && DAT_GameCore::instance.numOfAIsWithCastleUnk <= 8) { + MACRO_CALL(UI::Helpers_Func::ClearLobbyHoveredAI)(); + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::setExtraActiveModalDialog, + DAT_MenuModalComposition1::ptr)(UI::Enums::MMT_BASIC_AI_LORD_SELECT, + DAT_WindowAndDirectDraw::instance.mainMenuBorderWidth + 4, + DAT_WindowAndDirectDraw::instance.mainMenuBorderHeight + 205); + } + return; + } + + // single player + DAT_GameCore::instance.numOfAIsWithCastleUnk = this->numberOfAIsWithAIV; + int count = 0; + for (int aiIndex = 0; aiIndex < 16; ++aiIndex) { + if (this->aivFileAvailabilityPerAIArray[aiIndex] != 0) { + DAT_GameCore::instance.arrayOfLordIdsWithAIVsUnk[count] = aiIndex + 1; + ++count; + } + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 638c9c6ff..7f0a2a9e1 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37873,7 +37873,7 @@ SHC_3BB0A8C1_0x004ECAB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECAD0 | 0.0% | Pending -SHC_3BB0A8C1_0x004ECBC0 | 0.0% | Pending +SHC_3BB0A8C1_0x004ECBC0 | 64.7% | Register allocation SHC_3BB0A8C1_0x004ECCA0 | 0.0% | Pending From 63812f56a7983b3f856af46f50089b45c1449077 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:14:03 +0200 Subject: [PATCH 27/85] reimplement: SHC_3BB0A8C1_0x004ECDB0 93.20% Header has extra int param --- .../setAIVFilePresenceByFileHashArray.cpp | 48 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/setAIVFilePresenceByFileHashArray.cpp diff --git a/src/OpenSHC/AI/AIVState/setAIVFilePresenceByFileHashArray.cpp b/src/OpenSHC/AI/AIVState/setAIVFilePresenceByFileHashArray.cpp new file mode 100644 index 000000000..ea1f6a0b3 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/setAIVFilePresenceByFileHashArray.cpp @@ -0,0 +1,48 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + // an AIV is only usable in multiplayer if all players have the same file + // FUNCTION: STRONGHOLDCRUSADER 0x004ECDB0 + void AIVState::setAIVFilePresenceByFileHashArray(int unused) + { + if (DAT_GameSynchronyState::instance.currentGameMode == Game::GM_SKIRMISH_SINGLE_PLAYER) { + for (int aiIndex = 0; aiIndex < 16; ++aiIndex) { + for (int castleID = 0; castleID < 8; ++castleID) { + this->aivFilePresent[aiIndex][castleID] = 1; + } + } + return; + } + for (int aiIndex = 0; aiIndex < 16; ++aiIndex) { + for (int castleID = 0; castleID < 8; ++castleID) { + int hash = -1; + this->aivFilePresent[aiIndex][castleID] = 1; + for (int player = 1; player < 9; ++player) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1) { + continue; + } + // the received hashes use the same layout as aivFileHash + if (hash == -1) { + hash = ((int*)this->receivedAIVFileHash[player])[aiIndex * 8 + castleID]; + if (hash == -1) { + this->aivFilePresent[aiIndex][castleID] = 0; + break; + } + } else if (hash != ((int*)this->receivedAIVFileHash[player])[aiIndex * 8 + castleID]) { + this->aivFilePresent[aiIndex][castleID] = 0; + break; + } + } + if (hash == -1) { + this->aivFilePresent[aiIndex][castleID] = 0; + } + } + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 7f0a2a9e1..8e17ff399 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37879,7 +37879,7 @@ SHC_3BB0A8C1_0x004ECCA0 | 0.0% | Pending SHC_3BB0A8C1_0x004ECD90 | 0.0% | Pending -SHC_3BB0A8C1_0x004ECDB0 | 0.0% | Pending +SHC_3BB0A8C1_0x004ECDB0 | 93.2% | Header has extra int param SHC_3BB0A8C1_0x004ECDE0 | 0.0% | Pending From 5814d6e4afb8eb1baa72ac52d1ea2e2aa1d568de Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:14:03 +0200 Subject: [PATCH 28/85] reimplement: SHC_3BB0A8C1_0x004ECAD0 98.59% Reimplemented --- src/OpenSHC/AI/AIVState/setAvailableAIV.cpp | 40 +++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/setAvailableAIV.cpp diff --git a/src/OpenSHC/AI/AIVState/setAvailableAIV.cpp b/src/OpenSHC/AI/AIVState/setAvailableAIV.cpp new file mode 100644 index 000000000..8efa8cb7b --- /dev/null +++ b/src/OpenSHC/AI/AIVState/setAvailableAIV.cpp @@ -0,0 +1,40 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/IO/ResourceManager.func.hpp" + +#include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_ResourceManager.hpp" + +namespace OpenSHC { +namespace AI { + + // hashes all AIV files and lists the AIs that have at least one AIV + // FUNCTION: STRONGHOLDCRUSADER 0x004ECAD0 + void AIVState::setAvailableAIV() + { + for (int i = 0; i < 20; ++i) { + this->aivFileAvailabilityPerAIArray[i] = 0; + } + this->numberOfAIsWithAIV = 0; + for (int aiIndex = 0; aiIndex < 16; ++aiIndex) { + int availableAIVs = 0; + for (int castleID = 0; castleID < 8; ++castleID) { + int const hash = MACRO_CALL_MEMBER(IO::ResourceManager_Func::getSimpleFirst1024ByteSumOfFile, + DAT_ResourceManager::ptr)(DAT_AIVDefinedData::instance.AIVFileNames[aiIndex][castleID]); + this->aivFileHash[aiIndex][castleID] = hash; + if (hash != -1) { + ++availableAIVs; + } + } + if (availableAIVs != 0) { + this->aivFileAvailabilityPerAIArray[aiIndex] = availableAIVs; + DAT_GameCore::instance.arrayOfLordIdsWithAIVsUnk[this->numberOfAIsWithAIV] = aiIndex + 1; + ++this->numberOfAIsWithAIV; + } + } + DAT_GameCore::instance.numOfAIsWithCastleUnk = this->numberOfAIsWithAIV; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 8e17ff399..eafd07892 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37871,7 +37871,7 @@ SHC_3BB0A8C1_0x004ECA90 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECAB0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004ECAD0 | 0.0% | Pending +SHC_3BB0A8C1_0x004ECAD0 | 98.6% | Reimplemented SHC_3BB0A8C1_0x004ECBC0 | 64.7% | Register allocation From f30c105f7f6b86e837ae87717a8c8b96ac51abef Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:16:03 +0200 Subject: [PATCH 29/85] reimplement: SHC_3BB0A8C1_0x004EDCB0 100.00% Reimplemented --- .../AI/AIVState/resetCountdownInHeatMap.cpp | 14 ++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/resetCountdownInHeatMap.cpp diff --git a/src/OpenSHC/AI/AIVState/resetCountdownInHeatMap.cpp b/src/OpenSHC/AI/AIVState/resetCountdownInHeatMap.cpp new file mode 100644 index 000000000..69f26e8a1 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/resetCountdownInHeatMap.cpp @@ -0,0 +1,14 @@ +#include "../AIVState.func.hpp" + +namespace OpenSHC { +namespace AI { + + // blocks building on the 5x5 heat map cell of the given tile for a while, e.g. after a building got destroyed + // FUNCTION: STRONGHOLDCRUSADER 0x004EDCB0 + void AIVState::resetCountdownInHeatMap(int x, int y) + { + this->heatMaps[x / 5][y / 5].destructionBasedPlacementCooldown = 10; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index eafd07892..4fcd8d826 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37935,7 +37935,7 @@ SHC_3BB0A8C1_0x004ED410 | 85.3% | Stack slot assignment SHC_3BB0A8C1_0x004EDC90 | 85.7% | Reimplemented -SHC_3BB0A8C1_0x004EDCB0 | 0.0% | Pending +SHC_3BB0A8C1_0x004EDCB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented From 4548b8b25ff004d892aa42283530189ee92dd77f Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:16:03 +0200 Subject: [PATCH 30/85] reimplement: SHC_3BB0A8C1_0x004ECF70 87.10% Reimplemented --- .../AIVState/setKeepOffsetAndOrientation.cpp | 32 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp diff --git a/src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp b/src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp new file mode 100644 index 000000000..0ee867452 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp @@ -0,0 +1,32 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ECF70 + void AIVState::setKeepOffsetAndOrientation(int aivID, int keepX, int keepY) + { + // the AIV is 100x100 tiles with the keep at 43/43 + this->aivs[aivID].keepXOffset = keepX - 43; + this->aivs[aivID].keepY = keepY; + this->aivs[aivID].keepX = keepX; + this->aivs[aivID].keepYOffset = keepY - 43; + // orient the castle towards the map center + MACRO_CALL_MEMBER(Map::Navigation::DirectionAlgorithmState_Func::calculatePreferredRelativeOrientation, + DAT_DirectionAlgorithmState::ptr)(keepX, keepY, 200, 200); + this->aivs[aivID].keepOrientation = DAT_DirectionAlgorithmState::instance.orientation & 0xfffe; + if (this->aivs[aivID].keepOrientation == 6) { + this->aivs[aivID].keepOrientation = 2; + return; + } + if (this->aivs[aivID].keepOrientation == 2) { + this->aivs[aivID].keepOrientation = 6; + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 4fcd8d826..7b3283adf 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37889,7 +37889,7 @@ SHC_3BB0A8C1_0x004ECE60 | 0.0% | Pending SHC_3BB0A8C1_0x004ECEF0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004ECF70 | 0.0% | Pending +SHC_3BB0A8C1_0x004ECF70 | 87.1% | Reimplemented SHC_3BB0A8C1_0x004ECFE0 | 100.0% | Reimplemented From 2a1b10b59e71dec0732a10a291a98ef8096dcbba Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:16:03 +0200 Subject: [PATCH 31/85] reimplement: SHC_3BB0A8C1_0x004F14F0 91.55% Reimplemented --- .../tryPlaceAIVAndReturnFitPercentage.cpp | 43 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp diff --git a/src/OpenSHC/AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp b/src/OpenSHC/AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp new file mode 100644 index 000000000..753e15298 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp @@ -0,0 +1,43 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/IO/FilePackager.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" + +#include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/FilePackagerObj.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004F14F0 + int AIVState::tryPlaceAIVAndReturnFitPercentage(int aivID, int castleID) + { + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(this->isHandled), 0, this->isHandled); + if (!MACRO_CALL_MEMBER(IO::FilePackager_Func::unpackAIVFile, FilePackagerObj::ptr)( + DAT_AIVDefinedData::instance.AIVSectionAddressArray, + DAT_AIVDefinedData::instance.AIVFileNames[this->aivs[aivID].aiType - 2][castleID])) { + return -3; + } + MACRO_CALL_MEMBER(AIVState_Func::rotateAIV, this)(this->aivs[aivID].keepOrientation); + int const fit = MACRO_CALL_MEMBER(AIVState_Func::computeAIVPlacementFit, this)(aivID); + if (fit <= 0) { + return -2; + } + this->aivs[aivID].castleID = castleID; + if (fit == 999999) { + // perfect fit + this->aivs[aivID].aivSubType = 2; + return 100; + } + this->aivs[aivID].aivSubType = 1; + // the counters are declared unsigned but used as signed values + if ((int)this->buildingCounter == 0) { + return 100; + } + return ((int)this->buildingCounter - (int)this->invalidTileCounter) * 100 / (int)this->buildingCounter; + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 7b3283adf..ab5b432c1 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38003,7 +38003,7 @@ SHC_3BB0A8C1_0x004F13E0 | 83.3% | Reimplemented SHC_3BB0A8C1_0x004F13F0 | 0.0% | Pending -SHC_3BB0A8C1_0x004F14F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F14F0 | 91.5% | Reimplemented SHC_3BB0A8C1_0x004F15C0 | 63.1% | Register allocation From b80525c92aaa92ee1d1acf6469f09b97d1889c06 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:16:04 +0200 Subject: [PATCH 32/85] reimplement: SHC_3BB0A8C1_0x004EF090 90.00% Reimplemented --- .../AI/AIVState/wipeAIVsAndHeatMaps.cpp | 22 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/wipeAIVsAndHeatMaps.cpp diff --git a/src/OpenSHC/AI/AIVState/wipeAIVsAndHeatMaps.cpp b/src/OpenSHC/AI/AIVState/wipeAIVsAndHeatMaps.cpp new file mode 100644 index 000000000..195d5d825 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/wipeAIVsAndHeatMaps.cpp @@ -0,0 +1,22 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004EF090 + void AIVState::wipeAIVsAndHeatMaps() + { + this->aivCount = 0; + for (int aivID = 0; aivID < 9; ++aivID) { + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(this->aivs[aivID]), 0, &this->aivs[aivID]); + } + MACRO_CALL_MEMBER(AIVState_Func::clearTheHeatmaps, this)(); + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index ab5b432c1..7afd1a528 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37961,7 +37961,7 @@ SHC_3BB0A8C1_0x004EEEC0 | 0.0% | Pending SHC_3BB0A8C1_0x004EEF20 | 0.0% | Pending -SHC_3BB0A8C1_0x004EF090 | 0.0% | Pending +SHC_3BB0A8C1_0x004EF090 | 90.0% | Reimplemented SHC_3BB0A8C1_0x004EF0D0 | 36.9% | Stack slots; 77% normalized From 61270dd960e742759a01803553113881a6f059ed Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:19:23 +0200 Subject: [PATCH 33/85] reimplement: SHC_3BB0A8C1_0x004ED0B0 95.33% Reimplemented --- src/OpenSHC/AI/AIVState/rotateAIV.cpp | 46 +++++++++++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/rotateAIV.cpp diff --git a/src/OpenSHC/AI/AIVState/rotateAIV.cpp b/src/OpenSHC/AI/AIVState/rotateAIV.cpp new file mode 100644 index 000000000..61c3d3256 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/rotateAIV.cpp @@ -0,0 +1,46 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace AI { + + // rotates the loaded AIV grid to the keep orientation + // FUNCTION: STRONGHOLDCRUSADER 0x004ED0B0 + void AIVState::rotateAIV(int orientation) + { + if (orientation == 0) { + return; + } + if (orientation == 6) { + for (int y = 0; y < 100; ++y) { + for (int x = 0; x < 100; ++x) { + this->rotatedConstructions[x][99 - y] = this->constructions[y * 100 + x]; + this->rotatedSteps[x][99 - y] = this->steps[y][x]; + } + } + } else if (orientation == 4) { + for (int y = 0; y < 100; ++y) { + for (int x = 0; x < 100; ++x) { + this->rotatedConstructions[99 - y][99 - x] = this->constructions[y * 100 + x]; + this->rotatedSteps[99 - y][99 - x] = this->steps[y][x]; + } + } + } else if (orientation == 2) { + for (int y = 0; y < 100; ++y) { + for (int x = 0; x < 100; ++x) { + this->rotatedConstructions[99 - x][y] = this->constructions[y * 100 + x]; + this->rotatedSteps[99 - x][y] = this->steps[y][x]; + } + } + } + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::copyData, DAT_LowLevelMemory::ptr)( + sizeof(this->rotatedConstructions), this->rotatedConstructions, this->constructions); + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::copyData, DAT_LowLevelMemory::ptr)( + sizeof(this->rotatedSteps), this->rotatedSteps, this->steps); + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 7afd1a528..d3cbc2c4f 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37923,7 +37923,7 @@ SHC_3BB0A8C1_0x004ED0A4 | 0.0% | Pending SHC_3BB0A8C1_0x004ED0A8 | 0.0% | Pending -SHC_3BB0A8C1_0x004ED0B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004ED0B0 | 95.3% | Reimplemented SHC_3BB0A8C1_0x004ED240 | 94.7% | Reimplemented From 9ac9079bca02c2625586a82a2acbb26afcc04122 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:19:23 +0200 Subject: [PATCH 34/85] reimplement: SHC_3BB0A8C1_0x004F13F0 90.00% Reimplemented --- .../AIVState/selectBestAIVwithRandomStart.cpp | 62 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp diff --git a/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp b/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp new file mode 100644 index 000000000..8ed8ae1e8 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp @@ -0,0 +1,62 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/IO/FilePackager.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" + +#include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/FilePackagerObj.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + // starts at a random castle and picks the first one that fits perfectly, otherwise the best fitting one + // FUNCTION: STRONGHOLDCRUSADER 0x004F13F0 + void AIVState::selectBestAIVwithRandomStart(int aivID) + { + uint castleID = (byte)SEC_RNG::instance.currentNumber2 & 7; + int bestFit = 0; + int bestCastleID = -1; + MACRO_CALL_MEMBER(Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(this->isHandled), 0, this->isHandled); + for (int tries = 0; tries < 8; ++tries) { + ++castleID; + if ((int)castleID > 7) { + castleID = 0; + } + int const aiType = this->aivs[aivID].aiType; + if (!this->aivFilePresent[aiType - 2][castleID]) { + continue; + } + if (!MACRO_CALL_MEMBER(IO::FilePackager_Func::unpackAIVFile, FilePackagerObj::ptr)( + DAT_AIVDefinedData::instance.AIVSectionAddressArray, + DAT_AIVDefinedData::instance.AIVFileNames[aiType - 2][castleID])) { + continue; + } + MACRO_CALL_MEMBER(AIVState_Func::rotateAIV, this)(this->aivs[aivID].keepOrientation); + int const fit = MACRO_CALL_MEMBER(AIVState_Func::computeAIVPlacementFit, this)(aivID); + if (fit <= 0) { + continue; + } + if (fit == 999999) { + // the first perfectly fitting castle is taken + this->aivs[aivID].castleID = castleID; + this->aivs[aivID].aivSubType = 2; + return; + } + if (fit > bestFit) { + bestFit = fit; + bestCastleID = castleID; + } + } + if (bestCastleID >= 0) { + this->aivs[aivID].aivSubType = 1; + this->aivs[aivID].castleID = bestCastleID; + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index d3cbc2c4f..a714e0ed8 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38001,7 +38001,7 @@ SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending SHC_3BB0A8C1_0x004F13E0 | 83.3% | Reimplemented -SHC_3BB0A8C1_0x004F13F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004F13F0 | 90.0% | Reimplemented SHC_3BB0A8C1_0x004F14F0 | 91.5% | Reimplemented From 80df82d875309e538ff2879c7f7a9ade6d2d29a9 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:19:24 +0200 Subject: [PATCH 35/85] reimplement: SHC_3BB0A8C1_0x004ECE60 97.78% Reimplemented --- .../syncAIPlayerNamesAndBuildIntervals.cpp | 35 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/syncAIPlayerNamesAndBuildIntervals.cpp diff --git a/src/OpenSHC/AI/AIVState/syncAIPlayerNamesAndBuildIntervals.cpp b/src/OpenSHC/AI/AIVState/syncAIPlayerNamesAndBuildIntervals.cpp new file mode 100644 index 000000000..d5eb20c5d --- /dev/null +++ b/src/OpenSHC/AI/AIVState/syncAIPlayerNamesAndBuildIntervals.cpp @@ -0,0 +1,35 @@ +// disable deprecation warnings for strcpy +#pragma warning(disable : 4996) + +#include "../AIVState.func.hpp" + +#include "OpenSHC/AI/AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + // restores the names of the AI players from the skirmish results and refreshes the AIC build intervals + // FUNCTION: STRONGHOLDCRUSADER 0x004ECE60 + void AIVState::syncAIPlayerNamesAndBuildIntervals() + { + for (int playerID = 1; playerID < 9; ++playerID) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID] != -1 + || DAT_GameSynchronyState::instance.currentAIArray[playerID] == 0) { + continue; + } + strcpy(DAT_GameSynchronyState::instance.DAT_PlayerNames[playerID], + DAT_GameSynchronyState::instance.finalResults.names[playerID]); + int const aivID = DAT_GameState::instance.playerDataArray[playerID].aivID; + if (aivID != 0) { + this->aivs[aivID].aivPoorLimit_OR_AIC_buildInterval + = MACRO_CALL_MEMBER(AICState_Func::getAIBuildInterval, DAT_AICState::ptr)(playerID); + } + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index a714e0ed8..a51b073c5 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37885,7 +37885,7 @@ SHC_3BB0A8C1_0x004ECDE0 | 0.0% | Pending SHC_3BB0A8C1_0x004ECDF0 | 0.0% | Pending -SHC_3BB0A8C1_0x004ECE60 | 0.0% | Pending +SHC_3BB0A8C1_0x004ECE60 | 97.8% | Reimplemented SHC_3BB0A8C1_0x004ECEF0 | 100.0% | Reimplemented From 0025638430335ecbe96d4ef0b16a07ba465cfdb8 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:22:24 +0200 Subject: [PATCH 36/85] reimplement: SHC_3BB0A8C1_0x004EE830 72.82% Register alloc; 86% normalized --- .../recomputeAIAvailableGridTiles.cpp | 111 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp diff --git a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp new file mode 100644 index 000000000..52b649089 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp @@ -0,0 +1,111 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + // counts the grid cells around the campground that are usable for the resource buildings + // FUNCTION: STRONGHOLDCRUSADER 0x004EE830 + void AIVState::recomputeAIAvailableGridTiles(int playerID) + { + DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk = -1; + DAT_GameState::instance.playerDataArray[playerID].algoOasisCooldownUnk = -1; + DAT_GameState::instance.playerDataArray[playerID].algoBoulderCooldown = -1; + DAT_GameState::instance.playerDataArray[playerID].algoIronCooldown = -1; + DAT_GameState::instance.playerDataArray[playerID].algoOilCooldown = -1; + DAT_GameState::instance.playerDataArray[playerID].availableTreeGridTiles = 0; + DAT_GameState::instance.playerDataArray[playerID].availableOasisGridTiles = 0; + DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles = 0; + DAT_GameState::instance.playerDataArray[playerID].availableIronGridTiles = 0; + DAT_GameState::instance.playerDataArray[playerID].availableOilGridTiles = 0; + this->visitCount = 1; + this->algBIndex = 1; + this->algAIndex = 0; + int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; + ++this->mapExtraInfo.algorithmIterationNumber; + int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; + this->heatMapXArray[0] = campX; + this->heatMapYArray[0] = campY; + this->heatMaps[campX][campY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + this->heatMaps[campX][campY].algorithmVisitCountUnk = this->visitCount; + + while (this->algAIndex != this->algBIndex) { + int const x = this->heatMapXArray[this->algAIndex]; + int const y = this->heatMapYArray[this->algAIndex]; + this->visitCount = (char)this->heatMaps[x][y].algorithmVisitCountUnk; + if (this->visitCount > 40) { + break; + } + // only the four cardinal directions + for (int direction = 0; direction < 8; direction += 2) { + uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const gridY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { + continue; + } + if (this->heatMaps[gridX][gridY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { + continue; + } + int const impassable = (char)this->heatMaps[gridX][gridY].impassableCount; + this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; + int const notInLargestArea = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; + int const blockedCount = notInLargestArea - impassable; + if (blockedCount >= 16) { + continue; + } + if ((char)this->heatMaps[gridX][gridY].oilCount >= 2 && (char)this->heatMaps[gridX][gridY].marshCount >= 10 && notInLargestArea == impassable) { + if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + ++DAT_GameState::instance.playerDataArray[playerID].availableOilGridTiles; + } + } else if ((char)this->heatMaps[gridX][gridY].ironCount >= 6 && blockedCount <= 4) { + if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + ++DAT_GameState::instance.playerDataArray[playerID].availableIronGridTiles; + } + } else if ((char)this->heatMaps[gridX][gridY].bouldersCount >= 8 && notInLargestArea == impassable) { + if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 40) { + ++DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles; + } + } else if ((char)this->heatMaps[gridX][gridY].treeCount >= 0 && blockedCount <= 5) { + if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + ++DAT_GameState::instance.playerDataArray[playerID].availableTreeGridTiles; + } + } else if ((char)this->heatMaps[gridX][gridY].oasisScrubCount > 25 && (char)this->heatMaps[gridX][gridY].oasisThickScrubCount > 14 + && notInLargestArea == impassable && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + ++DAT_GameState::instance.playerDataArray[playerID].availableOasisGridTiles; + } + this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount + 1; + this->heatMapXArray[this->algBIndex] = gridX; + this->heatMapYArray[this->algBIndex] = gridY; + ++this->algBIndex; + if (this->algBIndex > 6400) { + this->algBIndex = 0; + } + } + ++this->algAIndex; + if (this->algAIndex > 6400) { + this->algAIndex = 0; + } + } + + // allow searching right away if there is anything to find + if (DAT_GameState::instance.playerDataArray[playerID].availableTreeGridTiles > 0) { + DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk = 0; + } + if (DAT_GameState::instance.playerDataArray[playerID].availableOasisGridTiles > 0) { + DAT_GameState::instance.playerDataArray[playerID].algoOasisCooldownUnk = 0; + } + if (DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles > 0) { + DAT_GameState::instance.playerDataArray[playerID].algoBoulderCooldown = 0; + } + if (DAT_GameState::instance.playerDataArray[playerID].availableIronGridTiles > 0) { + DAT_GameState::instance.playerDataArray[playerID].algoIronCooldown = 0; + } + if (DAT_GameState::instance.playerDataArray[playerID].availableOilGridTiles > 0) { + DAT_GameState::instance.playerDataArray[playerID].algoOilCooldown = 0; + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index a51b073c5..4b735cde6 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37947,7 +37947,7 @@ SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending SHC_3BB0A8C1_0x004EE430 | 49.9% | Register alloc; 84% normalized -SHC_3BB0A8C1_0x004EE830 | 0.0% | Pending +SHC_3BB0A8C1_0x004EE830 | 72.8% | Register alloc; 86% normalized SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending From b987dc9fe223a5282754c721b87f7f114660b678 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:22:25 +0200 Subject: [PATCH 37/85] reimplement: SHC_3BB0A8C1_0x004F1860 78.84% Register alloc; 92% normalized --- ...dingsStateAndUpdateAIBuildingDecisions.cpp | 96 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp diff --git a/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp b/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp new file mode 100644 index 000000000..511cf6a03 --- /dev/null +++ b/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp @@ -0,0 +1,96 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Audio/SFX/SFXState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" + +#include "OpenSHC/string-literals.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_SFXState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004F1860 + void AIVState::updateBuildingsStateAndUpdateAIBuildingDecisions() + { + if (DAT_GameSynchronyState::instance.currentGameMode == Game::GM_SOLITARY) { + return; + } + + if (DAT_GameState::instance.mapAndTime.startOfDay) { + ++this->mapExtraInfo.algBuildingPlacementNudge; + this->counter = 0; + if (this->mapExtraInfo.algBuildingPlacementNudge > 31) { + this->mapExtraInfo.algBuildingPlacementNudge = 0; + } + for (int player = 1; player < 9; ++player) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1 + && DAT_GameSynchronyState::instance.currentAIArray[player] == 0) { + continue; + } + if (MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)(player)) { + DAT_GameState::instance.playerDataArray[player].keepEnclosementRelatedCountdown = 5; + continue; + } + // warn the local player when he broke into an enemy castle + if (DAT_GameState::instance.playerDataArray[player].keepEnclosementRelatedCountdown > 0 && --DAT_GameState::instance.playerDataArray[player].keepEnclosementRelatedCountdown == 0 + && DAT_GameState::instance.playerDataArray[player].defensesDamagedByPlayer == DAT_GameSynchronyState::instance.currentPlayerSlotID + && DAT_GameState::instance.mapAndTime.playerTeams[player] + != DAT_GameState::instance.mapAndTime + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID] + && DAT_GameCore::instance.genieVoiceActive) { + // "Once more unto the breach" + MACRO_CALL_MEMBER(Audio::SFX::SFXState_Func::playWAVSFX, DAT_SFXState::ptr)(s_Genie_35_wav_005ab8b8); + } + } + } + + if (DAT_GameState::instance.mapAndTime.monthChanged) { + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::recomputePlayerDataStocksBasedOnStorageBuildings, + DAT_BuildingsState::ptr)(); + } + MACRO_CALL_MEMBER(AICState_Func::updateAIBehaviour, DAT_AICState::ptr)(); + if (DAT_GameState::instance.mapAndTime.weekChanged) { + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::updatePathLinkageTileMap, DAT_BuildingsState::ptr)(2); + for (int player = 1; player < 9; ++player) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1 + && DAT_GameSynchronyState::instance.currentAIArray[player] != 0 && DAT_GameState::instance.playerDataArray[player].playerDeathRelated == 0 + && DAT_GameState::instance.playerDataArray[player].lordKilledByPlayerID == 0 && DAT_GameState::instance.playerDataArray[player].someCount31 == 0) { + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::updateHeatmapBasedOnBuildingAccessibility, + DAT_BuildingsState::ptr)(player); + MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::makeUnitsDisappearThatCannotReachTheKeep, + DAT_UnitsState::ptr)(player); + } + } + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::updatePathLinkageTileMap, DAT_BuildingsState::ptr)(0); + } + + // each AI player decides on its own tick of the day + for (int player = 1; player < 9; ++player) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1 + && DAT_GameSynchronyState::instance.currentAIArray[player] != 0 && DAT_GameState::instance.playerDataArray[player].playerDeathRelated == 0 + && DAT_GameState::instance.playerDataArray[player].lordKilledByPlayerID == 0 && DAT_GameState::instance.mapAndTime.dayTicks == player) { + MACRO_CALL_MEMBER(AIVState_Func::aiDecideOnNewBuildings, this)(player); + MACRO_CALL_MEMBER(AICState_Func::planToBuyWhenLowOnResourceAndSnoozeBuildings, DAT_AICState::ptr)(player); + } + } + if (DAT_GameState::instance.mapAndTime.dayTicks == 8) { + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::applySnoozedStateBasedOnPlayerData, + DAT_BuildingsState::ptr)(); + } + if (DAT_GameState::instance.mapAndTime.monthTicks == 10) { + MACRO_CALL_MEMBER(AIVState_Func::recomputeHeatmaps, this)(0); + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 4b735cde6..9732cfa73 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38007,7 +38007,7 @@ SHC_3BB0A8C1_0x004F14F0 | 91.5% | Reimplemented SHC_3BB0A8C1_0x004F15C0 | 63.1% | Register allocation -SHC_3BB0A8C1_0x004F1860 | 0.0% | Pending +SHC_3BB0A8C1_0x004F1860 | 78.8% | Register alloc; 92% normalized SHC_3BB0A8C1_0x004F1A60 | 0.0% | Pending From 6a8eaaea70cc1bf503d51ea38634693c6431a051 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 07:27:23 +0200 Subject: [PATCH 38/85] reimplement: SHC_3BB0A8C1_0x004F0E80 84.55% Register allocation --- src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp | 188 ++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp diff --git a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp new file mode 100644 index 000000000..93ea19f8d --- /dev/null +++ b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp @@ -0,0 +1,188 @@ +#include "../AIVState.func.hpp" + +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic1.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic2.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + using namespace Map::LogicHelpers; + + // recomputes the 5x5 tile heat map cells the AI uses to place buildings + // FUNCTION: STRONGHOLDCRUSADER 0x004F0E80 + void AIVState::recomputeHeatmaps(int initialize) + { + if (DAT_GameSynchronyState::instance.currentGameMode == Game::GM_SOLITARY) { + return; + } + + if (initialize) { + // full reset + MACRO_CALL_MEMBER(AIVState_Func::clearTheHeatmaps, this)(); + for (int y = 0; y < 80; ++y) { + for (int x = 0; x < 80; ++x) { + this->heatMaps[x][y].oasisThickScrubCount = 250; + this->heatMaps[x][y].impassableCount = 1; + } + } + this->mapExtraInfo.largestSeparateArea = MACRO_CALL_MEMBER(AIVState_Func::computeLargestSeparateArea, this)(); + } else { + for (int y = 0; y < 80; ++y) { + for (int x = 0; x < 80; ++x) { + this->heatMaps[x][y].treeCount = 0; + this->heatMaps[x][y].impassableCount = 0; + this->heatMaps[x][y].structureCount = 0; + this->heatMaps[x][y].tileOwnership = 0; + if ((char)this->heatMaps[x][y].woodRelatedCountdownTo0 > 0) { + --this->heatMaps[x][y].woodRelatedCountdownTo0; + } + if ((char)this->heatMaps[x][y].destructionBasedPlacementCooldown > 0) { + --this->heatMaps[x][y].destructionBasedPlacementCooldown; + } + } + } + } + + this->mapExtraInfo.totalWoodAvailable = 0; + if (this->mapExtraInfo.largestSeparateArea <= 0) { + return; + } + + if (initialize) { + for (int tile = 0; tile < 80400; ++tile) { + int const gridY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5; + int const gridX = (tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) + / 5; + if (gridX >= 1 && gridX <= 77 && gridY >= 1 && gridY <= 77) { + this->heatMaps[gridX][gridY].isNotNearEdgeUnk = 0; + } + if ((short)DAT_TileMapState::instance.PathConnectionLayer[tile] != this->mapExtraInfo.largestSeparateArea) { + ++this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; + } + if (DAT_TileMapState::instance.LogicLayer[tile] & (L_SEA | L_BORDER | L_BORDER_EDGE | L_RIVER | L_FORD)) { + continue; + } + + int const height = DAT_TileMapState::instance.DefaultHeightLayer[tile]; + if (height < this->heatMaps[gridX][gridY].lowestTerrainHeight) { + this->heatMaps[gridX][gridY].lowestTerrainHeight = (byte)height; + } + if (height > this->heatMaps[gridX][gridY].highestTerrainHeight) { + this->heatMaps[gridX][gridY].highestTerrainHeight = (byte)height; + } + if (this->heatMaps[gridX][gridY].lowestTerrainHeight + 12 < (uint)this->heatMaps[gridX][gridY].highestTerrainHeight) { + this->heatMaps[gridX][gridY].terrainDiffHigherThan12 = 1; + } + + int const logic = DAT_TileMapState::instance.LogicLayer[tile]; + if (logic & L_TREE) { + int const treeID = (short)DAT_TileMapState::instance.OrganismLayer[tile]; + // tree types 1 to 4 give wood + if (treeID != 0 + && (DAT_LandscapeState::instance.trees[treeID].treeType == 1 + || DAT_LandscapeState::instance.trees[treeID].treeType == 2 + || DAT_LandscapeState::instance.trees[treeID].treeType == 3 + || DAT_LandscapeState::instance.trees[treeID].treeType == 4) + && DAT_LandscapeState::instance.trees[treeID].stage < 4 + && DAT_LandscapeState::instance.trees[treeID].state == 2 + && MACRO_CALL_MEMBER(Map::LandscapeState_Func::isTreeAdult, DAT_LandscapeState::ptr)( + treeID, DAT_LandscapeState::instance.trees[treeID].uid)) { + this->heatMaps[gridX][gridY].treeCount += 3; + this->mapExtraInfo.totalWoodAvailable += 3; + } + continue; + } + if (logic & L_BOULDERS) { + ++this->heatMaps[gridX][gridY].bouldersCount; + } else if (logic & L_IRON) { + ++this->heatMaps[gridX][gridY].ironCount; + } else if (logic & L_OIL) { + ++this->heatMaps[gridX][gridY].oilCount; + } else if (logic & L_MARSH) { + ++this->heatMaps[gridX][gridY].marshCount; + } else if (logic & (L_BUILDING | L_KEEP_NON_MANOR_HOUSE)) { + ++this->heatMaps[gridX][gridY].structureCount; + } else if (logic & L_WALL_OR_GATEHOUSE) { + ++this->heatMaps[gridX][gridY].structureCount; + } else if (logic & L_ROCKY) { + ++this->heatMaps[gridX][gridY].impassableCount; + } + if (!(DAT_TileMapState::instance.LogicLayer[tile] & L_RIVER)) { + if (DAT_TileMapState::instance.Logic2Layer[tile] & (L2_SCRUB | L2_OASIS_GRASS | L2_THICK_SCRUB)) { + ++this->heatMaps[gridX][gridY].oasisScrubCount; + } + if (DAT_TileMapState::instance.Logic2Layer[tile] & (L2_OASIS_GRASS | L2_THICK_SCRUB)) { + ++this->heatMaps[gridX][gridY].oasisThickScrubCount; + } + } + } + return; + } + + // only update what can change during the game + for (int tile = 0; tile < 80400; ++tile) { + if (!(DAT_TileMapState::instance.LogicLayer[tile] + & (L_ROCKY | L_WALL_OR_GATEHOUSE | L_BUILDING | L_TREE | L_KEEP_NON_MANOR_HOUSE))) { + continue; + } + if (DAT_TileMapState::instance.LogicLayer[tile] & L_ROCKY) { + ++this->heatMaps[(tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) + / 5][DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5] + .impassableCount; + } + if (DAT_TileMapState::instance.LogicLayer[tile] & L_TREE) { + short const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const gridX = (tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile) / 5; + int const gridY = y / 5; + int const treeID = (short)DAT_TileMapState::instance.OrganismLayer[tile]; + // tree types 1 to 4 give wood + if (treeID != 0 + && (DAT_LandscapeState::instance.trees[treeID].treeType == 1 + || DAT_LandscapeState::instance.trees[treeID].treeType == 2 + || DAT_LandscapeState::instance.trees[treeID].treeType == 3 + || DAT_LandscapeState::instance.trees[treeID].treeType == 4) + && DAT_LandscapeState::instance.trees[treeID].stage < 4 + && DAT_LandscapeState::instance.trees[treeID].state == 2 + && MACRO_CALL_MEMBER(Map::LandscapeState_Func::isTreeAdult, DAT_LandscapeState::ptr)( + treeID, DAT_LandscapeState::instance.trees[treeID].uid)) { + this->heatMaps[gridX][gridY].treeCount += 3; + this->mapExtraInfo.totalWoodAvailable += 3; + } + } else if (DAT_TileMapState::instance.BuildingLayer[tile] != 0) { + int const gridX = (tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) + / 5; + int const gridY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5; + ++this->heatMaps[gridX][gridY].structureCount; + this->heatMaps[gridX][gridY].tileOwnership + = (byte)DAT_BuildingsState::instance.buildings[(short)DAT_TileMapState::instance.BuildingLayer[tile]].owner; + } else if (DAT_TileMapState::instance.LogicLayer[tile] & L_WALL_OR_GATEHOUSE) { + int const gridX = (tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) + / 5; + int const gridY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5; + ++this->heatMaps[gridX][gridY].structureCount; + this->heatMaps[gridX][gridY].tileOwnership = (DAT_TileMapState::instance.WallOwnerLayer[tile] & 7) + 1; + } + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 9732cfa73..3a043f888 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37995,7 +37995,7 @@ SHC_3BB0A8C1_0x004F0060 | 36.4% | Store scheduling; 77% normalized SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending -SHC_3BB0A8C1_0x004F0E80 | 0.0% | Pending +SHC_3BB0A8C1_0x004F0E80 | 84.5% | Register allocation SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending From 125f743be0ccaf7a30cff0c6368ff1f2478b799d Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 11:00:11 +0200 Subject: [PATCH 39/85] reimplement: SHC_3BB0A8C1_0x004ECDB0 100.00% Reimplemented --- .../AI/AIVState/setAIVFilePresenceByFileHashArray.cpp | 7 +++---- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/setAIVFilePresenceByFileHashArray.cpp b/src/OpenSHC/AI/AIVState/setAIVFilePresenceByFileHashArray.cpp index ea1f6a0b3..1d9b6899a 100644 --- a/src/OpenSHC/AI/AIVState/setAIVFilePresenceByFileHashArray.cpp +++ b/src/OpenSHC/AI/AIVState/setAIVFilePresenceByFileHashArray.cpp @@ -7,7 +7,7 @@ namespace AI { // an AIV is only usable in multiplayer if all players have the same file // FUNCTION: STRONGHOLDCRUSADER 0x004ECDB0 - void AIVState::setAIVFilePresenceByFileHashArray(int unused) + void AIVState::setAIVFilePresenceByFileHashArray() { if (DAT_GameSynchronyState::instance.currentGameMode == Game::GM_SKIRMISH_SINGLE_PLAYER) { for (int aiIndex = 0; aiIndex < 16; ++aiIndex) { @@ -25,14 +25,13 @@ namespace AI { if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1) { continue; } - // the received hashes use the same layout as aivFileHash if (hash == -1) { - hash = ((int*)this->receivedAIVFileHash[player])[aiIndex * 8 + castleID]; + hash = this->receivedAIVFileHash[player][aiIndex][castleID]; if (hash == -1) { this->aivFilePresent[aiIndex][castleID] = 0; break; } - } else if (hash != ((int*)this->receivedAIVFileHash[player])[aiIndex * 8 + castleID]) { + } else if (hash != this->receivedAIVFileHash[player][aiIndex][castleID]) { this->aivFilePresent[aiIndex][castleID] = 0; break; } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 3a043f888..f89a03dac 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37879,7 +37879,7 @@ SHC_3BB0A8C1_0x004ECCA0 | 0.0% | Pending SHC_3BB0A8C1_0x004ECD90 | 0.0% | Pending -SHC_3BB0A8C1_0x004ECDB0 | 93.2% | Header has extra int param +SHC_3BB0A8C1_0x004ECDB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECDE0 | 0.0% | Pending From cd2ea20e35311ce601fce8659dc3abc767a73de9 Mon Sep 17 00:00:00 2001 From: Gynt Date: Wed, 23 Sep 2026 11:03:39 +0200 Subject: [PATCH 40/85] repair formatting issues --- .../AI/AIVState/aiDecideOnNewBuildings.cpp | 10 +++-- .../AI/AIVState/aiPlaceAIVBuilding.cpp | 34 +++++++------- .../AI/AIVState/aiPlaceAdditionalOxtether.cpp | 8 +++- src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp | 13 +++--- src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp | 7 +-- src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp | 17 +++---- src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp | 6 ++- src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp | 6 ++- .../AI/AIVState/aiPlaceQuarryAndOxtether.cpp | 18 ++++---- src/OpenSHC/AI/AIVState/applyAIV.cpp | 14 +++--- .../AI/AIVState/executeDefaultCastleAIV.cpp | 45 ++++++++++++------- ...findAppropriateGridLocationForBuilding.cpp | 9 ++-- ...ateGridLocationForResourceTypeBuilding.cpp | 32 +++++++++---- .../findAppropriateWoodCutterGridLocation.cpp | 9 ++-- .../findSuitableFarmLocationAndPlaceFarm.cpp | 25 +++++++---- ...indSuitableGridLocationFittingCriteria.cpp | 3 +- .../hostChecksLobbyAIVAvailability.cpp | 6 ++- .../recomputeAIAvailableGridTiles.cpp | 35 ++++++++++----- src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp | 40 ++++++++++------- ...dingsStateAndUpdateAIBuildingDecisions.cpp | 37 +++++++++------ 20 files changed, 234 insertions(+), 140 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp b/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp index 5dbc385a3..438a163cd 100644 --- a/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp +++ b/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp @@ -30,7 +30,8 @@ namespace AI { if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] > 16000) { // rich stepIncrement = 2; - } else if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] > 5000) { + } else if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] + > 5000) { stepIncrement = 1; } else { // poor @@ -82,8 +83,9 @@ namespace AI { isPausing = 1; } } else { - int const pauseStep = DAT_GameState::instance.playerDataArray[playerID] - .aivPauses[DAT_GameState::instance.playerDataArray[playerID].aivCurrentPauseIndex]; + int const pauseStep + = DAT_GameState::instance.playerDataArray[playerID] + .aivPauses[DAT_GameState::instance.playerDataArray[playerID].aivCurrentPauseIndex]; if (pauseStep > 0 && this->aivs[aivID].currentStepGoal >= pauseStep) { DAT_GameState::instance.playerDataArray[playerID].aivCurrentPause = DAT_GameState::instance.playerDataArray[playerID].aivPauseDelay; @@ -98,7 +100,7 @@ namespace AI { MACRO_CALL_MEMBER(AIVState_Func::executeDefaultCastleAIV, this)(playerID, isPausing); } else if (!MACRO_CALL_MEMBER(AICState_Func::destroyHouse, DAT_AICState::ptr)(playerID)) { for (int step = 1; step <= this->aivs[aivID].totalSteps && step <= this->aivs[aivID].currentStepGoal; - ++step) { + ++step) { if (MACRO_CALL_MEMBER(AIVState_Func::aiPlaceAIVBuilding, this)(playerID, step, isPausing)) { break; } diff --git a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp index 5874764ad..1cea95231 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp @@ -106,13 +106,16 @@ namespace AI { --this->aivs[aivID].aivBuildingSteps[step].wait; return FALSE; } - if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_STONE] <= 0) { + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_STONE] + <= 0) { // no stone, request some - if (DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_STONE] + if (DAT_GameState::instance.playerDataArray[playerID] + .resourcesToAcquireArray[Game::Resources::RT_STONE] > 0) { return FALSE; } - DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_STONE] = 5; + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_STONE] + = 5; return FALSE; } } @@ -143,8 +146,9 @@ namespace AI { --this->aivs[aivID].aivBuildingSteps[step].wait; return FALSE; } - uint const y = DAT_ViewportRenderState::instance - .tileTranslationMatrix_YComponent[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; + uint const y + = DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; uint const x = this->aivs[aivID].aivBuildingSteps[step].location.tile.tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( @@ -170,8 +174,8 @@ namespace AI { if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingHovel, DAT_AICState::ptr)(playerID, mapper)) { return FALSE; } - if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildMarketArmoryGranaryBarracksMercenarypost, - DAT_AICState::ptr)(playerID, mapper)) { + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildMarketArmoryGranaryBarracksMercenarypost, DAT_AICState::ptr)( + playerID, mapper)) { return FALSE; } if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildKillingPits, DAT_AICState::ptr)(playerID, mapper)) { @@ -252,32 +256,28 @@ namespace AI { // keep orientation is 0, 2, 4 or 6, halving it gives the index of the part offsets if (mapper == Commands::M_MAPPER_BARRACKS_EURO || mapper == Commands::M_MAPPER_BARRACKS_ARAB) { // the three fields in front of the barracks - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( - playerID, + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].x + x, DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].y + y, mapper, buildingSize); - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( - playerID, + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, mapper, buildingSize); - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( - playerID, + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].x + x, DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].y + y, mapper, buildingSize); } else if (mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD) { // the guild field - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( - playerID, + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, mapper, buildingSize); } else if (mapper == Commands::M_MAPPER_OIL_SMELTER) { - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( - playerID, DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].x + x, + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, + DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].x + x, DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].y + y, mapper, buildingSize); } diff --git a/src/OpenSHC/AI/AIVState/aiPlaceAdditionalOxtether.cpp b/src/OpenSHC/AI/AIVState/aiPlaceAdditionalOxtether.cpp index 724ef65e8..346e3ca0b 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceAdditionalOxtether.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceAdditionalOxtether.cpp @@ -23,9 +23,13 @@ namespace AI { int const quarryID = DAT_GameState::instance.playerDataArray[playerID].highestLoadedQuarryUnk; MACRO_CALL_MEMBER(AIVState_Func::findSuitableGridLocationFittingCriteria, this)( (short)DAT_BuildingsState::instance.buildings[quarryID].x / 5 - + DAT_AIVDefinedData::instance.OffsetsForFiveByFiveAreaSpotCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9].x, + + DAT_AIVDefinedData::instance + .OffsetsForFiveByFiveAreaSpotCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9] + .x, (short)DAT_BuildingsState::instance.buildings[quarryID].y / 5 - + DAT_AIVDefinedData::instance.OffsetsForFiveByFiveAreaSpotCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9].y); + + DAT_AIVDefinedData::instance + .OffsetsForFiveByFiveAreaSpotCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9] + .y); if (this->buildingApproriateGridXPosition == -1) { return; } diff --git a/src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp b/src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp index d31f27d48..dea520702 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceBrazier.cpp @@ -1,8 +1,8 @@ #include "../AIVState.func.hpp" -#include "OpenSHC/AI/AIVUnitType.hpp" #include "OpenSHC/Map/Entities/EntityState.func.hpp" #include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" #include "OpenSHC/Globals/DAT_EntityState.hpp" #include "OpenSHC/Globals/DAT_GameState.hpp" @@ -24,7 +24,8 @@ namespace AI { continue; } int const entityID = DAT_TileMapState::instance.EntityLayer[tile]; - if (entityID != 0 && DAT_EntityState::instance.entityArray[entityID].entityType == Map::Entities::ET_BRAZIER) { + if (entityID != 0 + && DAT_EntityState::instance.entityArray[entityID].entityType == Map::Entities::ET_BRAZIER) { continue; } uint const height @@ -32,12 +33,12 @@ namespace AI { if ((int)height <= 0) { continue; } - MACRO_CALL_MEMBER(Map::Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)(0, - playerID, playerID, + MACRO_CALL_MEMBER(Map::Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)(0, playerID, + playerID, (tile - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] - .addXgetTile) + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) * 8, DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] * 8, height, 0, 0, 0, Map::Entities::ET_BRAZIER, 0); diff --git a/src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp b/src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp index 0b9894fca..1b28f86ba 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceFarm.cpp @@ -16,14 +16,15 @@ namespace AI { if (buildingType <= 0) { return FALSE; } - if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, - DAT_GameState::ptr)( + if (!MACRO_CALL_MEMBER( + Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, DAT_GameState::ptr)( MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::convertBuildingTypeToCommandBuildingType, DAT_BuildingsState::ptr)((Map::Buildings::BuildingType)buildingType), playerID, FALSE)) { return FALSE; } - return MACRO_CALL_MEMBER(AIVState_Func::findSuitableFarmLocationAndPlaceFarm, this)(playerID, buildingType) != 0; + return MACRO_CALL_MEMBER(AIVState_Func::findSuitableFarmLocationAndPlaceFarm, this)(playerID, buildingType) + != 0; } } diff --git a/src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp b/src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp index d445e4f07..1dc63dacd 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceFlag.cpp @@ -1,8 +1,8 @@ #include "../AIVState.func.hpp" -#include "OpenSHC/AI/AIVUnitType.hpp" #include "OpenSHC/Map/Entities/EntityState.func.hpp" #include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" #include "OpenSHC/Globals/DAT_AICState.hpp" #include "OpenSHC/Globals/DAT_EntityState.hpp" @@ -16,9 +16,10 @@ namespace AI { // FUNCTION: STRONGHOLDCRUSADER 0x004ED240 void AIVState::aiPlaceFlag(PlayerID playerID) { - Map::Entities::EntityType const flagType = (Map::Entities::EntityType)DAT_AICState::instance - .aics[DAT_GameState::instance.playerDataArray[playerID].aiType - 1] - .flagType; + Map::Entities::EntityType const flagType + = (Map::Entities::EntityType)DAT_AICState::instance + .aics[DAT_GameState::instance.playerDataArray[playerID].aiType - 1] + .flagType; if (DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[AIVUT_FLAG] <= 0) { return; } @@ -36,12 +37,12 @@ namespace AI { if (height <= 0) { continue; } - MACRO_CALL_MEMBER(Map::Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)(0, - playerID, playerID, + MACRO_CALL_MEMBER(Map::Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)(0, playerID, + playerID, (tile - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] - .addXgetTile) + .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] + .addXgetTile) * 8 + 4, DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] * 8 + 4, height, 0, 0, 0, diff --git a/src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp b/src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp index 442153b1a..1e09fbedc 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceIronMine.cpp @@ -18,13 +18,15 @@ namespace AI { || DAT_BuildingsState::instance.unknownCountdown01 < 20) { return; } - MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForResourceTypeBuilding, this)(playerID, AIGCRT_IRON); + MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForResourceTypeBuilding, this)( + playerID, AIGCRT_IRON); if (this->buildingApproriateGridXPosition == -1) { return; } ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, - this->buildingApproriateGridXPosition * 5, this->buildingAppropriateGridYPosition * 5, Commands::M_MAPPER_IRON_MINE, + this->buildingApproriateGridXPosition * 5, this->buildingAppropriateGridYPosition * 5, + Commands::M_MAPPER_IRON_MINE, MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( Commands::M_MAPPER_IRON_MINE), 15); diff --git a/src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp b/src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp index 8e4f12822..1097dc076 100644 --- a/src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlacePitchrig.cpp @@ -18,13 +18,15 @@ namespace AI { || DAT_BuildingsState::instance.unknownCountdown01 < 20) { return; } - MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForResourceTypeBuilding, this)(playerID, AIGCRT_OIL); + MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForResourceTypeBuilding, this)( + playerID, AIGCRT_OIL); if (this->buildingApproriateGridXPosition == -1) { return; } ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, - this->buildingApproriateGridXPosition * 5, this->buildingAppropriateGridYPosition * 5, Commands::M_MAPPER_PITCH_WORKINGS, + this->buildingApproriateGridXPosition * 5, this->buildingAppropriateGridYPosition * 5, + Commands::M_MAPPER_PITCH_WORKINGS, MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( Commands::M_MAPPER_PITCH_WORKINGS), 15); diff --git a/src/OpenSHC/AI/AIVState/aiPlaceQuarryAndOxtether.cpp b/src/OpenSHC/AI/AIVState/aiPlaceQuarryAndOxtether.cpp index c177dd6a5..b714825ce 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceQuarryAndOxtether.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceQuarryAndOxtether.cpp @@ -26,12 +26,14 @@ namespace AI { } ++this->heatMaps[this->buildingApproriateGridXPosition][this->buildingAppropriateGridYPosition].structureCount; - int const quarryXOffset = DAT_AIVDefinedData::instance - .OffsetsForClockwiseThreeByThreeAreaCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9] - .x; - int const quarryYOffset = DAT_AIVDefinedData::instance - .OffsetsForClockwiseThreeByThreeAreaCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9] - .y; + int const quarryXOffset + = DAT_AIVDefinedData::instance + .OffsetsForClockwiseThreeByThreeAreaCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9] + .x; + int const quarryYOffset + = DAT_AIVDefinedData::instance + .OffsetsForClockwiseThreeByThreeAreaCheck[this->mapExtraInfo.algBuildingPlacementNudge % 9] + .y; MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, quarryXOffset + this->buildingApproriateGridXPosition * 5, quarryYOffset + this->buildingAppropriateGridYPosition * 5, Commands::M_MAPPER_QUARRY, @@ -53,8 +55,8 @@ namespace AI { int const xOffset = DAT_AIVDefinedData::instance.OffsetsForSpotCheckThreeByThreeAreaOnTheSide[side].x; int const yOffset = DAT_AIVDefinedData::instance.OffsetsForSpotCheckThreeByThreeAreaOnTheSide[side].y; MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, - xOffset + this->buildingApproriateGridXPosition * 5, yOffset + this->buildingAppropriateGridYPosition * 5, - Commands::M_MAPPER_OXENBASE, + xOffset + this->buildingApproriateGridXPosition * 5, + yOffset + this->buildingAppropriateGridYPosition * 5, Commands::M_MAPPER_OXENBASE, MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( Commands::M_MAPPER_OXENBASE), 15); diff --git a/src/OpenSHC/AI/AIVState/applyAIV.cpp b/src/OpenSHC/AI/AIVState/applyAIV.cpp index 52dcdf95c..d28b9e700 100644 --- a/src/OpenSHC/AI/AIVState/applyAIV.cpp +++ b/src/OpenSHC/AI/AIVState/applyAIV.cpp @@ -1,8 +1,8 @@ #include "../AIVState.func.hpp" -#include "OpenSHC/AI/AIVUnitType.hpp" #include "OpenSHC/IO/FilePackager.func.hpp" #include "OpenSHC/Rendering/ViewportRenderState.func.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" #include "OpenSHC/Globals/DAT_AIVDefinedData.hpp" #include "OpenSHC/Globals/DAT_AIVState.hpp" @@ -42,8 +42,8 @@ namespace AI { } this->isHandled[index] = 1; } else { - MappersEnum const mapper = MACRO_CALL_MEMBER(AIVState_Func::convertAIVBuildingTypeToCommandBuildingType, - this)((AIV::AIVBuildingType2)type); + MappersEnum const mapper = MACRO_CALL_MEMBER( + AIVState_Func::convertAIVBuildingTypeToCommandBuildingType, this)((AIV::AIVBuildingType2)type); int const step = this->steps[0][index]; // walls, moats and pitch ditches are collected below if (mapper == Commands::M_MAPPER_WALL || mapper == Commands::M_MAPPER_WOODWALL @@ -56,8 +56,8 @@ namespace AI { continue; } int const realY = this->aivs[aivID].keepYOffset + y; - if (!MACRO_CALL_MEMBER(Rendering::ViewportRenderState_Func::xyAreValid, DAT_ViewportRenderState::ptr)( - this->aivs[aivID].keepXOffset + x, realY)) { + if (!MACRO_CALL_MEMBER(Rendering::ViewportRenderState_Func::xyAreValid, + DAT_ViewportRenderState::ptr)(this->aivs[aivID].keepXOffset + x, realY)) { continue; } this->aivs[aivID].aivBuildingSteps[step].buildingType = mapper; @@ -152,8 +152,8 @@ namespace AI { y = 99 - relativeX; x = relativeY; } - int const tile = DAT_ViewportRenderState::instance.translationMatrix[this->aivs[aivID].keepYOffset + y] - .addXgetTile + int const tile + = DAT_ViewportRenderState::instance.translationMatrix[this->aivs[aivID].keepYOffset + y].addXgetTile + this->aivs[aivID].keepXOffset + x; DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlots[unitType][i] = tile; if (tile > 0) { diff --git a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp index 4a3df1af6..bdd706444 100644 --- a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp +++ b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp @@ -1,10 +1,10 @@ #include "../AIVState.func.hpp" #include "OpenSHC/AI/AICState.func.hpp" -#include "OpenSHC/AI/AIType.hpp" #include "OpenSHC/Game/GameStateStructures.func.hpp" #include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" #include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" #include "OpenSHC/Globals/DAT_AICState.hpp" #include "OpenSHC/Globals/DAT_AIVState.hpp" @@ -455,9 +455,11 @@ namespace AI { --chapels; } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_INN) { --inns; - } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_ENGINEERS_GUILD) { + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType + == Commands::M_MAPPER_ENGINEERS_GUILD) { --engineersGuilds; - } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_TUNNELERS_GUILD) { + } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType + == Commands::M_MAPPER_TUNNELERS_GUILD) { --tunnelersGuilds; } else if (this->aivs[aivID].aivBuildingSteps[step].buildingType == Commands::M_MAPPER_STABLES) { --stables; @@ -472,10 +474,13 @@ namespace AI { } if (status == AIVBSS_built) { // only rebuild buildings that were destroyed - short const buildingID = DAT_TileMapState::instance.BuildingLayer[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; + short const buildingID + = DAT_TileMapState::instance + .BuildingLayer[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; if (buildingID != 0 && DAT_BuildingsState::instance.buildings[buildingID].buildingType - == MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + == MACRO_CALL_MEMBER( + Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, DAT_BuildingsState::ptr)(mapper)) { continue; } @@ -569,29 +574,38 @@ namespace AI { // the three fields in front of the barracks MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( playerID, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].x + x, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].y + y, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].x + + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].y + + y, mapper, buildingSize); MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( playerID, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + + y, mapper, buildingSize); MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( playerID, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].x + x, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].y + y, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].x + + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].y + + y, mapper, buildingSize); } else if (mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD) { // the guild field MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( playerID, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + + y, mapper, buildingSize); } else if (mapper == Commands::M_MAPPER_OIL_SMELTER) { MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( - playerID, DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].x + x, + playerID, + DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].x + x, DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].y + y, mapper, buildingSize); } @@ -609,7 +623,8 @@ namespace AI { this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; } } - this->aivs[aivID].aivBuildingSteps[step].wait = this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built ? 10 : 16; + this->aivs[aivID].aivBuildingSteps[step].wait + = this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built ? 10 : 16; if (!DAT_TileMapState::instance.buildingPlacementFail) { return TRUE; } diff --git a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp index 416a2841c..af25107c8 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp @@ -37,12 +37,15 @@ namespace AI { } this->visitCount = (char)this->heatMaps[x][y].algorithmVisitCountUnk; for (int direction = 0; direction < 8; ++direction) { - candidateX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; - candidateY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + candidateX + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + candidateY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; if (this->heatMaps[x][y].isNotNearEdgeUnk && (candidateX > 79 || candidateY > 79)) { continue; } - int const notInLargestArea = (char)this->heatMaps[candidateX][candidateY].tilesNotPartOfLargestAreaCount; + int const notInLargestArea + = (char)this->heatMaps[candidateX][candidateY].tilesNotPartOfLargestAreaCount; if (!this->heatMaps[candidateX][candidateY].terrainDiffHigherThan12 && !this->heatMaps[candidateX][candidateY].structureCount && !this->heatMaps[candidateX][candidateY].isNotNearEdgeUnk diff --git a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp index 7a9d484b9..f77899b64 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp @@ -77,40 +77,54 @@ namespace AI { } // only the four cardinal directions for (int direction = 0; direction < 8; direction += 2) { - uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; - uint const gridY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + uint const gridX + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const gridY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { continue; } - if (this->heatMaps[gridX][gridY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { + if (this->heatMaps[gridX][gridY].algorithmIterationNumber + == this->mapExtraInfo.algorithmIterationNumber) { continue; } this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; - int const blockedCount = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount - (char)this->heatMaps[gridX][gridY].impassableCount; + int const blockedCount = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount + - (char)this->heatMaps[gridX][gridY].impassableCount; if (blockedCount >= 16) { continue; } int const owner = (char)this->heatMaps[gridX][gridY].tileOwnership; - if ((blockedCount == 0 || resourceType == AIGCRT_IRON) && blockedCount <= 4 && !this->heatMaps[gridX][gridY].structureCount + if ((blockedCount == 0 || resourceType == AIGCRT_IRON) && blockedCount <= 4 + && !this->heatMaps[gridX][gridY].structureCount && !this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown && (owner == 0 || DAT_GameState::instance.mapAndTime.playerTeams[playerID] == DAT_GameState::instance.mapAndTime.playerTeams[owner])) { if (resourceType == AIGCRT_OIL) { - if ((char)this->heatMaps[gridX][gridY].oilCount > 2 && (char)this->heatMaps[gridX][gridY].marshCount >= 10 - && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + if ((char)this->heatMaps[gridX][gridY].oilCount > 2 + && (char)this->heatMaps[gridX][gridY].marshCount >= 10 + && this->heatMaps[gridX][gridY].highestTerrainHeight + - this->heatMaps[gridX][gridY].lowestTerrainHeight + < 12) { this->buildingApproriateGridXPosition = gridX; this->buildingAppropriateGridYPosition = gridY; return; } } else if (resourceType == AIGCRT_IRON) { - if ((char)this->heatMaps[gridX][gridY].ironCount > 6 && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + if ((char)this->heatMaps[gridX][gridY].ironCount > 6 + && this->heatMaps[gridX][gridY].highestTerrainHeight + - this->heatMaps[gridX][gridY].lowestTerrainHeight + < 12) { this->buildingApproriateGridXPosition = gridX; this->buildingAppropriateGridYPosition = gridY; return; } } else if (resourceType == AIGCRT_BOULDER) { - if ((char)this->heatMaps[gridX][gridY].bouldersCount >= 8 && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 40) { + if ((char)this->heatMaps[gridX][gridY].bouldersCount >= 8 + && this->heatMaps[gridX][gridY].highestTerrainHeight + - this->heatMaps[gridX][gridY].lowestTerrainHeight + < 40) { this->buildingApproriateGridXPosition = gridX; this->buildingAppropriateGridYPosition = gridY; return; diff --git a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp index 3925139ff..0297ccb7c 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp @@ -40,12 +40,15 @@ namespace AI { } // only the four cardinal directions for (int direction = 0; direction < 8; direction += 2) { - uint const gridY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; - uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const gridY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + uint const gridX + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { continue; } - if (this->heatMaps[gridX][gridY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { + if (this->heatMaps[gridX][gridY].algorithmIterationNumber + == this->mapExtraInfo.algorithmIterationNumber) { continue; } this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; diff --git a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp index b3443ebc9..39bb2f5d9 100644 --- a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp +++ b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp @@ -56,12 +56,15 @@ namespace AI { } // only the four cardinal directions for (int direction = 0; direction < 8; direction += 2) { - uint const gridY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; - uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const gridY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + uint const gridX + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { continue; } - if (this->heatMaps[gridX][gridY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { + if (this->heatMaps[gridX][gridY].algorithmIterationNumber + == this->mapExtraInfo.algorithmIterationNumber) { continue; } int const notInLargestArea = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; @@ -76,20 +79,24 @@ namespace AI { if (this->algBIndex > 6400) { this->algBIndex = 0; } - if (this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount || this->heatMaps[gridX][gridY].structureCount || this->heatMaps[gridX][gridY].treeCount - || this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown || (char)this->heatMaps[gridX][gridY].oasisScrubCount <= 25 + if (this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount + || this->heatMaps[gridX][gridY].structureCount || this->heatMaps[gridX][gridY].treeCount + || this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown + || (char)this->heatMaps[gridX][gridY].oasisScrubCount <= 25 || (char)this->heatMaps[gridX][gridY].oasisThickScrubCount <= 14) { continue; } int const xOffset - = DAT_AIVDefinedData::instance.TilesSelfAndRightThree[this->mapExtraInfo.algBuildingPlacementNudge].x; + = DAT_AIVDefinedData::instance.TilesSelfAndRightThree[this->mapExtraInfo.algBuildingPlacementNudge] + .x; int const yOffset - = DAT_AIVDefinedData::instance.TilesSelfAndRightThree[this->mapExtraInfo.algBuildingPlacementNudge].y; + = DAT_AIVDefinedData::instance.TilesSelfAndRightThree[this->mapExtraInfo.algBuildingPlacementNudge] + .y; ++this->counter; MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, xOffset + gridX * 5, yOffset + gridY * 5, mapper, - MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, - DAT_TileMapState::ptr)(mapper), + MACRO_CALL_MEMBER( + Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)(mapper), 15); if (!DAT_TileMapState::instance.buildingPlacementFail) { this->heatMaps[gridX][gridY].structureCount = 1; diff --git a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp index 72a1f5ce9..2708eb69c 100644 --- a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp +++ b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp @@ -35,7 +35,8 @@ namespace AI { if (this->heatMaps[x][y].isNotNearEdgeUnk && (candidateX >= 79 || candidateY >= 79)) { continue; } - int const notInLargestArea = (char)this->heatMaps[candidateX][candidateY].tilesNotPartOfLargestAreaCount; + int const notInLargestArea + = (char)this->heatMaps[candidateX][candidateY].tilesNotPartOfLargestAreaCount; if (this->heatMaps[candidateX][candidateY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { continue; diff --git a/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp b/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp index 1a0128c43..92299fd13 100644 --- a/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp +++ b/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp @@ -40,7 +40,8 @@ namespace AI { } else if (player == localPlayer) { count = this->aivFileAvailabilityPerAIArray[aiIndex]; } else { - count = DAT_GameSynchronyState::instance.DAT_ReceivedAIVFileAvailabilityPerAIArray[player][aiIndex]; + count = DAT_GameSynchronyState::instance + .DAT_ReceivedAIVFileAvailabilityPerAIArray[player][aiIndex]; } if (count == 0) { available = false; @@ -79,7 +80,8 @@ namespace AI { Commands::GCT_HOST_REMOVE_PLAYER_BY_SLOT); } if (removedPlayer) { - MACRO_CALL_MEMBER(Synchrony::GameSynchronyState_Func::reorderTeamsAndPositions, DAT_GameSynchronyState::ptr)(); + MACRO_CALL_MEMBER( + Synchrony::GameSynchronyState_Func::reorderTeamsAndPositions, DAT_GameSynchronyState::ptr)(); DAT_GameSynchronyState::instance.reparseMaps = TRUE; } diff --git a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp index 52b649089..aff269952 100644 --- a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp @@ -40,12 +40,15 @@ namespace AI { } // only the four cardinal directions for (int direction = 0; direction < 8; direction += 2) { - uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; - uint const gridY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; + uint const gridX + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const gridY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { continue; } - if (this->heatMaps[gridX][gridY].algorithmIterationNumber == this->mapExtraInfo.algorithmIterationNumber) { + if (this->heatMaps[gridX][gridY].algorithmIterationNumber + == this->mapExtraInfo.algorithmIterationNumber) { continue; } int const impassable = (char)this->heatMaps[gridX][gridY].impassableCount; @@ -55,24 +58,36 @@ namespace AI { if (blockedCount >= 16) { continue; } - if ((char)this->heatMaps[gridX][gridY].oilCount >= 2 && (char)this->heatMaps[gridX][gridY].marshCount >= 10 && notInLargestArea == impassable) { - if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + if ((char)this->heatMaps[gridX][gridY].oilCount >= 2 + && (char)this->heatMaps[gridX][gridY].marshCount >= 10 && notInLargestArea == impassable) { + if (this->heatMaps[gridX][gridY].highestTerrainHeight + - this->heatMaps[gridX][gridY].lowestTerrainHeight + < 12) { ++DAT_GameState::instance.playerDataArray[playerID].availableOilGridTiles; } } else if ((char)this->heatMaps[gridX][gridY].ironCount >= 6 && blockedCount <= 4) { - if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + if (this->heatMaps[gridX][gridY].highestTerrainHeight + - this->heatMaps[gridX][gridY].lowestTerrainHeight + < 12) { ++DAT_GameState::instance.playerDataArray[playerID].availableIronGridTiles; } } else if ((char)this->heatMaps[gridX][gridY].bouldersCount >= 8 && notInLargestArea == impassable) { - if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 40) { + if (this->heatMaps[gridX][gridY].highestTerrainHeight + - this->heatMaps[gridX][gridY].lowestTerrainHeight + < 40) { ++DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles; } } else if ((char)this->heatMaps[gridX][gridY].treeCount >= 0 && blockedCount <= 5) { - if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + if (this->heatMaps[gridX][gridY].highestTerrainHeight + - this->heatMaps[gridX][gridY].lowestTerrainHeight + < 12) { ++DAT_GameState::instance.playerDataArray[playerID].availableTreeGridTiles; } - } else if ((char)this->heatMaps[gridX][gridY].oasisScrubCount > 25 && (char)this->heatMaps[gridX][gridY].oasisThickScrubCount > 14 - && notInLargestArea == impassable && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { + } else if ((char)this->heatMaps[gridX][gridY].oasisScrubCount > 25 + && (char)this->heatMaps[gridX][gridY].oasisThickScrubCount > 14 && notInLargestArea == impassable + && this->heatMaps[gridX][gridY].highestTerrainHeight + - this->heatMaps[gridX][gridY].lowestTerrainHeight + < 12) { ++DAT_GameState::instance.playerDataArray[playerID].availableOasisGridTiles; } this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount + 1; diff --git a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp index 93ea19f8d..724a70112 100644 --- a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp @@ -32,7 +32,8 @@ namespace AI { this->heatMaps[x][y].impassableCount = 1; } } - this->mapExtraInfo.largestSeparateArea = MACRO_CALL_MEMBER(AIVState_Func::computeLargestSeparateArea, this)(); + this->mapExtraInfo.largestSeparateArea + = MACRO_CALL_MEMBER(AIVState_Func::computeLargestSeparateArea, this)(); } else { for (int y = 0; y < 80; ++y) { for (int x = 0; x < 80; ++x) { @@ -60,16 +61,19 @@ namespace AI { int const gridY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5; int const gridX = (tile - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] - .addXgetTile) + .translationMatrix[DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[tile]] + .addXgetTile) / 5; if (gridX >= 1 && gridX <= 77 && gridY >= 1 && gridY <= 77) { this->heatMaps[gridX][gridY].isNotNearEdgeUnk = 0; } - if ((short)DAT_TileMapState::instance.PathConnectionLayer[tile] != this->mapExtraInfo.largestSeparateArea) { + if ((short)DAT_TileMapState::instance.PathConnectionLayer[tile] + != this->mapExtraInfo.largestSeparateArea) { ++this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; } - if (DAT_TileMapState::instance.LogicLayer[tile] & (L_SEA | L_BORDER | L_BORDER_EDGE | L_RIVER | L_FORD)) { + if (DAT_TileMapState::instance.LogicLayer[tile] + & (L_SEA | L_BORDER | L_BORDER_EDGE | L_RIVER | L_FORD)) { continue; } @@ -80,7 +84,8 @@ namespace AI { if (height > this->heatMaps[gridX][gridY].highestTerrainHeight) { this->heatMaps[gridX][gridY].highestTerrainHeight = (byte)height; } - if (this->heatMaps[gridX][gridY].lowestTerrainHeight + 12 < (uint)this->heatMaps[gridX][gridY].highestTerrainHeight) { + if (this->heatMaps[gridX][gridY].lowestTerrainHeight + 12 + < (uint)this->heatMaps[gridX][gridY].highestTerrainHeight) { this->heatMaps[gridX][gridY].terrainDiffHigherThan12 = 1; } @@ -138,10 +143,11 @@ namespace AI { if (DAT_TileMapState::instance.LogicLayer[tile] & L_ROCKY) { ++this->heatMaps[(tile - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] - .addXgetTile) - / 5][DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5] - .impassableCount; + .translationMatrix[DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[tile]] + .addXgetTile) + / 5][DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5] + .impassableCount; } if (DAT_TileMapState::instance.LogicLayer[tile] & L_TREE) { short const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; @@ -164,18 +170,22 @@ namespace AI { } else if (DAT_TileMapState::instance.BuildingLayer[tile] != 0) { int const gridX = (tile - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] - .addXgetTile) + .translationMatrix[DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[tile]] + .addXgetTile) / 5; int const gridY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5; ++this->heatMaps[gridX][gridY].structureCount; this->heatMaps[gridX][gridY].tileOwnership - = (byte)DAT_BuildingsState::instance.buildings[(short)DAT_TileMapState::instance.BuildingLayer[tile]].owner; + = (byte)DAT_BuildingsState::instance + .buildings[(short)DAT_TileMapState::instance.BuildingLayer[tile]] + .owner; } else if (DAT_TileMapState::instance.LogicLayer[tile] & L_WALL_OR_GATEHOUSE) { int const gridX = (tile - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]] - .addXgetTile) + .translationMatrix[DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[tile]] + .addXgetTile) / 5; int const gridY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5; ++this->heatMaps[gridX][gridY].structureCount; diff --git a/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp b/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp index 511cf6a03..ed5e2f319 100644 --- a/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp +++ b/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp @@ -5,7 +5,6 @@ #include "OpenSHC/Game/GameStateStructures.func.hpp" #include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" #include "OpenSHC/Map/Units/UnitsState.func.hpp" - #include "OpenSHC/string-literals.hpp" #include "OpenSHC/Globals/DAT_AICState.hpp" @@ -42,14 +41,17 @@ namespace AI { continue; } // warn the local player when he broke into an enemy castle - if (DAT_GameState::instance.playerDataArray[player].keepEnclosementRelatedCountdown > 0 && --DAT_GameState::instance.playerDataArray[player].keepEnclosementRelatedCountdown == 0 - && DAT_GameState::instance.playerDataArray[player].defensesDamagedByPlayer == DAT_GameSynchronyState::instance.currentPlayerSlotID + if (DAT_GameState::instance.playerDataArray[player].keepEnclosementRelatedCountdown > 0 + && --DAT_GameState::instance.playerDataArray[player].keepEnclosementRelatedCountdown == 0 + && DAT_GameState::instance.playerDataArray[player].defensesDamagedByPlayer + == DAT_GameSynchronyState::instance.currentPlayerSlotID && DAT_GameState::instance.mapAndTime.playerTeams[player] != DAT_GameState::instance.mapAndTime - .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID] && DAT_GameCore::instance.genieVoiceActive) { // "Once more unto the breach" - MACRO_CALL_MEMBER(Audio::SFX::SFXState_Func::playWAVSFX, DAT_SFXState::ptr)(s_Genie_35_wav_005ab8b8); + MACRO_CALL_MEMBER(Audio::SFX::SFXState_Func::playWAVSFX, DAT_SFXState::ptr)( + s_Genie_35_wav_005ab8b8); } } } @@ -60,32 +62,39 @@ namespace AI { } MACRO_CALL_MEMBER(AICState_Func::updateAIBehaviour, DAT_AICState::ptr)(); if (DAT_GameState::instance.mapAndTime.weekChanged) { - MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::updatePathLinkageTileMap, DAT_BuildingsState::ptr)(2); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::updatePathLinkageTileMap, DAT_BuildingsState::ptr)( + 2); for (int player = 1; player < 9; ++player) { if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1 - && DAT_GameSynchronyState::instance.currentAIArray[player] != 0 && DAT_GameState::instance.playerDataArray[player].playerDeathRelated == 0 - && DAT_GameState::instance.playerDataArray[player].lordKilledByPlayerID == 0 && DAT_GameState::instance.playerDataArray[player].someCount31 == 0) { + && DAT_GameSynchronyState::instance.currentAIArray[player] != 0 + && DAT_GameState::instance.playerDataArray[player].playerDeathRelated == 0 + && DAT_GameState::instance.playerDataArray[player].lordKilledByPlayerID == 0 + && DAT_GameState::instance.playerDataArray[player].someCount31 == 0) { MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::updateHeatmapBasedOnBuildingAccessibility, DAT_BuildingsState::ptr)(player); MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::makeUnitsDisappearThatCannotReachTheKeep, DAT_UnitsState::ptr)(player); } } - MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::updatePathLinkageTileMap, DAT_BuildingsState::ptr)(0); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::updatePathLinkageTileMap, DAT_BuildingsState::ptr)( + 0); } // each AI player decides on its own tick of the day for (int player = 1; player < 9; ++player) { if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1 - && DAT_GameSynchronyState::instance.currentAIArray[player] != 0 && DAT_GameState::instance.playerDataArray[player].playerDeathRelated == 0 - && DAT_GameState::instance.playerDataArray[player].lordKilledByPlayerID == 0 && DAT_GameState::instance.mapAndTime.dayTicks == player) { + && DAT_GameSynchronyState::instance.currentAIArray[player] != 0 + && DAT_GameState::instance.playerDataArray[player].playerDeathRelated == 0 + && DAT_GameState::instance.playerDataArray[player].lordKilledByPlayerID == 0 + && DAT_GameState::instance.mapAndTime.dayTicks == player) { MACRO_CALL_MEMBER(AIVState_Func::aiDecideOnNewBuildings, this)(player); - MACRO_CALL_MEMBER(AICState_Func::planToBuyWhenLowOnResourceAndSnoozeBuildings, DAT_AICState::ptr)(player); + MACRO_CALL_MEMBER(AICState_Func::planToBuyWhenLowOnResourceAndSnoozeBuildings, DAT_AICState::ptr)( + player); } } if (DAT_GameState::instance.mapAndTime.dayTicks == 8) { - MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::applySnoozedStateBasedOnPlayerData, - DAT_BuildingsState::ptr)(); + MACRO_CALL_MEMBER( + Map::Buildings::BuildingsState_Func::applySnoozedStateBasedOnPlayerData, DAT_BuildingsState::ptr)(); } if (DAT_GameState::instance.mapAndTime.monthTicks == 10) { MACRO_CALL_MEMBER(AIVState_Func::recomputeHeatmaps, this)(0); From f3aecba11f8e84d094964dc99dd455ca7e16ea20 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 01:48:59 +0200 Subject: [PATCH 41/85] reimplement: SHC_3BB0A8C1_0x004EE830 73.79% orig literal --- src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp index aff269952..cc37f2b26 100644 --- a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp @@ -77,7 +77,7 @@ namespace AI { < 40) { ++DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles; } - } else if ((char)this->heatMaps[gridX][gridY].treeCount >= 0 && blockedCount <= 5) { + } else if ((char)this->heatMaps[gridX][gridY].treeCount >= 0 && blockedCount < 6) { if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index f89a03dac..32f7feb76 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37947,7 +37947,7 @@ SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending SHC_3BB0A8C1_0x004EE430 | 49.9% | Register alloc; 84% normalized -SHC_3BB0A8C1_0x004EE830 | 72.8% | Register alloc; 86% normalized +SHC_3BB0A8C1_0x004EE830 | 73.8% | orig literal SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending From 0389dd2fc202b38347f2d3545ae82193afee0917 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 01:49:00 +0200 Subject: [PATCH 42/85] reimplement: SHC_3BB0A8C1_0x004F13F0 88.75% orig literal --- src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp b/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp index 8ed8ae1e8..777dcc9ab 100644 --- a/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp +++ b/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp @@ -24,7 +24,7 @@ namespace AI { sizeof(this->isHandled), 0, this->isHandled); for (int tries = 0; tries < 8; ++tries) { ++castleID; - if ((int)castleID > 7) { + if ((int)castleID >= 8) { castleID = 0; } int const aiType = this->aivs[aivID].aiType; diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 32f7feb76..ed8c61a4c 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38001,7 +38001,7 @@ SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending SHC_3BB0A8C1_0x004F13E0 | 83.3% | Reimplemented -SHC_3BB0A8C1_0x004F13F0 | 90.0% | Reimplemented +SHC_3BB0A8C1_0x004F13F0 | 88.8% | orig literal SHC_3BB0A8C1_0x004F14F0 | 91.5% | Reimplemented From b8ff8880c95da8715607dc67941610a0ad1eba75 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 14:47:29 +0200 Subject: [PATCH 43/85] improve by ordering stack variable declarations --- src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp b/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp index 438a163cd..49da92bc7 100644 --- a/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp +++ b/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp @@ -12,12 +12,14 @@ namespace AI { void AIVState::aiDecideOnNewBuildings(PlayerID playerID) { int const aivID = DAT_GameState::instance.playerDataArray[playerID].aivID; - int stepIncrement = 1; - int isPausing = 0; + if (aivID <= 0) { return; } + int isPausing = 0; + int stepIncrement = 1; + if (DAT_GameState::instance.playerDataArray[playerID].resourceRebuildDelay != 0) { ++DAT_GameState::instance.playerDataArray[playerID].resourceRebuildDelay; if (DAT_GameState::instance.playerDataArray[playerID].resourceRebuildDelay From 2dba13ee2c54f16dab3dbc587937bd132529ed3c Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 15:53:19 +0200 Subject: [PATCH 44/85] reimplement: SHC_3BB0A8C1_0x004EE430 57.61% regalloc: orig caches playerID --- ...ateGridLocationForResourceTypeBuilding.cpp | 27 +++++-------------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp index f77899b64..dd9280e7c 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp @@ -17,13 +17,8 @@ namespace AI { this->buildingApproriateGridXPosition = -1; this->buildingAppropriateGridYPosition = -1; if (resourceType == AIGCRT_OIL) { - if (DAT_GameState::instance.playerDataArray[playerID].availableOilGridTiles - <= DAT_GameState::instance.playerDataArray[playerID].countPitchRigs) { - this->visitCount = 1; - this->algAIndex = 0; - this->algBIndex = 1; - this->buildingApproriateGridXPosition = -1; - this->buildingAppropriateGridYPosition = -1; + if (DAT_GameState::instance.playerDataArray[playerID].countPitchRigs + >= DAT_GameState::instance.playerDataArray[playerID].availableOilGridTiles) { return; } if (DAT_GameState::instance.playerDataArray[playerID].algoOilCooldown > 0) { @@ -31,13 +26,8 @@ namespace AI { return; } } else if (resourceType == AIGCRT_IRON) { - if (DAT_GameState::instance.playerDataArray[playerID].availableIronGridTiles - <= DAT_GameState::instance.playerDataArray[playerID].countIronMines) { - this->visitCount = 1; - this->algAIndex = 0; - this->algBIndex = 1; - this->buildingApproriateGridXPosition = -1; - this->buildingAppropriateGridYPosition = -1; + if (DAT_GameState::instance.playerDataArray[playerID].countIronMines + >= DAT_GameState::instance.playerDataArray[playerID].availableIronGridTiles) { return; } if (DAT_GameState::instance.playerDataArray[playerID].algoIronCooldown > 0) { @@ -45,13 +35,8 @@ namespace AI { return; } } else if (resourceType == AIGCRT_BOULDER) { - if (DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles - <= DAT_GameState::instance.playerDataArray[playerID].countStoneQuarries) { - this->visitCount = 1; - this->algAIndex = 0; - this->algBIndex = 1; - this->buildingApproriateGridXPosition = -1; - this->buildingAppropriateGridYPosition = -1; + if (DAT_GameState::instance.playerDataArray[playerID].countStoneQuarries + >= DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles) { return; } if (DAT_GameState::instance.playerDataArray[playerID].algoBoulderCooldown > 0) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index ed8c61a4c..a3493eb10 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37945,7 +37945,7 @@ SHC_3BB0A8C1_0x004EE140 | 59.3% | Register alloc; 79% normalized SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending -SHC_3BB0A8C1_0x004EE430 | 49.9% | Register alloc; 84% normalized +SHC_3BB0A8C1_0x004EE430 | 57.6% | regalloc: orig caches playerID SHC_3BB0A8C1_0x004EE830 | 73.8% | orig literal From bfcb5fdf2d8a1fe79956c2b7ffca869c5307204d Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 15:57:00 +0200 Subject: [PATCH 45/85] reimplement: SHC_3BB0A8C1_0x004EE140 62.57% regalloc: orig anchors directi --- .../findAppropriateWoodCutterGridLocation.cpp | 50 ++++++++++--------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp index 0297ccb7c..addfa622a 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp @@ -15,14 +15,14 @@ namespace AI { this->visitCount = 1; this->algBIndex = 1; int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; + this->heatMapXArray[0] = campX; int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; + this->heatMapYArray[0] = campY; this->buildingApproriateGridXPosition = -1; this->buildingAppropriateGridYPosition = -1; int bestScore = -100; uint bestX = -1; uint bestY = -1; - this->heatMapXArray[0] = campX; - this->heatMapYArray[0] = campY; if (DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk > 0) { --DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk; return; @@ -52,32 +52,36 @@ namespace AI { continue; } this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; - int const notInLargestArea = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; + char const notInLargestArea = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; if (notInLargestArea >= 16 || this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown) { continue; } - int const treeCount = (char)this->heatMaps[gridX][gridY].treeCount; - if (notInLargestArea < 6 && treeCount > 0) { - ++foundCount; - int score = treeCount * 5 - this->visitCount * 3; - // prefer cells that were not used for a woodcutter recently - if (this->heatMaps[gridX][gridY].woodRelatedCountdownTo0) { - if (score <= 0) { - score *= 2; - } else { - score /= 2; - } - } - if (score > bestScore) { - bestScore = score; - bestX = gridX; - bestY = gridY; - } - if (foundCount > 10 || (foundCount > 5 && this->visitCount > 20) - || (foundCount > 0 && this->visitCount > 30)) { - break; + if (notInLargestArea >= 6) { + continue; + } + char const treeCount = (char)this->heatMaps[gridX][gridY].treeCount; + if (treeCount <= 0) { + continue; + } + ++foundCount; + int score = treeCount * 5 - this->visitCount * 3; + // prefer cells that were not used for a woodcutter recently + if (this->heatMaps[gridX][gridY].woodRelatedCountdownTo0) { + if (score > 0) { + score /= 2; + } else { + score *= 2; } } + if (score > bestScore) { + bestX = gridX; + bestY = gridY; + bestScore = score; + } + if (foundCount > 10 || (foundCount > 5 && this->visitCount > 20) + || (foundCount > 0 && this->visitCount > 30)) { + break; + } this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount + 1; this->heatMapXArray[this->algBIndex] = gridX; this->heatMapYArray[this->algBIndex] = gridY; diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index a3493eb10..a3f27f0cc 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37941,7 +37941,7 @@ SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented SHC_3BB0A8C1_0x004EDDF0 | 83.0% | Loop pointer anchor, registers -SHC_3BB0A8C1_0x004EE140 | 59.3% | Register alloc; 79% normalized +SHC_3BB0A8C1_0x004EE140 | 62.6% | regalloc: orig anchors directi SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending From 4d65f90b986527c60ccabd8f3dd3aaf394dceef6 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:00:17 +0200 Subject: [PATCH 46/85] reimplement: SHC_3BB0A8C1_0x004EDDF0 89.22% direction ptr anchored 4 low --- .../AIVState/findSuitableFarmLocationAndPlaceFarm.cpp | 11 ++++++----- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp index 39bb2f5d9..e7bda0adb 100644 --- a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp +++ b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp @@ -15,7 +15,7 @@ namespace AI { // FUNCTION: STRONGHOLDCRUSADER 0x004EDDF0 int AIVState::findSuitableFarmLocationAndPlaceFarm(PlayerID playerID, MappersEnum farmType) { - if (DAT_BuildingsState::instance.unknownCountdown01 <= 20 + if (DAT_BuildingsState::instance.unknownCountdown01 < 20 || DAT_GameState::instance.playerDataArray[playerID].countFarms >= DAT_GameState::instance.playerDataArray[playerID].availableOasisGridTiles) { return 0; @@ -29,7 +29,8 @@ namespace AI { this->algAIndex = 0; this->algBIndex = 1; // despite its declared type, farmType holds the BuildingType of the farm - MappersEnum mapper = farmType; + // the original leaves mapper untouched when farmType is none of the four below + MappersEnum mapper; if (farmType == Map::Buildings::BT_WHEATFARM) { mapper = Commands::M_MAPPER_WHEATFARM; } else if (farmType == Map::Buildings::BT_HOPFARM) { @@ -40,9 +41,9 @@ namespace AI { mapper = Commands::M_MAPPER_CATTLEFARM; } int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; + this->heatMapXArray[0] = campX; ++this->mapExtraInfo.algorithmIterationNumber; int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; - this->heatMapXArray[0] = campX; this->heatMapYArray[0] = campY; this->heatMaps[campX][campY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; this->heatMaps[campX][campY].algorithmVisitCountUnk = this->visitCount; @@ -69,14 +70,14 @@ namespace AI { } int const notInLargestArea = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; - if (notInLargestArea >= 16) { + if (notInLargestArea > 16) { continue; } this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount + 1; this->heatMapXArray[this->algBIndex] = gridX; this->heatMapYArray[this->algBIndex] = gridY; ++this->algBIndex; - if (this->algBIndex > 6400) { + if ((int)this->algBIndex >= 6400) { this->algBIndex = 0; } if (this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index a3f27f0cc..a1368ceb0 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37939,7 +37939,7 @@ SHC_3BB0A8C1_0x004EDCB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented -SHC_3BB0A8C1_0x004EDDF0 | 83.0% | Loop pointer anchor, registers +SHC_3BB0A8C1_0x004EDDF0 | 89.2% | direction ptr anchored 4 low SHC_3BB0A8C1_0x004EE140 | 62.6% | regalloc: orig anchors directi From b8c8884949812dbbc680d7548030ad5f1f97a9be Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:00:17 +0200 Subject: [PATCH 47/85] reimplement: SHC_3BB0A8C1_0x004EEBD0 96.12% orig CSEs heatmap addr --- .../AI/AIVState/findSuitableGridLocationFittingCriteria.cpp | 4 ++-- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp index 2708eb69c..b586139a7 100644 --- a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp +++ b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp @@ -60,12 +60,12 @@ namespace AI { this->heatMapXArray[this->algBIndex] = candidateX; this->heatMapYArray[this->algBIndex] = candidateY; ++this->algBIndex; - if (this->algBIndex > 6400) { + if ((int)this->algBIndex >= 6400) { this->algBIndex = 0; } } ++this->algAIndex; - if (this->algAIndex > 6400) { + if ((int)this->algAIndex >= 6400) { this->algAIndex = 0; } } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index a1368ceb0..59e95c8eb 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37951,7 +37951,7 @@ SHC_3BB0A8C1_0x004EE830 | 73.8% | orig literal SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending -SHC_3BB0A8C1_0x004EEBD0 | 94.6% | Register allocation +SHC_3BB0A8C1_0x004EEBD0 | 96.1% | orig CSEs heatmap addr SHC_3BB0A8C1_0x004EECC0 | 0.0% | Pending From 02c51ea0642fb646d54f26e55efb598164ac5120 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:00:18 +0200 Subject: [PATCH 48/85] reimplement: SHC_3BB0A8C1_0x004EE830 74.27% loop structure differs --- src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp | 4 ++-- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp index cc37f2b26..ecddaca7e 100644 --- a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp @@ -94,12 +94,12 @@ namespace AI { this->heatMapXArray[this->algBIndex] = gridX; this->heatMapYArray[this->algBIndex] = gridY; ++this->algBIndex; - if (this->algBIndex > 6400) { + if ((int)this->algBIndex >= 6400) { this->algBIndex = 0; } } ++this->algAIndex; - if (this->algAIndex > 6400) { + if ((int)this->algAIndex >= 6400) { this->algAIndex = 0; } } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 59e95c8eb..b81ba5da0 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37947,7 +37947,7 @@ SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending SHC_3BB0A8C1_0x004EE430 | 57.6% | regalloc: orig caches playerID -SHC_3BB0A8C1_0x004EE830 | 73.8% | orig literal +SHC_3BB0A8C1_0x004EE830 | 74.3% | loop structure differs SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending From a251b2271fe24577fe1aabc18195f5744000cbf8 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:08:44 +0200 Subject: [PATCH 49/85] reimplement: SHC_3BB0A8C1_0x004EEE10 87.45% orig spills fewer locals --- .../AI/AIVState/findAppropriateGridLocationForBuilding.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp index af25107c8..9135bc373 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp @@ -51,7 +51,7 @@ namespace AI { && !this->heatMaps[candidateX][candidateY].isNotNearEdgeUnk && !this->heatMaps[candidateX][candidateY].destructionBasedPlacementCooldown && !this->heatMaps[candidateX][candidateY].treeCount - && (char)this->heatMaps[candidateX][candidateY].bouldersCount < 0 && !notInLargestArea) { + && (char)this->heatMaps[candidateX][candidateY].bouldersCount <= 0 && !notInLargestArea) { ++freeCount; } if (this->heatMaps[candidateX][candidateY].algorithmIterationNumber diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index b81ba5da0..281d8f5f0 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37955,7 +37955,7 @@ SHC_3BB0A8C1_0x004EEBD0 | 96.1% | orig CSEs heatmap addr SHC_3BB0A8C1_0x004EECC0 | 0.0% | Pending -SHC_3BB0A8C1_0x004EEE10 | 86.7% | Register allocation +SHC_3BB0A8C1_0x004EEE10 | 87.5% | orig spills fewer locals SHC_3BB0A8C1_0x004EEEC0 | 0.0% | Pending From 33c80d1de73988ff962fa0491faed61a0e33655e Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:08:44 +0200 Subject: [PATCH 50/85] reimplement: SHC_3BB0A8C1_0x004EEBD0 97.67% scheduling only --- .../AI/AIVState/findSuitableGridLocationFittingCriteria.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp index b586139a7..3663841a5 100644 --- a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp +++ b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp @@ -32,7 +32,7 @@ namespace AI { = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; uint const candidateY = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; - if (this->heatMaps[x][y].isNotNearEdgeUnk && (candidateX >= 79 || candidateY >= 79)) { + if (this->heatMaps[x][y].isNotNearEdgeUnk && (candidateX > 79 || candidateY > 79)) { continue; } int const notInLargestArea diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 281d8f5f0..37ad48ccf 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37951,7 +37951,7 @@ SHC_3BB0A8C1_0x004EE830 | 74.3% | loop structure differs SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending -SHC_3BB0A8C1_0x004EEBD0 | 96.1% | orig CSEs heatmap addr +SHC_3BB0A8C1_0x004EEBD0 | 97.7% | scheduling only SHC_3BB0A8C1_0x004EECC0 | 0.0% | Pending From 98d0d01caf74089b6fe8e66fe01df6c265f46c45 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:08:45 +0200 Subject: [PATCH 51/85] reimplement: SHC_3BB0A8C1_0x004F0E80 85.11% loop structure differs --- src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp index 724a70112..fdc65af11 100644 --- a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp @@ -65,7 +65,7 @@ namespace AI { .tileTranslationMatrix_YComponent[tile]] .addXgetTile) / 5; - if (gridX >= 1 && gridX <= 77 && gridY >= 1 && gridY <= 77) { + if (gridX >= 1 && gridX <= 78 && gridY >= 1 && gridY <= 78) { this->heatMaps[gridX][gridY].isNotNearEdgeUnk = 0; } if ((short)DAT_TileMapState::instance.PathConnectionLayer[tile] diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 37ad48ccf..b844e2941 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37995,7 +37995,7 @@ SHC_3BB0A8C1_0x004F0060 | 36.4% | Store scheduling; 77% normalized SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending -SHC_3BB0A8C1_0x004F0E80 | 84.5% | Register allocation +SHC_3BB0A8C1_0x004F0E80 | 85.1% | loop structure differs SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending From 6a8ab2ba5160a35f216f9a9128014c989a311045 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:08:45 +0200 Subject: [PATCH 52/85] reimplement: SHC_3BB0A8C1_0x004F1860 79.67% orig compares member directly --- .../updateBuildingsStateAndUpdateAIBuildingDecisions.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp b/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp index ed5e2f319..37bc8fb5c 100644 --- a/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp +++ b/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp @@ -28,7 +28,7 @@ namespace AI { if (DAT_GameState::instance.mapAndTime.startOfDay) { ++this->mapExtraInfo.algBuildingPlacementNudge; this->counter = 0; - if (this->mapExtraInfo.algBuildingPlacementNudge > 31) { + if (this->mapExtraInfo.algBuildingPlacementNudge >= 31) { this->mapExtraInfo.algBuildingPlacementNudge = 0; } for (int player = 1; player < 9; ++player) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index b844e2941..e10be0e2d 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38007,7 +38007,7 @@ SHC_3BB0A8C1_0x004F14F0 | 91.5% | Reimplemented SHC_3BB0A8C1_0x004F15C0 | 63.1% | Register allocation -SHC_3BB0A8C1_0x004F1860 | 78.8% | Register alloc; 92% normalized +SHC_3BB0A8C1_0x004F1860 | 79.7% | orig compares member directly SHC_3BB0A8C1_0x004F1A60 | 0.0% | Pending From 11bbb6a9b9666106fa2d5c0aa0c6b44bd08bd12f Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:09:47 +0200 Subject: [PATCH 53/85] reimplement: SHC_3BB0A8C1_0x004EE830 76.70% orig spills direction ptr --- .../AI/AIVState/recomputeAIAvailableGridTiles.cpp | 10 +++++----- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp index ecddaca7e..67bd628c0 100644 --- a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp @@ -58,14 +58,14 @@ namespace AI { if (blockedCount >= 16) { continue; } - if ((char)this->heatMaps[gridX][gridY].oilCount >= 2 + if ((char)this->heatMaps[gridX][gridY].oilCount > 2 && (char)this->heatMaps[gridX][gridY].marshCount >= 10 && notInLargestArea == impassable) { if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { ++DAT_GameState::instance.playerDataArray[playerID].availableOilGridTiles; } - } else if ((char)this->heatMaps[gridX][gridY].ironCount >= 6 && blockedCount <= 4) { + } else if ((char)this->heatMaps[gridX][gridY].ironCount > 6 && blockedCount <= 4) { if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { @@ -77,14 +77,14 @@ namespace AI { < 40) { ++DAT_GameState::instance.playerDataArray[playerID].availableBoulderGridTiles; } - } else if ((char)this->heatMaps[gridX][gridY].treeCount >= 0 && blockedCount < 6) { + } else if ((char)this->heatMaps[gridX][gridY].treeCount > 0 && blockedCount < 6) { if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { ++DAT_GameState::instance.playerDataArray[playerID].availableTreeGridTiles; } - } else if ((char)this->heatMaps[gridX][gridY].oasisScrubCount > 25 - && (char)this->heatMaps[gridX][gridY].oasisThickScrubCount > 14 && notInLargestArea == impassable + } else if ((char)this->heatMaps[gridX][gridY].oasisScrubCount >= 25 + && (char)this->heatMaps[gridX][gridY].oasisThickScrubCount >= 14 && notInLargestArea == impassable && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index e10be0e2d..a22d6c3c7 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37947,7 +37947,7 @@ SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending SHC_3BB0A8C1_0x004EE430 | 57.6% | regalloc: orig caches playerID -SHC_3BB0A8C1_0x004EE830 | 74.3% | loop structure differs +SHC_3BB0A8C1_0x004EE830 | 76.7% | orig spills direction ptr SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending From bcf4888190a6446693c15862016beaf179b0852c Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:11:28 +0200 Subject: [PATCH 54/85] reimplement: SHC_3BB0A8C1_0x004F0060 36.43% Style pass, asm differs --- src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp index bdd706444..0d4648201 100644 --- a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp +++ b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp @@ -485,7 +485,7 @@ namespace AI { continue; } } - if (DAT_GameState::instance.playerDataArray[playerID].closestEnemyDistanceToTheLord <= 120) { + if (DAT_GameState::instance.playerDataArray[playerID].closestEnemyDistanceToTheLord < 120) { continue; } if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index a22d6c3c7..29e834687 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37991,7 +37991,7 @@ SHC_3BB0A8C1_0x004EFE30 | 91.1% | Reimplemented SHC_3BB0A8C1_0x004EFED0 | 93.8% | Reimplemented -SHC_3BB0A8C1_0x004F0060 | 36.4% | Store scheduling; 77% normalized +SHC_3BB0A8C1_0x004F0060 | 36.4% | Style pass, asm differs SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending From 43b547e479872e9efd89fd4f6f174f8507ca991c Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:11:28 +0200 Subject: [PATCH 55/85] reimplement: SHC_3BB0A8C1_0x004EDDF0 90.69% direction ptr anchored 4 low --- .../AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp | 4 ++-- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp index e7bda0adb..5ecfc16ad 100644 --- a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp +++ b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp @@ -83,8 +83,8 @@ namespace AI { if (this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount || this->heatMaps[gridX][gridY].structureCount || this->heatMaps[gridX][gridY].treeCount || this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown - || (char)this->heatMaps[gridX][gridY].oasisScrubCount <= 25 - || (char)this->heatMaps[gridX][gridY].oasisThickScrubCount <= 14) { + || (char)this->heatMaps[gridX][gridY].oasisScrubCount < 25 + || (char)this->heatMaps[gridX][gridY].oasisThickScrubCount < 14) { continue; } int const xOffset diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 29e834687..54e78b56c 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37939,7 +37939,7 @@ SHC_3BB0A8C1_0x004EDCB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented -SHC_3BB0A8C1_0x004EDDF0 | 89.2% | direction ptr anchored 4 low +SHC_3BB0A8C1_0x004EDDF0 | 90.7% | direction ptr anchored 4 low SHC_3BB0A8C1_0x004EE140 | 62.6% | regalloc: orig anchors directi From ddad806d0797d01b2ddb54cf9500f2f5db0001bf Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 16:11:28 +0200 Subject: [PATCH 56/85] reimplement: SHC_3BB0A8C1_0x004EEBD0 100.00% Reimplemented --- .../AI/AIVState/findSuitableGridLocationFittingCriteria.cpp | 4 ++-- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp index 3663841a5..7797d5b61 100644 --- a/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp +++ b/src/OpenSHC/AI/AIVState/findSuitableGridLocationFittingCriteria.cpp @@ -16,7 +16,7 @@ namespace AI { this->heatMapYArray[0] = gridY; this->buildingApproriateGridXPosition = -1; this->buildingAppropriateGridYPosition = -1; - if (gridX >= 79 || gridY >= 79 || this->heatMaps[gridX][gridY].isNotNearEdgeUnk) { + if (gridX > 79 || gridY > 79 || this->heatMaps[gridX][gridY].isNotNearEdgeUnk) { return; } ++this->mapExtraInfo.algorithmIterationNumber; @@ -51,7 +51,7 @@ namespace AI { && !this->heatMaps[candidateX][candidateY].isNotNearEdgeUnk && !this->heatMaps[candidateX][candidateY].destructionBasedPlacementCooldown && !this->heatMaps[candidateX][candidateY].treeCount - && (char)this->heatMaps[candidateX][candidateY].bouldersCount < 0 && !notInLargestArea) { + && (char)this->heatMaps[candidateX][candidateY].bouldersCount <= 0 && !notInLargestArea) { this->buildingApproriateGridXPosition = candidateX; this->buildingAppropriateGridYPosition = candidateY; return; diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 54e78b56c..4e3cac516 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37951,7 +37951,7 @@ SHC_3BB0A8C1_0x004EE830 | 76.7% | orig spills direction ptr SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending -SHC_3BB0A8C1_0x004EEBD0 | 97.7% | scheduling only +SHC_3BB0A8C1_0x004EEBD0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EECC0 | 0.0% | Pending From 01f0f747ba6bd8e764ff2be9f870e85aca9f4e7c Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 18:59:53 +0200 Subject: [PATCH 57/85] reimplement: SHC_3BB0A8C1_0x004EF0D0 37.06% register allocation differs (t --- src/OpenSHC/AI/AIVState/applyAIV.cpp | 10 ++++------ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/applyAIV.cpp b/src/OpenSHC/AI/AIVState/applyAIV.cpp index d28b9e700..0ed037e9a 100644 --- a/src/OpenSHC/AI/AIVState/applyAIV.cpp +++ b/src/OpenSHC/AI/AIVState/applyAIV.cpp @@ -78,22 +78,18 @@ namespace AI { BOOLEnum selected; do { selected = FALSE; + int index = startY * 100; if (startY >= 100) { break; } int step; MappersEnum mapper; - int index = startY * 100; for (int y = startY; y < 100; ++y) { for (int x = 0; x < 100; ++x, ++index) { if (this->isHandled[index]) { continue; } - if (selected) { - if (this->steps[0][index] != step) { - continue; - } - } else { + if (!selected) { mapper = MACRO_CALL_MEMBER(AIVState_Func::convertAIVBuildingTypeToCommandBuildingType, this)( (AIV::AIVBuildingType2)this->constructions[index]); step = this->steps[0][index]; @@ -103,6 +99,8 @@ namespace AI { if (y > startY) { startY = y; } + } else if (this->steps[0][index] != step) { + continue; } uint const realX = this->aivs[aivID].keepXOffset + x; uint const realY = this->aivs[aivID].keepYOffset + y; diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 4e3cac516..879e5c408 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37963,7 +37963,7 @@ SHC_3BB0A8C1_0x004EEF20 | 0.0% | Pending SHC_3BB0A8C1_0x004EF090 | 90.0% | Reimplemented -SHC_3BB0A8C1_0x004EF0D0 | 36.9% | Stack slots; 77% normalized +SHC_3BB0A8C1_0x004EF0D0 | 37.1% | register allocation differs (t SHC_3BB0A8C1_0x004EF130 | 0.0% | Pending From eeabafb391250fec36b9a62bd506f5a968a47d2b Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 20:14:47 +0200 Subject: [PATCH 58/85] reimplement: SHC_3BB0A8C1_0x004EE430 100.00% Reimplemented --- .../findAppropriateGridLocationForResourceTypeBuilding.cpp | 6 +++--- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp index dd9280e7c..238f13827 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForResourceTypeBuilding.cpp @@ -46,9 +46,9 @@ namespace AI { } int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; - ++this->mapExtraInfo.algorithmIterationNumber; - int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; this->heatMapXArray[0] = campX; + int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; + ++this->mapExtraInfo.algorithmIterationNumber; this->heatMapYArray[0] = campY; this->heatMaps[campX][campY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; this->heatMaps[campX][campY].algorithmVisitCountUnk = this->visitCount; @@ -83,7 +83,7 @@ namespace AI { if ((blockedCount == 0 || resourceType == AIGCRT_IRON) && blockedCount <= 4 && !this->heatMaps[gridX][gridY].structureCount && !this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown - && (owner == 0 + && (!this->heatMaps[gridX][gridY].tileOwnership || DAT_GameState::instance.mapAndTime.playerTeams[playerID] == DAT_GameState::instance.mapAndTime.playerTeams[owner])) { if (resourceType == AIGCRT_OIL) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 879e5c408..478152700 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37945,7 +37945,7 @@ SHC_3BB0A8C1_0x004EE140 | 62.6% | regalloc: orig anchors directi SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending -SHC_3BB0A8C1_0x004EE430 | 57.6% | regalloc: orig caches playerID +SHC_3BB0A8C1_0x004EE430 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EE830 | 76.7% | orig spills direction ptr From ebc8f1f02caff99b01251a5f428ce432742e2da3 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 20:20:58 +0200 Subject: [PATCH 59/85] reimplement: SHC_3BB0A8C1_0x004EE140 92.35% orig keeps -1 in ebp --- .../findAppropriateWoodCutterGridLocation.cpp | 48 +++++++++---------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp index addfa622a..9413e3508 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp @@ -40,10 +40,10 @@ namespace AI { } // only the four cardinal directions for (int direction = 0; direction < 8; direction += 2) { - uint const gridY - = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const gridY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { continue; } @@ -56,31 +56,27 @@ namespace AI { if (notInLargestArea >= 16 || this->heatMaps[gridX][gridY].destructionBasedPlacementCooldown) { continue; } - if (notInLargestArea >= 6) { - continue; - } - char const treeCount = (char)this->heatMaps[gridX][gridY].treeCount; - if (treeCount <= 0) { - continue; - } - ++foundCount; - int score = treeCount * 5 - this->visitCount * 3; - // prefer cells that were not used for a woodcutter recently - if (this->heatMaps[gridX][gridY].woodRelatedCountdownTo0) { - if (score > 0) { - score /= 2; - } else { - score *= 2; + if (notInLargestArea < 6 && (char)this->heatMaps[gridX][gridY].treeCount > 0) { + ++foundCount; + int score + = (char)this->heatMaps[gridX][gridY].treeCount * 5 - this->visitCount * 3; + // prefer cells that were not used for a woodcutter recently + if (this->heatMaps[gridX][gridY].woodRelatedCountdownTo0) { + if (score > 0) { + score /= 2; + } else { + score *= 2; + } + } + if (score > bestScore) { + bestX = gridX; + bestY = gridY; + bestScore = score; + } + if (foundCount > 10 || (foundCount > 5 && this->visitCount > 20) + || (foundCount > 0 && this->visitCount > 30)) { + break; } - } - if (score > bestScore) { - bestX = gridX; - bestY = gridY; - bestScore = score; - } - if (foundCount > 10 || (foundCount > 5 && this->visitCount > 20) - || (foundCount > 0 && this->visitCount > 30)) { - break; } this->heatMaps[gridX][gridY].algorithmVisitCountUnk = this->visitCount + 1; this->heatMapXArray[this->algBIndex] = gridX; diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 478152700..d95f9eeb7 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37941,7 +37941,7 @@ SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented SHC_3BB0A8C1_0x004EDDF0 | 90.7% | direction ptr anchored 4 low -SHC_3BB0A8C1_0x004EE140 | 62.6% | regalloc: orig anchors directi +SHC_3BB0A8C1_0x004EE140 | 92.3% | orig keeps -1 in ebp SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending From 2a761986193a75cd5fdbaf81ba83b71bb3a5fa3c Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 20:22:16 +0200 Subject: [PATCH 60/85] reimplement: SHC_3BB0A8C1_0x004EF8C0 94.39% only index/y init store order --- src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp | 7 ++++--- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp b/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp index 9189251d3..0839c0ad7 100644 --- a/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp +++ b/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp @@ -42,17 +42,18 @@ namespace AI { Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, DAT_BuildingsState::ptr)(mapper)]; ++this->buildingCounter; - uint const realY = this->aivs[playerID].keepYOffset + y; uint const realX = this->aivs[playerID].keepXOffset + x; + uint const realY = this->aivs[playerID].keepYOffset + y; if (realX > 399 || realY > 399 || !DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[realY * 400 + realX]) { continue; } - int const rowTile = DAT_ViewportRenderState::instance.translationMatrix[realY].addXgetTile; + int const tile + = DAT_ViewportRenderState::instance.translationMatrix[realY].addXgetTile + realX; MACRO_CALL_MEMBER(Map::TileMapState_Func::storeMinAndMaxHeightOfArea, DAT_TileMapState::ptr)( realX, realY, 1); if (MACRO_CALL_MEMBER(Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, DAT_TileMapState::ptr)( - rowTile + realX, 0, mapper, 0)) { + tile, 0, mapper, 0)) { this->isHandled[index] = 1; // steps is accessed as a flat 100 * 100 array if (this->steps[0][index] < minSteps) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index d95f9eeb7..b0b8b152f 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37971,7 +37971,7 @@ SHC_3BB0A8C1_0x004EF150 | 0.0% | Pending SHC_3BB0A8C1_0x004EF450 | 0.0% | Pending -SHC_3BB0A8C1_0x004EF8C0 | 60.2% | Register alloc; 93% normalized +SHC_3BB0A8C1_0x004EF8C0 | 94.4% | only index/y init store order SHC_3BB0A8C1_0x004EF900 | 0.0% | Pending From ea05334fbd2023ded282023a3a2f0499a10b6103 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 20:26:03 +0200 Subject: [PATCH 61/85] reimplement: SHC_3BB0A8C1_0x004EE830 100.00% Reimplemented --- .../AI/AIVState/recomputeAIAvailableGridTiles.cpp | 14 +++++++------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp index 67bd628c0..6810e985f 100644 --- a/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeAIAvailableGridTiles.cpp @@ -24,9 +24,9 @@ namespace AI { this->algBIndex = 1; this->algAIndex = 0; int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; - ++this->mapExtraInfo.algorithmIterationNumber; - int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; this->heatMapXArray[0] = campX; + int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; + ++this->mapExtraInfo.algorithmIterationNumber; this->heatMapYArray[0] = campY; this->heatMaps[campX][campY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; this->heatMaps[campX][campY].algorithmVisitCountUnk = this->visitCount; @@ -53,13 +53,13 @@ namespace AI { } int const impassable = (char)this->heatMaps[gridX][gridY].impassableCount; this->heatMaps[gridX][gridY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; - int const notInLargestArea = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount; - int const blockedCount = notInLargestArea - impassable; + int const blockedCount + = (char)this->heatMaps[gridX][gridY].tilesNotPartOfLargestAreaCount - impassable; if (blockedCount >= 16) { continue; } if ((char)this->heatMaps[gridX][gridY].oilCount > 2 - && (char)this->heatMaps[gridX][gridY].marshCount >= 10 && notInLargestArea == impassable) { + && (char)this->heatMaps[gridX][gridY].marshCount >= 10 && blockedCount == 0) { if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { @@ -71,7 +71,7 @@ namespace AI { < 12) { ++DAT_GameState::instance.playerDataArray[playerID].availableIronGridTiles; } - } else if ((char)this->heatMaps[gridX][gridY].bouldersCount >= 8 && notInLargestArea == impassable) { + } else if ((char)this->heatMaps[gridX][gridY].bouldersCount >= 8 && blockedCount == 0) { if (this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 40) { @@ -84,7 +84,7 @@ namespace AI { ++DAT_GameState::instance.playerDataArray[playerID].availableTreeGridTiles; } } else if ((char)this->heatMaps[gridX][gridY].oasisScrubCount >= 25 - && (char)this->heatMaps[gridX][gridY].oasisThickScrubCount >= 14 && notInLargestArea == impassable + && (char)this->heatMaps[gridX][gridY].oasisThickScrubCount >= 14 && blockedCount == 0 && this->heatMaps[gridX][gridY].highestTerrainHeight - this->heatMaps[gridX][gridY].lowestTerrainHeight < 12) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index b0b8b152f..d7ae2db80 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37947,7 +37947,7 @@ SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending SHC_3BB0A8C1_0x004EE430 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EE830 | 76.7% | orig spills direction ptr +SHC_3BB0A8C1_0x004EE830 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EE920 | 0.0% | Pending From 3a55d09049ea1c22791e9de6f3ee872552391051 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 20:28:21 +0200 Subject: [PATCH 62/85] reimplement: SHC_3BB0A8C1_0x004F0E80 85.96% fixed init fields (lowestTerra --- src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp | 8 ++++---- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp index fdc65af11..0d274cdf2 100644 --- a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp @@ -28,8 +28,8 @@ namespace AI { MACRO_CALL_MEMBER(AIVState_Func::clearTheHeatmaps, this)(); for (int y = 0; y < 80; ++y) { for (int x = 0; x < 80; ++x) { - this->heatMaps[x][y].oasisThickScrubCount = 250; - this->heatMaps[x][y].impassableCount = 1; + this->heatMaps[x][y].lowestTerrainHeight = 250; + this->heatMaps[x][y].isNotNearEdgeUnk = 1; } } this->mapExtraInfo.largestSeparateArea @@ -84,8 +84,8 @@ namespace AI { if (height > this->heatMaps[gridX][gridY].highestTerrainHeight) { this->heatMaps[gridX][gridY].highestTerrainHeight = (byte)height; } - if (this->heatMaps[gridX][gridY].lowestTerrainHeight + 12 - < (uint)this->heatMaps[gridX][gridY].highestTerrainHeight) { + if ((int)this->heatMaps[gridX][gridY].highestTerrainHeight + > (int)this->heatMaps[gridX][gridY].lowestTerrainHeight + 12) { this->heatMaps[gridX][gridY].terrainDiffHigherThan12 = 1; } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index d7ae2db80..f4f4928a4 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37995,7 +37995,7 @@ SHC_3BB0A8C1_0x004F0060 | 36.4% | Style pass, asm differs SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending -SHC_3BB0A8C1_0x004F0E80 | 85.1% | loop structure differs +SHC_3BB0A8C1_0x004F0E80 | 86.0% | fixed init fields (lowestTerra SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending From 4cbeba56b7c692fc691461cc7f29c7ab42fcaaf8 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 20:29:17 +0200 Subject: [PATCH 63/85] reimplement: SHC_3BB0A8C1_0x004EDDF0 100.00% Reimplemented --- .../AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp | 6 +++--- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp index 5ecfc16ad..0c5dcf02b 100644 --- a/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp +++ b/src/OpenSHC/AI/AIVState/findSuitableFarmLocationAndPlaceFarm.cpp @@ -42,8 +42,8 @@ namespace AI { } int const campX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5; this->heatMapXArray[0] = campX; - ++this->mapExtraInfo.algorithmIterationNumber; int const campY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5; + ++this->mapExtraInfo.algorithmIterationNumber; this->heatMapYArray[0] = campY; this->heatMaps[campX][campY].algorithmIterationNumber = this->mapExtraInfo.algorithmIterationNumber; this->heatMaps[campX][campY].algorithmVisitCountUnk = this->visitCount; @@ -57,10 +57,10 @@ namespace AI { } // only the four cardinal directions for (int direction = 0; direction < 8; direction += 2) { - uint const gridY - = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; uint const gridX = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.xOffset + x; + uint const gridY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction].int_.yOffset + y; if (this->heatMaps[x][y].isNotNearEdgeUnk && (gridX > 79 || gridY > 79)) { continue; } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index f4f4928a4..55f7d3940 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37939,7 +37939,7 @@ SHC_3BB0A8C1_0x004EDCB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented -SHC_3BB0A8C1_0x004EDDF0 | 90.7% | direction ptr anchored 4 low +SHC_3BB0A8C1_0x004EDDF0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EE140 | 92.3% | orig keeps -1 in ebp From 77c76bad6309344ed0c6c78881ec5de1f48a8e8f Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 21:58:11 +0200 Subject: [PATCH 64/85] reimplement: SHC_3BB0A8C1_0x004EF0D0 65.73% orig spills y in loop 1, we ke --- src/OpenSHC/AI/AIVState/applyAIV.cpp | 14 +++++++------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/applyAIV.cpp b/src/OpenSHC/AI/AIVState/applyAIV.cpp index 0ed037e9a..fa638dca4 100644 --- a/src/OpenSHC/AI/AIVState/applyAIV.cpp +++ b/src/OpenSHC/AI/AIVState/applyAIV.cpp @@ -23,9 +23,11 @@ namespace AI { MACRO_CALL_MEMBER(AIVState_Func::rotateAIV, this)(this->aivs[aivID].keepOrientation); // single buildings and the keep, steps is accessed as a flat 100 * 100 array + int x; + int y; int index = 0; - for (int y = 0; y < 100; ++y) { - for (int x = 0; x < 100; ++x, ++index) { + for (y = 0; y < 100; ++y) { + for (x = 0; x < 100; ++x, ++index) { if (this->isHandled[index]) { continue; } @@ -78,14 +80,14 @@ namespace AI { BOOLEnum selected; do { selected = FALSE; - int index = startY * 100; + index = startY * 100; if (startY >= 100) { break; } int step; MappersEnum mapper; - for (int y = startY; y < 100; ++y) { - for (int x = 0; x < 100; ++x, ++index) { + for (y = startY; y < 100; ++y) { + for (x = 0; x < 100; ++x, ++index) { if (this->isHandled[index]) { continue; } @@ -123,8 +125,6 @@ namespace AI { } while (selected); // rotate the unit locations and copy them to the player - int y; - int x; for (int unitType = 0; unitType < 22; ++unitType) { DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[unitType] = 0; if (unitType == AIVUT_PIKEMAN || unitType == AIVUT_SWORDSMAN || unitType == AIVUT_ARABIAN_SWORDSMAN) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 55f7d3940..8c69a37c5 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37963,7 +37963,7 @@ SHC_3BB0A8C1_0x004EEF20 | 0.0% | Pending SHC_3BB0A8C1_0x004EF090 | 90.0% | Reimplemented -SHC_3BB0A8C1_0x004EF0D0 | 37.1% | register allocation differs (t +SHC_3BB0A8C1_0x004EF0D0 | 65.7% | orig spills y in loop 1, we ke SHC_3BB0A8C1_0x004EF130 | 0.0% | Pending From bc7aad179258bb09b6664e7ef7d456c6506fcb60 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 22:27:57 +0200 Subject: [PATCH 65/85] reimplement: SHC_3BB0A8C1_0x004F0060 36.43% chain register naming; orig sp --- src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp index 0d4648201..32796a3ea 100644 --- a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp +++ b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp @@ -366,7 +366,7 @@ namespace AI { } for (int step = 0; step <= 50; ++step) { - if (this->aivs[aivID].currentStepGoal < step) { + if (step > this->aivs[aivID].currentStepGoal) { return FALSE; } if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_disabled) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 8c69a37c5..2d0357968 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37991,7 +37991,7 @@ SHC_3BB0A8C1_0x004EFE30 | 91.1% | Reimplemented SHC_3BB0A8C1_0x004EFED0 | 93.8% | Reimplemented -SHC_3BB0A8C1_0x004F0060 | 36.4% | Style pass, asm differs +SHC_3BB0A8C1_0x004F0060 | 36.4% | chain register naming; orig sp SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending From ce0adc7d84be3ed270b3b0c3ea04e60353ada589 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 22:50:23 +0200 Subject: [PATCH 66/85] reimplement: SHC_3BB0A8C1_0x004ECBC0 64.71% BOOLEnum flags; gap is MSVC in --- .../AI/AIVState/hostChecksLobbyAIVAvailability.cpp | 8 ++++---- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp b/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp index 92299fd13..b43eeee48 100644 --- a/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp +++ b/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp @@ -25,7 +25,7 @@ namespace AI { int const localPlayer = DAT_GameSynchronyState::instance.currentPlayerSlotID; DAT_GameCore::instance.numOfAIsWithCastleUnk = 0; for (int aiIndex = 0; aiIndex < 16; ++aiIndex) { - bool available = true; + BOOLEnum available = TRUE; for (int player = 1; player < 9; ++player) { if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1) { continue; @@ -44,7 +44,7 @@ namespace AI { .DAT_ReceivedAIVFileAvailabilityPerAIArray[player][aiIndex]; } if (count == 0) { - available = false; + available = FALSE; break; } } @@ -61,11 +61,11 @@ namespace AI { || DAT_GameSynchronyState::instance.currentAIArray[player] == 0) { continue; } - bool hasAIV = false; + BOOLEnum hasAIV = FALSE; for (int i = 0; i < DAT_GameCore::instance.numOfAIsWithCastleUnk; ++i) { if (DAT_GameSynchronyState::instance.currentAIArray[player] == DAT_GameCore::instance.arrayOfLordIdsWithAIVsUnk[i]) { - hasAIV = true; + hasAIV = TRUE; break; } } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 2d0357968..a9be612fb 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37873,7 +37873,7 @@ SHC_3BB0A8C1_0x004ECAB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECAD0 | 98.6% | Reimplemented -SHC_3BB0A8C1_0x004ECBC0 | 64.7% | Register allocation +SHC_3BB0A8C1_0x004ECBC0 | 64.7% | BOOLEnum flags; gap is MSVC in SHC_3BB0A8C1_0x004ECCA0 | 0.0% | Pending From 1956f6bbebce32c3dcaf2fe4b0cd21efb020d70f Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 23:17:56 +0200 Subject: [PATCH 67/85] reimplement: SHC_3BB0A8C1_0x004ED410 90.92% orig branches to a shared retu --- .../AI/AIVState/aiPlaceAIVBuilding.cpp | 21 ++++++++++--------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp index 1cea95231..a17220d09 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp @@ -19,6 +19,8 @@ namespace AI { BOOLEnum AIVState::aiPlaceAIVBuilding(PlayerID playerID, int step, int isPausing) { int const aivID = DAT_GameState::instance.playerDataArray[playerID].aivID; + int x; + int y; MappersEnum const mapper = (MappersEnum)this->aivs[aivID].aivBuildingSteps[step].buildingType; // when pausing, only hovels are built @@ -85,8 +87,8 @@ namespace AI { for (int i = 0; i < this->aivs[aivID].aivBuildingSteps[step].quantity; ++i) { int const tile = this->aivs[aivID].locationsArray[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + i]; - int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; - int const x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, x, y, Commands::M_MAPPER_PITCH_DITCH, MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, @@ -122,8 +124,8 @@ namespace AI { for (int i = 0; i < this->aivs[aivID].aivBuildingSteps[step].quantity; ++i) { int const tile = this->aivs[aivID].locationsArray[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + i]; - uint const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; - uint const x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( playerID, x, y, mapper, 1); MACRO_CALL_MEMBER(Map::TileMapState_Func::placeDefensiveStructureTile, DAT_TileMapState::ptr)( @@ -146,10 +148,9 @@ namespace AI { --this->aivs[aivID].aivBuildingSteps[step].wait; return FALSE; } - uint const y - = DAT_ViewportRenderState::instance - .tileTranslationMatrix_YComponent[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; - uint const x = this->aivs[aivID].aivBuildingSteps[step].location.tile.tile + y = DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; + x = this->aivs[aivID].aivBuildingSteps[step].location.tile.tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( playerID, x, y, mapper, 1); @@ -183,8 +184,8 @@ namespace AI { } int const tile = this->aivs[aivID].aivBuildingSteps[step].location.tile.tile; - int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; - int const x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; int orientation = 15; if (mapper == Commands::M_MAPPER_BARRACKS_ARAB || mapper == Commands::M_MAPPER_BARRACKS_EURO || mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index a9be612fb..9cd10a41f 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37931,7 +37931,7 @@ SHC_3BB0A8C1_0x004ED300 | 96.2% | Reimplemented SHC_3BB0A8C1_0x004ED3B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004ED410 | 85.3% | Stack slot assignment +SHC_3BB0A8C1_0x004ED410 | 90.9% | orig branches to a shared retu SHC_3BB0A8C1_0x004EDC90 | 85.7% | Reimplemented From ce387957e20dde94b10414ab6c2ebc32fd717397 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 23:17:56 +0200 Subject: [PATCH 68/85] reimplement: SHC_3BB0A8C1_0x004F0E80 87.76% rest is scheduling + a hoisted --- src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp | 32 ++++++------------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp index 0d274cdf2..930ebd328 100644 --- a/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp +++ b/src/OpenSHC/AI/AIVState/recomputeHeatmaps.cpp @@ -141,16 +141,12 @@ namespace AI { continue; } if (DAT_TileMapState::instance.LogicLayer[tile] & L_ROCKY) { - ++this->heatMaps[(tile - - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance - .tileTranslationMatrix_YComponent[tile]] - .addXgetTile) - / 5][DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5] - .impassableCount; + int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const gridX = (tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile) / 5; + ++this->heatMaps[gridX][y / 5].impassableCount; } if (DAT_TileMapState::instance.LogicLayer[tile] & L_TREE) { - short const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; int const gridX = (tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile) / 5; int const gridY = y / 5; int const treeID = (short)DAT_TileMapState::instance.OrganismLayer[tile]; @@ -168,26 +164,18 @@ namespace AI { this->mapExtraInfo.totalWoodAvailable += 3; } } else if (DAT_TileMapState::instance.BuildingLayer[tile] != 0) { - int const gridX = (tile - - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance - .tileTranslationMatrix_YComponent[tile]] - .addXgetTile) - / 5; - int const gridY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5; + int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const gridX = (tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile) / 5; + int const gridY = y / 5; ++this->heatMaps[gridX][gridY].structureCount; this->heatMaps[gridX][gridY].tileOwnership = (byte)DAT_BuildingsState::instance .buildings[(short)DAT_TileMapState::instance.BuildingLayer[tile]] .owner; } else if (DAT_TileMapState::instance.LogicLayer[tile] & L_WALL_OR_GATEHOUSE) { - int const gridX = (tile - - DAT_ViewportRenderState::instance - .translationMatrix[DAT_ViewportRenderState::instance - .tileTranslationMatrix_YComponent[tile]] - .addXgetTile) - / 5; - int const gridY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile] / 5; + int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const gridX = (tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile) / 5; + int const gridY = y / 5; ++this->heatMaps[gridX][gridY].structureCount; this->heatMaps[gridX][gridY].tileOwnership = (DAT_TileMapState::instance.WallOwnerLayer[tile] & 7) + 1; } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 9cd10a41f..da93ebff2 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37995,7 +37995,7 @@ SHC_3BB0A8C1_0x004F0060 | 36.4% | chain register naming; orig sp SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending -SHC_3BB0A8C1_0x004F0E80 | 86.0% | fixed init fields (lowestTerra +SHC_3BB0A8C1_0x004F0E80 | 87.8% | rest is scheduling + a hoisted SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending From aa42e07bfe749169f3e25194f8dbadf07d79f544 Mon Sep 17 00:00:00 2001 From: Gynt Date: Sat, 26 Sep 2026 23:26:07 +0200 Subject: [PATCH 69/85] reimplement: SHC_3BB0A8C1_0x004EEE10 88.21% loop rotation; orig rematerial --- .../AI/AIVState/findAppropriateGridLocationForBuilding.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp index 9135bc373..21e6ae516 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp @@ -29,9 +29,9 @@ namespace AI { uint candidateX; uint candidateY; for (;;) { + int freeCount = 0; int const x = this->heatMapXArray[this->algAIndex]; int const y = this->heatMapYArray[this->algAIndex]; - int freeCount = 0; if (!this->heatMaps[x][y].tilesNotPartOfLargestAreaCount) { freeCount = 1; } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index da93ebff2..3f1a0cda1 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37955,7 +37955,7 @@ SHC_3BB0A8C1_0x004EEBD0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EECC0 | 0.0% | Pending -SHC_3BB0A8C1_0x004EEE10 | 87.5% | orig spills fewer locals +SHC_3BB0A8C1_0x004EEE10 | 88.2% | loop rotation; orig rematerial SHC_3BB0A8C1_0x004EEEC0 | 0.0% | Pending From ccf333decb182849136159a715aba55aa53820b6 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:48 +0200 Subject: [PATCH 70/85] reimplement: SHC_3BB0A8C1_0x004EF8C0 100.00% Reimplemented Co-Authored-By: Claude Fable 5.1 --- src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp | 6 ++---- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp b/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp index 0839c0ad7..54dbba337 100644 --- a/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp +++ b/src/OpenSHC/AI/AIVState/computeAIVPlacementFit.cpp @@ -20,8 +20,7 @@ namespace AI { DAT_TileMapState::instance.buildingPlacementProperty_6 = 0; DAT_TileMapState::instance.buildingPlacementProperty_5 = 0; DAT_TileMapState::instance.buildingPlacementProperty_3 = 0; - int index = 0; - for (int y = 0; y < 100; ++y) { + for (int y = 0, index = 0; y < 100; ++y) { for (int x = 0; x < 100; ++x, ++index) { int const type = this->constructions[index]; if (type == 0) { @@ -48,8 +47,7 @@ namespace AI { || !DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[realY * 400 + realX]) { continue; } - int const tile - = DAT_ViewportRenderState::instance.translationMatrix[realY].addXgetTile + realX; + int const tile = DAT_ViewportRenderState::instance.translationMatrix[realY].addXgetTile + realX; MACRO_CALL_MEMBER(Map::TileMapState_Func::storeMinAndMaxHeightOfArea, DAT_TileMapState::ptr)( realX, realY, 1); if (MACRO_CALL_MEMBER(Map::TileMapState_Func::isBuildingPlacementAllowedAtTile, DAT_TileMapState::ptr)( diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 3f1a0cda1..df41b8402 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37971,7 +37971,7 @@ SHC_3BB0A8C1_0x004EF150 | 0.0% | Pending SHC_3BB0A8C1_0x004EF450 | 0.0% | Pending -SHC_3BB0A8C1_0x004EF8C0 | 94.4% | only index/y init store order +SHC_3BB0A8C1_0x004EF8C0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EF900 | 0.0% | Pending From bbc1a1f51aac01528af439f0c6fe32497866bc28 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:48 +0200 Subject: [PATCH 71/85] reimplement: SHC_3BB0A8C1_0x004F13F0 100.00% Reimplemented Co-Authored-By: Claude Fable 5.1 --- src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp b/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp index 777dcc9ab..486724816 100644 --- a/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp +++ b/src/OpenSHC/AI/AIVState/selectBestAIVwithRandomStart.cpp @@ -16,9 +16,9 @@ namespace AI { // FUNCTION: STRONGHOLDCRUSADER 0x004F13F0 void AIVState::selectBestAIVwithRandomStart(int aivID) { - uint castleID = (byte)SEC_RNG::instance.currentNumber2 & 7; int bestFit = 0; int bestCastleID = -1; + uint castleID = (byte)SEC_RNG::instance.currentNumber2 & 7; MACRO_CALL_MEMBER(Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( sizeof(this->isHandled), 0, this->isHandled); diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index df41b8402..c520ead7c 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38001,7 +38001,7 @@ SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending SHC_3BB0A8C1_0x004F13E0 | 83.3% | Reimplemented -SHC_3BB0A8C1_0x004F13F0 | 88.8% | orig literal +SHC_3BB0A8C1_0x004F13F0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F14F0 | 91.5% | Reimplemented From 8f8a3c25ae43da711e69b25569327a2ab6b7c920 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:49 +0200 Subject: [PATCH 72/85] reimplement: SHC_3BB0A8C1_0x004ECF70 100.00% Reimplemented Co-Authored-By: Claude Fable 5.1 --- src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp | 4 ++-- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp b/src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp index 0ee867452..06041b5a3 100644 --- a/src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp +++ b/src/OpenSHC/AI/AIVState/setKeepOffsetAndOrientation.cpp @@ -11,9 +11,9 @@ namespace AI { void AIVState::setKeepOffsetAndOrientation(int aivID, int keepX, int keepY) { // the AIV is 100x100 tiles with the keep at 43/43 - this->aivs[aivID].keepXOffset = keepX - 43; - this->aivs[aivID].keepY = keepY; this->aivs[aivID].keepX = keepX; + this->aivs[aivID].keepY = keepY; + this->aivs[aivID].keepXOffset = keepX - 43; this->aivs[aivID].keepYOffset = keepY - 43; // orient the castle towards the map center MACRO_CALL_MEMBER(Map::Navigation::DirectionAlgorithmState_Func::calculatePreferredRelativeOrientation, diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index c520ead7c..d781f46f1 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37889,7 +37889,7 @@ SHC_3BB0A8C1_0x004ECE60 | 97.8% | Reimplemented SHC_3BB0A8C1_0x004ECEF0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004ECF70 | 87.1% | Reimplemented +SHC_3BB0A8C1_0x004ECF70 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECFE0 | 100.0% | Reimplemented From 5eebadf3c3a7b41ad08c52ef6a49e4c4adf92c55 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:49 +0200 Subject: [PATCH 73/85] reimplement: SHC_3BB0A8C1_0x004F14F0 100.00% Reimplemented Co-Authored-By: Claude Fable 5.1 --- .../AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp | 5 +++-- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp b/src/OpenSHC/AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp index 753e15298..68b4a4304 100644 --- a/src/OpenSHC/AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp +++ b/src/OpenSHC/AI/AIVState/tryPlaceAIVAndReturnFitPercentage.cpp @@ -33,10 +33,11 @@ namespace AI { } this->aivs[aivID].aivSubType = 1; // the counters are declared unsigned but used as signed values - if ((int)this->buildingCounter == 0) { + int const buildingCounter = this->buildingCounter; + if (buildingCounter == 0) { return 100; } - return ((int)this->buildingCounter - (int)this->invalidTileCounter) * 100 / (int)this->buildingCounter; + return (buildingCounter - (int)this->invalidTileCounter) * 100 / buildingCounter; } } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index d781f46f1..1fa96a032 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38003,7 +38003,7 @@ SHC_3BB0A8C1_0x004F13E0 | 83.3% | Reimplemented SHC_3BB0A8C1_0x004F13F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F14F0 | 91.5% | Reimplemented +SHC_3BB0A8C1_0x004F14F0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F15C0 | 63.1% | Register allocation From 4c607dbfacab534199d917721b880dc611c5b4e2 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:49 +0200 Subject: [PATCH 74/85] reimplement: SHC_3BB0A8C1_0x004EE140 100.00% Reimplemented Co-Authored-By: Claude Fable 5.1 --- .../AIVState/findAppropriateWoodCutterGridLocation.cpp | 9 ++++----- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp index 9413e3508..37db4676a 100644 --- a/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp +++ b/src/OpenSHC/AI/AIVState/findAppropriateWoodCutterGridLocation.cpp @@ -10,6 +10,9 @@ namespace AI { // FUNCTION: STRONGHOLDCRUSADER 0x004EE140 void AIVState::findAppropriateWoodCutterGridLocation(int playerID) { + int bestScore = -100; + uint bestX = -1; + uint bestY = -1; this->algAIndex = 0; int foundCount = 0; this->visitCount = 1; @@ -20,9 +23,6 @@ namespace AI { this->heatMapYArray[0] = campY; this->buildingApproriateGridXPosition = -1; this->buildingAppropriateGridYPosition = -1; - int bestScore = -100; - uint bestX = -1; - uint bestY = -1; if (DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk > 0) { --DAT_GameState::instance.playerDataArray[playerID].algoTreeCooldownUnk; return; @@ -58,8 +58,7 @@ namespace AI { } if (notInLargestArea < 6 && (char)this->heatMaps[gridX][gridY].treeCount > 0) { ++foundCount; - int score - = (char)this->heatMaps[gridX][gridY].treeCount * 5 - this->visitCount * 3; + int score = (char)this->heatMaps[gridX][gridY].treeCount * 5 - this->visitCount * 3; // prefer cells that were not used for a woodcutter recently if (this->heatMaps[gridX][gridY].woodRelatedCountdownTo0) { if (score > 0) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 1fa96a032..e5c3770d1 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37941,7 +37941,7 @@ SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented SHC_3BB0A8C1_0x004EDDF0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EE140 | 92.3% | orig keeps -1 in ebp +SHC_3BB0A8C1_0x004EE140 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EE230 | 0.0% | Pending From 44981b5dff23dae1910541a25741438fd82705f6 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:50 +0200 Subject: [PATCH 75/85] reimplement: SHC_3BB0A8C1_0x004F15C0 98.51% init stores sit after the aivID branch Co-Authored-By: Claude Fable 5.1 --- src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp | 3 +-- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp b/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp index 49da92bc7..a0c2c6e69 100644 --- a/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp +++ b/src/OpenSHC/AI/AIVState/aiDecideOnNewBuildings.cpp @@ -79,11 +79,10 @@ namespace AI { if (DAT_GameState::instance.playerDataArray[playerID].aivCurrentPause != 0) { if (stepIncrement > 1) { DAT_GameState::instance.playerDataArray[playerID].aivCurrentPause = 0; - isPausing = 1; } else { --DAT_GameState::instance.playerDataArray[playerID].aivCurrentPause; - isPausing = 1; } + isPausing = 1; } else { int const pauseStep = DAT_GameState::instance.playerDataArray[playerID] diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index e5c3770d1..57b39f28e 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38005,7 +38005,7 @@ SHC_3BB0A8C1_0x004F13F0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F14F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F15C0 | 63.1% | Register allocation +SHC_3BB0A8C1_0x004F15C0 | 98.5% | init stores sit after the aivID branch SHC_3BB0A8C1_0x004F1860 | 79.7% | orig compares member directly From 1ea712e332f7f5eeeacdcb321b07b01de688bfaf Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:50 +0200 Subject: [PATCH 76/85] reimplement: SHC_3BB0A8C1_0x004F1860 90.83% orig has a redundant cmp eax,eax Co-Authored-By: Claude Fable 5.1 --- .../updateBuildingsStateAndUpdateAIBuildingDecisions.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp b/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp index 37bc8fb5c..0c208ec57 100644 --- a/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp +++ b/src/OpenSHC/AI/AIVState/updateBuildingsStateAndUpdateAIBuildingDecisions.cpp @@ -26,8 +26,8 @@ namespace AI { } if (DAT_GameState::instance.mapAndTime.startOfDay) { - ++this->mapExtraInfo.algBuildingPlacementNudge; this->counter = 0; + ++this->mapExtraInfo.algBuildingPlacementNudge; if (this->mapExtraInfo.algBuildingPlacementNudge >= 31) { this->mapExtraInfo.algBuildingPlacementNudge = 0; } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 57b39f28e..1bf6920ed 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -38007,7 +38007,7 @@ SHC_3BB0A8C1_0x004F14F0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F15C0 | 98.5% | init stores sit after the aivID branch -SHC_3BB0A8C1_0x004F1860 | 79.7% | orig compares member directly +SHC_3BB0A8C1_0x004F1860 | 90.8% | orig has a redundant cmp eax,eax SHC_3BB0A8C1_0x004F1A60 | 0.0% | Pending From 2e4b6afd241b9e579b27ab244e522ca9df698eae Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:51 +0200 Subject: [PATCH 77/85] reimplement: SHC_3BB0A8C1_0x004ECBC0 68.63% orig branch shape; registers differ Co-Authored-By: Claude Fable 5.1 --- .../hostChecksLobbyAIVAvailability.cpp | 19 +++++++++++-------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp b/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp index b43eeee48..b8deb1d27 100644 --- a/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp +++ b/src/OpenSHC/AI/AIVState/hostChecksLobbyAIVAvailability.cpp @@ -30,20 +30,23 @@ namespace AI { if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] == -1) { continue; } - int count; if (DAT_GameSynchronyState::instance.DAT_ReceivedAIVFileAvailabilityPerAIArray[player][0] == -1) { // nothing received yet if (player != localPlayer) { continue; } - count = this->aivFileAvailabilityPerAIArray[aiIndex]; + if (this->aivFileAvailabilityPerAIArray[aiIndex] == 0) { + available = FALSE; + break; + } } else if (player == localPlayer) { - count = this->aivFileAvailabilityPerAIArray[aiIndex]; - } else { - count = DAT_GameSynchronyState::instance - .DAT_ReceivedAIVFileAvailabilityPerAIArray[player][aiIndex]; - } - if (count == 0) { + if (this->aivFileAvailabilityPerAIArray[aiIndex] == 0) { + available = FALSE; + break; + } + } else if (DAT_GameSynchronyState::instance + .DAT_ReceivedAIVFileAvailabilityPerAIArray[player][aiIndex] + == 0) { available = FALSE; break; } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 1bf6920ed..41701ecd8 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37873,7 +37873,7 @@ SHC_3BB0A8C1_0x004ECAB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECAD0 | 98.6% | Reimplemented -SHC_3BB0A8C1_0x004ECBC0 | 64.7% | BOOLEnum flags; gap is MSVC in +SHC_3BB0A8C1_0x004ECBC0 | 68.6% | orig branch shape; registers differ SHC_3BB0A8C1_0x004ECCA0 | 0.0% | Pending From 2f2382f57763dbe253415b79df0ea04117c6985b Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:51 +0200 Subject: [PATCH 78/85] reimplement: SHC_3BB0A8C1_0x004F0060 36.68% aiType - 1 chain; registers differ Co-Authored-By: Claude Fable 5.1 --- .../AI/AIVState/executeDefaultCastleAIV.cpp | 194 +++++++++--------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 99 insertions(+), 97 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp index 32796a3ea..0377e8069 100644 --- a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp +++ b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp @@ -5,6 +5,7 @@ #include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" #include "OpenSHC/Map/TileMapState.func.hpp" #include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeA.hpp" #include "OpenSHC/Globals/DAT_AICState.hpp" #include "OpenSHC/Globals/DAT_AIVState.hpp" @@ -22,6 +23,7 @@ namespace AI { BOOLEnum AIVState::executeDefaultCastleAIV(PlayerID playerID, int isPausing) { int const aivID = DAT_GameState::instance.playerDataArray[playerID].aivID; + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; int stockpiles = 0; int hovels = 0; int granaries = 1; @@ -43,12 +45,14 @@ namespace AI { int engineersGuilds = 0; int tunnelersGuilds = 0; int stables = 0; - if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_RAT) { + if (aiType == AITA_RAT) { stockpiles = 0; hovels = 3; armories = 1; barracks = 1; mercenaryPosts = 0; + cathedrals = 0; + chapels = 0; inns = 0; breweries = 0; bakeries = 0; @@ -58,57 +62,56 @@ namespace AI { tanners = 0; blacksmiths = 0; engineersGuilds = 0; - chapels = 0; armourers = 0; - cathedrals = 0; stables = 0; tunnelersGuilds = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_SNAKE) { - mercenaryPosts = 1; - tanners = 0; - blacksmiths = 0; - poleturners = 2; + } else if (aiType == AITA_SNAKE) { stockpiles = 2; - inns = 0; - mills = 0; - bakeries = 0; - breweries = 0; + hovels = 4; armories = 1; barracks = 1; - hovels = 4; - engineersGuilds = 1; - fletchers = 2; + mercenaryPosts = 1; + cathedrals = 0; chapels = 0; + inns = 0; + breweries = 0; + bakeries = 0; + mills = 0; + poleturners = 2; + fletchers = 2; + tanners = 0; + blacksmiths = 0; + engineersGuilds = 1; armourers = 0; - cathedrals = 0; stables = 0; tunnelersGuilds = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_PIG) { + } else if (aiType == AITA_PIG) { + stockpiles = 2; + hovels = 4; + armories = 1; + barracks = 1; mercenaryPosts = 0; + cathedrals = 0; + chapels = 0; + inns = 0; + breweries = 0; + bakeries = 0; + mills = 0; poleturners = 0; + fletchers = 2; tanners = 2; blacksmiths = 2; - stockpiles = 2; - inns = 0; - mills = 0; - bakeries = 0; - breweries = 0; - armories = 1; - barracks = 1; - hovels = 4; engineersGuilds = 1; - fletchers = 2; - chapels = 0; armourers = 0; - cathedrals = 0; stables = 0; tunnelersGuilds = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_WOLF) { + } else if (aiType == AITA_WOLF) { stockpiles = 3; hovels = 5; armories = 1; barracks = 1; mercenaryPosts = 1; + cathedrals = 0; chapels = 2; inns = 2; breweries = 3; @@ -118,52 +121,51 @@ namespace AI { fletchers = 2; tanners = 1; blacksmiths = 2; - armourers = 1; engineersGuilds = 1; - cathedrals = 0; + armourers = 1; stables = 0; tunnelersGuilds = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_SALADIN) { + } else if (aiType == AITA_SALADIN) { stockpiles = 3; hovels = 8; + armories = 0; + barracks = 0; + mercenaryPosts = 1; + cathedrals = 0; + chapels = 0; inns = 0; breweries = 0; bakeries = 4; mills = 2; - mercenaryPosts = 1; - barracks = 0; - armories = 0; poleturners = 0; fletchers = 0; tanners = 0; blacksmiths = 0; engineersGuilds = 1; - chapels = 0; armourers = 0; - cathedrals = 0; stables = 0; tunnelersGuilds = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_CALIPH) { + } else if (aiType == AITA_CALIPH) { stockpiles = 2; + hovels = 6; + armories = 0; + barracks = 0; + mercenaryPosts = 1; + cathedrals = 0; + chapels = 0; inns = 2; breweries = 5; bakeries = 0; mills = 0; - hovels = 6; - mercenaryPosts = 1; - barracks = 0; - armories = 0; poleturners = 0; fletchers = 0; tanners = 0; blacksmiths = 0; engineersGuilds = 1; - chapels = 0; armourers = 0; - cathedrals = 0; stables = 0; tunnelersGuilds = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_SULTAN) { + } else if (aiType == AITA_SULTAN) { stockpiles = 0; hovels = 3; armories = 0; @@ -179,11 +181,11 @@ namespace AI { fletchers = 0; tanners = 0; blacksmiths = 0; - armourers = 0; engineersGuilds = 1; - tunnelersGuilds = 0; + armourers = 0; stables = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_RICHARD) { + tunnelersGuilds = 0; + } else if (aiType == AITA_RICHARD) { stockpiles = 2; hovels = 6; armories = 1; @@ -199,13 +201,13 @@ namespace AI { fletchers = 2; tanners = 0; blacksmiths = 2; - armourers = 2; engineersGuilds = 1; - tunnelersGuilds = 1; + armourers = 2; stables = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_FREDERICK) { - hovels = 7; + tunnelersGuilds = 1; + } else if (aiType == AITA_FREDERICK) { stockpiles = 3; + hovels = 7; armories = 1; barracks = 1; mercenaryPosts = 0; @@ -219,11 +221,11 @@ namespace AI { fletchers = 2; tanners = 2; blacksmiths = 2; - armourers = 2; engineersGuilds = 1; - tunnelersGuilds = 0; + armourers = 2; stables = 2; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_PHILIPP) { + tunnelersGuilds = 0; + } else if (aiType == AITA_PHILIPP) { stockpiles = 2; hovels = 5; armories = 1; @@ -239,51 +241,52 @@ namespace AI { fletchers = 2; tanners = 0; blacksmiths = 2; - armourers = 2; engineersGuilds = 1; - tunnelersGuilds = 0; + armourers = 2; stables = 5; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_WAZIR) { + tunnelersGuilds = 0; + } else if (aiType == AITA_WAZIR) { + stockpiles = 2; hovels = 6; armories = 0; barracks = 0; + mercenaryPosts = 1; + cathedrals = 0; + chapels = 0; inns = 0; breweries = 0; bakeries = 3; - fletchers = 0; - mercenaryPosts = 1; - stockpiles = 2; + mills = 1; poleturners = 0; - chapels = 0; - cathedrals = 0; + fletchers = 0; tanners = 0; blacksmiths = 0; + engineersGuilds = 1; armourers = 0; - tunnelersGuilds = 0; stables = 0; - mills = 1; - engineersGuilds = 1; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_EMIR) { + tunnelersGuilds = 0; + } else if (aiType == AITA_EMIR) { + stockpiles = 3; hovels = 7; armories = 1; barracks = 1; + mercenaryPosts = 1; + cathedrals = 0; + chapels = 0; inns = 1; breweries = 2; bakeries = 2; - fletchers = 1; - mercenaryPosts = 1; - stockpiles = 3; + mills = 1; poleturners = 0; - chapels = 0; - cathedrals = 0; + fletchers = 1; tanners = 0; blacksmiths = 0; + engineersGuilds = 1; armourers = 0; - tunnelersGuilds = 0; stables = 0; - mills = 1; - engineersGuilds = 1; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_NIZAR) { + tunnelersGuilds = 0; + } else if (aiType == AITA_NIZAR) { + stockpiles = 2; hovels = 8; armories = 0; barracks = 0; @@ -292,18 +295,17 @@ namespace AI { chapels = 0; inns = 1; breweries = 3; + bakeries = 4; mills = 1; - fletchers = 0; - stockpiles = 2; poleturners = 0; - bakeries = 4; + fletchers = 0; tanners = 0; blacksmiths = 0; - armourers = 0; engineersGuilds = 1; - tunnelersGuilds = 0; + armourers = 0; stables = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_SHERIFF) { + tunnelersGuilds = 0; + } else if (aiType == AITA_SHERIFF) { stockpiles = 3; hovels = 6; armories = 1; @@ -319,11 +321,11 @@ namespace AI { fletchers = 2; tanners = 2; blacksmiths = 2; - armourers = 0; engineersGuilds = 1; - tunnelersGuilds = 0; + armourers = 0; stables = 0; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_MARSHAL) { + tunnelersGuilds = 0; + } else if (aiType == AITA_MARSHAL) { stockpiles = 2; hovels = 5; armories = 1; @@ -339,30 +341,30 @@ namespace AI { fletchers = 2; tanners = 0; blacksmiths = 2; - armourers = 2; engineersGuilds = 1; - tunnelersGuilds = 0; + armourers = 2; stables = 4; - } else if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_ABBOT) { - mills = 2; - chapels = 5; + tunnelersGuilds = 0; + } else if (aiType == AITA_ABBOT) { + stockpiles = 2; hovels = 6; armories = 1; barracks = 1; mercenaryPosts = 0; cathedrals = 1; + chapels = 5; inns = 3; breweries = 6; - fletchers = 3; - stockpiles = 2; - poleturners = 0; bakeries = 4; + mills = 2; + poleturners = 0; + fletchers = 3; tanners = 0; blacksmiths = 0; - armourers = 0; engineersGuilds = 1; - tunnelersGuilds = 0; + armourers = 0; stables = 0; + tunnelersGuilds = 0; } for (int step = 0; step <= 50; ++step) { @@ -616,7 +618,7 @@ namespace AI { orientation); int const placedID = DAT_TileMapState::instance.placedBuildingID; if (!DAT_TileMapState::instance.buildingPlacementFail) { - DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].field245_0x2c8 = 1; + DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].field242_0x2c8 = 1; MACRO_CALL_MEMBER(AICState_Func::setupWorkshopProductionType, DAT_AICState::ptr)(playerID, placedID); this->aivs[aivID].aivBuildingSteps[step].location.tile.tile = rowTile + x; if (!DAT_TileMapState::instance.buildingPlacementFail) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 41701ecd8..1d0a6c6fb 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37991,7 +37991,7 @@ SHC_3BB0A8C1_0x004EFE30 | 91.1% | Reimplemented SHC_3BB0A8C1_0x004EFED0 | 93.8% | Reimplemented -SHC_3BB0A8C1_0x004F0060 | 36.4% | chain register naming; orig sp +SHC_3BB0A8C1_0x004F0060 | 36.7% | aiType - 1 chain; registers differ SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending From 477c374112215e5d0b6880be9e9ed15e98612994 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:51 +0200 Subject: [PATCH 79/85] reimplement: SHC_3BB0A8C1_0x004ED410 95.31% orig branches to a shared return Co-Authored-By: Claude Fable 5.1 --- src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp | 2 +- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp index a17220d09..fcfafff47 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp @@ -289,7 +289,7 @@ namespace AI { orientation); int const placedID = DAT_TileMapState::instance.placedBuildingID; if (!DAT_TileMapState::instance.buildingPlacementFail) { - DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].field245_0x2c8 = 1; + DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].field242_0x2c8 = 1; MACRO_CALL_MEMBER(AICState_Func::setupWorkshopProductionType, DAT_AICState::ptr)(playerID, placedID); } if (!DAT_TileMapState::instance.buildingPlacementFail) { diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 1d0a6c6fb..06aff1ce2 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37931,7 +37931,7 @@ SHC_3BB0A8C1_0x004ED300 | 96.2% | Reimplemented SHC_3BB0A8C1_0x004ED3B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004ED410 | 90.9% | orig branches to a shared retu +SHC_3BB0A8C1_0x004ED410 | 95.3% | orig branches to a shared return SHC_3BB0A8C1_0x004EDC90 | 85.7% | Reimplemented From c63e0f5fe642a881c8dd4a4f0b62f7a071aa19b0 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 19:45:52 +0200 Subject: [PATCH 80/85] enable clearTheHeatmaps resolver so wipeAIVsAndHeatMaps tail call matches Co-Authored-By: Claude Fable 5.1 --- src/OpenSHC/AI/AIVState.func.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenSHC/AI/AIVState.func.hpp b/src/OpenSHC/AI/AIVState.func.hpp index 52040ac8d..70fd854fa 100644 --- a/src/OpenSHC/AI/AIVState.func.hpp +++ b/src/OpenSHC/AI/AIVState.func.hpp @@ -72,7 +72,7 @@ namespace AI { aiPlaceAIVBuilding; MACRO_FUNCTION_RESOLVER( - void (AIVState::*)(), false, Address::SHC_3BB0A8C1_0x004EDC90, &AIVState::clearTheHeatmaps) + void (AIVState::*)(), true, Address::SHC_3BB0A8C1_0x004EDC90, &AIVState::clearTheHeatmaps) clearTheHeatmaps; MACRO_FUNCTION_RESOLVER( From 1e65179b02b676cf5a4b3c178807e7ba03271fc0 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 20:44:11 +0200 Subject: [PATCH 81/85] status: update AIVState percentages from reccmp with --resolve-wrapped-calls Co-Authored-By: Claude Fable 5.1 --- status/addresses-SHC-3BB0A8C1.txt | 36 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 06aff1ce2..30594706f 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37871,7 +37871,7 @@ SHC_3BB0A8C1_0x004ECA90 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECAB0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004ECAD0 | 98.6% | Reimplemented +SHC_3BB0A8C1_0x004ECAD0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECBC0 | 68.6% | orig branch shape; registers differ @@ -37885,7 +37885,7 @@ SHC_3BB0A8C1_0x004ECDE0 | 0.0% | Pending SHC_3BB0A8C1_0x004ECDF0 | 0.0% | Pending -SHC_3BB0A8C1_0x004ECE60 | 97.8% | Reimplemented +SHC_3BB0A8C1_0x004ECE60 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ECEF0 | 100.0% | Reimplemented @@ -37923,21 +37923,21 @@ SHC_3BB0A8C1_0x004ED0A4 | 0.0% | Pending SHC_3BB0A8C1_0x004ED0A8 | 0.0% | Pending -SHC_3BB0A8C1_0x004ED0B0 | 95.3% | Reimplemented +SHC_3BB0A8C1_0x004ED0B0 | 97.2% | Reimplemented -SHC_3BB0A8C1_0x004ED240 | 94.7% | Reimplemented +SHC_3BB0A8C1_0x004ED240 | 98.2% | Reimplemented -SHC_3BB0A8C1_0x004ED300 | 96.2% | Reimplemented +SHC_3BB0A8C1_0x004ED300 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ED3B0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ED410 | 95.3% | orig branches to a shared return -SHC_3BB0A8C1_0x004EDC90 | 85.7% | Reimplemented +SHC_3BB0A8C1_0x004EDC90 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EDCB0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EDD00 | 97.6% | Reimplemented +SHC_3BB0A8C1_0x004EDD00 | 98.8% | Reimplemented SHC_3BB0A8C1_0x004EDDF0 | 100.0% | Reimplemented @@ -37961,9 +37961,9 @@ SHC_3BB0A8C1_0x004EEEC0 | 0.0% | Pending SHC_3BB0A8C1_0x004EEF20 | 0.0% | Pending -SHC_3BB0A8C1_0x004EF090 | 90.0% | Reimplemented +SHC_3BB0A8C1_0x004EF090 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EF0D0 | 65.7% | orig spills y in loop 1, we ke +SHC_3BB0A8C1_0x004EF0D0 | 67.0% | orig spills y in loop 1, we ke SHC_3BB0A8C1_0x004EF130 | 0.0% | Pending @@ -37977,29 +37977,29 @@ SHC_3BB0A8C1_0x004EF900 | 0.0% | Pending SHC_3BB0A8C1_0x004EF920 | 0.0% | Pending -SHC_3BB0A8C1_0x004EFA60 | 91.9% | Reimplemented +SHC_3BB0A8C1_0x004EFA60 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EFAB0 | 82.8% | Register reuse after search +SHC_3BB0A8C1_0x004EFAB0 | 91.4% | Register reuse after search -SHC_3BB0A8C1_0x004EFB90 | 97.7% | Reimplemented +SHC_3BB0A8C1_0x004EFB90 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EFC30 | 96.0% | Reimplemented +SHC_3BB0A8C1_0x004EFC30 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EFD90 | 91.1% | Reimplemented +SHC_3BB0A8C1_0x004EFD90 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EFE30 | 91.1% | Reimplemented +SHC_3BB0A8C1_0x004EFE30 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EFED0 | 93.8% | Reimplemented +SHC_3BB0A8C1_0x004EFED0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F0060 | 36.7% | aiType - 1 chain; registers differ SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending -SHC_3BB0A8C1_0x004F0E80 | 87.8% | rest is scheduling + a hoisted +SHC_3BB0A8C1_0x004F0E80 | 88.9% | rest is scheduling + a hoisted SHC_3BB0A8C1_0x004F1190 | 0.0% | Pending -SHC_3BB0A8C1_0x004F13E0 | 83.3% | Reimplemented +SHC_3BB0A8C1_0x004F13E0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004F13F0 | 100.0% | Reimplemented From 536189d18eb08572e745a976c5931e80ee9dc389 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 21:40:07 +0200 Subject: [PATCH 82/85] reimplement: SHC_3BB0A8C1_0x004ED410 100.00% Reimplemented Co-Authored-By: Claude Fable 5.1 --- .../AI/AIVState/aiPlaceAIVBuilding.cpp | 276 +++++++++--------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 143 insertions(+), 135 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp index fcfafff47..920fcbd29 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp @@ -136,11 +136,7 @@ namespace AI { if (!DAT_TileMapState::instance.illegalBuild) { return TRUE; } - return FALSE; - } - - // stairs - if (mapper == Commands::M_MAPPER_STAIR1 || mapper == Commands::M_MAPPER_STAIR2 + } else if (mapper == Commands::M_MAPPER_STAIR1 || mapper == Commands::M_MAPPER_STAIR2 || mapper == Commands::M_MAPPER_STAIR3 || mapper == Commands::M_MAPPER_STAIR4 || mapper == Commands::M_MAPPER_STAIR5 || mapper == Commands::M_MAPPER_STAIR6) { if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built @@ -163,149 +159,161 @@ namespace AI { } this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_insufficient_resources; this->aivs[aivID].aivBuildingSteps[step].wait = 4; - return FALSE; - } - - if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingGranary, DAT_AICState::ptr)(playerID, mapper)) { - return FALSE; - } - if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingArmoury, DAT_AICState::ptr)(playerID, mapper)) { - return FALSE; - } - if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingHovel, DAT_AICState::ptr)(playerID, mapper)) { - return FALSE; - } - if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildMarketArmoryGranaryBarracksMercenarypost, DAT_AICState::ptr)( - playerID, mapper)) { - return FALSE; - } - if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildKillingPits, DAT_AICState::ptr)(playerID, mapper)) { - return FALSE; - } - - int const tile = this->aivs[aivID].aivBuildingSteps[step].location.tile.tile; - y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; - x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; - int orientation = 15; - if (mapper == Commands::M_MAPPER_BARRACKS_ARAB || mapper == Commands::M_MAPPER_BARRACKS_EURO - || mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD - || mapper == Commands::M_MAPPER_OIL_SMELTER) { - orientation = this->aivs[aivID].keepOrientation; - } else if (mapper == Commands::M_MAPPER_GATE_STONE1A || mapper == Commands::M_MAPPER_GATE_STONE2A) { - if (this->aivs[aivID].keepOrientation == 0 || this->aivs[aivID].keepOrientation == 4) { - orientation = 81; - } else { - orientation = 80; + } else { + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingGranary, DAT_AICState::ptr)(playerID, mapper)) { + return FALSE; } - } else if (mapper == Commands::M_MAPPER_GATE_STONE1B || mapper == Commands::M_MAPPER_GATE_STONE2B) { - if (this->aivs[aivID].keepOrientation == 2 || this->aivs[aivID].keepOrientation == 6) { - orientation = 81; - } else { - orientation = 80; + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingArmoury, DAT_AICState::ptr)(playerID, mapper)) { + return FALSE; } - } else if (mapper == Commands::M_MAPPER_POLETURNER || mapper == Commands::M_MAPPER_BLACKSMITH - || mapper == Commands::M_MAPPER_ARMOURER || mapper == Commands::M_MAPPER_TANNER - || mapper == Commands::M_MAPPER_FLETCHER || mapper == Commands::M_MAPPER_BAKER - || mapper == Commands::M_MAPPER_BREWER) { - orientation = 8; - } else if (mapper == Commands::M_MAPPER_DRAWBRIDGE) { - DAT_TileMapState::instance.buildingPlacementFail = FALSE; - MACRO_CALL_MEMBER(Map::TileMapState_Func::checkDrawbridgePlacement, DAT_TileMapState::ptr)(x, y); - if (DAT_TileMapState::instance.buildingPlacementFail) { + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBeBuildingHovel, DAT_AICState::ptr)(playerID, mapper)) { return FALSE; } - orientation = DAT_TileMapState::instance.uiBuildingRotation; - } - - if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built) { - // rebuild a destroyed building - int const buildingID = (short)DAT_TileMapState::instance.BuildingLayer[tile]; - if ((short)DAT_BuildingsState::instance.buildings[buildingID].buildingType - == MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, - DAT_BuildingsState::ptr)(mapper)) { + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildMarketArmoryGranaryBarracksMercenarypost, + DAT_AICState::ptr)(playerID, mapper)) { return FALSE; } - if (DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker == 0 - && DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker == 0 - && this->aivs[aivID].aivBuildingSteps[step].wait > 0) { - --this->aivs[aivID].aivBuildingSteps[step].wait; + if (MACRO_CALL_MEMBER(AICState_Func::shouldNotBuildKillingPits, DAT_AICState::ptr)(playerID, mapper)) { return FALSE; } - if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, - DAT_GameState::ptr)(mapper, playerID, FALSE)) { + + int const tile = this->aivs[aivID].aivBuildingSteps[step].location.tile.tile; + y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + int orientation = 15; + if (mapper == Commands::M_MAPPER_BARRACKS_ARAB || mapper == Commands::M_MAPPER_BARRACKS_EURO + || mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD + || mapper == Commands::M_MAPPER_OIL_SMELTER) { + orientation = this->aivs[aivID].keepOrientation; + } else if (mapper == Commands::M_MAPPER_GATE_STONE1A || mapper == Commands::M_MAPPER_GATE_STONE2A) { + if (this->aivs[aivID].keepOrientation == 0 || this->aivs[aivID].keepOrientation == 4) { + orientation = 81; + } else { + orientation = 80; + } + } else if (mapper == Commands::M_MAPPER_GATE_STONE1B || mapper == Commands::M_MAPPER_GATE_STONE2B) { + if (this->aivs[aivID].keepOrientation == 2 || this->aivs[aivID].keepOrientation == 6) { + orientation = 81; + } else { + orientation = 80; + } + } else if (mapper == Commands::M_MAPPER_POLETURNER || mapper == Commands::M_MAPPER_BLACKSMITH + || mapper == Commands::M_MAPPER_ARMOURER || mapper == Commands::M_MAPPER_TANNER + || mapper == Commands::M_MAPPER_FLETCHER || mapper == Commands::M_MAPPER_BAKER + || mapper == Commands::M_MAPPER_BREWER) { + orientation = 8; + } else if (mapper == Commands::M_MAPPER_DRAWBRIDGE) { + DAT_TileMapState::instance.buildingPlacementFail = FALSE; + MACRO_CALL_MEMBER(Map::TileMapState_Func::checkDrawbridgePlacement, DAT_TileMapState::ptr)(x, y); + if (DAT_TileMapState::instance.buildingPlacementFail) { + return FALSE; + } + orientation = DAT_TileMapState::instance.uiBuildingRotation; + } + + if (this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built) { + // rebuild a destroyed building + int const buildingID = (short)DAT_TileMapState::instance.BuildingLayer[tile]; + if ((short)DAT_BuildingsState::instance.buildings[buildingID].buildingType + == MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, + DAT_BuildingsState::ptr)(mapper)) { + return FALSE; + } + if (DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker == 0 + && DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker == 0 + && this->aivs[aivID].aivBuildingSteps[step].wait > 0) { + --this->aivs[aivID].aivBuildingSteps[step].wait; + return FALSE; + } + if (!MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkRequiredResourcesForBuildingOrPlanToBuy, + DAT_GameState::ptr)(mapper, playerID, FALSE)) { + return FALSE; + } + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldWaitWithBuildingAPositiveFearFactor, DAT_AICState::ptr)( + playerID, mapper)) { + return FALSE; + } + if (MACRO_CALL_MEMBER(AICState_Func::shouldWaitWithBuildingNegativeFearFactor, DAT_AICState::ptr)( + playerID, mapper)) { return FALSE; } - } - if (MACRO_CALL_MEMBER(AICState_Func::shouldWaitWithBuildingAPositiveFearFactor, DAT_AICState::ptr)( - playerID, mapper)) { - return FALSE; - } - if (MACRO_CALL_MEMBER(AICState_Func::shouldWaitWithBuildingNegativeFearFactor, DAT_AICState::ptr)( - playerID, mapper)) { - return FALSE; - } - if (orientation == 15) { - DAT_TileMapState::instance.DAT_TempBuildingRotation = 0; - } else { - DAT_TileMapState::instance.DAT_TempBuildingRotation = orientation / 2; - } - int const buildingSize = MACRO_CALL_MEMBER( - Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)(mapper); - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( - playerID, x, y, mapper, buildingSize); - // keep orientation is 0, 2, 4 or 6, halving it gives the index of the part offsets - if (mapper == Commands::M_MAPPER_BARRACKS_EURO || mapper == Commands::M_MAPPER_BARRACKS_ARAB) { - // the three fields in front of the barracks - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].x + x, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].y + y, - mapper, buildingSize); - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, - mapper, buildingSize); - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].x + x, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].y + y, - mapper, buildingSize); + if (orientation == 15) { + DAT_TileMapState::instance.DAT_TempBuildingRotation = 0; + } else { + DAT_TileMapState::instance.DAT_TempBuildingRotation = orientation / 2; + } + int const buildingSize = MACRO_CALL_MEMBER( + Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)(mapper); + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, x, y, mapper, buildingSize); + // keep orientation is 0, 2, 4 or 6, halving it gives the index of the part offsets + if (mapper == Commands::M_MAPPER_BARRACKS_EURO || mapper == Commands::M_MAPPER_BARRACKS_ARAB) { + // the three fields in front of the barracks + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].x + + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][0].y + + y, + mapper, buildingSize); + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + + y, + mapper, buildingSize); + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].x + + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][2].y + + y, + mapper, buildingSize); - } else if (mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD) { - // the guild field - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + x, - DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + y, - mapper, buildingSize); - } else if (mapper == Commands::M_MAPPER_OIL_SMELTER) { - MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)(playerID, - DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].x + x, - DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].y + y, mapper, - buildingSize); - } + } else if (mapper == Commands::M_MAPPER_ENGINEERS_GUILD || mapper == Commands::M_MAPPER_TUNNELERS_GUILD) { + // the guild field + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].x + + x, + DAT_TerrainDefinedData::instance.BuildingPartsOffsets[this->aivs[aivID].keepOrientation / 2][1].y + + y, + mapper, buildingSize); + } else if (mapper == Commands::M_MAPPER_OIL_SMELTER) { + MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( + playerID, + DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].x + x, + DAT_TerrainDefinedData::instance.field63_0x19c[this->aivs[aivID].keepOrientation / 2].y + y, mapper, + buildingSize); + } - MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, x, y, mapper, - MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( - mapper), - orientation); - int const placedID = DAT_TileMapState::instance.placedBuildingID; - if (!DAT_TileMapState::instance.buildingPlacementFail) { - DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].field242_0x2c8 = 1; - MACRO_CALL_MEMBER(AICState_Func::setupWorkshopProductionType, DAT_AICState::ptr)(playerID, placedID); - } - if (!DAT_TileMapState::instance.buildingPlacementFail) { - this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; - } else if (mapper == Commands::M_MAPPER_WHEATFARM || mapper == Commands::M_MAPPER_HOPSFARM - || mapper == Commands::M_MAPPER_APPLEFARM || mapper == Commands::M_MAPPER_CATTLEFARM) { - this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_insufficient_roomUnk; - } else { - this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_insufficient_resources; - } - this->aivs[aivID].aivBuildingSteps[step].wait - = this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built ? 10 : 16; - if (DAT_TileMapState::instance.buildingPlacementFail) { - return FALSE; + MACRO_CALL_MEMBER(Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)(playerID, x, y, mapper, + MACRO_CALL_MEMBER(Map::TileMapState_Func::getBuildingSizeForCommandBuildingType, DAT_TileMapState::ptr)( + mapper), + orientation); + int const placedID = DAT_TileMapState::instance.placedBuildingID; + if (!DAT_TileMapState::instance.buildingPlacementFail) { + DAT_BuildingsState::instance.buildings[placedID].field242_0x2c8 = 1; + MACRO_CALL_MEMBER(AICState_Func::setupWorkshopProductionType, DAT_AICState::ptr)(playerID, placedID); + } + if (!DAT_TileMapState::instance.buildingPlacementFail) { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; + } else if (mapper == Commands::M_MAPPER_WHEATFARM || mapper == Commands::M_MAPPER_HOPSFARM + || mapper == Commands::M_MAPPER_APPLEFARM || mapper == Commands::M_MAPPER_CATTLEFARM) { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_insufficient_roomUnk; + } else { + this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_insufficient_resources; + } + this->aivs[aivID].aivBuildingSteps[step].wait + = this->aivs[aivID].aivBuildingSteps[step].buildStatus == AIVBSS_built ? 10 : 16; + if (!DAT_TileMapState::instance.buildingPlacementFail) { + return TRUE; + } } - return TRUE; + return FALSE; } } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 30594706f..391c9a622 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37931,7 +37931,7 @@ SHC_3BB0A8C1_0x004ED300 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004ED3B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004ED410 | 95.3% | orig branches to a shared return +SHC_3BB0A8C1_0x004ED410 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EDC90 | 100.0% | Reimplemented From 22bb8d7ebfb0f9bd02e2b4e9cfabe34d90d68d56 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 21:40:08 +0200 Subject: [PATCH 83/85] reimplement: SHC_3BB0A8C1_0x004F0060 53.56% step < 50 fix; registers differ Co-Authored-By: Claude Fable 5.1 --- .../AI/AIVState/executeDefaultCastleAIV.cpp | 25 +++++++++++-------- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp index 0377e8069..cb4e3aa55 100644 --- a/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp +++ b/src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp @@ -23,7 +23,6 @@ namespace AI { BOOLEnum AIVState::executeDefaultCastleAIV(PlayerID playerID, int isPausing) { int const aivID = DAT_GameState::instance.playerDataArray[playerID].aivID; - int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; int stockpiles = 0; int hovels = 0; int granaries = 1; @@ -45,6 +44,7 @@ namespace AI { int engineersGuilds = 0; int tunnelersGuilds = 0; int stables = 0; + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; if (aiType == AITA_RAT) { stockpiles = 0; hovels = 3; @@ -367,7 +367,7 @@ namespace AI { tunnelersGuilds = 0; } - for (int step = 0; step <= 50; ++step) { + for (int step = 0; step < 50; ++step) { if (step > this->aivs[aivID].currentStepGoal) { return FALSE; } @@ -476,11 +476,10 @@ namespace AI { } if (status == AIVBSS_built) { // only rebuild buildings that were destroyed - short const buildingID - = DAT_TileMapState::instance - .BuildingLayer[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; + int const buildingID = (short)DAT_TileMapState::instance + .BuildingLayer[this->aivs[aivID].aivBuildingSteps[step].location.tile.tile]; if (buildingID != 0 - && DAT_BuildingsState::instance.buildings[buildingID].buildingType + && (short)DAT_BuildingsState::instance.buildings[buildingID].buildingType == MACRO_CALL_MEMBER( Map::Buildings::BuildingsState_Func::convertCommandBuildingTypeToBuildingType, DAT_BuildingsState::ptr)(mapper)) { @@ -556,19 +555,25 @@ namespace AI { MACRO_CALL_MEMBER(AIVState_Func::findAppropriateGridLocationForBuilding, this)( DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5, DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5); + if (this->buildingApproriateGridXPosition == -1) { + continue; + } } else { MACRO_CALL_MEMBER(AIVState_Func::findSuitableGridLocationFittingCriteria, this)( DAT_GameState::instance.playerDataArray[playerID].campground.xEntry / 5, DAT_GameState::instance.playerDataArray[playerID].campground.yEntry / 5); + if (this->buildingApproriateGridXPosition == -1) { + continue; + } } - if (this->buildingApproriateGridXPosition == -1 || this->buildingAppropriateGridYPosition == -1) { + if (this->buildingAppropriateGridYPosition == -1) { continue; } x = this->buildingApproriateGridXPosition * 5; y = this->buildingAppropriateGridYPosition * 5; } - int const rowTile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + int const tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; MACRO_CALL_MEMBER(Map::TileMapState_Func::prepareAreaForBuildingPlacement, DAT_TileMapState::ptr)( playerID, x, y, mapper, buildingSize); // keep orientation is 0, 2, 4 or 6, halving it gives the index of the part offsets @@ -618,9 +623,9 @@ namespace AI { orientation); int const placedID = DAT_TileMapState::instance.placedBuildingID; if (!DAT_TileMapState::instance.buildingPlacementFail) { - DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].field242_0x2c8 = 1; + DAT_BuildingsState::instance.buildings[placedID].field242_0x2c8 = 1; MACRO_CALL_MEMBER(AICState_Func::setupWorkshopProductionType, DAT_AICState::ptr)(playerID, placedID); - this->aivs[aivID].aivBuildingSteps[step].location.tile.tile = rowTile + x; + this->aivs[aivID].aivBuildingSteps[step].location.tile.tile = tile; if (!DAT_TileMapState::instance.buildingPlacementFail) { this->aivs[aivID].aivBuildingSteps[step].buildStatus = AIVBSS_built; } diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 391c9a622..bdd497510 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37991,7 +37991,7 @@ SHC_3BB0A8C1_0x004EFE30 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004EFED0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004F0060 | 36.7% | aiType - 1 chain; registers differ +SHC_3BB0A8C1_0x004F0060 | 53.6% | step < 50 fix; registers differ SHC_3BB0A8C1_0x004F0690 | 0.0% | Pending From c98df003cf50bf75e271f4fbc2397a7e70ef05f4 Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 21:40:08 +0200 Subject: [PATCH 84/85] reimplement: SHC_3BB0A8C1_0x004EF0D0 67.19% orig spills y in loop 1 Co-Authored-By: Claude Fable 5.1 --- src/OpenSHC/AI/AIVState/applyAIV.cpp | 4 +++- status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/applyAIV.cpp b/src/OpenSHC/AI/AIVState/applyAIV.cpp index fa638dca4..b4e1b2a48 100644 --- a/src/OpenSHC/AI/AIVState/applyAIV.cpp +++ b/src/OpenSHC/AI/AIVState/applyAIV.cpp @@ -118,8 +118,10 @@ namespace AI { if (step > this->aivs[aivID].totalSteps) { this->aivs[aivID].totalSteps = step; } + this->isHandled[index] = 1; + } else { + this->isHandled[index] = 1; } - this->isHandled[index] = 1; } } } while (selected); diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index bdd497510..59be8904c 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -37963,7 +37963,7 @@ SHC_3BB0A8C1_0x004EEF20 | 0.0% | Pending SHC_3BB0A8C1_0x004EF090 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004EF0D0 | 67.0% | orig spills y in loop 1, we ke +SHC_3BB0A8C1_0x004EF0D0 | 67.2% | orig spills y in loop 1 SHC_3BB0A8C1_0x004EF130 | 0.0% | Pending From 16e56afe7e4b1259d123598ede6ac3aad112262d Mon Sep 17 00:00:00 2001 From: Gynt Date: Mon, 5 Oct 2026 21:40:08 +0200 Subject: [PATCH 85/85] build executeDefaultCastleAIV and findAppropriateGridLocationForBuilding with /GL The original keeps this in ecx across the call, which needs /GL on caller and callee and a direct call (resolver enabled). Co-Authored-By: Claude Fable 5.1 --- cmake/compiler-flags-gl.txt | 3 +++ src/OpenSHC/AI/AIVState.func.hpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/compiler-flags-gl.txt b/cmake/compiler-flags-gl.txt index 848d70de6..d312aa04a 100644 --- a/cmake/compiler-flags-gl.txt +++ b/cmake/compiler-flags-gl.txt @@ -7,3 +7,6 @@ src/OpenSHC/IO/ResourceManager/swapMapDataWithNextMap.cpp src/OpenSHC/UI/Rendering/WindowAndDirectDraw/takeScreenshot.cpp src/OpenSHC/IO/ResourceManager/readFirstPartOfCurrentResourceIntoMemory.cpp src/OpenSHC/IO/ResourceManager/readNextPartOfCurrentResourceIntoMemory.cpp + +src/OpenSHC/AI/AIVState/executeDefaultCastleAIV.cpp +src/OpenSHC/AI/AIVState/findAppropriateGridLocationForBuilding.cpp diff --git a/src/OpenSHC/AI/AIVState.func.hpp b/src/OpenSHC/AI/AIVState.func.hpp index 70fd854fa..0eaf7ba9a 100644 --- a/src/OpenSHC/AI/AIVState.func.hpp +++ b/src/OpenSHC/AI/AIVState.func.hpp @@ -103,7 +103,7 @@ namespace AI { &AIVState::findSuitableGridLocationFittingCriteria) findSuitableGridLocationFittingCriteria; - MACRO_FUNCTION_RESOLVER(void (AIVState::*)(uint, uint), false, Address::SHC_3BB0A8C1_0x004EEE10, + MACRO_FUNCTION_RESOLVER(void (AIVState::*)(uint, uint), true, Address::SHC_3BB0A8C1_0x004EEE10, &AIVState::findAppropriateGridLocationForBuilding) findAppropriateGridLocationForBuilding;