From 22a0cad4ebb32ea28cfa5ea5c790990cafb94ee4 Mon Sep 17 00:00:00 2001 From: Jake Vawter Date: Tue, 27 Oct 2026 17:07:24 -0400 Subject: [PATCH 1/7] headers: spawnCrowForBuilding and updateBuildingAreaTileGraphics return void Both originals leave whatever is in eax: updateBuildingAreaTileGraphics ends on a dead lea of the last MiscDisplayLayer address (which Ghidra took as a ushort* return), spawnCrowForBuilding on the last call's result. The only callers (deleteBuilding, UpdateDairyFarm) ignore the value. Returning void drops the xor eax, eax our reimplementations would otherwise add. Co-Authored-By: Claude Opus 5.5 --- src/OpenSHC/Map/Buildings/BuildingsState.func.hpp | 4 ++-- src/OpenSHC/Map/Buildings/BuildingsState.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OpenSHC/Map/Buildings/BuildingsState.func.hpp b/src/OpenSHC/Map/Buildings/BuildingsState.func.hpp index 27683cd69..8d511c50e 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState.func.hpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState.func.hpp @@ -63,7 +63,7 @@ namespace Map { Address::SHC_3BB0A8C1_0x0040A060, &BuildingsState::displayPopularityAndGoldPopups) displayPopularityAndGoldPopups; - MACRO_FUNCTION_RESOLVER(int (BuildingsState::*)(int), false, Address::SHC_3BB0A8C1_0x0040A260, + MACRO_FUNCTION_RESOLVER(void (BuildingsState::*)(int), false, Address::SHC_3BB0A8C1_0x0040A260, &BuildingsState::spawnCrowForBuilding) spawnCrowForBuilding; @@ -347,7 +347,7 @@ namespace Map { &BuildingsState::harmHopFarmsOfPlayer) harmHopFarmsOfPlayer; - MACRO_FUNCTION_RESOLVER(ushort* (BuildingsState::*)(int), false, Address::SHC_3BB0A8C1_0x0040EFD0, + MACRO_FUNCTION_RESOLVER(void (BuildingsState::*)(int), false, Address::SHC_3BB0A8C1_0x0040EFD0, &BuildingsState::updateBuildingAreaTileGraphics) updateBuildingAreaTileGraphics; diff --git a/src/OpenSHC/Map/Buildings/BuildingsState.hpp b/src/OpenSHC/Map/Buildings/BuildingsState.hpp index a5d0166f6..136eb8f75 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState.hpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState.hpp @@ -104,7 +104,7 @@ namespace Map { void displayPopularityAndGoldPopups(int buildingID, int param_2, int param_3, undefined4 param_4); - int spawnCrowForBuilding(int buildingID); + void spawnCrowForBuilding(int buildingID); BOOLEnum isFearFactorBuilding(int buildingID); @@ -251,7 +251,7 @@ namespace Map { void harmHopFarmsOfPlayer(int param_1); - ushort* updateBuildingAreaTileGraphics(int param_1); + void updateBuildingAreaTileGraphics(int param_1); void setDairyFarmCheeseCounter(int param_1); From 8b2ab1a12c5b92d439637384835e963ea2ff90dc Mon Sep 17 00:00:00 2001 From: Jake Vawter Date: Tue, 27 Oct 2026 17:07:46 -0400 Subject: [PATCH 2/7] reimplement: BuildingsState functions that are now 100% 17 functions from #273 that now match the original exactly (reccmp 100%, with --resolve-wrapped-calls), with their status lines. canUnitReachBuildingPerimeter and processResourceLoss are built with /GL, as in #273. Co-Authored-By: Claude Opus 5.5 --- cmake/compiler-flags-gl.txt | 4 +- .../canUnitReachAdjacentTile.cpp | 48 ++++++ .../canUnitReachBuildingPerimeter.cpp | 72 +++++++++ .../BuildingsState/countPlayerResources.cpp | 57 +++++++ .../findClosestReachableAlliedBuilding.cpp | 64 ++++++++ .../findFreeCathedralAssemblyTile.cpp | 57 +++++++ .../findFreeEngineerAssemblyTile.cpp | 94 ++++++++++++ .../getPriceForDisbandedUnitType.cpp | 77 ++++++++++ .../Buildings/BuildingsState/growWheat.cpp | 84 +++++++++++ .../isSpaceAvailableAtUnitRallyPoint.cpp | 114 ++++++++++++++ .../processBuildingIDsNeedPeasantsQueue.cpp | 139 ++++++++++++++++++ .../processDamageToUnitsOnBuilding.cpp | 52 +++++++ .../BuildingsState/processResourceLoss.cpp | 82 +++++++++++ .../setNewOnFarmDestination.cpp | 96 ++++++++++++ .../setupBarracksCampgroundPositions.cpp | 117 +++++++++++++++ .../setupMercenaryPostCampgroundPositions.cpp | 130 ++++++++++++++++ .../spawnRandomFireEffectOnBuilding.cpp | 64 ++++++++ .../updateBuildingAreaTileGraphics.cpp | 30 ++++ status/addresses-SHC-3BB0A8C1.txt | 34 ++--- 19 files changed, 1397 insertions(+), 18 deletions(-) create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachAdjacentTile.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachBuildingPerimeter.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/countPlayerResources.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/findClosestReachableAlliedBuilding.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/findFreeCathedralAssemblyTile.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/findFreeEngineerAssemblyTile.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/getPriceForDisbandedUnitType.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/growWheat.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/isSpaceAvailableAtUnitRallyPoint.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/processBuildingIDsNeedPeasantsQueue.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/processDamageToUnitsOnBuilding.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/processResourceLoss.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/setupBarracksCampgroundPositions.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/spawnRandomFireEffectOnBuilding.cpp create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/updateBuildingAreaTileGraphics.cpp diff --git a/cmake/compiler-flags-gl.txt b/cmake/compiler-flags-gl.txt index 0e2e4f053..bc44f2052 100644 --- a/cmake/compiler-flags-gl.txt +++ b/cmake/compiler-flags-gl.txt @@ -53,4 +53,6 @@ src/OpenSHC/Map/Buildings/BuildingsState/applyVersionUpgradeAccessibilityRecompu src/OpenSHC/Map/Buildings/BuildingsState/recomputeAccessibilityForAllBuildings.cpp src/OpenSHC/Map/Buildings/BuildingsState/getResourceSpace.cpp src/OpenSHC/Synchrony/GameSynchronyState/processSyncPacket.cpp -src/OpenSHC/Synchrony/GameSynchronyState/translateMultiplayerIDsIntoPlayerIDs.cpp \ No newline at end of file +src/OpenSHC/Synchrony/GameSynchronyState/translateMultiplayerIDsIntoPlayerIDs.cpp +src/OpenSHC/Map/Buildings/BuildingsState/processResourceLoss.cpp +src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachBuildingPerimeter.cpp \ No newline at end of file diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachAdjacentTile.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachAdjacentTile.cpp new file mode 100644 index 000000000..6bd43dfcf --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachAdjacentTile.cpp @@ -0,0 +1,48 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +#include + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x004105F0 + BOOLEnum BuildingsState::canUnitReachAdjacentTile(int param_1, int param_2) + { + int const row = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[param_1]; + int const unitArea + = (short)DAT_TileMapState::instance.PathConnectionLayer[DAT_UnitsState::instance.units[param_2].tile]; + int const height = DAT_TileMapState::instance.HeightLayer[param_1]; + if (MACRO_CALL_MEMBER(Units::UnitsState_Func::selectionContainsOnlyArabAssassins, DAT_UnitsState::ptr)() + != FALSE) { + return FALSE; + } + for (int direction = 0; direction < 8; ++direction) { + int const area + = (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_TileMapState::instance.directionTranslationMatrix[row][direction] + + param_1]; + // The original compares the height of param_1 with itself, not with the neighbouring tile. + if (abs(height - DAT_TileMapState::instance.HeightLayer[param_1]) < 16 && area != 0 + && MACRO_CALL_MEMBER( + Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(DAT_UnitsState::instance.units[param_2].owner, unitArea, area, + MACRO_CALL_MEMBER(Units::UnitsState_Func::canAUnitClimb, DAT_UnitsState::ptr)()) + != 0) { + return TRUE; + } + } + return FALSE; + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachBuildingPerimeter.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachBuildingPerimeter.cpp new file mode 100644 index 000000000..672f573b8 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/canUnitReachBuildingPerimeter.cpp @@ -0,0 +1,72 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +#include + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x00410440 + BOOLEnum BuildingsState::canUnitReachBuildingPerimeter(int buildingID, int unitID) + { + if (buildingID == 0 || buildingID >= 2000) { + return FALSE; + } + int const unitArea + = (short)DAT_TileMapState::instance.PathConnectionLayer[DAT_UnitsState::instance.units[unitID].tile]; + int const size = this->buildings[buildingID].widthOrHeight; + int const buildingHeight + = DAT_TileMapState::instance.DefaultHeightLayer[DAT_BuildingsState::instance.buildings[buildingID] + .currentTilePositionAdjusted]; + int const tileCount = DAT_BuildingDefinedData::instance.BuildingAccessibleTilesCount[size]; + int onlyAssassins = 0; + if (MACRO_CALL_MEMBER(Units::UnitsState_Func::selectionContainsOnlyArabAssassins, DAT_UnitsState::ptr)() + != FALSE) { + onlyAssassins = 1; + } + for (int i = 0; i < tileCount; ++i) { + MACRO_CALL_MEMBER(BuildingsState_Func::setupNextCandidateLocationComputeOffsets2, this)(size, 1, i, 0); + int const tile = DAT_ViewportRenderState::instance + .translationMatrix[this->buildings[buildingID].y + this->DAT_TempYOffset] + .addXgetTile + + this->buildings[buildingID].x + this->DAT_TempXOffset; + int const area = (short)DAT_TileMapState::instance.PathConnectionLayer[tile]; + int tileHeight = DAT_TileMapState::instance.HeightLayer[tile]; + if (DAT_TileMapState::instance.BuildingLayer[tile] != 0) { + tileHeight += MACRO_CALL_MEMBER(BuildingsState_Func::getBuildingHeightForBuildingID, + DAT_BuildingsState::ptr)(DAT_TileMapState::instance.BuildingLayer[tile]); + } + if (abs(buildingHeight - tileHeight) >= 16 || area == 0) { + continue; + } + int reachable; + if (onlyAssassins == 1) { + reachable + = MACRO_CALL_MEMBER(Navigation::PathFindingState_Func::calculateCanReachUsingCachedAreaLogic, + DAT_PathFindingState::ptr)(DAT_UnitsState::instance.units[unitID].tile, tile); + } else { + reachable = MACRO_CALL_MEMBER( + Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(DAT_UnitsState::instance.units[unitID].owner, unitArea, area, + MACRO_CALL_MEMBER(Units::UnitsState_Func::canAUnitClimb, DAT_UnitsState::ptr)()); + } + if (reachable != 0) { + return TRUE; + } + } + return FALSE; + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/countPlayerResources.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/countPlayerResources.cpp new file mode 100644 index 000000000..c2c0bc27a --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/countPlayerResources.cpp @@ -0,0 +1,57 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + using OpenSHC::Map::Buildings::BuildingLogicalState; + using OpenSHC::Map::Buildings::BuildingType; + + // FUNCTION: STRONGHOLDCRUSADER 0x0040C300 + void BuildingsState::countPlayerResources(int playerID) + { + int gold + = DAT_GameState::instance.playerDataArray[playerID].currentResources[OpenSHC::Game::Resources::RT_GOLD]; + + // Reset the stored resources and recount them from the storage buildings, gold is kept + for (int i = 0; i < 25; ++i) { + if (i != OpenSHC::Game::Resources::RT_GOLD) { + DAT_GameState::instance.playerDataArray[playerID].currentResources[i] = 0; + } + } + for (int i = 1; i < this->maxBuildingsCount; ++i) { + if (this->buildings[i].logicalState != BLS_NORMAL || this->buildings[i].owner != playerID) { + continue; + } + if (this->buildings[i].buildingType != BT_STOCKPILE && this->buildings[i].buildingType != BT_GRANARY + && this->buildings[i].buildingType != BT_ARMORY) { + continue; + } + if (this->buildings[i].buildingType == BT_STOCKPILE) { + for (int r = OpenSHC::Game::Resources::RT_WOOD; r <= OpenSHC::Game::Resources::RT_FLOUR; ++r) { + DAT_GameState::instance.playerDataArray[playerID].currentResources[r] + += this->buildings[i].resources[r]; + } + } else if (this->buildings[i].buildingType == BT_GRANARY) { + for (int r = OpenSHC::Game::Resources::RT_BREAD; r <= OpenSHC::Game::Resources::RT_APPLE; ++r) { + DAT_GameState::instance.playerDataArray[playerID].currentResources[r] + += this->buildings[i].resources[r]; + } + } else if (this->buildings[i].buildingType == BT_ARMORY) { + for (int r = OpenSHC::Game::Resources::RT_BOW; r < 25; ++r) { + DAT_GameState::instance.playerDataArray[playerID].currentResources[r] + += this->buildings[i].resources[r]; + } + } + } + DAT_GameState::instance.playerDataArray[playerID].currentResources[OpenSHC::Game::Resources::RT_GOLD] + = gold; + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/findClosestReachableAlliedBuilding.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/findClosestReachableAlliedBuilding.cpp new file mode 100644 index 000000000..d20c70a02 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/findClosestReachableAlliedBuilding.cpp @@ -0,0 +1,64 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x00410B10 + int BuildingsState::findClosestReachableAlliedBuilding(int unitID) + { + int closestBuildingID = 0; + int closestDistance = 1000; + int const fromArea + = (short)DAT_TileMapState::instance.PathConnectionLayer[DAT_UnitsState::instance.units[unitID].tile]; + for (int buildingID = 1; buildingID < this->maxBuildingsCount; ++buildingID) { + if (this->buildings[buildingID].logicalState == 0 + || this->buildings[buildingID].logicalState == BLS_REMOVE) { + continue; + } + if (DAT_GameState::instance.mapAndTime.playerTeams[this->buildings[buildingID].owner] + != DAT_GameState::instance.mapAndTime.playerTeams[DAT_UnitsState::instance.units[unitID].owner]) { + continue; + } + if (this->buildings[buildingID].fireDuration == 0) { + continue; + } + MACRO_CALL_MEMBER(Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)(DAT_UnitsState::instance.units[unitID].x, + DAT_UnitsState::instance.units[unitID].y, this->buildings[buildingID].buildingEntryX, + this->buildings[buildingID].buildingEntryY); + if (DAT_DirectionAlgorithmState::instance.distanceHigh >= closestDistance) { + continue; + } + int const tile = DAT_ViewportRenderState::instance + .translationMatrix[this->buildings[buildingID].buildingEntryY] + .addXgetTile + + this->buildings[buildingID].buildingEntryX; + if (MACRO_CALL_MEMBER(Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(DAT_UnitsState::instance.units[unitID].owner, fromArea, + (short)DAT_TileMapState::instance.PathConnectionLayer[tile], 0) + != 0) { + closestDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + closestBuildingID = buildingID; + } + } + if (closestBuildingID != 0) { + return closestBuildingID; + } + return 0; + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/findFreeCathedralAssemblyTile.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/findFreeCathedralAssemblyTile.cpp new file mode 100644 index 000000000..2cccfeda3 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/findFreeCathedralAssemblyTile.cpp @@ -0,0 +1,57 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040E610 + BOOLEnum BuildingsState::findFreeCathedralAssemblyTile(int playerID, int unitID) + { + if (DAT_GameState::instance.playerDataArray[playerID].specialBuildingAssemblyPoints[3].x != 0) { + int const assemblyX + = DAT_GameState::instance.playerDataArray[playerID].specialBuildingAssemblyPoints[3].x; + int const assemblyY + = DAT_GameState::instance.playerDataArray[playerID].specialBuildingAssemblyPoints[3].y; + for (int i = (short)DAT_GameState::instance.playerDataArray[playerID].someCount30; i < 49; ++i) { + // The original reads the fixed offset [3] on every iteration instead of offset [i]. + int const x = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[3].x + assemblyX; + int const y = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[3].y + assemblyY; + int const tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + int const occupantID = (short)DAT_TileMapState::instance.UnitLayer[tile]; + if (occupantID != 0 && occupantID != unitID + && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { + continue; + } + if (MACRO_CALL_MEMBER( + Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(playerID, + (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance.units[unitID].tile], + (short)DAT_TileMapState::instance.PathConnectionLayer[tile], 0) + != 0) { + this->DAT_TempXOffset = x; + this->DAT_TempYOffset = y; + if (x == DAT_UnitsState::instance.units[unitID].x + && y == DAT_UnitsState::instance.units[unitID].y) { + return FALSE; + } + return TRUE; + } + } + return FALSE; + } + return FALSE; + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/findFreeEngineerAssemblyTile.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/findFreeEngineerAssemblyTile.cpp new file mode 100644 index 000000000..17dc17fa3 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/findFreeEngineerAssemblyTile.cpp @@ -0,0 +1,94 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040E120 + int BuildingsState::findFreeEngineerAssemblyTile(int playerID, int unitID) + { + int const laddermenCount = (short)DAT_GameState::instance.playerDataArray[playerID].someCount28; + int index = (int)DAT_GameState::instance.playerDataArray[playerID].someCount27 - laddermenCount; + int isLadderman = 0; + int assemblyIndex = 0; + if (DAT_UnitsState::instance.units[unitID].unitType == Units::UT_E_LADDER) { + isLadderman = 1; + assemblyIndex = 1; + index = laddermenCount; + } + if (DAT_GameState::instance.playerDataArray[playerID].specialBuildingAssemblyPoints[assemblyIndex].x != 0) { + int const assemblyX + = DAT_GameState::instance.playerDataArray[playerID].specialBuildingAssemblyPoints[assemblyIndex].x; + int const assemblyY + = DAT_GameState::instance.playerDataArray[playerID].specialBuildingAssemblyPoints[assemblyIndex].y; + for (int i = index; i < 49; ++i) { + int const x = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[i].x + assemblyX; + int const y = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[i].y + assemblyY; + int const tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + int const occupantID = (short)DAT_TileMapState::instance.UnitLayer[tile]; + if (occupantID != 0 && occupantID != unitID + && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { + continue; + } + if (MACRO_CALL_MEMBER( + Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(playerID, + (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance.units[unitID].tile], + (short)DAT_TileMapState::instance.PathConnectionLayer[tile], 0) + != 0) { + this->DAT_TempXOffset = x; + this->DAT_TempYOffset = y; + if (x == DAT_UnitsState::instance.units[unitID].x + && y == DAT_UnitsState::instance.units[unitID].y) { + return 0; + } + return 1; + } + } + return 0; + } + for (int i = index; i < 25; ++i) { + int x; + int y; + if (isLadderman != 0) { + x = DAT_GameState::instance.playerDataArray[playerID].engineersParadegroundLocations[24 - i].x; + y = DAT_GameState::instance.playerDataArray[playerID].engineersParadegroundLocations[24 - i].y; + } else { + x = DAT_GameState::instance.playerDataArray[playerID].engineersParadegroundLocations[i].x; + y = DAT_GameState::instance.playerDataArray[playerID].engineersParadegroundLocations[i].y; + } + if ((uint)x > 399 || (uint)y > 399 + || DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + continue; + } + int const occupantID + = (short)DAT_TileMapState::instance + .UnitLayer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x]; + if (occupantID != 0 && occupantID != unitID + && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { + continue; + } + this->DAT_TempXOffset = x; + this->DAT_TempYOffset = y; + if (x == DAT_UnitsState::instance.units[unitID].x && y == DAT_UnitsState::instance.units[unitID].y) { + return 0; + } + return 1; + } + return 0; + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/getPriceForDisbandedUnitType.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/getPriceForDisbandedUnitType.cpp new file mode 100644 index 000000000..6f449fd08 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/getPriceForDisbandedUnitType.cpp @@ -0,0 +1,77 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040C800 + void BuildingsState::getPriceForDisbandedUnitType(UnitType unitType, int* outPrice) + { + switch (unitType) { + case Units::UT_E_ARCHER: + *outPrice = 12; + return; + case Units::UT_E_SPEAR: + *outPrice = 8; + return; + case Units::UT_E_PIKE: + *outPrice = 20; + return; + case Units::UT_E_MACE: + *outPrice = 20; + return; + case Units::UT_E_XBOW: + *outPrice = 20; + return; + case Units::UT_E_SWORD: + *outPrice = 40; + return; + case Units::UT_E_KNIGHT: + *outPrice = 40; + return; + case Units::UT_E_LADDER: + *outPrice = 1; + return; + case Units::UT_E_ENGINEER: + *outPrice = 30; + return; + case Units::UT_E_MONK: + *outPrice = 10; + return; + case Units::UT_TUNNELER: + *outPrice = 20; + return; + case Units::UT_S_CATAPULT: + *outPrice = this->buildingCosts[BT_CATAPULT].requiredGold; + return; + case Units::UT_S_TREBUCHET: + *outPrice = this->buildingCosts[BT_TREBUCHET].requiredGold; + return; + case Units::UT_S_MANGONEL: + *outPrice = this->buildingCosts[BT_MANGONEL].requiredGold; + return; + case Units::UT_S_BALLISTA: + *outPrice = this->buildingCosts[BT_BALLISTA].requiredGold; + return; + case Units::UT_S_FBALLISTA: + *outPrice = this->buildingCosts[BT_FIREBALLISTA].requiredGold; + return; + case Units::UT_S_SHIELD: + *outPrice = this->buildingCosts[BT_SHIELD].requiredGold; + return; + // the original pairs the tower with the battering ram's cost and vice versa + case Units::UT_S_TOWER: + *outPrice = this->buildingCosts[BT_BATTERINGRAM].requiredGold; + return; + case Units::UT_S_BATTERINGRAM: + *outPrice = this->buildingCosts[BT_SIEGETOWER].requiredGold; + return; + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/growWheat.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/growWheat.cpp new file mode 100644 index 000000000..12129ed99 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/growWheat.cpp @@ -0,0 +1,84 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" + +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040CC30 + void BuildingsState::growWheat(int buildingID) + { + // The damage layer holds the growth stage of the wheat on the field tiles. + // Not all stage counters are used, the compiler removes those. + int empty = 0; + int seeded = 0; + int stage2To4 = 0; + int stage5To7 = 0; + int stage8To10 = 0; + int growing = 0; + int stage101 = 0; + int stage102 = 0; + int stage103To119 = 0; + int stage120 = 0; + int stage121 = 0; + for (int i = 0; i < 36; ++i) { + int tile = this->buildings[buildingID].tileRefs[i]; + if ((char)DAT_TileMapState::instance.DamageLayer[tile] >= 2 + && (char)DAT_TileMapState::instance.DamageLayer[tile] < 103) { + ++DAT_TileMapState::instance.DamageLayer[tile]; + } + char stage = DAT_TileMapState::instance.DamageLayer[tile]; + if (stage < 1) { + ++empty; + } else if (stage < 2) { + ++seeded; + } else if (stage < 5) { + ++stage2To4; + } else if (stage < 8) { + ++stage5To7; + } else if (stage < 11) { + ++stage8To10; + } else if (stage < 101) { + ++growing; + } else if (stage < 102) { + ++stage101; + } else if (stage < 103) { + ++stage102; + } else if (stage < 120) { + ++stage103To119; + } else if (stage == 120) { + ++stage120; + } else if (stage == 121) { + ++stage121; + } + } + + if (stage121 >= 4) { + this->buildings[buildingID].farmGrowState = 8; + return; + } + if (growing >= 12 || (growing != 0 && stage120 != 0)) { + this->buildings[buildingID].farmGrowState = 6; + return; + } + if (stage121 != 0) { + this->buildings[buildingID].farmGrowState = 8; + return; + } + if (empty != 0) { + this->buildings[buildingID].farmGrowState = 3; + } else if (stage120 != 0) { + this->buildings[buildingID].farmGrowState = 3; + } else if (stage102 != 0) { + this->buildings[buildingID].farmGrowState = 3; + } else if (stage103To119 != 0) { + this->buildings[buildingID].farmGrowState = 3; + } else { + this->buildings[buildingID].farmGrowState = seeded != 0 ? 5 : 2; + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/isSpaceAvailableAtUnitRallyPoint.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/isSpaceAvailableAtUnitRallyPoint.cpp new file mode 100644 index 000000000..22c521cd4 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/isSpaceAvailableAtUnitRallyPoint.cpp @@ -0,0 +1,114 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040D6E0 + BOOLEnum BuildingsState::isSpaceAvailableAtUnitRallyPoint(int playerID, int group, int unitID) + { + int const requestedGroup = group; + if (DAT_GameSynchronyState::instance.currentGameMode != 0 + && DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID] == -1 + && DAT_GameSynchronyState::instance.currentAIArray[playerID] != 0 + && DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType != 2) { + int const keepID = DAT_GameState::instance.playerDataArray[playerID].keep.id; + if (keepID == 0) { + return FALSE; + } + this->DAT_TempXOffset = DAT_BuildingsState::instance.buildings[keepID].someX; + this->DAT_TempYOffset = DAT_BuildingsState::instance.buildings[keepID].someY; + return TRUE; + } + if (group == 6) { + group = 5; + } + int offset = DAT_GameState::instance.playerDataArray[playerID].rallySearchOffsetsUnk[group]; + if (DAT_GameState::instance.playerDataArray[playerID].barracksAssemblyPoints[requestedGroup].x != 0) { + if (requestedGroup == 6) { + offset = DAT_GameState::instance.playerDataArray[playerID].rallySearchOffset; + group = requestedGroup; + } else if (requestedGroup == 5) { + offset -= DAT_GameState::instance.playerDataArray[playerID].rallySearchOffset; + } + int const assemblyX = DAT_GameState::instance.playerDataArray[playerID].barracksAssemblyPoints[group].x; + int const assemblyY = DAT_GameState::instance.playerDataArray[playerID].barracksAssemblyPoints[group].y; + for (int i = offset; i < 49; ++i) { + int const x = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[i].x + assemblyX; + int const y = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[i].y + assemblyY; + int const tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; + int const occupantID = (short)DAT_TileMapState::instance.UnitLayer[tile]; + if (occupantID != 0 && occupantID != unitID + && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { + continue; + } + if (MACRO_CALL_MEMBER( + Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(playerID, + (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance.units[unitID].tile], + (short)DAT_TileMapState::instance.PathConnectionLayer[tile], 0) + != 0) { + this->DAT_TempXOffset = x; + this->DAT_TempYOffset = y; + if (x == DAT_UnitsState::instance.units[unitID].x + && y == DAT_UnitsState::instance.units[unitID].y) { + return FALSE; + } + return TRUE; + } + } + return FALSE; + } + if (DAT_GameState::instance.playerDataArray[playerID].barracks.id <= 0) { + return FALSE; + } + if (requestedGroup == 6) { + if (DAT_GameState::instance.playerDataArray[playerID].barracksAssemblyPoints[5].x != 0) { + offset = DAT_GameState::instance.playerDataArray[playerID].rallySearchOffset; + } + } else if (requestedGroup == 5) { + if (DAT_GameState::instance.playerDataArray[playerID].barracksAssemblyPoints[6].x != 0) { + offset -= DAT_GameState::instance.playerDataArray[playerID].rallySearchOffset; + } + } + for (int i = offset; i < 72; ++i) { + int const x + = DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations[group][i].x; + int const y + = DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations[group][i].y; + if ((uint)x > 399 || (uint)y > 399 + || DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[y * 400 + x] == 0) { + continue; + } + int const occupantID + = (short)DAT_TileMapState::instance + .UnitLayer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x]; + if (occupantID != 0 && occupantID != unitID + && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { + continue; + } + this->DAT_TempXOffset = x; + this->DAT_TempYOffset = y; + if (x == DAT_UnitsState::instance.units[unitID].x && y == DAT_UnitsState::instance.units[unitID].y) { + return FALSE; + } + return TRUE; + } + return FALSE; + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/processBuildingIDsNeedPeasantsQueue.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/processBuildingIDsNeedPeasantsQueue.cpp new file mode 100644 index 000000000..c61f9bd62 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/processBuildingIDsNeedPeasantsQueue.cpp @@ -0,0 +1,139 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x0040F9D0 + void BuildingsState::processBuildingIDsNeedPeasantsQueue(undefined4 playerID) + { + // Assigns peasants to the free worker slots of the queued buildings + for (int i = 0; i < (int)this->DAT_CountOfBuildingsNeedPeasants; ++i) { + int buildingID = (short)this->DAT_BuildingIDsNeedPeasantsQueue[i]; + if (this->buildings[buildingID].buildingTypeBasedEmployeeCount == 1) { + UnitType workerType = (UnitType)DAT_BuildingDefinedData::instance + .WorkerTypeForBuildingType[this->buildings[buildingID].buildingType]; + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(workerType, buildingID, playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 0); + continue; + } + + if (this->buildings[buildingID].buildingType == BT_QUARRY) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)(buildingID, 0) + == FALSE) { + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(OpenSHC::Map::Units::UT_QUARRYMASON, buildingID, playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 0); + } + + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)(buildingID, 1) + == FALSE) { + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(OpenSHC::Map::Units::UT_QUARRYMASON, buildingID, playerID, 1); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 1); + } + + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)(buildingID, 2) + == FALSE) { + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(OpenSHC::Map::Units::UT_QUARRYMASON, buildingID, playerID, 2); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 2); + } + + } else if (this->buildings[buildingID].buildingType == BT_IRONMINE) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)(buildingID, 0) + == FALSE) { + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(OpenSHC::Map::Units::UT_MINER, buildingID, playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 0); + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)(buildingID, 1) + == FALSE) { + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(OpenSHC::Map::Units::UT_TRANSPORTMINER, buildingID, playerID, 1); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 1); + } + } else { + // The mill, water pot and cathedral share the code for their three workers + UnitType workerType; + if (this->buildings[buildingID].buildingType == BT_MILL) { + workerType = (UnitType)DAT_BuildingDefinedData::instance.WorkerTypeForBuildingType[BT_MILL]; + } else if (this->buildings[buildingID].buildingType == BT_WATERPOT) { + workerType = (UnitType)DAT_BuildingDefinedData::instance.WorkerTypeForBuildingType[BT_WATERPOT]; + } else if (this->buildings[buildingID].buildingType == BT_CHURCH) { + workerType = (UnitType)DAT_BuildingDefinedData::instance.WorkerTypeForBuildingType[BT_CHURCH]; + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)( + buildingID, 0) + == FALSE) { + int unitID + = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(workerType, buildingID, playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, + this)(buildingID, unitID, 0); + } + + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)( + buildingID, 1) + == FALSE) { + int unitID + = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(workerType, buildingID, playerID, 1); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, + this)(buildingID, unitID, 1); + } + + continue; + } else if (this->buildings[buildingID].buildingType == BT_CATHEDRAL) { + workerType + = (UnitType)DAT_BuildingDefinedData::instance.WorkerTypeForBuildingType[BT_CATHEDRAL]; + } else { + continue; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)(buildingID, 0) + == FALSE) { + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(workerType, buildingID, playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 0); + } + + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)(buildingID, 1) + == FALSE) { + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(workerType, buildingID, playerID, 1); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 1); + } + + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::hasWorker, this)(buildingID, 2) + == FALSE) { + int unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::assignPeasantToBuilding, + DAT_UnitsState::ptr)(workerType, buildingID, playerID, 2); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::assignWorkerToBuilding, this)( + buildingID, unitID, 2); + } + } + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/processDamageToUnitsOnBuilding.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/processDamageToUnitsOnBuilding.cpp new file mode 100644 index 000000000..c82cdd5e5 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/processDamageToUnitsOnBuilding.cpp @@ -0,0 +1,52 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/States/UnitState.hpp" + +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x004106D0 + void BuildingsState::processDamageToUnitsOnBuilding(int buildingID, int damagePercentage) + { + int i = 0; + int const size = this->buildings[buildingID].widthOrHeight; + int const x = this->buildings[buildingID].x; + int const y = this->buildings[buildingID].y; + do { + MACRO_CALL_MEMBER(TileMapState_Func::getBuildingSizeIndexMappingData, DAT_TileMapState::ptr)(i, size); + for (int unitID = (short)DAT_TileMapState::instance + .UnitLayer[DAT_ViewportRenderState::instance + .translationMatrix[y + DAT_TileMapState::instance.buildingY] + .addXgetTile + + DAT_TileMapState::instance.buildingX + x]; + unitID != 0; unitID = (short)DAT_UnitsState::instance.units[unitID].nextUnitOnTheSameTile) { + int const maxHealth = DAT_UnitsState::instance.units[unitID].maxHealth; + DAT_UnitsState::instance.units[unitID].health += maxHealth * damagePercentage / -100; + if (DAT_UnitsState::instance.units[unitID].health <= 0) { + DAT_UnitsState::instance.units[unitID].health = 0; + DAT_UnitsState::instance.units[unitID].dying = 1; + DAT_UnitsState::instance.units[unitID].animationCycleNumber = 0; + DAT_UnitsState::instance.units[unitID].state.generic = Units::States::US_STONE_DEATH_01; + } + int healthPercentage; + if (maxHealth == 0) { + healthPercentage = 100; + } else { + healthPercentage = DAT_UnitsState::instance.units[unitID].health * 100 / maxHealth; + } + DAT_UnitsState::instance.units[unitID].healthPercentage = healthPercentage; + DAT_UnitsState::instance.units[unitID].healthbar = (short)healthPercentage / 10; + } + ++i; + } while (i < DAT_TileMapState::instance.constructionTileCount); + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/processResourceLoss.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/processResourceLoss.cpp new file mode 100644 index 000000000..e6ca808c8 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/processResourceLoss.cpp @@ -0,0 +1,82 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0041BD70 + void BuildingsState::processResourceLoss(int playerID, ResourceType resourceType, int amount, int param_4) + { + int foundStorage = 0; + if (resourceType == 0) { + return; + } + if (DAT_GameCore::instance.gameMode_2 == Game::GM_SIEGE_THAT) { + if (param_4 == 0) { + DAT_GameState::instance.playerDataArray[playerID].startResources[resourceType] -= amount; + } + return; + } + BuildingType const storageType + = MACRO_CALL_MEMBER(BuildingsState_Func::getBuildingStorageTypeForResourceType, this)(resourceType); + if (storageType == BT_MANORHOUSE) { + if (param_4 == 0) { + DAT_GameState::instance.playerDataArray[playerID].currentResources[resourceType] -= amount; + } + return; + } + for (int buildingID = 1; buildingID < this->maxBuildingsCount; ++buildingID) { + if (this->buildings[buildingID].logicalState != BLS_NORMAL) { + continue; + } + if (this->buildings[buildingID].buildingType != storageType + || this->buildings[buildingID].owner != playerID + || this->buildings[buildingID].resources[resourceType] <= 0) { + continue; + } + if (param_4 == 0) { + this->buildings[buildingID].someResourceNumber = 0; + } + foundStorage = 1; + if (this->buildings[buildingID].resources[resourceType] >= amount) { + if (param_4 != 0) { + this->buildings[buildingID].someResourceNumber = amount; + } else { + this->buildings[buildingID].resources[resourceType] -= amount; + DAT_GameState::instance.playerDataArray[playerID].currentResources[resourceType] -= amount; + } + amount = 0; + } else { + amount -= this->buildings[buildingID].resources[resourceType]; + if (param_4 != 0) { + this->buildings[buildingID].someResourceNumber + = this->buildings[buildingID].resources[resourceType]; + } else { + int const left = this->buildings[buildingID].resources[resourceType]; + this->buildings[buildingID].resources[resourceType] = 0; + DAT_GameState::instance.playerDataArray[playerID].currentResources[resourceType] -= left; + } + } + if (param_4 == 0) { + MACRO_CALL_MEMBER(BuildingsState_Func::computeResourceSumForBuilding, this)(buildingID); + MACRO_CALL_MEMBER(BuildingsState_Func::countPlayerResources, this)(playerID); + } + MACRO_CALL_MEMBER(TileMapState_Func::updateBuildingGraphicsLayer, DAT_TileMapState::ptr)(buildingID); + } + if (foundStorage == 0) { + DAT_GameState::instance.playerDataArray[playerID].currentResources[resourceType] = 0; + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp new file mode 100644 index 000000000..12460172d --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp @@ -0,0 +1,96 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_TerrainDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +#include + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040EA00 + BOOLEnum BuildingsState::setNewOnFarmDestination(int buildingID) + { + uint const entryX = this->buildings[buildingID].buildingEntryX; + uint const entryY = this->buildings[buildingID].buildingEntryY; + bool const onMap = entryX <= 399 && entryY <= 399 + && DAT_ViewportRenderState::instance.DAT_BinaryTileMap400x400[entryY * 400 + entryX] != 0; + if (!onMap) { + return FALSE; + } + int const entryArea + = (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_ViewportRenderState::instance.translationMatrix[entryY].addXgetTile + + entryX]; + // The header has no field for the short at 0x26e. + int direction = *(short*)this->buildings[buildingID].padding_0x26e == 0x51 ? 4 : 2; + for (int i = 0; i < 24; ++i) { + int const tile = this->buildings[buildingID].tileRefs[i]; + int const row = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const damage = (char)DAT_TileMapState::instance.DamageLayer[tile]; + DAT_BuildingsState::instance.farmerDestinationTile = tile; + int const height + = MACRO_CALL_MEMBER(TileMapState_Func::getTotalHeightAtTile, DAT_TileMapState::ptr)(tile); + if (this->buildings[buildingID].farmGrowState == 5) { + if ((uint)(damage - 14) > 13) { + continue; + } + for (int k = 0; k < 4; ++k) { + int const target = DAT_TileMapState::instance.directionTranslationMatrix[row][direction] + tile; + DAT_BuildingsState::instance.hopFarmerDestinationOffsetX + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction] + .int_.xOffset; + DAT_BuildingsState::instance.hopFarmerDestinationOffsetY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction] + .int_.yOffset; + if ((short)DAT_TileMapState::instance.PathConnectionLayer[target] == entryArea + && abs((int)MACRO_CALL_MEMBER( + TileMapState_Func::getTotalHeightAtTile, DAT_TileMapState::ptr)(target) + - height) + < 16) { + return TRUE; + } + direction += 2; + if (direction >= 8) { + direction = 0; + } + } + } else if (this->buildings[buildingID].farmGrowState == 3) { + if ((uint)damage > 1) { + continue; + } + for (int k = 0; k < 4; ++k) { + int const target = DAT_TileMapState::instance.directionTranslationMatrix[row][direction] + tile; + DAT_BuildingsState::instance.hopFarmerDestinationOffsetX + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction] + .int_.xOffset; + DAT_BuildingsState::instance.hopFarmerDestinationOffsetY + = DAT_TerrainDefinedData::instance.clockwiseCardinalTranslationMatrix[direction] + .int_.yOffset; + if ((short)DAT_TileMapState::instance.PathConnectionLayer[target] == entryArea + && abs((int)MACRO_CALL_MEMBER( + TileMapState_Func::getTotalHeightAtTile, DAT_TileMapState::ptr)(target) + - height) + < 16) { + return TRUE; + } + direction += 2; + if (direction >= 8) { + direction = 0; + } + } + } else { + return FALSE; + } + } + return FALSE; + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/setupBarracksCampgroundPositions.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/setupBarracksCampgroundPositions.cpp new file mode 100644 index 000000000..6e2d83d92 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/setupBarracksCampgroundPositions.cpp @@ -0,0 +1,117 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040D120 + void BuildingsState::setupBarracksCampgroundPositions(int playerID) + { + int paradeGround1 = 0; + int paradeGround2 = 0; + int paradeGround3 = 0; + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations), 0, + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations); + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocationsTotal = 0; + if (DAT_GameState::instance.playerDataArray[playerID].barracks.id <= 0) { + return; + } + int buildingID; + for (buildingID = 1; buildingID < this->maxBuildingsCount; ++buildingID) { + if (this->buildings[buildingID].logicalState == 0 + || this->buildings[buildingID].logicalState == BLS_REMOVE + || this->buildings[buildingID].owner != playerID) { + continue; + } + if (this->buildings[buildingID].buildingType != BT_PARADEGROUND2 + && this->buildings[buildingID].buildingType != BT_PARADEGROUND3 + && this->buildings[buildingID].buildingType != BT_PARADEGROUND4) { + continue; + } + // quarryStockpileID holds the linked building here. + if (DAT_BuildingsState::instance.buildings[DAT_BuildingsState::instance.buildings[buildingID].quarryStockpileID].buildingType + != BT_BARRACKS) { + continue; + } + if (this->buildings[buildingID].buildingType == BT_PARADEGROUND2) { + paradeGround1 = buildingID; + } else if (this->buildings[buildingID].buildingType == BT_PARADEGROUND3) { + paradeGround2 = buildingID; + } else if (this->buildings[buildingID].buildingType == BT_PARADEGROUND4) { + paradeGround3 = buildingID; + } + } + if (paradeGround1 == 0 || paradeGround2 == 0 || paradeGround3 == 0) { + return; + } + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocationsTotal = 72; + for (int formation = 0; formation < 6; ++formation) { + int const shape = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_1[formation]; + // The loop index above is reused, so an unknown selector keeps the previous building. + int const selector0 + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_2[formation][0]; + if (selector0 == 0) { + buildingID = paradeGround1; + } else if (selector0 == 1) { + buildingID = paradeGround2; + } else if (selector0 == 2) { + buildingID = paradeGround3; + } + int const x0 = this->buildings[buildingID].x; + int const y0 = this->buildings[buildingID].y; + for (int i = 0; i < 24; ++i) { + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations[formation][i].x + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x0; + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations[formation][i].y + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y0; + } + int const selector1 + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_2[formation][1]; + if (selector1 == 0) { + buildingID = paradeGround1; + } else if (selector1 == 1) { + buildingID = paradeGround2; + } else if (selector1 == 2) { + buildingID = paradeGround3; + } + int const x1 = this->buildings[buildingID].x; + int const y1 = this->buildings[buildingID].y; + for (int i = 0; i < 24; ++i) { + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations[formation][24 + i].x + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x1; + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations[formation][24 + i].y + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y1; + } + int const selector2 + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_2[formation][2]; + if (selector2 == 0) { + buildingID = paradeGround1; + } else if (selector2 == 1) { + buildingID = paradeGround2; + } else if (selector2 == 2) { + buildingID = paradeGround3; + } + int const x2 = this->buildings[buildingID].x; + int const y2 = this->buildings[buildingID].y; + for (int i = 0; i < 24; ++i) { + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations[formation][48 + i].x + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x2; + DAT_GameState::instance.playerDataArray[playerID].barracksParadegroundLocations[formation][48 + i].y + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y2; + } + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp new file mode 100644 index 000000000..bb61e74a8 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp @@ -0,0 +1,130 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040D400 + void BuildingsState::setupMercenaryPostCampgroundPositions(int playerID) + { + int paradeGround1 = 0; + int paradeGround2 = 0; + int paradeGround3 = 0; + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + sizeof(DAT_GameState::instance.playerDataArray[playerID].structure), 0, + &DAT_GameState::instance.playerDataArray[playerID].structure); + DAT_GameState::instance.playerDataArray[playerID].structureRelated1 = 0; + if (DAT_GameState::instance.playerDataArray[playerID].mercenaryPost.id <= 0) { + return; + } + int buildingID; + for (buildingID = 1; buildingID < this->maxBuildingsCount; ++buildingID) { + if (this->buildings[buildingID].logicalState == 0 + || this->buildings[buildingID].logicalState == BLS_REMOVE + || this->buildings[buildingID].owner != playerID) { + continue; + } + if (this->buildings[buildingID].buildingType != BT_PARADEGROUND2 + && this->buildings[buildingID].buildingType != BT_PARADEGROUND3 + && this->buildings[buildingID].buildingType != BT_PARADEGROUND4) { + continue; + } + // quarryStockpileID holds the linked building here. + if (DAT_BuildingsState::instance.buildings[DAT_BuildingsState::instance.buildings[buildingID].quarryStockpileID].buildingType + != BT_MERCENARYPOST) { + continue; + } + if (this->buildings[buildingID].buildingType == BT_PARADEGROUND2) { + paradeGround1 = buildingID; + } else if (this->buildings[buildingID].buildingType == BT_PARADEGROUND3) { + paradeGround2 = buildingID; + } else if (this->buildings[buildingID].buildingType == BT_PARADEGROUND4) { + paradeGround3 = buildingID; + } + } + if (paradeGround1 == 0 || paradeGround2 == 0 || paradeGround3 == 0) { + return; + } + DAT_GameState::instance.playerDataArray[playerID].structureRelated1 = 72; + // The header splits this 6 x 72 location table into separately named fields. + for (int formation = 0; formation < 6; ++formation) { + int const shape = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_1[formation]; + // The loop index above is reused, so an unknown selector keeps the previous building. + int const selector0 + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_2[formation][0]; + if (selector0 == 0) { + buildingID = paradeGround1; + } else if (selector0 == 1) { + buildingID = paradeGround2; + } else if (selector0 == 2) { + buildingID = paradeGround3; + } + int const x0 = this->buildings[buildingID].x; + int const y0 = this->buildings[buildingID].y; + for (int i = 0; i < 24; ++i) { + (&DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + i] + .x + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x0; + (&DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + i] + .y + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y0; + } + int const selector1 + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_2[formation][1]; + if (selector1 == 0) { + buildingID = paradeGround1; + } else if (selector1 == 1) { + buildingID = paradeGround2; + } else if (selector1 == 2) { + buildingID = paradeGround3; + } + int const x1 = this->buildings[buildingID].x; + int const y1 = this->buildings[buildingID].y; + for (int i = 0; i < 24; ++i) { + (&DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + 24 + i] + .x + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x1; + (&DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + 24 + i] + .y + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y1; + } + int const selector2 + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_2[formation][2]; + if (selector2 == 0) { + buildingID = paradeGround1; + } else if (selector2 == 1) { + buildingID = paradeGround2; + } else if (selector2 == 2) { + buildingID = paradeGround3; + } + int const x2 = this->buildings[buildingID].x; + int const y2 = this->buildings[buildingID].y; + for (int i = 0; i < 24; ++i) { + (&DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + 48 + i] + .x + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x2; + (&DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + 48 + i] + .y + = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y2; + } + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/spawnRandomFireEffectOnBuilding.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/spawnRandomFireEffectOnBuilding.cpp new file mode 100644 index 000000000..55f96e15b --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/spawnRandomFireEffectOnBuilding.cpp @@ -0,0 +1,64 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Audio/SFX/SFXState.func.hpp" +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/DE/SHCDE/eSFX.hpp" +#include "OpenSHC/Map/Entities/EntityType.hpp" + +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_SFXState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x00410800 + void BuildingsState::spawnRandomFireEffectOnBuilding(int buildingID, undefined4 playerID) + { + if (this->buildings[buildingID].fireDuration < 400) { + return; + } + int const size = this->buildings[buildingID].widthOrHeight; + if (size <= 3) { + if ((this->buildings[buildingID].fireRelatedRNG1 + this->buildings[buildingID].fireDuration) + % (24 - size * 5) + != 0) { + return; + } + } else if (size <= 6) { + if ((this->buildings[buildingID].fireRelatedRNG1 + this->buildings[buildingID].fireDuration) + % ((8 - size) * 5) + != 0) { + return; + } + } else { + if ((this->buildings[buildingID].fireRelatedRNG1 + this->buildings[buildingID].fireDuration) + % ((16 - size) * 5) + != 0) { + return; + } + } + int const height + = DAT_TileMapState::instance.HeightLayer[this->buildings[buildingID].currentTilePositionAdjusted]; + int const x = (SEC_RNG::instance.currentNumber2 % (int)this->buildings[buildingID].widthOrHeight + + this->buildings[buildingID].x) + * 8; + int const y = ((SEC_RNG::instance.currentNumber2 >> 8) % (int)this->buildings[buildingID].widthOrHeight + + this->buildings[buildingID].y) + * 8; + // EntityType has no constant for 32. + MACRO_CALL_MEMBER(Entities::EntityState_Func::spawnProjectileEntity, DAT_EntityState::ptr)( + 5, playerID, 0, x, y, height, x, y, height, (Entities::EntityType)32, 0); + MACRO_CALL_MEMBER(Random::RNG_Func::nextRandomNumber1, SEC_RNG::ptr)(); + if ((SEC_RNG::instance.currentNumber1 & 1) != 0) { + MACRO_CALL_MEMBER(Audio::SFX::SFXState_Func::playSFXAtLocation, DAT_SFXState::ptr)( + x / 8, y / 8, DE::SHCDE::FX_FIRE_POP); + } + } + + } +} +} diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/updateBuildingAreaTileGraphics.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/updateBuildingAreaTileGraphics.cpp new file mode 100644 index 000000000..7485f4ad3 --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/updateBuildingAreaTileGraphics.cpp @@ -0,0 +1,30 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" + +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/GMTotalPicturesProcessed.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x0040EFD0 + void BuildingsState::updateBuildingAreaTileGraphics(int param_1) + { + for (int i = 0; i < 23; ++i) { + int tile = this->buildings[param_1].tileRefs[4 + i]; + int const damage = (char)DAT_TileMapState::instance.DamageLayer[tile]; + int const stage + = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::getRubbleGraphicStageForDamageLevel, + DAT_TileMapState::ptr)(damage); + int gfx = GMTotalPicturesProcessed::instance[14] + 55 + stage; + if (gfx != DAT_TileMapState::instance.GfxLayer[tile]) { + DAT_TileMapState::instance.GfxLayer[tile] = gfx; + DAT_TileMapState::instance.MiscDisplayLayer[tile] &= 0xfff3; + } + } + } + + } +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 7f65823de..39d5a825a 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -4345,7 +4345,7 @@ SHC_3BB0A8C1_0x0040C1A0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040C1F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0040C300 | 0.0% | Pending +SHC_3BB0A8C1_0x0040C300 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040C4B0 | 100.0% | Reimplemented @@ -4763,7 +4763,7 @@ SHC_3BB0A8C1_0x0040C7F7 | 0.0% | Pending SHC_3BB0A8C1_0x0040C7F8 | 0.0% | Pending -SHC_3BB0A8C1_0x0040C800 | 0.0% | Pending +SHC_3BB0A8C1_0x0040C800 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040C918 | 0.0% | Pending @@ -4961,21 +4961,21 @@ SHC_3BB0A8C1_0x0040CB90 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040CBB0 | 0.0% | Pending -SHC_3BB0A8C1_0x0040CC30 | 0.0% | Pending +SHC_3BB0A8C1_0x0040CC30 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040CF20 | 0.0% | Pending SHC_3BB0A8C1_0x0040D090 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0040D120 | 0.0% | Pending +SHC_3BB0A8C1_0x0040D120 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040D190 | 0.0% | Pending -SHC_3BB0A8C1_0x0040D400 | 0.0% | Pending +SHC_3BB0A8C1_0x0040D400 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040D470 | 0.0% | Pending -SHC_3BB0A8C1_0x0040D6E0 | 0.0% | Pending +SHC_3BB0A8C1_0x0040D6E0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040D910 | 0.0% | Pending @@ -4985,7 +4985,7 @@ SHC_3BB0A8C1_0x0040DC70 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040E040 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0040E120 | 0.0% | Pending +SHC_3BB0A8C1_0x0040E120 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040E330 | 100.0% | Reimplemented @@ -4993,7 +4993,7 @@ SHC_3BB0A8C1_0x0040E410 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040E550 | 0.0% | Pending -SHC_3BB0A8C1_0x0040E610 | 0.0% | Pending +SHC_3BB0A8C1_0x0040E610 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040E660 | 0.0% | Pending @@ -5005,7 +5005,7 @@ SHC_3BB0A8C1_0x0040E900 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040E990 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0040EA00 | 0.0% | Pending +SHC_3BB0A8C1_0x0040EA00 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040EBF0 | 100.0% | Reimplemented @@ -5017,7 +5017,7 @@ SHC_3BB0A8C1_0x0040EE30 | 0.0% | Pending SHC_3BB0A8C1_0x0040EF40 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0040EFD0 | 0.0% | Pending +SHC_3BB0A8C1_0x0040EFD0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040F040 | 100.0% | Reimplemented @@ -5057,7 +5057,7 @@ SHC_3BB0A8C1_0x0040F790 | 0.0% | Pending SHC_3BB0A8C1_0x0040F8A0 | 0.0% | Pending -SHC_3BB0A8C1_0x0040F9D0 | 0.0% | Pending +SHC_3BB0A8C1_0x0040F9D0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0040FC40 | 100.0% | Reimplemented @@ -5139,13 +5139,13 @@ SHC_3BB0A8C1_0x00410290 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00410320 | 0.0% | Pending -SHC_3BB0A8C1_0x00410440 | 0.0% | Pending +SHC_3BB0A8C1_0x00410440 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004105F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004105F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004106D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004106D0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00410800 | 0.0% | Pending +SHC_3BB0A8C1_0x00410800 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00410920 | 100.0% | Reimplemented @@ -5373,7 +5373,7 @@ SHC_3BB0A8C1_0x004109E2 | 0.0% | Pending SHC_3BB0A8C1_0x004109F0 | 0.0% | Pending -SHC_3BB0A8C1_0x00410B10 | 0.0% | Pending +SHC_3BB0A8C1_0x00410B10 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00410C40 | 100.0% | Reimplemented @@ -6109,7 +6109,7 @@ SHC_3BB0A8C1_0x0041BC10 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0041BCA0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0041BD70 | 0.0% | Pending +SHC_3BB0A8C1_0x0041BD70 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0041BE30 | 0.0% | Pending From 9799046d90b5b820cb17f19bfa8f1c86c5f1ff77 Mon Sep 17 00:00:00 2001 From: Jake Vawter Date: Tue, 27 Oct 2026 19:14:40 -0400 Subject: [PATCH 3/7] setNewOnFarmDestination: write the damage-stage checks as ranges The original's add/cmp/ja is MSVC's folded range check, so damage < 14 || damage > 27 and damage < 0 || damage > 1 give the same code as the unsigned casts (still 100%). Co-Authored-By: Claude Opus 5.5 --- .../Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp index 12460172d..06dcf0452 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp @@ -37,7 +37,7 @@ namespace Map { int const height = MACRO_CALL_MEMBER(TileMapState_Func::getTotalHeightAtTile, DAT_TileMapState::ptr)(tile); if (this->buildings[buildingID].farmGrowState == 5) { - if ((uint)(damage - 14) > 13) { + if (damage < 14 || damage > 27) { continue; } for (int k = 0; k < 4; ++k) { @@ -61,7 +61,7 @@ namespace Map { } } } else if (this->buildings[buildingID].farmGrowState == 3) { - if ((uint)damage > 1) { + if (damage < 0 || damage > 1) { continue; } for (int k = 0; k < 4; ++k) { From f4af45286037bbb98e3503d5c998eee64d63cb6e Mon Sep 17 00:00:00 2001 From: Jake Vawter Date: Wed, 28 Oct 2026 08:36:29 -0400 Subject: [PATCH 4/7] buildings: follow main's header updates Renamed Building fields (field202_0x2c8 -> field201, field162_0x280 -> field161, ...), someFarmRelatedState instead of padding_0x26e, mercenaryOutpostCampgroundLocations as [6][3][24], and casts made redundant by the short/int types dropped. The renames also cover main's own users so the BuildingsState sources compile again. Co-Authored-By: Claude Opus 5.5 --- .../AI/AIVState/aiPlaceAIVBuilding.cpp | 2 +- .../countBuildingsForPlayer.cpp | 2 +- .../Buildings/BuildingsState/countFarms.cpp | 2 +- .../findFreeCathedralAssemblyTile.cpp | 2 +- .../findFreeEngineerAssemblyTile.cpp | 4 +-- .../hasBuildingEntranceFlagForOrientation.cpp | 8 +++--- .../hasBuildingExitFlagForOrientation.cpp | 8 +++--- .../initializeGatePathfindingForOwner.cpp | 2 +- .../isSpaceAvailableAtUnitRallyPoint.cpp | 4 +-- .../processBuildingIDsNeedPeasantsQueue.cpp | 4 +-- .../processDamageToUnitsOnBuilding.cpp | 4 +-- .../setNewOnFarmDestination.cpp | 2 +- .../setupMercenaryPostCampgroundPositions.cpp | 25 +++++++++---------- .../Map/Version/UpgradeBuildingField4.cpp | 2 +- 14 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp index 5e947a8bf..194e8a59b 100644 --- a/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp +++ b/src/OpenSHC/AI/AIVState/aiPlaceAIVBuilding.cpp @@ -297,7 +297,7 @@ namespace AI { orientation); int const placedID = DAT_TileMapState::instance.placedBuildingID; if (!DAT_TileMapState::instance.buildingPlacementFail) { - DAT_BuildingsState::instance.buildings[placedID].field202_0x2c8 = 1; + DAT_BuildingsState::instance.buildings[placedID].field201_0x2c8 = 1; MACRO_CALL_MEMBER(AICState_Func::setupWorkshopProductionType, DAT_AICState::ptr)(playerID, placedID); } if (!DAT_TileMapState::instance.buildingPlacementFail) { diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/countBuildingsForPlayer.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/countBuildingsForPlayer.cpp index e83869f67..994ba8785 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/countBuildingsForPlayer.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/countBuildingsForPlayer.cpp @@ -14,7 +14,7 @@ namespace Map { if (this->buildings[buildingID].logicalState == BLS_NORMAL && this->buildings[buildingID].owner == playerID && this->buildings[buildingID].buildingType == buildingType - && (includeBool == 0 || this->buildings[buildingID].field202_0x2c8 == 0)) { + && (includeBool == 0 || this->buildings[buildingID].field201_0x2c8 == 0)) { ++count; } } diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/countFarms.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/countFarms.cpp index fe869da07..b8d6940f4 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/countFarms.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/countFarms.cpp @@ -17,7 +17,7 @@ namespace Map { && (this->buildings[buildingID].buildingType == BT_WHEATFARM || this->buildings[buildingID].buildingType == BT_APPLEFARM || this->buildings[buildingID].buildingType == BT_DAIRYFARM) - && (param_2 == 0 || this->buildings[buildingID].field202_0x2c8 == 0)) { + && (param_2 == 0 || this->buildings[buildingID].field201_0x2c8 == 0)) { ++count; } } diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/findFreeCathedralAssemblyTile.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/findFreeCathedralAssemblyTile.cpp index 2cccfeda3..f0ed34375 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/findFreeCathedralAssemblyTile.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/findFreeCathedralAssemblyTile.cpp @@ -26,7 +26,7 @@ namespace Map { int const x = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[3].x + assemblyX; int const y = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[3].y + assemblyY; int const tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; - int const occupantID = (short)DAT_TileMapState::instance.UnitLayer[tile]; + int const occupantID = DAT_TileMapState::instance.UnitLayer[tile]; if (occupantID != 0 && occupantID != unitID && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { continue; diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/findFreeEngineerAssemblyTile.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/findFreeEngineerAssemblyTile.cpp index 17dc17fa3..2c5acb0c3 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/findFreeEngineerAssemblyTile.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/findFreeEngineerAssemblyTile.cpp @@ -35,7 +35,7 @@ namespace Map { int const x = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[i].x + assemblyX; int const y = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[i].y + assemblyY; int const tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; - int const occupantID = (short)DAT_TileMapState::instance.UnitLayer[tile]; + int const occupantID = DAT_TileMapState::instance.UnitLayer[tile]; if (occupantID != 0 && occupantID != unitID && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { continue; @@ -73,7 +73,7 @@ namespace Map { continue; } int const occupantID - = (short)DAT_TileMapState::instance + = DAT_TileMapState::instance .UnitLayer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x]; if (occupantID != 0 && occupantID != unitID && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/hasBuildingEntranceFlagForOrientation.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/hasBuildingEntranceFlagForOrientation.cpp index 0c345776c..82965409c 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/hasBuildingEntranceFlagForOrientation.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/hasBuildingEntranceFlagForOrientation.cpp @@ -11,19 +11,19 @@ namespace Map { BOOLEnum BuildingsState::hasBuildingEntranceFlagForOrientation(int buildingID) { if (DAT_TileMapState::instance.mapOrientation == 0) { - if (DAT_BuildingsState::instance.buildings[buildingID].field162_0x280[1]) { + if (DAT_BuildingsState::instance.buildings[buildingID].field161_0x280[1]) { return TRUE; } } else if (DAT_TileMapState::instance.mapOrientation == 2) { - if (DAT_BuildingsState::instance.buildings[buildingID].field162_0x280[2]) { + if (DAT_BuildingsState::instance.buildings[buildingID].field161_0x280[2]) { return TRUE; } } else if (DAT_TileMapState::instance.mapOrientation == 4) { - if (DAT_BuildingsState::instance.buildings[buildingID].field162_0x280[3]) { + if (DAT_BuildingsState::instance.buildings[buildingID].field161_0x280[3]) { return TRUE; } } else if (DAT_TileMapState::instance.mapOrientation == 6) { - if (DAT_BuildingsState::instance.buildings[buildingID].field162_0x280[0]) { + if (DAT_BuildingsState::instance.buildings[buildingID].field161_0x280[0]) { return TRUE; } } diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/hasBuildingExitFlagForOrientation.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/hasBuildingExitFlagForOrientation.cpp index 33ba3d398..0a074b0f3 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/hasBuildingExitFlagForOrientation.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/hasBuildingExitFlagForOrientation.cpp @@ -11,19 +11,19 @@ namespace Map { BOOLEnum BuildingsState::hasBuildingExitFlagForOrientation(int buildingID) { if (DAT_TileMapState::instance.mapOrientation == 0) { - if (DAT_BuildingsState::instance.buildings[buildingID].field162_0x280[2]) { + if (DAT_BuildingsState::instance.buildings[buildingID].field161_0x280[2]) { return TRUE; } } else if (DAT_TileMapState::instance.mapOrientation == 2) { - if (DAT_BuildingsState::instance.buildings[buildingID].field162_0x280[3]) { + if (DAT_BuildingsState::instance.buildings[buildingID].field161_0x280[3]) { return TRUE; } } else if (DAT_TileMapState::instance.mapOrientation == 4) { - if (DAT_BuildingsState::instance.buildings[buildingID].field162_0x280[0]) { + if (DAT_BuildingsState::instance.buildings[buildingID].field161_0x280[0]) { return TRUE; } } else if (DAT_TileMapState::instance.mapOrientation == 6) { - if (DAT_BuildingsState::instance.buildings[buildingID].field162_0x280[1]) { + if (DAT_BuildingsState::instance.buildings[buildingID].field161_0x280[1]) { return TRUE; } } diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp index 0216eb9bc..71d34cdf1 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp @@ -18,7 +18,7 @@ namespace Map { && DAT_BuildingsState::instance.buildings[buildingID].owner == param_1 && (DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_GATEHOUSELARGE || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_GATEHOUSESMALL) - && DAT_BuildingsState::instance.buildings[buildingID].field201_0x2c6 == 0) { + && DAT_BuildingsState::instance.buildings[buildingID].field200_0x2c6 == 0) { DAT_PathFindingState::instance .climbData[DAT_BuildingsState::instance.buildings[buildingID].laddermanDataID] .isRecognizedByPathfinding = 1; diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/isSpaceAvailableAtUnitRallyPoint.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/isSpaceAvailableAtUnitRallyPoint.cpp index 22c521cd4..10b9ecefa 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/isSpaceAvailableAtUnitRallyPoint.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/isSpaceAvailableAtUnitRallyPoint.cpp @@ -48,7 +48,7 @@ namespace Map { int const x = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[i].x + assemblyX; int const y = DAT_BuildingDefinedData::instance.SearchRelatedXYOffsets_1[i].y + assemblyY; int const tile = DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x; - int const occupantID = (short)DAT_TileMapState::instance.UnitLayer[tile]; + int const occupantID = DAT_TileMapState::instance.UnitLayer[tile]; if (occupantID != 0 && occupantID != unitID && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { continue; @@ -93,7 +93,7 @@ namespace Map { continue; } int const occupantID - = (short)DAT_TileMapState::instance + = DAT_TileMapState::instance .UnitLayer[DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile + x]; if (occupantID != 0 && occupantID != unitID && DAT_UnitsState::instance.units[unitID].movementRelated == 8) { diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/processBuildingIDsNeedPeasantsQueue.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/processBuildingIDsNeedPeasantsQueue.cpp index c61f9bd62..d7ac898ef 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/processBuildingIDsNeedPeasantsQueue.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/processBuildingIDsNeedPeasantsQueue.cpp @@ -19,8 +19,8 @@ namespace Map { void BuildingsState::processBuildingIDsNeedPeasantsQueue(undefined4 playerID) { // Assigns peasants to the free worker slots of the queued buildings - for (int i = 0; i < (int)this->DAT_CountOfBuildingsNeedPeasants; ++i) { - int buildingID = (short)this->DAT_BuildingIDsNeedPeasantsQueue[i]; + for (int i = 0; i < this->DAT_CountOfBuildingsNeedPeasants; ++i) { + int buildingID = this->DAT_BuildingIDsNeedPeasantsQueue[i]; if (this->buildings[buildingID].buildingTypeBasedEmployeeCount == 1) { UnitType workerType = (UnitType)DAT_BuildingDefinedData::instance .WorkerTypeForBuildingType[this->buildings[buildingID].buildingType]; diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/processDamageToUnitsOnBuilding.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/processDamageToUnitsOnBuilding.cpp index c82cdd5e5..397ab0a79 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/processDamageToUnitsOnBuilding.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/processDamageToUnitsOnBuilding.cpp @@ -20,12 +20,12 @@ namespace Map { int const y = this->buildings[buildingID].y; do { MACRO_CALL_MEMBER(TileMapState_Func::getBuildingSizeIndexMappingData, DAT_TileMapState::ptr)(i, size); - for (int unitID = (short)DAT_TileMapState::instance + for (int unitID = DAT_TileMapState::instance .UnitLayer[DAT_ViewportRenderState::instance .translationMatrix[y + DAT_TileMapState::instance.buildingY] .addXgetTile + DAT_TileMapState::instance.buildingX + x]; - unitID != 0; unitID = (short)DAT_UnitsState::instance.units[unitID].nextUnitOnTheSameTile) { + unitID != 0; unitID = DAT_UnitsState::instance.units[unitID].nextUnitOnTheSameTile) { int const maxHealth = DAT_UnitsState::instance.units[unitID].maxHealth; DAT_UnitsState::instance.units[unitID].health += maxHealth * damagePercentage / -100; if (DAT_UnitsState::instance.units[unitID].health <= 0) { diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp index 06dcf0452..fbaf01040 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/setNewOnFarmDestination.cpp @@ -28,7 +28,7 @@ namespace Map { .PathConnectionLayer[DAT_ViewportRenderState::instance.translationMatrix[entryY].addXgetTile + entryX]; // The header has no field for the short at 0x26e. - int direction = *(short*)this->buildings[buildingID].padding_0x26e == 0x51 ? 4 : 2; + int direction = this->buildings[buildingID].someFarmRelatedState == 0x51 ? 4 : 2; for (int i = 0; i < 24; ++i) { int const tile = this->buildings[buildingID].tileRefs[i]; int const row = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp index bb61e74a8..91310bdc6 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp @@ -55,7 +55,6 @@ namespace Map { return; } DAT_GameState::instance.playerDataArray[playerID].structureRelated1 = 72; - // The header splits this 6 x 72 location table into separately named fields. for (int formation = 0; formation < 6; ++formation) { int const shape = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_1[formation]; // The loop index above is reused, so an unknown selector keeps the previous building. @@ -71,12 +70,12 @@ namespace Map { int const x0 = this->buildings[buildingID].x; int const y0 = this->buildings[buildingID].y; for (int i = 0; i < 24; ++i) { - (&DAT_GameState::instance.playerDataArray[playerID] - .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + i] + DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[formation][0][i] .x = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x0; - (&DAT_GameState::instance.playerDataArray[playerID] - .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + i] + DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[formation][0][i] .y = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y0; } @@ -92,12 +91,12 @@ namespace Map { int const x1 = this->buildings[buildingID].x; int const y1 = this->buildings[buildingID].y; for (int i = 0; i < 24; ++i) { - (&DAT_GameState::instance.playerDataArray[playerID] - .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + 24 + i] + DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[formation][1][i] .x = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x1; - (&DAT_GameState::instance.playerDataArray[playerID] - .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + 24 + i] + DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[formation][1][i] .y = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y1; } @@ -113,12 +112,12 @@ namespace Map { int const x2 = this->buildings[buildingID].x; int const y2 = this->buildings[buildingID].y; for (int i = 0; i < 24; ++i) { - (&DAT_GameState::instance.playerDataArray[playerID] - .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + 48 + i] + DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[formation][2][i] .x = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].x + x2; - (&DAT_GameState::instance.playerDataArray[playerID] - .structure.mercenaryOutpostCampgroundLocations[0])[formation * 72 + 48 + i] + DAT_GameState::instance.playerDataArray[playerID] + .structure.mercenaryOutpostCampgroundLocations[formation][2][i] .y = DAT_BuildingDefinedData::instance.PlayerDataUnknownStructureRelatedArray_3[shape][i].y + y2; } diff --git a/src/OpenSHC/Map/Version/UpgradeBuildingField4.cpp b/src/OpenSHC/Map/Version/UpgradeBuildingField4.cpp index dc56f4c40..f4f7a26eb 100644 --- a/src/OpenSHC/Map/Version/UpgradeBuildingField4.cpp +++ b/src/OpenSHC/Map/Version/UpgradeBuildingField4.cpp @@ -15,7 +15,7 @@ namespace Map { { for (int buildingID = 1; buildingID < 2000; ++buildingID) { if (DAT_BuildingsState::instance.buildings[buildingID].logicalState != (BuildingLogicalState)0) { - DAT_BuildingsState::instance.buildings[buildingID].field201_0x2c6 = 0; + DAT_BuildingsState::instance.buildings[buildingID].field200_0x2c6 = 0; } } } From f7b396cf45705b83995479f43af866ae73838f67 Mon Sep 17 00:00:00 2001 From: Jake Vawter Date: Wed, 28 Oct 2026 08:54:22 -0400 Subject: [PATCH 5/7] style: clang-format Co-Authored-By: Claude Opus 5.5 --- .../BuildingsState/findClosestReachableAlliedBuilding.cpp | 6 +++--- .../BuildingsState/initializeGatePathfindingForOwner.cpp | 3 ++- .../BuildingsState/setupBarracksCampgroundPositions.cpp | 4 +++- .../setupMercenaryPostCampgroundPositions.cpp | 4 +++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/findClosestReachableAlliedBuilding.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/findClosestReachableAlliedBuilding.cpp index d20c70a02..2699a4863 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/findClosestReachableAlliedBuilding.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/findClosestReachableAlliedBuilding.cpp @@ -41,9 +41,9 @@ namespace Map { if (DAT_DirectionAlgorithmState::instance.distanceHigh >= closestDistance) { continue; } - int const tile = DAT_ViewportRenderState::instance - .translationMatrix[this->buildings[buildingID].buildingEntryY] - .addXgetTile + int const tile + = DAT_ViewportRenderState::instance.translationMatrix[this->buildings[buildingID].buildingEntryY] + .addXgetTile + this->buildings[buildingID].buildingEntryX; if (MACRO_CALL_MEMBER(Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, DAT_PathFindingState::ptr)(DAT_UnitsState::instance.units[unitID].owner, fromArea, diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp index 71d34cdf1..8d44f364b 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp @@ -21,7 +21,8 @@ namespace Map { && DAT_BuildingsState::instance.buildings[buildingID].field200_0x2c6 == 0) { DAT_PathFindingState::instance .climbData[DAT_BuildingsState::instance.buildings[buildingID].laddermanDataID] - .isRecognizedByPathfinding = 1; + .isRecognizedByPathfinding + = 1; DAT_BuildingsState::instance.buildings[buildingID].pathLinkageRelated2 = 0; DAT_BuildingsState::instance.buildings[buildingID].gateState = 11; DAT_BuildingsState::instance.buildings[buildingID].gateCloseOpenTimer = 2500; diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/setupBarracksCampgroundPositions.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/setupBarracksCampgroundPositions.cpp index 6e2d83d92..ac179d7d5 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/setupBarracksCampgroundPositions.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/setupBarracksCampgroundPositions.cpp @@ -39,7 +39,9 @@ namespace Map { continue; } // quarryStockpileID holds the linked building here. - if (DAT_BuildingsState::instance.buildings[DAT_BuildingsState::instance.buildings[buildingID].quarryStockpileID].buildingType + if (DAT_BuildingsState::instance + .buildings[DAT_BuildingsState::instance.buildings[buildingID].quarryStockpileID] + .buildingType != BT_BARRACKS) { continue; } diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp index 91310bdc6..c09a8f69a 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/setupMercenaryPostCampgroundPositions.cpp @@ -39,7 +39,9 @@ namespace Map { continue; } // quarryStockpileID holds the linked building here. - if (DAT_BuildingsState::instance.buildings[DAT_BuildingsState::instance.buildings[buildingID].quarryStockpileID].buildingType + if (DAT_BuildingsState::instance + .buildings[DAT_BuildingsState::instance.buildings[buildingID].quarryStockpileID] + .buildingType != BT_MERCENARYPOST) { continue; } From b2f3910c3bc00966a4a3d73573097aae73f80147 Mon Sep 17 00:00:00 2001 From: Jake Vawter Date: Wed, 28 Oct 2026 09:23:49 -0400 Subject: [PATCH 6/7] style: clang-format 22 for initializeGatePathfindingForOwner Co-Authored-By: Claude Opus 5.5 --- .../BuildingsState/initializeGatePathfindingForOwner.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp index 8d44f364b..71d34cdf1 100644 --- a/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp +++ b/src/OpenSHC/Map/Buildings/BuildingsState/initializeGatePathfindingForOwner.cpp @@ -21,8 +21,7 @@ namespace Map { && DAT_BuildingsState::instance.buildings[buildingID].field200_0x2c6 == 0) { DAT_PathFindingState::instance .climbData[DAT_BuildingsState::instance.buildings[buildingID].laddermanDataID] - .isRecognizedByPathfinding - = 1; + .isRecognizedByPathfinding = 1; DAT_BuildingsState::instance.buildings[buildingID].pathLinkageRelated2 = 0; DAT_BuildingsState::instance.buildings[buildingID].gateState = 11; DAT_BuildingsState::instance.buildings[buildingID].gateCloseOpenTimer = 2500; From 3af0ff1b162fa969d0a493726fe1374e37751d0c Mon Sep 17 00:00:00 2001 From: Jake Vawter Date: Wed, 28 Oct 2026 10:05:44 -0400 Subject: [PATCH 7/7] reimplement: extinguishBuildingFire, now 100% (from #273) The second loop's row tile plus x goes through a local, which gives the original's add order. Co-Authored-By: Claude Opus 5.5 --- .../BuildingsState/extinguishBuildingFire.cpp | 59 +++++++++++++++++++ status/addresses-SHC-3BB0A8C1.txt | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/OpenSHC/Map/Buildings/BuildingsState/extinguishBuildingFire.cpp diff --git a/src/OpenSHC/Map/Buildings/BuildingsState/extinguishBuildingFire.cpp b/src/OpenSHC/Map/Buildings/BuildingsState/extinguishBuildingFire.cpp new file mode 100644 index 000000000..aa162dd7c --- /dev/null +++ b/src/OpenSHC/Map/Buildings/BuildingsState/extinguishBuildingFire.cpp @@ -0,0 +1,59 @@ +#include "../BuildingsState.func.hpp" + +#include "OpenSHC/Map/Entities/EntityState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace Map { + namespace Buildings { + + // FUNCTION: STRONGHOLDCRUSADER 0x004109F0 + void BuildingsState::extinguishBuildingFire(int buildingID) + { + if (this->buildings[buildingID].fireDuration == 0) { + return; + } + int const x = this->buildings[buildingID].x; + int const y = this->buildings[buildingID].y; + int size = this->buildings[buildingID].widthOrHeight; + this->buildings[buildingID].fireDuration = 0; + this->buildings[buildingID].cooldownTimer = 2000; + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_WHEATFARM) { + size = 9; + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_HOPFARM) { + size = 9; + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_DAIRYFARM) { + size = 10; + } else if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_APPLEFARM) { + size = 11; + } + int tile = 0; + do { + MACRO_CALL_MEMBER(TileMapState_Func::getBuildingSizeIndexMappingData, DAT_TileMapState::ptr)( + tile, size); + MACRO_CALL_MEMBER(Entities::EntityState_Func::updateAllFireEntitiesAtTile, DAT_EntityState::ptr)( + DAT_ViewportRenderState::instance.translationMatrix[y + DAT_TileMapState::instance.buildingY] + .addXgetTile + + DAT_TileMapState::instance.buildingX + x); + ++tile; + } while (tile < DAT_TileMapState::instance.constructionTileCount); + int const tileCount = DAT_BuildingDefinedData::instance.BuildingAccessibleTilesCount[size]; + for (int try_ = 0; try_ < tileCount; ++try_) { + MACRO_CALL_MEMBER(BuildingsState_Func::setupBuildingEntrancesOffset, this)(size, 1, try_, 0); + int const rowTile + = DAT_ViewportRenderState::instance.translationMatrix[this->DAT_TempYOffset + y].addXgetTile + x; + MACRO_CALL_MEMBER(Entities::EntityState_Func::updateAllFireEntitiesAtTile, DAT_EntityState::ptr)( + rowTile + this->DAT_TempXOffset); + } + } + + } +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 39d5a825a..c0e612a7e 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -5371,7 +5371,7 @@ SHC_3BB0A8C1_0x004109E1 | 0.0% | Pending SHC_3BB0A8C1_0x004109E2 | 0.0% | Pending -SHC_3BB0A8C1_0x004109F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004109F0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00410B10 | 100.0% | Reimplemented