diff --git a/src/OpenSHC/AI/AICState/Constructor_AICState.cpp b/src/OpenSHC/AI/AICState/Constructor_AICState.cpp new file mode 100644 index 00000000..104d8e01 --- /dev/null +++ b/src/OpenSHC/AI/AICState/Constructor_AICState.cpp @@ -0,0 +1,14 @@ +#include "OpenSHC/AI/AICState.func.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3940 + AICState* AICState::Constructor_AICState() + { + MACRO_CALL_MEMBER(AICState_Func::setInitialAIC, this)(); + this->aiTauntResponseTimeWindow = 0; + return this; + } +} +} diff --git a/src/OpenSHC/AI/AICState/abortAttackAndGoIdle.cpp b/src/OpenSHC/AI/AICState/abortAttackAndGoIdle.cpp new file mode 100644 index 00000000..bdd9f731 --- /dev/null +++ b/src/OpenSHC/AI/AICState/abortAttackAndGoIdle.cpp @@ -0,0 +1,23 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D45E0 + void AICState::abortAttackAndGoIdle(int playerID) + { + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::removeSiegeBuildings, DAT_BuildingsState::ptr)( + DAT_GameState::instance.playerDataArray[playerID].currentAttackWave, playerID); + MACRO_CALL_MEMBER(AICState_Func::disbandAIPlayerSiegeUnits, this)(playerID); + MACRO_CALL_MEMBER(AICState_Func::addEngineersToTribe, this)(playerID); + MACRO_CALL_MEMBER(AICState_Func::aiReassignTunnelersToTribe, this)(playerID); + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 0; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/addBuildingToTargetableBuildings.cpp b/src/OpenSHC/AI/AICState/addBuildingToTargetableBuildings.cpp new file mode 100644 index 00000000..8f92126f --- /dev/null +++ b/src/OpenSHC/AI/AICState/addBuildingToTargetableBuildings.cpp @@ -0,0 +1,24 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDA50 + void AICState::addBuildingToTargetableBuildings(int buildingID) + { + int _player = DAT_BuildingsState::instance.buildings[buildingID].owner; + if (DAT_GameSynchronyState::instance.DAT_CurrentGameMode != Game::GM_SOLITARY) { + int* piVar1 = &DAT_GameState::instance.playerDataArray[_player].top100TargetableBuildingsTracker; + if ((*piVar1 < 100) && (DAT_BuildingsState::instance.buildings[buildingID].fireDuration == 0)) { + DAT_GameState::instance.playerDataArray[_player].top100TargetableBuildings[*piVar1] = (short)buildingID; + *piVar1 = *piVar1 + 1; + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/addEngineersToSelection.cpp b/src/OpenSHC/AI/AICState/addEngineersToSelection.cpp new file mode 100644 index 00000000..db339291 --- /dev/null +++ b/src/OpenSHC/AI/AICState/addEngineersToSelection.cpp @@ -0,0 +1,45 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/States/UnitState.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" + +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC680 + int AICState::addEngineersToSelection(int playerID, int unitCount) + { + int tribeID; + uint unitID; + + tribeID = MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + unitID = 1; + if (1 < (int)DAT_UnitsState::instance.maxUnitCount) { + /* Unit offset 150 */ + Map::Units::Unit* _ptrUnit = &DAT_UnitsState::instance.units[1]; + do { + if (unitCount <= 0) { + return tribeID; + } + if ((((_ptrUnit->logicalState == Map::Units::ULS_NORMAL) && (_ptrUnit->owner == playerID)) + && (_ptrUnit->dying == 0)) + && (((_ptrUnit->unitType == Map::Units::UT_E_ENGINEER) && (_ptrUnit->aiUnitBehaviourType == 10)) + && ((_ptrUnit->tribeID == 0) + && ((_ptrUnit->state).generic != Map::Units::States::US_STAND_UPUnk)))) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + unitID, tribeID); + unitCount = unitCount + -1; + } + unitID = unitID + 1; + _ptrUnit = _ptrUnit + 1; + } while ((int)unitID < (int)DAT_UnitsState::instance.maxUnitCount); + } + return tribeID; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/addEngineersToTribe.cpp b/src/OpenSHC/AI/AICState/addEngineersToTribe.cpp new file mode 100644 index 00000000..2d3d8b87 --- /dev/null +++ b/src/OpenSHC/AI/AICState/addEngineersToTribe.cpp @@ -0,0 +1,32 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitLogicState.hpp" + +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4040 + void AICState::addEngineersToTribe(int playerID) + { + int _unitID = 1; + if (1 < (int)DAT_UnitsState::instance.maxUnitCount) { + Map::Units::Unit* _pUnit = &DAT_UnitsState::instance.units[1]; + do { + /* if logicalState == normal && owner == playerID && unitType == 0x1e (engineer) + && aiUnitBehaviourType == 10 */ + if ((((_pUnit->logicalState == Map::Units::ULS_NORMAL) && (_pUnit->owner == playerID)) + && (_pUnit->dying == 0)) + && (((_pUnit->unitType == Map::Units::UT_E_ENGINEER) && (_pUnit->aiUnitBehaviourType == 10)) + && (_pUnit->tribeID == 0))) { + MACRO_CALL_MEMBER(AICState_Func::addUnitToItsTribe, this)(_unitID, 10); + } + _unitID = _unitID + 1; + _pUnit = _pUnit + 1; + } while (_unitID < (int)DAT_UnitsState::instance.maxUnitCount); + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/addRallyPointForTribe.cpp b/src/OpenSHC/AI/AICState/addRallyPointForTribe.cpp new file mode 100644 index 00000000..1ae3180d --- /dev/null +++ b/src/OpenSHC/AI/AICState/addRallyPointForTribe.cpp @@ -0,0 +1,101 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Units/Unit.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/Map/Units/UnitTypeShort.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Units::Unit; + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::Map::Units::UnitTypeShort; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CFA10 + void AICState ::addRallyPointForTribe(int tribeID) + + { + + short* psVar1; + + BOOLEnum _areInDifferentTeamsUnk; + + Unit* _ptrUnit; + + UnitTypeShort _unitType; + + DAT_TribesState::instance.tribes[tribeID].rallyPointCount = 0; + + _ptrUnit = &DAT_UnitsState::instance.units[1]; + + do { + + if (_ptrUnit->logicalState != OpenSHC::Map::Units::ULS_INVISIBLE) { + + _areInDifferentTeamsUnk = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::getTeamsDifferent, + DAT_GameState::ptr)(DAT_TribesState::instance.tribes[tribeID].owner, (int)((int)(_ptrUnit->owner))); + + if (_areInDifferentTeamsUnk == FALSE) { + + _unitType = _ptrUnit->unitType; + + if ((((_unitType == OpenSHC::Map::Units::UT_S_CATAPULT) + || (_unitType == OpenSHC::Map::Units::UT_S_TREBUCHET)) + || (_unitType == OpenSHC::Map::Units::UT_S_FBALLISTA)) + && (_ptrUnit->unknownSiegeTentRelated02 == 3)) { + + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::computeNextRallyPointDestination, + DAT_PathFindingState::ptr)(-1, (int)((int)(_ptrUnit->x)), (int)((int)(_ptrUnit->y))); + + DAT_TribesState::instance.tribes[tribeID] + .rallyPointArray[DAT_TribesState::instance.tribes[tribeID].rallyPointCount][0] + = (short)DAT_PathFindingState::instance.ALG_ResultX; + + DAT_TribesState::instance.tribes[tribeID] + .rallyPointArray[DAT_TribesState::instance.tribes[tribeID].rallyPointCount][1] + = (short)DAT_PathFindingState::instance.ALG_ResultY; + + psVar1 = &DAT_TribesState::instance.tribes[tribeID].rallyPointCount; + + *psVar1 = *psVar1 + 1; + + if (8 < DAT_TribesState::instance.tribes[tribeID].rallyPointCount) + break; + } + } + } + + _ptrUnit = _ptrUnit + 0x248; + + } while ((int)_ptrUnit < 0x165141a); + + if (DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex < 0) { + + DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex = 0; + } + + if (DAT_TribesState::instance.tribes[tribeID].rallyPointCount + <= DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex) { + + DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex = 0; + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/addUnitToItsTribe.cpp b/src/OpenSHC/AI/AICState/addUnitToItsTribe.cpp new file mode 100644 index 00000000..216be457 --- /dev/null +++ b/src/OpenSHC/AI/AICState/addUnitToItsTribe.cpp @@ -0,0 +1,26 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D27E0 + void AICState::addUnitToItsTribe(int unitID, int aiUnitBehaviourType) + { + int iVar1 = (int)DAT_UnitsState::instance.units[unitID].owner; + if (DAT_GameState::instance.playerDataArray[iVar1].aiType != AITA_NULL) { + DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = (short)aiUnitBehaviourType; + iVar1 = MACRO_CALL_MEMBER(AICState_Func::addUnitToSmallestBehaviourTypeTribe, this)( + iVar1, unitID, aiUnitBehaviourType); + if (iVar1 != 0) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, iVar1); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/addUnitToSmallestBehaviourTypeTribe.cpp b/src/OpenSHC/AI/AICState/addUnitToSmallestBehaviourTypeTribe.cpp new file mode 100644 index 00000000..c26db36f --- /dev/null +++ b/src/OpenSHC/AI/AICState/addUnitToSmallestBehaviourTypeTribe.cpp @@ -0,0 +1,91 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CCD20 + int AICState::addUnitToSmallestBehaviourTypeTribe( + undefined4 playerID, undefined4 param_2, undefined4 aiUnitBehaviourType) + { + int _playerID = (int)playerID; + int AVar1 = DAT_GameState::instance.playerDataArray[_playerID].aiType; + if (AVar1 == AITA_NULL) { + return 0; + } + int _tribeTypeOffset + = DAT_SkirmishDefinedData::instance.AIUnitBehaviourTypeMapping[(int)aiUnitBehaviourType + -10]; + int _smallestTribe = 0; + int _smallestTribeOffset = 0; + short _smallestTribeSize = 1000; + dword _groupsCount; + + if (_tribeTypeOffset == 0xf) { + _groupsCount = this->aics[AVar1 - 1].AttUnitPatrolGroupsCount; + } else if (_tribeTypeOffset == 0xba) { + _groupsCount = this->aics[AVar1 - 1].AttUnitBackupGroupsCount; + } else if (_tribeTypeOffset == 0xbe) { + _groupsCount = this->aics[AVar1 - 1].AttUnitSiegeDefGroupsCount; + } else if (_tribeTypeOffset == 0xc0) { + _groupsCount = this->aics[AVar1 - 1].AttMainGroupsCount; + } else { + _groupsCount = 1; + } + + int _selectedTribe; + int _selectedSlot; + if ((int)_groupsCount < 1 || _groupsCount == 1) { + int _tribeID_groupsCount1 + = (int)DAT_GameState::instance.playerDataArray[_playerID].aiTribeIDs[_tribeTypeOffset]; + if ((_tribeID_groupsCount1 != 0) + && (DAT_TribesState::instance.tribes[_tribeID_groupsCount1].uid + == DAT_GameState::instance.playerDataArray[_playerID].aiTribeUIDs[_tribeTypeOffset])) { + return _tribeID_groupsCount1; + } + _selectedTribe = MACRO_CALL_MEMBER( + Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(_playerID); + _selectedSlot = _tribeTypeOffset; + } else { + int _maxOffset = _groupsCount + _tribeTypeOffset; + _selectedTribe = 0; + _selectedSlot = 0; + short* _pTribeIDs = DAT_GameState::instance.playerDataArray[_playerID].aiTribeIDs + _tribeTypeOffset; + if (_tribeTypeOffset < _maxOffset) { + do { + int _tribeID_2 = (int)*_pTribeIDs; + if ((_tribeID_2 == 0) + || (DAT_TribesState::instance.tribes[_tribeID_2].uid + != DAT_GameState::instance.playerDataArray[_playerID].aiTribeUIDs[_tribeTypeOffset])) { + _selectedTribe = MACRO_CALL_MEMBER( + Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(_playerID); + _selectedSlot = _tribeTypeOffset; + break; + } + short _tribeSize = DAT_TribesState::instance.tribes[_tribeID_2].size; + if (_tribeSize < _smallestTribeSize) { + _smallestTribeOffset = _tribeTypeOffset; + _smallestTribe = _tribeID_2; + _smallestTribeSize = _tribeSize; + } + _pTribeIDs = _pTribeIDs + 1; + _tribeTypeOffset = _tribeTypeOffset + 1; + _selectedTribe = _smallestTribe; + _selectedSlot = _smallestTribeOffset; + } while (_tribeTypeOffset < _maxOffset); + } + } + if (0 < _selectedTribe) { + DAT_GameState::instance.playerDataArray[_playerID].aiTribeIDs[_selectedSlot] = (short)_selectedTribe; + DAT_GameState::instance.playerDataArray[_playerID].aiTribeUIDs[_selectedSlot] + = DAT_TribesState::instance.tribes[_selectedTribe].uid; + return _selectedTribe; + } + return 0; + } +} +} diff --git a/src/OpenSHC/AI/AICState/addUnitToSmallestPatrolTribe.cpp b/src/OpenSHC/AI/AICState/addUnitToSmallestPatrolTribe.cpp new file mode 100644 index 00000000..2939e158 --- /dev/null +++ b/src/OpenSHC/AI/AICState/addUnitToSmallestPatrolTribe.cpp @@ -0,0 +1,27 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_AICState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2730 + void AICState::addUnitToSmallestPatrolTribe(int unitID) + { + int iVar1 = (int)DAT_UnitsState::instance.units[unitID].owner; + int _aiType = DAT_GameState::instance.playerDataArray[iVar1].aiType; + if (_aiType != AITA_NULL) { + DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = 4; + iVar1 = MACRO_CALL_MEMBER(AI::AICState_Func::getSmallestPatrolTribe, DAT_AICState::ptr)( + iVar1, (int)this->aics[_aiType - 1].OuterPatrolGroupsCount); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, iVar1); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/addUnitsToTribe166.cpp b/src/OpenSHC/AI/AICState/addUnitsToTribe166.cpp new file mode 100644 index 00000000..fa55cb88 --- /dev/null +++ b/src/OpenSHC/AI/AICState/addUnitsToTribe166.cpp @@ -0,0 +1,37 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC720 + void AICState::addUnitsToTribe166(int playerID) + { + int tribeID; + uint unitID = 1; + if (1 < (int)DAT_UnitsState::instance.maxUnitCount) { + Map::Units::Unit* pUVar1 = &DAT_UnitsState::instance.units[1]; + do { + if ((((pUVar1->owner == playerID) && (pUVar1->logicalState == Map::Units::ULS_NORMAL)) + && (pUVar1->dying == 0)) + && (pUVar1->aiUnitBehaviourType == 6)) { + tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[166]; + if (pUVar1->tribeID != tribeID) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::removeUnitFromTribe, DAT_TribesState::ptr)( + unitID, (int)pUVar1->tribeID); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + unitID, tribeID); + } + } + unitID = unitID + 1; + pUVar1 = pUVar1 + 1; + } while ((int)unitID < (int)DAT_UnitsState::instance.maxUnitCount); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiAddUnitToMoatDiggerTribe.cpp b/src/OpenSHC/AI/AICState/aiAddUnitToMoatDiggerTribe.cpp new file mode 100644 index 00000000..c9a91b8d --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiAddUnitToMoatDiggerTribe.cpp @@ -0,0 +1,31 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC840 + void AICState::aiAddUnitToMoatDiggerTribe(int unitID) + { + int _owner = (int)DAT_UnitsState::instance.units[unitID].owner; + DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = 5; + int _tribe = (int)DAT_GameState::instance.playerDataArray[_owner].aiTribeIDs[10]; + if ((_tribe == 0) + || (DAT_TribesState::instance.tribes[_tribe].uid + != DAT_GameState::instance.playerDataArray[_owner].aiTribeUIDs[10])) { + _tribe + = MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(_owner); + int _uid = DAT_TribesState::instance.tribes[_tribe].uid; + DAT_GameState::instance.playerDataArray[_owner].aiTribeIDs[10] = (short)_tribe; + DAT_GameState::instance.playerDataArray[_owner].aiTribeUIDs[10] = _uid; + } + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, _tribe); + DAT_TribesState::instance.tribes[_tribe].unitStance = Map::Units::Behavior::USE_DEFENSIVE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiAssignMoatDiggers.cpp b/src/OpenSHC/AI/AICState/aiAssignMoatDiggers.cpp new file mode 100644 index 00000000..0cc81166 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiAssignMoatDiggers.cpp @@ -0,0 +1,49 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3F70 + void AICState::aiAssignMoatDiggers(int playerID) + { + int _unitID; + int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + Map::Units::UnitTypeShort _unitType; + if (((_aiType != AITA_NULL) && (0 < DAT_GameState::instance.playerDataArray[playerID].totalTroopsType0)) + && (_unitID = 1, 1 < (int)DAT_UnitsState::instance.maxUnitCount)) { + Map::Units::Unit* _ptrUnit = &DAT_UnitsState::instance.units[1]; + do { + if ((((((_ptrUnit->logicalState != Map::Units::ULS_INVISIBLE) && (_ptrUnit->owner == playerID)) + && ((_ptrUnit->isSelectable_OR_matchTime != 0 + && ((_ptrUnit->aiUnitBehaviourType == 0 && (_ptrUnit->dying == 0)))))) + && (_unitType = _ptrUnit->unitType, _unitType != Map::Units::UT_LORD)) + && ((((_unitType != Map::Units::UT_E_ENGINEER && (_unitType != Map::Units::UT_TUNNELER)) + && (_unitType != Map::Units::UT_S_CATAPULT)) + && (( + _unitType != Map::Units::UT_S_TREBUCHET && (_unitType != Map::Units::UT_S_MANGONEL)))))) + && (( + (_unitType != Map::Units::UT_S_TOWER + && ((_unitType != Map::Units::UT_S_BATTERINGRAM && (_unitType != Map::Units::UT_S_SHIELD)))) + && ((_unitType != Map::Units::UT_S_BALLISTA && (_unitType != Map::Units::UT_S_FBALLISTA)))))) { + if (((((int)this->aics[_aiType - 1].DefDiggingUnitMax == 0) + || (_unitType == Map::Units::UT_E_ARCHER)) + || (_unitType == Map::Units::UT_E_XBOW)) + || (_ptrUnit->someUnitStat4 == 0)) { + MACRO_CALL_MEMBER(AICState_Func::assignUnitToATribe, this)(_unitID); + } else { + MACRO_CALL_MEMBER(AICState_Func::aiAddUnitToMoatDiggerTribe, this)(_unitID); + } + } + _unitID = _unitID + 1; + _ptrUnit = _ptrUnit + 1; + } while (_unitID < (int)DAT_UnitsState::instance.maxUnitCount); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiAssignUnitToDefensiveTribe.cpp b/src/OpenSHC/AI/AICState/aiAssignUnitToDefensiveTribe.cpp new file mode 100644 index 00000000..cd5bb7b9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiAssignUnitToDefensiveTribe.cpp @@ -0,0 +1,25 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2790 + void AICState::aiAssignUnitToDefensiveTribe(int unitID) + { + int _playerID = (int)DAT_UnitsState::instance.units[unitID].owner; + if (DAT_GameState::instance.playerDataArray[_playerID].aiType != AITA_NULL) { + DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = 2; + int _tribeID = MACRO_CALL_MEMBER(AICState_Func::getDefensiveTribeForUnit, this)(_playerID, unitID); + if (_tribeID != 0) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, _tribeID); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiBuyAndSellGoods.cpp b/src/OpenSHC/AI/AICState/aiBuyAndSellGoods.cpp new file mode 100644 index 00000000..0181f825 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiBuyAndSellGoods.cpp @@ -0,0 +1,53 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Game::Player::PlayerData; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D48F0 + void AICState ::aiBuyAndSellGoods(int playerID) + + { + + PlayerData* _pPlayerData; + + int _pMarketID; + + _pMarketID = DAT_GameState::instance.playerDataArray[playerID].marketplace.id; + + DAT_GameState::instance.playerDataArray[playerID].hasMarketUnk = 0; + + if (_pMarketID != 0) { + + DAT_GameState::instance.playerDataArray[playerID].hasMarketUnk = 1; + } + + if (DAT_GameState::instance.playerDataArray[playerID].hasMarketUnk != 0) { + + _pPlayerData = &DAT_GameState::instance.playerDataArray[playerID]; + + _pPlayerData->willBuy = _pPlayerData->willBuy ^ TRUE; + + if (_pPlayerData->willBuy == FALSE) { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sellExcessGoods, this)(playerID); + + return; + } + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::buyRequiredGoods, this)(playerID); + + return; + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiChoiceAttForceRallyPercentage.cpp b/src/OpenSHC/AI/AICState/aiChoiceAttForceRallyPercentage.cpp new file mode 100644 index 00000000..de707eeb --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiChoiceAttForceRallyPercentage.cpp @@ -0,0 +1,65 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CEC80 + BOOLEnum AICState ::aiChoiceAttForceRallyPercentage(int param_1) + + { + + AITypeInt _aiType = DAT_GameState::instance.playerDataArray[param_1].aiType; + + if (_aiType == OpenSHC::AI::AIT_NULL) { + + return 0; + } + + int const _aiIndex = _aiType - 1; + int _activeTribes = 0; + int _nonMoving = 0; + + for (int _index = 0; _index < 11; _index++) { + + int const _tribeTypeStart = (int)DAT_SkirmishDefinedData::instance.MaxAttackTribes1[_index].tribeType; + + int const _maxTribeCount = (int)DAT_SkirmishDefinedData::instance.MaxAttackTribes1[_index].tribeCount; + + for (int _tribeIndex = 0; _tribeIndex < _maxTribeCount; _tribeIndex++) { + + int const _tribeID + = (int)DAT_GameState::instance.playerDataArray[param_1].aiTribeIDs[_tribeTypeStart + _tribeIndex]; + + if (((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[param_1] + .aiTribeUIDs[_tribeIndex + _tribeTypeStart])) + && (_activeTribes += 1, DAT_TribesState::instance.tribes[_tribeID].percentageMovingUnk < 0x14)) { + + _nonMoving += 1; + } + } + } + + if (_activeTribes <= 0) { + + return 0; + } + + return (uint)((_nonMoving * 100) / _activeTribes >= (int)this->aics[_aiIndex].AttForceRallyPercentage); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiCommandSiegeEngineTribes.cpp b/src/OpenSHC/AI/AICState/aiCommandSiegeEngineTribes.cpp new file mode 100644 index 00000000..cee33b45 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCommandSiegeEngineTribes.cpp @@ -0,0 +1,123 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" +#include "OpenSHC/Map/Units/Unit.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/Map/Units/UnitTypeShort.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +namespace OpenSHC { +namespace AI { + using OpenSHC::Map::Units::SomeTribeBehaviorType; + using OpenSHC::Map::Units::Unit; + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::Map::Units::UnitTypeShort; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + // FUNCTION: STRONGHOLDCRUSADER 0x004CFCD0 + void AICState ::aiCommandSiegeEngineTribes(int playerID) + { + int _targetTribeIDs[3]; + _targetTribeIDs[0] = 0; + _targetTribeIDs[1] = 0; + _targetTribeIDs[2] = 0; + int _validCount = 0; + int _tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xba]; + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xba])) { + _targetTribeIDs[0] = _tribeID; + _validCount = 1; + } + _tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xbb]; + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xbb])) { + _targetTribeIDs[_validCount] = _tribeID; + _validCount += 1; + } + _tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xbc]; + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xbc])) { + _targetTribeIDs[_validCount] = _tribeID; + } + int _shieldCount = 0; + for (int _unitID = 1; _unitID < 2500; _unitID++) { + UnitTypeShort _unitType; + if ((DAT_UnitsState::instance.units[_unitID].logicalState != OpenSHC::Map::Units::ULS_INVISIBLE) + && ((((_unitType = DAT_UnitsState::instance.units[_unitID].unitType, + _unitType == OpenSHC::Map::Units::UT_S_TOWER + || (_unitType == OpenSHC::Map::Units::UT_S_BATTERINGRAM)) + || (_unitType == OpenSHC::Map::Units::UT_S_SHIELD)) + && ((DAT_UnitsState::instance.units[_unitID].unknownSiegeTentRelated02 == 3 + && (DAT_UnitsState::instance.units[_unitID].aiUnitBehaviourType != 0x15)))))) { + if (_unitType == OpenSHC::Map::Units::UT_S_TOWER) { + int _engineTribeID = (int)DAT_UnitsState::instance.units[_unitID].tribeID; + if ((_engineTribeID != 0) + && (*(int*)((int)DAT_TroopValueState::instance.attackInfo.wideValuesArray + playerID * 0x177bc + + -4) + != 0)) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::assignAttackTargetsForTribe, + DAT_TribesState::ptr)(_engineTribeID, OpenSHC::Map::Units::STBT_0x414); + } + } else if (_unitType == OpenSHC::Map::Units::UT_S_BATTERINGRAM) { + int _engineTribeID = (int)DAT_UnitsState::instance.units[_unitID].tribeID; + if (_engineTribeID != 0) { + if (*(int*)((int)DAT_TroopValueState::instance.attackInfo.gateValuesArray + playerID * 0x177bc + + -4) + != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::assignAttackTargetsForTribe, + DAT_TribesState::ptr)(_engineTribeID, OpenSHC::Map::Units::STBT_0x3f6); + } else { + if (*(int*)((int)DAT_TroopValueState::instance.attackInfo.wideValuesArray + + playerID * 0x177bc + -4) + != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::assignAttackTargetsForTribe, + DAT_TribesState::ptr)(_engineTribeID, OpenSHC::Map::Units::STBT_0x413); + } + } + } + } else if (_unitType == OpenSHC::Map::Units::UT_S_SHIELD) { + int _shieldTribeID = (int)DAT_UnitsState::instance.units[_unitID].tribeID; + if ((_shieldTribeID != 0) && (_shieldCount < 3)) { + int _targetTribeID = _targetTribeIDs[_shieldCount]; + DAT_TribesState::instance.tribes[_shieldTribeID].field56_0x1f2 = (short)_targetTribeID; + DAT_TribesState::instance.tribes[_shieldTribeID].uid2 + = DAT_TribesState::instance.tribes[_targetTribeID].uid; + int _targetUnitID = (int)DAT_TribesState::instance.tribes[_targetTribeID].selectionTargetUnitID; + DAT_TribesState::instance.tribes[_targetTribeID].tribeID = (short)_shieldTribeID; + int _targetTile = DAT_UnitsState::instance.units[_targetUnitID].tile; + _shieldCount = _shieldCount + 1; + if (0 < _targetTile) { + uint y1 + = (uint)DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_targetTile]; + uint x1 = _targetTile - DAT_ViewportRenderState::instance.translationMatrix[y1].addXgetTile; + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)( + _shieldTribeID, x1, y1, 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::DirectionAlgorithmState_Func:: + calculatePreferredRelativeOrientation, + DAT_DirectionAlgorithmState::ptr)((int)DAT_UnitsState::instance.units[_targetUnitID].x, + (int)((int)(DAT_UnitsState::instance.units[_targetUnitID].y)), (int)((int)(x1)), + (int)((int)(y1))); + DAT_TribesState::instance.tribes[_shieldTribeID].orientation + = (short)DAT_DirectionAlgorithmState::instance.orientation; + } + } + } + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiCommandTribe11StandGroundToGate.cpp b/src/OpenSHC/AI/AICState/aiCommandTribe11StandGroundToGate.cpp new file mode 100644 index 00000000..ffd86e8b --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCommandTribe11StandGroundToGate.cpp @@ -0,0 +1,45 @@ +#include "OpenSHC/Map/Units/TroopValueState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF470 + void AICState::aiCommandTribe11StandGroundToGate(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) { + return; + } + + int const iVar2 = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xb]; + + if (iVar2 == 0) { + return; + } + + if (DAT_TribesState::instance.tribes[iVar2].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xb]) { + return; + } + + if (DAT_TribesState::instance.tribes[iVar2].selectionTargetUnitID == 0) { + return; + } + + DAT_TribesState::instance.tribes[iVar2].unitStance = Map::Units::Behavior::USE_STAND_GROUND; + + if (MACRO_CALL_MEMBER(Map::Units::TroopValueState_Func::moveTribeToNearbyGatehouse, DAT_TroopValueState::ptr)( + iVar2) + == 0) { + MACRO_CALL_MEMBER(Map::Units::TroopValueState_Func::moveTowardsParticularUnits, DAT_TroopValueState::ptr)( + iVar2); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiCommandTribe12AggressiveApproach.cpp b/src/OpenSHC/AI/AICState/aiCommandTribe12AggressiveApproach.cpp new file mode 100644 index 00000000..8dd97308 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCommandTribe12AggressiveApproach.cpp @@ -0,0 +1,85 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF2E0 + void AICState ::aiCommandTribe12AggressiveApproach(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) { + return; + } + + int tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xc]; + + if (tribeID == 0) { + return; + } + + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xc]) { + return; + } + + int targetUnitID = (int)DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID; + + if (targetUnitID == 0) { + return; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findLinkageBasedPathOrWalkRadius, + DAT_PathFindingState::ptr)((int)DAT_UnitsState::instance.units[targetUnitID].x, + (uint)((int)((int)DAT_UnitsState::instance.units[targetUnitID].y)), -1, -1, 10000, FALSE); + + int attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + + DAT_TribesState::instance.tribes[tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + // Try progressively smaller search costs; stop at the first one that finds a destination. + // The later probes deliberately re-read attackedPlayerID, as the calls may update it. + if ((MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findDestinationCostLowerThan6, + DAT_PathFindingState::ptr)(attackedPlayerID, 200) + != FALSE) + || (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findDestinationCostLowerThan6, + DAT_PathFindingState::ptr)( + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID, 150) + != FALSE) + || (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findDestinationCostLowerThan6, + DAT_PathFindingState::ptr)( + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID, 100) + != FALSE) + || (MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findDestinationCostLowerThan6, + DAT_PathFindingState::ptr)( + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID, 50) + != FALSE)) { + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)( + tribeID, (uint)((int)(DAT_PathFindingState::instance.ALG_ResultX)), + (uint)((int)(DAT_PathFindingState::instance.ALG_ResultY)), 0, 0, + OpenSHC::Map::Units::Instructions::UMSE_0); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiCommandTribe13DefensiveWave.cpp b/src/OpenSHC/AI/AICState/aiCommandTribe13DefensiveWave.cpp new file mode 100644 index 00000000..bf47a5df --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCommandTribe13DefensiveWave.cpp @@ -0,0 +1,41 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF270 + void AICState::aiCommandTribe13DefensiveWave(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) { + return; + } + + int tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xd]; + + if (tribeID == 0) { + return; + } + + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xd]) { + return; + } + + if (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID == 0) { + return; + } + + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_DEFENSIVE; + + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, Map::Units::STBT_0x3f4); + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiCommandWallAssaultTribes.cpp b/src/OpenSHC/AI/AICState/aiCommandWallAssaultTribes.cpp new file mode 100644 index 00000000..3f1f25be --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCommandWallAssaultTribes.cpp @@ -0,0 +1,35 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF660 + void AICState::aiCommandWallAssaultTribes(int param_1) + { + if (DAT_GameState::instance.playerDataArray[param_1].aiType != AITA_NULL) { + int iVar1 = 0; + short* psVar2 = DAT_GameState::instance.playerDataArray[param_1].aiTribeIDs + 0xbe; + do { + int tribeID = (int)*psVar2; + if (((tribeID != 0) + && (DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[param_1].aiTribeUIDs[iVar1 + 0xbe])) + && (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID != 0)) { + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_AGGRESSIVE; + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, Map::Units::STBT_0x3f2); + } + iVar1 = iVar1 + 1; + psVar2 = psVar2 + 1; + } while (iVar1 < 2); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiCreateSiegeUnits.cpp b/src/OpenSHC/AI/AICState/aiCreateSiegeUnits.cpp new file mode 100644 index 00000000..886073a2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCreateSiegeUnits.cpp @@ -0,0 +1,345 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/AI/Tribes/AITribeType.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Buildings/BuildingTypeShort.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::AI::Tribes::AITribeType; + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Buildings::BuildingTypeShort; + using OpenSHC::Map::Units::SomeTribeBehaviorType; + using OpenSHC::Map::Units::UnitInstructionType; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2000 + void AICState ::aiCreateSiegeUnits(int playerID) + { + AITypeInt _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == OpenSHC::AI::AIT_NULL) + return; + + int _engineersGuildID = DAT_GameState::instance.playerDataArray[playerID].engineersGuild.id; + if (!((0 < _engineersGuildID) + && (DAT_BuildingsState::instance.buildings[_engineersGuildID].buildingType + == OpenSHC::Map::Buildings::BT_ENGINEERSGUILD)) + || DAT_GameState::instance.playerDataArray[playerID].canStartSpending == 0) + return; + + int _siegeInfoIndex = 0; + int _incrementsBy5 = 0; + int _mappingOffset = 0; + int* _pDelay; + + do { + UnitType _unitType + = *(UnitType*)((int)&DAT_SkirmishDefinedData::instance.SiegeEngineMetaInfoArray[0].unitType + + _mappingOffset); + int _tribeSize = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[*( + int*)((int)&DAT_SkirmishDefinedData::instance.SiegeEngineMetaInfoArray[0].slot + _mappingOffset)]; + int _slotIndex = 0; + int _nextSlotIndex; + + if (_tribeSize >= 4) + _tribeSize = 3; + + if (0 < _tribeSize) { + do { + _nextSlotIndex = _slotIndex + 1; + int _location = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlots[*( + int*)((int)&DAT_SkirmishDefinedData::instance.SiegeEngineMetaInfoArray[0].slot + + _mappingOffset)][_slotIndex]; + + if (0 < _location) { + int _existingBuildingID = (int)(short)DAT_TileMapState::instance.BuildingLayer[_location]; + int _engineIndex = _incrementsBy5 + _slotIndex; + int _unitID = DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .unitID; + int _uid = DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .uid; + int _tile = DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .buildingID; + + if ((_unitID == 0) || (DAT_UnitsState::instance.units[_unitID].uid == _uid)) { + if ((_tile == 0) || (DAT_BuildingsState::instance.buildings[_tile].uid == _uid)) { + if (_unitID == 0) { + if (_tile == 0) { + bool _doPlace = false; + if (_existingBuildingID == 0) { + if (0x1f < _mappingOffset) + _doPlace = true; + } else if ((_mappingOffset < 0x20)) { + BuildingTypeShort _existingBuildingType + = DAT_BuildingsState::instance.buildings[_existingBuildingID] + .buildingType; + if (_existingBuildingType == OpenSHC::Map::Buildings::BT_TOWER4 + || _existingBuildingType == OpenSHC::Map::Buildings::BT_TOWER5) + _doPlace = true; + } else { + _doPlace = true; + } + + if (_doPlace) { + int _requiredGold + = DAT_BuildingsState::instance + .buildingCosts[*(int*)((int)&DAT_SkirmishDefinedData::instance + .SiegeEngineMetaInfoArray[0] + .buildingType + + _mappingOffset)] + .requiredGold; + int _aicOffset = (_aiType + ~OpenSHC::AI::AIT_NULL) * 0x2a4; + _pDelay = &DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .delay; + *_pDelay = *_pDelay + 1; + if ((_requiredGold <= DAT_GameState::instance.playerDataArray[playerID] + .currentResources[0xf] + + *(int*)((int)this + _aicOffset + 0x118)) + && (*(int*)((int)this + _aicOffset + 0x11c) + <= DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .delay)) { + int _y = (int)DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[_location]; + int _placeTile = _location + - DAT_ViewportRenderState::instance.translationMatrix[_y] + .addXgetTile; + if (_siegeInfoIndex < 2) { + int _spawnedUnit = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::UnitsState_Func::spawnUnit, + DAT_UnitsState::ptr)(playerID, playerID, _placeTile * 8 + 4, + _y * 8 + 4, + (int)((int)(DAT_BuildingsState::instance + .buildings[_existingBuildingID] + .terrainHeightUnk)), + _unitType); + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeInfoIndex][_slotIndex] + .unitID = _spawnedUnit; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeInfoIndex][_slotIndex] + .uid = DAT_UnitsState::instance.units[_spawnedUnit].uid; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeInfoIndex][_slotIndex] + .buildingID = 0; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func:: + processPlacementResourceLossForBuildingType, + DAT_BuildingsState::ptr)(playerID, + (BuildingType)((int)(DAT_SkirmishDefinedData::instance + .SiegeEngineMetaInfoArray[_siegeInfoIndex] + .buildingType)), + 0); + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBuilding, + DAT_TileMapState::ptr)(playerID, _placeTile, _y, + (MappersEnum)(( + int)((uint)(ushort)(short)DAT_SkirmishDefinedData::instance + .SiegeEngineMetaInfoArray[_siegeInfoIndex] + .commandBuildingType)), + 3, 0xf); + if (DAT_TileMapState::instance.buildingPlacementFail == FALSE) { + int* _pGold = DAT_GameState::instance.playerDataArray[playerID] + .currentResources + + 0xf; + *_pGold = *_pGold - _requiredGold; + int _newBuildingID + = DAT_TileMapState::instance.placedBuildingID; + int _newUID = DAT_BuildingsState::instance + .buildings[DAT_TileMapState::instance + .placedBuildingID] + .uid; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeInfoIndex][_slotIndex] + .unitID = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeInfoIndex][_slotIndex] + .uid = _newUID; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeInfoIndex][_slotIndex] + .buildingID = _newBuildingID; + } + } + *(undefined4*)(playerID * 0x39f4 + 0x115f48c + _slotIndex * 20 + + _siegeInfoIndex * 100) = 0; + return; + } + } + } else { + int _requiredEngineerCount + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func:: + getRequiredEngineerCountForSiegeBuilding, + DAT_BuildingsState::ptr)(_tile); + if (DAT_BuildingsState::instance.buildings[_tile].currentEmployeeCount + != _requiredEngineerCount) { + if (_requiredEngineerCount + <= DAT_GameState::instance.playerDataArray[playerID] + .engineerCountRelated) { + int _engineersTribe = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::assignRequiredIdleEngineersToNewTribe, + this)(playerID, _requiredEngineerCount); + DAT_TribesState::instance.tribes[_engineersTribe].tribeType + = OpenSHC::AI::Tribes::AITT_ENGINEERS; + DAT_TribesState::instance.tribes[_engineersTribe].tribeBehaviorType + = OpenSHC::Map::Units::STBT_0x410_SIEGE_EQUIPMENT_CONSTRUCTION; + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeAnInstruction, + DAT_TribesState::ptr)(_engineersTribe, + OpenSHC::Map::Units:: + UIT_CONSTRUCT_SIEGE_EQUIPMENTOIL_DUTYENGINEERRELATED, + _tile, DAT_BuildingsState::instance.buildings[_tile].uid, 0); + return; + } + DAT_GameState::instance.playerDataArray[playerID].isEngineerRequired = TRUE; + } + } + } else { + if (_tile != 0) { + int _requiredEngineerCount2 + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func:: + getRequiredEngineerCountForSiegeBuilding, + DAT_BuildingsState::ptr)(_tile); + if (DAT_BuildingsState::instance.buildings[_tile].currentEmployeeCount + != _requiredEngineerCount2) { + if (_requiredEngineerCount2 + <= DAT_GameState::instance.playerDataArray[playerID] + .engineerCountRelated) { + int _engineersTribe2 = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::assignRequiredIdleEngineersToNewTribe, + this)(playerID, _requiredEngineerCount2); + DAT_TribesState::instance.tribes[_engineersTribe2].tribeType + = OpenSHC::AI::Tribes::AITT_ENGINEERS; + DAT_TribesState::instance.tribes[_engineersTribe2].tribeBehaviorType + = OpenSHC::Map::Units::STBT_0x410_SIEGE_EQUIPMENT_CONSTRUCTION; + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeAnInstruction, + DAT_TribesState::ptr)(_engineersTribe2, + OpenSHC::Map::Units:: + UIT_CONSTRUCT_SIEGE_EQUIPMENTOIL_DUTYENGINEERRELATED, + _tile, DAT_BuildingsState::instance.buildings[_tile].uid, 0); + return; + } + DAT_GameState::instance.playerDataArray[playerID].isEngineerRequired = TRUE; + } + } else { + int _remainingRequiredEngineers = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::UnitsState_Func::getRemainingRequiredEngineers, + DAT_UnitsState::ptr)(_unitID); + if (0 < _remainingRequiredEngineers) { + if (DAT_GameState::instance.playerDataArray[playerID].engineerCountRelated + < _remainingRequiredEngineers) { + DAT_GameState::instance.playerDataArray[playerID].isEngineerRequired + = TRUE; + } else { + int _tribeID = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::assignRequiredIdleEngineersToNewTribe, + this)(playerID, _remainingRequiredEngineers); + int _targetUnitID = (int)DAT_TribesState::instance.tribes[_tribeID] + .selectionTargetUnitID; + if ((_targetUnitID != 0) + && (MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func:: + calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(playerID, + (dword)((int)((int)(short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance + .units[_unitID] + .tile])), + (dword)((int)((int)(short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance + .units[_targetUnitID] + .tile])), + 0) + != 0)) { + DAT_TribesState::instance.tribes[_tribeID].tribeType + = OpenSHC::AI::Tribes::AITT_ENGINEERS; + DAT_TribesState::instance.tribes[_tribeID].tribeBehaviorType + = OpenSHC::Map::Units::STBT_0x41c; + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::UnitsState_Func::relayTribeInstruction, + DAT_UnitsState::ptr)(_tribeID, + OpenSHC::Map::Units::UIT_MAN_SIEGE_EQUIPMENT, _unitID, + DAT_UnitsState::instance.units[_unitID].uid, 0); + return; + } + } + } + } + } + } else { + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .unitID = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .uid = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .delay = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .unknown = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .buildingID = 0; + } + } else { + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .unitID = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .uid = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .delay = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .unknown = 0; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[0][_engineIndex] + .buildingID = 0; + } + } + + _slotIndex = _nextSlotIndex; + } while (_nextSlotIndex < _tribeSize); + } + + _siegeInfoIndex = _siegeInfoIndex + 1; + _incrementsBy5 = _incrementsBy5 + 5; + _mappingOffset = _mappingOffset + 16; + if (64 < _mappingOffset) + return; + } while (true); + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiFarmChoice.cpp b/src/OpenSHC/AI/AICState/aiFarmChoice.cpp new file mode 100644 index 00000000..ca1d6515 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiFarmChoice.cpp @@ -0,0 +1,22 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB180 + int AICState::aiFarmChoice(PlayerID playerID) + + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) { + return 0; + } + + /* ugly because EAX is always 1 or higher */ + return (&this->aics[(aiType - 1)].farm1)[DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex]; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiGiveOuterPatrolCommand.cpp b/src/OpenSHC/AI/AICState/aiGiveOuterPatrolCommand.cpp new file mode 100644 index 00000000..90ab12ec --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiGiveOuterPatrolCommand.cpp @@ -0,0 +1,92 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D28B0 + void AICState::aiGiveOuterPatrolCommand(int playerID) + { + int* piVar1; + short sVar2; + int iVar3; + short* psVar4; + int _buildingID; + int _buildingID2; + int _tile; + BOOLEnum _canNavigate; + int _aicOffset; + int _tribeID; + int _index; + int _count; + int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType != 0) { + _aicOffset = (_aiType + -1) * 0x2a4; + _count = (int)this->aics[_aiType - 1].OuterPatrolGroupsCount; + _index = 0; + if (((0 < _count) + && (piVar1 = &DAT_GameState::instance.playerDataArray[playerID].outerPatrolRallyDelayTracker, + *piVar1 = *piVar1 + 1, + (int)this->aics[_aiType - 1].OuterPatrolRallyDelay + <= DAT_GameState::instance.playerDataArray[playerID].outerPatrolRallyDelayTracker)) + && (DAT_GameState::instance.playerDataArray[playerID].outerPatrolRallyDelayTracker = 0, 0 < _count)) { + psVar4 = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + 0xaa; + do { + _tribeID = (int)*psVar4; + DAT_TribesState::instance.tribes[_tribeID].unitStance = Map::Units::Behavior::USE_AGGRESSIVE; + if (((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_index + 0xaa])) + && ((DAT_TribesState::instance.tribes[_tribeID].percentageMovingUnk <= 0xa + && ((DAT_TribesState::instance.tribes[_tribeID].percentageShootingUnk <= 0xa + && (DAT_TribesState::instance.tribes[_tribeID].percentageAttackingUnk <= 0xa)))))) { + bool _hasTarget = true; + if (((int)this->aics[_aiType - 1].OuterPatrolGroupsMove == 0) + || (_buildingID = MACRO_CALL_MEMBER(AICState_Func::getTargetableBuildingForPlayerID, this)( + playerID, _index), + _buildingID == 0)) { + _buildingID2 = MACRO_CALL_MEMBER( + AICState_Func::selectBuildingFromAListOfBuildingTypes, this)(playerID); + if (_buildingID2 == 0) { + _hasTarget = false; + } else { + _tile = (int)DAT_BuildingsState::instance.buildings[_buildingID2].buildingEntryX + + DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[_buildingID2] + .buildingEntryY] + .addXgetTile; + } + } else { + _tile = (int)DAT_BuildingsState::instance.buildings[_buildingID].buildingEntryX + + DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[_buildingID] + .buildingEntryY] + .addXgetTile; + } + if (_hasTarget && _tile != 0) { + sVar2 = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_tile]; + iVar3 = DAT_ViewportRenderState::instance.translationMatrix[sVar2].addXgetTile; + _canNavigate = MACRO_CALL_MEMBER(AICState_Func::canNavigateUnitsFromTileToTargetTile, this)( + _tribeID, _tile); + if (_canNavigate == FALSE) { + MACRO_CALL_MEMBER(AICState_Func::sendUnitsToCampfire, this)(_tribeID, playerID); + } else { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::commandUnitsToLocation, + DAT_TribesState::ptr)(_tribeID, _tile - iVar3, (int)sVar2, 0); + } + } + } + _index = _index + 1; + psVar4 = psVar4 + 1; + } while (_index < _count); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiGiveRaidInstructions.cpp b/src/OpenSHC/AI/AICState/aiGiveRaidInstructions.cpp new file mode 100644 index 00000000..eaf78554 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiGiveRaidInstructions.cpp @@ -0,0 +1,224 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Global.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Units::UnitInstructionType; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2A70 + void AICState ::aiGiveRaidInstructions(int playerID) + { + int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == 0) + return; + + int* piVar1 = &DAT_GameState::instance.playerDataArray[playerID].raidRetargetDelayCounter; + *piVar1 = *piVar1 + 1; + + BOOLEnum _indexTo6 = FALSE; + + if (*(int*)((int)this + (_aiType + -1) * 0x2a4 + 0x1f0) + <= DAT_GameState::instance.playerDataArray[playerID].raidRetargetDelayCounter) { + DAT_GameState::instance.playerDataArray[playerID].raidRetargetDelayCounter = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].totalRaidingTroopsUnk == 0) { + DAT_GameState::instance.playerDataArray[playerID].someTotalDefenseTroopsRelatedCountdown = 0; + } else { + int iVar4 = DAT_GameState::instance.playerDataArray[playerID].someTotalDefenseTroopsRelatedCountdown; + if (0 < iVar4) + DAT_GameState::instance.playerDataArray[playerID].someTotalDefenseTroopsRelatedCountdown = iVar4 + -1; + } + +// Helper: do the movement command given a tile +// (was shared LAB_004d2d8d block) +#define DO_MOVE_TO_TILE(_tile, _tribeID3) \ + do { \ + if ((_tile) != 0) { \ + short sVar2_ = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[(_tile)]; \ + int iVar4_ = DAT_ViewportRenderState::instance.translationMatrix[sVar2_].addXgetTile; \ + BOOLEnum BVar5_ = MACRO_CALL_MEMBER( \ + OpenSHC::AI::AICState_Func::canNavigateUnitsFromTileToTargetTile, this)((_tribeID3), (_tile)); \ + if (BVar5_ == FALSE) { \ + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)((_tribeID3), playerID); \ + } else { \ + MACRO_CALL_MEMBER( \ + OpenSHC::Map::Units::TribesState_Func::commandUnitsToLocation, DAT_TribesState::ptr)( \ + (_tribeID3), (uint)((int)((_tile) - iVar4_)), (uint)((int)((int)sVar2_)), 0); \ + } \ + } \ + } while (0) + + short* _ptrTribeID3 = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + 0xb4; + + do { + int _tribeID3 = (int)*_ptrTribeID3; + DAT_TribesState::instance.tribes[_tribeID3].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + if ((_tribeID3 != 0) + && (DAT_TribesState::instance.tribes[_tribeID3].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_indexTo6 + 0xb4])) { + + int iVar4; + BOOLEnum BVar5; + int _buildingID, _buildingID_2, _buildingTile; + + if ((DAT_GameState::instance.playerDataArray[playerID].requestStateUnk == 2) + && (MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(DAT_GameState::instance.playerDataArray[playerID].playerID_askerUnk) + != 0)) { + + if (DAT_GameState::instance.playerDataArray[playerID].raidRetargetDelayCounter == 0) { + _buildingID_2 = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::selectRandomBuildingIDFromListOfBuildingTypes, this)( + DAT_GameState::instance.playerDataArray[playerID].playerID_askerUnk, + (undefined4)((int)(_indexTo6))); + if (_buildingID_2 == 0) { + _buildingID = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::selectBuildingFromAListOfBuildingTypes, this)(playerID); + if (_buildingID != 0) { + _buildingTile = (int)DAT_BuildingsState::instance.buildings[_buildingID].buildingEntryX + + DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[_buildingID] + .buildingEntryY] + .addXgetTile; + DO_MOVE_TO_TILE(_buildingTile, _tribeID3); + } + } else { + _buildingTile = (int)DAT_BuildingsState::instance.buildings[_buildingID_2].buildingEntryX + + DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[_buildingID_2] + .buildingEntryY] + .addXgetTile; + DO_MOVE_TO_TILE(_buildingTile, _tribeID3); + } + } + + } else if ((DAT_GameState::instance.playerDataArray[playerID].someTotalDefenseTroopsRelatedCountdown + == 0) + && (iVar4 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::getCurrentDesiredAttackRaidUnitCount, + this)(_aiType + -1, playerID), + DAT_GameState::instance.playerDataArray[playerID].totalRaidingTroopsUnk < iVar4)) { + + if ((DAT_GameState::instance.playerDataArray[playerID].raidRetargetDelayCounter == 0) + && (BVar5 = MACRO_CALL(OpenSHC::Global_Func::CheckTribeActivityPercentages)( + _tribeID3, _indexTo6, TRUE), + BVar5 == FALSE)) { + + _buildingID = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::selectRandomBuildingIDFromListOfBuildingTypes, this)( + playerID, (undefined4)((int)(_indexTo6))); + + if (_buildingID != 0) { + _buildingTile = (int)DAT_BuildingsState::instance.buildings[_buildingID].buildingEntryX + + DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[_buildingID] + .buildingEntryY] + .addXgetTile; + DO_MOVE_TO_TILE(_buildingTile, _tribeID3); + } else { + _buildingID_2 = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::selectBuildingFromAListOfBuildingTypes, this)(playerID); + if (_buildingID_2 != 0) { + _buildingTile + = (int)DAT_BuildingsState::instance.buildings[_buildingID_2].buildingEntryX + + DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[_buildingID_2] + .buildingEntryY] + .addXgetTile; + DO_MOVE_TO_TILE(_buildingTile, _tribeID3); + } + } + } + + } else if ((DAT_SkirmishDefinedData::instance.TribeTypeID[_indexTo6] == 0xb6) + || (DAT_SkirmishDefinedData::instance.TribeTypeID[_indexTo6] == 0xb5)) { + + if ((DAT_GameState::instance.playerDataArray[playerID].raidRetargetDelayCounter == 0) + && (BVar5 = MACRO_CALL(OpenSHC::Global_Func::CheckTribeActivityPercentages)( + _tribeID3, _indexTo6, FALSE), + BVar5 == FALSE)) { + + _buildingID = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::selectRandomBuildingIDFromListOfBuildingTypes, this)( + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID_2Unk, + (undefined4)((int)(_indexTo6))); + + if (_buildingID == 0) { + _buildingID = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::chooseRandomBuildingIDFromPlayersArrayOfBuildings, this)( + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID_2Unk); + } + + if (_buildingID != 0) { + _buildingTile = (int)DAT_BuildingsState::instance.buildings[_buildingID].buildingEntryX + + DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[_buildingID] + .buildingEntryY] + .addXgetTile; + DO_MOVE_TO_TILE(_buildingTile, _tribeID3); + } + } + + } else { + BVar5 + = MACRO_CALL(OpenSHC::Global_Func::CheckTribeActivityPercentages)(_tribeID3, _indexTo6, FALSE); + if (BVar5 == FALSE) { + iVar4 = (int)DAT_TribesState::instance.tribes[_tribeID3].targetBuildingID; + if ((((iVar4 == 0) + || (DAT_BuildingsState::instance.buildings[iVar4].uid + != DAT_TribesState::instance.tribes[_tribeID3].targetBuildingUID)) + || (DAT_BuildingsState::instance.buildings[iVar4].fireDuration != 0)) + || (BVar5 = MACRO_CALL(OpenSHC::Global_Func::CheckTribeActivityPercentages)( + _tribeID3, _indexTo6, TRUE), + BVar5 == FALSE)) { + iVar4 = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::chooseRandomBuildingIDFromPlayersArrayOfBuildings, this)( + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID_2Unk); + if (iVar4 == 0) { + DAT_TribesState::instance.tribes[_tribeID3].targetBuildingID = 0; + DAT_GameState::instance.playerDataArray[playerID].someTotalDefenseTroopsRelatedCountdown + = 0; + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::relayTribeInstruction, + DAT_UnitsState::ptr)(_tribeID3, OpenSHC::Map::Units::UIT_ATTACK_BUILDING, iVar4, + DAT_BuildingsState::instance.buildings[iVar4].uid, 0); + int iVar3 = DAT_BuildingsState::instance.buildings[iVar4].uid; + DAT_TribesState::instance.tribes[_tribeID3].targetBuildingID = (short)iVar4; + DAT_TribesState::instance.tribes[_tribeID3].targetBuildingUID = iVar3; + DAT_GameState::instance.playerDataArray[playerID].someTotalDefenseTroopsRelatedCountdown + = 100; + } + } else { + if (DAT_GameState::instance.mapAndTime.yearChanged != 0) + DAT_TribesState::instance.tribes[_tribeID3].targetBuildingID = 0; + DAT_GameState::instance.playerDataArray[playerID].someTotalDefenseTroopsRelatedCountdown + = 100; + } + } + } + } + + _ptrTribeID3 = _ptrTribeID3 + 1; + _indexTo6 = _indexTo6 + TRUE; + } while ((int)_indexTo6 < 6); + +#undef DO_MOVE_TO_TILE + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiOfTypeInCurrentGame.cpp b/src/OpenSHC/AI/AICState/aiOfTypeInCurrentGame.cpp new file mode 100644 index 00000000..0de10b08 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiOfTypeInCurrentGame.cpp @@ -0,0 +1,55 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Game::Player::PlayerData; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names */ + + /* + WARNING: Enum "DPERRInt": Some values do not have unique names */ + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0580 + BOOLEnum AICState ::aiOfTypeInCurrentGame(int param_1) + + { + + int* _ptrCurrentAIArray; + + PlayerData* _playerData; + + _playerData = &DAT_GameState::instance.playerDataArray[1]; + + _ptrCurrentAIArray = DAT_GameSynchronyState::instance.currentAIArray + 1; + + while (((*_ptrCurrentAIArray == 0 || (_playerData->lordKilledByPlayerID != 0)) + || (param_1 != _playerData->aiType + ~OpenSHC::AI::AIT_NULL))) { + + _ptrCurrentAIArray = _ptrCurrentAIArray + 1; + + _playerData = _playerData + 0xe7d; + + if (0x191de9f < (int)_ptrCurrentAIArray) { + + return FALSE; + } + } + + return TRUE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiReassignTunnelersToTribe.cpp b/src/OpenSHC/AI/AICState/aiReassignTunnelersToTribe.cpp new file mode 100644 index 00000000..4b40f07e --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiReassignTunnelersToTribe.cpp @@ -0,0 +1,30 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D40B0 + void AICState::aiReassignTunnelersToTribe(int param_1) + { + for (int unitID = 1; unitID < (int)DAT_UnitsState::instance.maxUnitCount; unitID++) { + Map::Units::Unit* _pUnit = &DAT_UnitsState::instance.units[unitID]; + if ((((_pUnit->logicalState == Map::Units::ULS_NORMAL) && (_pUnit->owner == param_1)) + && (_pUnit->dying == 0)) + && ((_pUnit->unitType == Map::Units::UT_TUNNELER && (_pUnit->aiUnitBehaviourType == 0xf)))) { + if (_pUnit->tribeID != 0) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::removeUnitFromTribe, DAT_TribesState::ptr)( + unitID, (int)_pUnit->tribeID); + } + MACRO_CALL_MEMBER(AICState_Func::addUnitToItsTribe, this)(unitID, 0xf); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiRecruitEngineerForOilDuty.cpp b/src/OpenSHC/AI/AICState/aiRecruitEngineerForOilDuty.cpp new file mode 100644 index 00000000..c96ca96b --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRecruitEngineerForOilDuty.cpp @@ -0,0 +1,73 @@ +#include "OpenSHC/Global.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/AI/Tribes/AITribeType.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2610 + void AICState::aiRecruitEngineerForOilDuty(int playerID) + { + BOOLEnum _hasTribes; + int _newTribe; + int _unitID; + int _amount; + int _oilSmelter; + int _buildingUID; + if (((((DAT_GameState::instance.playerDataArray[playerID].aiType != AITA_NULL) + && (_newTribe = DAT_GameState::instance.playerDataArray[playerID].engineersGuild.id, + DAT_GameState::instance.playerDataArray[playerID].canStartSpending != 0)) + && (29 < DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf])) + && ((0 < _newTribe + && (DAT_BuildingsState::instance.buildings[_newTribe].buildingType + == Map::Buildings::BT_ENGINEERSGUILD)))) + && ((_oilSmelter = DAT_GameState::instance.playerDataArray[playerID].oilSmelter.id, + 0 < _oilSmelter + && ((DAT_BuildingsState::instance.buildings[_oilSmelter].buildingType + == Map::Buildings::BT_OILSMELTER + && (_amount + = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[1] + 1, + 1 < _amount)))))) { + if ((DAT_BuildingsState::instance.buildings[_oilSmelter].resourcePitch < 1) + && (DAT_GameState::instance.playerDataArray[playerID].currentResources[7] < 1)) { + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[7] = 4; + return; + } + _hasTribes + = MACRO_CALL(Global_Func::ChecksAndGenerateAITribesForPlayerIfNotExisting)(playerID, _amount, TRUE); + if (_hasTribes != FALSE) { + _newTribe = MACRO_CALL(Global_Func::ChecksAndGenerateAITribesForPlayerIfNotExisting)( + playerID, _amount, FALSE); + _unitID = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::nonEuroRecruit, DAT_UnitsState::ptr)( + Map::Units::UT_E_ENGINEER, DAT_GameState::instance.playerDataArray[playerID].engineersGuild.id, + playerID, 0); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + _unitID, _newTribe); + if (_unitID != 0) { + DAT_UnitsState::instance.units[_unitID].engineerRelatedUnk = 1; + DAT_TribesState::instance.tribes[_newTribe].unitStance = Map::Units::Behavior::USE_DEFENSIVE; + _buildingUID = DAT_BuildingsState::instance.buildings[_oilSmelter].uid; + DAT_TribesState::instance.tribes[_newTribe].tribeType = Tribes::AITT_ENGINEERS; + DAT_TribesState::instance.tribes[_newTribe].tribeBehaviorType = Map::Units::STBT_0x41d; + MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::relayTribeInstruction, DAT_UnitsState::ptr)( + _newTribe, Map::Units::UIT_CONSTRUCT_SIEGE_EQUIPMENTOIL_DUTYENGINEERRELATED, _oilSmelter, + _buildingUID, 0); + } + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiRecruitEngineers.cpp b/src/OpenSHC/AI/AICState/aiRecruitEngineers.cpp new file mode 100644 index 00000000..c42651ee --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRecruitEngineers.cpp @@ -0,0 +1,33 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC520 + void AICState::aiRecruitEngineers(int playerID) + { + int recruitmentBuilding; + int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if ((((_aiType != AITA_NULL) && (DAT_GameState::instance.playerDataArray[playerID].isEngineerRequired != FALSE)) + && (DAT_GameState::instance.playerDataArray[playerID].canStartSpending != 0)) + && (((0x1d < (int)this->aics[_aiType - 1].recruitGoldThreshold + + DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf] + && (recruitmentBuilding = DAT_GameState::instance.playerDataArray[playerID].engineersGuild.id, + 0 < recruitmentBuilding)) + && (DAT_BuildingsState::instance.buildings[recruitmentBuilding].buildingType + == Map::Buildings::BT_ENGINEERSGUILD)))) { + MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::nonEuroRecruit, DAT_UnitsState::ptr)( + Map::Units::UT_E_ENGINEER, recruitmentBuilding, playerID, 0); + DAT_GameState::instance.playerDataArray[playerID].isEngineerRequired = FALSE; + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiRecruitSortieMeleeUnits.cpp b/src/OpenSHC/AI/AICState/aiRecruitSortieMeleeUnits.cpp new file mode 100644 index 00000000..3b011e85 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRecruitSortieMeleeUnits.cpp @@ -0,0 +1,113 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::Map::Units::UnitType; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD690 + void AICState ::aiRecruitSortieMeleeUnits(int playerID) + + { + + AITypeInt AVar1; + + int iVar2; + + int _unitID; + + int _section1016id; + + UnitType _unitType; + + AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (AVar1 != OpenSHC::AI::AIT_NULL) { + + iVar2 = (AVar1 + ~OpenSHC::AI::AIT_NULL) * 0x2a4; + + _section1016id = *(int*)((int)this + iVar2 + 0x154); + + if (((((-1 < _section1016id) + && (DAT_GameState::instance.playerDataArray[playerID].totalTroopsType7 < _section1016id)) + && (0 < DAT_GameState::instance.playerDataArray[playerID].idlePeasantsCount)) + && ((DAT_GameState::instance.playerDataArray[playerID].canStartSpending != 0 + && (_unitType = *(UnitType*)((int)this + iVar2 + 0x158), + _unitType != OpenSHC::Map::Units::UT_E_ENGINEER)))) + && ((_unitType != OpenSHC::Map::Units::UT_E_LADDER + && (_unitType != OpenSHC::Map::Units::UT_TUNNELER)))) { + + if ((int)_unitType < 0x46) { + + _section1016id = DAT_GameState::instance.playerDataArray[playerID].barracks.id; + + } + + else { + + _section1016id = DAT_GameState::instance.playerDataArray[playerID].mercenaryPost.id; + } + + if (_section1016id != 0) { + + if ((int)_unitType < 0x46) { + + _unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::euroRecruit, + DAT_UnitsState::ptr)(_unitType, (undefined4)((int)(_section1016id)), playerID, 0); + + } + + else { + + _unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::nonEuroRecruit, + DAT_UnitsState::ptr)(_unitType, (undefined4)((int)(_section1016id)), playerID, 0); + } + + if (_unitID == 0) { + + _section1016id = *(int*)((int)this + iVar2 + 0x9c); + + if ((0 < _section1016id) && (DAT_UnitsState::instance.DAT_EuroUnitAcquisitionFailReason == 2)) { + + DAT_GameState::instance.playerDataArray[playerID] + .resourcesToAcquireArray[DAT_UnitsState::instance.DAT_EuroUnitRequiredResource] + = _section1016id; + + return; + } + + } + + else { + + DAT_UnitsState::instance.units[_unitID].aiUnitBehaviourType = 7; + + _section1016id = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::createTribeForUnitType, this)( + playerID, (int)((int)(167))); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + _unitID, _section1016id); + } + } + } + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiRecruitSortieRangedUnits.cpp b/src/OpenSHC/AI/AICState/aiRecruitSortieRangedUnits.cpp new file mode 100644 index 00000000..b9e473e2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRecruitSortieRangedUnits.cpp @@ -0,0 +1,117 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::Map::Units::UnitType; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD560 + void AICState ::aiRecruitSortieRangedUnits(int playerID) + + { + + UnitType unitType; + + int iVar1; + + uint _unitID; + + int iVar2; + + int _buildingID; + + AITypeInt _aiType; + + _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (_aiType != OpenSHC::AI::AIT_NULL) { + + iVar1 = (_aiType + ~OpenSHC::AI::AIT_NULL) * 0x2a4; + + iVar2 = *(int*)((int)this + iVar1 + 0x14c); + + /* + bug: missing != 0? */ + + if (((((-1 < iVar2) + && (DAT_GameState::instance.playerDataArray[playerID].totalTroopsType6 + < DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01 / 2 + iVar2)) + && (0 < DAT_GameState::instance.playerDataArray[playerID].idlePeasantsCount)) + && ((DAT_GameState::instance.playerDataArray[playerID].canStartSpending != 0 + && (unitType = *(UnitType*)((int)this + iVar1 + 0x150), + unitType != OpenSHC::Map::Units::UT_E_ENGINEER)))) + && ((unitType != OpenSHC::Map::Units::UT_E_LADDER && (unitType != OpenSHC::Map::Units::UT_TUNNELER)))) { + + if ((int)unitType < 0x46) { + + _buildingID = DAT_GameState::instance.playerDataArray[playerID].barracks.id; + + } + + else { + + _buildingID = DAT_GameState::instance.playerDataArray[playerID].mercenaryPost.id; + } + + if (_buildingID != 0) { + + if ((int)unitType < 0x46) { + + _unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::euroRecruit, + DAT_UnitsState::ptr)(unitType, (undefined4)((int)(_buildingID)), playerID, 0); + + } + + else { + + _unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::nonEuroRecruit, + DAT_UnitsState::ptr)(unitType, (undefined4)((int)(_buildingID)), playerID, 0); + } + + if (_unitID == 0) { + + iVar2 = *(int*)((int)this + iVar1 + 0x9c); + + if ((0 < iVar2) && (DAT_UnitsState::instance.DAT_EuroUnitAcquisitionFailReason == 2)) { + + DAT_GameState::instance.playerDataArray[playerID] + .resourcesToAcquireArray[DAT_UnitsState::instance.DAT_EuroUnitRequiredResource] = iVar2; + + return; + } + + } + + else { + + DAT_UnitsState::instance.units[_unitID].aiUnitBehaviourType = 6; + + iVar2 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::createTribeForUnitType, this)( + playerID, (int)((int)(166))); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + _unitID, iVar2); + } + } + } + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiRecruitUnits.cpp b/src/OpenSHC/AI/AICState/aiRecruitUnits.cpp new file mode 100644 index 00000000..22307f6a --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRecruitUnits.cpp @@ -0,0 +1,238 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AIRecruitUnitChoice.hpp" +#include "OpenSHC/AI/AIRecruitUnitChoiceInt.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/AI/AIUnitBehaviourType.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Game/TrailType.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AICState; + using OpenSHC::AI::AIRecruitUnitChoice; + using OpenSHC::AI::AIRecruitUnitChoiceInt; + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::AI::AIUnitBehaviourType; + using OpenSHC::Game::GameMode2; + using OpenSHC::Game::TrailType; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3AE0 + void AICState ::aiRecruitUnits(int playerID) + { + int _playerID2 = playerID; + AITypeInt _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + UnitType _unitType = ((UnitType)0); + AICState* _pAICState = this; + int _tribeID_defDiggingUnitMax; + + if (_aiType == OpenSHC::AI::AIT_NULL) + return; + + bool bVar3 = false; + bool _recruitDefDiggingUnit = false; + int iVar6 = _aiType + ~OpenSHC::AI::AIT_NULL; + + if (((DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_SKIRMISH_AND_MULTIPLAYER) + && (DAT_GameCore::instance.isSkirmishTrail == TRUE)) + && (DAT_GameCore::instance.currentTrailType == OpenSHC::Game::TT_EXTREME)) + bVar3 = true; + + int iVar4 = iVar6 * 0xa9; + int _aicRecruitInterval = *(int*)((int)this + + (DAT_GameState::instance.playerDataArray[playerID].aiStrengthFeeling + iVar4) * 4 + 0x164); + int local_8 = -(uint)(_aicRecruitInterval != 0) + 2; + if (bVar3) + local_8 = -(uint)(_aicRecruitInterval != 0) + 3; + + int _nervousActionTracker = DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker; + + if (_nervousActionTracker < 1) { + int* piVar1 = &DAT_GameState::instance.playerDataArray[playerID].aiRecruitIntervalTracker; + *piVar1 = *piVar1 + 1; + if (DAT_GameState::instance.playerDataArray[playerID].aiRecruitIntervalTracker < _aicRecruitInterval) + return; + } else { + local_8 = 4; + } + + int iVar5 = DAT_GameState::instance.playerDataArray[playerID].idlePeasantsCount; + DAT_GameState::instance.playerDataArray[playerID].aiRecruitIntervalTracker = 0; + + if ((0 < iVar5) && (DAT_GameState::instance.playerDataArray[playerID].canStartSpending != 0)) { + AIRecruitUnitChoiceInt AVar2 = DAT_GameState::instance.playerDataArray[playerID].aiRecruitUnitChoiceState; + + if (AVar2 == OpenSHC::AI::AIRUC_DEFENSIVE) { + int _defTotal = *(int*)((int)this + iVar6 * 0x2a4 + 0x170); + if (bVar3) _defTotal = (_defTotal * 4) / 3; + if (0 < _nervousActionTracker) _defTotal = _defTotal * 4; + if (_defTotal <= DAT_GameState::instance.playerDataArray[playerID].totalDefensiveTroopsUnk) + return; + } else if (AVar2 == OpenSHC::AI::AIRUC_RAIDING) { + iVar5 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::getCurrentDesiredAttackRaidUnitCount, this)( + iVar6, playerID); + if (iVar5 <= DAT_GameState::instance.playerDataArray[playerID].totalRaidingTroopsUnk) + return; + } else if ((AVar2 == OpenSHC::AI::AIRUC_ATTACKING) + && (DAT_GameState::instance.playerDataArray[playerID].aiPlayerState != 0)) + return; + + iVar6 = iVar6 * 0x2a4; + + if (((*(int*)((int)this + iVar6 + 0x15c) != 0) + && (iVar5 = MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::countUnfinishedMoatTilesForPlayer, + DAT_TileMapState::ptr)(playerID), iVar5 != 0)) + && ((_tribeID_defDiggingUnitMax = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[10], + _tribeID_defDiggingUnitMax == 0 + || ((DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[10] + != DAT_TribesState::instance.tribes[_tribeID_defDiggingUnitMax].uid + || ((int)DAT_TribesState::instance.tribes[_tribeID_defDiggingUnitMax].size + < *(int*)((int)this + iVar6 + 0x15c))))))) { + _recruitDefDiggingUnit = true; + } + + playerID = 0; + if (0 < local_8) { + do { + // If we should recruit a digging unit but RNG says no, fall through to normal recruit + if (_recruitDefDiggingUnit && ((byte)SEC_RNG::instance.currentNumber2 & 1) != 0) + _recruitDefDiggingUnit = false; + + if (_recruitDefDiggingUnit) { + if (DAT_GameState::instance.playerDataArray[_playerID2].barracks.id == 0) { + if (DAT_GameState::instance.playerDataArray[_playerID2].mercenaryPost.id == 0) + return; + } + _unitType = *(UnitType*)((int)this + iVar6 + 0x160); + } else { + AIRecruitUnitChoiceInt _unitTypeChoice = + DAT_GameState::instance.playerDataArray[_playerID2].aiRecruitUnitChoiceState; + + if (_unitTypeChoice == OpenSHC::AI::AIRUC_DEFENSIVE) { + if (7 < DAT_GameState::instance.playerDataArray[_playerID2].aiDefUnitChoiceIndex) + DAT_GameState::instance.playerDataArray[_playerID2].aiDefUnitChoiceIndex = 0; + if (*(int*)((int)this + + (DAT_GameState::instance.playerDataArray[_playerID2].aiDefUnitChoiceIndex + iVar4) + * 4 + 0x184) == 0) + DAT_GameState::instance.playerDataArray[_playerID2].aiDefUnitChoiceIndex = 0; + int _unitChoiceIndex = + DAT_GameState::instance.playerDataArray[_playerID2].aiDefUnitChoiceIndex; + _unitType = *(UnitType*)((int)this + (iVar4 + _unitChoiceIndex) * 4 + 0x184); + DAT_GameState::instance.playerDataArray[_playerID2].aiDefUnitChoiceIndex = + _unitChoiceIndex + 1; + } else if (_unitTypeChoice == OpenSHC::AI::AIRUC_RAIDING) { + if (7 < DAT_GameState::instance.playerDataArray[_playerID2].aiRaidUnitChoiceIndex) + DAT_GameState::instance.playerDataArray[_playerID2].aiRaidUnitChoiceIndex = 0; + if (*(int*)((int)this + + (DAT_GameState::instance.playerDataArray[_playerID2].aiRaidUnitChoiceIndex + iVar4) + * 4 + 0x1ac) == 0) + DAT_GameState::instance.playerDataArray[_playerID2].aiRaidUnitChoiceIndex = 0; + iVar5 = DAT_GameState::instance.playerDataArray[_playerID2].aiRaidUnitChoiceIndex; + _unitType = *(UnitType*)((int)this + (iVar4 + iVar5) * 4 + 0x1ac); + DAT_GameState::instance.playerDataArray[_playerID2].aiRaidUnitChoiceIndex = iVar5 + 1; + } else if (_unitTypeChoice == OpenSHC::AI::AIRUC_ATTACKING) { + _pAICState = (AICState*)MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::randomlySelectAttackUnitTypeToRecruit, this)(_playerID2); + _unitType = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::getUnitTypeForUnitBehaviourType, this)( + _playerID2, (AIUnitBehaviourType)((int)(_pAICState))); + } + } + + if (_unitType == ((UnitType)0)) + return; + + int _buildingID; + if (_unitType == OpenSHC::Map::Units::UT_E_ENGINEER) + _buildingID = DAT_GameState::instance.playerDataArray[_playerID2].engineersGuild.id; + else if (_unitType == OpenSHC::Map::Units::UT_E_LADDER) + _buildingID = DAT_GameState::instance.playerDataArray[_playerID2].engineersGuild.id; + else if (_unitType == OpenSHC::Map::Units::UT_TUNNELER) + _buildingID = DAT_GameState::instance.playerDataArray[_playerID2].tunnelersGuild.id; + else if (_unitType == OpenSHC::Map::Units::UT_E_MONK) + _buildingID = MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::findFirstBuildingIDForPlayerAndType, + DAT_BuildingsState::ptr)(_playerID2, OpenSHC::Map::Buildings::BT_CATHEDRAL); + else if ((int)_unitType < 0x46) + _buildingID = DAT_GameState::instance.playerDataArray[_playerID2].barracks.id; + else + _buildingID = DAT_GameState::instance.playerDataArray[_playerID2].mercenaryPost.id; + + if (_buildingID == 0) + return; + + int _unitID; + if (((_unitType == OpenSHC::Map::Units::UT_E_ENGINEER) + || (_unitType == OpenSHC::Map::Units::UT_E_LADDER) + || (_unitType == OpenSHC::Map::Units::UT_TUNNELER)) + || ((_unitType == OpenSHC::Map::Units::UT_E_MONK || (0x45 < (int)_unitType)))) { + _unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::nonEuroRecruit, + DAT_UnitsState::ptr)(_unitType, (undefined4)((int)(_buildingID)), _playerID2, 0); + } else { + _unitID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::euroRecruit, + DAT_UnitsState::ptr)(_unitType, (undefined4)((int)(_buildingID)), _playerID2, 0); + } + + if (_unitID == 0) { + iVar6 = *(int*)((int)this + iVar6 + 0x9c); + if (iVar6 < 1) + return; + if (DAT_UnitsState::instance.DAT_EuroUnitAcquisitionFailReason != 2) + return; + if (DAT_GameState::instance.playerDataArray[_playerID2].aiNervousActionsTracker < 1) { + DAT_GameState::instance.playerDataArray[_playerID2] + .resourcesToAcquireArray[DAT_UnitsState::instance.DAT_EuroUnitRequiredResource] = iVar6; + return; + } + DAT_GameState::instance.playerDataArray[_playerID2] + .resourcesToAcquireArray[DAT_UnitsState::instance.DAT_EuroUnitRequiredResource] = 5; + return; + } + + if (_recruitDefDiggingUnit) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiAddUnitToMoatDiggerTribe, this)(_unitID); + } else { + AVar2 = DAT_GameState::instance.playerDataArray[_playerID2].aiRecruitUnitChoiceState; + if (AVar2 == OpenSHC::AI::AIRUC_DEFENSIVE) { + if (DAT_GameState::instance.playerDataArray[_playerID2].totalDefensiveTroopsUnk + < *(int*)((int)this + iVar6 + 0x180)) + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::assignUnitToATribe, this)(_unitID); + else + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addUnitToSmallestPatrolTribe, this)(_unitID); + } else if (AVar2 == OpenSHC::AI::AIRUC_RAIDING) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiAssignUnitToDefensiveTribe, this)(_unitID); + } else if (AVar2 == OpenSHC::AI::AIRUC_ATTACKING) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addUnitToItsTribe, this)( + _unitID, (int)((int)(_pAICState))); + } + } + + playerID = playerID + 1; + if (local_8 <= playerID) + return; + } while (true); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiRegisterTribeAndAssignTarget.cpp b/src/OpenSHC/AI/AICState/aiRegisterTribeAndAssignTarget.cpp new file mode 100644 index 00000000..74dfe4fd --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRegisterTribeAndAssignTarget.cpp @@ -0,0 +1,69 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D1950 + void AICState ::aiRegisterTribeAndAssignTarget(int tribeID, int tribeUID) + + { + + int _index; + + short* _tribe; + + int* _uid; + + if (DAT_TribesState::instance.tribes[tribeID].size < 1) { + + DAT_TribesState::instance.tribes[tribeID].tribeState = 3; + + return; + } + + if (DAT_GameState::instance.playerDataArray[DAT_TribesState::instance.tribes[tribeID].owner].aiType + != OpenSHC::AI::AIT_NULL) { + + _index = 0; + + _uid = this->tribeUIDArray; + + _tribe = this->tribeIDArray; + + do { + + if ((*_tribe == 0) || (DAT_TribesState::instance.tribes[*_tribe].uid != *_uid)) { + + this->tribeIDArray[_index] = (short)tribeID; + + this->tribeUIDArray[_index] = tribeUID; + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setTargetForTribeBasedOnTargetChoiceLogic, this)( + DAT_TribesState::instance.tribes[tribeID].owner, tribeID); + + return; + } + + _index = _index + 1; + + _tribe = _tribe + 1; + + _uid = _uid + 1; + + } while (_index < 1000); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiRequiresExtraOxtethers.cpp b/src/OpenSHC/AI/AICState/aiRequiresExtraOxtethers.cpp new file mode 100644 index 00000000..8ccc1c4d --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRequiresExtraOxtethers.cpp @@ -0,0 +1,97 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Buildings::BuildingType; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB3A0 + undefined4 AICState ::aiRequiresExtraOxtethers(int playerID) + + { + int _playerID = playerID; + + int _buildingID + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findFirstBuildingIDForPlayerAndType, + DAT_BuildingsState::ptr)(playerID, OpenSHC::Map::Buildings::BT_QUARRY); + + // _stoneWaiting declared first so it gets lower stack slot + int _stoneWaiting = 0; + int _quarryID = 0; + + int _oxTetherCount = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::countBuildingsForPlayer, + DAT_BuildingsState::ptr)(_playerID, OpenSHC::Map::Buildings::BT_OXTETHER, 1); + + if (_oxTetherCount >= 0xa) { + return (undefined4)(0); + } + + int* piVar1 = &DAT_GameState::instance.playerDataArray[_playerID].highestLoadedQuarryUnk; + + *piVar1 = 0; + + if (_buildingID != 0) { + + do { + int _localStone + = DAT_BuildingsState::instance + .buildings[(short)DAT_BuildingsState::instance.buildings[_buildingID].quarryStockpileID] + .resourceStone; + + int _linkedTetherCount = 0; + + short* _linkedTetherIDs = DAT_BuildingsState::instance.buildings[_buildingID].quarryLinkedOxTethers; + + int _oxTetherLoopIndex = 3; + + do { + int _linkedTetherID = (int)*_linkedTetherIDs; + + if (_linkedTetherID != 0) { + if ((DAT_BuildingsState::instance.buildings[_linkedTetherID].buildingType + == OpenSHC::Map::Buildings::BT_OXTETHER) + && (DAT_BuildingsState::instance.buildings[_linkedTetherID].oxtetherLinkedQuarryID + == _buildingID)) { + _linkedTetherCount = _linkedTetherCount + 1; + } else { + *_linkedTetherIDs = 0; + } + } + + _linkedTetherIDs = _linkedTetherIDs + 1; + _oxTetherLoopIndex = _oxTetherLoopIndex + -1; + + } while (_oxTetherLoopIndex != 0); + + if (_linkedTetherCount <= 0) { + *piVar1 = _buildingID; + return (undefined4)(1); + } + + if (_stoneWaiting < _localStone / _linkedTetherCount) { + _quarryID = _buildingID; + _stoneWaiting = _localStone / _linkedTetherCount; + } + + _buildingID + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findNextBuildingForPlayerAndType, + DAT_BuildingsState::ptr)(_playerID, OpenSHC::Map::Buildings::BT_QUARRY, _buildingID); + + } while (_buildingID != 0); + + if (0x14 < _stoneWaiting) { + *piVar1 = _quarryID; + return (undefined4)(1); + } + } + + return (undefined4)(0); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiRetreatUnits.cpp b/src/OpenSHC/AI/AICState/aiRetreatUnits.cpp new file mode 100644 index 00000000..a80d0065 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRetreatUnits.cpp @@ -0,0 +1,60 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + using OpenSHC::AI::AIType; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE4D0 + void AICState::aiRetreatUnits(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return; + + for (int _index1 = 0; _index1 < 11; _index1++) { + int const _tribeTypeStart = (int)DAT_SkirmishDefinedData::instance.MaxAttackTribes1[_index1].tribeType; + int _tribeIndex = 0; + int const _maxTribeCount = (int)DAT_SkirmishDefinedData::instance.MaxAttackTribes1[_index1].tribeCount; + + for (; _tribeIndex < _maxTribeCount; _tribeIndex++) { + int const _tribeID + = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[_tribeTypeStart + _tribeIndex]; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[_tribeTypeStart + _tribeIndex])) { + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + if (_tribeTypeStart == 0xbe) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)(_tribeID, playerID); + } else if (_tribeTypeStart == 0xd) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)(_tribeID, playerID); + } else { + int const _hasHorses + = MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::selectionContainsHorses, + DAT_TribesState::ptr)(_tribeID); + if (_hasHorses != FALSE) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)( + _tribeID, playerID); + } else { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(_tribeID, playerID); + } + } + } + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiShouldAttackOrWaitForTeamCoordination.cpp b/src/OpenSHC/AI/AICState/aiShouldAttackOrWaitForTeamCoordination.cpp new file mode 100644 index 00000000..1f3a4f19 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiShouldAttackOrWaitForTeamCoordination.cpp @@ -0,0 +1,109 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Synchrony/GameSynchronyState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::Game::GameMode2; + using OpenSHC::Game::Player::PlayerData; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDCF0 + BOOLEnum AICState ::aiShouldAttackOrWaitForTeamCoordination(int playerID) + + { + + AITypeInt _aiType; + + _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (_aiType == OpenSHC::AI::AIT_NULL) { + return FALSE; + } + + if (DAT_GameState::instance.playerDataArray[playerID].requestStateUnk != 1) { + int _attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + if (DAT_GameState::instance.playerDataArray[_attackedPlayerID].totalTroopValue < 100) { + DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength = 0; + } + + int _attForceBase = *(int*)((int)this + (_aiType - 1) * 0x2a4 + 0x1f4); + int _currentWaveStrength = DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength; + int _totalAttack = DAT_GameState::instance.playerDataArray[playerID].totalAttackTroops + - DAT_GameState::instance.playerDataArray[playerID].totalAttackingEngineerTroops; + + if (_totalAttack < _attForceBase + _currentWaveStrength) { + return FALSE; + } + + if (DAT_GameState::instance.playerDataArray[playerID].aiAttackCoordinationLevel == 0) { + return TRUE; + } + + int _readyTeamMembers = 0; + int _unreadyTeamMembers = 0; + int _iPlayer = 1; + PlayerData* _pPlayerData = &DAT_GameState::instance.playerDataArray[1]; + + do { + if (_iPlayer != playerID) { + int _isAlive = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(_iPlayer); + if (_isAlive != 0) { + BOOLEnum _isAI = MACRO_CALL_MEMBER(OpenSHC::Synchrony::GameSynchronyState_Func::isAIPlayer, + DAT_GameSynchronyState::ptr)(_iPlayer); + if (_isAI != FALSE) { + if (DAT_GameState::instance.mapAndTime.playerTeams[_iPlayer] + == DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + if (_pPlayerData->aiPlayerState != 0) { + return TRUE; + } + if (_pPlayerData->totalAttackTroops - _pPlayerData->totalAttackingEngineerTroops + >= *(int*)((int)this + (_pPlayerData->aiType - 1) * 0x2a4 + 0x1f4) + + _pPlayerData->currentWaveRandomAttackingStrength) { + _readyTeamMembers = _readyTeamMembers + 1; + } + else { + _unreadyTeamMembers = _unreadyTeamMembers + 1; + } + } + } + } + } + _iPlayer = _iPlayer + 1; + _pPlayerData = _pPlayerData + 1; + } while (_iPlayer < 9); + + if (_readyTeamMembers >= DAT_GameState::instance.playerDataArray[playerID].aiAttackCoordinationLevel) { + return TRUE; + } + if (_unreadyTeamMembers == 0) { + return TRUE; + } + DAT_GameState::instance.playerDataArray[playerID].aiCoordinatedAttackPatience = + DAT_GameState::instance.playerDataArray[playerID].aiCoordinatedAttackPatience + 1; + if (DAT_GameState::instance.playerDataArray[playerID].aiCoordinatedAttackPatience <= 0x30 + || (DAT_GameCore::instance.gameMode_2 == OpenSHC::Game::GM_CAMPAIGN_MISSION)) { + return FALSE; + } + } + + return TRUE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiShouldBuildFarm.cpp b/src/OpenSHC/AI/AICState/aiShouldBuildFarm.cpp new file mode 100644 index 00000000..8f077577 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiShouldBuildFarm.cpp @@ -0,0 +1,31 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB220 + BOOLEnum AICState::aiShouldBuildFarm(int playerID) + { + unsigned int aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) { + return (BOOLEnum)aiType; + } + aiType -= 1; + int const _farmCount + = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::countFarms, DAT_BuildingsState::ptr)(playerID, 1); + if (_farmCount <= 0) { + return TRUE; + } + if (_farmCount < (int)this->aics[aiType].maxFarms) { + return DAT_GameState::instance.playerDataArray[playerID].currentPopulation / _farmCount + >= (int)this->aics[aiType].populationPerFarm; + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiShouldBuildIronMine.cpp b/src/OpenSHC/AI/AICState/aiShouldBuildIronMine.cpp new file mode 100644 index 00000000..7ddf0e04 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiShouldBuildIronMine.cpp @@ -0,0 +1,34 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB4D0 + uint AICState::aiShouldBuildIronMine(PlayerID playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) { + return 0; + } + AICSpecification const* spec = &this->aics[aiType - 1]; + if ((int)spec->populationPerIronmine <= 0) { + return 0; + } + int iVar3 = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::countBuildingsForPlayer, + DAT_BuildingsState::ptr)(playerID, Map::Buildings::BT_IRONMINE, 1); + if (iVar3 < 1) { + iVar3 = 1; + } else if (iVar3 >= (int)spec->maxIronmines) { + return 0; + } + return DAT_GameState::instance.playerDataArray[playerID].currentPopulation / iVar3 + >= (int)spec->populationPerIronmine; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiShouldBuildPitchrig.cpp b/src/OpenSHC/AI/AICState/aiShouldBuildPitchrig.cpp new file mode 100644 index 00000000..0227c21c --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiShouldBuildPitchrig.cpp @@ -0,0 +1,36 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB540 + uint AICState::aiShouldBuildPitchrig(PlayerID param_1) + { + int const aiType = DAT_GameState::instance.playerDataArray[param_1].aiType; + if (aiType == AITA_NULL) { + return 0; + } + AICSpecification const* spec = &this->aics[aiType - 1]; + if ((int)spec->populationPerPitchrig <= 0) { + return 0; + } + int iVar3 = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::countBuildingsForPlayer, + DAT_BuildingsState::ptr)(param_1, Map::Buildings::BT_PITCHRIG, 1); + if (iVar3 < 1) { + iVar3 = 1; + } else { + if (iVar3 >= (int)spec->maxPitchrigs) { + return 0; + } + } + return DAT_GameState::instance.playerDataArray[param_1].currentPopulation / iVar3 + >= (int)spec->populationPerPitchrig; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiShouldBuildQuarry.cpp b/src/OpenSHC/AI/AICState/aiShouldBuildQuarry.cpp new file mode 100644 index 00000000..ac4afab7 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiShouldBuildQuarry.cpp @@ -0,0 +1,34 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB330 + uint AICState::aiShouldBuildQuarry(PlayerID playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) { + return 0; + } + AICSpecification const* spec = &this->aics[aiType - 1]; + if ((int)spec->populationPerQuarry <= 0) { + return 0; + } + int _quarryCount = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::countBuildingsForPlayer, + DAT_BuildingsState::ptr)(playerID, Map::Buildings::BT_QUARRY, 1); + if (_quarryCount < 1) { + _quarryCount = 1; + } else if (_quarryCount >= (int)spec->maxQuarries) { + return 0; + } + return DAT_GameState::instance.playerDataArray[playerID].currentPopulation / _quarryCount + >= (int)spec->populationPerQuarry; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiShouldBuildWoodcutter.cpp b/src/OpenSHC/AI/AICState/aiShouldBuildWoodcutter.cpp new file mode 100644 index 00000000..221047ca --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiShouldBuildWoodcutter.cpp @@ -0,0 +1,47 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB290 + BOOLEnum AICState::aiShouldBuildWoodcutter(int playerID) + { + if (DAT_AIVState::instance.mapExtraInfo.totalWoodAvailable <= 0) { + return FALSE; + } + int aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) { + return FALSE; + } + aiType -= 1; + // AICSpecification const* spec = &this->aics[aiType]; + if ((int)this->aics[aiType].populationPerWoodcutter <= 0) { + return FALSE; + } + int iVar3 = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::countBuildingsForPlayer, + DAT_BuildingsState::ptr)(playerID, Map::Buildings::BT_WOODCUTTERSHUT, 1); + if (iVar3 <= 0) { + iVar3 = 1; + } else { + if (iVar3 >= (int)this->aics[aiType].maxWoodcutters) { + return FALSE; + } + if ((6 <= iVar3) && (DAT_AIVState::instance.mapExtraInfo.totalWoodAvailable <= 90)) { + return FALSE; + } + if ((3 <= iVar3) && (DAT_AIVState::instance.mapExtraInfo.totalWoodAvailable <= 30)) { + return FALSE; + } + } + return DAT_GameState::instance.playerDataArray[playerID].currentPopulation / iVar3 + >= (int)this->aics[aiType].populationPerWoodcutter; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiUpdateTaxesAndRations.cpp b/src/OpenSHC/AI/AICState/aiUpdateTaxesAndRations.cpp new file mode 100644 index 00000000..7bd2e0f1 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiUpdateTaxesAndRations.cpp @@ -0,0 +1,105 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CAEA0 + void AICState::aiUpdateTaxesAndRations(int playerID) + { + AITypeInt _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == OpenSHC::AI::AIT_NULL) + return; + + _aiType = _aiType - 1; + int const _popularity = DAT_GameState::instance.playerDataArray[playerID].popularity; + + if (_popularity <= (int)this->aics[_aiType].criticalPopularity) { + DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue = 2; + } else if (_popularity <= (int)this->aics[_aiType].lowestPopularity) { + DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue = 1; + } else if (_popularity >= (int)this->aics[_aiType].highestPopularity) { + DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue = 0; + } + + int const _decision = DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue; + + if (_decision == 2) { + int const _totalFood = DAT_GameState::instance.playerDataArray[playerID].totalFood; + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = 2; + if (_totalFood <= 0) { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 0; + } else if (_totalFood <= 4) { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 2; + } else { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = (_totalFood > 8) + 3; + } + } else if (_decision == 1) { + int const _currentTaxes = DAT_GameState::instance.playerDataArray[playerID].taxesSetting; + int const _taxesMin = (int)this->aics[_aiType].taxesMin; + if (_currentTaxes > _taxesMin) { + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = _currentTaxes - 1; + } else { + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = _taxesMin; + } + int const _totalFood = DAT_GameState::instance.playerDataArray[playerID].totalFood; + if (_totalFood <= 0) { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 0; + } else if (_totalFood <= 10) { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 2; + } else { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 3; + } + int const _rationsMod = (int)this->aics[_aiType].doubleRationsFoodThreshold; + if (_rationsMod != 0 && _totalFood >= _rationsMod) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 4; + } else if (_decision == 0) { + int const _taxes = DAT_GameState::instance.playerDataArray[playerID].taxesSetting; + int const _taxesMax = (int)this->aics[_aiType].taxesMax; + + if (_taxes < _taxesMax) { + int const _peasants = DAT_GameState::instance.playerDataArray[playerID].previousAvailablePeasants; + int _newTaxesVal; + if (_peasants >= 0xf) { + _newTaxesVal = 6; + } else { + _newTaxesVal = (_peasants >= 0xa) + 4; + } + + if (!(DAT_GameState::instance.playerDataArray[playerID].storedPopularityPercent > _popularity)) { + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = _taxes + 1; + } else if (_taxes > _newTaxesVal) { + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = _taxes - 1; + } + } else { + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = _taxesMax; + } + + int const _totalFood = DAT_GameState::instance.playerDataArray[playerID].totalFood; + if (_totalFood <= 10) { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 0; + } else { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 2; + int const _rationsMod = (int)this->aics[_aiType].doubleRationsFoodThreshold; + if (_rationsMod != 0 && _totalFood >= _rationsMod) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 4; + } + } + + if (DAT_GameState::instance.playerDataArray[playerID].taxesSetting < 0) + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = 0; + if (11 < DAT_GameState::instance.playerDataArray[playerID].taxesSetting) + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = 11; + if (DAT_GameState::instance.playerDataArray[playerID].rationsSetting < 0) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 0; + if (4 < DAT_GameState::instance.playerDataArray[playerID].rationsSetting) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 4; + } +} +} diff --git a/src/OpenSHC/AI/AICState/assignRequiredIdleEngineersToNewTribe.cpp b/src/OpenSHC/AI/AICState/assignRequiredIdleEngineersToNewTribe.cpp new file mode 100644 index 00000000..d1f3f488 --- /dev/null +++ b/src/OpenSHC/AI/AICState/assignRequiredIdleEngineersToNewTribe.cpp @@ -0,0 +1,48 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Map/Units/States/UnitState.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC5A0 + int AICState::assignRequiredIdleEngineersToNewTribe(int playerID, int count) + { + Map::Units::States::UnitStateShort UVar1; + int tribeID + = MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + uint unitID = 1; + if (1 < (int)DAT_UnitsState::instance.maxUnitCount) { + Map::Units::Unit* psVar2 = &DAT_UnitsState::instance.units[1]; + do { + if (count < 1) { + return tribeID; + } + if (((((psVar2->logicalState == Map::Units::ULS_NORMAL) && (psVar2->owner == playerID)) + && (psVar2->dying == 0)) + && ((psVar2->unitType == Map::Units::UT_E_ENGINEER && (psVar2->aiUnitBehaviourType != 0x16)))) + && ((psVar2->aiUnitBehaviourType != 10 && (psVar2->engineerRelatedUnk == 0))) + && (((UVar1 = (psVar2->state).generic, UVar1 == 0x69 || (UVar1 == 2)) + || (UVar1 == Map::Units::States::US_IDLEUnk)))) { + if (psVar2->tribeID != 0) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::removeUnitFromTribe, DAT_TribesState::ptr)( + unitID, (int)psVar2->tribeID); + } + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + unitID, tribeID); + count = count + -1; + } + unitID = unitID + 1; + psVar2 = psVar2 + 1; + } while ((int)unitID < (int)DAT_UnitsState::instance.maxUnitCount); + } + return tribeID; + } +} +} diff --git a/src/OpenSHC/AI/AICState/assignUnitToATribe.cpp b/src/OpenSHC/AI/AICState/assignUnitToATribe.cpp new file mode 100644 index 00000000..f3cfc892 --- /dev/null +++ b/src/OpenSHC/AI/AICState/assignUnitToATribe.cpp @@ -0,0 +1,50 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Global.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2660 + void AICState::assignUnitToATribe(int unitID) + { + short* _unitTypeIndex; + int _isOfType; + int _tribeID; + int _playerID = (int)DAT_UnitsState::instance.units[unitID].owner; + if (DAT_GameState::instance.playerDataArray[_playerID].aiType != AITA_NULL) { + int iVar1; + if ((DAT_GameState::instance.playerDataArray[_playerID].aivCurrentPauseIndex == 1) + && (0 < DAT_GameState::instance.playerDataArray[_playerID].aivPauses[1])) { + iVar1 = 1; + } else { + iVar1 = 0; + } + _unitTypeIndex = (short*)MACRO_CALL(Global_Func::GetUnitTypeIndexForUnitID)(unitID, iVar1); + int _tribeSize = DAT_GameState::instance.playerDataArray[_playerID] + .aivUnitLocationSlotLocationCount[(int)_unitTypeIndex]; + DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = 1; + if (_tribeSize < 1) { + _isOfType = MACRO_CALL_MEMBER(AICState_Func::unitIDIsRangedOrArmored, this)(unitID); + if (_isOfType != 0) { + _tribeID = MACRO_CALL_MEMBER(AICState_Func::createTribeForUnitType, this)(_playerID, 0); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + unitID, _tribeID); + return; + } + _tribeID = MACRO_CALL_MEMBER(AICState_Func::createTribeForUnitType, this)(_playerID, 1); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, _tribeID); + return; + } + _tribeID = MACRO_CALL_MEMBER(AICState_Func::smallestTribeOfUnitType, this)( + _playerID, (OpenSHC::Map::Units::UnitType)((int)_unitTypeIndex)); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, _tribeID); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/buyGoods.cpp b/src/OpenSHC/AI/AICState/buyGoods.cpp new file mode 100644 index 00000000..40422864 --- /dev/null +++ b/src/OpenSHC/AI/AICState/buyGoods.cpp @@ -0,0 +1,30 @@ +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC000 + BOOLEnum AICState::buyGoods(int playerID, ResourceType resourceType, int amount) + { + int _cost = MACRO_CALL_MEMBER(Game::GameStateStructures_Func::getBuyPrice, DAT_GameState::ptr)( + playerID, resourceType, amount); + BOOLEnum _gain = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::processResourceGain, + DAT_BuildingsState::ptr)(playerID, resourceType, amount); + if (_gain != FALSE) { + int* piVar1 = DAT_GameState::instance.playerDataArray[playerID].currentResources + 0xf; + *piVar1 = *piVar1 - _cost; + piVar1 = &DAT_GameState::instance.playerDataArray[playerID].marketGold; + *piVar1 = *piVar1 - _cost; + MACRO_CALL_MEMBER(Game::GameStateStructures_Func::displayPlayerTradeVisualEffect, DAT_GameState::ptr)( + playerID, 0, amount, resourceType); + return TRUE; + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/buyRequiredGoods.cpp b/src/OpenSHC/AI/AICState/buyRequiredGoods.cpp new file mode 100644 index 00000000..900f0e7a --- /dev/null +++ b/src/OpenSHC/AI/AICState/buyRequiredGoods.cpp @@ -0,0 +1,103 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Game/Resources/ResourceType.hpp" +#include "OpenSHC/Game/Resources/ResourceTypeInt.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::Game::Resources::ResourceType; + using OpenSHC::Game::Resources::ResourceTypeInt; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D39B0 + void AICState ::buyRequiredGoods(int param_1) + + { + + AITypeInt AVar1; + + int amount; + + int playerID; + + int iVar2; + + BOOLEnum BVar3; + + ResourceTypeInt resourceType; + + playerID = param_1; + + AVar1 = DAT_GameState::instance.playerDataArray[param_1].aiType; + + if (AVar1 == OpenSHC::AI::AIT_NULL) { + + return; + } + + param_1 = 0; + + do { + + resourceType = DAT_SkirmishDefinedData::instance.ResourceAcquisitionPreferenceOrder[param_1]; + + if ((DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker <= 0) || (0xb < param_1)) { + + if ((DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker != 0) + && (DAT_GameState::instance.playerDataArray[playerID].currentResources[2] < 20)) { + + resourceType = OpenSHC::Game::Resources::RT_WOOD; + } + + amount = DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[resourceType]; + + if ((amount != 0) + && ((((param_1 <= 3 + && (DAT_GameState::instance.playerDataArray[playerID].popularity + < *(int*)((int)this + (AVar1 + ~OpenSHC::AI::AIT_NULL) * 0x2a4 + 0x18))) + || (0 < DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker)) + || (DAT_GameState::instance.playerDataArray[playerID].canStartSpending != 0)))) { + + iVar2 = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::getBuyPrice, DAT_GameState::ptr)( + playerID, (int)((int)(resourceType)), amount); + + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf] < iVar2) { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::requestGoods, this)( + playerID, (ResourceType)((int)(resourceType)), amount * 2); + + } + + else { + + BVar3 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::buyGoods, this)( + playerID, (ResourceType)((int)(resourceType)), amount); + + if (BVar3 != FALSE) { + + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[resourceType] = 0; + + return; + } + } + } + } + + param_1 = param_1 + 1; + + } while (param_1 < 25); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/canNavigateUnitsFromTileToTargetTile.cpp b/src/OpenSHC/AI/AICState/canNavigateUnitsFromTileToTargetTile.cpp new file mode 100644 index 00000000..b68d9605 --- /dev/null +++ b/src/OpenSHC/AI/AICState/canNavigateUnitsFromTileToTargetTile.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD250 + BOOLEnum AICState::canNavigateUnitsFromTileToTargetTile(int tribeID, int tile) + { + int iVar1 = MACRO_CALL_MEMBER( + Map::Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(DAT_TribesState::instance.tribes[tribeID].owner, + (int)(short)DAT_TileMapState::instance.PathConnectionLayer + [DAT_UnitsState::instance.units[DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID].tile], + (int)(short)DAT_TileMapState::instance.PathConnectionLayer[tile], 0); + return (uint)(iVar1 != 0); + } +} +} diff --git a/src/OpenSHC/AI/AICState/chooseRandomBuildingIDFromPlayersArrayOfBuildings.cpp b/src/OpenSHC/AI/AICState/chooseRandomBuildingIDFromPlayersArrayOfBuildings.cpp new file mode 100644 index 00000000..73e758d0 --- /dev/null +++ b/src/OpenSHC/AI/AICState/chooseRandomBuildingIDFromPlayersArrayOfBuildings.cpp @@ -0,0 +1,81 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingTypeInt.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Buildings::BuildingTypeInt; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDB20 + int AICState ::chooseRandomBuildingIDFromPlayersArrayOfBuildings(int playerID) + + { + + int _count = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker; + + int _chosenBuilding = 0; + + int _chosenBuildingTypeIndex = 46; + + if (0 < _count) { + + int _rng = (int)(unsigned char)SEC_RNG::instance.currentNumber2 & 7; + + short* _ptr = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildings; + + do { + + int _buildingID = (int)*_ptr; + + if (_buildingID != 0) { + + int _selectionIndex = 0; + + if (0 < _chosenBuildingTypeIndex) { + + BuildingTypeInt _buildingType + = (BuildingTypeInt)(short)DAT_BuildingsState::instance.buildings[_buildingID].buildingType; + + do { + + BuildingTypeInt _cmpType; + + if (_rng < 2) { + _cmpType + = DAT_SkirmishDefinedData::instance.BuildingTargetPrioritySet3[_selectionIndex]; + } else if (_rng < 4) { + _cmpType + = DAT_SkirmishDefinedData::instance.BuildingTargetPrioritySet2[_selectionIndex]; + } else { + _cmpType + = DAT_SkirmishDefinedData::instance.BuildingTargetPrioritySet1[_selectionIndex]; + } + + if (_buildingType == _cmpType) { + _chosenBuilding = _buildingID; + _chosenBuildingTypeIndex = _selectionIndex; + } + + _selectionIndex = _selectionIndex + 1; + + } while (_selectionIndex < _chosenBuildingTypeIndex); + } + } + + _ptr = _ptr + 1; + + _count = _count - 1; + + } while (_count != 0); + } + + return _chosenBuilding; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/clearOutdatedAITribes.cpp b/src/OpenSHC/AI/AICState/clearOutdatedAITribes.cpp new file mode 100644 index 00000000..913be600 --- /dev/null +++ b/src/OpenSHC/AI/AICState/clearOutdatedAITribes.cpp @@ -0,0 +1,65 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF870 + void AICState ::clearOutdatedAITribes(int playerID) + + { + + if (DAT_GameState::instance.playerDataArray[playerID].aiType != OpenSHC::AI::AIT_NULL) { + + int _index = 0; + + do { + + if (_index != 8) { + + int _tribeCount = DAT_SkirmishDefinedData::instance.MaxBreachTribes[_index].tribeCount; + + int _tribeType = DAT_SkirmishDefinedData::instance.MaxBreachTribes[_index].tribeType; + + int _offset = 0; + + if (0 < _tribeCount) { + + short* _ptrTribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + _tribeType; + + do { + int _tribeIDVal = (int)*_ptrTribeID; + if ((_tribeIDVal != 0) + && (DAT_TribesState::instance.tribes[_tribeIDVal].uid + != DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[_offset + _tribeType])) { + + *_ptrTribeID = 0; + + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_offset + _tribeType] = 0; + } + + _offset = _offset + 1; + + _ptrTribeID = _ptrTribeID + 1; + + } while (_offset < _tribeCount); + } + } + + _index = _index + 1; + + } while (_index < 0xb); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/clearPlayerDataInformationChunk.cpp b/src/OpenSHC/AI/AICState/clearPlayerDataInformationChunk.cpp new file mode 100644 index 00000000..5d3637d4 --- /dev/null +++ b/src/OpenSHC/AI/AICState/clearPlayerDataInformationChunk.cpp @@ -0,0 +1,36 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/OS.func.hpp" +#include "OpenSHC/Synchrony/GameSynchronyState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Synchrony::GameSynchronyState; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CAC70 + void AICState ::clearPlayerDataInformationChunk() + + { + int* _Dst = &DAT_GameState::instance.playerDataArray[0].aiFarmIndex; + int* _pAI = DAT_GameSynchronyState::instance.currentAIArray; + int* _pFullID = DAT_GameSynchronyState::instance.currentPlayerFullIDArray; + + do { + if (*_pFullID != -1 || *_pAI == 0) { + MACRO_CALL(OpenSHC::OS_Func::_memset)(_Dst, 0, (size_t)((int)(3748))); + } + + _pAI = _pAI + 1; + _pFullID = _pFullID + 1; + _Dst = _Dst + 3709; + + } while ((int)_pAI < 0x191dea0); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/clearRallyFlagOnAllAIAttackTribes.cpp b/src/OpenSHC/AI/AICState/clearRallyFlagOnAllAIAttackTribes.cpp new file mode 100644 index 00000000..e0180814 --- /dev/null +++ b/src/OpenSHC/AI/AICState/clearRallyFlagOnAllAIAttackTribes.cpp @@ -0,0 +1,85 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE790 + void AICState ::clearRallyFlagOnAllAIAttackTribes(int param_1) + + { + + int _tribeID; + + int _counter; + + short* _ptrTribeID; + + int* _ptrTribeCount; + + int _tribeCount; + + int _tribeType; + + if (DAT_GameState::instance.playerDataArray[param_1].aiType != OpenSHC::AI::AIT_NULL) { + + do { + + /* + first iteration: 0 */ + + _counter = 0; + + _tribeCount = DAT_SkirmishDefinedData::instance.MaxBreachTribes[_counter].tribeCount; + + _tribeType = DAT_SkirmishDefinedData::instance.MaxBreachTribes[_counter].tribeType; + + /* + first iteration: 186 */ + + // _tribeType = (*(int (*)[2])(_ptrTribeCount + -1))[0]; + + if (0 < _tribeCount) { + + _ptrTribeID = DAT_GameState::instance.playerDataArray[param_1].aiTribeIDs + _tribeType; + + do { + + _tribeID = (int)*_ptrTribeID; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[param_1] + .aiTribeUIDs[_counter + _tribeType])) { + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::unsetRallyRelatedFlagOnUnits, + DAT_TribesState::ptr)(_tribeID); + } + + _counter = _counter + 1; + + _ptrTribeID = _ptrTribeID + 1; + + } while (_counter < _tribeCount); + } + + _ptrTribeCount = _ptrTribeCount + 2; + + } while ((int)_ptrTribeCount < 0xb42a2c); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/commandFourTribesToMove.cpp b/src/OpenSHC/AI/AICState/commandFourTribesToMove.cpp new file mode 100644 index 00000000..d701a1a3 --- /dev/null +++ b/src/OpenSHC/AI/AICState/commandFourTribesToMove.cpp @@ -0,0 +1,68 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF7C0 + void AICState ::commandFourTribesToMove(int playerID) + + { + + if (DAT_GameState::instance.playerDataArray[playerID].aiType != OpenSHC::AI::AIT_NULL) { + + int _counter2 = 0; + + int _counter = 0; + + short* _ptrTribeIDArray = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + 0xba; + + do { + + int _tribeID = (int)*_ptrTribeIDArray; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_counter + 0xba])) { + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_STAND_GROUND; + + /* + fixme: array too small? */ + + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)(_tribeID, + (uint)((int)(DAT_GameState::instance.mapAndTime + .aiTribeMoveDestinationXYPairArray1[playerID * 5][_counter2] + .xOffset)), + (uint)((int)(DAT_GameState::instance.mapAndTime + .aiTribeMoveDestinationXYPairArray1[playerID * 5][_counter2] + .yOffset)), + 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + + _counter2 = _counter2 + 1; + } + + _counter = _counter + 1; + + _ptrTribeIDArray = _ptrTribeIDArray + 1; + + } while (_counter < 3); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/computeAttackPossibilities.cpp b/src/OpenSHC/AI/AICState/computeAttackPossibilities.cpp new file mode 100644 index 00000000..bb31d983 --- /dev/null +++ b/src/OpenSHC/AI/AICState/computeAttackPossibilities.cpp @@ -0,0 +1,60 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Units/TroopValueState.func.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Game::Player::PlayerData; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2EC0 + void AICState ::computeAttackPossibilities(int playerID) + + { + + PlayerData* piVar1; + + piVar1 = &DAT_GameState::instance.playerDataArray[playerID]; + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 80400, '\0', (void*)((int)(DAT_TileMapState::instance.AIInfoLayer))); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAttackPrimeInfo, this)(playerID, piVar1->attackedPlayerID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::recomputeTargetedBuildingTilesArray, + DAT_TroopValueState::ptr)(playerID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::recomputeAttackInfo, DAT_TroopValueState::ptr)( + playerID, piVar1->attackedPlayerID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::computeSiegeSpotScores, DAT_TroopValueState::ptr)( + piVar1->attackedPlayerID, playerID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::setScale3, DAT_TroopValueState::ptr)(1, playerID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::setTown2, DAT_TroopValueState::ptr)(1, playerID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::setGate2, DAT_TroopValueState::ptr)(1, playerID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::setWide3, DAT_TroopValueState::ptr)(1, playerID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func::setMoat3, DAT_TroopValueState::ptr)(1, playerID); + + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::calculateAttackVectorsToCampFireOfPlayer, + DAT_GameState::ptr)(piVar1->attackedPlayerID); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/computeEnemyKeepApproachTile.cpp b/src/OpenSHC/AI/AICState/computeEnemyKeepApproachTile.cpp new file mode 100644 index 00000000..7f28e426 --- /dev/null +++ b/src/OpenSHC/AI/AICState/computeEnemyKeepApproachTile.cpp @@ -0,0 +1,173 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/AI/TileDistancePair.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::TileDistancePair; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE200 + void AICState ::computeEnemyKeepApproachTile(int playerID) + + { + + int _shortestDistanceTile; + + int _shortestDistanceX; + + TileDistancePair* _tilePointer; + + int iVar1; + + int* _distancePointer; + + int _shortestDistance; + + int local_c; + + int _keepX; + + int _keepY; + + int _tile; + + short _y; + + _keepX = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry; + + _keepY = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry; + + this->aiBorderTilesIndex = 0; + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 8000, '\0', (void*)((int)(this->aiBorderTiles))); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recomputeAIPathCostToAttackedKeep, this)(playerID, 1); + + local_c = 0; + + if (0 < this->aiBorderTilesIndex) { + + _distancePointer = &this->aiBorderTiles[0].distance; + + do { + + _tile = ((TileDistancePair*)(_distancePointer + -1))->tile; + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)(_keepX, _keepY, + _tile + - DAT_ViewportRenderState::instance + .translationMatrix[DAT_ViewportRenderState::instance + .tileTranslationMatrix_YComponent[_tile]] + .addXgetTile, + (int)((int)(DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_tile]))); + + /* + avoiding unit death areas */ + + *_distancePointer = (uint)DAT_TileMapState::instance.unitDeathHeatMap[_tile] + + DAT_DirectionAlgorithmState::instance.distanceHigh; + + local_c = local_c + 1; + + _distancePointer = _distancePointer + 2; + + } while (local_c < this->aiBorderTilesIndex); + } + + _shortestDistanceTile = 0; + + _shortestDistance = 100000; + + if (0 < this->aiBorderTilesIndex) { + + _tilePointer = this->aiBorderTiles; + + iVar1 = this->aiBorderTilesIndex; + + do { + + if (_tilePointer->distance < _shortestDistance) { + + _shortestDistanceTile = _tilePointer->tile; + + _shortestDistance = _tilePointer->distance; + } + + _tilePointer = _tilePointer + 1; + + iVar1 = iVar1 + -1; + + } while (iVar1 != 0); + } + + /* + sets the first attack distance to the enemy keep */ + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceTile = 0; + + if (0 < _shortestDistanceTile) { + + _y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_shortestDistanceTile]; + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY = (int)_y; + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceTile = _shortestDistanceTile; + + _shortestDistanceX + = _shortestDistanceTile - DAT_ViewportRenderState::instance.translationMatrix[_y].addXgetTile; + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX = _shortestDistanceX; + + DAT_GameState::instance.mapAndTime.signpostEntryData[playerID + -1][0] = _shortestDistanceX; + + DAT_GameState::instance.mapAndTime.signpostEntryData[playerID + -1][1] + = DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY; + + if ((short)DAT_TileMapState::instance.OrganismLayer + [DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY] + .addXgetTile + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX] + != 0) { + + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeTree, DAT_LandscapeState::ptr)( + (int)(short)DAT_TileMapState::instance.OrganismLayer + [DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY] + .addXgetTile + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX]); + } + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findLinkageBasedPathOrWalkRadius, + DAT_PathFindingState::ptr)(DAT_GameState::instance.mapAndTime.signpostEntryData[playerID + -1][0], + (uint)((int)(DAT_GameState::instance.mapAndTime.signpostEntryData[playerID + -1][1])), -1, -1, 2000, + FALSE); + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::storeDestinationOptionsUnk, + DAT_PathFindingState::ptr)(playerID + -1); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/computeNervousness.cpp b/src/OpenSHC/AI/AICState/computeNervousness.cpp new file mode 100644 index 00000000..ca79afa7 --- /dev/null +++ b/src/OpenSHC/AI/AICState/computeNervousness.cpp @@ -0,0 +1,54 @@ +#include "OpenSHC/AI/AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3780 + void AICState ::computeNervousness(int playerID) + + { + int _one = 1; + + int iVar1 = DAT_TroopValueState::instance.attackInfo.playerTotalTroopValueOfTroopsNearKeep[playerID]; + + int _totalTroopValueOfEnemy = DAT_GameState::instance.playerDataArray[playerID].totalEnemyTroopValue; + + int _someCutoff; + + if (_totalTroopValueOfEnemy < 50) { + _someCutoff = _one - 0x15; + } else if (_totalTroopValueOfEnemy < 100) { + _someCutoff = 0; + } else if (_totalTroopValueOfEnemy < 200) { + _someCutoff = 20; + } else if (_totalTroopValueOfEnemy < 400) { + _someCutoff = 100; + } else if (_totalTroopValueOfEnemy < 600) { + _someCutoff = 200; + } else { + _someCutoff = ((_totalTroopValueOfEnemy >= 800) - _one & 0xffffff38) + 500; + } + + if (iVar1 > _someCutoff + _totalTroopValueOfEnemy) { + DAT_GameState::instance.playerDataArray[playerID].isNotNervousByEnemyTroopValue = 0; + return; + } + + if (DAT_GameState::instance.playerDataArray[playerID].isNotNervousByEnemyTroopValue == 0) { + if (DAT_GameState::instance.playerDataArray[playerID] + .totalEnemyTroopValueByPlayerID[DAT_GameSynchronyState::instance.currentPlayerSlotID] + > iVar1) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playNervous1BikFromPlayer, this)(playerID); + } + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playRequestHelpBikFromPlayer, this)(playerID); + } + + DAT_GameState::instance.playerDataArray[playerID].isNotNervousByEnemyTroopValue = _one; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/countSiegeEngineUnits.cpp b/src/OpenSHC/AI/AICState/countSiegeEngineUnits.cpp new file mode 100644 index 00000000..e58e3615 --- /dev/null +++ b/src/OpenSHC/AI/AICState/countSiegeEngineUnits.cpp @@ -0,0 +1,63 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/Unit.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/Map/Units/UnitTypeShort.hpp" + +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Units::Unit; + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::Map::Units::UnitTypeShort; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CFBC0 + int AICState::countSiegeEngineUnits(int playerID) + { + int _count = 0; + Unit* _units = &DAT_UnitsState::instance.units[0]; + + for (int i = 1; i < 2500; i += 1) { + // Unit[1] + if (_units[i].logicalState != OpenSHC::Map::Units::ULS_INVISIBLE && _units[i].owner == playerID + && _units[i].unknownSiegeTentRelated02 == 3 && _units[i].aiUnitBehaviourType != 0x15) { + UnitTypeShort t = _units[i].unitType; + if (t == OpenSHC::Map::Units::UT_S_CATAPULT || t == OpenSHC::Map::Units::UT_S_TREBUCHET + || t == OpenSHC::Map::Units::UT_S_TOWER || t == OpenSHC::Map::Units::UT_S_BATTERINGRAM + || t == OpenSHC::Map::Units::UT_S_FBALLISTA) { + _count = _count + 1; + } + } + + // Compiler unrolled the loop because 2499 is divisible by 3? + // // Unit[2] + // if (_units[i + 1].logicalState != OpenSHC::Map::Units::ULS_INVISIBLE && _units[i + 1].owner == playerID + // && _units[i + 1].unknownSiegeTentRelated02 == 3 && _units[i + 1].aiUnitBehaviourType != 0x15) { + // UnitTypeShort t = _units[i + 1].unitType; + // if (t == OpenSHC::Map::Units::UT_S_CATAPULT || t == OpenSHC::Map::Units::UT_S_TREBUCHET + // || t == OpenSHC::Map::Units::UT_S_TOWER || t == OpenSHC::Map::Units::UT_S_BATTERINGRAM + // || t == OpenSHC::Map::Units::UT_S_FBALLISTA) { + // _count = _count + 1; + // } + // } + + // // Unit[3] + // if (_units[i + 2].logicalState != OpenSHC::Map::Units::ULS_INVISIBLE && _units[i + 2].owner == playerID + // && _units[i + 2].unknownSiegeTentRelated02 == 3 && _units[i + 2].aiUnitBehaviourType != 0x15) { + // UnitTypeShort t = _units[i + 2].unitType; + // if (t == OpenSHC::Map::Units::UT_S_CATAPULT || t == OpenSHC::Map::Units::UT_S_TREBUCHET + // || t == OpenSHC::Map::Units::UT_S_TOWER || t == OpenSHC::Map::Units::UT_S_BATTERINGRAM + // || t == OpenSHC::Map::Units::UT_S_FBALLISTA) { + // _count = _count + 1; + // } + // } + }; + + return _count; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/createTribeForUnitType.cpp b/src/OpenSHC/AI/AICState/createTribeForUnitType.cpp new file mode 100644 index 00000000..f161e8f1 --- /dev/null +++ b/src/OpenSHC/AI/AICState/createTribeForUnitType.cpp @@ -0,0 +1,27 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC960 + int AICState::createTribeForUnitType(int playerID, int unitSelectionTypeIndex) + { + short* psVar1 = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + unitSelectionTypeIndex; + int _tribeID = (int)*psVar1; + if ((_tribeID == 0) + || (DAT_TribesState::instance.tribes[_tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[unitSelectionTypeIndex])) { + _tribeID + = MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + int _tribeUID = DAT_TribesState::instance.tribes[_tribeID].uid; + *psVar1 = (short)_tribeID; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[unitSelectionTypeIndex] = _tribeUID; + } + return _tribeID; + } +} +} diff --git a/src/OpenSHC/AI/AICState/decideOnTribeAttackLocation.cpp b/src/OpenSHC/AI/AICState/decideOnTribeAttackLocation.cpp new file mode 100644 index 00000000..5d078d37 --- /dev/null +++ b/src/OpenSHC/AI/AICState/decideOnTribeAttackLocation.cpp @@ -0,0 +1,175 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Global.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Units::UnitInstructionType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D1A00 + void AICState ::decideOnTribeAttackLocation(uint tribeID_OR_moatWasHit) + { + uint _tribeID = tribeID_OR_moatWasHit; + + BOOLEnum _canHaveActivity + = MACRO_CALL(OpenSHC::Global_Func::CheckTribeActivityPercentages)(tribeID_OR_moatWasHit, FALSE, TRUE); + if (_canHaveActivity != FALSE) { + DAT_TribesState::instance.tribes[_tribeID].counter = 0; + return; + } + + int _targetPlayer = (int)DAT_TribesState::instance.tribes[_tribeID].targetPlayer_outpost; + + if (DAT_TribesState::instance.tribes[_tribeID].randomNumber == 0) { + DAT_TribesState::instance.tribes[_tribeID].randomNumber = (int)SEC_RNG::instance.currentNumber2; + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + } + + int _rng = DAT_TribesState::instance.tribes[_tribeID].randomNumber; + short* psVar1 = &DAT_TribesState::instance.tribes[_tribeID].counter; + *psVar1 = *psVar1 + 1; + + if ((int)DAT_TribesState::instance.tribes[_tribeID].counter == _rng % 6 + 7) { + DAT_TribesState::instance.tribes[_tribeID].counter = 0; + tribeID_OR_moatWasHit = tribeID_OR_moatWasHit & 0xffffff00; + int _x = 0; + int _y = 0; + int _unitID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::getUnitIDForIndexInTribe, DAT_TribesState::ptr)(_tribeID, 0); + if ((_unitID != 0) + && MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::pathFindingToAttackCastleIncludingMoat, + DAT_PathFindingState::ptr)(DAT_TribesState::instance.tribes[_tribeID].owner, _targetPlayer, + (uint)((int)((int)DAT_UnitsState::instance.units[_unitID].x)), + (uint)((int)((int)DAT_UnitsState::instance.units[_unitID].y)), (byte*)&tribeID_OR_moatWasHit, &_x, + &_y)) { + if ((char)tribeID_OR_moatWasHit != '\0') { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::relayTribeInstruction, DAT_UnitsState::ptr)( + _tribeID, ((UnitInstructionType)0x23), + DAT_ViewportRenderState::instance.translationMatrix[_y].addXgetTile + _x, 0, 0); + return; + } + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::relayTribeInstruction, DAT_UnitsState::ptr)( + _tribeID, OpenSHC::Map::Units::UIT_FILL_MOAT, _x, _y, 1000); + return; + } + } + + short sVar2 = DAT_TribesState::instance.tribes[_tribeID].tribeSubtype1; + + if ((sVar2 != 0xb6) && (sVar2 != 0xb9)) { + BOOLEnum BVar4 = MACRO_CALL(OpenSHC::Global_Func::CheckTribeActivityPercentages)(_tribeID, FALSE, FALSE); + if (BVar4 != FALSE) + return; + + int _targetBuilding = (int)DAT_TribesState::instance.tribes[_tribeID].targetBuildingID; + if ((((_targetBuilding != 0) + && (DAT_BuildingsState::instance.buildings[_targetBuilding].uid + == DAT_TribesState::instance.tribes[_tribeID].targetBuildingUID)) + && (DAT_BuildingsState::instance.buildings[_targetBuilding].fireDuration == 0)) + && (BVar4 = MACRO_CALL(OpenSHC::Global_Func::CheckTribeActivityPercentages)(_tribeID, FALSE, TRUE), + BVar4 != FALSE)) { + if (DAT_GameState::instance.mapAndTime.yearChanged == 0) + return; + DAT_TribesState::instance.tribes[_tribeID].targetBuildingID = 0; + return; + } + + int _buildingID + = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::selectARandomBuildingOwnedByPlayer, + DAT_GameState::ptr)(_targetPlayer); + if (_buildingID != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::relayTribeInstruction, DAT_UnitsState::ptr)( + _tribeID, OpenSHC::Map::Units::UIT_ATTACK_BUILDING, _buildingID, + DAT_BuildingsState::instance.buildings[_buildingID].uid, 0); + int _buildingUID = DAT_BuildingsState::instance.buildings[_buildingID].uid; + DAT_TribesState::instance.tribes[_tribeID].targetBuildingID = (short)_buildingID; + DAT_TribesState::instance.tribes[_tribeID].targetBuildingUID = _buildingUID; + return; + } + + int _destinationX = DAT_GameState::instance.playerDataArray[_targetPlayer].campground.xEntry; + DAT_TribesState::instance.tribes[_tribeID].targetBuildingID = 0; + int _destinationY = DAT_GameState::instance.playerDataArray[_targetPlayer].campground.yEntry; + BVar4 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::canNavigateUnitsFromTileToTargetTile, this)(_tribeID, + DAT_ViewportRenderState::instance.translationMatrix[_destinationY].addXgetTile + _destinationX); + if (BVar4 == FALSE) + return; + // was LAB_004d1c59 + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::commandUnitsToLocation, DAT_TribesState::ptr)( + _tribeID, (uint)((int)(_destinationX)), (uint)((int)(_destinationY)), 1); + return; + } + + int _buildingID_2 + = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::selectARandomBuildingOwnedByPlayer, + DAT_GameState::ptr)(_targetPlayer); + + int _destinationTile; + int _destinationX2; + int _destinationY2; + + if (_buildingID_2 == 0) { + int iVar5 = DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[_targetPlayer].campground.yEntry] + .addXgetTile; + _destinationTile = iVar5 + DAT_GameState::instance.playerDataArray[_targetPlayer].campground.xEntry; + } else { + int iVar5 = (int)DAT_TribesState::instance.tribes[_tribeID].selectionTargetUnitID; + if (iVar5 == 0) { + _destinationTile = (int)DAT_BuildingsState::instance.buildings[_buildingID_2].buildingEntryX + + DAT_ViewportRenderState::instance + .translationMatrix[DAT_BuildingsState::instance.buildings[_buildingID_2].buildingEntryY] + .addXgetTile; + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::pathfindingForAttacksUnk, + DAT_PathFindingState::ptr)(_tribeID, _buildingID_2, 1, + (dword)((int)((int)(short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance.units[iVar5].tile])), + _targetPlayer); + _destinationTile = DAT_PathFindingState::instance.searchQueue.destinationsArray[0].tile1; + if (DAT_PathFindingState::instance.searchQueue.destinationsArray[0].tile2OrAHelper == 0) { + // was LAB_004d1d11 + int iVar5b = DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[_targetPlayer] + .campground.yEntry] + .addXgetTile; + _destinationTile + = iVar5b + DAT_GameState::instance.playerDataArray[_targetPlayer].campground.xEntry; + } + } + } + + if (_destinationTile != 0) { + _destinationY2 = (int)DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_destinationTile]; + _destinationX2 + = _destinationTile - DAT_ViewportRenderState::instance.translationMatrix[_destinationY2].addXgetTile; + BOOLEnum BVar4 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::canNavigateUnitsFromTileToTargetTile, this)( + _tribeID, _destinationTile); + if (BVar4 != FALSE) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::commandUnitsToLocation, DAT_TribesState::ptr)( + _tribeID, (uint)((int)(_destinationX2)), (uint)((int)(_destinationY2)), 1); + return; + } + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)(_tribeID, _targetPlayer); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/destroyAnyBuildingInCategory.cpp b/src/OpenSHC/AI/AICState/destroyAnyBuildingInCategory.cpp new file mode 100644 index 00000000..8f1d56ea --- /dev/null +++ b/src/OpenSHC/AI/AICState/destroyAnyBuildingInCategory.cpp @@ -0,0 +1,75 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0280 + void AICState::destroyAnyBuildingInCategory(int playerID, AINervousDestroyBuildingCategory buildingCategory) + { + int _buildingID = 1; + if (1 < DAT_BuildingsState::instance.maxBuildingsCount) { + Map::Buildings::Building* pBVar2 = &DAT_BuildingsState::instance.buildings[1]; + do { + if (((pBVar2->logicalState != 0) && (pBVar2->logicalState != Map::Buildings::BLS_REMOVE)) + && (pBVar2->owner == playerID)) { + + bool _matches = false; + Map::Buildings::BuildingTypeShort BVar1 = pBVar2->buildingType; + + if (buildingCategory == AINDBC_FEAR_FACTOR_BUILDINGS) { + _matches = (BVar1 == Map::Buildings::BT_CESSPIT || BVar1 == Map::Buildings::BT_BURNINGSTAKE + || BVar1 == Map::Buildings::BT_GIBBET || BVar1 == Map::Buildings::BT_DUNGEON + || BVar1 == Map::Buildings::BT_STRETCHINGRACK || BVar1 == Map::Buildings::BT_RACKFLOGGING + || BVar1 == Map::Buildings::BT_CHOPPINGBLOCK || BVar1 == Map::Buildings::BT_DUNKINGSTOOL + || BVar1 == Map::Buildings::BT_DOGCAGE || BVar1 == Map::Buildings::BT_STATUE + || BVar1 == Map::Buildings::BT_SHRINE || BVar1 == Map::Buildings::BT_DANCINGBEAR + || BVar1 == Map::Buildings::BT_POND || BVar1 == Map::Buildings::BT_GALLOWS + || BVar1 == Map::Buildings::BT_STOCKS || BVar1 == Map::Buildings::BT_WITCHHOIST + || BVar1 == Map::Buildings::BT_MAYPOLE || BVar1 == Map::Buildings::BT_GARDEN); + } else if (buildingCategory == AINDBC_WORKSHOPS_AND_RELIGIOUS_BUILDINGS) { + _matches = (BVar1 == Map::Buildings::BT_FLETCHER || BVar1 == Map::Buildings::BT_BLACKSMITH + || BVar1 == Map::Buildings::BT_POLETURNER || BVar1 == Map::Buildings::BT_ARMOURER + || BVar1 == Map::Buildings::BT_TANNER || BVar1 == Map::Buildings::BT_INN + || BVar1 == Map::Buildings::BT_APOTHECARY || BVar1 == Map::Buildings::BT_CHAPEL + || BVar1 == Map::Buildings::BT_CHURCH || BVar1 == Map::Buildings::BT_CATHEDRAL); + } else if (buildingCategory == AINDBC_GATEHOUSES_TOWERS_GUILDS) { + _matches = (BVar1 == Map::Buildings::BT_GATEHOUSELARGE + || BVar1 == Map::Buildings::BT_GATEHOUSESMALL || BVar1 == Map::Buildings::BT_DRAWBRIDGE + || BVar1 == Map::Buildings::BT_TOWER1 || BVar1 == Map::Buildings::BT_TOWER2 + || BVar1 == Map::Buildings::BT_TOWER3 || BVar1 == Map::Buildings::BT_TOWER4 + || BVar1 == Map::Buildings::BT_TOWER5 || BVar1 == Map::Buildings::BT_ENGINEERSGUILD + || BVar1 == Map::Buildings::BT_TUNNELERSGUILD || BVar1 == Map::Buildings::BT_OILSMELTER); + } else if (buildingCategory == AINDBC_BARRACKS_MERCPOST_STABLES) { + _matches = (BVar1 == Map::Buildings::BT_MERCENARYPOST || BVar1 == Map::Buildings::BT_BARRACKS + || BVar1 == Map::Buildings::BT_STABLES); + } else if (buildingCategory == AINDBC_WATER_BUILDINGS) { + _matches = (BVar1 == Map::Buildings::BT_WELL || BVar1 == Map::Buildings::BT_WATERPOT); + } else if (buildingCategory == AINDBC_RESOURCE_BUILDINGS) { + _matches = (BVar1 == Map::Buildings::BT_OXTETHER || BVar1 == Map::Buildings::BT_IRONMINE + || BVar1 == Map::Buildings::BT_PITCHRIG || BVar1 == Map::Buildings::BT_BAKERY + || BVar1 == Map::Buildings::BT_BREWERY || BVar1 == Map::Buildings::BT_QUARRY + || BVar1 == Map::Buildings::BT_MILL); + } else if (buildingCategory == AINDBC_FARMS) { + _matches = (BVar1 == Map::Buildings::BT_WHEATFARM || BVar1 == Map::Buildings::BT_HOPFARM + || BVar1 == Map::Buildings::BT_APPLEFARM || BVar1 == Map::Buildings::BT_DAIRYFARM); + } + + if (_matches) { + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::giveBackResourceForDestroyedBuilding, + DAT_BuildingsState::ptr)(_buildingID, playerID, 0x32); + MACRO_CALL_MEMBER( + Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(_buildingID); + } + } + _buildingID = _buildingID + 1; + pBVar2 = pBVar2 + 1; + } while (_buildingID < DAT_BuildingsState::instance.maxBuildingsCount); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/destroyBuildingIfNoWorker.cpp b/src/OpenSHC/AI/AICState/destroyBuildingIfNoWorker.cpp new file mode 100644 index 00000000..3cff7bcb --- /dev/null +++ b/src/OpenSHC/AI/AICState/destroyBuildingIfNoWorker.cpp @@ -0,0 +1,47 @@ +#include "OpenSHC/AI/AIVState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Game/GameMode.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB940 + undefined4 AICState::destroyBuildingIfNoWorker(int buildingID) + { + short _value = DAT_BuildingsState::instance.buildings[buildingID].unknownTickRelatedValue; + int _owner = (int)DAT_BuildingsState::instance.buildings[buildingID].owner; + + if (0 < _value && DAT_GameSynchronyState::instance.currentPlayerFullIDArray[_owner] == -1 + && DAT_GameSynchronyState::instance.currentAIArray[_owner] != 0 + && DAT_GameSynchronyState::instance.DAT_CurrentGameMode != Game::GM_SOLITARY) { + + if (DAT_BuildingsState::instance.buildings[buildingID].workers[0] != 0) { + if (DAT_GameState::instance.mapAndTime.monthTicks == _owner) + DAT_BuildingsState::instance.buildings[buildingID].unknownTickRelatedValue = _value + -1; + return 0; + } + + // workers == 0: check if old enough + if ((short)_value < (short)0x14) + return 0; + + // old enough: destroy + MACRO_CALL_MEMBER(AIVState_Func::resetCountdownInHeatMap, DAT_AIVState::ptr)( + (int)(short)DAT_BuildingsState::instance.buildings[buildingID].x, + (int)(short)DAT_BuildingsState::instance.buildings[buildingID].y); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::giveBackResourceForDestroyedBuilding, + DAT_BuildingsState::ptr)(buildingID, _owner, 0x32); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)( + buildingID); + return 1; + } + return 0; + } +} +} diff --git a/src/OpenSHC/AI/AICState/destroyHouse.cpp b/src/OpenSHC/AI/AICState/destroyHouse.cpp new file mode 100644 index 00000000..108bf6d8 --- /dev/null +++ b/src/OpenSHC/AI/AICState/destroyHouse.cpp @@ -0,0 +1,43 @@ +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB810 + BOOLEnum AICState::destroyHouse(int playerID) + { + int _hovelID; + + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) + return FALSE; + + _hovelID = DAT_GameState::instance.playerDataArray[playerID].currentPopulation; + + if (_hovelID > DAT_GameState::instance.playerDataArray[playerID].populationCap) + return FALSE; + + if (_hovelID <= 8) + return FALSE; + + if (DAT_GameState::instance.playerDataArray[playerID].averagePopulationGrowthUnk <= 0x14) + return FALSE; + + _hovelID = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::findFirstBuildingIDForPlayerAndType, + DAT_BuildingsState::ptr)(playerID, Map::Buildings::BT_HOVEL); + + if (_hovelID == 0) + return FALSE; + + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::giveBackResourceForDestroyedBuilding, + DAT_BuildingsState::ptr)(_hovelID, playerID, 0x32); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(_hovelID); + return TRUE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/determineAIPlayerAttackRequestResponse.cpp b/src/OpenSHC/AI/AICState/determineAIPlayerAttackRequestResponse.cpp new file mode 100644 index 00000000..ecdd9457 --- /dev/null +++ b/src/OpenSHC/AI/AICState/determineAIPlayerAttackRequestResponse.cpp @@ -0,0 +1,55 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Global.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D38C0 + BOOLEnum AICState ::determineAIPlayerAttackRequestResponse( + int playerID, undefined4 param_2, int requestedByPlayerID) + + { + + AITypeInt AVar1; + + AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (AVar1 != OpenSHC::AI::AIT_NULL) { + + if (DAT_GameState::instance.playerDataArray[playerID].isNotNervousByEnemyTroopValue == 0) { + + if (DAT_GameState::instance.playerDataArray[playerID].totalAttackTroops + - DAT_GameState::instance.playerDataArray[playerID].totalAttackingEngineerTroops + < *(int*)((int)this + (AVar1 + ~OpenSHC::AI::AIT_NULL) * 0x2a4 + 0x1fc)) { + + MACRO_CALL(OpenSHC::Global_Func::PlayWillNotAttack2BikFromPlayerToPlayer)( + playerID, requestedByPlayerID); + + return FALSE; + } + + MACRO_CALL(OpenSHC::Global_Func::PlayWillAttackBikFromPlayerToPlayer)(playerID, requestedByPlayerID); + + return TRUE; + } + + MACRO_CALL(OpenSHC::Global_Func::PlayWillNotAttack1BikFromPlayerToPlayer)(playerID, requestedByPlayerID); + } + + return FALSE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/determineAIPlayerHelp.cpp b/src/OpenSHC/AI/AICState/determineAIPlayerHelp.cpp new file mode 100644 index 00000000..41f1ba2e --- /dev/null +++ b/src/OpenSHC/AI/AICState/determineAIPlayerHelp.cpp @@ -0,0 +1,53 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Global.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3850 + BOOLEnum AICState ::determineAIPlayerHelp(int playerID, int requestedByPlayerID) + + { + + AITypeInt AVar1; + + AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (AVar1 != OpenSHC::AI::AIT_NULL) { + + if (DAT_GameState::instance.playerDataArray[playerID].isNotNervousByEnemyTroopValue == 0) { + + if (DAT_GameState::instance.playerDataArray[playerID].totalAttackTroops + < *(int*)((int)this + (AVar1 + ~OpenSHC::AI::AIT_NULL) * 0x2a4 + 0x1fc)) { + + MACRO_CALL(OpenSHC::Global_Func::PlayWillNotHelp2BikFromPlayerToPlayer)( + playerID, requestedByPlayerID); + + return FALSE; + } + + MACRO_CALL(OpenSHC::Global_Func::PlayWillHelpBikFromPlayerToPlayer)(playerID, requestedByPlayerID); + + return TRUE; + } + + MACRO_CALL(OpenSHC::Global_Func::PlayWillNotHelp1BikFromPlayerToPlayer)(playerID, requestedByPlayerID); + } + + return FALSE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/digNearestMoat.cpp b/src/OpenSHC/AI/AICState/digNearestMoat.cpp new file mode 100644 index 00000000..9187b70c --- /dev/null +++ b/src/OpenSHC/AI/AICState/digNearestMoat.cpp @@ -0,0 +1,50 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD160 + void AICState::digNearestMoat(int playerID) + { + int iVar1; + BOOLEnum tile; + int tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[10]; + if ((((tribeID != 0) + && (DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[10])) + && (0 < DAT_TribesState::instance.tribes[tribeID].size)) + && (DAT_TribesState::instance.tribes[tribeID].percentageSomething < 0x51)) { + iVar1 = MACRO_CALL_MEMBER(Map::TileMapState_Func::countUnfinishedMoatTilesForPlayer, DAT_TileMapState::ptr)( + playerID); + if (iVar1 < 1) { + MACRO_CALL_MEMBER(AICState_Func::sendUnitsToKeep, this)(tribeID, playerID); + return; + } + iVar1 = (int)DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID; + tile = MACRO_CALL_MEMBER(Map::TileMapState_Func::findNearestFriendlyMoatTileForDigging, + DAT_TileMapState::ptr)(playerID, iVar1, 1); + if (-1 < (int)tile) { + MACRO_CALL_MEMBER(Map::TileMapState_Func::setXYBasedOnMoatID, DAT_TileMapState::ptr)(tile, 1, + (int)DAT_UnitsState::instance.units[iVar1].x, (int)DAT_UnitsState::instance.units[iVar1].y); + iVar1 = MACRO_CALL_MEMBER(Map::Units::TribesState_Func::updateTribeRallyFlags, DAT_TribesState::ptr)( + tribeID); + if (iVar1 == 0) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::giveTribeAnInstruction, DAT_TribesState::ptr)( + tribeID, Map::Units::UIT_DIG_MOAT, DAT_TileMapState::instance.ALG_MoatXResult, + DAT_TileMapState::instance.ALG_MoatYResult, 0); + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_STAND_GROUND; + } + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/disbandAIPlayerSiegeUnits.cpp b/src/OpenSHC/AI/AICState/disbandAIPlayerSiegeUnits.cpp new file mode 100644 index 00000000..531b03ab --- /dev/null +++ b/src/OpenSHC/AI/AICState/disbandAIPlayerSiegeUnits.cpp @@ -0,0 +1,72 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/States/UnitState.hpp" +#include "OpenSHC/Map/Units/Unit.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/Map/Units/UnitTypeShort.hpp" + +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Units::Unit; + using OpenSHC::Map::Units::UnitInstructionType; + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::Map::Units::UnitTypeShort; + using OpenSHC::Map::Units::States::UnitState; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CFB20 + void AICState ::disbandAIPlayerSiegeUnits(int playerID) + + { + + Unit* _unit; + + int _unitIDCounter; + + UnitTypeShort _unitType; + + _unitIDCounter = 1; + + _unit = &DAT_UnitsState::instance.units[1]; + + do { + + if ((((_unit->logicalState != OpenSHC::Map::Units::ULS_INVISIBLE) && (_unit->owner == playerID)) + && (((_unitType = _unit->unitType, + _unitType == OpenSHC::Map::Units::UT_S_CATAPULT + || (((_unitType == OpenSHC::Map::Units::UT_S_TREBUCHET + || (_unitType == OpenSHC::Map::Units::UT_S_TOWER)) + || (_unitType == OpenSHC::Map::Units::UT_S_BATTERINGRAM)))) + || ((_unitType == OpenSHC::Map::Units::UT_S_SHIELD + || (_unitType == OpenSHC::Map::Units::UT_S_FBALLISTA)))))) + && ((_unit->unknownSiegeTentRelated02 == 3 && (_unit->aiUnitBehaviourType != 0x15)))) { + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeAnInstruction, DAT_TribesState::ptr)( + (int)_unit->tribeID, OpenSHC::Map::Units::UIT_EXIT_SIEGE_EQUIPMENT, _unitIDCounter, _unit->uid, 0); + + /* + shouldn't logicalState be set to something too? */ + + (_unit->state).generic = OpenSHC::Map::Units::States::US_DISAPPEAR; + } + + _unit = _unit + 0x248; + + _unitIDCounter = _unitIDCounter + 1; + + } while ((int)_unit < 0x165141a); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/findAttackTribePositionAndPathToDefenses.cpp b/src/OpenSHC/AI/AICState/findAttackTribePositionAndPathToDefenses.cpp new file mode 100644 index 00000000..e896ea9e --- /dev/null +++ b/src/OpenSHC/AI/AICState/findAttackTribePositionAndPathToDefenses.cpp @@ -0,0 +1,67 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE930 + void AICState ::findAttackTribePositionAndPathToDefenses(int playerID) + { + uint local_8 = 0; + uint local_4 = 0; + int* local_c = &DAT_SkirmishDefinedData::instance.MaxAttackTribes1[0].tribeCount; + + bool _found = false; + do { + int iVar2 = 0; + if (0 < *local_c) { + short* psVar3 + = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + (*(int (*)[2])(local_c + -1))[0]; + do { + int iVar1 = (int)*psVar3; + if ((iVar1 != 0) + && (DAT_TribesState::instance.tribes[iVar1].uid + == DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[iVar2 + (*(int (*)[2])(local_c + -1))[0]])) { + iVar2 = (int)DAT_TribesState::instance.tribes[iVar1].selectionTargetUnitID; + local_8 = (uint)DAT_UnitsState::instance.units[iVar2].x; + local_4 = (uint)DAT_UnitsState::instance.units[iVar2].y; + if (iVar2 != 0) { + _found = true; + break; + } + break; + } + iVar2 = iVar2 + 1; + psVar3 = psVar3 + 1; + } while (iVar2 < *local_c); + } + if (_found) + break; + local_c = local_c + 2; + } while ((int)local_c <= 0xb42a2b); + + DAT_GameState::instance.playerDataArray[playerID].someTile = 0; + + if (local_8 != 0) { + int _tile = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::tracePathToFriendlyDefensesAndReturnTileUnk, + DAT_PathFindingState::ptr)(playerID, local_8, local_4); + if (0 < _tile) { + short _y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_tile]; + DAT_GameState::instance.playerDataArray[playerID].someTile = _tile; + int iVar2 = DAT_ViewportRenderState::instance.translationMatrix[_y].addXgetTile; + DAT_GameState::instance.playerDataArray[playerID].someY = (int)_y; + DAT_GameState::instance.playerDataArray[playerID].someX = _tile - iVar2; + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/generateSiegeCreationInformation.cpp b/src/OpenSHC/AI/AICState/generateSiegeCreationInformation.cpp new file mode 100644 index 00000000..1fddcaf4 --- /dev/null +++ b/src/OpenSHC/AI/AICState/generateSiegeCreationInformation.cpp @@ -0,0 +1,97 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/Map/Units/UnitTypeShort.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Game::GameMode; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::Map::Units::UnitTypeShort; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC420 + void AICState ::generateSiegeCreationInformation(int playerID, int buildingID, int unitID) + + { + + UnitTypeShort UVar1; + + int iVar2; + + int _siegeIndex; + + int _engineIndexUnk; + + int _nextIndex; + + int _goal; + + if ((DAT_GameState::instance.playerDataArray[playerID].aiType != OpenSHC::AI::AIT_NULL) + && (DAT_GameSynchronyState::instance.DAT_CurrentGameMode != OpenSHC::Game::GM_SOLITARY)) { + + UVar1 = DAT_UnitsState::instance.units[unitID].unitType; + + if (UVar1 == OpenSHC::Map::Units::UT_S_TREBUCHET) { + + _siegeIndex = 2; + + } + + else { + + if (UVar1 != OpenSHC::Map::Units::UT_S_FBALLISTA) { + + return; + } + + _siegeIndex = 3; + } + + iVar2 = DAT_SkirmishDefinedData::instance.SiegeEngineMetaInfoArray[_siegeIndex].slot; + + _goal = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[iVar2]; + + if (0 < _goal) { + + _engineIndexUnk = 0; + + while ((_nextIndex = _engineIndexUnk + 1, + *(int*)(playerID * 0x39f4 + iVar2 * 40 + 0x115eb14 + _engineIndexUnk * 4) <= 0 + || (buildingID + != *(int*)(playerID * 0x39f4 + 0x115f494 + (_engineIndexUnk + _siegeIndex * 5) * 0x14)))) { + + _engineIndexUnk = _nextIndex; + + if (_goal <= _nextIndex) { + + return; + } + } + + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeIndex][_engineIndexUnk] + .unitID = unitID; + + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeIndex][_engineIndexUnk] + .uid = DAT_UnitsState::instance.units[unitID].uid; + + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[_siegeIndex][_engineIndexUnk] + .buildingID = 0; + } + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/getAIBuildInterval.cpp b/src/OpenSHC/AI/AICState/getAIBuildInterval.cpp index d3ed4a5e..8c35a40a 100644 --- a/src/OpenSHC/AI/AICState/getAIBuildInterval.cpp +++ b/src/OpenSHC/AI/AICState/getAIBuildInterval.cpp @@ -15,7 +15,7 @@ namespace AI { return 0; } - return this->DAT_AICArray[(aiType - 1)].buildInterval; + return this->aics[(aiType - 1)].buildInterval; } } } diff --git a/src/OpenSHC/AI/AICState/getCurrentDesiredAttackRaidUnitCount.cpp b/src/OpenSHC/AI/AICState/getCurrentDesiredAttackRaidUnitCount.cpp new file mode 100644 index 00000000..b3697220 --- /dev/null +++ b/src/OpenSHC/AI/AICState/getCurrentDesiredAttackRaidUnitCount.cpp @@ -0,0 +1,26 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Game/TrailType.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D12A0 + int AICState::getCurrentDesiredAttackRaidUnitCount(int aiType, int playerID) + { + if (((DAT_GameCore::instance.gameMode_2 == Game::GM_SKIRMISH_AND_MULTIPLAYER) + && (DAT_GameCore::instance.isSkirmishTrail == TRUE)) + && (DAT_GameCore::instance.currentTrailType == Game::TT_EXTREME)) { + return (int)((DAT_GameState::instance.playerDataArray[playerID].currentAttackRaidParameter + + this->aics[aiType].RaidUnitsBase) + * 4) + / 3; + } + return DAT_GameState::instance.playerDataArray[playerID].currentAttackRaidParameter + + this->aics[aiType].RaidUnitsBase; + } +} +} diff --git a/src/OpenSHC/AI/AICState/getDefensiveTribeForUnit.cpp b/src/OpenSHC/AI/AICState/getDefensiveTribeForUnit.cpp new file mode 100644 index 00000000..24934a4d --- /dev/null +++ b/src/OpenSHC/AI/AICState/getDefensiveTribeForUnit.cpp @@ -0,0 +1,71 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CCBF0 + int AICState::getDefensiveTribeForUnit(int playerID, int unitID) + { + int _index = 0; + int _unitType = (int)(short)DAT_UnitsState::instance.units[unitID].unitType; + int _unitType2; + do { + if ((_unitType == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index]) + || (_unitType2 = (int)(short)DAT_UnitsState::instance.units[unitID].unitTypeToChangeInto, + _unitType2 == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index])) + break; + if ((_unitType == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index + 1]) + || (_unitType2 == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index + 1])) { + _index = _index + 1; break; + } + if ((_unitType == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index + 2]) + || (_unitType2 == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index + 2])) { + _index = _index + 2; break; + } + if ((_unitType == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index + 3]) + || (_unitType2 == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index + 3])) { + _index = _index + 3; break; + } + if ((_unitType == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index + 4]) + || (_unitType2 == DAT_SkirmishDefinedData::instance.DefenseTribesUnitTypeArray[_index + 4])) { + _index = _index + 4; break; + } + _index = _index + 5; + } while (_index < 0x14); + + int _tribeIndex; + if (_index < 1) { + _tribeIndex = 0xb4; + } else if (_index < 2) { + _tribeIndex = 0xb5; + } else if (_index < 7) { + _tribeIndex = 0xb6; + } else if (_index < 0xc) { + _tribeIndex = 0xb7; + } else if (_index < 0xf) { + _tribeIndex = 0xb8; + } else if (_index < 0x14) { + _tribeIndex = 0xb9; + } else { + return 0; + } + + short* _pTribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + _tribeIndex; + int _tribeID = (int)*_pTribeID; + if ((_tribeID == 0) || (DAT_TribesState::instance.tribes[_tribeID].uid != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_tribeIndex])) { + _tribeID = MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + int _tribeUID = DAT_TribesState::instance.tribes[_tribeID].uid; + *_pTribeID = (short)_tribeID; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_tribeIndex] = _tribeUID; + } + return _tribeID; + } +} +} diff --git a/src/OpenSHC/AI/AICState/getResourceRebuildDelay.cpp b/src/OpenSHC/AI/AICState/getResourceRebuildDelay.cpp new file mode 100644 index 00000000..d20a9e2f --- /dev/null +++ b/src/OpenSHC/AI/AICState/getResourceRebuildDelay.cpp @@ -0,0 +1,21 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB150 + int AICState::getResourceRebuildDelay(int playerID) + + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) { + return 0; + } + + return this->aics[(aiType - 1)].resourceRebuildDelay; + } +} +} diff --git a/src/OpenSHC/AI/AICState/getSmallestPatrolTribe.cpp b/src/OpenSHC/AI/AICState/getSmallestPatrolTribe.cpp new file mode 100644 index 00000000..daeffb4e --- /dev/null +++ b/src/OpenSHC/AI/AICState/getSmallestPatrolTribe.cpp @@ -0,0 +1,55 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CCAF0 + int AICState ::getSmallestPatrolTribe(int playerID, int tribeCount) + { + int _selectedTribeID = 0; + int _tribeNumber = 0; + int _selectedTribeNumber = 0; + short _lowestTribeUnitCount = 1000; + + if (0 < tribeCount) { + short* _pTribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + 0xaa; + do { + int _tribeID = (int)*_pTribeID; + if ((_tribeID == 0) + || (DAT_TribesState::instance.tribes[_tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_tribeNumber + 0xaa])) { + _selectedTribeID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + _selectedTribeNumber = _tribeNumber; + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[_selectedTribeNumber + 0xaa] + = (short)_selectedTribeID; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_selectedTribeNumber + 0xaa] + = DAT_TribesState::instance.tribes[_selectedTribeID].uid; + return _selectedTribeID; + } + short _tribeSize = DAT_TribesState::instance.tribes[_tribeID].size; + if (_tribeSize < _lowestTribeUnitCount) { + _selectedTribeID = _tribeID; + _selectedTribeNumber = _tribeNumber; + _lowestTribeUnitCount = _tribeSize; + } + _tribeNumber = _tribeNumber + 1; + _pTribeID = _pTribeID + 1; + } while (_tribeNumber < tribeCount); + + if (_selectedTribeID != 0) { + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[_selectedTribeNumber + 0xaa] + = (short)_selectedTribeID; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_selectedTribeNumber + 0xaa] + = DAT_TribesState::instance.tribes[_selectedTribeID].uid; + return _selectedTribeID; + } + } + return 0; + } +} +} diff --git a/src/OpenSHC/AI/AICState/getTargetableBuildingForPlayerID.cpp b/src/OpenSHC/AI/AICState/getTargetableBuildingForPlayerID.cpp new file mode 100644 index 00000000..cd9b3b5e --- /dev/null +++ b/src/OpenSHC/AI/AICState/getTargetableBuildingForPlayerID.cpp @@ -0,0 +1,116 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" +#include "OpenSHC/Map/Buildings/BuildingTypeInt.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Game::Player::PlayerData; + using OpenSHC::Map::Buildings::BuildingTypeInt; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDBC0 + int AICState ::getTargetableBuildingForPlayerID(int playerID, int param_2) + + { + + int* piVar1; + + PlayerData* pPVar2; + + int iVar3; + + int _candidateBuilding; + + BuildingTypeInt* _pTargets; + + int _index; + + int _aiType; + + int _outerPatrolGroupsCount; + + int _tracker; + + iVar3 = playerID; + + _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if ((_aiType != 0) + && (_outerPatrolGroupsCount = *(int*)((int)this + (_aiType + -1) * 0x2a4 + 0x174), + 0 < _outerPatrolGroupsCount)) { + + _tracker = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker; + + if (0 < _tracker) { + + piVar1 = &DAT_GameState::instance.playerDataArray[playerID].someCounter2; + + *piVar1 = *piVar1 + 1; + + if (_tracker <= DAT_GameState::instance.playerDataArray[playerID].someCounter2) { + + DAT_GameState::instance.playerDataArray[playerID].someCounter2 = 0; + } + + pPVar2 = DAT_GameState::instance.playerDataArray + playerID; + + playerID = 0; + + _index = ((_tracker / _outerPatrolGroupsCount) * param_2 + pPVar2->someCounter2) % _tracker; + + if (0 < _tracker) { + + do { + + _candidateBuilding + = (int)DAT_GameState::instance.playerDataArray[iVar3].top100TargetableBuildings[_index]; + + _index = _index + 1; + + if (_tracker <= _index) { + + _index = 0; + } + + if (_candidateBuilding != 0) { + + _pTargets = DAT_SkirmishDefinedData::instance.BuildingTargetPrioritySet1; + + do { + + if ((int)(short)DAT_BuildingsState::instance.buildings[_candidateBuilding].buildingType + == *_pTargets) { + + return _candidateBuilding; + } + + _pTargets = _pTargets + 1; + + /* + Iterates only over resource buildings, so if resource building, return + */ + + } while ((int)_pTargets < 0xb427c8); + } + + playerID = playerID + 1; + + } while (playerID < _tracker); + } + } + + return 0; + } + + return 0; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/getUnitTypeForUnitBehaviourType.cpp b/src/OpenSHC/AI/AICState/getUnitTypeForUnitBehaviourType.cpp new file mode 100644 index 00000000..fcf4860b --- /dev/null +++ b/src/OpenSHC/AI/AICState/getUnitTypeForUnitBehaviourType.cpp @@ -0,0 +1,118 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/AI/AIUnitBehaviourType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::AI::AIUnitBehaviourType; + using OpenSHC::Map::Units::UnitType; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC250 + UnitType AICState ::getUnitTypeForUnitBehaviourType(int playerID, AIUnitBehaviourType unitBehaviourType) + + { + + AITypeInt AVar1; + + int iVar2; + + UnitType UVar3; + + AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (AVar1 == OpenSHC::AI::AIT_NULL) { + + return ((UnitType)0); + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_ENGINEERING) { + + return OpenSHC::Map::Units::UT_E_ENGINEER; + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_DIGGING) { + + return (UnitType)(this->aics[AVar1 + ~OpenSHC::AI::AIT_NULL].AttDiggingUnit); + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_ASSASSINATION) { + + return OpenSHC::Map::Units::UT_A_ASSASSIN; + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_ATTUNIT2) { + + return (UnitType)(this->aics[AVar1 + ~OpenSHC::AI::AIT_NULL].AttUnit2); + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_LADDERING) { + + return OpenSHC::Map::Units::UT_E_LADDER; + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_TUNNELING) { + + return OpenSHC::Map::Units::UT_TUNNELER; + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_PATROLLING) { + + return (UnitType)(this->aics[AVar1 + ~OpenSHC::AI::AIT_NULL].AttUnitPatrol); + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_BACKUPING) { + + return (UnitType)(this->aics[AVar1 + ~OpenSHC::AI::AIT_NULL].AttUnitBackup); + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_ENGAGING) { + + return (UnitType)(this->aics[AVar1 + ~OpenSHC::AI::AIT_NULL].AttUnitEngage); + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_SIEGEDEFENSE) { + + return (UnitType)(this->aics[AVar1 + ~OpenSHC::AI::AIT_NULL].AttUnitSiegeDef); + } + + if (unitBehaviourType == OpenSHC::AI::AIUBT_ATTUNITMAIN) { + + if (*(int*)((int)this->aics + DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex * 4 + + AVar1 * 0x2a4 + -0x1c) + == 0) { + + DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex = 0; + } + + if (3 < DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex) { + + DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex = 0; + } + + iVar2 = DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex; + + /* + AttUnitMain[] */ + + UVar3 = *(UnitType*)((int)this->aics + AVar1 * 0x2a4 + iVar2 * 4 + -0x1c); + + DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex = iVar2 + 1; + + return UVar3; + } + + return (UnitType)(this->aics[AVar1 + ~OpenSHC::AI::AIT_NULL].AttUnitMain1); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/giveMoveCommandToAttackTribes.cpp b/src/OpenSHC/AI/AICState/giveMoveCommandToAttackTribes.cpp new file mode 100644 index 00000000..a224fd2e --- /dev/null +++ b/src/OpenSHC/AI/AICState/giveMoveCommandToAttackTribes.cpp @@ -0,0 +1,105 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CEB50 + void AICState ::giveMoveCommandToAttackTribes(int playerID) + + { + + int _aiTribeID; + + int _counter; + + int _groupCounter; + + int* _ptrGroupCount; + + int _groupCount; + + int _playerID; + + int _tribeType; + + _playerID = playerID; + + if (DAT_GameState::instance.playerDataArray[playerID].aiType != OpenSHC::AI::AIT_NULL) { + + _counter = 0; + + _ptrGroupCount = &DAT_SkirmishDefinedData::instance.MaxAttackTribes1[0].tribeCount; + + do { + + _tribeType = (*(int (*)[2])(_ptrGroupCount + -1))[0]; + + _groupCount = *_ptrGroupCount; + + if ((((_tribeType != 18) && (_tribeType != 190)) && (_tribeType != 15)) + && (_groupCounter = 0, 0 < _groupCount)) { + + /* + fixme: param reusage */ + + playerID = (_playerID * 0x1cfa + _tribeType) * 2 + 0x115ef04; + + do { + + _aiTribeID = (int)*(short*)playerID; + + if ((_aiTribeID != 0) + && (DAT_TribesState::instance.tribes[_aiTribeID].uid + == DAT_GameState::instance.playerDataArray[_playerID] + .aiTribeUIDs[_groupCounter + _tribeType])) { + + /* + bug: why give a random unit stance? */ + + DAT_TribesState::instance.tribes[_aiTribeID].unitStance + = (short)(char)((byte)SEC_RNG::instance.currentNumber2 & 1); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, + DAT_TribesState::ptr)(_aiTribeID, + (uint)((int)(DAT_GameState::instance.mapAndTime + .aiTribeMoveDestinationXYPairArray1[_playerID * 5][_counter] + .xOffset)), + (uint)((int)(DAT_GameState::instance.mapAndTime + .aiTribeMoveDestinationXYPairArray1[_playerID * 5][_counter] + .yOffset)), + 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + + _counter = _counter + 1; + } + + playerID = playerID + 2; + + _groupCounter = _groupCounter + 1; + + } while (_groupCounter < _groupCount); + } + + _ptrGroupCount = _ptrGroupCount + 2; + + } while ((int)_ptrGroupCount < 0xb42a2c); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/giveMoveCommandToSortieUnits.cpp b/src/OpenSHC/AI/AICState/giveMoveCommandToSortieUnits.cpp new file mode 100644 index 00000000..a4632438 --- /dev/null +++ b/src/OpenSHC/AI/AICState/giveMoveCommandToSortieUnits.cpp @@ -0,0 +1,81 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD8F0 + void AICState ::giveMoveCommandToSortieUnits(int playerID) + { + int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == 0) + return; + + int _tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xa7]; + if (_tribeID == 0) + return; + if (DAT_TribesState::instance.tribes[_tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xa7]) + return; + + if ((int)DAT_TribesState::instance.tribes[_tribeID].size + < DAT_GameState::instance.playerDataArray[playerID].totalTroopsType7 + -1) + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::syncBehaviourType7UnitTribes, this)(playerID); + + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker > 0) { + // nervous: send to keep then set stance + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(_tribeID, playerID); + DAT_TribesState::instance.tribes[_tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + return; + } + + if ((int)DAT_TribesState::instance.tribes[_tribeID].size + < *(int*)((int)this + (_aiType + -1) * 0x2a4 + 0x154)) { + // tribe too small: just set stance + DAT_TribesState::instance.tribes[_tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + return; + } + + int _unitID = DAT_GameState::instance.playerDataArray[playerID].lastEncounteredTroopUnitID; + int iVar1; + if (((_unitID != 0) + && (DAT_GameState::instance.playerDataArray[playerID].lastEncounteredTroopUnitUID + == DAT_UnitsState::instance.units[_unitID].uid)) + && (iVar1 = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(playerID, + (dword)((int)((int)(short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance + .units[DAT_TribesState::instance.tribes[_tribeID].selectionTargetUnitID].tile])), + (dword)((int)((int)(short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance.units[_unitID].tile])), + 0), + iVar1 != 0)) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::unsetRallyRelatedFlagOnUnits, + DAT_TribesState::ptr)(_tribeID); + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, + DAT_TribesState::ptr)(_tribeID, + (uint)((int)((int)DAT_UnitsState::instance.units[_unitID].x)), + (uint)((int)((int)DAT_UnitsState::instance.units[_unitID].y)), 0, 0, + OpenSHC::Map::Units::Instructions::UMSE_0); + DAT_TribesState::instance.tribes[_tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + return; + } + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(_tribeID, playerID); + DAT_TribesState::instance.tribes[_tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/giveSomeRaidCommand.cpp b/src/OpenSHC/AI/AICState/giveSomeRaidCommand.cpp new file mode 100644 index 00000000..eb6658bc --- /dev/null +++ b/src/OpenSHC/AI/AICState/giveSomeRaidCommand.cpp @@ -0,0 +1,61 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF920 + undefined4 AICState ::giveSomeRaidCommand(int tribeIndex, int tribeType) + + { + int _tribeOffset = tribeIndex * 0x334; + int _attacked = DAT_TribesState::instance.tribes[tribeIndex].owner; + int _targetBuildingID = (int)DAT_TribesState::instance.tribes[tribeIndex].targetBuildingID; + + if (tribeType != 0xba && tribeType != 0x12 && tribeType != 0xe && tribeType != 0xd) { + + if (_targetBuildingID != 0 + && DAT_BuildingsState::instance.buildings[_targetBuildingID].uid + == DAT_TribesState::instance.tribes[tribeIndex].targetBuildingUID + && DAT_BuildingsState::instance.buildings[_targetBuildingID].fireDuration == 0) { + return (undefined4)(1); + } + + int _attackedPlayerID = DAT_GameState::instance.playerDataArray[_attacked].attackedPlayerID; + + DAT_TribesState::instance.tribes[tribeIndex].targetBuildingID = 0; + + int _chosenBuildingID = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::chooseRandomBuildingIDFromPlayersArrayOfBuildings, this)(_attackedPlayerID); + + if (_chosenBuildingID != 0) { + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::queueDisbandAndAttackCommand4Params, + DAT_UnitsState::ptr)(tribeIndex, 9, (undefined4)((int)(_chosenBuildingID)), + (undefined4)((int)(DAT_BuildingsState::instance.buildings[_chosenBuildingID].uid))); + + // Store UID before targetBuildingID (matching orig order) + DAT_TribesState::instance.tribes[tribeIndex].targetBuildingUID + = DAT_BuildingsState::instance.buildings[_chosenBuildingID].uid; + + DAT_TribesState::instance.tribes[tribeIndex].unitStance = OpenSHC::Map::Units::Behavior::USE_DEFENSIVE; + + DAT_TribesState::instance.tribes[tribeIndex].targetBuildingID = (short)_chosenBuildingID; + + return (undefined4)(1); + } + } + + return (undefined4)(0); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/hasNoTroopsOrAllAreDiggers.cpp b/src/OpenSHC/AI/AICState/hasNoTroopsOrAllAreDiggers.cpp new file mode 100644 index 00000000..fe3050b2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/hasNoTroopsOrAllAreDiggers.cpp @@ -0,0 +1,58 @@ +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CFFD0 + BOOLEnum AICState::hasNoTroopsOrAllAreDiggers(int playerID) + { + int _totalMisc = DAT_GameState::instance.playerDataArray[playerID].totalAssassinTroops + + DAT_GameState::instance.playerDataArray[playerID].totalUnit2Troops + + DAT_GameState::instance.playerDataArray[playerID].totalUnitPatrolTroops; + int _halfOfDiggingTroops = 0; + int _totalTroops; + + if (DAT_GameState::instance.playerDataArray[playerID].aiPlayerState == 6) { + _totalMisc += DAT_GameState::instance.playerDataArray[playerID].totalDiggingUnitTroops; + int _attackTicker = DAT_GameState::instance.playerDataArray[playerID].attackTicker; + int _backup; + if (_attackTicker > 0x10) { + int _bp = DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops; + _backup = (_bp + (_bp >> 0x1f & 7)) >> 3; + } else { + _backup = DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops; + } + _totalMisc += _backup; + _totalTroops = _totalMisc + + DAT_GameState::instance.playerDataArray[playerID].totalUnitEngageTroops + + DAT_GameState::instance.playerDataArray[playerID].totalUnitSiegeDefTroops + + DAT_GameState::instance.playerDataArray[playerID].totalMaxDefaultTroops; + } else { + int _digging = DAT_GameState::instance.playerDataArray[playerID].totalDiggingUnitTroops; + _halfOfDiggingTroops = _digging / 2; + int _attackTicker = DAT_GameState::instance.playerDataArray[playerID].attackTicker; + int _backup; + if (_attackTicker > 0xc) { + int _bp2 = DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops; + _backup = (_bp2 + (_bp2 >> 0x1f & 7)) >> 3; + } else { + _backup = DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops; + } + _totalMisc += _backup; + _totalTroops = _totalMisc + + DAT_GameState::instance.playerDataArray[playerID].totalUnitEngageTroops + + DAT_GameState::instance.playerDataArray[playerID].totalUnitSiegeDefTroops + + DAT_GameState::instance.playerDataArray[playerID].totalMaxDefaultTroops; + } + + if (1 < _totalTroops) { + if (4 < _totalTroops || _halfOfDiggingTroops <= _totalTroops + _totalTroops) { + return FALSE; + } + } + return TRUE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/hasNotEnoughSupplies.cpp b/src/OpenSHC/AI/AICState/hasNotEnoughSupplies.cpp new file mode 100644 index 00000000..28dffd26 --- /dev/null +++ b/src/OpenSHC/AI/AICState/hasNotEnoughSupplies.cpp @@ -0,0 +1,33 @@ +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D00A0 + BOOLEnum AICState::hasNotEnoughSupplies(int playerID) + { + BOOLEnum BVar1 = TRUE; + if (DAT_GameState::instance.playerDataArray[playerID].marketplace.id == 0 + && DAT_GameState::instance.playerDataArray[playerID].currentResources[2] + < DAT_BuildingsState::instance.buildingCosts[0x1a].requiredWood + && DAT_GameState::instance.playerDataArray[playerID].noLabourerBuildingCount < 1) { + return BVar1; + } + if (DAT_GameState::instance.playerDataArray[playerID].granary.id == 0 + && DAT_GameState::instance.playerDataArray[playerID].currentResources[2] + < DAT_BuildingsState::instance.buildingCosts[0x13].requiredWood + && DAT_GameState::instance.playerDataArray[playerID].noLabourerBuildingCount <= 1) { + return BVar1; + } + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf] <= 0 + && DAT_GameState::instance.playerDataArray[playerID].popularity <= 25 + && DAT_GameState::instance.playerDataArray[playerID].totalFood <= 0) { + return BVar1; + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/ifHasTribe0SendUnitsToKeep.cpp b/src/OpenSHC/AI/AICState/ifHasTribe0SendUnitsToKeep.cpp new file mode 100644 index 00000000..b118a2cc --- /dev/null +++ b/src/OpenSHC/AI/AICState/ifHasTribe0SendUnitsToKeep.cpp @@ -0,0 +1,20 @@ +#include "OpenSHC/AI/AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2830 + void AICState::ifHasTribe0SendUnitsToKeep(int playerID) + { + int tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0]; + if ((0 < tribeID) + && (DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0])) { + MACRO_CALL_MEMBER(AICState_Func::sendUnitsToKeep, this)(tribeID, playerID); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/instructTribe166ToMove.cpp b/src/OpenSHC/AI/AICState/instructTribe166ToMove.cpp new file mode 100644 index 00000000..620439b2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/instructTribe166ToMove.cpp @@ -0,0 +1,77 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD7B0 + void AICState ::instructTribe166ToMove(int playerID) + { + AITypeInt _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == OpenSHC::AI::AIT_NULL) + return; + + int _counter = DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01; + if (0 < _counter) + DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01 = _counter + -1; + if (50 < DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01) + DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01 = 50; + + int _tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[166]; + BOOLEnum _keepEnclosed = MACRO_CALL_MEMBER( + OpenSHC::Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)(playerID); + + if (_tribeID == 0) + return; + if (DAT_TribesState::instance.tribes[_tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xa6]) + return; + + if ((int)DAT_TribesState::instance.tribes[_tribeID].size + < DAT_GameState::instance.playerDataArray[playerID].totalTroopsType6 + -5) + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addUnitsToTribe166, this)(playerID); + + if ((DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker < 1) + || (_keepEnclosed != FALSE)) { + + if ((int)DAT_TribesState::instance.tribes[_tribeID].size + < *(int*)((int)this + (_aiType + ~OpenSHC::AI::AIT_NULL) * 0x2a4 + 0x14c)) { + // tribe too small: just set stance + DAT_TribesState::instance.tribes[_tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + return; + } + + if ((0 < DAT_GameState::instance.playerDataArray[playerID].someXPosition) + && (0 < DAT_GameState::instance.playerDataArray[playerID].someYPosition)) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::unsetRallyRelatedFlagOnUnits, + DAT_TribesState::ptr)(_tribeID); + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, + DAT_TribesState::ptr)(_tribeID, + (uint)((int)(DAT_GameState::instance.playerDataArray[playerID].someXPosition)), + (uint)((int)(DAT_GameState::instance.playerDataArray[playerID].someYPosition)), 0, 0, + OpenSHC::Map::Units::Instructions::UMSE_0); + DAT_TribesState::instance.tribes[_tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + return; + } + } + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(_tribeID, playerID); + DAT_TribesState::instance.tribes[_tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/isResourceLargerOrEqualThanMinimumGoodsRequiredAfterTrade.cpp b/src/OpenSHC/AI/AICState/isResourceLargerOrEqualThanMinimumGoodsRequiredAfterTrade.cpp new file mode 100644 index 00000000..9b205892 --- /dev/null +++ b/src/OpenSHC/AI/AICState/isResourceLargerOrEqualThanMinimumGoodsRequiredAfterTrade.cpp @@ -0,0 +1,25 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Game/Resources/ResourceType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0520 + BOOLEnum AICState::isResourceLargerOrEqualThanMinimumGoodsRequiredAfterTrade( + int playerID, ResourceType resourceType, int buffer) + { + int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == AITA_NULL) { + return TRUE; + } + if (resourceType == Game::Resources::RT_GOLD) { + return FALSE; + } + return (int)this->aics[_aiType - 1].minimumGoodsRequiredAfterTrade + buffer + <= DAT_GameState::instance.playerDataArray[playerID].currentResources[resourceType]; + } +} +} diff --git a/src/OpenSHC/AI/AICState/makeOutpostTribesAttack.cpp b/src/OpenSHC/AI/AICState/makeOutpostTribesAttack.cpp new file mode 100644 index 00000000..17c068b4 --- /dev/null +++ b/src/OpenSHC/AI/AICState/makeOutpostTribesAttack.cpp @@ -0,0 +1,25 @@ +#include "OpenSHC/AI/AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3960 + void AICState::makeOutpostTribesAttack() + { + int* _tribeUIDs = this->tribeUIDArray; + short* _tribeIDs = this->tribeIDArray; + int _countDown = 1000; + do { + unsigned int _tribeID = (uint)*_tribeIDs; + if ((_tribeID != 0) && (DAT_TribesState::instance.tribes[_tribeID].uid == *_tribeUIDs)) { + MACRO_CALL_MEMBER(AICState_Func::decideOnTribeAttackLocation, this)(_tribeID); + } + _tribeIDs = _tribeIDs + 1; + _tribeUIDs = _tribeUIDs + 1; + _countDown = _countDown + -1; + } while (_countDown != 0); + } +} +} diff --git a/src/OpenSHC/AI/AICState/makeUnitsGoDefensiveAndBackToSomeLocation.cpp b/src/OpenSHC/AI/AICState/makeUnitsGoDefensiveAndBackToSomeLocation.cpp new file mode 100644 index 00000000..540b6cdd --- /dev/null +++ b/src/OpenSHC/AI/AICState/makeUnitsGoDefensiveAndBackToSomeLocation.cpp @@ -0,0 +1,100 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CEA50 + void AICState ::makeUnitsGoDefensiveAndBackToSomeLocation(int param_1) + + { + + int iVar1; + + int iVar2; + + int _commandResult; + + short* psVar3; + + int tribeID; + + int iVar4; + + int* local_c; + + uint _x; + + if ((DAT_GameState::instance.playerDataArray[param_1].aiType != OpenSHC::AI::AIT_NULL) + && (DAT_GameState::instance.playerDataArray[param_1].someTile != 0)) { + + local_c = &DAT_SkirmishDefinedData::instance.MaxAttackTribes1[0].tribeCount; + + do { + + iVar1 = *local_c; + + iVar2 = (*(int (*)[2])(local_c + -1))[0]; + + iVar4 = 0; + + if (0 < iVar1) { + + psVar3 = DAT_GameState::instance.playerDataArray[param_1].aiTribeIDs + iVar2; + + do { + + tribeID = (int)*psVar3; + + if ((tribeID != 0) + && (DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[param_1].aiTribeUIDs[iVar4 + iVar2])) { + + _x = DAT_GameState::instance.playerDataArray[param_1].someX; + + DAT_TribesState::instance.tribes[tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_DEFENSIVE; + + _commandResult = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)( + tribeID, _x, (uint)((int)(DAT_GameState::instance.playerDataArray[param_1].someY)), 0, + 0, OpenSHC::Map::Units::Instructions::UMSE_0); + + if (_commandResult == 0) { + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::removeAllTribeUnits, + DAT_TribesState::ptr)(tribeID); + } + } + + iVar4 = iVar4 + 1; + + psVar3 = psVar3 + 1; + + } while (iVar4 < iVar1); + } + + local_c = local_c + 2; + + } while ((int)local_c < 0xb42a2c); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/mapperIsWoodCuttersHutWhileNoWoodAvailable.cpp b/src/OpenSHC/AI/AICState/mapperIsWoodCuttersHutWhileNoWoodAvailable.cpp new file mode 100644 index 00000000..09d8f657 --- /dev/null +++ b/src/OpenSHC/AI/AICState/mapperIsWoodCuttersHutWhileNoWoodAvailable.cpp @@ -0,0 +1,17 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ECAB0 + BOOLEnum AICState::mapperIsWoodCuttersHutWhileNoWoodAvailable( + undefined4 playerID, Commands::MappersEnum commandBuildingType) + { + return ((commandBuildingType == Commands::M_MAPPER_WOODSMAN) + && (DAT_AIVState::instance.mapExtraInfo.totalWoodAvailable <= 0)); + } +} +} diff --git a/src/OpenSHC/AI/AICState/moveAttackTribesToLocations.cpp b/src/OpenSHC/AI/AICState/moveAttackTribesToLocations.cpp new file mode 100644 index 00000000..6de76caf --- /dev/null +++ b/src/OpenSHC/AI/AICState/moveAttackTribesToLocations.cpp @@ -0,0 +1,61 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE830 + void AICState::moveAttackTribesToLocations(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return; + + int _destIndex = 0; + + for (int _index = 0; _index < 11; _index++) { + int const _tribeTypeStart = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[_index].tribeType; + int _tribeIndex = 0; + int const _maxTribeCount = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[_index].tribeCount; + + for (; _tribeIndex < _maxTribeCount; _tribeIndex++) { + + int const _tribeID + = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[_tribeTypeStart + _tribeIndex]; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[_tribeIndex + _tribeTypeStart])) { + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_DEFENSIVE; + + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)(_tribeID, + (uint)((int)(DAT_GameState::instance.mapAndTime + .aiTribeMoveDestinationXYPairArray1[playerID * 5][_destIndex] + .xOffset)), + (uint)((int)(DAT_GameState::instance.mapAndTime + .aiTribeMoveDestinationXYPairArray1[playerID * 5][_destIndex] + .yOffset)), + 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + + _destIndex = _destIndex + 1; + } + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/moveAttackingUnitsToNextRallyPoint.cpp b/src/OpenSHC/AI/AICState/moveAttackingUnitsToNextRallyPoint.cpp new file mode 100644 index 00000000..18634bdf --- /dev/null +++ b/src/OpenSHC/AI/AICState/moveAttackingUnitsToNextRallyPoint.cpp @@ -0,0 +1,151 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2F80 + void AICState ::moveAttackingUnitsToNextRallyPoint(int playerID) + + { + + short sVar1; + + short* _ptrAttackingUnitsTribeID; + + BOOLEnum _allArrived; + + int _rallyPointIndex; + + uint _x; + + uint _y; + + int _tribeID; + + bool bVar2; + + bool bVar3; + + bool bVar4; + + int _counter; + + short _totalRallyPointSteps; + + short _targetUnitID; + + if (DAT_GameState::instance.playerDataArray[playerID].aiType != OpenSHC::AI::AIT_NULL) { + + _ptrAttackingUnitsTribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + 0xbe; + + _counter = 0; + + do { + + _tribeID = (int)*_ptrAttackingUnitsTribeID; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_counter + 0xbe])) { + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + _allArrived + = MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::allUnitsReachedTheirDestination, + DAT_TribesState::ptr)(_tribeID); + + if (_allArrived != FALSE) { + + DAT_TribesState::instance.tribes[_tribeID].unknownAttackRelatedUpdateCounter = 0; + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addRallyPointForTribe, this)(_tribeID); + + _totalRallyPointSteps = DAT_TribesState::instance.tribes[_tribeID].rallyPointCount; + + _rallyPointIndex = DAT_TribesState::instance.tribes[_tribeID].currentRallyPointIndex + 1; + + if (_totalRallyPointSteps <= _rallyPointIndex) { + + _rallyPointIndex = 0; + } + + _x = (uint)DAT_TribesState::instance.tribes[_tribeID].rallyPointArray[_rallyPointIndex][0]; + + _y = (uint)DAT_TribesState::instance.tribes[_tribeID].rallyPointArray[_rallyPointIndex][1]; + + _targetUnitID = DAT_TribesState::instance.tribes[_tribeID].selectionTargetUnitID; + + DAT_TribesState::instance.tribes[_tribeID].currentRallyPointIndex = (short)_rallyPointIndex; + + if (0 < _totalRallyPointSteps) { + + if (_totalRallyPointSteps < 2) { + + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)( + (int)DAT_UnitsState::instance.units[_targetUnitID].x, + (int)((int)(DAT_UnitsState::instance.units[_targetUnitID].y)), (int)((int)(_x)), + (int)((int)(_y))); + + bVar4 = (DAT_DirectionAlgorithmState::instance.distanceHigh < 5); + + bVar3 = DAT_DirectionAlgorithmState::instance.distanceHigh + -5 < 0; + + bVar2 = DAT_DirectionAlgorithmState::instance.distanceHigh == 5; + + } + + else { + + sVar1 = DAT_UnitsState::instance.units[_targetUnitID].closestEnemyMicroDistance; + + bVar4 = (sVar1 < 96); + + bVar3 = (short)(sVar1 + 96) < 0; + + bVar2 = sVar1 == 96; + } + + if (!bVar2 && bVar4 == bVar3) { + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, + DAT_TribesState::ptr)( + _tribeID, _x, _y, 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + } + } + } + } + + _counter = _counter + 1; + + _ptrAttackingUnitsTribeID = _ptrAttackingUnitsTribeID + 1; + + } while (_counter < 2); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/percentageNonMovingTribesGTEAICSpecified.cpp b/src/OpenSHC/AI/AICState/percentageNonMovingTribesGTEAICSpecified.cpp new file mode 100644 index 00000000..dba68ea4 --- /dev/null +++ b/src/OpenSHC/AI/AICState/percentageNonMovingTribesGTEAICSpecified.cpp @@ -0,0 +1,96 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CED90 + BOOLEnum AICState ::percentageNonMovingTribesGTEAICSpecified(int playerID) + + { + + int _tribe; + + int _index; + + short* _ptrTribeNumber; + + int _tribesCount; + + int _movingFewerThan20Percent; + + int* _ptrSize; + + AITypeInt _aiType; + + _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (_aiType == OpenSHC::AI::AIT_NULL) { + + return FALSE; + } + + _tribesCount = 0; + + _movingFewerThan20Percent = 0; + + _ptrSize = &DAT_SkirmishDefinedData::instance.MaxAttackTribes1[0].tribeCount; + + do { + + _index = 0; + + if (0 < *_ptrSize) { + + _ptrTribeNumber + = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + (*(int (*)[2])(_ptrSize + -1))[0]; + + do { + + _tribe = (int)*_ptrTribeNumber; + + if (((_tribe != 0) + && (DAT_TribesState::instance.tribes[_tribe].uid + == DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[_index + (*(int (*)[2])(_ptrSize + -1))[0]])) + && (_tribesCount = _tribesCount + 1, + DAT_TribesState::instance.tribes[_tribe].percentageMovingUnk < 20)) { + + _movingFewerThan20Percent = _movingFewerThan20Percent + 1; + } + + _index = _index + 1; + + _ptrTribeNumber = _ptrTribeNumber + 1; + + } while (_index < *_ptrSize); + } + + _ptrSize = _ptrSize + 2; + + } while ((int)_ptrSize < 0xb42a2c); + + if (0 < _tribesCount) { + + return (uint)(*(int*)((int)this + (_aiType + ~OpenSHC::AI::AIT_NULL) * 0x2a4 + 0x204) + <= (_movingFewerThan20Percent * 100) / _tribesCount); + } + + return FALSE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/planToBuyWhenLowOnResourceAndSnoozeBuildings.cpp b/src/OpenSHC/AI/AICState/planToBuyWhenLowOnResourceAndSnoozeBuildings.cpp new file mode 100644 index 00000000..dd0c5b81 --- /dev/null +++ b/src/OpenSHC/AI/AICState/planToBuyWhenLowOnResourceAndSnoozeBuildings.cpp @@ -0,0 +1,330 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CBA50 + void AICState ::planToBuyWhenLowOnResourceAndSnoozeBuildings(int playerID) + + { + + int _buildingBurning; + + int _offset; + + int _currentWood; + + int _noWood; + + int _currentPopulation; + + int* _pResourceCounter; + + int _hasArmoyUnk; + + int* _pNoBeerCounter; + + int* _pNoHopsTracker; + + int* _pNoFlourTracker; + + int* _pNoIronTracker; + + bool _enoughFood; + + bool _enoughGold; + + bool _enoughPeopleUnk; + + int _nerves; + + int _noBeerCounterUnk; + + int _noFlourCounterUnk; + + int _noHopsCounterUnk; + + int _noIronCounterUnk; + + bool _noNerves; + + int* _pNoWoodTracker; + + int _resourceCounter; + + _offset = playerID * 0x39f4; + + if (DAT_GameState::instance.playerDataArray[playerID].aiType != OpenSHC::AI::AIT_NULL) { + + _enoughPeopleUnk = true; + + _enoughFood = true; + + _enoughGold = true; + + _buildingBurning = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::playerHasBurningBuilding, + DAT_BuildingsState::ptr)(playerID); + + _currentWood = DAT_GameState::instance.playerDataArray[playerID].currentResources[2]; + + if (_currentWood < 1) { + + _pNoWoodTracker = &DAT_GameState::instance.playerDataArray[playerID].noWoodTracker; + + *_pNoWoodTracker = *_pNoWoodTracker + 1; + + } + + else { + + DAT_GameState::instance.playerDataArray[playerID].noWoodTracker = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[6] < 1) { + + _pNoIronTracker = &DAT_GameState::instance.playerDataArray[playerID].noIronTracker; + + *_pNoIronTracker = *_pNoIronTracker + 1; + + } + + else { + + DAT_GameState::instance.playerDataArray[playerID].noIronTracker = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[0x10] < 1) { + + _pNoFlourTracker = &DAT_GameState::instance.playerDataArray[playerID].noFlourTracker; + + *_pNoFlourTracker = *_pNoFlourTracker + 1; + + } + + else { + + DAT_GameState::instance.playerDataArray[playerID].noFlourTracker = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[3] < 1) { + + _pNoHopsTracker = &DAT_GameState::instance.playerDataArray[playerID].noHopsTracker; + + *_pNoHopsTracker = *_pNoHopsTracker + 1; + + } + + else { + + DAT_GameState::instance.playerDataArray[playerID].noHopsTracker = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[0xe] < 1) { + + _pNoBeerCounter = &DAT_GameState::instance.playerDataArray[playerID].noBeerCounterUnk; + + *_pNoBeerCounter = *_pNoBeerCounter + 1; + + } + + else { + + DAT_GameState::instance.playerDataArray[playerID].noBeerCounterUnk = 0; + } + + _pResourceCounter = &DAT_GameState::instance.playerDataArray[playerID].someResourceCounter; + + *_pResourceCounter = *_pResourceCounter + 1; + + if (DAT_GameState::instance.playerDataArray[playerID].countFletchersPoleturners < 1) { + + DAT_GameState::instance.playerDataArray[playerID].noWoodTracker = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].countArmorersAndBlacksmiths < 1) { + + DAT_GameState::instance.playerDataArray[playerID].noIronTracker = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].countBakers < 1) { + + DAT_GameState::instance.playerDataArray[playerID].noFlourTracker = 0; + } + + if (DAT_GameState::instance.playerDataArray[playerID].countBrewers < 1) { + + DAT_GameState::instance.playerDataArray[playerID].noHopsTracker = 0; + } + + _currentPopulation = DAT_GameState::instance.playerDataArray[playerID].currentPopulation; + + if ((_currentPopulation < 10) + && ((DAT_GameState::instance.playerDataArray[playerID].farmsWithoutWorkers != 0 + || (2 < DAT_GameState::instance.playerDataArray[playerID].countWoodcutters)))) { + + _enoughPeopleUnk = false; + } + + if ((((20 < _currentWood) && (_currentPopulation < 10)) + && (DAT_GameState::instance.playerDataArray[playerID].farmsWithoutWorkers != 0)) + && (DAT_GameState::instance.playerDataArray[playerID].totalFood < 8)) { + + _enoughFood = false; + } + + if (((_buildingBurning == 0) + && (DAT_GameState::instance.playerDataArray[playerID].countFarms + <= DAT_GameState::instance.playerDataArray[playerID].farmsWithoutWorkers)) + && (DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf] < 500)) { + + _enoughGold = false; + } + + _hasArmoyUnk = DAT_GameState::instance.playerDataArray[playerID].armory.id; + + _noWood = DAT_GameState::instance.playerDataArray[playerID].noWoodTracker; + + _noIronCounterUnk = DAT_GameState::instance.playerDataArray[playerID].noIronTracker; + + _resourceCounter = DAT_GameState::instance.playerDataArray[playerID].someResourceCounter; + + _noFlourCounterUnk = DAT_GameState::instance.playerDataArray[playerID].noFlourTracker; + + _noHopsCounterUnk = DAT_GameState::instance.playerDataArray[playerID].noHopsTracker; + + _noBeerCounterUnk = DAT_GameState::instance.playerDataArray[playerID].noBeerCounterUnk; + + _nerves = DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker; + + _noNerves = _nerves < 1; + + if (!_noNerves) { + + _enoughPeopleUnk = false; + + _enoughFood = false; + } + + if (_enoughGold) { + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[27] = false; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x46] = false; + + } + + else { + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x1b] = true; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x46] = true; + } + + if (_enoughPeopleUnk) { + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[20] = false; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[4] = false; + + } + + else { + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x14] = true; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[4] = true; + } + + *(bool*)(_offset + 0x115df8f) = !_enoughFood; + + *(bool*)(_offset + 0x115df91) = !_noNerves; + + *(bool*)(_offset + 0x115df92) = !_noNerves; + + *(bool*)(_offset + 0x115df98) = _nerves < 1 && (36 < _noWood || _hasArmoyUnk == 0); + + *(bool*)(_offset + 0x115df9a) = _nerves < 1 && (36 < _noWood || _hasArmoyUnk == 0); + + *(bool*)(_offset + 0x115df99) = _noNerves && (0x24 < _noIronCounterUnk || _hasArmoyUnk == 0); + + *(bool*)(_offset + 0x115df9b) = _noNerves && (0x24 < _noIronCounterUnk || _hasArmoyUnk == 0); + + *(bool*)(_offset + 0x115df9c) = _noNerves && (0x24 < _resourceCounter || _hasArmoyUnk == 0); + + *(bool*)(_offset + 0x115df9d) = !_noNerves || 0x24 < _noFlourCounterUnk; + + *(bool*)(_offset + 0x115df9e) = !_noNerves || 0x48 < _noHopsCounterUnk; + + *(bool*)(_offset + 0x115dfa2) = !_noNerves || 0x48 < _noBeerCounterUnk; + + if (_noNerves) { + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x1e] = false; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x1f] = false; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x20] = false; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x21] = false; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[7] = false; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[34] = false; + + } + + else { + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x1e] = true; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x1f] = true; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x20] = true; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x21] = true; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[7] = true; + + DAT_GameState::instance.playerDataArray[playerID].snoozedBuildings[0x22] = true; + } + + if (DAT_GameState::instance.playerDataArray[playerID].canStartSpending != 0) { + + if ((36 < DAT_GameState::instance.playerDataArray[playerID].noWoodTracker) + && (DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[2] == 0)) { + + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[2] = 5; + } + + if ((36 < DAT_GameState::instance.playerDataArray[playerID].noIronTracker) + && (DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[6] == 0)) { + + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[6] = 2; + } + + if ((36 < DAT_GameState::instance.playerDataArray[playerID].noFlourTracker) + && (DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[0x10] == 0)) { + + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[0x10] = 2; + } + + if ((72 < DAT_GameState::instance.playerDataArray[playerID].noHopsTracker) + && (DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[3] == 0)) { + + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[3] = 2; + } + } + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/playAnger1BikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playAnger1BikFromPlayer.cpp new file mode 100644 index 00000000..fed855c2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playAnger1BikFromPlayer.cpp @@ -0,0 +1,36 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" +#include "OpenSHC/Globals/INT_ARRAY_00eb0af0.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0830 + void AICState::playAnger1BikFromPlayer(int playerID) + { + int iVar2 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (iVar2 != 0) { + int _playerTeam = DAT_GameState::instance.mapAndTime.playerTeams[playerID]; + int AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + int _curPlayer = DAT_GameSynchronyState::instance.currentPlayerSlotID; + if ((_playerTeam != DAT_GameState::instance.mapAndTime.playerTeams[_curPlayer]) + && (DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID == _curPlayer)) { + DWORD DVar3 = timeGetTime(); + if ((INT_ARRAY_00eb0af0::instance[playerID] == 0) + || (DVar3 - INT_ARRAY_00eb0af0::instance[playerID] >= 0x927c0)) { + INT_ARRAY_00eb0af0::instance[playerID] = DVar3; + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, + DAT_VideoBikQueue::ptr)(playerID, AVar1, 5); + } + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playAnger2BikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playAnger2BikFromPlayerToPlayer.cpp new file mode 100644 index 00000000..dbef793f --- /dev/null +++ b/src/OpenSHC/AI/AICState/playAnger2BikFromPlayerToPlayer.cpp @@ -0,0 +1,30 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0C00 + void AICState::playAnger2BikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType != AITA_NULL) { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + bool bVar2; + if (((iVar1 != 0) && (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown3 == 0)) + && (bVar2 = targetPlayerID == DAT_GameSynchronyState::instance.currentPlayerSlotID, + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown3 = 0x60, bVar2)) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 6); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playFriendlyVictoryBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playFriendlyVictoryBikFromPlayer.cpp new file mode 100644 index 00000000..ea8497a8 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playFriendlyVictoryBikFromPlayer.cpp @@ -0,0 +1,23 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0FC0 + void AICState::playFriendlyVictoryBikFromPlayer(int playerID) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (iVar1 != 0) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 10); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playGoodsNotSentBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playGoodsNotSentBikFromPlayer.cpp new file mode 100644 index 00000000..62fd7fb6 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playGoodsNotSentBikFromPlayer.cpp @@ -0,0 +1,23 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0B70 + void AICState::playGoodsNotSentBikFromPlayer(int playerID) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (iVar1 != 0) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x1c); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playGoodsRequestBikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playGoodsRequestBikFromPlayerToPlayer.cpp new file mode 100644 index 00000000..484a7fbb --- /dev/null +++ b/src/OpenSHC/AI/AICState/playGoodsRequestBikFromPlayerToPlayer.cpp @@ -0,0 +1,27 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0A20 + void AICState::playGoodsRequestBikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (((iVar1 != 0) && (targetPlayerID == DAT_GameSynchronyState::instance.currentPlayerSlotID)) + && (DAT_GameState::instance.playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .lordKilledByPlayerID + == 0)) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0xe); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playGoodsSentBikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playGoodsSentBikFromPlayerToPlayer.cpp new file mode 100644 index 00000000..b9afb883 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playGoodsSentBikFromPlayerToPlayer.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0BB0 + void AICState::playGoodsSentBikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if ((iVar1 != 0) && (targetPlayerID == DAT_GameSynchronyState::instance.currentPlayerSlotID)) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x1d); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playJokeBikFromAIToHuman.cpp b/src/OpenSHC/AI/AICState/playJokeBikFromAIToHuman.cpp new file mode 100644 index 00000000..e6528e25 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playJokeBikFromAIToHuman.cpp @@ -0,0 +1,22 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0F90 + void AICState::playJokeBikFromAIToHuman(int playerID, int aiType) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (iVar1 != 0) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, aiType, 0x14); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playNervous1BikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playNervous1BikFromPlayer.cpp new file mode 100644 index 00000000..93c274c9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playNervous1BikFromPlayer.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0CF0 + void AICState::playNervous1BikFromPlayer(int playerID) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if ((iVar1 != 0) && (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown5 == 0)) { + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown5 = 0x30; + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 8); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playNervous2BikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playNervous2BikFromPlayer.cpp new file mode 100644 index 00000000..a2b044f2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playNervous2BikFromPlayer.cpp @@ -0,0 +1,30 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0B10 + void AICState::playNervous2BikFromPlayer(int playerID) + { + int iVar3 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + int iVar2 = DAT_GameSynchronyState::instance.currentPlayerSlotID; + if ((iVar3 != 0) && (DAT_GameState::instance.playerDataArray[playerID].nervousBikCountdown == 0)) { + int AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType; + DAT_GameState::instance.playerDataArray[playerID].nervousBikCountdown = 48; + if (DAT_GameState::instance.mapAndTime.playerTeams[playerID] + != DAT_GameState::instance.mapAndTime.playerTeams[iVar2]) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, AVar1 - 1, 9); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playNoHelpBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playNoHelpBikFromPlayer.cpp new file mode 100644 index 00000000..2a4c1101 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playNoHelpBikFromPlayer.cpp @@ -0,0 +1,23 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D1050 + void AICState::playNoHelpBikFromPlayer(int playerID) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (iVar1 != 0) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x1f); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playPlayerKilledBiksFromKilledAndKillerPlayer.cpp b/src/OpenSHC/AI/AICState/playPlayerKilledBiksFromKilledAndKillerPlayer.cpp new file mode 100644 index 00000000..b520df92 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playPlayerKilledBiksFromKilledAndKillerPlayer.cpp @@ -0,0 +1,67 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D08B0 + void AICState::playPlayerKilledBiksFromKilledAndKillerPlayer(int killerPlayerID, int killedPlayerID) + { + int iVar1 = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)( + killerPlayerID); + if (iVar1 != 0) { + int _curPlayer = DAT_GameSynchronyState::instance.currentPlayerSlotID; + if (killedPlayerID == _curPlayer) { + iVar1 = 0xc; + } else { + if (killerPlayerID == _curPlayer) { + MACRO_CALL_MEMBER( + Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killedPlayerID, (int)DAT_GameState::instance.playerDataArray[killedPlayerID].aiType - 1, 7); + int i = 1; + do { + if ((DAT_GameState::instance.playerDataArray[i].aiType != AITA_NULL) + && (DAT_GameState::instance.mapAndTime.playerTeams[i] + == DAT_GameState::instance.mapAndTime.playerTeams[_curPlayer])) { + int iVar2 = MACRO_CALL_MEMBER( + Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(i); + if (iVar2 != 0) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, + DAT_VideoBikQueue::ptr)( + i, (int)DAT_GameState::instance.playerDataArray[i].aiType - 1, 0x11); + } + } + i = i + 1; + } while (i < 9); + return; + } + int _curTeam = DAT_GameState::instance.mapAndTime.playerTeams[_curPlayer]; + if (_curTeam == DAT_GameState::instance.mapAndTime.playerTeams[killedPlayerID]) { + MACRO_CALL_MEMBER( + Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killedPlayerID, (int)DAT_GameState::instance.playerDataArray[killedPlayerID].aiType - 1, 0x10); + return; + } + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killedPlayerID, (int)DAT_GameState::instance.playerDataArray[killedPlayerID].aiType - 1, 7); + if (DAT_GameState::instance.mapAndTime.playerTeams[killerPlayerID] == _curTeam) { + MACRO_CALL_MEMBER( + Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killerPlayerID, (int)DAT_GameState::instance.playerDataArray[killerPlayerID].aiType - 1, 0x12); + return; + } + iVar1 = 0xd; + } + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killerPlayerID, (int)DAT_GameState::instance.playerDataArray[killerPlayerID].aiType - 1, iVar1); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playRequestHelpBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playRequestHelpBikFromPlayer.cpp new file mode 100644 index 00000000..3be00819 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playRequestHelpBikFromPlayer.cpp @@ -0,0 +1,30 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0D40 + void AICState::playRequestHelpBikFromPlayer(int playerID) + { + int iVar2 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if ((iVar2 != 0) && (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown6 == 0)) { + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown6 = 0x30; + int iVar1 = DAT_GameSynchronyState::instance.currentPlayerSlotID; + if ((DAT_GameState::instance.mapAndTime.playerTeams[playerID] + == DAT_GameState::instance.mapAndTime.playerTeams[iVar1]) + && (DAT_GameState::instance.playerDataArray[iVar1].lordKilledByPlayerID == 0)) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x13); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playTauntToAttackedPlayerBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playTauntToAttackedPlayerBikFromPlayer.cpp new file mode 100644 index 00000000..cc48af4e --- /dev/null +++ b/src/OpenSHC/AI/AICState/playTauntToAttackedPlayerBikFromPlayer.cpp @@ -0,0 +1,38 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D07C0 + void AICState::playTauntToAttackedPlayerBikFromPlayer(int playerID) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (iVar1 != 0) { + int _aiTypeMinusOne = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + int _myTeam = DAT_GameState::instance.mapAndTime.playerTeams[playerID]; + int _curPlayer = DAT_GameSynchronyState::instance.currentPlayerSlotID; + int _bik = 0; + if (_myTeam == DAT_GameState::instance.mapAndTime.playerTeams[_curPlayer]) { + _bik = 0x17; + } + int _attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + if (_bik == 0) { + _bik = ((byte)SEC_RNG::instance.currentNumber2 & 3) + 1; + } + if (_attackedPlayerID == _curPlayer) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, _aiTypeMinusOne, _bik); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playTeamWinningBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playTeamWinningBikFromPlayer.cpp new file mode 100644 index 00000000..02745d87 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playTeamWinningBikFromPlayer.cpp @@ -0,0 +1,24 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D1000 + void AICState::playTeamWinningBikFromPlayer(int playerID) + { + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (((iVar1 != 0) && (DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker == 0)) + && (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker == 0)) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x1e); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playThanksBikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playThanksBikFromPlayerToPlayer.cpp new file mode 100644 index 00000000..110bb938 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playThanksBikFromPlayerToPlayer.cpp @@ -0,0 +1,36 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" +#include "OpenSHC/Globals/DWORD_00eb0b18.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0A80 + void AICState::playThanksBikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + DWORD DVar2; + int iVar1 + = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID); + if (iVar1 != 0) { + iVar1 = (int)DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + DAT_GameState::instance.playerDataArray[playerID].aiRequestDelay = this->aics[iVar1].aiRequestDelay; + if (((targetPlayerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) + && (DAT_GameState::instance.playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .lordKilledByPlayerID + == 0)) + && ((DVar2 = timeGetTime(), + DWORD_00eb0b18::instance == 0 || (1199999 < DVar2 - DWORD_00eb0b18::instance)))) { + DWORD_00eb0b18::instance = DVar2; + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, iVar1, 0xf); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/playVictory2BikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playVictory2BikFromPlayerToPlayer.cpp new file mode 100644 index 00000000..97437bc3 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playVictory2BikFromPlayerToPlayer.cpp @@ -0,0 +1,34 @@ +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" +#include "OpenSHC/Globals/DAT_VideoBikQueue.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0C70 + void AICState::playVictory2BikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + int iVar1; + bool bVar2; + if ((((DAT_GameState::instance.playerDataArray[targetPlayerID].aiType != AITA_NULL) + && (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID] != -1)) + && (iVar1 = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)( + targetPlayerID), + iVar1 != 0)) + && (((iVar1 = MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)( + playerID), + iVar1 != 0 && (DAT_GameState::instance.playerDataArray[targetPlayerID].someAiCountdown4 == 0)) + && (bVar2 = playerID == DAT_GameSynchronyState::instance.currentPlayerSlotID, + DAT_GameState::instance.playerDataArray[targetPlayerID].someAiCountdown4 = 0x30, bVar2)))) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + targetPlayerID, (int)DAT_GameState::instance.playerDataArray[targetPlayerID].aiType - 1, 0xb); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/processAttForceRallyPercentage.cpp b/src/OpenSHC/AI/AICState/processAttForceRallyPercentage.cpp new file mode 100644 index 00000000..b53a16f1 --- /dev/null +++ b/src/OpenSHC/AI/AICState/processAttForceRallyPercentage.cpp @@ -0,0 +1,79 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CEEA0 + BOOLEnum AICState ::processAttForceRallyPercentage(int playerID) + + { + AITypeInt AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (AVar1 == OpenSHC::AI::AIT_NULL) + return FALSE; + + AITypeInt _aiTypeMinus1 = AVar1 - 1; + int _tribeTotal = 0; + int _notReallyMoving = 0; + + int* local_c = &DAT_SkirmishDefinedData::instance.MaxAttackTribes1[0].tribeCount; + + do { + int iVar2 = (*(int (*)[2])(local_c + -1))[0]; + int iVar3 = *local_c; + + if (iVar2 != 0x12 && iVar2 != 0xbe && iVar2 != 0xf) { + int iVar5 = 0; + if (0 < iVar3) { + short* psVar4 = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + iVar2; + + do { + int _tribeID = (int)*psVar4; + + if (_tribeID != 0 + && DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[iVar5 + iVar2]) { + + _tribeTotal = _tribeTotal + 1; + + if (DAT_TribesState::instance.tribes[_tribeID].percentageMovingUnk < 0x14) + _notReallyMoving = _notReallyMoving + 1; + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_STAND_GROUND; + } + + iVar5 = iVar5 + 1; + psVar4 = psVar4 + 1; + + } while (iVar5 < iVar3); + } + } + + local_c = local_c + 2; + + } while ((int)local_c < 0xb42a2c); + + if (0 < _tribeTotal) { + return ( + BOOLEnum)((_notReallyMoving * 100) / _tribeTotal >= *(int*)((int)this + _aiTypeMinus1 * 0x2a4 + 0x200)); + } + + return FALSE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/randomlySelectAttackUnitTypeToRecruit.cpp b/src/OpenSHC/AI/AICState/randomlySelectAttackUnitTypeToRecruit.cpp new file mode 100644 index 00000000..0fc1d175 --- /dev/null +++ b/src/OpenSHC/AI/AICState/randomlySelectAttackUnitTypeToRecruit.cpp @@ -0,0 +1,233 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/AI/AIUnitBehaviourType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::AI::AIUnitBehaviourType; + + /* + doc: + Determines which AI unit behaviour groups need recruiting because AIC Max units hasn't been + reached for that category, then randomly selects one of those groups to recruit + + @returns AiUnitBehaviourType The selected unit behaviour type + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC070 + AIUnitBehaviourType AICState ::randomlySelectAttackUnitTypeToRecruit(int playerID) + + { + + int* piVar1; + + dword _engineersCount; + + int _index2; + + int _rng; + + int _index; + + int _aiTypeMin1; + + dword _maxDiggingUnits; + + dword _maxAssassins; + + dword _maxUnit2; + + dword _maxLaddermen; + + dword _maxTunnelers; + + dword _maxUnitPatrol; + + AITypeInt _aiType; + + int _attackedPlayerID; + + dword _maxEngineers; + + _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (_aiType == OpenSHC::AI::AIT_NULL) { + + return OpenSHC::AI::AIUBT_ATTUNITMAIN; + } + + _attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + + _index = 0; + + /* + clear out flags */ + + piVar1 = &DAT_SkirmishDefinedData::instance.attackUnitRequired[0].required; + + do { + + *piVar1 = 0; + + piVar1 = piVar1 + 2; + + } while ((int)piVar1 < 0xb425ec); + + _maxEngineers = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttMaxEngineers; + + _engineersCount = DAT_GameState::instance.playerDataArray[playerID].currentAttackWave * 4; + + if ((int)_maxEngineers < (int)_engineersCount) { + + _engineersCount = _maxEngineers; + } + + if ((_maxEngineers != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalAttackingEngineerTroops + < (int)_engineersCount)) { + + _index = 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[0].required = 1; + } + + _maxDiggingUnits = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttDiggingUnitMax; + + if (((_maxDiggingUnits != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalDiggingUnitTroops < (int)_maxDiggingUnits)) + && (5 < DAT_GameState::instance.playerDataArray[_attackedPlayerID].moatsOwned)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[1].required = 1; + } + + _maxAssassins = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttMaxAssassins; + + if ((_maxAssassins != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalAssassinTroops < (int)_maxAssassins)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[2].required = 1; + } + + _maxUnit2 = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttUnit2Max; + + if ((_maxUnit2 != 0) && (DAT_GameState::instance.playerDataArray[playerID].totalUnit2Troops < (int)_maxUnit2)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[3].required = 1; + } + + _maxLaddermen = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttMaxLaddermen; + + if ((_maxLaddermen != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalLaddermenTroops < (int)_maxLaddermen)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[4].required = 1; + } + + _maxTunnelers = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttMaxTunnelers; + + if ((_maxTunnelers != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalTunnelerTroops < (int)_maxTunnelers)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[5].required = 1; + } + + _maxUnitPatrol = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttUnitPatrolMax; + + if ((_maxUnitPatrol != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalUnitPatrolTroops < (int)_maxUnitPatrol)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[6].required = 1; + } + + _maxEngineers = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttUnitBackupMax; + + if ((_maxEngineers != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops < (int)_maxEngineers)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[7].required = 1; + } + + _maxEngineers = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttUnitEngageMax; + + if ((_maxEngineers != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalUnitEngageTroops < (int)_maxEngineers)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[8].required = 1; + } + + _maxEngineers = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttUnitSiegeDefMax; + + if ((_maxEngineers != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalUnitSiegeDefTroops < (int)_maxEngineers)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[9].required = 1; + } + + _maxEngineers = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].AttMaxDefault; + + if ((_maxEngineers != 0) + && (DAT_GameState::instance.playerDataArray[playerID].totalMaxDefaultTroops < (int)_maxEngineers)) { + + _index = _index + 1; + + DAT_SkirmishDefinedData::instance.attackUnitRequired[10].required = 1; + } + + if (_index != 0) { + + _rng = (int)SEC_RNG::instance.currentNumber2 % _index; + + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + + _index2 = 0; + + do { + + if (DAT_SkirmishDefinedData::instance.attackUnitRequired[_index2].required != 0) { + + if (_rng == 0) { + + return (AIUnitBehaviourType)(DAT_SkirmishDefinedData::instance.attackUnitRequired[_index2] + .unitBehaviourType); + } + + _rng = _rng + -1; + } + + _index2 = _index2 + 1; + + } while (_index2 < 0xb); + } + + return OpenSHC::AI::AIUBT_ATTUNITMAIN; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/recomputeAIPathCostToAttackedKeep.cpp b/src/OpenSHC/AI/AICState/recomputeAIPathCostToAttackedKeep.cpp new file mode 100644 index 00000000..3964e391 --- /dev/null +++ b/src/OpenSHC/AI/AICState/recomputeAIPathCostToAttackedKeep.cpp @@ -0,0 +1,93 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE040 + void AICState ::recomputeAIPathCostToAttackedKeep(int playerID, int useBorderDistance) + + { + + BOOLEnum BVar1; + + uint _borderDistance; + + bool _cannotNavigateToKeep; + + int _attackedPlayerID; + + int _bd; + + ushort _keepArea; + + _attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + + _keepArea = DAT_TileMapState::instance + .PathConnectionLayer[DAT_GameState::instance.playerDataArray[playerID].campground.tileEntry]; + + _bd = DAT_GameState::instance.playerDataArray[_attackedPlayerID].unknownPathBorderDistanceRelated; + + if (_bd < 120) { + + _borderDistance = ((_bd < 90) - 1 & 20) + 50; + + } + + else { + + _borderDistance = 98; + } + + if (useBorderDistance == 0) { + + _borderDistance = 0; + } + + *(int*)((int)DAT_TroopValueState::instance.attackInfo.hackValuesArray + playerID * 0x177bc + -0x10) + = _attackedPlayerID; + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 80400, '\0', (void*)((int)(DAT_TileMapState::instance.AIZoneLayer))); + + BVar1 = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, + DAT_GameState::ptr)(playerID, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID); + + _cannotNavigateToKeep = BVar1 == FALSE; + + if (_cannotNavigateToKeep) { + + _attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + + } + + else { + + _attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::computeAIZoneLayer, + DAT_PathFindingState::ptr)(_attackedPlayerID, (int)((int)((uint)!_cannotNavigateToKeep))); + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkLayerAndAIPathCostLayer, + DAT_PathFindingState::ptr)(110, _borderDistance, (dword)((int)((int)(short)_keepArea)), playerID); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/recomputeAIZonerLayer.cpp b/src/OpenSHC/AI/AICState/recomputeAIZonerLayer.cpp new file mode 100644 index 00000000..199d93ec --- /dev/null +++ b/src/OpenSHC/AI/AICState/recomputeAIZonerLayer.cpp @@ -0,0 +1,42 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE1A0 + void AICState ::recomputeAIZonerLayer() + + { + + DAT_TroopValueState::instance.attackInfo.playerInfo[0].currentPlayerID_OR_attackedPlayerID_OR_one = 1; + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 80400, '\0', (void*)((int)(DAT_TileMapState::instance.AIZoneLayer))); + + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::computeAIZoneLayer, DAT_PathFindingState::ptr)(1, 2); + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkLayerAndAIPathCostLayer, + DAT_PathFindingState::ptr)(252, 0, 1, 1); + + DAT_GameState::instance.playerDataArray[1].unknownPathBorderDistanceRelated + = DAT_PathFindingState::instance.distance; + + DAT_TroopValueState::instance.attackInfo.playerInfo[0].currentPlayerID_OR_attackedPlayerID_OR_one = 1; + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/recomputeAttackAIZone.cpp b/src/OpenSHC/AI/AICState/recomputeAttackAIZone.cpp new file mode 100644 index 00000000..3396df9e --- /dev/null +++ b/src/OpenSHC/AI/AICState/recomputeAttackAIZone.cpp @@ -0,0 +1,71 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/AttackInfoSubElement.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Map::AttackInfoSubElement; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE110 + void AICState ::recomputeAttackAIZone() + + { + + int* _pBorderDistance; + + int _playerID; + + AttackInfoSubElement* _pi; + + int _fullID; + + _playerID = 1; + + _pi = DAT_TroopValueState::instance.attackInfo.playerInfo; + + _pBorderDistance = &DAT_GameState::instance.playerDataArray[1].unknownPathBorderDistanceRelated; + + do { + + _fullID = DAT_GameSynchronyState::instance.currentPlayerFullIDArray[_playerID]; + + _pi->currentPlayerID_OR_attackedPlayerID_OR_one = _playerID; + + if ((_fullID != -1) || (DAT_GameSynchronyState::instance.currentAIArray[_playerID] != 0)) { + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 80400, '\0', (void*)((int)(DAT_TileMapState::instance.AIZoneLayer))); + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::computeAIZoneLayer, + DAT_PathFindingState::ptr)(_playerID, 2); + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkLayerAndAIPathCostLayer, + DAT_PathFindingState::ptr)(252, 0, 1, _playerID); + + *_pBorderDistance = DAT_PathFindingState::instance.distance; + + _pi->currentPlayerID_OR_attackedPlayerID_OR_one = 1; + } + + _playerID = _playerID + 1; + + _pBorderDistance = _pBorderDistance + 0xe7d; + + _pi = _pi + 1; + + } while (_playerID < 9); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/recreateEngineersTribe.cpp b/src/OpenSHC/AI/AICState/recreateEngineersTribe.cpp new file mode 100644 index 00000000..78ed1689 --- /dev/null +++ b/src/OpenSHC/AI/AICState/recreateEngineersTribe.cpp @@ -0,0 +1,23 @@ +#include "OpenSHC/AI/AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4630 + void AICState::recreateEngineersTribe(int playerID) + { + if (0 < DAT_GameState::instance.playerDataArray[playerID].totalAttackingEngineerTroops) { + int _tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0x12]; + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0x12])) { + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0x12] = 0; + } + MACRO_CALL_MEMBER(AICState_Func::addEngineersToTribe, this)(playerID); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/recruitHarrassingSiegeEngines.cpp b/src/OpenSHC/AI/AICState/recruitHarrassingSiegeEngines.cpp new file mode 100644 index 00000000..f497eabd --- /dev/null +++ b/src/OpenSHC/AI/AICState/recruitHarrassingSiegeEngines.cpp @@ -0,0 +1,253 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIVState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/Tribes/AITribeType.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" +#include "OpenSHC/Map/Units/Unit.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::Tribes::AITribeType; + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Units::SomeTribeBehaviorType; + using OpenSHC::Map::Units::Unit; + using OpenSHC::Map::Units::UnitInstructionType; + using OpenSHC::Map::Units::UnitLogicState; + using OpenSHC::Map::Units::UnitType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD2A0 + void AICState ::recruitHarrassingSiegeEngines(int playerID) + + { + + uint _unitID; + + int iVar1; + + Unit* pUVar4; + + int _unit; + + int _tribeID; + + int _harrassingSiegeEnginesMax; + + int _recruitTimeoutValue; + + int _engineersGuild; + + int _aiType; + + int _tribeUID; + + int _buildingID; + + int _aicEnginesArrayIndex; + + MappersEnum _siegeEngineToBuild; + + _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + _engineersGuild = DAT_GameState::instance.playerDataArray[playerID].engineersGuild.id; + + if ((_aiType != 0) && (499 < DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf])) { + + _harrassingSiegeEnginesMax = *(int*)((int)this + (_aiType + -1) * 0x2a4 + 0x1ec); + + if (0 < _harrassingSiegeEnginesMax) { + + _recruitTimeoutValue = DAT_GameState::instance.playerDataArray[playerID].harrasingEngineRecruitTimeout; + + if (_recruitTimeoutValue != 0) { + + DAT_GameState::instance.playerDataArray[playerID].harrasingEngineRecruitTimeout + = _recruitTimeoutValue + -1; + + return; + } + + if ((DAT_GameState::instance.playerDataArray[playerID].harassingSiegeEnginesCountUnk + < _harrassingSiegeEnginesMax) + && (DAT_GameState::instance.playerDataArray[playerID].unknownHarrassingSiegeRelated < 0x14)) { + + _tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xa5]; + + if ((_tribeID == 0) + || ((DAT_TribesState::instance.tribes[_tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xa5] + || (DAT_TribesState::instance.tribes[_tribeID].size < 2)))) { + + if (DAT_GameState::instance.playerDataArray[playerID].canStartSpending == 0) { + + return; + } + + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf] < 30) { + + return; + } + + if (_engineersGuild < 1) { + + return; + } + + if (DAT_BuildingsState::instance.buildings[_engineersGuild].buildingType + != OpenSHC::Map::Buildings::BT_ENGINEERSGUILD) { + + return; + } + + _unitID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::UnitsState_Func::nonEuroRecruit, DAT_UnitsState::ptr)( + OpenSHC::Map::Units::UT_E_ENGINEER, (undefined4)((int)(_engineersGuild)), playerID, 0); + + if (_unitID == 0) { + + return; + } + + if ((_tribeID == 0) + || (DAT_TribesState::instance.tribes[_tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xa5])) { + + _tribeID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::createTribeForPlayer, + DAT_TribesState::ptr)(playerID); + } + + _tribeUID = DAT_TribesState::instance.tribes[_tribeID].uid; + + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xa5] = (short)_tribeID; + + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xa5] = _tribeUID; + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + _unitID, _tribeID); + + DAT_UnitsState::instance.units[_unitID].aiUnitBehaviourType = 0x16; + + } + + else { + + if (7 < DAT_GameState::instance.playerDataArray[playerID].harrassingSiegeEnginesIndex) { + + DAT_GameState::instance.playerDataArray[playerID].harrassingSiegeEnginesIndex = 0; + } + + iVar1 = (_aiType + -1) * 0xa9; + + if (*(int*)((int)this + + (DAT_GameState::instance.playerDataArray[playerID].harrassingSiegeEnginesIndex + + iVar1) + * 4 + + 0x1cc) + == 0) { + + DAT_GameState::instance.playerDataArray[playerID].harrassingSiegeEnginesIndex = 0; + } + + _aicEnginesArrayIndex + = DAT_GameState::instance.playerDataArray[playerID].harrassingSiegeEnginesIndex; + + _siegeEngineToBuild = *(MappersEnum*)((int)this + (iVar1 + _aicEnginesArrayIndex) * 4 + 0x1cc); + + DAT_GameState::instance.playerDataArray[playerID].harrassingSiegeEnginesIndex + = _aicEnginesArrayIndex + 1; + + iVar1 = MACRO_CALL_MEMBER( + OpenSHC::AI::AIVState_Func::findSpotNearEngineersGuild, DAT_AIVState::ptr)(playerID); + + if (iVar1 == 0) { + + return; + } + + iVar1 = DAT_AIVState::instance.buildingAppropriateGridYPosition * 5; + + _aicEnginesArrayIndex = DAT_AIVState::instance.buildingAppropriateGridYPosition * 5; + + DAT_TribesState::instance.tribes[_tribeID].tribeType = OpenSHC::AI::Tribes::AITT_ENGINEERS; + + DAT_TribesState::instance.tribes[_tribeID].tribeBehaviorType + = OpenSHC::Map::Units::STBT_0x410_SIEGE_EQUIPMENT_CONSTRUCTION; + + /* + palce siege engine tent? */ + + MACRO_CALL_MEMBER(OpenSHC::Map::TileMapState_Func::placeBuilding, DAT_TileMapState::ptr)( + playerID, _aicEnginesArrayIndex, iVar1, _siegeEngineToBuild, 3, 0xf); + + _buildingID = DAT_TileMapState::instance.placedBuildingID; + + if (DAT_TileMapState::instance.buildingPlacementFail != FALSE) { + + return; + } + + DAT_BuildingsState::instance.buildings[DAT_TileMapState::instance.placedBuildingID].attackWave + = 0; + + DAT_BuildingsState::instance.buildings[_buildingID].unknownSiegeTentRelated01 = 2; + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeAnInstruction, + DAT_TribesState::ptr)(_tribeID, + OpenSHC::Map::Units::UIT_CONSTRUCT_SIEGE_EQUIPMENTOIL_DUTYENGINEERRELATED, _buildingID, + DAT_BuildingsState::instance.buildings[_buildingID].uid, 0); + } + + _unit = 1; + + if (1 < (int)DAT_UnitsState::instance.maxUnitCount) { + + pUVar4 = &DAT_UnitsState::instance.units[1]; + + do { + + if ((((pUVar4->logicalState != OpenSHC::Map::Units::ULS_INVISIBLE) && (pUVar4->dying == 0)) + && ((pUVar4->unitType == OpenSHC::Map::Units::UT_S_CATAPULT + || (pUVar4->unitType == OpenSHC::Map::Units::UT_S_FBALLISTA)))) + && (pUVar4->aiUnitBehaviourType == 0x15)) { + + pUVar4->siegeTargetPlayerID + = (short)DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + } + + _unit = _unit + 1; + + pUVar4 = pUVar4 + 0x248; + + } while (_unit < (int)DAT_UnitsState::instance.maxUnitCount); + } + + DAT_GameState::instance.playerDataArray[playerID].harrasingEngineRecruitTimeout = 8; + } + } + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/removeOrganismsAndSetMoveDestinationPairs.cpp b/src/OpenSHC/AI/AICState/removeOrganismsAndSetMoveDestinationPairs.cpp new file mode 100644 index 00000000..34db026d --- /dev/null +++ b/src/OpenSHC/AI/AICState/removeOrganismsAndSetMoveDestinationPairs.cpp @@ -0,0 +1,66 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE430 + void AICState ::removeOrganismsAndSetMoveDestinationPairs(int playerID) + + { + + AITypeInt _aiType; + + if ((short)DAT_TileMapState::instance.OrganismLayer + [DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY] + .addXgetTile + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX] + != 0) { + + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeTree, DAT_LandscapeState::ptr)( + (int)(short)DAT_TileMapState::instance.OrganismLayer + [DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY] + .addXgetTile + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX]); + } + + _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + /* + bug: special hardcoded exception for AI type 8 */ + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findLinkageBasedPathOrWalkRadius, + DAT_PathFindingState::ptr)(DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX, + (uint)((int)(DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY)), -1, -1, 10000, FALSE); + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::setMoveDestinationPairs, + DAT_PathFindingState::ptr)(playerID + -1, + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID, + (int)((int)(*(int*)(DAT_TroopValueState::instance.attackInfo.scaleValuesArray + playerID * 0x177bc + -0x1c) + + (-(uint)(_aiType != OpenSHC::AI::AIT_SULTAN) & 0xc) + 8))); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/renderDebugDataAiInfo.cpp b/src/OpenSHC/AI/AICState/renderDebugDataAiInfo.cpp new file mode 100644 index 00000000..46687d25 --- /dev/null +++ b/src/OpenSHC/AI/AICState/renderDebugDataAiInfo.cpp @@ -0,0 +1,248 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/OS.func.hpp" +#include "OpenSHC/Text/TextManager.func.hpp" +#include "OpenSHC/AI/AIC/AICSpecification.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AIStrengthType.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" +#include "OpenSHC/Text/TextAlignment.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_BlendingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_RenderingDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TextManagerObject.hpp" +#include "OpenSHC/Globals/MSVC_SecurityCookie.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AICState; + using OpenSHC::AI::AIStrengthType; + using OpenSHC::AI::AIType; + using OpenSHC::AI::AIC::AICSpecification; + using OpenSHC::Game::Player::PlayerData; + using OpenSHC::Text::TextAlignment; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names */ + + /* + WARNING: Enum "DPERRInt": Some values do not have unique names */ + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D1320 + void AICState ::renderDebugDataAiInfo(int x, int y, int width, int height) + + { + + uint foregroundColor; + + int iVar1; + + PlayerData* _playerData; + + int yParam; + + char* pcVar2; + + uint foregroundColor_00; + + char (*pPlayerNames)[250]; + + int _aiTypeMin1; + + AICSpecification* local_3f4; + + AICState* local_3f0; + + char local_3ec[1000]; + + uint local_4; + + int _playerID; + + local_4 = MSVC_SecurityCookie::instance ^ (uint)&_playerID; + + local_3f0 = this; + + _playerID = 1; + + _playerData = &DAT_GameState::instance.playerDataArray[1]; + + pPlayerNames = DAT_GameSynchronyState::instance.DAT_PlayerNames + 1; + + do { + + yParam = y + 0x18; + + if (((_playerData->playerDeathRelated == 0) && (_playerData->lordKilledByPlayerID == 0)) + && ((_playerData->aiType != OpenSHC::AI::AIT_NULL + || (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[_playerID] != -1)))) { + + /* + aiType - 1 */ + + _aiTypeMin1 = _playerData->aiType + ~OpenSHC::AI::AIT_NULL; + + foregroundColor + = DAT_RenderingDefinedData::instance + .DAT_ColorArray[DAT_BlendingDefinedData::instance.SEC_PlayerSlotUnitColor[_playerID]]; + + MACRO_CALL_MEMBER(OpenSHC::Text::TextManager_Func::renderNumberToScreen2, DAT_TextManagerObject::ptr)( + _playerID, x + 6, yParam, OpenSHC::Text::TTA_LEFT, 0xc2f0eb, 0x12, FALSE, 0); + + MACRO_CALL_MEMBER(OpenSHC::Text::TextManager_Func::renderTextToScreen, DAT_TextManagerObject::ptr)( + "-", x + 0x10, yParam, OpenSHC::Text::TTA_LEFT, 0xc2f0eb, 0x12, FALSE, 0); + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + *pPlayerNames, x + 0x1a, y + 0x19, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, FALSE, 0); + + if (_playerData->aiStrengthFeeling == OpenSHC::AI::AIST_WEAK) { + + pcVar2 = "Weak"; + + } + + else if (_playerData->aiStrengthFeeling == OpenSHC::AI::AIST_DEFAULT) { + + pcVar2 = "Normal"; + + } + + else { + + pcVar2 = "Strong"; + } + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + pcVar2, x + 0xca, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, FALSE, 0); + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + "P:", x + 0x106, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, FALSE, 0); + + MACRO_CALL_MEMBER(OpenSHC::Text::TextManager_Func::renderNumber2, DAT_TextManagerObject::ptr)( + _playerData->aivCurrentPause, x + 0x108, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, + TRUE, 0); + + MACRO_CALL(OpenSHC::OS_Func::_sprintf)( + local_3ec, "Atk:%d W:%d", _playerData->attackedPlayerID, _playerData->aiAttackCoordinationLevel); + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + local_3ec, x + 0x12e, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, FALSE, 0); + + if (_playerData->aiType == OpenSHC::AI::AIT_NULL) { + + iVar1 = DAT_AIVState::instance.aivs[_playerData->aivID].castleID; + + pcVar2 = "AIV:n/a"; + + } + + else { + + iVar1 = DAT_AIVState::instance.aivs[_playerData->aivID].castleID; + + pcVar2 = "AIV: %d"; + } + + MACRO_CALL(OpenSHC::OS_Func::_sprintf)(local_3ec, (char const*)((int)(pcVar2)), iVar1 + 1); + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + local_3ec, x + 0x17e, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, FALSE, 0); + + local_3f4 = local_3f0->aics + _aiTypeMin1; + + /* + total engineers? */ + + MACRO_CALL(OpenSHC::OS_Func::_sprintf)(local_3ec, "E:%d/%d", _playerData->totalTroopsType6, + _playerData->unknownCounter_01 + local_3f0->aics[_aiTypeMin1].SortieUnitRangedMin); + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + local_3ec, x + 0x1c4, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, FALSE, 0); + + /* + total attack troops - engineer troops */ + + iVar1 = _playerData->totalAttackTroops - _playerData->totalAttackingEngineerTroops; + + /* + ai player state == attacking */ + + if (_playerData->aiPlayerState == 0) { + + /* + AttForceBase + currentWaveRandomAttackingStrength */ + + MACRO_CALL(OpenSHC::OS_Func::_sprintf)(local_3ec, "Sg:%d/%d", iVar1, + _playerData->currentWaveRandomAttackingStrength + local_3f4->AttForceBase); + + foregroundColor_00 = foregroundColor; + + } + + else { + + MACRO_CALL(OpenSHC::OS_Func::_sprintf)(local_3ec, "%d/%d", iVar1, _playerData->attackTicker); + + foregroundColor_00 = 0xffffff; + } + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + local_3ec, x + 0x200, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor_00, 0, 0x12, FALSE, 0); + + iVar1 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::getCurrentDesiredAttackRaidUnitCount, local_3f0)( + _aiTypeMin1, _playerID); + + MACRO_CALL(OpenSHC::OS_Func::_sprintf)( + local_3ec, "Hr:%d/%d", _playerData->totalRaidingTroopsUnk, iVar1); + + /* + display raid/harassing units */ + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + local_3ec, x + 0x250, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, FALSE, 0); + + /* + TotalDefensiveTroops / DefWalls */ + + MACRO_CALL(OpenSHC::OS_Func::_sprintf)( + local_3ec, "Df:%d/%d", _playerData->totalDefensiveTroopsUnk, local_3f4->DefWalls); + + MACRO_CALL_MEMBER( + OpenSHC::Text::TextManager_Func::renderInGameTextWithShadow, DAT_TextManagerObject::ptr)( + local_3ec, x + 0x291, yParam, OpenSHC::Text::TTA_LEFT, foregroundColor, 0, 0x12, FALSE, 0); + } + + pPlayerNames = pPlayerNames + 1; + + _playerID = _playerID + 1; + + _playerData = _playerData + 0xe7d; + + y = yParam; + + } while (_playerID < 9); + + ; + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/requestGoods.cpp b/src/OpenSHC/AI/AICState/requestGoods.cpp new file mode 100644 index 00000000..20c9e32d --- /dev/null +++ b/src/OpenSHC/AI/AICState/requestGoods.cpp @@ -0,0 +1,70 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Game/Resources/ResourceType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Game::Resources::ResourceType; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D1F00 + void AICState ::requestGoods(int playerID, ResourceType resourceType, int amount) + + { + + if ((((((DAT_GameState::instance.playerDataArray[playerID].aiType != OpenSHC::AI::AIT_NULL) + && (DAT_GameState::instance.playerDataArray[playerID].aiRequestDelay == 0)) + && (resourceType != OpenSHC::Game::Resources::RT_WHEAT)) + && ((resourceType != OpenSHC::Game::Resources::RT_HOPS + && (resourceType != OpenSHC::Game::Resources::RT_ALE)))) + && ((((resourceType != OpenSHC::Game::Resources::RT_BREAD + && ((resourceType != OpenSHC::Game::Resources::RT_CHEESE + && (resourceType != OpenSHC::Game::Resources::RT_MEAT)))) + && (resourceType != OpenSHC::Game::Resources::RT_APPLE)) + || (DAT_GameState::instance.playerDataArray[playerID].totalFood < 5)))) + && (((((resourceType != OpenSHC::Game::Resources::RT_BREAD + && (resourceType != OpenSHC::Game::Resources::RT_CHEESE)) + && (resourceType != OpenSHC::Game::Resources::RT_MEAT)) + && (resourceType != OpenSHC::Game::Resources::RT_APPLE)) + || (DAT_GameState::instance.playerDataArray[playerID].totalFood < 5)))) { + + int iVar1; + int _playerID = 1; + + short* psVar2 = DAT_GameState::instance.playerDataArray[1].requestedGoodsArray1Unk + playerID; + + do { + + if (((DAT_GameSynchronyState::instance.currentPlayerFullIDArray[_playerID] != -1) + && (DAT_GameState::instance.mapAndTime.playerTeams[_playerID] + == DAT_GameState::instance.mapAndTime.playerTeams[playerID])) + && ((iVar1 = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(_playerID), + iVar1 != 0 && (*psVar2 == 0)))) { + + *psVar2 = (short)resourceType; + + psVar2[10] = (short)amount; + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playGoodsRequestBikFromPlayerToPlayer, this)( + playerID, _playerID); + } + + _playerID = _playerID + 1; + + psVar2 = psVar2 + 0x1cfa; + + } while (_playerID < 9); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/selectAttackTarget.cpp b/src/OpenSHC/AI/AICState/selectAttackTarget.cpp new file mode 100644 index 00000000..be79bfac --- /dev/null +++ b/src/OpenSHC/AI/AICState/selectAttackTarget.cpp @@ -0,0 +1,147 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" +#include "OpenSHC/AI/AIC/AITargetChoiceInt.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::AI::AIC::AITargetChoice; + using OpenSHC::AI::AIC::AITargetChoiceInt; + using OpenSHC::Game::Player::PlayerData; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4680 + void AICState ::selectAttackTarget(int playerID) + { + AITypeInt _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + int _selectedPlayer = 0; + + if (_aiType != OpenSHC::AI::AIT_NULL) { + int fromXPosition = DAT_GameState::instance.playerDataArray[playerID].campground.xEntry; + int fromYPosition = DAT_GameState::instance.playerDataArray[playerID].campground.yEntry; + int _lowestDistance = 10000; + int _highestGold = 0; + int _lowestPoints = 1000000; + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeNervousness, this)(playerID); + + int _reqState = DAT_GameState::instance.playerDataArray[playerID].requestStateUnk; + if (((_reqState == 2) || (_reqState == 1)) + && MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(DAT_GameState::instance.playerDataArray[playerID].playerID_askerUnk) == 0) { + DAT_GameState::instance.playerDataArray[playerID].requestStateUnk = 0; + } + + int _reqState2 = DAT_GameState::instance.playerDataArray[playerID].requestStateUnk; + if (_reqState2 == 2) { + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID = 0; + return; + } + if (_reqState2 == 1) { + if (MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(DAT_GameState::instance.playerDataArray[playerID].requestedAttackTargetUnk) != 0) { + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID + = DAT_GameState::instance.playerDataArray[playerID].requestedAttackTargetUnk; + return; + } + DAT_GameState::instance.playerDataArray[playerID].requestStateUnk = 0; + } + + PlayerData* _pPlayerData2 = (PlayerData*)(&DAT_GameState::instance.playerDataArray[1]); + int _iPlayer = 1; + + do { + if ((DAT_GameState::instance.mapAndTime.playerTeams[_iPlayer] + != DAT_GameState::instance.mapAndTime.playerTeams[playerID]) + && MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(_iPlayer) != 0) { + + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)((_pPlayerData2->campground).xEntry, + (_pPlayerData2->campground).yEntry, fromXPosition, fromYPosition); + + AITargetChoiceInt _targetChoiceType = this->aics[_aiType + ~OpenSHC::AI::AIT_NULL].TargetChoice; + int _currentGoldValue = _highestGold; + int _points = _lowestPoints; + + if (_targetChoiceType == OpenSHC::AI::AIC::AITC_PLAYER) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[_iPlayer] != -1) { + if (DAT_DirectionAlgorithmState::instance.distanceHigh <= _lowestDistance) { + _lowestDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + _lowestPoints = _points; + _highestGold = _currentGoldValue; + _selectedPlayer = _iPlayer; + } + } + } else if (_targetChoiceType == OpenSHC::AI::AIC::AITC_CLOSEST) { + if (DAT_DirectionAlgorithmState::instance.distanceHigh <= _lowestDistance) { + _lowestDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + _lowestPoints = _points; + _highestGold = _currentGoldValue; + _selectedPlayer = _iPlayer; + } + } else if (_targetChoiceType == OpenSHC::AI::AIC::AITC_GOLD) { + _currentGoldValue = _pPlayerData2->currentResources[0xf]; + if (_highestGold <= _pPlayerData2->currentResources[0xf]) { + _lowestPoints = _points; + _highestGold = _currentGoldValue; + _selectedPlayer = _iPlayer; + } + } else if (_targetChoiceType == OpenSHC::AI::AIC::AITC_BALANCED) { + _points = _pPlayerData2->currentPopulation * 5 + _pPlayerData2->totalTroopValue + + _pPlayerData2->currentResources[0xf] / 100 + + DAT_DirectionAlgorithmState::instance.distanceHigh * 2; + if (_points <= _lowestPoints) { + _lowestPoints = _points; + _highestGold = _currentGoldValue; + _selectedPlayer = _iPlayer; + } + } + } + + _iPlayer = _iPlayer + 1; + _pPlayerData2 = _pPlayerData2 + 0xe7d; + } while (_iPlayer < 9); + + if (_selectedPlayer == 0) { + int _iPlayer2 = 1; + PlayerData* _ptrPlayerData = &DAT_GameState::instance.playerDataArray[1]; + do { + if (((DAT_GameState::instance.mapAndTime.playerTeams[_iPlayer2] + != DAT_GameState::instance.mapAndTime.playerTeams[playerID]) + && MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(_iPlayer2) != 0) + && (MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)((_ptrPlayerData->campground).xEntry, + (_ptrPlayerData->campground).yEntry, fromXPosition, fromYPosition), + DAT_DirectionAlgorithmState::instance.distanceHigh <= _lowestDistance)) { + _lowestDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + _selectedPlayer = _iPlayer2; + } + _iPlayer2 = _iPlayer2 + 1; + _ptrPlayerData = _ptrPlayerData + 0xe7d; + } while (_iPlayer2 < 9); + + if (_selectedPlayer == 0) + return; + } + + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID_2Unk = _selectedPlayer; + DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID = _selectedPlayer; + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/selectBuildingFromAListOfBuildingTypes.cpp b/src/OpenSHC/AI/AICState/selectBuildingFromAListOfBuildingTypes.cpp new file mode 100644 index 00000000..75b65a9e --- /dev/null +++ b/src/OpenSHC/AI/AICState/selectBuildingFromAListOfBuildingTypes.cpp @@ -0,0 +1,75 @@ +#include "OpenSHC/AI/AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDAB0 + int AICState ::selectBuildingFromAListOfBuildingTypes(int playerID) + + { + + int _selectedBuildingID; + + int _loopCounter; + + int _loopLimit; + + short* psVar1; + + int _arraySize; + + int _buildingID; + + _arraySize = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker; + + _selectedBuildingID = 0; + + _loopLimit = 0x2e; + + if (0 < _arraySize) { + + psVar1 = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildings; + + do { + + _buildingID = (int)*psVar1; + + if ((_buildingID != 0) && (_loopCounter = 0, 0 < _loopLimit)) { + + do { + + if ((int)(short)DAT_BuildingsState::instance.buildings[_buildingID].buildingType + == DAT_SkirmishDefinedData::instance.BuildingTargetPrioritySet1[_loopCounter]) { + + /* + fancy break statement? */ + + _selectedBuildingID = _buildingID; + + _loopLimit = _loopCounter; + } + + _loopCounter = _loopCounter + 1; + + } while (_loopCounter < _loopLimit); + } + + psVar1 = psVar1 + 1; + + _arraySize = _arraySize + -1; + + } while (_arraySize != 0); + } + + return _selectedBuildingID; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/selectRandomBuildingIDFromListOfBuildingTypes.cpp b/src/OpenSHC/AI/AICState/selectRandomBuildingIDFromListOfBuildingTypes.cpp new file mode 100644 index 00000000..072e4b84 --- /dev/null +++ b/src/OpenSHC/AI/AICState/selectRandomBuildingIDFromListOfBuildingTypes.cpp @@ -0,0 +1,37 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDCA0 + int AICState ::selectRandomBuildingIDFromListOfBuildingTypes(int playerID, undefined4 param_2) + + { + + int iVar1; + + int iVar2; + + iVar1 = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker; + + if (iVar1 < 1) { + + return 0; + } + + iVar2 = (int)SEC_RNG::instance.currentNumber2; + + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + + return (int)DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildings[iVar2 % iVar1]; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sellExcessGoods.cpp b/src/OpenSHC/AI/AICState/sellExcessGoods.cpp new file mode 100644 index 00000000..643d5642 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sellExcessGoods.cpp @@ -0,0 +1,105 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/AI/Trading/AIResourceTradeCategory.hpp" +#include "OpenSHC/AI/Trading/AIResourceTradeCategoryInt.hpp" +#include "OpenSHC/AI/Trading/ResourceAICResourceMappingEntry.hpp" +#include "OpenSHC/Game/Resources/ResourceType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::AI::AIC::AICSpecification; + using OpenSHC::AI::Trading::AIResourceTradeCategory; + using OpenSHC::AI::Trading::AIResourceTradeCategoryInt; + using OpenSHC::AI::Trading::ResourceAICResourceMappingEntry; + using OpenSHC::Game::Resources::ResourceType; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D1D60 + void AICState ::sellExcessGoods(int param_1) + { + AITypeInt aiType = DAT_GameState::instance.playerDataArray[param_1].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) + return; + + int aicIndex = aiType - 1; + AICSpecification& aic = this->aics[aicIndex]; + int nervousActions = DAT_GameState::instance.playerDataArray[param_1].aiNervousActionsTracker; + int& goldResource = DAT_GameState::instance.playerDataArray[param_1].currentResources[0xf]; + int& popularity = DAT_GameState::instance.playerDataArray[param_1].popularity; + + for (int entryIndex = 0; entryIndex < 20; entryIndex = entryIndex + 1) { + ResourceType resourceType = (OpenSHC::Game::Resources::ResourceType)DAT_SkirmishDefinedData::instance + .AIResourceTradeAICMapping[entryIndex] + .game; + AIResourceTradeCategoryInt category + = DAT_SkirmishDefinedData::instance.AIResourceTradeAICMapping[entryIndex].aic; + int variance = aic.maxResourceVariance; + int& currentResource = DAT_GameState::instance.playerDataArray[param_1].currentResources[resourceType]; + + int amount; + if (category == OpenSHC::AI::Trading::AIRTC_WOOD) { + amount = aic.maxWood; + } else if (category == OpenSHC::AI::Trading::AIRTC_FOOD) { + amount = aic.maxFood; + } else if (category == OpenSHC::AI::Trading::AIRTC_STONE) { + amount = aic.maxStone; + } else if (category == OpenSHC::AI::Trading::AIRTC_WEAPONS) { + amount = aic.maxEquipment; + } else if (category == OpenSHC::AI::Trading::AIRTC_ALE) { + amount = aic.maxBeer; + } else { + amount = aic.maxResourceOther; + } + + amount = amount + variance; + + if ((nervousActions > 0) && (goldResource < 0x1f4)) { + + if (category == OpenSHC::AI::Trading::AIRTC_FOOD) { + amount = amount / 4; + } else { + amount = ((category != OpenSHC::AI::Trading::AIRTC_WEAPONS) - 1) & 10000; + } + variance = 0; + + } else { + if (popularity < aic.criticalPopularity) { + if (category != OpenSHC::AI::Trading::AIRTC_FOOD) { + if (category == OpenSHC::AI::Trading::AIRTC_WEAPONS) { + amount = 0; + } else { + amount = amount / 4; + } + } + variance = 0; + } else { + amount = amount + + DAT_GameState::instance.playerDataArray[param_1].resourcesToAcquireArray[resourceType]; + } + } + + int* sellResourceList = &aic.sellResource01; + for (int sellResourceIndex = 0; sellResourceIndex < 0xf; sellResourceIndex = sellResourceIndex + 1) { + if (resourceType == sellResourceList[sellResourceIndex]) { + amount = 0; + variance = 0; + break; + } + } + + if (currentResource > amount) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sellGoods, this)(param_1, resourceType, + (DAT_GameState::instance.playerDataArray[param_1].currentResources[resourceType] - amount) + + variance); + return; + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/sellGoods.cpp b/src/OpenSHC/AI/AICState/sellGoods.cpp new file mode 100644 index 00000000..d8dceeb3 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sellGoods.cpp @@ -0,0 +1,29 @@ +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CBFA0 + void AICState::sellGoods(int playerID, ResourceType resourceType, int amount) + { + int _yield = MACRO_CALL_MEMBER(Game::GameStateStructures_Func::getSellPrice, DAT_GameState::ptr)( + playerID, resourceType, amount); + int* _pFinalGold = DAT_GameSynchronyState::instance.SEC_FinalResults.finalGold + playerID; + *_pFinalGold = *_pFinalGold + _yield; + int* _pGold = DAT_GameState::instance.playerDataArray[playerID].currentResources + 0xf; + *_pGold = *_pGold + _yield; + int* _pGoldSell = &DAT_GameState::instance.playerDataArray[playerID].marketGold; + *_pGoldSell = *_pGoldSell + _yield; + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::processResourceLoss, DAT_BuildingsState::ptr)( + playerID, resourceType, amount, 0); + MACRO_CALL_MEMBER(Game::GameStateStructures_Func::displayPlayerTradeVisualEffect, DAT_GameState::ptr)( + playerID, 1, -amount, resourceType); + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendAITribe_0xf_toAttackPatrol.cpp b/src/OpenSHC/AI/AICState/sendAITribe_0xf_toAttackPatrol.cpp new file mode 100644 index 00000000..791472b6 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendAITribe_0xf_toAttackPatrol.cpp @@ -0,0 +1,31 @@ +#include "OpenSHC/Map/Units/TroopValueState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF4F0 + void AICState::sendAITribe_0xf_toAttackPatrol(int playerID) + { + int _tribeID; + /* bug: only sends 0xf, while 0xf+1 could also be send if defined by 'AIC: AttUnitPatrolGroupsCount' */ + if ((DAT_GameState::instance.playerDataArray[playerID].aiType != AITA_NULL) + && (_tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xf], _tribeID != 0)) { + if ((DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xf]) + && (DAT_TribesState::instance.tribes[_tribeID].selectionTargetUnitID != 0)) { + DAT_TribesState::instance.tribes[_tribeID].unitStance = Map::Units::Behavior::USE_DEFENSIVE; + MACRO_CALL_MEMBER(Map::Units::TroopValueState_Func::sendAttackingPatrolTribeToComputedDestination, + DAT_TroopValueState::ptr)(_tribeID); + return; + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed.cpp b/src/OpenSHC/AI/AICState/sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed.cpp new file mode 100644 index 00000000..7b01dc9c --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed.cpp @@ -0,0 +1,109 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/AI/AIVUnitTypeInt.hpp" +#include "OpenSHC/AI/Tribes/AIVUnitTypeMaxLocationPair.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIVUnitType; + using OpenSHC::AI::AIVUnitTypeInt; + using OpenSHC::AI::Tribes::AIVUnitTypeMaxLocationPair; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4220 + void AICState ::sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed(int playerID) + + { + + BOOLEnum _enclosed; + + AIVUnitTypeInt* _ptr; + + int _tribe; + + int _index; + + int _limit; + + int* _nervous; + + _enclosed = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)( + playerID); + + _ptr = &DAT_SkirmishDefinedData::instance.MaxAIVLocationForGroundUnits[0].aivUnitType; + + do { + + _limit = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[*_ptr]; + + _index = 0; + + if (0 < _limit) { + + _nervous = &DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker; + + do { + + _tribe = (int)DAT_GameState::instance.playerDataArray[playerID] + .aiTribeIDs[((AIVUnitTypeMaxLocationPair*)(_ptr + -1))->tribeArrayOffset + _index]; + + if ((_tribe != 0) + && (DAT_TribesState::instance.tribes[_tribe].uid + == DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[((AIVUnitTypeMaxLocationPair*)(_ptr + -1))->tribeArrayOffset + _index])) { + + if ((*_nervous < 1) || (_enclosed != FALSE)) { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeToAIVLocationSlot, this)( + _tribe, (AIVUnitType)((int)(*_ptr)), _index); + + } + + else { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(_tribe, playerID); + } + } + + /* + bug:fixme: _tribe could be 0 */ + + if ((*_nervous < 1) || (_enclosed != FALSE)) { + + DAT_TribesState::instance.tribes[_tribe].unitStance + = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + } + + else { + + DAT_TribesState::instance.tribes[_tribe].unitStance + = OpenSHC::Map::Units::Behavior::USE_DEFENSIVE; + } + + _index = _index + 1; + + } while (_index < _limit); + } + + _ptr = _ptr + 2; + + } while ((int)_ptr < 0xb4277c); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sendOilMenForOilPickupOrToAIVLocations.cpp b/src/OpenSHC/AI/AICState/sendOilMenForOilPickupOrToAIVLocations.cpp new file mode 100644 index 00000000..afc77615 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendOilMenForOilPickupOrToAIVLocations.cpp @@ -0,0 +1,104 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIVUnitType; + using OpenSHC::Map::Units::UnitInstructionType; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D44E0 + void AICState ::sendOilMenForOilPickupOrToAIVLocations(int playerID) + + { + + short* _ptrTribeIDArray; + + int _oilManLocationsCount; + + int _tribeID; + + int _index; + + int _oilSmelter; + + _oilSmelter = DAT_GameState::instance.playerDataArray[playerID].oilSmelter.id; + + _oilManLocationsCount = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[1]; + + if (0 < _oilManLocationsCount) { + + if (_oilManLocationsCount == 10) { + + _oilManLocationsCount = 9; + } + + _index = 1; + + if (0 < _oilManLocationsCount) { + + /* + oil men offset in tribe id array */ + + _ptrTribeIDArray = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + 71; + + do { + + _tribeID = (int)*_ptrTribeIDArray; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[_index + 0x46])) { + + if (DAT_UnitsState::instance + .units[DAT_TribesState::instance.tribes[_tribeID].selectionTargetUnitID] + .resourceToDeposit + == 0) { + + if (_oilSmelter != 0) { + + /* + send for oil duty */ + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::relayTribeInstruction, + DAT_UnitsState::ptr)(_tribeID, + OpenSHC::Map::Units::UIT_CONSTRUCT_SIEGE_EQUIPMENTOIL_DUTYENGINEERRELATED, + _oilSmelter, DAT_BuildingsState::instance.buildings[_oilSmelter].uid, 0); + } + + } + + else { + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::unsetRallyRelatedFlagOnUnits, + DAT_TribesState::ptr)(_tribeID); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeToAIVLocationSlot, this)( + _tribeID, OpenSHC::AI::AIVUT_ENGINEER, _index + -1); + } + } + + _index = _index + 1; + + _ptrTribeIDArray = _ptrTribeIDArray + 1; + + } while (_index <= _oilManLocationsCount); + } + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sendRangedUnitTribesToAIVSlotsOrKeepIfNervous.cpp b/src/OpenSHC/AI/AICState/sendRangedUnitTribesToAIVSlotsOrKeepIfNervous.cpp new file mode 100644 index 00000000..5e202cb2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendRangedUnitTribesToAIVSlotsOrKeepIfNervous.cpp @@ -0,0 +1,75 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/AI/AIVUnitTypeInt.hpp" +#include "OpenSHC/AI/Tribes/AIVUnitTypeMaxLocationPair.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIVUnitType; + using OpenSHC::AI::AIVUnitTypeInt; + using OpenSHC::AI::Tribes::AIVUnitTypeMaxLocationPair; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4130 + void AICState ::sendRangedUnitTribesToAIVSlotsOrKeepIfNervous(int playerID) + + { + + AIVUnitTypeInt* _ptr = &DAT_SkirmishDefinedData::instance.MaxAIVLocationForRangedUnits[0].aivUnitType; + + do { + + int _limit = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[*_ptr]; + + int _index = 0; + + if (0 < _limit) { + + do { + + int _tribe = (int)DAT_GameState::instance.playerDataArray[playerID] + .aiTribeIDs[((AIVUnitTypeMaxLocationPair*)(_ptr + -1))->tribeArrayOffset + _index]; + + if ((_tribe != 0) + && (DAT_TribesState::instance.tribes[_tribe].uid + == DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[((AIVUnitTypeMaxLocationPair*)(_ptr + -1))->tribeArrayOffset + _index])) { + + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker > 0) { + + if ((DAT_TileMapState::instance.LogicLayer[DAT_UnitsState::instance + .units[DAT_TribesState::instance.tribes[_tribe].selectionTargetUnitID] + .tile] + & 0x10000100U) + == 0) { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(_tribe, playerID); + } + + } else { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeToAIVLocationSlot, this)( + _tribe, (AIVUnitType)((int)(*_ptr)), _index); + } + } + + _index = _index + 1; + + } while (_index < _limit); + } + + _ptr = _ptr + 2; + + } while ((int)_ptr < 0xb42744); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sendTribeAtOffset1ToCampfire.cpp b/src/OpenSHC/AI/AICState/sendTribeAtOffset1ToCampfire.cpp new file mode 100644 index 00000000..f595abbd --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendTribeAtOffset1ToCampfire.cpp @@ -0,0 +1,20 @@ +#include "OpenSHC/AI/AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2870 + void AICState::sendTribeAtOffset1ToCampfire(int playerID) + { + int _tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[1]; + if ((0 < _tribeID) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[1])) { + MACRO_CALL_MEMBER(AICState_Func::sendUnitsToCampfire, this)(_tribeID, playerID); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendTribeToAIVLocationSlot.cpp b/src/OpenSHC/AI/AICState/sendTribeToAIVLocationSlot.cpp new file mode 100644 index 00000000..2d8aad2b --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendTribeToAIVLocationSlot.cpp @@ -0,0 +1,50 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIVUnitType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2E00 + void AICState ::sendTribeToAIVLocationSlot(int tribeID, AIVUnitType aivUnitType, int slotIndex) + { + int _owner = DAT_TribesState::instance.tribes[tribeID].owner; + int _tile = 0; + int _slotIndex = 0; + int* _ptrSlot = (int*)(_owner * 0x39f4 + 0x115eb14 + aivUnitType * 0x28); + + // Find the slot at slotIndex, skipping empty entries + while (*_ptrSlot == 0 || (slotIndex = slotIndex + -1, -1 < slotIndex)) { + _slotIndex = _slotIndex + 1; + _ptrSlot = _ptrSlot + 1; + if (9 < _slotIndex) + break; + } + + if (_slotIndex <= 9) + _tile = DAT_GameState::instance.playerDataArray[_owner].aivUnitLocationSlots[aivUnitType][_slotIndex]; + + short _y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_tile]; + int _x = DAT_ViewportRenderState::instance.translationMatrix[_y].addXgetTile; + + BOOLEnum _canNavigate + = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::canNavigateUnitsFromTileToTargetTile, this)(tribeID, _tile); + + if (_canNavigate == FALSE) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(tribeID, _owner); + return; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::commandUnitsToLocation, DAT_TribesState::ptr)( + tribeID, (uint)((int)(_tile - _x)), (uint)((int)((int)_y)), 0); + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendTribeToAttack.cpp b/src/OpenSHC/AI/AICState/sendTribeToAttack.cpp new file mode 100644 index 00000000..86bc9971 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendTribeToAttack.cpp @@ -0,0 +1,29 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF750 + void AICState::sendTribeToAttack(int playerID) + { + int tribeID; + if ((DAT_GameState::instance.playerDataArray[playerID].aiType != AITA_NULL) + && (tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xbd], tribeID != 0)) { + if ((DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xbd]) + && (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID != 0)) { + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_AGGRESSIVE; + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, Map::Units::STBT_0x3fb); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendUnitsToAttackBreachedCastle.cpp b/src/OpenSHC/AI/AICState/sendUnitsToAttackBreachedCastle.cpp new file mode 100644 index 00000000..54406b44 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendUnitsToAttackBreachedCastle.cpp @@ -0,0 +1,180 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Map::Units::SomeTribeBehaviorType; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D30E0 + undefined4 AICState ::sendUnitsToAttackBreachedCastle(int attackingPlayerIndex) + + { + + int tribeType; + + int _defendingLordIndex; + + int _successUnk; + + int iVar1; + + int _vector; + + int _tribeID; + + short* local_20; + + int local_18; + + int (*_ptrGroupCount)[2]; + + int _attackedPlayerID; + + int _groupCount; + + short _targetUnit; + + bool sentUnitsToAttackLord; + + _vector = 0; + + if (DAT_GameState::instance.playerDataArray[attackingPlayerIndex].aiType == OpenSHC::AI::AIT_NULL) { + + return (undefined4)(1); + } + + sentUnitsToAttackLord = false; + + _defendingLordIndex = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(DAT_GameState::instance.playerDataArray[attackingPlayerIndex].attackedPlayerID); + + _ptrGroupCount = (int (*)[2])(&DAT_SkirmishDefinedData::instance.MaxBreachTribes[0].tribeCount); + + do { + + tribeType = _ptrGroupCount[-1][1]; + + _groupCount = (*_ptrGroupCount)[0]; + + if ((((tribeType != 18) && (tribeType != 14)) && (tribeType != 13)) && (local_18 = 0, 0 < _groupCount)) { + + local_20 = DAT_GameState::instance.playerDataArray[attackingPlayerIndex].aiTribeIDs + tribeType; + + do { + + _tribeID = (int)*local_20; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[attackingPlayerIndex] + .aiTribeUIDs[local_18 + tribeType])) { + + _targetUnit = DAT_TribesState::instance.tribes[_tribeID].selectionTargetUnitID; + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_DEFENSIVE; + + if ((sentUnitsToAttackLord) || ((_defendingLordIndex == 0 || (tribeType == 186)))) { + + iVar1 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::giveSomeRaidCommand, this)( + _tribeID, tribeType); + + if (iVar1 == 0) { + + if (tribeType == 0xba) { + + _vector = _vector + 4; + } + + DAT_TribesState::instance.tribes[_tribeID].tribeBehaviorType + = OpenSHC::Map::Units::STBT_8; + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + iVar1 = DAT_GameState::instance.playerDataArray[attackingPlayerIndex].attackedPlayerID; + + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)( + (int)DAT_UnitsState::instance.units[_targetUnit].x, + (int)((int)(DAT_UnitsState::instance.units[_targetUnit].y)), + DAT_GameState::instance.mapAndTime.attackVectors[iVar1][_vector].x, + DAT_GameState::instance.mapAndTime.attackVectors[iVar1][_vector].y); + + if (4 < DAT_DirectionAlgorithmState::instance.distanceHigh) { + + _attackedPlayerID = DAT_GameState::instance.playerDataArray[attackingPlayerIndex] + .attackedPlayerID; + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, + DAT_TribesState::ptr)(_tribeID, + (uint)((int)(DAT_GameState::instance.mapAndTime + .attackVectors[_attackedPlayerID][_vector] + .x)), + (uint)((int)(DAT_GameState::instance.mapAndTime + .attackVectors[_attackedPlayerID][_vector] + .y)), + 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + } + + _vector = _vector + 1; + } + + } + + else { + + /* + Send units to attack lord */ + + _successUnk = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)( + _tribeID, (uint)((int)((int)DAT_UnitsState::instance.units[_defendingLordIndex].x)), + (uint)((int)((int)DAT_UnitsState::instance.units[_defendingLordIndex].y)), 0, 0, + OpenSHC::Map::Units::Instructions::UMSE_0); + + if (_successUnk == 0) { + + return (undefined4)(0); + } + + sentUnitsToAttackLord = true; + } + } + + local_20 = local_20 + 1; + + local_18 = local_18 + 1; + + } while (local_18 < _groupCount); + } + + _ptrGroupCount = _ptrGroupCount + 1; + + } while ((int)_ptrGroupCount < 0xb42acc); + + return (undefined4)(1); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sendUnitsToCampfire.cpp b/src/OpenSHC/AI/AICState/sendUnitsToCampfire.cpp new file mode 100644 index 00000000..b81436f3 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendUnitsToCampfire.cpp @@ -0,0 +1,22 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD110 + void AICState::sendUnitsToCampfire(int tribeID, int playerID) + { + if (0 < DAT_GameState::instance.playerDataArray[playerID].campground.id) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::commandUnitsToLocation, DAT_TribesState::ptr)(tribeID, + DAT_GameState::instance.playerDataArray[playerID].campground.xEntry, + DAT_GameState::instance.playerDataArray[playerID].campground.yEntry, 0); + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_AGGRESSIVE; + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendUnitsToKeep.cpp b/src/OpenSHC/AI/AICState/sendUnitsToKeep.cpp new file mode 100644 index 00000000..dd0c25b0 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendUnitsToKeep.cpp @@ -0,0 +1,22 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD0C0 + void AICState::sendUnitsToKeep(int tribeID, int playerID) + { + int iVar1 = DAT_GameState::instance.playerDataArray[playerID].keep.id; + if (0 < iVar1) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::commandUnitsToLocation, DAT_TribesState::ptr)(tribeID, + (int)(short)DAT_BuildingsState::instance.buildings[iVar1].x + 3, + (int)(short)DAT_BuildingsState::instance.buildings[iVar1].y + 4, 0); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendUnitsToPlayerIDsKeepIfPossible.cpp b/src/OpenSHC/AI/AICState/sendUnitsToPlayerIDsKeepIfPossible.cpp new file mode 100644 index 00000000..00653f14 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendUnitsToPlayerIDsKeepIfPossible.cpp @@ -0,0 +1,96 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/Instructions/UnitMatchSpeedEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::Map::Units::Instructions::UnitMatchSpeedEnum; + + /** + * I seem unable to make this match better than 75.42% even though this function flow is dumb: _ghX and _ghY are + * only specified if not navigatable, which is checked twice for some reason. + */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE5F0 + void AICState::sendUnitsToPlayerIDsKeepIfPossible(int playerID1, int playerID2) + { + uint _p1_campfireX = DAT_GameState::instance.playerDataArray[playerID1].campground.xEntry; + uint _p1_campfireY = DAT_GameState::instance.playerDataArray[playerID1].campground.yEntry; + + int const _canNavigate = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, + DAT_GameState::ptr)(playerID1, playerID2); + + uint _ghX; + uint _ghY; + + if (_canNavigate == FALSE) { + dword const _gatehouseTile + = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findOwnedGatehouse, + DAT_PathFindingState::ptr)(playerID2, _p1_campfireX, _p1_campfireY); + if (_gatehouseTile == 0) { + DAT_GameState::instance.playerDataArray[playerID1].requestStateUnk = 0; + return; + } + _ghY = (uint)DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[_gatehouseTile]; + _ghX = _gatehouseTile - DAT_ViewportRenderState::instance.translationMatrix[_ghY].addXgetTile; + } + + for (int _index = 0; _index < 11; _index++) { + int const _tribeTypeStart = (int)DAT_SkirmishDefinedData::instance.MaxAttackTribes1[_index].tribeType; + int _tribeIndex = 0; + int const _maxTribeCount = (int)DAT_SkirmishDefinedData::instance.MaxAttackTribes1[_index].tribeCount; + + for (; _tribeIndex < _maxTribeCount; _tribeIndex++) { + + int const _tribeID + = (int)DAT_GameState::instance.playerDataArray[playerID1].aiTribeIDs[_tribeTypeStart + _tribeIndex]; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID1] + .aiTribeUIDs[_tribeIndex + _tribeTypeStart])) { + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + int const _canNav2 + = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, + DAT_GameState::ptr)(playerID1, playerID2); + + if (_canNav2 != FALSE) { + if (_tribeTypeStart == 0xbe) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)( + _tribeID, playerID2); + } else { + int const _hasHorses + = MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::selectionContainsHorses, + DAT_TribesState::ptr)(_tribeID); + if (_hasHorses != FALSE) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)( + _tribeID, playerID2); + } else { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)( + _tribeID, playerID2); + } + } + } else { + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)( + _tribeID, _ghX, _ghY, 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + } + } + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendWallPatrolUnitTribesToAIVLocations.cpp b/src/OpenSHC/AI/AICState/sendWallPatrolUnitTribesToAIVLocations.cpp new file mode 100644 index 00000000..6683a569 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendWallPatrolUnitTribesToAIVLocations.cpp @@ -0,0 +1,155 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/AI/AIVUnitTypeInt.hpp" +#include "OpenSHC/AI/Tribes/AIVUnitTypeMaxLocationPair.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::AI::AIVUnitType; + using OpenSHC::AI::AIVUnitTypeInt; + using OpenSHC::AI::Tribes::AIVUnitTypeMaxLocationPair; + using OpenSHC::Map::Units::Behavior::UnitStanceEnum; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4340 + void AICState ::sendWallPatrolUnitTribesToAIVLocations(int playerID) + + { + + BOOLEnum _enclosed; + + int _tribeID; + + AIVUnitTypeInt* _ptr; + + int _patrolGroupIndex; + + int _index; + + int _minCount; + + int _stepSize; + + int _defWallPatrolGroups; + + AITypeInt _aiType; + + int _count; + + _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (_aiType != OpenSHC::AI::AIT_NULL) { + + _enclosed = MACRO_CALL_MEMBER( + OpenSHC::Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)(playerID); + + /* + defWallPatrolGroups */ + + _ptr = &DAT_SkirmishDefinedData::instance.MaxAIVLocationForPatrolUnits[0].aivUnitType; + + do { + + _defWallPatrolGroups = *(int*)((int)this + (_aiType + ~OpenSHC::AI::AIT_NULL) * 0x2a4 + 0x114); + + _count = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[*_ptr]; + + _index = 0; + + _minCount = _count; + + if (_defWallPatrolGroups < _count) { + + _minCount = _defWallPatrolGroups; + } + + if (0 < _minCount) { + + _stepSize = _count / _minCount; + + if (_stepSize < 1) { + + _stepSize = 1; + } + + if (0 < _minCount) { + + _patrolGroupIndex = 0; + + do { + + _tribeID = (int)DAT_GameState::instance.playerDataArray[playerID] + .aiTribeIDs[((AIVUnitTypeMaxLocationPair*)(_ptr + -1))->tribeArrayOffset + + _index]; + + if ((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[((AIVUnitTypeMaxLocationPair*)(_ptr + -1))->tribeArrayOffset + + _index])) { + + if ((DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker < 1) + || (_enclosed != FALSE)) { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeToAIVLocationSlot, this)( + _tribeID, (AIVUnitType)((int)(*_ptr)), + (DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeHits + + _patrolGroupIndex) + % _count); + + } + + else { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)( + _tribeID, playerID); + } + + if ((DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker < 1) + || (_enclosed != FALSE)) { + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + } + + else { + + DAT_TribesState::instance.tribes[_tribeID].unitStance + = OpenSHC::Map::Units::Behavior::USE_DEFENSIVE; + } + } + + _patrolGroupIndex = _patrolGroupIndex + _stepSize; + + _index = _index + 1; + + } while (_index < _minCount); + } + } + + _ptr = _ptr + 2; + + } while ((int)_ptr < 0xb42794); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_01.cpp b/src/OpenSHC/AI/AICState/setAICParameters_01.cpp new file mode 100644 index 00000000..d085d91a --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_01.cpp @@ -0,0 +1,150 @@ +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" +#include "OpenSHC/AI/AICState.hpp" + +namespace OpenSHC { +namespace AI { + + using AIC::AITC_ANY; + using AIC::AITC_BALANCED; + using AIC::AITC_CLOSEST; + using AIC::AITC_GOLD; + using AIC::AITC_PLAYER; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C6D60 + void AICState::setAICParameters_01(int aicID) + { + this->aics[aicID].minimumCheese = -1; + this->aics[aicID].minimumBread = -1; + this->aics[aicID].minimumWheat = -1; + this->aics[aicID].minimumHop = -1; + this->aics[aicID].aiRequestDelay = 0x12; + this->aics[aicID].populationPerQuarry = 0x12; + this->aics[aicID].populationPerFarm = 6; + this->aics[aicID].maxWoodcutters = 6; + this->aics[aicID].maxFarms = 6; + this->aics[aicID].flagType = 0xc; + this->aics[aicID].unknown002 = 0; + this->aics[aicID].unknown001 = 0; + this->aics[aicID].unknown003 = 0; + this->aics[aicID].unknown004 = 0; + this->aics[aicID].unknown005 = 0; + this->aics[aicID].criticalPopularity = 4000; + this->aics[aicID].lowestPopularity = 5500; + this->aics[aicID].highestPopularity = 7500; + this->aics[aicID].taxesMin = 3; + this->aics[aicID].taxesMax = 10; + this->aics[aicID].farm1 = 0x20; + this->aics[aicID].unknown011 = 0x14; + this->aics[aicID].maxFood = 100; + this->aics[aicID].minimumApples = 10; + this->aics[aicID].tradeAmountFood = 10; + this->aics[aicID].tradeAmountEquipment = 2; + this->aics[aicID].minimumGoodsRequiredAfterTrade = 0; + this->aics[aicID].doubleRationsFoodThreshold = 0; + this->aics[aicID].maxWood = 0x1e; + this->aics[aicID].maxStone = 0x28; + this->aics[aicID].maxResourceOther = 0x14; + this->aics[aicID].maxEquipment = 0x14; + this->aics[aicID].maxBeer = 0x14; + this->aics[aicID].maxResourceVariance = 5; + this->aics[aicID].recruitGoldThreshold = 200; + this->aics[aicID].populationPerWoodcutter = 8; + this->aics[aicID].populationPerIronmine = 0; + this->aics[aicID].populationPerPitchrig = 0; + this->aics[aicID].maxQuarries = 1; + this->aics[aicID].maxIronmines = 1; + this->aics[aicID].maxPitchrigs = 1; + this->aics[aicID].buildInterval = 1; + this->aics[aicID].resourceRebuildDelay = 10; + this->aics[aicID].blacksmithSetting = 0x16; + this->aics[aicID].fletcherSetting = 0x11; + this->aics[aicID].poleturnerSetting = 0x13; + this->aics[aicID].sellResource02 = 3; + this->aics[aicID].sellResource03 = 6; + this->aics[aicID].sellResource04 = 7; + this->aics[aicID].sellResource05 = 9; + this->aics[aicID].sellResource06 = 0xe; + this->aics[aicID].sellResource07 = 0x10; + this->aics[aicID].sellResource09 = 0x12; + this->aics[aicID].sellResource11 = 0x14; + this->aics[aicID].sellResource12 = 0x15; + this->aics[aicID].sellResource13 = 0x16; + this->aics[aicID].sellResource14 = 0x17; + this->aics[aicID].sellResource15 = 0x18; + this->aics[aicID].defWallPatrolRallyTime = 0x14; + this->aics[aicID].defWallPatrolGroups = 1; + this->aics[aicID].defSiegeEngineGoldThreshold = 200; + this->aics[aicID].defSiegeEngineBuildDelay = 0x14; + this->aics[aicID].unknown072 = 800; + this->aics[aicID].unknown073 = 0x50; + this->aics[aicID].recruitProbDefWeak = 0x32; + this->aics[aicID].RecruitIntervalWeak = 1; + this->aics[aicID].RecruitInterval = 1; + this->aics[aicID].RecruitIntervalStrong = 1; + this->aics[aicID].SortieUnitRanged = 0x16; + this->aics[aicID].SortieUnitMelee = 0x18; + this->aics[aicID].DefDiggingUnit = 0x18; + this->aics[aicID].OuterPatrolGroupsCount = 1; + this->aics[aicID].OuterPatrolRallyDelay = 10; + this->aics[aicID].DefUnit1 = 0x18; + this->aics[aicID].DefUnit2 = 0x18; + this->aics[aicID].DefUnit3 = 0x16; + this->aics[aicID].DefUnit4 = 0x16; + this->aics[aicID].RaidUnitsBase = 10; + this->aics[aicID].RaidUnit1 = 0x18; + this->aics[aicID].RaidUnit2 = 0x18; + this->aics[aicID].RaidUnit3 = 0x16; + this->aics[aicID].recruitProbDefDefault = 0; + this->aics[aicID].recruitProbDefStrong = 0; + this->aics[aicID].recruitProbRaidWeak = 0x32; + this->aics[aicID].recruitProbRaidDefault = 0x32; + this->aics[aicID].recruitProbRaidStrong = 0; + this->aics[aicID].recruitProbAttackWeak = 0; + this->aics[aicID].recruitProbAttackDefault = 0x32; + this->aics[aicID].recruitProbAttackStrong = 100; + this->aics[aicID].SortieUnitRangedMin = 5; + this->aics[aicID].SortieUnitMeleeMin = 6; + this->aics[aicID].DefDiggingUnitMax = 0; + this->aics[aicID].DefWalls = 0x14; + this->aics[aicID].DefTotal = 0x1e; + this->aics[aicID].OuterPatrolGroupsMove = 0; + this->aics[aicID].RaidUnitsRandom = 0xf; + this->aics[aicID].HarassingSiegeEnginesMax = 0; + this->aics[aicID].HarassingSiegeEngine1 = 0xbe; + this->aics[aicID].RaidRetargetDelay = 3; + this->aics[aicID].AttForceBase = 0x14; + this->aics[aicID].AttForceRandom = 10; + this->aics[aicID].AttForceSupportAllyThreshold = 0xf; + this->aics[aicID].AttForceRallyPercentage = 0x32; + this->aics[aicID].AttMovingTribesThreshold = 100; + this->aics[aicID].AttAssaultDelay = 0; + this->aics[aicID].AttUnitPatrolRecommandDelay = 0x19; + this->aics[aicID].AttCoordinationPreference = 0x14; + this->aics[aicID].SiegeEngine1 = 0; + this->aics[aicID].CowThrowInterval = 1000; + this->aics[aicID].AttMaxEngineers = 0; + this->aics[aicID].AttDiggingUnit = 0x18; + this->aics[aicID].AttDiggingUnitMax = 0x14; + this->aics[aicID].AttMaxAssassins = 0; + this->aics[aicID].AttUnit2 = 0x16; + this->aics[aicID].AttUnit2Max = 0; + this->aics[aicID].AttMaxLaddermen = 0; + this->aics[aicID].AttMaxTunnelers = 0; + this->aics[aicID].AttUnitPatrol = 0x16; + this->aics[aicID].AttUnitPatrolMax = 0; + this->aics[aicID].AttUnitPatrolGroupsCount = 0; + this->aics[aicID].AttUnitBackup = 0x16; + this->aics[aicID].AttUnitBackupMax = 0x10; + this->aics[aicID].AttUnitBackupGroupsCount = 0; + this->aics[aicID].AttUnitEngageMax = 0; + this->aics[aicID].AttUnitEngage = 0; + this->aics[aicID].AttUnitSiegeDef = 0x18; + this->aics[aicID].AttUnitSiegeDefMax = 0; + this->aics[aicID].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicID].AttUnitMain1 = 0x18; + this->aics[aicID].AttMaxDefault = 200; + this->aics[aicID].AttMainGroupsCount = 1; + this->aics[aicID].TargetChoice = AITC_PLAYER; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_02.cpp b/src/OpenSHC/AI/AICState/setAICParameters_02.cpp new file mode 100644 index 00000000..c3e0c0b6 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_02.cpp @@ -0,0 +1,148 @@ +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" +#include "OpenSHC/AI/AICState.hpp" + +namespace OpenSHC { +namespace AI { + + using AIC::AITC_ANY; + using AIC::AITC_BALANCED; + using AIC::AITC_CLOSEST; + using AIC::AITC_GOLD; + using AIC::AITC_PLAYER; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C7120 + void AICState::setAICParameters_02(int param_1) + { + this->aics[param_1].minimumBread = 0xffffffff; + this->aics[param_1].minimumWheat = 0xffffffff; + this->aics[param_1].minimumHop = 0xffffffff; + this->aics[param_1].populationPerFarm = 5; + this->aics[param_1].unknown011 = 0xf; + this->aics[param_1].tradeAmountFood = 5; + this->aics[param_1].aiRequestDelay = 0xc; + this->aics[param_1].maxResourceOther = 0xf; + this->aics[param_1].maxEquipment = 0xc; + this->aics[param_1].maxBeer = 5; + this->aics[param_1].maxResourceVariance = 5; + this->aics[param_1].populationPerWoodcutter = 5; + this->aics[param_1].populationPerQuarry = 0xf; + this->aics[param_1].resourceRebuildDelay = 0xc; + this->aics[param_1].flagType = 0xc; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 0; + this->aics[param_1].unknown005 = 0; + this->aics[param_1].criticalPopularity = 4000; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 8000; + this->aics[param_1].taxesMin = 3; + this->aics[param_1].taxesMax = 0xb; + this->aics[param_1].farm1 = 0x20; + this->aics[param_1].maxFood = 0x96; + this->aics[param_1].minimumApples = 10; + this->aics[param_1].minimumCheese = 0x14; + this->aics[param_1].tradeAmountEquipment = 6; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 10; + this->aics[param_1].doubleRationsFoodThreshold = 0x50; + this->aics[param_1].maxWood = 0x30; + this->aics[param_1].maxStone = 0x28; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerIronmine = 0; + this->aics[param_1].populationPerPitchrig = 0; + this->aics[param_1].maxQuarries = 2; + this->aics[param_1].maxIronmines = 1; + this->aics[param_1].maxWoodcutters = 10; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].maxFarms = 10; + this->aics[param_1].buildInterval = 2; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x11; + this->aics[param_1].poleturnerSetting = 0x13; + this->aics[param_1].sellResource02 = 3; + this->aics[param_1].sellResource03 = 6; + this->aics[param_1].sellResource04 = 7; + this->aics[param_1].sellResource05 = 9; + this->aics[param_1].sellResource06 = 0xe; + this->aics[param_1].sellResource07 = 0x10; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource13 = 0x16; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].sellResource15 = 0x18; + this->aics[param_1].defWallPatrolRallyTime = 2; + this->aics[param_1].defWallPatrolGroups = 4; + this->aics[param_1].defSiegeEngineGoldThreshold = 500; + this->aics[param_1].defSiegeEngineBuildDelay = 0x14; + this->aics[param_1].unknown072 = 400; + this->aics[param_1].unknown073 = 0x3c; + this->aics[param_1].recruitProbDefStrong = 0x14; + this->aics[param_1].SortieUnitRanged = 0x16; + this->aics[param_1].DefDiggingUnitMax = 0x14; + this->aics[param_1].DefUnit1 = 0x16; + this->aics[param_1].RaidUnitsRandom = 10; + this->aics[param_1].recruitProbDefWeak = 0x46; + this->aics[param_1].recruitProbRaidDefault = 0x28; + this->aics[param_1].recruitProbRaidStrong = 0x28; + this->aics[param_1].recruitProbAttackWeak = 0; + this->aics[param_1].recruitProbAttackStrong = 0x28; + this->aics[param_1].RecruitIntervalWeak = 0; + this->aics[param_1].RecruitInterval = 1; + this->aics[param_1].RecruitIntervalStrong = 1; + this->aics[param_1].SortieUnitRangedMin = 2; + this->aics[param_1].SortieUnitMeleeMin = 8; + this->aics[param_1].SortieUnitMelee = 0x18; + this->aics[param_1].DefDiggingUnit = 0x18; + this->aics[param_1].DefWalls = 0x32; + this->aics[param_1].DefTotal = 0x50; + this->aics[param_1].OuterPatrolGroupsCount = 3; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].OuterPatrolRallyDelay = 2; + this->aics[param_1].RaidUnit1 = 0x47; + this->aics[param_1].RaidUnit2 = 0x47; + this->aics[param_1].RaidUnit3 = 0x47; + this->aics[param_1].RaidUnit4 = 0x48; + this->aics[param_1].HarassingSiegeEnginesMax = 1; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].recruitProbDefDefault = 0x1e; + this->aics[param_1].recruitProbRaidWeak = 0x1e; + this->aics[param_1].recruitProbAttackDefault = 0x1e; + this->aics[param_1].RaidUnitsBase = 0x1e; + this->aics[param_1].RaidRetargetDelay = 3; + this->aics[param_1].AttForceBase = 0x1e; + this->aics[param_1].AttForceRandom = 10; + this->aics[param_1].AttForceSupportAllyThreshold = 0x1e; + this->aics[param_1].AttForceRallyPercentage = 0x32; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0; + this->aics[param_1].AttUnitPatrolRecommandDelay = 0x19; + this->aics[param_1].AttCoordinationPreference = 0x28; + this->aics[param_1].SiegeEngine1 = 0x27; + this->aics[param_1].CowThrowInterval = 1000; + this->aics[param_1].AttMaxEngineers = 2; + this->aics[param_1].AttDiggingUnit = 0x18; + this->aics[param_1].AttDiggingUnitMax = 0x14; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0x16; + this->aics[param_1].AttUnit2Max = 0x14; + this->aics[param_1].AttMaxLaddermen = 10; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0x48; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x46; + this->aics[param_1].AttUnitBackupMax = 0x32; + this->aics[param_1].AttUnitBackupGroupsCount = 1; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0x16; + this->aics[param_1].AttUnitSiegeDefMax = 0x1e; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + this->aics[param_1].AttUnitMain1 = 0x18; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 1; + this->aics[param_1].TargetChoice = AITC_BALANCED; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_03.cpp b/src/OpenSHC/AI/AICState/setAICParameters_03.cpp new file mode 100644 index 00000000..7f8f963a --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_03.cpp @@ -0,0 +1,148 @@ +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" +#include "OpenSHC/AI/AICState.hpp" + +namespace OpenSHC { +namespace AI { + + using AIC::AITC_ANY; + using AIC::AITC_BALANCED; + using AIC::AITC_CLOSEST; + using AIC::AITC_GOLD; + using AIC::AITC_PLAYER; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C7520 + void AICState::setAICParameters_03(int param_1) + { + this->aics[param_1].minimumWheat = -1; + this->aics[param_1].minimumHop = -1; + this->aics[param_1].flagType = 0xc; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 0; + this->aics[param_1].unknown005 = 0; + this->aics[param_1].criticalPopularity = 5000; + this->aics[param_1].lowestPopularity = 7000; + this->aics[param_1].highestPopularity = 0x2134; + this->aics[param_1].taxesMin = 3; + this->aics[param_1].taxesMax = 0xb; + this->aics[param_1].farm1 = 0x21; + this->aics[param_1].populationPerFarm = 4; + this->aics[param_1].unknown011 = 0x14; + this->aics[param_1].maxFood = 400; + this->aics[param_1].minimumApples = 10; + this->aics[param_1].minimumCheese = 10; + this->aics[param_1].minimumBread = 10; + this->aics[param_1].tradeAmountFood = 5; + this->aics[param_1].tradeAmountEquipment = 4; + this->aics[param_1].aiRequestDelay = 0xc; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 100; + this->aics[param_1].doubleRationsFoodThreshold = 0x3c; + this->aics[param_1].maxWood = 0x46; + this->aics[param_1].maxStone = 0x28; + this->aics[param_1].maxResourceOther = 10; + this->aics[param_1].maxEquipment = 10; + this->aics[param_1].maxBeer = 5; + this->aics[param_1].maxResourceVariance = 5; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerWoodcutter = 6; + this->aics[param_1].populationPerQuarry = 0x19; + this->aics[param_1].populationPerIronmine = 10; + this->aics[param_1].populationPerPitchrig = 0x14; + this->aics[param_1].maxQuarries = 2; + this->aics[param_1].maxIronmines = 4; + this->aics[param_1].maxWoodcutters = 8; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].maxFarms = 0xc; + this->aics[param_1].buildInterval = 3; + this->aics[param_1].resourceRebuildDelay = 0x32; + this->aics[param_1].blacksmithSetting = 0x15; + this->aics[param_1].fletcherSetting = 0x12; + this->aics[param_1].poleturnerSetting = 0x13; + this->aics[param_1].sellResource02 = 3; + this->aics[param_1].sellResource04 = 7; + this->aics[param_1].sellResource05 = 9; + this->aics[param_1].sellResource06 = 0xe; + this->aics[param_1].sellResource07 = 0x10; + this->aics[param_1].sellResource08 = 0x11; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource13 = 0x16; + this->aics[param_1].sellResource15 = 0x18; + this->aics[param_1].defWallPatrolRallyTime = 10; + this->aics[param_1].defWallPatrolGroups = 4; + this->aics[param_1].defSiegeEngineGoldThreshold = 100; + this->aics[param_1].defSiegeEngineBuildDelay = 0x14; + this->aics[param_1].unknown072 = 200; + this->aics[param_1].unknown073 = 0x28; + this->aics[param_1].recruitProbDefWeak = 100; + this->aics[param_1].recruitProbDefDefault = 0x3c; + this->aics[param_1].recruitProbDefStrong = 0x1e; + this->aics[param_1].recruitProbRaidWeak = 0; + this->aics[param_1].recruitProbRaidDefault = 0; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].RecruitInterval = 1; + this->aics[param_1].RecruitIntervalStrong = 4; + this->aics[param_1].SortieUnitRangedMin = 4; + this->aics[param_1].SortieUnitMeleeMin = 5; + this->aics[param_1].SortieUnitRanged = 0x17; + this->aics[param_1].SortieUnitMelee = 0x1a; + this->aics[param_1].OuterPatrolRallyDelay = 0x14; + this->aics[param_1].RaidUnitsRandom = 5; + this->aics[param_1].recruitProbRaidStrong = 0; + this->aics[param_1].recruitProbAttackWeak = 0; + this->aics[param_1].recruitProbAttackDefault = 0x28; + this->aics[param_1].recruitProbAttackStrong = 0x46; + this->aics[param_1].DefDiggingUnitMax = 0; + this->aics[param_1].DefDiggingUnit = 0x18; + this->aics[param_1].DefWalls = 0x1e; + this->aics[param_1].DefTotal = 0x28; + this->aics[param_1].OuterPatrolGroupsCount = 2; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].RaidUnitsBase = 10; + this->aics[param_1].HarassingSiegeEnginesMax = 2; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].RaidRetargetDelay = 3; + this->aics[param_1].AttForceBase = 10; + this->aics[param_1].AttForceRandom = 10; + this->aics[param_1].AttForceSupportAllyThreshold = 10; + this->aics[param_1].AttForceRallyPercentage = 0x3c; + this->aics[param_1].DefUnit1 = 0x17; + this->aics[param_1].DefUnit2 = 0x17; + this->aics[param_1].DefUnit3 = 0x17; + this->aics[param_1].RaidUnit1 = 0x1a; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 5; + this->aics[param_1].AttUnitPatrolRecommandDelay = 0xf; + this->aics[param_1].AttCoordinationPreference = 0x14; + this->aics[param_1].CowThrowInterval = 0x1e; + this->aics[param_1].SiegeEngine1 = 0x27; + this->aics[param_1].SiegeEngine2 = 0x27; + this->aics[param_1].SiegeEngine3 = 0x27; + this->aics[param_1].SiegeEngine4 = 0x3b; + this->aics[param_1].AttMaxEngineers = 10; + this->aics[param_1].AttDiggingUnit = 0x1a; + this->aics[param_1].AttDiggingUnitMax = 10; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x17; + this->aics[param_1].AttUnitBackupMax = 5; + this->aics[param_1].AttUnitBackupGroupsCount = 1; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0x1a; + this->aics[param_1].AttUnitSiegeDefMax = 0; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + this->aics[param_1].AttUnitMain1 = 0x1a; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 2; + this->aics[param_1].TargetChoice = AITC_GOLD; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_04.cpp b/src/OpenSHC/AI/AICState/setAICParameters_04.cpp new file mode 100644 index 00000000..91312740 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_04.cpp @@ -0,0 +1,149 @@ +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" +#include "OpenSHC/AI/AICState.hpp" + +namespace OpenSHC { +namespace AI { + + using AIC::AITC_ANY; + using AIC::AITC_BALANCED; + using AIC::AITC_CLOSEST; + using AIC::AITC_GOLD; + using AIC::AITC_PLAYER; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C7910 + void AICState::setAICParameters_04(int param_1) + { + this->aics[param_1].unknown004 = 1; + this->aics[param_1].unknown005 = 1; + this->aics[param_1].farm2 = 0x20; + this->aics[param_1].farm4 = 0x20; + this->aics[param_1].minimumWheat = -1; + this->aics[param_1].minimumHop = -1; + this->aics[param_1].flagType = 0xd; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].criticalPopularity = 0x1194; + this->aics[param_1].lowestPopularity = 6000; + this->aics[param_1].highestPopularity = 9000; + this->aics[param_1].taxesMin = 3; + this->aics[param_1].taxesMax = 0xb; + this->aics[param_1].farm1 = 0x21; + this->aics[param_1].farm3 = 0x1f; + this->aics[param_1].populationPerFarm = 5; + this->aics[param_1].unknown011 = 0xc; + this->aics[param_1].maxFood = 200; + this->aics[param_1].minimumApples = 5; + this->aics[param_1].minimumCheese = 5; + this->aics[param_1].minimumBread = 5; + this->aics[param_1].tradeAmountFood = 10; + this->aics[param_1].tradeAmountEquipment = 4; + this->aics[param_1].aiRequestDelay = 0x14; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 0x28; + this->aics[param_1].doubleRationsFoodThreshold = 0x1e; + this->aics[param_1].maxWood = 0x3c; + this->aics[param_1].maxStone = 0x32; + this->aics[param_1].maxResourceOther = 10; + this->aics[param_1].maxEquipment = 10; + this->aics[param_1].maxBeer = 0xf; + this->aics[param_1].maxResourceVariance = 5; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerWoodcutter = 4; + this->aics[param_1].populationPerQuarry = 9; + this->aics[param_1].populationPerIronmine = 0xd; + this->aics[param_1].populationPerPitchrig = 0xf; + this->aics[param_1].maxQuarries = 6; + this->aics[param_1].maxIronmines = 3; + this->aics[param_1].maxWoodcutters = 0xc; + this->aics[param_1].maxPitchrigs = 2; + this->aics[param_1].maxFarms = 10; + this->aics[param_1].buildInterval = 3; + this->aics[param_1].resourceRebuildDelay = 0x14; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x12; + this->aics[param_1].poleturnerSetting = 0x14; + this->aics[param_1].sellResource05 = 9; + this->aics[param_1].sellResource07 = 0x10; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].defWallPatrolRallyTime = 4; + this->aics[param_1].defWallPatrolGroups = 3; + this->aics[param_1].defSiegeEngineGoldThreshold = 0xfa; + this->aics[param_1].defSiegeEngineBuildDelay = 2; + this->aics[param_1].unknown072 = 100; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 0x5a; + this->aics[param_1].recruitProbDefDefault = 0x3c; + this->aics[param_1].recruitProbDefStrong = 0x1e; + this->aics[param_1].recruitProbRaidWeak = 10; + this->aics[param_1].recruitProbRaidDefault = 0x1e; + this->aics[param_1].recruitProbRaidStrong = 10; + this->aics[param_1].recruitProbAttackDefault = 10; + this->aics[param_1].RecruitInterval = 4; + this->aics[param_1].RecruitIntervalStrong = 4; + this->aics[param_1].OuterPatrolGroupsCount = 4; + this->aics[param_1].HarassingSiegeEnginesMax = 4; + this->aics[param_1].recruitProbAttackWeak = 0; + this->aics[param_1].OuterPatrolRallyDelay = 3; + this->aics[param_1].RaidUnitsRandom = 0x14; + this->aics[param_1].recruitProbAttackStrong = 0x3c; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].SortieUnitRangedMin = 8; + this->aics[param_1].SortieUnitRanged = 0x46; + this->aics[param_1].SortieUnitMeleeMin = 6; + this->aics[param_1].SortieUnitMelee = 0x19; + this->aics[param_1].DefDiggingUnitMax = 8; + this->aics[param_1].DefDiggingUnit = 0x19; + this->aics[param_1].DefWalls = 0x50; + this->aics[param_1].DefTotal = 100; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].DefUnit1 = 0x17; + this->aics[param_1].DefUnit2 = 0x17; + this->aics[param_1].DefUnit3 = 0x16; + this->aics[param_1].DefUnit4 = 0x16; + this->aics[param_1].RaidUnitsBase = 5; + this->aics[param_1].RaidUnit1 = 0x4a; + this->aics[param_1].RaidUnit2 = 0x4a; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].RaidRetargetDelay = 5; + this->aics[param_1].AttForceBase = 0x28; + this->aics[param_1].AttForceRandom = 0xf; + this->aics[param_1].AttForceSupportAllyThreshold = 0x1e; + this->aics[param_1].AttForceRallyPercentage = 0x50; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0xc; + this->aics[param_1].AttUnitPatrolRecommandDelay = 5; + this->aics[param_1].AttCoordinationPreference = 0x32; + this->aics[param_1].CowThrowInterval = 8; + this->aics[param_1].SiegeEngine1 = 0x27; + this->aics[param_1].SiegeEngine2 = 0x28; + this->aics[param_1].SiegeEngine3 = 0x4d; + this->aics[param_1].SiegeEngine4 = 0x28; + this->aics[param_1].AttMaxEngineers = 0xc; + this->aics[param_1].AttDiggingUnit = 0x19; + this->aics[param_1].AttDiggingUnitMax = 0x14; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x17; + this->aics[param_1].AttUnitBackupMax = 0x1e; + this->aics[param_1].AttUnitBackupGroupsCount = 3; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0x1b; + this->aics[param_1].AttUnitSiegeDefMax = 5; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 1; + this->aics[param_1].AttUnitMain1 = 0x1b; + this->aics[param_1].AttUnitMain2 = 0x19; + this->aics[param_1].AttUnitMain3 = 0x19; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 3; + this->aics[param_1].TargetChoice = AITC_CLOSEST; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_05.cpp b/src/OpenSHC/AI/AICState/setAICParameters_05.cpp new file mode 100644 index 00000000..ad9eb85f --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_05.cpp @@ -0,0 +1,148 @@ +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" +#include "OpenSHC/AI/AICState.hpp" + +namespace OpenSHC { +namespace AI { + + using AIC::AITC_ANY; + using AIC::AITC_BALANCED; + using AIC::AITC_CLOSEST; + using AIC::AITC_GOLD; + using AIC::AITC_PLAYER; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C7D20 + void AICState::setAICParameters_05(int param_1) + { + this->aics[param_1].maxFood = 0x32; + this->aics[param_1].maxStone = 0x32; + this->aics[param_1].taxesMin = 3; + this->aics[param_1].taxesMax = 0xc; + this->aics[param_1].farm2 = 0x1e; + this->aics[param_1].farm3 = 0x1e; + this->aics[param_1].populationPerFarm = 3; + this->aics[param_1].populationPerIronmine = 0xc; + this->aics[param_1].maxQuarries = 4; + this->aics[param_1].maxIronmines = 4; + this->aics[param_1].defWallPatrolGroups = 4; + this->aics[param_1].flagType = 10; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 0; + this->aics[param_1].unknown005 = 0; + this->aics[param_1].criticalPopularity = 4000; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 8000; + this->aics[param_1].farm1 = 0x1f; + this->aics[param_1].farm4 = 0x20; + this->aics[param_1].unknown011 = 0x14; + this->aics[param_1].minimumApples = 5; + this->aics[param_1].minimumCheese = 5; + this->aics[param_1].minimumBread = 5; + this->aics[param_1].minimumWheat = 5; + this->aics[param_1].minimumHop = 5; + this->aics[param_1].tradeAmountFood = 10; + this->aics[param_1].tradeAmountEquipment = 0; + this->aics[param_1].aiRequestDelay = 0x28; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 0x14; + this->aics[param_1].doubleRationsFoodThreshold = 0x14; + this->aics[param_1].maxWood = 0x3c; + this->aics[param_1].maxResourceOther = 10; + this->aics[param_1].maxEquipment = 5; + this->aics[param_1].maxBeer = 10; + this->aics[param_1].maxResourceVariance = 2; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerWoodcutter = 6; + this->aics[param_1].populationPerQuarry = 10; + this->aics[param_1].populationPerPitchrig = 0x14; + this->aics[param_1].maxWoodcutters = 0xe; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].maxFarms = 0xf; + this->aics[param_1].buildInterval = 2; + this->aics[param_1].resourceRebuildDelay = 0x28; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x12; + this->aics[param_1].poleturnerSetting = 0x14; + this->aics[param_1].defWallPatrolRallyTime = 3; + this->aics[param_1].defSiegeEngineGoldThreshold = 0x14; + this->aics[param_1].defSiegeEngineBuildDelay = 8; + this->aics[param_1].sellResource08 = 0x11; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].unknown072 = 100; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 100; + this->aics[param_1].recruitProbDefDefault = 0x22; + this->aics[param_1].recruitProbDefStrong = 0x19; + this->aics[param_1].RaidUnitsRandom = 5; + this->aics[param_1].HarassingSiegeEnginesMax = 5; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].RecruitInterval = 1; + this->aics[param_1].RecruitIntervalStrong = 1; + this->aics[param_1].SortieUnitRangedMin = 10; + this->aics[param_1].SortieUnitMeleeMin = 7; + this->aics[param_1].SortieUnitMelee = 0x4b; + this->aics[param_1].SortieUnitRanged = 0x46; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].OuterPatrolRallyDelay = 10; + this->aics[param_1].RaidUnitsBase = 10; + this->aics[param_1].AttForceRandom = 10; + this->aics[param_1].AttForceBase = 0x32; + this->aics[param_1].AttForceSupportAllyThreshold = 8; + this->aics[param_1].AttForceRallyPercentage = 0x50; + this->aics[param_1].recruitProbRaidWeak = 0; + this->aics[param_1].recruitProbRaidStrong = 0x19; + this->aics[param_1].recruitProbAttackWeak = 0; + this->aics[param_1].recruitProbAttackStrong = 0x32; + this->aics[param_1].DefDiggingUnitMax = 4; + this->aics[param_1].DefDiggingUnit = 0x47; + this->aics[param_1].DefWalls = 0x32; + this->aics[param_1].OuterPatrolGroupsCount = 2; + this->aics[param_1].DefUnit1 = 0x4b; + this->aics[param_1].DefUnit2 = 0x46; + this->aics[param_1].DefUnit3 = 0x46; + this->aics[param_1].DefUnit4 = 0x46; + this->aics[param_1].DefUnit5 = 0x4c; + this->aics[param_1].RaidUnit1 = 0x4a; + this->aics[param_1].RaidRetargetDelay = 3; + this->aics[param_1].recruitProbRaidDefault = 0x21; + this->aics[param_1].recruitProbAttackDefault = 0x21; + this->aics[param_1].DefTotal = 0x46; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 4; + this->aics[param_1].AttUnitPatrolRecommandDelay = 1; + this->aics[param_1].AttCoordinationPreference = 0x3c; + this->aics[param_1].CowThrowInterval = 2000; + this->aics[param_1].SiegeEngine1 = 0x4d; + this->aics[param_1].SiegeEngine2 = 0x4d; + this->aics[param_1].SiegeEngine3 = 0x27; + this->aics[param_1].SiegeEngine4 = 0x27; + this->aics[param_1].AttMaxEngineers = 10; + this->aics[param_1].AttDiggingUnit = 0x47; + this->aics[param_1].AttDiggingUnitMax = 0x14; + this->aics[param_1].AttMaxAssassins = 5; + this->aics[param_1].AttUnit2 = 0x4a; + this->aics[param_1].AttUnit2Max = 0x14; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0x4a; + this->aics[param_1].AttUnitPatrolMax = 0x14; + this->aics[param_1].AttUnitPatrolGroupsCount = 1; + this->aics[param_1].AttUnitBackup = 0x46; + this->aics[param_1].AttUnitBackupMax = 0x14; + this->aics[param_1].AttUnitBackupGroupsCount = 3; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0x4c; + this->aics[param_1].AttUnitSiegeDef = 0x4c; + this->aics[param_1].AttUnitSiegeDefMax = 6; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 1; + this->aics[param_1].AttUnitMain1 = 0x4b; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 3; + this->aics[param_1].TargetChoice = AITC_GOLD; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_06.cpp b/src/OpenSHC/AI/AICState/setAICParameters_06.cpp new file mode 100644 index 00000000..b80d1635 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_06.cpp @@ -0,0 +1,289 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004C8110 + void AICState ::setAICParameters_06(int param_1) + + { + + this->aics[param_1].minimumCheese = -1; + + this->aics[param_1].minimumBread = -1; + + this->aics[param_1].minimumWheat = -1; + + this->aics[param_1].minimumHop = -1; + + this->aics[param_1].taxesMax = 0xc; + + this->aics[param_1].farm1 = 0x20; + + this->aics[param_1].farm3 = 0x20; + + this->aics[param_1].unknown011 = 0xc; + + this->aics[param_1].aiRequestDelay = 0xc; + + this->aics[param_1].minimumGoodsRequiredAfterTrade = 0x32; + + this->aics[param_1].doubleRationsFoodThreshold = 0x32; + + this->aics[param_1].maxWood = 0x32; + + this->aics[param_1].maxStone = 0x32; + + this->aics[param_1].populationPerIronmine = 0xc; + + this->aics[param_1].flagType = 10; + + this->aics[param_1].unknown002 = 0; + + this->aics[param_1].unknown001 = 0; + + this->aics[param_1].unknown003 = 0; + + this->aics[param_1].unknown004 = 0; + + this->aics[param_1].unknown005 = 1; + + this->aics[param_1].criticalPopularity = 4000; + + this->aics[param_1].lowestPopularity = 5000; + + this->aics[param_1].highestPopularity = 9000; + + this->aics[param_1].taxesMin = 3; + + this->aics[param_1].farm2 = 0x1f; + + this->aics[param_1].populationPerFarm = 5; + + this->aics[param_1].maxFood = 100; + + this->aics[param_1].minimumApples = 10; + + this->aics[param_1].tradeAmountFood = 10; + + this->aics[param_1].tradeAmountEquipment = 0; + + this->aics[param_1].maxResourceOther = 10; + + this->aics[param_1].maxEquipment = 5; + + this->aics[param_1].maxBeer = 0xf; + + this->aics[param_1].maxResourceVariance = 5; + + this->aics[param_1].recruitGoldThreshold = 200; + + this->aics[param_1].populationPerWoodcutter = 7; + + this->aics[param_1].populationPerQuarry = 10; + + this->aics[param_1].populationPerPitchrig = 7; + + this->aics[param_1].maxQuarries = 2; + + this->aics[param_1].maxIronmines = 1; + + this->aics[param_1].maxWoodcutters = 8; + + this->aics[param_1].maxPitchrigs = 10; + + this->aics[param_1].maxFarms = 10; + + this->aics[param_1].buildInterval = 3; + + this->aics[param_1].resourceRebuildDelay = 0x3c; + + this->aics[param_1].blacksmithSetting = 0x16; + + this->aics[param_1].fletcherSetting = 0x12; + + this->aics[param_1].poleturnerSetting = 0x14; + + this->aics[param_1].defWallPatrolRallyTime = 5; + + this->aics[param_1].defWallPatrolGroups = 2; + + this->aics[param_1].defSiegeEngineGoldThreshold = 0x78; + + this->aics[param_1].defSiegeEngineBuildDelay = 8; + + this->aics[param_1].sellResource03 = 6; + + this->aics[param_1].sellResource05 = 9; + + this->aics[param_1].sellResource07 = 0x10; + + this->aics[param_1].sellResource08 = 0x11; + + this->aics[param_1].sellResource09 = 0x12; + + this->aics[param_1].sellResource10 = 0x13; + + this->aics[param_1].sellResource11 = 0x14; + + this->aics[param_1].sellResource12 = 0x15; + + this->aics[param_1].sellResource13 = 0x16; + + this->aics[param_1].sellResource14 = 0x17; + + this->aics[param_1].sellResource15 = 0x18; + + this->aics[param_1].unknown072 = 100; + + this->aics[param_1].unknown073 = 0x14; + + this->aics[param_1].recruitProbRaidWeak = 0x14; + + this->aics[param_1].recruitProbAttackDefault = 0x14; + + this->aics[param_1].recruitProbAttackWeak = 0; + + this->aics[param_1].RecruitInterval = 0; + + this->aics[param_1].RecruitIntervalStrong = 0; + + this->aics[param_1].SortieUnitRangedMin = 7; + + this->aics[param_1].SortieUnitMeleeMin = 5; + + this->aics[param_1].SortieUnitRanged = 0x46; + + this->aics[param_1].SortieUnitMelee = 0x4b; + + this->aics[param_1].OuterPatrolGroupsCount = 2; + + this->aics[param_1].HarassingSiegeEnginesMax = 2; + + this->aics[param_1].recruitProbDefWeak = 0x50; + + this->aics[param_1].recruitProbDefDefault = 0x28; + + this->aics[param_1].recruitProbDefStrong = 0x28; + + this->aics[param_1].recruitProbRaidDefault = 0x28; + + this->aics[param_1].recruitProbRaidStrong = 0x1e; + + this->aics[param_1].recruitProbAttackStrong = 0x1e; + + this->aics[param_1].RecruitIntervalWeak = 1; + + this->aics[param_1].DefDiggingUnitMax = 4; + + this->aics[param_1].DefDiggingUnit = 0x47; + + this->aics[param_1].DefWalls = 0x3c; + + this->aics[param_1].DefTotal = 0x3c; + + this->aics[param_1].OuterPatrolGroupsMove = 1; + + this->aics[param_1].OuterPatrolRallyDelay = 10; + + this->aics[param_1].DefUnit1 = 0x4c; + + this->aics[param_1].DefUnit2 = 0x46; + + this->aics[param_1].DefUnit3 = 0x46; + + this->aics[param_1].DefUnit4 = 0x46; + + this->aics[param_1].RaidUnitsBase = 0x1e; + + this->aics[param_1].RaidUnitsRandom = 0x1e; + + this->aics[param_1].RaidUnit1 = 0x47; + + this->aics[param_1].HarassingSiegeEngine1 = 0x166; + + this->aics[param_1].HarassingSiegeEngine2 = 0xbe; + + this->aics[param_1].RaidRetargetDelay = 3; + + this->aics[param_1].AttForceBase = 0xf; + + this->aics[param_1].AttForceRandom = 10; + + this->aics[param_1].AttForceSupportAllyThreshold = 0x46; + + this->aics[param_1].AttForceRallyPercentage = 100; + + this->aics[param_1].AttMovingTribesThreshold = 100; + + this->aics[param_1].AttAssaultDelay = 2; + + this->aics[param_1].AttUnitPatrolRecommandDelay = 10; + + this->aics[param_1].AttCoordinationPreference = 0x32; + + this->aics[param_1].CowThrowInterval = 7; + + this->aics[param_1].SiegeEngine1 = 0x4d; + + this->aics[param_1].SiegeEngine2 = 0x4d; + + this->aics[param_1].SiegeEngine3 = 0x4d; + + this->aics[param_1].AttMaxEngineers = 6; + + this->aics[param_1].AttDiggingUnit = 0x47; + + this->aics[param_1].AttDiggingUnitMax = 0x14; + + this->aics[param_1].AttMaxAssassins = 0; + + this->aics[param_1].AttUnit2 = 0x4b; + + this->aics[param_1].AttUnit2Max = 0; + + this->aics[param_1].AttMaxLaddermen = 0; + + this->aics[param_1].AttMaxTunnelers = 0; + + this->aics[param_1].AttUnitPatrol = 0x4a; + + this->aics[param_1].AttUnitPatrolMax = 0; + + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + + this->aics[param_1].AttUnitBackup = 0x4a; + + this->aics[param_1].AttUnitBackupMax = 0x28; + + this->aics[param_1].AttUnitBackupGroupsCount = 2; + + this->aics[param_1].AttUnitEngageMax = 0; + + this->aics[param_1].AttUnitEngage = 0; + + this->aics[param_1].AttUnitSiegeDef = 0x46; + + this->aics[param_1].AttUnitSiegeDefMax = 10; + + this->aics[param_1].AttUnitSiegeDefGroupsCount = 1; + + this->aics[param_1].AttUnitMain1 = 0x4b; + + this->aics[param_1].AttMaxDefault = 200; + + this->aics[param_1].AttMainGroupsCount = 5; + + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_BALANCED; + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_07.cpp b/src/OpenSHC/AI/AICState/setAICParameters_07.cpp new file mode 100644 index 00000000..589ad5bd --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_07.cpp @@ -0,0 +1,283 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004C8530 + void AICState ::setAICParameters_07(int param_1) + + { + + this->aics[param_1].minimumCheese = -1; + + this->aics[param_1].minimumBread = -1; + + this->aics[param_1].minimumWheat = -1; + + this->aics[param_1].minimumHop = -1; + + this->aics[param_1].flagType = 10; + + this->aics[param_1].unknown002 = 0; + + this->aics[param_1].unknown001 = 0; + + this->aics[param_1].unknown003 = 0; + + this->aics[param_1].unknown004 = 0; + + this->aics[param_1].unknown005 = 0; + + this->aics[param_1].criticalPopularity = 5000; + + this->aics[param_1].lowestPopularity = 6000; + + this->aics[param_1].highestPopularity = 9000; + + this->aics[param_1].taxesMin = 1; + + this->aics[param_1].taxesMax = 8; + + this->aics[param_1].farm1 = 0; + + this->aics[param_1].populationPerFarm = 100; + + this->aics[param_1].unknown011 = 0x14; + + this->aics[param_1].maxFood = 500; + + this->aics[param_1].minimumApples = 0x14; + + this->aics[param_1].tradeAmountFood = 10; + + this->aics[param_1].tradeAmountEquipment = 0; + + this->aics[param_1].aiRequestDelay = 0x1e; + + this->aics[param_1].minimumGoodsRequiredAfterTrade = 0; + + this->aics[param_1].doubleRationsFoodThreshold = 0; + + this->aics[param_1].maxWood = 0x1e; + + this->aics[param_1].maxStone = 0x14; + + this->aics[param_1].maxResourceOther = 10; + + this->aics[param_1].maxEquipment = 5; + + this->aics[param_1].maxBeer = 0x14; + + this->aics[param_1].maxResourceVariance = 5; + + this->aics[param_1].recruitGoldThreshold = 200; + + this->aics[param_1].populationPerWoodcutter = 2; + + this->aics[param_1].populationPerQuarry = 5; + + this->aics[param_1].populationPerIronmine = 0; + + this->aics[param_1].populationPerPitchrig = 0; + + this->aics[param_1].maxQuarries = 2; + + this->aics[param_1].maxIronmines = 1; + + this->aics[param_1].maxWoodcutters = 0xf; + + this->aics[param_1].maxPitchrigs = 1; + + this->aics[param_1].maxFarms = 6; + + this->aics[param_1].buildInterval = 5; + + this->aics[param_1].resourceRebuildDelay = 0x50; + + this->aics[param_1].blacksmithSetting = 0x16; + + this->aics[param_1].fletcherSetting = 0x11; + + this->aics[param_1].poleturnerSetting = 0x13; + + this->aics[param_1].sellResource02 = 3; + + this->aics[param_1].sellResource03 = 6; + + this->aics[param_1].sellResource04 = 7; + + this->aics[param_1].sellResource05 = 9; + + this->aics[param_1].sellResource06 = 0xe; + + this->aics[param_1].sellResource07 = 0x10; + + this->aics[param_1].sellResource08 = 0x11; + + this->aics[param_1].sellResource09 = 0x12; + + this->aics[param_1].sellResource10 = 0x13; + + this->aics[param_1].sellResource11 = 0x14; + + this->aics[param_1].sellResource12 = 0x15; + + this->aics[param_1].sellResource13 = 0x16; + + this->aics[param_1].sellResource14 = 0x17; + + this->aics[param_1].sellResource15 = 0x18; + + this->aics[param_1].defWallPatrolRallyTime = 10; + + this->aics[param_1].defWallPatrolGroups = 2; + + this->aics[param_1].defSiegeEngineGoldThreshold = 200; + + this->aics[param_1].defSiegeEngineBuildDelay = 0x14; + + this->aics[param_1].unknown072 = 800; + + this->aics[param_1].unknown073 = 0x50; + + this->aics[param_1].recruitProbDefStrong = 0x1e; + + this->aics[param_1].recruitProbRaidWeak = 10; + + this->aics[param_1].recruitProbRaidStrong = 0x1e; + + this->aics[param_1].recruitProbAttackDefault = 0x1e; + + this->aics[param_1].DefDiggingUnitMax = 2; + + this->aics[param_1].OuterPatrolGroupsCount = 1; + + this->aics[param_1].OuterPatrolRallyDelay = 10; + + this->aics[param_1].RaidUnitsRandom = 10; + + this->aics[param_1].recruitProbDefWeak = 0x46; + + this->aics[param_1].recruitProbDefDefault = 0x32; + + this->aics[param_1].recruitProbRaidDefault = 0x14; + + this->aics[param_1].recruitProbAttackWeak = 0x14; + + this->aics[param_1].recruitProbAttackStrong = 0x28; + + this->aics[param_1].RecruitIntervalWeak = 8; + + this->aics[param_1].RecruitInterval = 8; + + this->aics[param_1].RecruitIntervalStrong = 4; + + this->aics[param_1].SortieUnitRangedMin = 0; + + this->aics[param_1].SortieUnitRanged = 0x48; + + this->aics[param_1].SortieUnitMeleeMin = 3; + + this->aics[param_1].SortieUnitMelee = 0x4b; + + this->aics[param_1].DefDiggingUnit = 0x47; + + this->aics[param_1].DefWalls = 0x28; + + this->aics[param_1].DefTotal = 0x32; + + this->aics[param_1].OuterPatrolGroupsMove = 0; + + this->aics[param_1].DefUnit1 = 0x48; + + this->aics[param_1].DefUnit2 = 0x48; + + this->aics[param_1].DefUnit3 = 0x4b; + + this->aics[param_1].RaidUnitsBase = 0x14; + + this->aics[param_1].RaidUnit1 = 0x48; + + this->aics[param_1].HarassingSiegeEnginesMax = 0; + + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + + this->aics[param_1].RaidRetargetDelay = 3; + + this->aics[param_1].AttForceBase = 10; + + this->aics[param_1].AttForceRandom = 0x14; + + this->aics[param_1].AttForceSupportAllyThreshold = 5; + + this->aics[param_1].AttForceRallyPercentage = 0x1e; + + this->aics[param_1].AttMovingTribesThreshold = 100; + + this->aics[param_1].AttAssaultDelay = 0; + + this->aics[param_1].AttUnitPatrolRecommandDelay = 0x28; + + this->aics[param_1].AttCoordinationPreference = 0x14; + + this->aics[param_1].CowThrowInterval = 10000; + + this->aics[param_1].SiegeEngine1 = 0; + + this->aics[param_1].AttMaxEngineers = 0; + + this->aics[param_1].AttDiggingUnit = 0; + + this->aics[param_1].AttDiggingUnitMax = 0; + + this->aics[param_1].AttMaxAssassins = 0; + + this->aics[param_1].AttUnit2 = 0; + + this->aics[param_1].AttUnit2Max = 0; + + this->aics[param_1].AttMaxLaddermen = 0; + + this->aics[param_1].AttMaxTunnelers = 0; + + this->aics[param_1].AttUnitPatrol = 0; + + this->aics[param_1].AttUnitPatrolMax = 0; + + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + + this->aics[param_1].AttUnitBackup = 0x46; + + this->aics[param_1].AttUnitBackupMax = 0x78; + + this->aics[param_1].AttUnitBackupGroupsCount = 1; + + this->aics[param_1].AttUnitEngageMax = 0; + + this->aics[param_1].AttUnitEngage = 0; + + this->aics[param_1].AttUnitSiegeDef = 0; + + this->aics[param_1].AttUnitSiegeDefMax = 0; + + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + + this->aics[param_1].AttUnitMain1 = 0x4b; + + this->aics[param_1].AttMaxDefault = 200; + + this->aics[param_1].AttMainGroupsCount = 1; + + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_CLOSEST; + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_08.cpp b/src/OpenSHC/AI/AICState/setAICParameters_08.cpp new file mode 100644 index 00000000..038ccf90 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_08.cpp @@ -0,0 +1,151 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + // FUNCTION: STRONGHOLDCRUSADER 0x004C8920 + void AICState ::setAICParameters_08(int param_1) + { + this->aics[param_1].farm2 = 0x20; + this->aics[param_1].farm3 = 0x20; + this->aics[param_1].unknown004 = 1; + this->aics[param_1].minimumApples = 5; + this->aics[param_1].minimumCheese = 5; + this->aics[param_1].minimumBread = -1; + this->aics[param_1].minimumWheat = -1; + this->aics[param_1].minimumHop = -1; + this->aics[param_1].tradeAmountFood = 5; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].flagType = 0xd; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown005 = 0; + this->aics[param_1].criticalPopularity = 4000; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 8000; + this->aics[param_1].taxesMin = 3; + this->aics[param_1].taxesMax = 10; + this->aics[param_1].farm1 = 0x21; + this->aics[param_1].populationPerFarm = 4; + this->aics[param_1].unknown011 = 0xc; + this->aics[param_1].maxFood = 200; + this->aics[param_1].tradeAmountEquipment = 2; + this->aics[param_1].aiRequestDelay = 0x16; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 10; + this->aics[param_1].doubleRationsFoodThreshold = 0x28; + this->aics[param_1].maxWood = 0x32; + this->aics[param_1].maxStone = 0x3c; + this->aics[param_1].maxResourceOther = 10; + this->aics[param_1].maxEquipment = 10; + this->aics[param_1].maxBeer = 10; + this->aics[param_1].maxResourceVariance = 4; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerWoodcutter = 7; + this->aics[param_1].populationPerQuarry = 10; + this->aics[param_1].populationPerIronmine = 10; + this->aics[param_1].populationPerPitchrig = 0; + this->aics[param_1].maxQuarries = 3; + this->aics[param_1].maxIronmines = 4; + this->aics[param_1].maxWoodcutters = 10; + this->aics[param_1].maxFarms = 0xf; + this->aics[param_1].buildInterval = 4; + this->aics[param_1].resourceRebuildDelay = 0x14; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x11; + this->aics[param_1].poleturnerSetting = 0x14; + this->aics[param_1].sellResource02 = 3; + this->aics[param_1].sellResource05 = 9; + this->aics[param_1].sellResource06 = 0xe; + this->aics[param_1].sellResource07 = 0x10; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].defWallPatrolRallyTime = 8; + this->aics[param_1].defWallPatrolGroups = 4; + this->aics[param_1].defSiegeEngineGoldThreshold = 200; + this->aics[param_1].defSiegeEngineBuildDelay = 2; + this->aics[param_1].unknown072 = 100; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 0x5a; + this->aics[param_1].recruitProbDefDefault = 0x1e; + this->aics[param_1].recruitProbDefStrong = 10; + this->aics[param_1].recruitProbAttackWeak = 10; + this->aics[param_1].RaidUnitsBase = 10; + this->aics[param_1].recruitProbRaidWeak = 0; + this->aics[param_1].recruitProbRaidDefault = 0; + this->aics[param_1].recruitProbRaidStrong = 0; + this->aics[param_1].SortieUnitRanged = 0x16; + this->aics[param_1].DefDiggingUnitMax = 0; + this->aics[param_1].DefWalls = 0x14; + this->aics[param_1].OuterPatrolGroupsCount = 2; + this->aics[param_1].DefUnit1 = 0x16; + this->aics[param_1].DefUnit2 = 0x16; + this->aics[param_1].RaidUnitsRandom = 4; + this->aics[param_1].RaidUnit1 = 0x16; + this->aics[param_1].HarassingSiegeEnginesMax = 2; + this->aics[param_1].AttForceBase = 0x14; + this->aics[param_1].recruitProbAttackDefault = 0x46; + this->aics[param_1].recruitProbAttackStrong = 0x5a; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].RecruitInterval = 1; + this->aics[param_1].RecruitIntervalStrong = 1; + this->aics[param_1].SortieUnitRangedMin = 6; + this->aics[param_1].SortieUnitMeleeMin = 6; + this->aics[param_1].SortieUnitMelee = 0x19; + this->aics[param_1].DefDiggingUnit = 0x19; + this->aics[param_1].DefTotal = 0x28; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].OuterPatrolRallyDelay = 5; + this->aics[param_1].DefUnit3 = 0x19; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].RaidRetargetDelay = 5; + this->aics[param_1].AttForceRandom = 5; + this->aics[param_1].AttForceSupportAllyThreshold = 10; + this->aics[param_1].AttForceRallyPercentage = 10; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0x14; + this->aics[param_1].AttUnitPatrolRecommandDelay = 2; + this->aics[param_1].AttCoordinationPreference = 10; + this->aics[param_1].CowThrowInterval = 10000; + this->aics[param_1].SiegeEngine1 = 0x27; + this->aics[param_1].SiegeEngine2 = 0x27; + this->aics[param_1].SiegeEngine3 = 0x27; + this->aics[param_1].SiegeEngine4 = 0x3b; + this->aics[param_1].SiegeEngine5 = 0x28; + this->aics[param_1].SiegeEngine6 = 0x28; + this->aics[param_1].AttMaxEngineers = 0x10; + this->aics[param_1].AttDiggingUnit = 0x19; + this->aics[param_1].AttDiggingUnitMax = 6; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 3; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x16; + this->aics[param_1].AttUnitBackupMax = 0x14; + this->aics[param_1].AttUnitBackupGroupsCount = 2; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0x1b; + this->aics[param_1].AttUnitSiegeDefMax = 4; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 2; + this->aics[param_1].AttUnitMain1 = 0x1b; + this->aics[param_1].AttUnitMain2 = 0x19; + this->aics[param_1].AttUnitMain3 = 0x19; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 3; + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_CLOSEST; + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_09.cpp b/src/OpenSHC/AI/AICState/setAICParameters_09.cpp new file mode 100644 index 00000000..1000c850 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_09.cpp @@ -0,0 +1,148 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + // FUNCTION: STRONGHOLDCRUSADER 0x004C8D20 + void AICState ::setAICParameters_09(int param_1) + { + this->aics[param_1].unknown004 = 1; + this->aics[param_1].unknown005 = 1; + this->aics[param_1].minimumApples = 5; + this->aics[param_1].minimumCheese = 5; + this->aics[param_1].minimumBread = -1; + this->aics[param_1].minimumWheat = -1; + this->aics[param_1].minimumHop = -1; + this->aics[param_1].maxWood = 0x3c; + this->aics[param_1].maxStone = 0x3c; + this->aics[param_1].flagType = 0xd; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].criticalPopularity = 4000; + this->aics[param_1].lowestPopularity = 0x157c; + this->aics[param_1].highestPopularity = 9000; + this->aics[param_1].taxesMin = 3; + this->aics[param_1].taxesMax = 10; + this->aics[param_1].farm1 = 0x21; + this->aics[param_1].farm2 = 0x20; + this->aics[param_1].farm3 = 0x1f; + this->aics[param_1].populationPerFarm = 4; + this->aics[param_1].unknown011 = 0xc; + this->aics[param_1].maxFood = 200; + this->aics[param_1].tradeAmountFood = 10; + this->aics[param_1].tradeAmountEquipment = 0; + this->aics[param_1].aiRequestDelay = 0x16; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 10; + this->aics[param_1].doubleRationsFoodThreshold = 0x28; + this->aics[param_1].maxResourceOther = 0xf; + this->aics[param_1].maxEquipment = 0x14; + this->aics[param_1].maxBeer = 10; + this->aics[param_1].maxResourceVariance = 4; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerWoodcutter = 7; + this->aics[param_1].populationPerQuarry = 0xc; + this->aics[param_1].populationPerIronmine = 10; + this->aics[param_1].populationPerPitchrig = 0; + this->aics[param_1].maxQuarries = 4; + this->aics[param_1].maxIronmines = 5; + this->aics[param_1].maxWoodcutters = 0xc; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].maxFarms = 0xc; + this->aics[param_1].buildInterval = 4; + this->aics[param_1].resourceRebuildDelay = 0x1e; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = -999; + this->aics[param_1].poleturnerSetting = 0x14; + this->aics[param_1].sellResource04 = 7; + this->aics[param_1].sellResource05 = 9; + this->aics[param_1].sellResource07 = 0x10; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].defWallPatrolRallyTime = 8; + this->aics[param_1].defWallPatrolGroups = 2; + this->aics[param_1].defSiegeEngineGoldThreshold = 200; + this->aics[param_1].defSiegeEngineBuildDelay = 2; + this->aics[param_1].unknown072 = 100; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 100; + this->aics[param_1].recruitProbDefDefault = 0x32; + this->aics[param_1].recruitProbDefStrong = 0x14; + this->aics[param_1].recruitProbRaidWeak = 0; + this->aics[param_1].recruitProbRaidDefault = 0x14; + this->aics[param_1].AttForceSupportAllyThreshold = 0x14; + this->aics[param_1].RecruitInterval = 4; + this->aics[param_1].RecruitIntervalStrong = 4; + this->aics[param_1].SortieUnitRanged = 0x16; + this->aics[param_1].SortieUnitMeleeMin = 4; + this->aics[param_1].DefDiggingUnitMax = 10; + this->aics[param_1].DefDiggingUnit = 0x16; + this->aics[param_1].OuterPatrolRallyDelay = 10; + this->aics[param_1].DefUnit1 = 0x16; + this->aics[param_1].DefUnit2 = 0x16; + this->aics[param_1].RaidUnitsBase = 4; + this->aics[param_1].AttForceRandom = 4; + this->aics[param_1].recruitProbRaidStrong = 0x1e; + this->aics[param_1].recruitProbAttackWeak = 0; + this->aics[param_1].recruitProbAttackDefault = 0x1e; + this->aics[param_1].recruitProbAttackStrong = 0x32; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].SortieUnitRangedMin = 0xc; + this->aics[param_1].SortieUnitMelee = 0x1b; + this->aics[param_1].DefWalls = 0x32; + this->aics[param_1].DefTotal = 0x50; + this->aics[param_1].OuterPatrolGroupsCount = 2; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].DefUnit3 = 0x17; + this->aics[param_1].RaidUnitsRandom = 0; + this->aics[param_1].RaidUnit1 = 0x1c; + this->aics[param_1].HarassingSiegeEnginesMax = 2; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].RaidRetargetDelay = 6; + this->aics[param_1].AttForceBase = 0x1e; + this->aics[param_1].AttForceRallyPercentage = 0x32; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0x14; + this->aics[param_1].AttUnitPatrolRecommandDelay = 5; + this->aics[param_1].AttCoordinationPreference = 0x32; + this->aics[param_1].CowThrowInterval = 10000; + this->aics[param_1].SiegeEngine1 = 0x28; + this->aics[param_1].SiegeEngine2 = 0x28; + this->aics[param_1].SiegeEngine3 = 0x28; + this->aics[param_1].SiegeEngine4 = 0x28; + this->aics[param_1].SiegeEngine5 = 0x28; + this->aics[param_1].SiegeEngine6 = 0x28; + this->aics[param_1].AttMaxEngineers = 0x12; + this->aics[param_1].AttDiggingUnit = 0x16; + this->aics[param_1].AttDiggingUnitMax = 0x14; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0x17; + this->aics[param_1].AttUnitPatrolMax = 0x14; + this->aics[param_1].AttUnitPatrolGroupsCount = 1; + this->aics[param_1].AttUnitBackup = 0x16; + this->aics[param_1].AttUnitBackupMax = 0x1e; + this->aics[param_1].AttUnitBackupGroupsCount = 2; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0x17; + this->aics[param_1].AttUnitSiegeDefMax = 10; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 1; + this->aics[param_1].AttUnitMain1 = 0x1b; + this->aics[param_1].AttUnitMain2 = 0x1b; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 4; + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_GOLD; + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_10.cpp b/src/OpenSHC/AI/AICState/setAICParameters_10.cpp new file mode 100644 index 00000000..8b1e50bc --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_10.cpp @@ -0,0 +1,147 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + // FUNCTION: STRONGHOLDCRUSADER 0x004C9120 + void AICState ::setAICParameters_10(int param_1) + { + this->aics[param_1].taxesMin = 2; + this->aics[param_1].farm1 = 0x20; + this->aics[param_1].farm2 = 0x20; + this->aics[param_1].tradeAmountEquipment = 2; + this->aics[param_1].minimumWheat = -1; + this->aics[param_1].minimumHop = -1; + this->aics[param_1].maxWood = 0x3c; + this->aics[param_1].maxStone = 0x3c; + this->aics[param_1].resourceRebuildDelay = 0x3c; + this->aics[param_1].flagType = 0xd; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 1; + this->aics[param_1].unknown005 = 1; + this->aics[param_1].criticalPopularity = 4000; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 8000; + this->aics[param_1].taxesMax = 10; + this->aics[param_1].populationPerFarm = 5; + this->aics[param_1].unknown011 = 10; + this->aics[param_1].maxFood = 100; + this->aics[param_1].minimumApples = 10; + this->aics[param_1].minimumCheese = 10; + this->aics[param_1].minimumBread = 10; + this->aics[param_1].tradeAmountFood = 0x14; + this->aics[param_1].aiRequestDelay = 0xc; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 10; + this->aics[param_1].doubleRationsFoodThreshold = 0x32; + this->aics[param_1].maxResourceOther = 0xf; + this->aics[param_1].maxEquipment = 6; + this->aics[param_1].maxBeer = 10; + this->aics[param_1].maxResourceVariance = 4; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerWoodcutter = 7; + this->aics[param_1].populationPerQuarry = 0; + this->aics[param_1].populationPerIronmine = 7; + this->aics[param_1].populationPerPitchrig = 0; + this->aics[param_1].maxQuarries = 1; + this->aics[param_1].maxIronmines = 3; + this->aics[param_1].maxWoodcutters = 10; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].maxFarms = 8; + this->aics[param_1].buildInterval = 4; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x11; + this->aics[param_1].poleturnerSetting = 0x13; + this->aics[param_1].sellResource02 = 3; + this->aics[param_1].sellResource04 = 7; + this->aics[param_1].sellResource05 = 9; + this->aics[param_1].sellResource06 = 0xe; + this->aics[param_1].sellResource07 = 0x10; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].defWallPatrolRallyTime = 2; + this->aics[param_1].defWallPatrolGroups = 3; + this->aics[param_1].defSiegeEngineGoldThreshold = 100; + this->aics[param_1].defSiegeEngineBuildDelay = 4; + this->aics[param_1].unknown072 = 200; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 0x46; + this->aics[param_1].recruitProbDefDefault = 0x28; + this->aics[param_1].RecruitIntervalWeak = 4; + this->aics[param_1].RecruitInterval = 4; + this->aics[param_1].RecruitIntervalStrong = 4; + this->aics[param_1].DefDiggingUnitMax = 4; + this->aics[param_1].DefWalls = 4; + this->aics[param_1].OuterPatrolGroupsCount = 4; + this->aics[param_1].RaidUnitsBase = 4; + this->aics[param_1].RaidUnitsRandom = 4; + this->aics[param_1].recruitProbDefStrong = 0x14; + this->aics[param_1].recruitProbRaidWeak = 0x14; + this->aics[param_1].recruitProbRaidStrong = 0x14; + this->aics[param_1].recruitProbAttackWeak = 10; + this->aics[param_1].recruitProbAttackDefault = 0x14; + this->aics[param_1].SortieUnitRanged = 0x16; + this->aics[param_1].SortieUnitMeleeMin = 0x14; + this->aics[param_1].DefTotal = 0x14; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].DefUnit2 = 0x16; + this->aics[param_1].DefUnit3 = 0x16; + this->aics[param_1].DefUnit4 = 0x16; + this->aics[param_1].DefUnit5 = 0x16; + this->aics[param_1].DefUnit6 = 0x16; + this->aics[param_1].HarassingSiegeEnginesMax = 1; + this->aics[param_1].recruitProbRaidDefault = 0x28; + this->aics[param_1].recruitProbAttackStrong = 0x3c; + this->aics[param_1].SortieUnitRangedMin = 2; + this->aics[param_1].SortieUnitMelee = 0x18; + this->aics[param_1].DefDiggingUnit = 0x18; + this->aics[param_1].OuterPatrolRallyDelay = 2; + this->aics[param_1].DefUnit1 = 0x1c; + this->aics[param_1].RaidUnit1 = 0x1c; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].RaidRetargetDelay = 4; + this->aics[param_1].AttForceBase = 10; + this->aics[param_1].AttForceRandom = 0x14; + this->aics[param_1].AttForceSupportAllyThreshold = 0x28; + this->aics[param_1].AttForceRallyPercentage = 0x28; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0; + this->aics[param_1].AttUnitPatrolRecommandDelay = 5; + this->aics[param_1].AttCoordinationPreference = 0x46; + this->aics[param_1].CowThrowInterval = 10000; + this->aics[param_1].SiegeEngine1 = 0; + this->aics[param_1].AttMaxEngineers = 0; + this->aics[param_1].AttDiggingUnit = 0x18; + this->aics[param_1].AttDiggingUnitMax = 0x3c; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0x18; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0; + this->aics[param_1].AttUnitBackupMax = 0; + this->aics[param_1].AttUnitBackupGroupsCount = 0; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0; + this->aics[param_1].AttUnitSiegeDefMax = 0; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + this->aics[param_1].AttUnitMain1 = 0x18; + this->aics[param_1].AttUnitMain2 = 0x18; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 2; + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_BALANCED; + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_11.cpp b/src/OpenSHC/AI/AICState/setAICParameters_11.cpp new file mode 100644 index 00000000..2296787e --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_11.cpp @@ -0,0 +1,147 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + // FUNCTION: STRONGHOLDCRUSADER 0x004C94F0 + void AICState ::setAICParameters_11(int param_1) + { + this->aics[param_1].taxesMin = 2; + this->aics[param_1].minimumApples = -1; + this->aics[param_1].minimumCheese = -1; + this->aics[param_1].minimumBread = -1; + this->aics[param_1].minimumWheat = 2; + this->aics[param_1].minimumHop = -1; + this->aics[param_1].flagType = 10; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 1; + this->aics[param_1].unknown005 = 1; + this->aics[param_1].criticalPopularity = 0x1194; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 8000; + this->aics[param_1].taxesMax = 0xc; + this->aics[param_1].farm1 = 0x1e; + this->aics[param_1].populationPerFarm = 5; + this->aics[param_1].unknown011 = 10; + this->aics[param_1].maxFood = 100; + this->aics[param_1].tradeAmountFood = 0x14; + this->aics[param_1].tradeAmountEquipment = 0; + this->aics[param_1].aiRequestDelay = 0x16; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 0x1e; + this->aics[param_1].doubleRationsFoodThreshold = 0x32; + this->aics[param_1].maxWood = 0x3c; + this->aics[param_1].maxStone = 0x28; + this->aics[param_1].maxResourceOther = 10; + this->aics[param_1].maxEquipment = 4; + this->aics[param_1].maxBeer = 10; + this->aics[param_1].maxResourceVariance = 4; + this->aics[param_1].recruitGoldThreshold = 100; + this->aics[param_1].populationPerWoodcutter = 7; + this->aics[param_1].populationPerQuarry = 6; + this->aics[param_1].populationPerIronmine = 0xc; + this->aics[param_1].populationPerPitchrig = 6; + this->aics[param_1].maxQuarries = 3; + this->aics[param_1].maxIronmines = 1; + this->aics[param_1].maxWoodcutters = 10; + this->aics[param_1].maxPitchrigs = 4; + this->aics[param_1].maxFarms = 8; + this->aics[param_1].buildInterval = 3; + this->aics[param_1].resourceRebuildDelay = 0x32; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x11; + this->aics[param_1].poleturnerSetting = 0x13; + this->aics[param_1].sellResource02 = 3; + this->aics[param_1].sellResource03 = 6; + this->aics[param_1].sellResource06 = 0xe; + this->aics[param_1].sellResource08 = 0x11; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource13 = 0x16; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].sellResource15 = 0x18; + this->aics[param_1].defWallPatrolRallyTime = 2; + this->aics[param_1].defWallPatrolGroups = 3; + this->aics[param_1].defSiegeEngineGoldThreshold = 100; + this->aics[param_1].defSiegeEngineBuildDelay = 4; + this->aics[param_1].unknown072 = 200; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 0x46; + this->aics[param_1].recruitProbDefDefault = 0x1e; + this->aics[param_1].recruitProbDefStrong = 0x14; + this->aics[param_1].recruitProbRaidDefault = 0x14; + this->aics[param_1].recruitProbRaidStrong = 0x14; + this->aics[param_1].recruitProbAttackWeak = 0x1e; + this->aics[param_1].DefDiggingUnitMax = 4; + this->aics[param_1].DefDiggingUnit = 0x47; + this->aics[param_1].RaidUnitsBase = 0x1e; + this->aics[param_1].HarassingSiegeEnginesMax = 4; + this->aics[param_1].HarassingSiegeEngine1 = 0x166; + this->aics[param_1].RaidRetargetDelay = 4; + this->aics[param_1].SortieUnitMeleeMin = 10; + this->aics[param_1].SortieUnitRangedMin = 6; + this->aics[param_1].SortieUnitMelee = 0x4b; + this->aics[param_1].SortieUnitRanged = 0x4a; + this->aics[param_1].RaidUnitsRandom = 10; + this->aics[param_1].recruitProbRaidWeak = 0; + this->aics[param_1].recruitProbAttackDefault = 0x32; + this->aics[param_1].recruitProbAttackStrong = 0x3c; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].RecruitInterval = 1; + this->aics[param_1].RecruitIntervalStrong = 1; + this->aics[param_1].DefWalls = 0x28; + this->aics[param_1].DefTotal = 0x28; + this->aics[param_1].OuterPatrolGroupsCount = 1; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].OuterPatrolRallyDelay = 2; + this->aics[param_1].DefUnit1 = 0x46; + this->aics[param_1].DefUnit2 = 0x4c; + this->aics[param_1].AttForceBase = 0x28; + this->aics[param_1].AttForceRandom = 5; + this->aics[param_1].RaidUnit1 = 0x4a; + this->aics[param_1].AttForceSupportAllyThreshold = 0x1e; + this->aics[param_1].AttForceRallyPercentage = 0x50; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 4; + this->aics[param_1].AttUnitPatrolRecommandDelay = 5; + this->aics[param_1].AttCoordinationPreference = 0x5a; + this->aics[param_1].CowThrowInterval = 10000; + this->aics[param_1].SiegeEngine1 = 0x28; + this->aics[param_1].SiegeEngine2 = 0x4d; + this->aics[param_1].SiegeEngine3 = 0x4d; + this->aics[param_1].SiegeEngine4 = 0x4d; + this->aics[param_1].AttMaxEngineers = 9; + this->aics[param_1].AttDiggingUnit = 0x47; + this->aics[param_1].AttDiggingUnitMax = 0xf; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x4a; + this->aics[param_1].AttUnitBackupMax = 0x50; + this->aics[param_1].AttUnitBackupGroupsCount = 1; + this->aics[param_1].AttUnitEngageMax = 10; + this->aics[param_1].AttUnitEngage = 0x4c; + this->aics[param_1].AttUnitSiegeDef = 0; + this->aics[param_1].AttUnitSiegeDefMax = 0; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + this->aics[param_1].AttUnitMain1 = 0x4b; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 2; + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_BALANCED; + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_12.cpp b/src/OpenSHC/AI/AICState/setAICParameters_12.cpp new file mode 100644 index 00000000..d2c250af --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_12.cpp @@ -0,0 +1,147 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + // FUNCTION: STRONGHOLDCRUSADER 0x004C9900 + void AICState ::setAICParameters_12(int param_1) + { + this->aics[param_1].farm1 = 0x1e; + this->aics[param_1].farm3 = 0x1e; + this->aics[param_1].minimumCheese = -1; + this->aics[param_1].minimumBread = -1; + this->aics[param_1].flagType = 10; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 1; + this->aics[param_1].unknown005 = 1; + this->aics[param_1].criticalPopularity = 0x1194; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 9000; + this->aics[param_1].taxesMin = 2; + this->aics[param_1].taxesMax = 0xc; + this->aics[param_1].farm2 = 0x20; + this->aics[param_1].farm4 = 0x1f; + this->aics[param_1].populationPerFarm = 4; + this->aics[param_1].unknown011 = 10; + this->aics[param_1].maxFood = 0x50; + this->aics[param_1].minimumApples = 10; + this->aics[param_1].minimumWheat = 2; + this->aics[param_1].minimumHop = 2; + this->aics[param_1].tradeAmountFood = 0x14; + this->aics[param_1].tradeAmountEquipment = 0; + this->aics[param_1].aiRequestDelay = 0xc; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 10; + this->aics[param_1].doubleRationsFoodThreshold = 0x28; + this->aics[param_1].maxWood = 0x3c; + this->aics[param_1].maxStone = 0x3c; + this->aics[param_1].maxResourceOther = 10; + this->aics[param_1].maxEquipment = 4; + this->aics[param_1].maxBeer = 10; + this->aics[param_1].maxResourceVariance = 4; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerWoodcutter = 7; + this->aics[param_1].populationPerQuarry = 6; + this->aics[param_1].populationPerIronmine = 10; + this->aics[param_1].populationPerPitchrig = 0; + this->aics[param_1].maxQuarries = 4; + this->aics[param_1].maxIronmines = 3; + this->aics[param_1].maxWoodcutters = 10; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].maxFarms = 0x10; + this->aics[param_1].buildInterval = 2; + this->aics[param_1].resourceRebuildDelay = 0x28; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x11; + this->aics[param_1].poleturnerSetting = 0x13; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource13 = 0x16; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].sellResource15 = 0x18; + this->aics[param_1].defWallPatrolRallyTime = 2; + this->aics[param_1].defWallPatrolGroups = 3; + this->aics[param_1].defSiegeEngineGoldThreshold = 100; + this->aics[param_1].defSiegeEngineBuildDelay = 4; + this->aics[param_1].unknown072 = 200; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 0x50; + this->aics[param_1].recruitProbDefDefault = 0x28; + this->aics[param_1].recruitProbDefStrong = 0x14; + this->aics[param_1].recruitProbRaidWeak = 10; + this->aics[param_1].SortieUnitRangedMin = 4; + this->aics[param_1].DefDiggingUnitMax = 4; + this->aics[param_1].RaidRetargetDelay = 4; + this->aics[param_1].recruitProbRaidDefault = 0x14; + this->aics[param_1].recruitProbAttackWeak = 10; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].SortieUnitMeleeMin = 0x14; + this->aics[param_1].OuterPatrolGroupsCount = 1; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].OuterPatrolRallyDelay = 2; + this->aics[param_1].RaidUnitsRandom = 10; + this->aics[param_1].recruitProbRaidStrong = 0x1e; + this->aics[param_1].recruitProbAttackDefault = 0x28; + this->aics[param_1].recruitProbAttackStrong = 0x32; + this->aics[param_1].RecruitInterval = 0; + this->aics[param_1].RecruitIntervalStrong = 0; + this->aics[param_1].SortieUnitRanged = 0x46; + this->aics[param_1].SortieUnitMelee = 0x4b; + this->aics[param_1].DefDiggingUnit = 0x16; + this->aics[param_1].DefWalls = 0x32; + this->aics[param_1].DefTotal = 0x32; + this->aics[param_1].DefUnit1 = 0x16; + this->aics[param_1].DefUnit2 = 0x46; + this->aics[param_1].RaidUnitsBase = 0x1e; + this->aics[param_1].RaidUnit1 = 0x4b; + this->aics[param_1].HarassingSiegeEnginesMax = 3; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].HarassingSiegeEngine2 = 0xbe; + this->aics[param_1].HarassingSiegeEngine3 = 0x166; + this->aics[param_1].AttForceBase = 0x1e; + this->aics[param_1].AttForceRandom = 10; + this->aics[param_1].AttForceSupportAllyThreshold = 0x14; + this->aics[param_1].AttForceRallyPercentage = 0x50; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0; + this->aics[param_1].AttUnitPatrolRecommandDelay = 5; + this->aics[param_1].AttCoordinationPreference = 0x50; + this->aics[param_1].CowThrowInterval = 10000; + this->aics[param_1].SiegeEngine1 = 0x27; + this->aics[param_1].SiegeEngine2 = 0x27; + this->aics[param_1].SiegeEngine3 = 0x3d; + this->aics[param_1].AttMaxEngineers = 6; + this->aics[param_1].AttDiggingUnit = 0x16; + this->aics[param_1].AttDiggingUnitMax = 0xf; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x4a; + this->aics[param_1].AttUnitBackupMax = 0x50; + this->aics[param_1].AttUnitBackupGroupsCount = 1; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0; + this->aics[param_1].AttUnitSiegeDefMax = 0; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + this->aics[param_1].AttUnitMain1 = 0x4b; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 2; + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_GOLD; + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_13.cpp b/src/OpenSHC/AI/AICState/setAICParameters_13.cpp new file mode 100644 index 00000000..1bfe3d4e --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_13.cpp @@ -0,0 +1,144 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + // FUNCTION: STRONGHOLDCRUSADER 0x004C9CF0 + void AICState ::setAICParameters_13(int param_1) + { + this->aics[param_1].minimumCheese = -1; + this->aics[param_1].minimumBread = -1; + this->aics[param_1].maxFood = 100; + this->aics[param_1].minimumWheat = 5; + this->aics[param_1].minimumHop = 5; + this->aics[param_1].recruitGoldThreshold = 100; + this->aics[param_1].flagType = 10; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 1; + this->aics[param_1].unknown005 = 1; + this->aics[param_1].criticalPopularity = 0x1194; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 9000; + this->aics[param_1].taxesMin = 2; + this->aics[param_1].taxesMax = 0xc; + this->aics[param_1].farm1 = 0x1e; + this->aics[param_1].farm2 = 0x1e; + this->aics[param_1].farm3 = 0x1e; + this->aics[param_1].farm4 = 0x1f; + this->aics[param_1].populationPerFarm = 3; + this->aics[param_1].unknown011 = 10; + this->aics[param_1].minimumApples = 10; + this->aics[param_1].tradeAmountFood = 10; + this->aics[param_1].tradeAmountEquipment = 0; + this->aics[param_1].aiRequestDelay = 0xc; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 0x1e; + this->aics[param_1].doubleRationsFoodThreshold = 0x1e; + this->aics[param_1].maxWood = 0x32; + this->aics[param_1].maxStone = 0x28; + this->aics[param_1].maxResourceOther = 10; + this->aics[param_1].maxEquipment = 4; + this->aics[param_1].maxBeer = 10; + this->aics[param_1].maxResourceVariance = 4; + this->aics[param_1].populationPerWoodcutter = 7; + this->aics[param_1].populationPerQuarry = 0x14; + this->aics[param_1].populationPerIronmine = 10; + this->aics[param_1].populationPerPitchrig = 0xf; + this->aics[param_1].maxQuarries = 1; + this->aics[param_1].maxIronmines = 2; + this->aics[param_1].maxWoodcutters = 0xc; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].maxFarms = 0x10; + this->aics[param_1].buildInterval = 1; + this->aics[param_1].resourceRebuildDelay = 0x14; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x11; + this->aics[param_1].poleturnerSetting = 0x13; + this->aics[param_1].sellResource03 = 6; + this->aics[param_1].sellResource08 = 0x11; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource13 = 0x16; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].sellResource15 = 0x18; + this->aics[param_1].defWallPatrolRallyTime = 2; + this->aics[param_1].defWallPatrolGroups = 3; + this->aics[param_1].defSiegeEngineGoldThreshold = 100; + this->aics[param_1].defSiegeEngineBuildDelay = 4; + this->aics[param_1].unknown072 = 200; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 0x50; + this->aics[param_1].recruitProbRaidStrong = 0x1e; + this->aics[param_1].recruitProbAttackDefault = 0x1e; + this->aics[param_1].RecruitIntervalStrong = 1; + this->aics[param_1].OuterPatrolGroupsCount = 1; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].RecruitInterval = 4; + this->aics[param_1].RaidRetargetDelay = 4; + this->aics[param_1].recruitProbDefDefault = 0x32; + this->aics[param_1].recruitProbDefStrong = 0x14; + this->aics[param_1].recruitProbRaidWeak = 0; + this->aics[param_1].recruitProbRaidDefault = 0x14; + this->aics[param_1].recruitProbAttackWeak = 0x14; + this->aics[param_1].recruitProbAttackStrong = 0x32; + this->aics[param_1].RecruitIntervalWeak = 8; + this->aics[param_1].SortieUnitRanged = 0x46; + this->aics[param_1].SortieUnitRangedMin = 5; + this->aics[param_1].SortieUnitMeleeMin = 0xf; + this->aics[param_1].SortieUnitMelee = 0x46; + this->aics[param_1].DefDiggingUnitMax = 0x3c; + this->aics[param_1].DefDiggingUnit = 0x47; + this->aics[param_1].DefWalls = 0x50; + this->aics[param_1].DefTotal = 0x50; + this->aics[param_1].OuterPatrolRallyDelay = 2; + this->aics[param_1].DefUnit1 = 0x46; + this->aics[param_1].HarassingSiegeEnginesMax = 0; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].AttForceBase = 0x28; + this->aics[param_1].AttForceRandom = 0; + this->aics[param_1].AttForceSupportAllyThreshold = 10; + this->aics[param_1].AttForceRallyPercentage = 0x32; + this->aics[param_1].RaidUnitsBase = 5; + this->aics[param_1].RaidUnitsRandom = 5; + this->aics[param_1].RaidUnit1 = 0x49; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0; + this->aics[param_1].AttUnitPatrolRecommandDelay = 5; + this->aics[param_1].AttCoordinationPreference = 0x32; + this->aics[param_1].CowThrowInterval = 10000; + this->aics[param_1].SiegeEngine1 = 0x27; + this->aics[param_1].AttMaxEngineers = 0; + this->aics[param_1].AttDiggingUnit = 0x47; + this->aics[param_1].AttDiggingUnitMax = 0x14; + this->aics[param_1].AttMaxAssassins = 0x28; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x4a; + this->aics[param_1].AttUnitBackupMax = 0; + this->aics[param_1].AttUnitBackupGroupsCount = 0; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0; + this->aics[param_1].AttUnitSiegeDefMax = 0; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + this->aics[param_1].AttUnitMain1 = 0x49; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 4; + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_CLOSEST; + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_14.cpp b/src/OpenSHC/AI/AICState/setAICParameters_14.cpp new file mode 100644 index 00000000..91bb5efe --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_14.cpp @@ -0,0 +1,289 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CA0C0 + void AICState ::setAICParameters_14(int param_1) + + { + + this->aics[param_1].minimumBread = 0xffffffff; + + this->aics[param_1].minimumWheat = 0xffffffff; + + this->aics[param_1].minimumHop = 2; + + this->aics[param_1].tradeAmountEquipment = 2; + + this->aics[param_1].defWallPatrolGroups = 2; + + this->aics[param_1].unknown004 = 1; + + this->aics[param_1].unknown005 = 1; + + this->aics[param_1].aiRequestDelay = 0x28; + + this->aics[param_1].minimumGoodsRequiredAfterTrade = 0x28; + + this->aics[param_1].doubleRationsFoodThreshold = 0x28; + + this->aics[param_1].defSiegeEngineGoldThreshold = 200; + + this->aics[param_1].unknown072 = 200; + + this->aics[param_1].flagType = 0xd; + + this->aics[param_1].unknown002 = 0; + + this->aics[param_1].unknown001 = 0; + + this->aics[param_1].unknown003 = 0; + + this->aics[param_1].criticalPopularity = 4000; + + this->aics[param_1].lowestPopularity = 6000; + + this->aics[param_1].highestPopularity = 9000; + + this->aics[param_1].taxesMin = 3; + + this->aics[param_1].taxesMax = 0xb; + + this->aics[param_1].farm1 = 0x20; + + this->aics[param_1].farm2 = 0x1f; + + this->aics[param_1].farm3 = 0x21; + + this->aics[param_1].populationPerFarm = 4; + + this->aics[param_1].unknown011 = 10; + + this->aics[param_1].maxFood = 300; + + this->aics[param_1].minimumApples = 0x14; + + this->aics[param_1].minimumCheese = 0x14; + + this->aics[param_1].tradeAmountFood = 10; + + this->aics[param_1].maxWood = 0x32; + + this->aics[param_1].maxStone = 0x32; + + this->aics[param_1].maxResourceOther = 10; + + this->aics[param_1].maxEquipment = 10; + + this->aics[param_1].maxBeer = 10; + + this->aics[param_1].maxResourceVariance = 5; + + this->aics[param_1].recruitGoldThreshold = 200; + + this->aics[param_1].populationPerWoodcutter = 7; + + this->aics[param_1].populationPerQuarry = 10; + + this->aics[param_1].populationPerIronmine = 0xc; + + this->aics[param_1].populationPerPitchrig = 0xf; + + this->aics[param_1].maxQuarries = 3; + + this->aics[param_1].maxIronmines = 6; + + this->aics[param_1].maxWoodcutters = 10; + + this->aics[param_1].maxPitchrigs = 4; + + this->aics[param_1].maxFarms = 10; + + this->aics[param_1].buildInterval = 3; + + this->aics[param_1].resourceRebuildDelay = 8; + + this->aics[param_1].blacksmithSetting = 0x15; + + this->aics[param_1].fletcherSetting = 0x12; + + this->aics[param_1].poleturnerSetting = 0x14; + + this->aics[param_1].sellResource05 = 9; + + this->aics[param_1].sellResource07 = 0x10; + + this->aics[param_1].sellResource08 = 0x11; + + this->aics[param_1].sellResource10 = 0x13; + + this->aics[param_1].sellResource11 = 0x14; + + this->aics[param_1].sellResource13 = 0x16; + + this->aics[param_1].sellResource15 = 0x18; + + this->aics[param_1].defWallPatrolRallyTime = 8; + + this->aics[param_1].defSiegeEngineBuildDelay = 4; + + this->aics[param_1].unknown073 = 0x1e; + + this->aics[param_1].recruitProbDefWeak = 100; + + this->aics[param_1].recruitProbDefDefault = 0x32; + + this->aics[param_1].recruitProbDefStrong = 0x1e; + + this->aics[param_1].recruitProbRaidDefault = 0x1e; + + this->aics[param_1].RecruitInterval = 4; + + this->aics[param_1].RecruitIntervalStrong = 4; + + this->aics[param_1].DefDiggingUnitMax = 4; + + this->aics[param_1].DefDiggingUnit = 0x1a; + + this->aics[param_1].RaidUnitsRandom = 4; + + this->aics[param_1].RaidRetargetDelay = 4; + + this->aics[param_1].recruitProbAttackDefault = 0x14; + + this->aics[param_1].recruitProbAttackStrong = 0x14; + + this->aics[param_1].recruitProbRaidStrong = 0x32; + + this->aics[param_1].SortieUnitRangedMin = 10; + + this->aics[param_1].SortieUnitMeleeMin = 10; + + this->aics[param_1].SortieUnitRanged = 0x17; + + this->aics[param_1].SortieUnitMelee = 0x1a; + + this->aics[param_1].DefTotal = 0x32; + + this->aics[param_1].RaidUnitsBase = 10; + + this->aics[param_1].recruitProbRaidWeak = 0; + + this->aics[param_1].recruitProbAttackWeak = 0; + + this->aics[param_1].RecruitIntervalWeak = 1; + + this->aics[param_1].DefWalls = 0x28; + + this->aics[param_1].OuterPatrolGroupsCount = 1; + + this->aics[param_1].OuterPatrolGroupsMove = 1; + + this->aics[param_1].OuterPatrolRallyDelay = 5; + + this->aics[param_1].DefUnit1 = 0x17; + + this->aics[param_1].DefUnit2 = 0x17; + + this->aics[param_1].DefUnit3 = 0x17; + + this->aics[param_1].DefUnit4 = 0x4c; + + this->aics[param_1].DefUnit5 = 0x49; + + this->aics[param_1].HarassingSiegeEnginesMax = 5; + + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + + this->aics[param_1].HarassingSiegeEngine2 = 0x166; + + this->aics[param_1].RaidUnit1 = 0x1a; + + this->aics[param_1].AttForceBase = 0x32; + + this->aics[param_1].AttForceRandom = 0x14; + + this->aics[param_1].AttForceSupportAllyThreshold = 0x32; + + this->aics[param_1].AttForceRallyPercentage = 0x50; + + this->aics[param_1].AttMovingTribesThreshold = 100; + + this->aics[param_1].AttAssaultDelay = 5; + + this->aics[param_1].AttUnitPatrolRecommandDelay = 8; + + this->aics[param_1].AttCoordinationPreference = 0x32; + + this->aics[param_1].CowThrowInterval = 6; + + this->aics[param_1].SiegeEngine1 = 0x27; + + this->aics[param_1].SiegeEngine2 = 0x27; + + this->aics[param_1].SiegeEngine3 = 0x4d; + + this->aics[param_1].SiegeEngine4 = 0x4d; + + this->aics[param_1].SiegeEngine5 = 0x4d; + + this->aics[param_1].AttMaxEngineers = 10; + + this->aics[param_1].AttDiggingUnit = 0x47; + + this->aics[param_1].AttDiggingUnitMax = 0x50; + + this->aics[param_1].AttMaxAssassins = 10; + + this->aics[param_1].AttUnit2 = 0x1a; + + this->aics[param_1].AttUnit2Max = 10; + + this->aics[param_1].AttMaxLaddermen = 0; + + this->aics[param_1].AttMaxTunnelers = 0; + + this->aics[param_1].AttUnitPatrol = 0x4c; + + this->aics[param_1].AttUnitPatrolMax = 0; + + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + + this->aics[param_1].AttUnitBackup = 0x17; + + this->aics[param_1].AttUnitBackupMax = 0x32; + + this->aics[param_1].AttUnitBackupGroupsCount = 1; + + this->aics[param_1].AttUnitEngageMax = 0; + + this->aics[param_1].AttUnitEngage = 0; + + this->aics[param_1].AttUnitSiegeDef = 0x1a; + + this->aics[param_1].AttUnitSiegeDefMax = 6; + + this->aics[param_1].AttUnitSiegeDefGroupsCount = 1; + + this->aics[param_1].AttUnitMain1 = 0x1a; + + this->aics[param_1].AttUnitMain2 = 0x1a; + + this->aics[param_1].AttMaxDefault = 200; + + this->aics[param_1].AttMainGroupsCount = 3; + + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_CLOSEST; + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_15.cpp b/src/OpenSHC/AI/AICState/setAICParameters_15.cpp new file mode 100644 index 00000000..f05d8235 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_15.cpp @@ -0,0 +1,144 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + // FUNCTION: STRONGHOLDCRUSADER 0x004CA4D0 + void AICState ::setAICParameters_15(int param_1) + { + this->aics[param_1].farm1 = 0x21; + this->aics[param_1].farm3 = 0x21; + this->aics[param_1].farm4 = 0x21; + this->aics[param_1].populationPerFarm = 5; + this->aics[param_1].minimumHop = 5; + this->aics[param_1].minimumBread = -1; + this->aics[param_1].minimumWheat = -1; + this->aics[param_1].populationPerWoodcutter = 7; + this->aics[param_1].populationPerQuarry = 7; + this->aics[param_1].populationPerIronmine = 7; + this->aics[param_1].aiRequestDelay = 0xc; + this->aics[param_1].maxResourceOther = 0xc; + this->aics[param_1].maxIronmines = 3; + this->aics[param_1].buildInterval = 3; + this->aics[param_1].flagType = 0xd; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 1; + this->aics[param_1].unknown005 = 1; + this->aics[param_1].criticalPopularity = 4000; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 8000; + this->aics[param_1].taxesMin = 2; + this->aics[param_1].taxesMax = 10; + this->aics[param_1].farm2 = 0x1f; + this->aics[param_1].unknown011 = 10; + this->aics[param_1].maxFood = 100; + this->aics[param_1].minimumApples = 10; + this->aics[param_1].minimumCheese = 10; + this->aics[param_1].tradeAmountFood = 0x14; + this->aics[param_1].tradeAmountEquipment = 0; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 0; + this->aics[param_1].doubleRationsFoodThreshold = 0x50; + this->aics[param_1].maxWood = 0x3c; + this->aics[param_1].maxStone = 0x32; + this->aics[param_1].maxEquipment = 10; + this->aics[param_1].maxBeer = 6; + this->aics[param_1].maxResourceVariance = 4; + this->aics[param_1].recruitGoldThreshold = 200; + this->aics[param_1].populationPerPitchrig = 0; + this->aics[param_1].maxQuarries = 1; + this->aics[param_1].maxWoodcutters = 8; + this->aics[param_1].maxPitchrigs = 1; + this->aics[param_1].maxFarms = 9; + this->aics[param_1].resourceRebuildDelay = 0x28; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x11; + this->aics[param_1].poleturnerSetting = 0x13; + this->aics[param_1].sellResource04 = 7; + this->aics[param_1].sellResource05 = 9; + this->aics[param_1].sellResource07 = 0x10; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].defWallPatrolRallyTime = 2; + this->aics[param_1].defWallPatrolGroups = 3; + this->aics[param_1].defSiegeEngineGoldThreshold = 100; + this->aics[param_1].defSiegeEngineBuildDelay = 4; + this->aics[param_1].unknown072 = 200; + this->aics[param_1].RecruitIntervalStrong = 4; + this->aics[param_1].SortieUnitRangedMin = 4; + this->aics[param_1].SortieUnitMeleeMin = 4; + this->aics[param_1].DefDiggingUnitMax = 4; + this->aics[param_1].RaidUnitsRandom = 4; + this->aics[param_1].RaidRetargetDelay = 4; + this->aics[param_1].recruitProbRaidWeak = 0x28; + this->aics[param_1].recruitProbRaidDefault = 0x28; + this->aics[param_1].recruitProbAttackWeak = 10; + this->aics[param_1].SortieUnitRanged = 0x16; + this->aics[param_1].DefDiggingUnit = 0x16; + this->aics[param_1].DefTotal = 0x28; + this->aics[param_1].DefUnit1 = 0x16; + this->aics[param_1].AttForceBase = 10; + this->aics[param_1].AttForceSupportAllyThreshold = 10; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 0x32; + this->aics[param_1].recruitProbDefDefault = 0x1e; + this->aics[param_1].recruitProbDefStrong = 0x14; + this->aics[param_1].recruitProbRaidStrong = 0x1e; + this->aics[param_1].recruitProbAttackDefault = 0x1e; + this->aics[param_1].recruitProbAttackStrong = 0x32; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].RecruitInterval = 1; + this->aics[param_1].SortieUnitMelee = 0x1c; + this->aics[param_1].DefWalls = 0x1e; + this->aics[param_1].OuterPatrolGroupsCount = 1; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].OuterPatrolRallyDelay = 2; + this->aics[param_1].RaidUnitsBase = 0x10; + this->aics[param_1].RaidUnit1 = 0x1c; + this->aics[param_1].HarassingSiegeEnginesMax = 1; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].AttForceRandom = 0x14; + this->aics[param_1].AttForceRallyPercentage = 0x32; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0; + this->aics[param_1].AttUnitPatrolRecommandDelay = 5; + this->aics[param_1].AttCoordinationPreference = 0x46; + this->aics[param_1].CowThrowInterval = 10000; + this->aics[param_1].SiegeEngine1 = 0x28; + this->aics[param_1].SiegeEngine2 = 0x28; + this->aics[param_1].AttMaxEngineers = 6; + this->aics[param_1].AttDiggingUnit = 0x16; + this->aics[param_1].AttDiggingUnitMax = 0x14; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x16; + this->aics[param_1].AttUnitBackupMax = 0x1e; + this->aics[param_1].AttUnitBackupGroupsCount = 3; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0; + this->aics[param_1].AttUnitSiegeDefMax = 0; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + this->aics[param_1].AttUnitMain1 = 0x1b; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 2; + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_BALANCED; + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_16.cpp b/src/OpenSHC/AI/AICState/setAICParameters_16.cpp new file mode 100644 index 00000000..d7e7a439 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_16.cpp @@ -0,0 +1,143 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CA8A0 + void AICState ::setAICParameters_16(int param_1) + { + this->aics[param_1].taxesMax = 0xc; + this->aics[param_1].farm2 = 0x1e; + this->aics[param_1].farm3 = 0x1e; + this->aics[param_1].aiRequestDelay = 0xc; + this->aics[param_1].maxFarms = 0xc; + this->aics[param_1].flagType = 0xd; + this->aics[param_1].unknown002 = 0; + this->aics[param_1].unknown001 = 0; + this->aics[param_1].unknown003 = 0; + this->aics[param_1].unknown004 = 1; + this->aics[param_1].unknown005 = 1; + this->aics[param_1].criticalPopularity = 0x1194; + this->aics[param_1].lowestPopularity = 5000; + this->aics[param_1].highestPopularity = 9000; + this->aics[param_1].taxesMin = 2; + this->aics[param_1].farm1 = 0x1f; + this->aics[param_1].populationPerFarm = 4; + this->aics[param_1].unknown011 = 10; + this->aics[param_1].maxFood = 100; + this->aics[param_1].minimumApples = 10; + this->aics[param_1].minimumCheese = -1; + this->aics[param_1].minimumBread = -1; + this->aics[param_1].minimumWheat = 2; + this->aics[param_1].minimumHop = 2; + this->aics[param_1].tradeAmountFood = 5; + this->aics[param_1].tradeAmountEquipment = 0; + this->aics[param_1].minimumGoodsRequiredAfterTrade = 10; + this->aics[param_1].doubleRationsFoodThreshold = 0x28; + this->aics[param_1].maxWood = 0x3c; + this->aics[param_1].maxStone = 0x28; + this->aics[param_1].maxResourceOther = 10; + this->aics[param_1].maxEquipment = 4; + this->aics[param_1].maxBeer = 5; + this->aics[param_1].maxResourceVariance = 4; + this->aics[param_1].recruitGoldThreshold = 0x32; + this->aics[param_1].populationPerWoodcutter = 7; + this->aics[param_1].populationPerQuarry = 6; + this->aics[param_1].populationPerIronmine = 10; + this->aics[param_1].populationPerPitchrig = 0; + this->aics[param_1].maxQuarries = 4; + this->aics[param_1].maxIronmines = 2; + this->aics[param_1].maxWoodcutters = 10; + this->aics[param_1].maxPitchrigs = 0; + this->aics[param_1].buildInterval = 2; + this->aics[param_1].resourceRebuildDelay = 0x28; + this->aics[param_1].blacksmithSetting = 0x16; + this->aics[param_1].fletcherSetting = 0x11; + this->aics[param_1].poleturnerSetting = 0x13; + this->aics[param_1].sellResource03 = 6; + this->aics[param_1].sellResource09 = 0x12; + this->aics[param_1].sellResource10 = 0x13; + this->aics[param_1].sellResource11 = 0x14; + this->aics[param_1].sellResource12 = 0x15; + this->aics[param_1].sellResource13 = 0x16; + this->aics[param_1].sellResource14 = 0x17; + this->aics[param_1].sellResource15 = 0x18; + this->aics[param_1].defWallPatrolRallyTime = 2; + this->aics[param_1].defWallPatrolGroups = 3; + this->aics[param_1].defSiegeEngineGoldThreshold = 100; + this->aics[param_1].defSiegeEngineBuildDelay = 4; + this->aics[param_1].unknown072 = 200; + this->aics[param_1].unknown073 = 0x14; + this->aics[param_1].recruitProbDefWeak = 0x50; + this->aics[param_1].recruitProbDefDefault = 0x28; + this->aics[param_1].recruitProbDefStrong = 0x14; + this->aics[param_1].recruitProbRaidWeak = 10; + this->aics[param_1].SortieUnitRangedMin = 4; + this->aics[param_1].DefDiggingUnitMax = 4; + this->aics[param_1].RaidRetargetDelay = 4; + this->aics[param_1].RecruitIntervalWeak = 1; + this->aics[param_1].RecruitInterval = 1; + this->aics[param_1].RecruitIntervalStrong = 1; + this->aics[param_1].SortieUnitRanged = 0x16; + this->aics[param_1].DefDiggingUnit = 0x16; + this->aics[param_1].OuterPatrolGroupsCount = 1; + this->aics[param_1].OuterPatrolGroupsMove = 1; + this->aics[param_1].OuterPatrolRallyDelay = 2; + this->aics[param_1].DefUnit1 = 0x16; + this->aics[param_1].recruitProbRaidDefault = 0x14; + this->aics[param_1].recruitProbRaidStrong = 0x1e; + this->aics[param_1].recruitProbAttackWeak = 10; + this->aics[param_1].recruitProbAttackDefault = 0x28; + this->aics[param_1].recruitProbAttackStrong = 0x32; + this->aics[param_1].SortieUnitMeleeMin = 0x14; + this->aics[param_1].SortieUnitMelee = 0x25; + this->aics[param_1].DefWalls = 0x1e; + this->aics[param_1].DefTotal = 0x1e; + this->aics[param_1].RaidUnitsBase = 0x1e; + this->aics[param_1].RaidUnitsRandom = 10; + this->aics[param_1].RaidUnit1 = 0x25; + this->aics[param_1].HarassingSiegeEnginesMax = 5; + this->aics[param_1].HarassingSiegeEngine1 = 0xbe; + this->aics[param_1].AttForceBase = 0x32; + this->aics[param_1].AttForceRandom = 10; + this->aics[param_1].AttForceSupportAllyThreshold = 0x28; + this->aics[param_1].AttForceRallyPercentage = 0x50; + this->aics[param_1].AttMovingTribesThreshold = 100; + this->aics[param_1].AttAssaultDelay = 0; + this->aics[param_1].AttUnitPatrolRecommandDelay = 5; + this->aics[param_1].AttCoordinationPreference = 0x50; + this->aics[param_1].CowThrowInterval = 10000; + this->aics[param_1].SiegeEngine1 = 0x27; + this->aics[param_1].SiegeEngine2 = 0x27; + this->aics[param_1].SiegeEngine3 = 0x27; + this->aics[param_1].SiegeEngine4 = 0x27; + this->aics[param_1].AttMaxEngineers = 8; + this->aics[param_1].AttDiggingUnit = 0x16; + this->aics[param_1].AttDiggingUnitMax = 10; + this->aics[param_1].AttMaxAssassins = 0; + this->aics[param_1].AttUnit2 = 0; + this->aics[param_1].AttUnit2Max = 0; + this->aics[param_1].AttMaxLaddermen = 0; + this->aics[param_1].AttMaxTunnelers = 0; + this->aics[param_1].AttUnitPatrol = 0; + this->aics[param_1].AttUnitPatrolMax = 0; + this->aics[param_1].AttUnitPatrolGroupsCount = 0; + this->aics[param_1].AttUnitBackup = 0x16; + this->aics[param_1].AttUnitBackupMax = 0x28; + this->aics[param_1].AttUnitBackupGroupsCount = 1; + this->aics[param_1].AttUnitEngageMax = 0; + this->aics[param_1].AttUnitEngage = 0; + this->aics[param_1].AttUnitSiegeDef = 0; + this->aics[param_1].AttUnitSiegeDefMax = 0; + this->aics[param_1].AttUnitSiegeDefGroupsCount = 0; + this->aics[param_1].AttUnitMain1 = 0x25; + this->aics[param_1].AttMaxDefault = 200; + this->aics[param_1].AttMainGroupsCount = 2; + this->aics[param_1].TargetChoice = OpenSHC::AI::AIC::AITC_BALANCED; + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAttackCoordinationLevel.cpp b/src/OpenSHC/AI/AICState/setAttackCoordinationLevel.cpp new file mode 100644 index 00000000..339f09d8 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAttackCoordinationLevel.cpp @@ -0,0 +1,32 @@ +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0110 + void AICState::setAttackCoordinationLevel(int playerID) + { + int const _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType != AITA_NULL) { + int _willingnessPercentage = (int)this->aics[_aiType - 1].AttCoordinationPreference + + DAT_GameState::instance.playerDataArray[playerID].currentAttackWave * 4; + if (90 < _willingnessPercentage) { + _willingnessPercentage = 90; + } + int _rngMod100 = (int)(short)SEC_RNG::instance.currentNumber2 % 100; + MACRO_CALL_MEMBER(Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + if (_rngMod100 < _willingnessPercentage / 2) { + DAT_GameState::instance.playerDataArray[playerID].aiAttackCoordinationLevel = 2; + return; + } + DAT_GameState::instance.playerDataArray[playerID].aiAttackCoordinationLevel + = (uint)(_rngMod100 < _willingnessPercentage); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAttackPrimeInfo.cpp b/src/OpenSHC/AI/AICState/setAttackPrimeInfo.cpp new file mode 100644 index 00000000..7910e23a --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAttackPrimeInfo.cpp @@ -0,0 +1,27 @@ +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDFD0 + void AICState::setAttackPrimeInfo(int playerID, int attackedPlayerID) + { + int _tile = DAT_GameState::instance.playerDataArray[playerID].shortestDistanceTile; + int _zone = (int)(char)DAT_TileMapState::instance.AIZoneLayer[_tile]; + int _pathCon = (int)(short)DAT_TileMapState::instance.PathConnectionLayer[_tile]; + int _zoneSize = DAT_PathFindingState::instance.zoneSizesArray[_pathCon]; + DAT_TroopValueState::instance.attackInfo.startCon = _pathCon; + int _attackedTile = DAT_GameState::instance.playerDataArray[attackedPlayerID].campground.tileEntry; + DAT_TroopValueState::instance.attackInfo.startZone = _zone; + DAT_TroopValueState::instance.attackInfo.zoneSize = _zoneSize; + DAT_TroopValueState::instance.attackInfo.unknownOne_0x20f90 = 1; + DAT_TroopValueState::instance.attackInfo.keepCon + = (int)(short)DAT_TileMapState::instance.PathConnectionLayer[_attackedTile]; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setCurrentAttackRaidParameter.cpp b/src/OpenSHC/AI/AICState/setCurrentAttackRaidParameter.cpp new file mode 100644 index 00000000..21e9aeba --- /dev/null +++ b/src/OpenSHC/AI/AICState/setCurrentAttackRaidParameter.cpp @@ -0,0 +1,37 @@ +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDF10 + void AICState::setCurrentAttackRaidParameter(int playerID) + { + int const _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType != AITA_NULL) { + int _rng = (int)SEC_RNG::instance.currentNumber2; + MACRO_CALL_MEMBER(Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + AICSpecification const* spec = &this->aics[_aiType - 1]; + int _gold = DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf]; + int _raidUnitsRandom = (int)spec->RaidUnitsRandom; + if (_gold < 1000) { + _raidUnitsRandom = 0; + } else if (4999 < _gold) { + _raidUnitsRandom = _raidUnitsRandom * 2; + } + if (DAT_GameState::instance + .playerDataArray[DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID_2Unk] + .currentResources[0xf] + < 500) { + _raidUnitsRandom = -((int)spec->RaidUnitsBase / 2); + } + DAT_GameState::instance.playerDataArray[playerID].currentAttackRaidParameter + = (_raidUnitsRandom * (_rng % 100)) / 100; + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setCurrentAttackStrength.cpp b/src/OpenSHC/AI/AICState/setCurrentAttackStrength.cpp new file mode 100644 index 00000000..a762c3c9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setCurrentAttackStrength.cpp @@ -0,0 +1,40 @@ +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDE60 + void AICState::setCurrentAttackStrength(int playerID) + { + int const _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType != AITA_NULL) { + int _randomNumber = (int)SEC_RNG::instance.currentNumber2; + MACRO_CALL_MEMBER(Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + int _attForceRandom = (int)this->aics[_aiType - 1].AttForceRandom; + int* _attackWave = &DAT_GameState::instance.playerDataArray[playerID].currentAttackWave; + *_attackWave = *_attackWave + 1; + int _currentGold = DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf]; + DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength + = (_attForceRandom * (_randomNumber % 100)) / 100; + int _currentAttackWave = DAT_GameState::instance.playerDataArray[playerID].currentAttackWave; + if (_currentGold < 10001) { + int* _ptrAttackStrength + = &DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength; + *_ptrAttackStrength = *_ptrAttackStrength + _currentAttackWave * 5; + } else { + int* _ptrAttackStrength + = &DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength; + *_ptrAttackStrength = *_ptrAttackStrength + _currentAttackWave * 7; + } + if (200 < DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength) { + DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength = 200; + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setEightTribe0xc0TribesToAggressiveAndAttack.cpp b/src/OpenSHC/AI/AICState/setEightTribe0xc0TribesToAggressiveAndAttack.cpp new file mode 100644 index 00000000..79dbdfb5 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setEightTribe0xc0TribesToAggressiveAndAttack.cpp @@ -0,0 +1,35 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF5E0 + void AICState::setEightTribe0xc0TribesToAggressiveAndAttack(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType != AITA_NULL) { + int iVar1 = 0; + short* psVar2 = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + 0xc0; + do { + int _tribeID = (int)*psVar2; + if (((_tribeID != 0) + && (DAT_TribesState::instance.tribes[_tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[iVar1 + 0xc0])) + && (DAT_TribesState::instance.tribes[_tribeID].selectionTargetUnitID != 0)) { + DAT_TribesState::instance.tribes[_tribeID].unitStance = Map::Units::Behavior::USE_AGGRESSIVE; + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + _tribeID, Map::Units::STBT_0x3f2); + } + iVar1 = iVar1 + 1; + psVar2 = psVar2 + 1; + } while (iVar1 < 8); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setFoodBuyPlan.cpp b/src/OpenSHC/AI/AICState/setFoodBuyPlan.cpp index c1221fa8..981ad02d 100644 --- a/src/OpenSHC/AI/AICState/setFoodBuyPlan.cpp +++ b/src/OpenSHC/AI/AICState/setFoodBuyPlan.cpp @@ -29,39 +29,39 @@ namespace AI { // Check each food type and queue purchase if current stock is below minimum // Apples: special handling with >= 0 check (allows -1 to disable) - int const minimumApples = this->DAT_AICArray[aiConfigIndex].minimumApples; + int const minimumApples = this->aics[aiConfigIndex].minimumApples; int* currentResources = DAT_GameState::ptr->playerDataArray[playerID].currentResources; if (minimumApples >= 0 && currentResources[ResourceType::RT_APPLE] < minimumApples) { DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_APPLE] - = this->DAT_AICArray[aiConfigIndex].tradeAmountFood; + = this->aics[aiConfigIndex].tradeAmountFood; } // Cheese: standard check with > 0 - int const minimumCheese = this->DAT_AICArray[aiConfigIndex].minimumCheese; + int const minimumCheese = this->aics[aiConfigIndex].minimumCheese; if (minimumCheese > 0 && currentResources[ResourceType::RT_CHEESE] < minimumCheese) { DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_CHEESE] - = this->DAT_AICArray[aiConfigIndex].tradeAmountFood; + = this->aics[aiConfigIndex].tradeAmountFood; } // Bread: standard check with > 0 - int const minimumBread = this->DAT_AICArray[aiConfigIndex].minimumBread; + int const minimumBread = this->aics[aiConfigIndex].minimumBread; if (minimumBread > 0 && currentResources[ResourceType::RT_BREAD] < minimumBread) { DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_BREAD] - = this->DAT_AICArray[aiConfigIndex].tradeAmountFood; + = this->aics[aiConfigIndex].tradeAmountFood; } // Wheat: standard check with > 0 - int const minimumWheat = this->DAT_AICArray[aiConfigIndex].minimumWheat; + int const minimumWheat = this->aics[aiConfigIndex].minimumWheat; if (minimumWheat > 0 && currentResources[ResourceType::RT_WHEAT] < minimumWheat) { DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_WHEAT] - = this->DAT_AICArray[aiConfigIndex].tradeAmountFood; + = this->aics[aiConfigIndex].tradeAmountFood; } // Hops: standard check with > 0 - int const minimumHop = this->DAT_AICArray[aiConfigIndex].minimumHop; + int const minimumHop = this->aics[aiConfigIndex].minimumHop; if (minimumHop > 0 && currentResources[ResourceType::RT_HOPS] < minimumHop) { DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_HOPS] - = this->DAT_AICArray[aiConfigIndex].tradeAmountFood; + = this->aics[aiConfigIndex].tradeAmountFood; } } } diff --git a/src/OpenSHC/AI/AICState/setInitialAIC.cpp b/src/OpenSHC/AI/AICState/setInitialAIC.cpp new file mode 100644 index 00000000..e8bcb8cd --- /dev/null +++ b/src/OpenSHC/AI/AICState/setInitialAIC.cpp @@ -0,0 +1,52 @@ +#include "OpenSHC/AI/AICState.func.hpp" + +namespace OpenSHC { +namespace AI { + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D18B0 + void AICState ::setInitialAIC() + + { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::wipeAICMemory, this)(); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_01, this)(1); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_02, this)(2); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_03, this)(3); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_04, this)(4); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_05, this)(5); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_06, this)(6); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_07, this)(7); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_08, this)(8); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_09, this)(9); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_10, this)(10); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_11, this)(0xb); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_12, this)(0xc); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_13, this)(0xd); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_14, this)(0xe); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_15, this)(0xf); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAICParameters_16, this)(0x10); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setNextFarmTypeToBuild.cpp b/src/OpenSHC/AI/AICState/setNextFarmTypeToBuild.cpp new file mode 100644 index 00000000..8f0706f8 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setNextFarmTypeToBuild.cpp @@ -0,0 +1,29 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB1B0 + int AICState::setNextFarmTypeToBuild(PlayerID playerID) + + { + unsigned int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == AITA_NULL) { + return 0; + } + _aiType += -1; + int* _ptrAiFarmIndex = &DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex; + *_ptrAiFarmIndex = *_ptrAiFarmIndex + 1; + if (8 <= DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex) { + DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex = 0; + } + if ((&this->aics[_aiType].farm1)[DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex] == 0) { + DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex = 0; + } + return (&this->aics[_aiType].farm1)[DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex]; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setNextMoveLocationForUnits.cpp b/src/OpenSHC/AI/AICState/setNextMoveLocationForUnits.cpp new file mode 100644 index 00000000..072dc667 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setNextMoveLocationForUnits.cpp @@ -0,0 +1,52 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/LandscapeState.func.hpp" +#include "OpenSHC/Map/Navigation/PathFindingState.func.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_LandscapeState.hpp" +#include "OpenSHC/Globals/DAT_PathFindingState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE3B0 + void AICState ::setNextMoveLocationForUnits(int playerID) + + { + + if ((short)DAT_TileMapState::instance.OrganismLayer + [DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY] + .addXgetTile + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX] + != 0) { + + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeTree, DAT_LandscapeState::ptr)( + (int)(short)DAT_TileMapState::instance.OrganismLayer + [DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY] + .addXgetTile + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX]); + } + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findLinkageBasedPathOrWalkRadius, + DAT_PathFindingState::ptr)(DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX, + (uint)((int)(DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY)), -1, -1, 4000, FALSE); + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::setDestinationPairsBasedOnPreviousSearch, + DAT_PathFindingState::ptr)( + playerID + -1, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID); + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setTargetForTribeBasedOnTargetChoiceLogic.cpp b/src/OpenSHC/AI/AICState/setTargetForTribeBasedOnTargetChoiceLogic.cpp new file mode 100644 index 00000000..3c35fb36 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTargetForTribeBasedOnTargetChoiceLogic.cpp @@ -0,0 +1,152 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Navigation/DirectionAlgorithmState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" +#include "OpenSHC/AI/AIC/AITargetChoiceInt.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" + +#include "OpenSHC/Globals/DAT_DirectionAlgorithmState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + using OpenSHC::AI::AIC::AITargetChoiceInt; + using OpenSHC::Game::Player::PlayerData; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CACB0 + void AICState ::setTargetForTribeBasedOnTargetChoiceLogic(int owner, int tribe) + + { + + int _aiType = DAT_GameState::instance.playerDataArray[owner].aiType; + + int _targetPlayer = 0; + + if (_aiType != 0) { + + int _ownerKeepY = DAT_GameState::instance.playerDataArray[owner].campground.yEntry; + int _ownerKeepX = DAT_GameState::instance.playerDataArray[owner].campground.xEntry; + + int _minDistance = 10000; + int _maxGold = 0; + int _minPoints = 1000000; + + int _player = 1; + PlayerData* _ptrPlayerData = (PlayerData*)(&DAT_GameState::instance.playerDataArray[1]); + + do { + + if ((DAT_GameState::instance.mapAndTime.playerTeams[_player] + != DAT_GameState::instance.mapAndTime.playerTeams[owner])) { + + int _lord = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(_player); + + if (_lord != 0) { + + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)((_ptrPlayerData->campground).xEntry, + (_ptrPlayerData->campground).yEntry, _ownerKeepX, _ownerKeepY); + + AITargetChoiceInt _targetChoice = this->aics[_aiType - 1].TargetChoice; + + if (_targetChoice == OpenSHC::AI::AIC::AITC_PLAYER) { + + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[_player] != -1) { + + int _distance = DAT_DirectionAlgorithmState::instance.distanceHigh; + if (_distance <= _minDistance) { + _minDistance = _distance; + _targetPlayer = _player; + } + } + + } else if (_targetChoice == OpenSHC::AI::AIC::AITC_CLOSEST) { + + int _distance = DAT_DirectionAlgorithmState::instance.distanceHigh; + if (_distance <= _minDistance) { + _minDistance = _distance; + _targetPlayer = _player; + } + + } else if (_targetChoice == OpenSHC::AI::AIC::AITC_GOLD) { + + int _gold = _ptrPlayerData->currentResources[0xf]; + if (_maxGold <= _gold) { + _minDistance = _minDistance; + _maxGold = _gold; + _minPoints = _minPoints; + _targetPlayer = _player; + } + + } else if (_targetChoice == OpenSHC::AI::AIC::AITC_BALANCED) { + + int _points = _ptrPlayerData->currentPopulation * 5 + + _ptrPlayerData->totalTroopValue + + _ptrPlayerData->currentResources[0xf] / 100 + + DAT_DirectionAlgorithmState::instance.distanceHigh * 2; + if (_points <= _minPoints) { + _minDistance = _minDistance; + _maxGold = _maxGold; + _minPoints = _points; + _targetPlayer = _player; + } + } + } + } + + _player = _player + 1; + _ptrPlayerData = _ptrPlayerData + 1; + + } while (_player < 9); + + if (_targetPlayer == 0) { + + int _playerID_backup = 1; + PlayerData* _ptrPlayerData_backup = &DAT_GameState::instance.playerDataArray[1]; + + do { + + if ((DAT_GameState::instance.mapAndTime.playerTeams[_playerID_backup] + != DAT_GameState::instance.mapAndTime.playerTeams[owner])) { + + int _lordBackup = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(_playerID_backup); + + if (_lordBackup != 0) { + + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)((_ptrPlayerData_backup->campground).xEntry, + (_ptrPlayerData_backup->campground).yEntry, _ownerKeepX, _ownerKeepY); + + if (DAT_DirectionAlgorithmState::instance.distanceHigh <= _minDistance) { + _minDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + _targetPlayer = _playerID_backup; + } + } + } + + _playerID_backup = _playerID_backup + 1; + _ptrPlayerData_backup = _ptrPlayerData_backup + 1; + + } while (_playerID_backup < 9); + + if (_targetPlayer == 0) { + return; + } + } + + DAT_TribesState::instance.tribes[tribe].targetPlayer_outpost = (short)_targetPlayer; + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setTribe0x11ToAggressiveAndAttackIfConditionMet.cpp b/src/OpenSHC/AI/AICState/setTribe0x11ToAggressiveAndAttackIfConditionMet.cpp new file mode 100644 index 00000000..b44d4a6c --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTribe0x11ToAggressiveAndAttackIfConditionMet.cpp @@ -0,0 +1,31 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF560 + void AICState::setTribe0x11ToAggressiveAndAttackIfConditionMet(int param_1) + { + int tribeID; + if (((0 < *(int*)((int)DAT_TroopValueState::instance.attackInfo.moatValuesArray + param_1 * 0x177bc + -4)) + && (DAT_GameState::instance.playerDataArray[param_1].aiType != AITA_NULL)) + && (tribeID = (int)DAT_GameState::instance.playerDataArray[param_1].aiTribeIDs[0x11], tribeID != 0)) { + if ((DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[param_1].aiTribeUIDs[0x11]) + && (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID != 0)) { + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_AGGRESSIVE; + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, Map::Units::STBT_0x3f7); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setTribe0xCtoAggressiveAndAttack.cpp b/src/OpenSHC/AI/AICState/setTribe0xCtoAggressiveAndAttack.cpp new file mode 100644 index 00000000..2290c59f --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTribe0xCtoAggressiveAndAttack.cpp @@ -0,0 +1,29 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF400 + void AICState::setTribe0xCtoAggressiveAndAttack(int playerID) + { + int tribeID; + if ((DAT_GameState::instance.playerDataArray[playerID].aiType != AITA_NULL) + && (tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xc], tribeID != 0)) { + if ((DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xc]) + && (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID != 0)) { + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_AGGRESSIVE; + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, Map::Units::STBT_0x3f2); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setTribe0xbToAggressiveAndAttack.cpp b/src/OpenSHC/AI/AICState/setTribe0xbToAggressiveAndAttack.cpp new file mode 100644 index 00000000..87e451e4 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTribe0xbToAggressiveAndAttack.cpp @@ -0,0 +1,29 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Units/Behavior/UnitStanceEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF6E0 + void AICState::setTribe0xbToAggressiveAndAttack(int playerID) + { + int tribeID; + if ((DAT_GameState::instance.playerDataArray[playerID].aiType != AITA_NULL) + && (tribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xb], tribeID != 0)) { + if ((DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xb]) + && (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID != 0)) { + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_AGGRESSIVE; + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, Map::Units::STBT_0x3f2); + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setTriggerForAITauntResponse.cpp b/src/OpenSHC/AI/AICState/setTriggerForAITauntResponse.cpp new file mode 100644 index 00000000..1b9dcf37 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTriggerForAITauntResponse.cpp @@ -0,0 +1,18 @@ +#include "OpenSHC/Game/GameCore.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D1090 + void AICState::setTriggerForAITauntResponse() + { + BOOLEnum BVar1 = MACRO_CALL_MEMBER(Game::GameCore_Func::getAreWeInAInGameMenu, DAT_GameCore::ptr)(); + if (BVar1 != FALSE) { + this->aiTauntResponseTimeWindow = timeGetTime(); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setupWorkshopProductionType.cpp b/src/OpenSHC/AI/AICState/setupWorkshopProductionType.cpp new file mode 100644 index 00000000..2ce93575 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setupWorkshopProductionType.cpp @@ -0,0 +1,96 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Game/Resources/ResourceType.hpp" +#include "OpenSHC/Game/Resources/ResourceTypeShort.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/Map/Buildings/BuildingTypeShort.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::Game::Resources::ResourceType; + using OpenSHC::Game::Resources::ResourceTypeShort; + using OpenSHC::Map::Buildings::BuildingType; + using OpenSHC::Map::Buildings::BuildingTypeShort; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CBE30 + void AICState ::setupWorkshopProductionType(int playerID, int buildingID) + { + AITypeInt AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (AVar1 == OpenSHC::AI::AIT_NULL) + return; + + BuildingTypeShort _buildingType = DAT_BuildingsState::instance.buildings[buildingID].buildingType; + int _aiTypeMin1 = AVar1 + ~OpenSHC::AI::AIT_NULL; + + if (_buildingType == OpenSHC::Map::Buildings::BT_BLACKSMITH) { + _aiTypeMin1 = *(int*)((int)this + _aiTypeMin1 * 0x2a4 + 200); + if (_aiTypeMin1 == -999) { + _aiTypeMin1 = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::chooseProductionItemType, + DAT_BuildingsState::ptr)((int)DAT_BuildingsState::instance.buildings[buildingID].owner, + OpenSHC::Map::Buildings::BT_BLACKSMITH); + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = (ResourceTypeShort)_aiTypeMin1; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = (ResourceTypeShort)_aiTypeMin1; + return; + } + if (_aiTypeMin1 == 0x16) { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = OpenSHC::Game::Resources::RT_SWORD; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = OpenSHC::Game::Resources::RT_SWORD; + } else { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = OpenSHC::Game::Resources::RT_MACE; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = OpenSHC::Game::Resources::RT_MACE; + } + return; + } + + if (_buildingType == OpenSHC::Map::Buildings::BT_POLETURNER) { + _aiTypeMin1 = *(int*)((int)this + _aiTypeMin1 * 0x2a4 + 0xd0); + if (_aiTypeMin1 == -999) { + _aiTypeMin1 = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::chooseProductionItemType, + DAT_BuildingsState::ptr)((int)DAT_BuildingsState::instance.buildings[buildingID].owner, + OpenSHC::Map::Buildings::BT_POLETURNER); + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = (ResourceTypeShort)_aiTypeMin1; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = (ResourceTypeShort)_aiTypeMin1; + return; + } + if (_aiTypeMin1 == 0x13) { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = OpenSHC::Game::Resources::RT_SPEAR; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = OpenSHC::Game::Resources::RT_SPEAR; + } else { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = OpenSHC::Game::Resources::RT_PIKE; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = OpenSHC::Game::Resources::RT_PIKE; + } + return; + } + + if (_buildingType == OpenSHC::Map::Buildings::BT_FLETCHER) { + _aiTypeMin1 = *(int*)((int)this + _aiTypeMin1 * 0x2a4 + 0xcc); + if (_aiTypeMin1 == -999) { + _aiTypeMin1 = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::chooseProductionItemType, + DAT_BuildingsState::ptr)((int)DAT_BuildingsState::instance.buildings[buildingID].owner, + OpenSHC::Map::Buildings::BT_FLETCHER); + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = (ResourceTypeShort)_aiTypeMin1; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = (ResourceTypeShort)_aiTypeMin1; + return; + } + if (_aiTypeMin1 == 0x11) { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = OpenSHC::Game::Resources::RT_BOW; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = OpenSHC::Game::Resources::RT_BOW; + } else { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = OpenSHC::Game::Resources::RT_CROSSBOW; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = OpenSHC::Game::Resources::RT_CROSSBOW; + } + } + + return; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shareGoldAmongTeamMembers.cpp b/src/OpenSHC/AI/AICState/shareGoldAmongTeamMembers.cpp new file mode 100644 index 00000000..80ba930b --- /dev/null +++ b/src/OpenSHC/AI/AICState/shareGoldAmongTeamMembers.cpp @@ -0,0 +1,203 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/OS.func.hpp" +#include "OpenSHC/Synchrony/GameSynchronyState.func.hpp" +#include "OpenSHC/Text/TextManager.func.hpp" +#include "OpenSHC/DE/SHCDE/eTextSections.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TextManagerObject.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::DE::SHCDE::eTextSections; + using OpenSHC::Game::Player::PlayerData; + + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names */ + + /* + WARNING: Enum "DPERRInt": Some values do not have unique names */ + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D05D0 + int AICState ::shareGoldAmongTeamMembers(int playerID, int gold) + + { + + int* piVar1; + + char cVar2; + + int _playerOffset; + + int _goldPerPlayer; + + char* pcVar3; + + char* pcVar4; + + PlayerData* _ptrPlayerData; + + int _totalPlayers; + + int _candidates[9]; + + int _targetPlayer; + + int _playerIndex; + + int _playerIndexTracker; + + int* _ptrCurrentGold; + + _totalPlayers = 0; + + _ptrPlayerData = &DAT_GameState::instance.playerDataArray[2]; + + _playerOffset = 0; + + _playerIndex = 3; + + do { + + if ((((*(int*)((int)DAT_GameSynchronyState::instance.currentAIArray + _playerOffset + 4) != 0) + || (*(int*)((int)DAT_GameSynchronyState::instance.currentPlayerFullIDArray + _playerOffset + 4) + != -1)) + && (_playerIndex + -2 != playerID)) + && ((*(int*)((int)DAT_GameState::instance.mapAndTime.playerTeams + _playerOffset + 4) + == DAT_GameState::instance.mapAndTime.playerTeams[playerID] + && (DAT_GameState::instance.playerDataArray[1].lordKilledByPlayerID == 0)))) { + + /* + if lord of team member not dead, then: */ + + _candidates[_totalPlayers] = _playerIndex + -2; + + _totalPlayers = _totalPlayers + 1; + } + + if (((*(int*)((int)DAT_GameSynchronyState::instance.currentAIArray + _playerOffset + 8) != 0) + || (*(int*)((int)DAT_GameSynchronyState::instance.currentPlayerFullIDArray + _playerOffset + 8) + != -1)) + && ((_playerIndex + -1 != playerID + && ((*(int*)((int)DAT_GameState::instance.mapAndTime.playerTeams + _playerOffset + 8) + == DAT_GameState::instance.mapAndTime.playerTeams[playerID] + && (_ptrPlayerData->lordKilledByPlayerID == 0)))))) { + + _candidates[_totalPlayers] = _playerIndex + -1; + + _totalPlayers = _totalPlayers + 1; + } + + if ((((*(int*)((int)DAT_GameSynchronyState::instance.currentAIArray + _playerOffset + 0xc) != 0) + || (*(int*)((int)DAT_GameSynchronyState::instance.currentPlayerFullIDArray + _playerOffset + 0xc) + != -1)) + && (_playerIndex != playerID)) + && ((*(int*)((int)DAT_GameState::instance.mapAndTime.playerTeams + _playerOffset + 0xc) + == DAT_GameState::instance.mapAndTime.playerTeams[playerID] + && (DAT_GameState::instance.playerDataArray[3].lordKilledByPlayerID == 0)))) { + + _candidates[_totalPlayers] = _playerIndex; + + _totalPlayers = _totalPlayers + 1; + } + + if (((*(int*)((int)DAT_GameSynchronyState::instance.currentAIArray + _playerOffset + 0x10) != 0) + || (*(int*)((int)DAT_GameSynchronyState::instance.currentPlayerFullIDArray + _playerOffset + 0x10) + != -1)) + && ((_playerIndex + 1 != playerID + && ((*(int*)((int)DAT_GameState::instance.mapAndTime.playerTeams + _playerOffset + 0x10) + == DAT_GameState::instance.mapAndTime.playerTeams[playerID] + && (DAT_GameState::instance.playerDataArray[4].lordKilledByPlayerID == 0)))))) { + + _candidates[_totalPlayers] = _playerIndex + 1; + + _totalPlayers = _totalPlayers + 1; + } + + _playerIndexTracker = _playerIndex + 2; + + _playerOffset = _playerOffset + 16; + + _ptrPlayerData = _ptrPlayerData + 0x39f4; + + _playerIndex = _playerIndex + 4; + + } while (_playerIndexTracker < 9); + + /* + fixme: repurposed variable */ + + if (((_totalPlayers != 0) + && (_goldPerPlayer = gold / _totalPlayers, _playerOffset = _goldPerPlayer, _goldPerPlayer != 0)) + && (_playerIndex = 0, 0 < _totalPlayers)) { + + _ptrCurrentGold = DAT_GameState::instance.playerDataArray[playerID].currentResources + 0xf; + + do { + + _targetPlayer = _candidates[_playerIndex]; + + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[_targetPlayer] != -1) { + + /* + "Goods received from ally" + added by script: "Goods received from ally" */ + + pcVar3 = MACRO_CALL_MEMBER(OpenSHC::Text::TextManager_Func::getTextStringInGroupAtOffset, + DAT_TextManagerObject::ptr)(OpenSHC::DE::SHCDE::TEXT_ALLIES2, 1); + + pcVar4 = DAT_GameSynchronyState::instance.receivedChatMessage; + + do { + + cVar2 = *pcVar3; + + *pcVar4 = cVar2; + + pcVar3 = pcVar3 + 1; + + pcVar4 = pcVar4 + 1; + + } while (cVar2 != '\0'); + + MACRO_CALL_MEMBER(OpenSHC::Synchrony::GameSynchronyState_Func::addChatMessageToDisplayList, + DAT_GameSynchronyState::ptr)(_targetPlayer, 0); + + /* + "Goods types" + added by script: "Gold" */ + + pcVar3 = MACRO_CALL_MEMBER(OpenSHC::Text::TextManager_Func::getTextStringInGroupAtOffset, + DAT_TextManagerObject::ptr)(OpenSHC::DE::SHCDE::TEXT_GOODS, 0xf); + + MACRO_CALL(OpenSHC::OS_Func::_sprintf)( + DAT_GameSynchronyState::instance.receivedChatMessage, "%d %s - ", _goldPerPlayer, pcVar3); + + MACRO_CALL_MEMBER(OpenSHC::Synchrony::GameSynchronyState_Func::addChatMessageToDisplayList, + DAT_GameSynchronyState::ptr)(_targetPlayer, playerID); + } + + *_ptrCurrentGold = *_ptrCurrentGold - _goldPerPlayer; + + piVar1 = DAT_GameState::instance.playerDataArray[_targetPlayer].currentResources + 0xf; + + *piVar1 = *piVar1 + _goldPerPlayer; + + _playerOffset = _targetPlayer * 0x39f4 + 0x115c304; + + _playerIndex = _playerIndex + 1; + + } while (_playerIndex < _totalPlayers); + } + + return _playerOffset; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/shouldDestroyWoodCutterUnk.cpp b/src/OpenSHC/AI/AICState/shouldDestroyWoodCutterUnk.cpp new file mode 100644 index 00000000..7b9edebb --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldDestroyWoodCutterUnk.cpp @@ -0,0 +1,46 @@ +#include "OpenSHC/AI/AIVState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB890 + BOOLEnum AICState::shouldDestroyWoodCutterUnk(int playerID) + { + int _hut; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) + return FALSE; + + if (DAT_AIVState::instance.mapExtraInfo.totalWoodAvailable <= 0) { + _hut = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::findFirstBuildingIDForPlayerAndType, + DAT_BuildingsState::ptr)(playerID, Map::Buildings::BT_WOODCUTTERSHUT); + } else { + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[2] <= 0x14) + return FALSE; + if (DAT_GameState::instance.playerDataArray[playerID].noLabourerBuildingCount < 2) + return FALSE; + if (DAT_GameState::instance.playerDataArray[playerID].someCount10 < 25) + return FALSE; + _hut = DAT_GameState::instance.playerDataArray[playerID].someCount11; + } + + if (_hut == 0) + return FALSE; + + MACRO_CALL_MEMBER(AIVState_Func::resetCountdownInHeatMap, DAT_AIVState::ptr)( + (int)(short)DAT_BuildingsState::instance.buildings[_hut].x, + (int)(short)DAT_BuildingsState::instance.buildings[_hut].y); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::giveBackResourceForDestroyedBuilding, + DAT_BuildingsState::ptr)(_hut, playerID, 50); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(_hut); + return TRUE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shouldNotBeBuildingArmoury.cpp b/src/OpenSHC/AI/AICState/shouldNotBeBuildingArmoury.cpp new file mode 100644 index 00000000..51ca9393 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBeBuildingArmoury.cpp @@ -0,0 +1,18 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ECA60 + BOOLEnum AICState::shouldNotBeBuildingArmoury(PlayerID playerID, Commands::MappersEnum commandBuildingType) + { + if (commandBuildingType != Commands::M_MAPPER_ARMOURY) { + return FALSE; + } + return DAT_GameState::instance.playerDataArray[playerID].isArmouryAlmostFilledUp == 0; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shouldNotBeBuildingGranary.cpp b/src/OpenSHC/AI/AICState/shouldNotBeBuildingGranary.cpp new file mode 100644 index 00000000..5ef58f27 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBeBuildingGranary.cpp @@ -0,0 +1,18 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ECA30 + BOOLEnum AICState::shouldNotBeBuildingGranary(int playerID, Commands::MappersEnum commandBuildingType) + { + if (commandBuildingType != Commands::M_MAPPER_GRANARY) { + return FALSE; + } + return DAT_GameState::instance.playerDataArray[playerID].granaryIsAlmostFilledUp == FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shouldNotBeBuildingHovel.cpp b/src/OpenSHC/AI/AICState/shouldNotBeBuildingHovel.cpp new file mode 100644 index 00000000..d14e60a1 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBeBuildingHovel.cpp @@ -0,0 +1,36 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB5B0 + BOOLEnum AICState::shouldNotBeBuildingHovel(PlayerID playerID, Commands::MappersEnum param_2) + { + if (param_2 != Commands::M_MAPPER_HOVEL) { + return FALSE; + } + + int const populationCap = DAT_GameState::instance.playerDataArray[playerID].populationCap; + if (populationCap <= 12) { + return FALSE; + } + + if (populationCap <= DAT_GameState::instance.playerDataArray[playerID].currentPopulation) { + return TRUE; + } + + if (DAT_GameState::instance.playerDataArray[playerID].previousAvailablePeasants >= 5) { + return TRUE; + } + + if (DAT_GameState::instance.playerDataArray[playerID].averagePopulationGrowthUnk >= 5) { + return TRUE; + } + + return DAT_GameState::instance.playerDataArray[playerID].popularity < 5000; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shouldNotBuildKillingPits.cpp b/src/OpenSHC/AI/AICState/shouldNotBuildKillingPits.cpp new file mode 100644 index 00000000..6c80452e --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBuildKillingPits.cpp @@ -0,0 +1,18 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_AIVState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ECA90 + BOOLEnum AICState::shouldNotBuildKillingPits(int playerID, Commands::MappersEnum commandBuildingType) + { + if (commandBuildingType == Commands::M_MAPPER_KILLING_PIT) { + return (BOOLEnum)(DAT_AIVState::instance.mapExtraInfo.playerTotalKillingPits[playerID] >= 100); + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shouldNotBuildMarketArmoryGranaryBarracksMercenarypost.cpp b/src/OpenSHC/AI/AICState/shouldNotBuildMarketArmoryGranaryBarracksMercenarypost.cpp new file mode 100644 index 00000000..c1ac9a6b --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBuildMarketArmoryGranaryBarracksMercenarypost.cpp @@ -0,0 +1,29 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB9F0 + BOOLEnum AICState::shouldNotBuildMarketArmoryGranaryBarracksMercenarypost( + int playerID, Commands::MappersEnum param_2) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker > 0) { + // nervous: reject market/mercenarypost/barracks/armory types + if (param_2 != Commands::M_MAPPER_TRADEPOST && param_2 != Commands::M_MAPPER_BARRACKS_ARAB + && param_2 != Commands::M_MAPPER_BARRACKS_EURO && param_2 != Commands::M_MAPPER_ARMOURY) { + return param_2 != Commands::M_MAPPER_GRANARY; + } + } else { + // not nervous: only reject if destroy tracker > 0 and not marketplace + if (DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker > 0 + && param_2 != Commands::M_MAPPER_TRADEPOST) { + return param_2 != Commands::M_MAPPER_GRANARY; + } + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shouldWaitWithBuildingAPositiveFearFactor.cpp b/src/OpenSHC/AI/AICState/shouldWaitWithBuildingAPositiveFearFactor.cpp new file mode 100644 index 00000000..83caadc1 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldWaitWithBuildingAPositiveFearFactor.cpp @@ -0,0 +1,50 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB700 + BOOLEnum AICState::shouldWaitWithBuildingAPositiveFearFactor(int playerID, Commands::MappersEnum param_2) + { + switch (param_2) { + case Commands::M_MAPPER_GARDEN1: + case Commands::M_MAPPER_GARDEN2: + case Commands::M_MAPPER_GARDEN3: + case Commands::M_MAPPER_GARDEN4: + case Commands::M_MAPPER_GARDEN5: + case Commands::M_MAPPER_GARDEN6: + case Commands::M_MAPPER_GARDEN7: + case Commands::M_MAPPER_GARDEN8: + case Commands::M_MAPPER_GARDEN9: + case Commands::M_MAPPER_GARDEN10: + case Commands::M_MAPPER_GARDEN11: + case Commands::M_MAPPER_GARDEN12: + case Commands::M_MAPPER_MAYPOLE: + case Commands::M_MAPPER_STATUE1: + case Commands::M_MAPPER_STATUE2: + case Commands::M_MAPPER_STATUE3: + case Commands::M_MAPPER_STATUE4: + case Commands::M_MAPPER_STATUE5: + case Commands::M_MAPPER_SHRINE1: + case Commands::M_MAPPER_SHRINE2: + case Commands::M_MAPPER_SHRINE3: + case Commands::M_MAPPER_SHRINE4: + case Commands::M_MAPPER_SHRINE5: + case Commands::M_MAPPER_DANCING_BEAR: + case Commands::M_MAPPER_POND1: + case Commands::M_MAPPER_POND2_SMALL: + case Commands::M_MAPPER_POND3_LARGE1: + case Commands::M_MAPPER_POND4_LARGE2: + DAT_GameState::instance.playerDataArray[playerID].aivPositiveFearFactorDelay += 1; + if (DAT_GameState::instance.playerDataArray[playerID].aivPositiveFearFactorDelay < 200) { + return TRUE; + } + DAT_GameState::instance.playerDataArray[playerID].aivPositiveFearFactorDelay = 0; + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shouldWaitWithBuildingNegativeFearFactor.cpp b/src/OpenSHC/AI/AICState/shouldWaitWithBuildingNegativeFearFactor.cpp new file mode 100644 index 00000000..db2fd86b --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldWaitWithBuildingNegativeFearFactor.cpp @@ -0,0 +1,38 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB610 + BOOLEnum AICState::shouldWaitWithBuildingNegativeFearFactor(int param_1, Commands::MappersEnum param_2) + { + int* piVar1; + switch (param_2) { + case Commands::M_MAPPER_GALLOWS: + case Commands::M_MAPPER_STOCKS: + case Commands::M_MAPPER_CESS_PIT1: + case Commands::M_MAPPER_CESS_PIT2: + case Commands::M_MAPPER_CESS_PIT3: + case Commands::M_MAPPER_CESS_PIT4: + case Commands::M_MAPPER_BURNING_STAKE: + case Commands::M_MAPPER_GIBBET: + case Commands::M_MAPPER_DUNGEON: + case Commands::M_MAPPER_RACK_STRETCHING: + case Commands::M_MAPPER_RACK_FLOGGING: + case Commands::M_MAPPER_CHOPPING_BLOCK: + case Commands::M_MAPPER_DUNKING_STOOL: + if ((DAT_GameState::instance.playerDataArray[param_1].popularity < 5000) + || (piVar1 = &DAT_GameState::instance.playerDataArray[param_1].aivNegativeFearFactorDelay, + *piVar1 = *piVar1 + 1, + DAT_GameState::instance.playerDataArray[param_1].aivNegativeFearFactorDelay < 100)) { + return TRUE; + } + DAT_GameState::instance.playerDataArray[param_1].aivNegativeFearFactorDelay = 0; + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/smallestTribeOfUnitType.cpp b/src/OpenSHC/AI/AICState/smallestTribeOfUnitType.cpp new file mode 100644 index 00000000..02619dda --- /dev/null +++ b/src/OpenSHC/AI/AICState/smallestTribeOfUnitType.cpp @@ -0,0 +1,63 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC9B0 + int AICState::smallestTribeOfUnitType(int playerID, UnitType unitType) + { + int iVar7 = 0; + int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == AITA_NULL) { + return 0; + } + int iVar3 = (int)this->aics[_aiType - 1].defWallPatrolGroups; + int iVar6 = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[(int)unitType]; + int _tribeID = 0; + int local_c = 0; + short sVar4 = 1000; + if ((iVar3 < iVar6) && (((unitType == 0x8 || (unitType == 0xa)) || (unitType == 0x11)))) { + iVar6 = iVar3; + } + iVar3 = DAT_SkirmishDefinedData::instance.AITribeIDOffsetForUnitType[(int)unitType]; + if (0 < iVar6) { + short* _aiTribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs + iVar3; + do { + int iVar5 = (int)*_aiTribeID; + if ((iVar5 == 0) + || (DAT_TribesState::instance.tribes[iVar5].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[iVar3 + iVar7])) { + _tribeID = MACRO_CALL_MEMBER( + Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[iVar3 + iVar7] = (short)_tribeID; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[iVar3 + iVar7] + = DAT_TribesState::instance.tribes[_tribeID].uid; + return _tribeID; + } + short sVar1 = DAT_TribesState::instance.tribes[iVar5].size; + if (sVar1 < sVar4) { + _tribeID = iVar5; + local_c = iVar7; + sVar4 = sVar1; + } + _aiTribeID = _aiTribeID + 1; + iVar7 = iVar7 + 1; + } while (iVar7 < iVar6); + iVar7 = local_c; + if (_tribeID != 0) { + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[iVar3 + iVar7] = (short)_tribeID; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[iVar3 + iVar7] + = DAT_TribesState::instance.tribes[_tribeID].uid; + return _tribeID; + } + } + return 0; + } +} +} diff --git a/src/OpenSHC/AI/AICState/someChatMessageSelection.cpp b/src/OpenSHC/AI/AICState/someChatMessageSelection.cpp new file mode 100644 index 00000000..a92dbbe8 --- /dev/null +++ b/src/OpenSHC/AI/AICState/someChatMessageSelection.cpp @@ -0,0 +1,213 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/GameCore.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/Synchrony/GameSynchronyState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Commands/GameCommandType.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::Commands::GameCommandType; + using OpenSHC::Game::GameMode; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + /* + WARNING: Enum "DPSEND_EnumInt": Some values do not have unique names */ + + /* + WARNING: Enum "DPERRInt": Some values do not have unique names */ + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004D10B0 + void AICState ::someChatMessageSelection() + + { + + BOOLEnum BVar1; + + DWORD DVar2; + + int iVar3; + + int iVar4; + + int iVar5; + + int iVar6; + + int aiStack_24[9]; + + BVar1 = MACRO_CALL_MEMBER(OpenSHC::Game::GameCore_Func::getAreWeInAInGameMenu, DAT_GameCore::ptr)(); + + if ((BVar1 != FALSE) + && (DAT_GameSynchronyState::instance.DAT_CurrentGameMode == OpenSHC::Game::GM_SKIRMISH_SINGLE_PLAYER)) { + + if (this->aiTauntResponseTimeWindow == 0) { + + return; + } + + DVar2 = timeGetTime(); + + if (DVar2 - this->aiTauntResponseTimeWindow < 2000) { + + return; + } + + iVar6 = 0; + + iVar3 = 0; + + iVar4 = 3; + + do { + + if ((*(int*)((int)DAT_GameSynchronyState::instance.currentAIArray + iVar3 + 4) != 0) + && (*(int*)((int)DAT_GameState::instance.mapAndTime.playerTeams + iVar3 + 4) + != DAT_GameState::instance.mapAndTime + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID])) { + + aiStack_24[iVar6] = iVar4 + -2; + + iVar6 = iVar6 + 1; + } + + if ((*(int*)((int)DAT_GameSynchronyState::instance.currentAIArray + iVar3 + 8) != 0) + && (*(int*)((int)DAT_GameState::instance.mapAndTime.playerTeams + iVar3 + 8) + != DAT_GameState::instance.mapAndTime + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID])) { + + aiStack_24[iVar6] = iVar4 + -1; + + iVar6 = iVar6 + 1; + } + + if ((*(int*)((int)DAT_GameSynchronyState::instance.currentAIArray + iVar3 + 0xc) != 0) + && (*(int*)((int)DAT_GameState::instance.mapAndTime.playerTeams + iVar3 + 0xc) + != DAT_GameState::instance.mapAndTime + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID])) { + + aiStack_24[iVar6] = iVar4; + + iVar6 = iVar6 + 1; + } + + if ((*(int*)((int)DAT_GameSynchronyState::instance.currentAIArray + iVar3 + 0x10) != 0) + && (*(int*)((int)DAT_GameState::instance.mapAndTime.playerTeams + iVar3 + 0x10) + != DAT_GameState::instance.mapAndTime + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID])) { + + aiStack_24[iVar6] = iVar4 + 1; + + iVar6 = iVar6 + 1; + } + + iVar5 = iVar4 + 2; + + iVar3 = iVar3 + 0x10; + + iVar4 = iVar4 + 4; + + } while (iVar5 < 9); + + if (0 < iVar6) { + + iVar3 = 0; + + iVar4 = 0; + + if (0 < iVar6) { + + do { + + iVar3 = iVar3 + + DAT_SkirmishDefinedData::instance + .MaxBreachTribes[DAT_GameState::instance.playerDataArray[aiStack_24[iVar4]].aiType + + OpenSHC::AI::AIT_FREDERICK] + .tribeType; + + iVar4 = iVar4 + 1; + + } while (iVar4 < iVar6); + + if (iVar3 != 0) { + + iVar5 = (int)SEC_RNG::instance.currentNumber1; + + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber1, SEC_RNG::ptr)(); + + iVar5 = iVar5 % iVar3; + + iVar4 = 0; + + do { + + iVar3 = aiStack_24[iVar4]; + + iVar5 = iVar5 + - DAT_SkirmishDefinedData::instance + .MaxBreachTribes[DAT_GameState::instance.playerDataArray[iVar3].aiType + + OpenSHC::AI::AIT_FREDERICK] + .tribeType; + + if (iVar5 < 0) { + + DAT_GameSynchronyState::instance.DAT_ChatTauntOrMessage = -2 + - DAT_SkirmishDefinedData::instance + .MaxBreachTribes[DAT_GameState::instance.playerDataArray[iVar3].aiType + + OpenSHC::AI::AIT_FREDERICK] + .tribeCount; + + DAT_GameSynchronyState::instance.DAT_ChatMessageReceiverArray[1] = 1; + + DAT_GameSynchronyState::instance.DAT_ChatMessageReceiverArray[2] = 1; + + DAT_GameSynchronyState::instance.DAT_ChatMessageReceiverArray[3] = 1; + + DAT_GameSynchronyState::instance.DAT_ChatMessageReceiverArray[4] = 1; + + DAT_GameSynchronyState::instance.DAT_ChatMessageReceiverArray[5] = 1; + + DAT_GameSynchronyState::instance.DAT_ChatMessageReceiverArray[6] = 1; + + DAT_GameSynchronyState::instance.DAT_ChatMessageReceiverArray[7] = 1; + + DAT_GameSynchronyState::instance.DAT_ChatMessageReceiverArray[8] = 1; + + DAT_GameSynchronyState::instance.DAT_GameCommandParam0 = iVar3; + + MACRO_CALL_MEMBER(OpenSHC::Synchrony::GameSynchronyState_Func::queueCommand, + DAT_GameSynchronyState::ptr)(OpenSHC::Commands::GCT_TAUNT_OR_CHAT); + + this->aiTauntResponseTimeWindow = 0; + + return; + } + + iVar4 = iVar4 + 1; + + } while (iVar4 < iVar6); + } + } + } + } + + this->aiTauntResponseTimeWindow = 0; + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/syncBehaviourType7UnitTribes.cpp b/src/OpenSHC/AI/AICState/syncBehaviourType7UnitTribes.cpp new file mode 100644 index 00000000..7cfb1dbd --- /dev/null +++ b/src/OpenSHC/AI/AICState/syncBehaviourType7UnitTribes.cpp @@ -0,0 +1,37 @@ +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/Map/Units/UnitLogicState.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC7B0 + void AICState::syncBehaviourType7UnitTribes(int param_1) + { + int tribeID; + uint unitID = 1; + if (1 < (int)DAT_UnitsState::instance.maxUnitCount) { + Map::Units::Unit* pUVar1 = &DAT_UnitsState::instance.units[1]; + do { + if ((((pUVar1->owner == param_1) && (pUVar1->logicalState == Map::Units::ULS_NORMAL)) + && (pUVar1->dying == 0)) + && (pUVar1->aiUnitBehaviourType == 7)) { + tribeID = (int)DAT_GameState::instance.playerDataArray[param_1].aiTribeIDs[0xa7]; + if (pUVar1->tribeID != tribeID) { + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::removeUnitFromTribe, DAT_TribesState::ptr)( + unitID, (int)pUVar1->tribeID); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + unitID, tribeID); + } + } + unitID = unitID + 1; + pUVar1 = pUVar1 + 1; + } while ((int)unitID < (int)DAT_UnitsState::instance.maxUnitCount); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/teamIsWeakRelativeToEnemy.cpp b/src/OpenSHC/AI/AICState/teamIsWeakRelativeToEnemy.cpp new file mode 100644 index 00000000..63e7743b --- /dev/null +++ b/src/OpenSHC/AI/AICState/teamIsWeakRelativeToEnemy.cpp @@ -0,0 +1,65 @@ +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0190 + BOOLEnum AICState::teamIsWeakRelativeToEnemy(int playerID, BOOLEnum param_2) + { + short _relativeStrength; + int _totalEnemyTroopValue = DAT_GameState::instance.playerDataArray[playerID].totalEnemyTroopValue; + if (_totalEnemyTroopValue == 0) { + _relativeStrength = 100; + } else { + _relativeStrength = (short)((DAT_GameState::instance.playerDataArray[playerID].totalTeamTroopValue * 100) + / _totalEnemyTroopValue); + } + DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy = _relativeStrength; + if (param_2 != FALSE) { + BOOLEnum BVar2 + = MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)(playerID); + if (BVar2 != FALSE) { + if ((short)DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy < 0x46) { + return TRUE; + } + return FALSE; + } + if ((int)DAT_GameCore::instance.mapTimeInTicks < 12000) { + if ((short)DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy < 300) { + return TRUE; + } + return FALSE; + } + if ((short)DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy < 100) { + return TRUE; + } + } else { + if (DAT_GameState::instance.playerDataArray[playerID].totalEnemyRangedTroopValue < 100) { + return FALSE; + } + BOOLEnum BVar2 + = MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)(playerID); + if (BVar2 != FALSE) { + if ((short)DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy < 50) { + return TRUE; + } + return FALSE; + } + if ((int)DAT_GameCore::instance.mapTimeInTicks < 12000) { + if ((short)DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy < 250) { + return TRUE; + } + return FALSE; + } + if ((short)DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy < 75) { + return TRUE; + } + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/unitIDIsRangedOrArmored.cpp b/src/OpenSHC/AI/AICState/unitIDIsRangedOrArmored.cpp new file mode 100644 index 00000000..ff18d5cb --- /dev/null +++ b/src/OpenSHC/AI/AICState/unitIDIsRangedOrArmored.cpp @@ -0,0 +1,32 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Game/Skirmish/SkirmishDefinedData.hpp" + +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::Game::Skirmish::SkirmishDefinedData; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC8D0 + undefined4 AICState ::unitIDIsRangedOrArmored(int unitID) + + { + int _unitType = (int)(short)DAT_UnitsState::instance.units[unitID].unitType; + + SkirmishDefinedData* pSVar1 = DAT_SkirmishDefinedData::ptr; + + do { + if (pSVar1->RangedAndArmoredUnits[0] == _unitType) + return (undefined4)(1); + + pSVar1 = (SkirmishDefinedData*)(pSVar1->RangedAndArmoredUnits + 1); + + } while ((int)pSVar1 < 0xb3eb50); + + return (undefined4)(0); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/updateAIBehaviour.cpp b/src/OpenSHC/AI/AICState/updateAIBehaviour.cpp new file mode 100644 index 00000000..09488387 --- /dev/null +++ b/src/OpenSHC/AI/AICState/updateAIBehaviour.cpp @@ -0,0 +1,105 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/AI/AIRecruitUnitChoice.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/Game/Player/PlayerData.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TileMapState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIRecruitUnitChoice; + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::Game::Player::PlayerData; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D53E0 + void AICState::updateAIBehaviour() + { + if (DAT_GameState::instance.mapAndTime.dayTicks == 10) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::makeOutpostTribesAttack, this)(); + } + + for (int _playerID = 1; _playerID < 9; _playerID++) { + // Compiler doesn't optimize it with ADJ() like it should, not sure why it doesn't do it. + PlayerData* const _pPlayerData = &DAT_GameState::instance.playerDataArray[_playerID]; + AITypeInt const _aiType = _pPlayerData->aiType; + + if (_aiType == OpenSHC::AI::AIT_NULL) + continue; + if (_pPlayerData->playerDeathRelated != 0) + continue; + if (_pPlayerData->lordKilledByPlayerID != 0) + continue; + + if (DAT_GameState::instance.mapAndTime.dayTicks == _playerID) { + if (DAT_GameState::instance.mapAndTime.totalNoRushTicks == 0) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiRecruitSortieRangedUnits, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiRecruitSortieMeleeUnits, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiRecruitUnits, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recruitHarrassingSiegeEngines, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiAssignMoatDiggers, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCreateSiegeUnits, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiRecruitEngineers, this)(_playerID); + } + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiBuyAndSellGoods, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiGiveRaidInstructions, this)(_playerID); + } + + if (DAT_GameState::instance.mapAndTime.weekTicks == _playerID) { + if (DAT_GameState::instance.mapAndTime.totalNoRushTicks == 0) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiRecruitEngineerForOilDuty, this)(_playerID); + } + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::updateTribeLocations, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::updateAIPlayerState, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiUpdateTaxesAndRations, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setFoodBuyPlan, this)(_playerID); + + if (DAT_GameState::instance.mapAndTime.monthTicks != _playerID) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::updateAIStrengthState, this)(_playerID, 0); + } + + if (_pPlayerData->aiRequestDelay != 0) { + _pPlayerData->aiRequestDelay -= 1; + } + if (_pPlayerData->nervousBikCountdown != 0) { + _pPlayerData->nervousBikCountdown -= 1; + } + if (_pPlayerData->someAiCountdown3 != 0) { + _pPlayerData->someAiCountdown3 -= 1; + } + if (_pPlayerData->someAiCountdown4 != 0) { + _pPlayerData->someAiCountdown4 -= 1; + } + if (_pPlayerData->someAiCountdown5 != 0) { + _pPlayerData->someAiCountdown5 -= 1; + } + if (_pPlayerData->someAiCountdown6 != 0) { + _pPlayerData->someAiCountdown6 -= 1; + } + } + + if (DAT_GameState::instance.mapAndTime.monthTicks == _playerID) { + MACRO_CALL_MEMBER( + OpenSHC::Map::TileMapState_Func::countMoatsOwnedByEachPlayer, DAT_TileMapState::ptr)(); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::instructTribe166ToMove, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::giveMoveCommandToSortieUnits, this)(_playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::updateAIStrengthState, this)(_playerID, 1); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::selectAttackTarget, this)(_playerID); + + if (_pPlayerData->aiRecruitUnitChoiceState == OpenSHC::AI::AIRUC_RAIDING) { + _pPlayerData->canStartSpending = 1; + } else { + // shouldSpend = recruitGoldThreshold < currentGold + _pPlayerData->canStartSpending = (uint)(((int)this->aics[_aiType - 1].recruitGoldThreshold) + < _pPlayerData->currentResources[0xf]); + } + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/updateAIPlayerState.cpp b/src/OpenSHC/AI/AICState/updateAIPlayerState.cpp new file mode 100644 index 00000000..c0bee72c --- /dev/null +++ b/src/OpenSHC/AI/AICState/updateAIPlayerState.cpp @@ -0,0 +1,307 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Audio/SFX/SFXState.func.hpp" +#include "OpenSHC/Game/GameStateStructures.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SFXState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // Helper shared by state 4 (canNavigate==FALSE) and state 6 (canUnitsNavigate==FALSE) + __forceinline static void recomputeAndReset(OpenSHC::AI::AICState* pThis, int playerID) + { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recomputeAIPathCostToAttackedKeep, pThis)(playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeAttackPossibilities, pThis)(playerID); + DAT_GameState::instance.playerDataArray[playerID].unknownCounter01 = 0; + } + + // FUNCTION: STRONGHOLDCRUSADER 0x004D49E0 + void AICState ::updateAIPlayerState(int playerID) + { + AITypeInt AVar4 = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (AVar4 == OpenSHC::AI::AIT_NULL) + return; + + int _aiType = AVar4 + ~OpenSHC::AI::AIT_NULL; + + int iVar5 = DAT_GameState::instance.playerDataArray[playerID].requestStateUnk; + if (((iVar5 != 1) && (iVar5 == 2)) + && (iVar5 = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(DAT_GameState::instance.playerDataArray[playerID].playerID_askerUnk), + iVar5 != 0)) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::clearRallyFlagOnAllAIAttackTribes, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToPlayerIDsKeepIfPossible, this)( + playerID, ((int)(DAT_GameState::instance.playerDataArray[playerID].playerID_askerUnk))); + return; + } + + int _aiPlayerState = DAT_GameState::instance.playerDataArray[playerID].aiPlayerState; + + if (_aiPlayerState == 0) { + BOOLEnum _shouldAttack = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::aiShouldAttackOrWaitForTeamCoordination, this)(playerID); + if (_shouldAttack == FALSE) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiRetreatUnits, this)(playerID); + return; + } + DAT_GameState::instance.playerDataArray[playerID].attackTicker = 0; + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 1; + DAT_GameState::instance.playerDataArray[playerID].unitPatrolRecommandRelatedDelayValue = 0; + DAT_GameState::instance.playerDataArray[playerID].aiCoordinatedAttackPatience = 0; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setAttackCoordinationLevel, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setCurrentAttackStrength, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setCurrentAttackRaidParameter, this)(playerID); + int* piVar2 = (int*)(DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID * 0x39f4 + 0x115e9d4 + + playerID * 0x20); + *piVar2 = *piVar2 + 1; + } else { + BOOLEnum BVar6 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::hasNoTroopsOrAllAreDiggers, this)(playerID); + if ((((BVar6 != FALSE) && (_aiPlayerState != 9)) + || ((0 < DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker + && (_aiPlayerState < 6)))) + || (199 < DAT_GameState::instance.playerDataArray[playerID].attackTicker)) + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 8; + } + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::clearRallyFlagOnAllAIAttackTribes, this)(playerID); + + int _attackedPlayerAlive = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID); + + if (_attackedPlayerAlive == 0) { + iVar5 = DAT_GameState::instance.playerDataArray[playerID].aiPlayerState; + if (iVar5 < 4) { + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 9; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::disbandAIPlayerSiegeUnits, this)(playerID); + MACRO_CALL_MEMBER( + OpenSHC::Map::Buildings::BuildingsState_Func::removeSiegeBuildings, DAT_BuildingsState::ptr)( + DAT_GameState::instance.playerDataArray[playerID].currentAttackWave, playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addEngineersToTribe, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiReassignTunnelersToTribe, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::findAttackTribePositionAndPathToDefenses, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::makeUnitsGoDefensiveAndBackToSomeLocation, this)( + playerID); + } else if (iVar5 < 7) { + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 7; + } + } + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::clearOutdatedAITribes, this)(playerID); + + int _aiPlayerState_2 = DAT_GameState::instance.playerDataArray[playerID].aiPlayerState; + BOOLEnum BVar6; + int* piVar2; + + if (_aiPlayerState_2 == 1) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recreateEngineersTribe, this)(playerID); + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 2; + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::initializeGatePathfindingForOwner, + DAT_BuildingsState::ptr)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeEnemyKeepApproachTile, this)(playerID); + if (0 < DAT_GameState::instance.playerDataArray[playerID].shortestDistanceTile) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::moveAttackTribesToLocations, this)(playerID); + return; + } + // state 1 with no path: reset to 0 + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 0; + return; + } + + if (_aiPlayerState_2 == 2) { + uint _rallyReady + = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiChoiceAttForceRallyPercentage, this)(playerID); + if (_rallyReady == 0) + return; + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 3; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::useAITribe_0x12_toPlaceSiegeTentsAndAssignEngineers, this)( + playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recomputeAIPathCostToAttackedKeep, this)(playerID, 1); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeAttackPossibilities, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setNextMoveLocationForUnits, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::giveMoveCommandToAttackTribes, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::useAITribe_0xe_toPlaceTunnels, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::removeOrganismsAndSetMoveDestinationPairs, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::commandFourTribesToMove, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playTauntToAttackedPlayerBikFromPlayer, this)(playerID); + return; + } + + if (_aiPlayerState_2 == 3) { + piVar2 = &DAT_GameState::instance.playerDataArray[playerID].attackTicker; + *piVar2 = *piVar2 + 1; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::moveAttackingUnitsToNextRallyPoint, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendAITribe_0xf_toAttackPatrol, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCommandSiegeEngineTribes, this)(playerID); + BVar6 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::processAttForceRallyPercentage, this)(playerID); + if (BVar6 == FALSE) + return; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recomputeAIPathCostToAttackedKeep, this)(playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeAttackPossibilities, this)(playerID); + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 4; + DAT_GameState::instance.playerDataArray[playerID].attackAssaultDelayRelatedValue = 0; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCommandTribe12AggressiveApproach, this)(playerID); + return; + } + + if (_aiPlayerState_2 == 4) { + piVar2 = &DAT_GameState::instance.playerDataArray[playerID].attackTicker; + *piVar2 = *piVar2 + 1; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::moveAttackingUnitsToNextRallyPoint, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendAITribe_0xf_toAttackPatrol, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCommandSiegeEngineTribes, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeToAttack, this)(playerID); + iVar5 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::countSiegeEngineUnits, this)(playerID); + if (iVar5 < 1) + DAT_GameState::instance.playerDataArray[playerID].attackAssaultDelayRelatedValue = 100; + piVar2 = &DAT_GameState::instance.playerDataArray[playerID].attackAssaultDelayRelatedValue; + *piVar2 = *piVar2 + 1; + if (DAT_GameState::instance.playerDataArray[playerID].attackAssaultDelayRelatedValue + <= ((int)this->aics[_aiType].AttAssaultDelay)) + return; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recomputeAIPathCostToAttackedKeep, this)(playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeAttackPossibilities, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::Audio::SFX::SFXState_Func::playSFXAtLocationFullVolume, DAT_SFXState::ptr)( + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX, + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY, 0x14); + BVar6 = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, + DAT_GameState::ptr)(playerID, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID); + if (BVar6 == FALSE) { + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 5; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCommandTribe12AggressiveApproach, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCommandTribe11StandGroundToGate, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCommandTribe13DefensiveWave, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recomputeAIPathCostToAttackedKeep, this)(playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeAttackPossibilities, this)(playerID); + DAT_GameState::instance.playerDataArray[playerID].unknownCounter01 = 0; + return; + } + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 6; + return; + } + + if (_aiPlayerState_2 == 5) { + if (DAT_GameState::instance.playerDataArray[playerID].shortestDistanceTile < 1) + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 0; + short* psVar1 = &DAT_GameState::instance.playerDataArray[playerID].unknownCounter01; + *psVar1 = *psVar1 + 1; + short sVar3 = DAT_GameState::instance.playerDataArray[playerID].unknownCounter01; + piVar2 = &DAT_GameState::instance.playerDataArray[playerID].attackTicker; + *piVar2 = *piVar2 + 1; + DAT_GameState::instance.playerDataArray[playerID].someResourceCounter = 0; + if (3 < sVar3) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recomputeAIPathCostToAttackedKeep, this)(playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeAttackPossibilities, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCommandWallAssaultTribes, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiCommandSiegeEngineTribes, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setTribe0x11ToAggressiveAndAttackIfConditionMet, this)( + playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setEightTribe0xc0TribesToAggressiveAndAttack, this)( + playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeToAttack, this)(playerID); + DAT_GameState::instance.playerDataArray[playerID].unknownCounter01 = 0; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::removeOrganismsAndSetMoveDestinationPairs, this)( + playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::commandFourTribesToMove, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setTribe0xCtoAggressiveAndAttack, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::setTribe0xbToAggressiveAndAttack, this)(playerID); + } + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendAITribe_0xf_toAttackPatrol, this)(playerID); + BVar6 = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, + DAT_GameState::ptr)(playerID, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID); + if ((BVar6 == FALSE) + && (BVar6 = MACRO_CALL_MEMBER( + OpenSHC::Game::GameStateStructures_Func::canUnitsNavigateFromKeepToKeep, DAT_GameState::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID), + BVar6 == FALSE)) + return; + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 6; + return; + } + + if (_aiPlayerState_2 == 6) { + if (DAT_GameState::instance.playerDataArray[playerID].shortestDistanceTile < 1) + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 0; + piVar2 = &DAT_GameState::instance.playerDataArray[playerID].attackTicker; + *piVar2 = *piVar2 + 1; + int _playerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + DAT_GameState::instance.playerDataArray[playerID].someResourceCounter = 0; + MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::calculateAttackVectorsToCampFireOfPlayer, + DAT_GameState::ptr)(_playerID); + iVar5 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToAttackBreachedCastle, this)(playerID); + if (iVar5 != 0) + return; + BVar6 = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, + DAT_GameState::ptr)(playerID, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID); + if (BVar6 != FALSE) + return; + BVar6 = MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canUnitsNavigateFromKeepToKeep, + DAT_GameState::ptr)(playerID, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID); + if (BVar6 != FALSE) + return; + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 5; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::recomputeAIPathCostToAttackedKeep, this)(playerID, 0); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::computeAttackPossibilities, this)(playerID); + DAT_GameState::instance.playerDataArray[playerID].unknownCounter01 = 0; + return; + } + + if (_aiPlayerState_2 == 7) { + piVar2 = &DAT_GameState::instance.playerDataArray[playerID].unitPatrolRecommandRelatedDelayValue; + *piVar2 = *piVar2 + 1; + if (DAT_GameState::instance.playerDataArray[playerID].unitPatrolRecommandRelatedDelayValue + <= ((int)this->aics[_aiType].AttUnitPatrolRecommandDelay)) + return; + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 9; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::disbandAIPlayerSiegeUnits, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::removeSiegeBuildings, + DAT_BuildingsState::ptr)(DAT_GameState::instance.playerDataArray[playerID].currentAttackWave, playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addEngineersToTribe, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiReassignTunnelersToTribe, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::findAttackTribePositionAndPathToDefenses, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::makeUnitsGoDefensiveAndBackToSomeLocation, this)(playerID); + return; + } + + if (_aiPlayerState_2 == 8) { + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 9; + DAT_GameState::instance.playerDataArray[playerID].attackTicker = 0; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::disbandAIPlayerSiegeUnits, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::removeSiegeBuildings, + DAT_BuildingsState::ptr)(DAT_GameState::instance.playerDataArray[playerID].currentAttackWave, playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addEngineersToTribe, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiReassignTunnelersToTribe, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::findAttackTribePositionAndPathToDefenses, this)(playerID); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::makeUnitsGoDefensiveAndBackToSomeLocation, this)(playerID); + if (0 < DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker) + return; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playAnger1BikFromPlayer, this)(playerID); + return; + } + + if (_aiPlayerState_2 == 9) { + DAT_GameState::instance.playerDataArray[playerID].attackTicker += 1; + if (((DAT_GameState::instance.playerDataArray[playerID].attackTicker < 20) + && (BVar6 = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::percentageNonMovingTribesGTEAICSpecified, this)(playerID), + BVar6 == FALSE)) + && (BVar6 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::hasNoTroopsOrAllAreDiggers, this)(playerID), + BVar6 == FALSE)) + return; + // was LAB_004d4ff7 + DAT_GameState::instance.playerDataArray[playerID].aiPlayerState = 0; + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/updateAIStrengthState.cpp b/src/OpenSHC/AI/AICState/updateAIStrengthState.cpp new file mode 100644 index 00000000..8ff7b185 --- /dev/null +++ b/src/OpenSHC/AI/AICState/updateAIStrengthState.cpp @@ -0,0 +1,250 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Random/RNG.func.hpp" +#include "OpenSHC/AI/AINervousDestroyBuildingCategory.hpp" +#include "OpenSHC/AI/AINervousDestroyBuildingCategoryInt.hpp" +#include "OpenSHC/AI/AIRecruitUnitChoice.hpp" +#include "OpenSHC/AI/AIStrengthType.hpp" +#include "OpenSHC/Game/GameMode2.hpp" +#include "OpenSHC/Game/TrailType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/SEC_RNG.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AINervousDestroyBuildingCategory; + using OpenSHC::AI::AINervousDestroyBuildingCategoryInt; + using OpenSHC::AI::AIRecruitUnitChoice; + using OpenSHC::AI::AIStrengthType; + using OpenSHC::Game::GameMode2; + using OpenSHC::Game::TrailType; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3320 + void AICState ::updateAIStrengthState(int playerID, int aiRecruitUnitChoicePreference) + { + int _aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (_aiType == 0) + return; + + if ((char)aiRecruitUnitChoicePreference == '\0') { + if (DAT_GameCore::instance.gameMode_2 != OpenSHC::Game::GM_SKIRMISH_AND_MULTIPLAYER) + return; + if (DAT_GameCore::instance.isSkirmishTrail != TRUE) + return; + if (DAT_GameCore::instance.currentTrailType != OpenSHC::Game::TT_EXTREME) + return; + } + + int _aiTypeMin1 = _aiType + -1; + int _currentGold = DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf]; + DAT_GameState::instance.playerDataArray[playerID].aiStrengthState = 0; + + if (_currentGold >= 200) + DAT_GameState::instance.playerDataArray[playerID].aiStrengthState = (1999 < _currentGold) + 1; + + int _currentPopulation = DAT_GameState::instance.playerDataArray[playerID].currentPopulation; + if (15 < _currentPopulation) { + int* piVar1 = &DAT_GameState::instance.playerDataArray[playerID].aiStrengthState; + if (_currentPopulation < 40) + *piVar1 = *piVar1 + 1; + else + *piVar1 = *piVar1 + 2; + } + + int _totalFood = DAT_GameState::instance.playerDataArray[playerID].totalFood; + if (20 < _totalFood) { + int* piVar1 = &DAT_GameState::instance.playerDataArray[playerID].aiStrengthState; + if (_totalFood < 200) + *piVar1 = *piVar1 + 1; + else + *piVar1 = *piVar1 + 2; + } + + int _totalTroopCount = DAT_GameState::instance.playerDataArray[playerID].totalAttackTroops + + DAT_GameState::instance.playerDataArray[playerID].totalRaidingTroopsUnk + + DAT_GameState::instance.playerDataArray[playerID].totalDefensiveTroopsUnk; + if (7 < _totalTroopCount) { + int* piVar1 = &DAT_GameState::instance.playerDataArray[playerID].aiStrengthState; + if (_totalTroopCount < 40) + *piVar1 = *piVar1 + 2; + else + *piVar1 = *piVar1 + 4; + } + + int _aiStrengthState = DAT_GameState::instance.playerDataArray[playerID].aiStrengthState; + if (_aiStrengthState < 3) + DAT_GameState::instance.playerDataArray[playerID].aiStrengthFeeling = OpenSHC::AI::AIST_WEAK; + else if (_aiStrengthState < 8) + DAT_GameState::instance.playerDataArray[playerID].aiStrengthFeeling = OpenSHC::AI::AIST_DEFAULT; + else + DAT_GameState::instance.playerDataArray[playerID].aiStrengthFeeling = OpenSHC::AI::AIST_STRONG; + + if ((DAT_GameState::instance.playerDataArray[playerID].aivCurrentPauseIndex == 1) + && (0 < DAT_GameState::instance.playerDataArray[playerID].aivPauses[1])) + DAT_GameState::instance.playerDataArray[playerID].aiStrengthFeeling = OpenSHC::AI::AIST_WEAK; + + int _aicOffset = DAT_GameState::instance.playerDataArray[playerID].aiStrengthFeeling + _aiTypeMin1 * 169; + int _recruitProbRaid = *(int*)((int)this + _aicOffset * 4 + 0x134); + int _recruitProbDefensive = *(int*)((int)this + _aicOffset * 4 + 0x128); + int _totalRecruitProb = *(int*)((int)this + _aicOffset * 4 + 0x140) + _recruitProbRaid + _recruitProbDefensive; + + int _aiRecruitUnitChoiceState = OpenSHC::AI::AIRUC_ATTACKING; + bool bVar3 = false; + int _subtraction = 0; + bool _needsCheck = false; + + if (_totalRecruitProb == 0) { + _aiRecruitUnitChoiceState = aiRecruitUnitChoicePreference; + if (aiRecruitUnitChoicePreference == 1) { + int _raidUnitCount = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::getCurrentDesiredAttackRaidUnitCount, this)(_aiTypeMin1, playerID); + int _totalDefTroops = DAT_GameState::instance.playerDataArray[playerID].totalRaidingTroopsUnk; + bVar3 = (_totalDefTroops < _raidUnitCount); + _subtraction = _totalDefTroops - _raidUnitCount; + _needsCheck = true; + } else if (aiRecruitUnitChoicePreference == 0) { + int _totalRaidTroops = DAT_GameState::instance.playerDataArray[playerID].totalDefensiveTroopsUnk; + int _defTotal = *(int*)((int)this + _aiTypeMin1 * 676 + 0x170); + bVar3 = (_totalRaidTroops < _defTotal); + _subtraction = _totalRaidTroops - _defTotal; + _needsCheck = true; + } + } else { + int _rng = (int)SEC_RNG::instance.currentNumber2; + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + + if ((int)DAT_GameCore::instance.mapTimeInTicks < 4800) { + _aiRecruitUnitChoiceState = OpenSHC::AI::AIRUC_DEFENSIVE; + int _totalRaidTroops = DAT_GameState::instance.playerDataArray[playerID].totalDefensiveTroopsUnk; + int _defTotal = *(int*)((int)this + _aiTypeMin1 * 676 + 0x170); + bVar3 = (_totalRaidTroops < _defTotal); + _subtraction = _totalRaidTroops - _defTotal; + _needsCheck = true; + } else if (_rng % _totalRecruitProb < _recruitProbDefensive) { + _aiRecruitUnitChoiceState = OpenSHC::AI::AIRUC_DEFENSIVE; + int _totalRaidTroops = DAT_GameState::instance.playerDataArray[playerID].totalDefensiveTroopsUnk; + int _defTotal = *(int*)((int)this + _aiTypeMin1 * 676 + 0x170); + bVar3 = (_totalRaidTroops < _defTotal); + _subtraction = _totalRaidTroops - _defTotal; + _needsCheck = true; + } else if (_rng % _totalRecruitProb < _recruitProbRaid + _recruitProbDefensive) { + _aiRecruitUnitChoiceState = OpenSHC::AI::AIRUC_RAIDING; + int _raidUnitCount = MACRO_CALL_MEMBER( + OpenSHC::AI::AICState_Func::getCurrentDesiredAttackRaidUnitCount, this)(_aiTypeMin1, playerID); + int _totalDefTroops = DAT_GameState::instance.playerDataArray[playerID].totalRaidingTroopsUnk; + bVar3 = (_totalDefTroops < _raidUnitCount); + _subtraction = _totalDefTroops - _raidUnitCount; + _needsCheck = true; + } + } + + if (!_needsCheck || !(bVar3 == (_subtraction < 0))) + _aiRecruitUnitChoiceState = OpenSHC::AI::AIRUC_ATTACKING; + + if (DAT_GameState::instance.playerDataArray[playerID].aiRecruitUnitChoiceState != _aiRecruitUnitChoiceState) + DAT_GameState::instance.playerDataArray[playerID].aiRecruitUnitChoiceState = _aiRecruitUnitChoiceState; + + if ((char)aiRecruitUnitChoicePreference == '\0') + return; + + if (0 < DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker) { + BOOLEnum _teamWeak = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::teamIsWeakRelativeToEnemy, this)(playerID, TRUE); + if (_teamWeak == FALSE) { + DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker = 0; + return; + } + int _currentGold_2 = DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf]; + DAT_GameState::instance.playerDataArray[playerID].aiStrengthFeeling = OpenSHC::AI::AIST_WEAK; + DAT_GameState::instance.playerDataArray[playerID].aiRecruitUnitChoiceState = OpenSHC::AI::AIRUC_DEFENSIVE; + + if (_currentGold_2 < 500) { + int _aiNervAct = DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker; + if (_aiNervAct < 8) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_FEAR_FACTOR_BUILDINGS); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_WORKSHOPS_AND_RELIGIOUS_BUILDINGS); + } else if (!(5 < _aiNervAct)) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_RESOURCE_BUILDINGS); + } + } else { + short* psVar2 = &DAT_GameState::instance.playerDataArray[playerID].aiStateCounter; + *psVar2 = *psVar2 + 1; + if ((2500 < _currentGold_2) + && ((DAT_GameState::instance.playerDataArray[playerID].aiStateCounter & 1U) != 0) + && (DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy < 20)) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::shareGoldAmongTeamMembers, this)( + playerID, _currentGold_2 / 10); + } + } + + int _aiNervAct2 = DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker; + if (_aiNervAct2 < 2) + return; + DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker = _aiNervAct2 + -1; + return; + } + + int _aiBuildDestroyTracker = DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker; + + if (_aiBuildDestroyTracker < 1) { + BOOLEnum _teamWeak2 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::teamIsWeakRelativeToEnemy, this)(playerID, FALSE); + if (_teamWeak2 != FALSE) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playNervous2BikFromPlayer, this)(playerID); + DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker = 8; + DAT_GameState::instance.playerDataArray[playerID].aiStateCounter = 0; + return; + } + BOOLEnum _outOfSupplies = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::hasNotEnoughSupplies, this)(playerID); + if (_outOfSupplies == FALSE) + return; + DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker = 8; + return; + } + + BOOLEnum _outOfSupplies2 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::hasNotEnoughSupplies, this)(playerID); + if (_outOfSupplies2 == FALSE) { + DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker = 0; + return; + } + + DAT_GameState::instance.playerDataArray[playerID].resourcesToAcquireArray[2] = 20; + DAT_GameState::instance.playerDataArray[playerID].aiStrengthFeeling = OpenSHC::AI::AIST_WEAK; + DAT_GameState::instance.playerDataArray[playerID].aiRecruitUnitChoiceState = OpenSHC::AI::AIRUC_DEFENSIVE; + + if (_aiBuildDestroyTracker == 8) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_FEAR_FACTOR_BUILDINGS); + } else if (_aiBuildDestroyTracker == 7) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_WORKSHOPS_AND_RELIGIOUS_BUILDINGS); + DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker--; + return; + } else if (_aiBuildDestroyTracker == 6) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_GATEHOUSES_TOWERS_GUILDS); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_BARRACKS_MERCPOST_STABLES); + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_WATER_BUILDINGS); + DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker--; + return; + } else if (_aiBuildDestroyTracker == 5) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_RESOURCE_BUILDINGS); + DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker--; + return; + } else if (_aiBuildDestroyTracker == 4) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::destroyAnyBuildingInCategory, this)( + playerID, OpenSHC::AI::AINDBC_FARMS); + } + + DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker--; + } +} +} diff --git a/src/OpenSHC/AI/AICState/updateTribeLocations.cpp b/src/OpenSHC/AI/AICState/updateTribeLocations.cpp new file mode 100644 index 00000000..d84f52f9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/updateTribeLocations.cpp @@ -0,0 +1,63 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" + +#include "OpenSHC/Globals/DAT_EntityState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4940 + void AICState ::updateTribeLocations(int playerID) + + { + + AITypeInt AVar2; + + AVar2 = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (AVar2 != OpenSHC::AI::AIT_NULL) { + + DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeWaitCounter = + DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeWaitCounter + 1; + + if (DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeWaitCounter + >= *(int*)((int)this + AVar2 * 0x2a4 - 0x194)) { + + DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeHits = + DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeHits + 1; + + DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeWaitCounter = 0; + } + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::ifHasTribe0SendUnitsToKeep, this)(playerID); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeAtOffset1ToCampfire, this)(playerID); + + if (DAT_GameState::instance.mapAndTime.monthTicks == playerID) { + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::digNearestMoat, this)(playerID); + } + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendRangedUnitTribesToAIVSlotsOrKeepIfNervous, this)( + playerID); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed, + this)(playerID); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendWallPatrolUnitTribesToAIVLocations, this)(playerID); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendOilMenForOilPickupOrToAIVLocations, this)(playerID); + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::aiGiveOuterPatrolCommand, this)(playerID); + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/useAITribe_0x12_toPlaceSiegeTentsAndAssignEngineers.cpp b/src/OpenSHC/AI/AICState/useAITribe_0x12_toPlaceSiegeTentsAndAssignEngineers.cpp new file mode 100644 index 00000000..f3e1e0af --- /dev/null +++ b/src/OpenSHC/AI/AICState/useAITribe_0x12_toPlaceSiegeTentsAndAssignEngineers.cpp @@ -0,0 +1,206 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/TroopValueState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/AI/Tribes/AITribeType.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::AITypeInt; + using OpenSHC::AI::Tribes::AITribeType; + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Map::Units::SomeTribeBehaviorType; + using OpenSHC::Map::Units::UnitInstructionType; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CEFD0 + void AICState ::useAITribe_0x12_toPlaceSiegeTentsAndAssignEngineers(int playerID) + + { + + AITypeInt AVar1; + + int iVar2; + + int _counterTill6; + + int _selectionID2; + + int iVar3; + + int* piVar4; + + MappersEnum _siegeEngineType; + + int iVar5; + + int _counterTill8; + + int _aiTribeID; + + int* _siegeEngineUnitType; + + int local_18; + + int _requiredEngineers; + + short _currentAttackWave; + + short _size; + + AVar1 = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (AVar1 != OpenSHC::AI::AIT_NULL) { + + _aiTribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0x12]; + + if ((_aiTribeID != 0) + && (DAT_TribesState::instance.tribes[_aiTribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0x12])) { + + _size = DAT_TribesState::instance.tribes[_aiTribeID].size; + + local_18 = (int)_size; + + iVar3 = 0; + + _siegeEngineUnitType = (int*)((int)this + (AVar1 + ~OpenSHC::AI::AIT_NULL) * 0x2a4 + 0x214); + + piVar4 = _siegeEngineUnitType; + + iVar5 = 0; + + do { + + if (*piVar4 < 1) + break; + + iVar2 = 0; + + do { + + if (*piVar4 == DAT_SkirmishDefinedData::instance.SiegeEngineUnitType[iVar2]) { + + _requiredEngineers + = DAT_SkirmishDefinedData::instance.SiegeEngineTypeRequiredEngineers[iVar2]; + + break; + } + + iVar2 = iVar2 + 1; + + } while (iVar2 < 6); + + iVar2 = _requiredEngineers + iVar5; + + if (local_18 < iVar2) + break; + + iVar3 = iVar3 + 1; + + piVar4 = piVar4 + 1; + + iVar5 = iVar2; + + } while (iVar3 < 8); + + if (0 < iVar5) { + + for (; (_size != 0 && (0 < iVar5)); iVar5 = iVar5 + -1) { + + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::popUnitFromTribe, DAT_TribesState::ptr)(_aiTribeID); + + _size = DAT_TribesState::instance.tribes[_aiTribeID].size; + } + + _counterTill8 = 0; + + /* + fixme: parameter reuse */ + + _siegeEngineType = (MappersEnum)playerID; + + do { + + if (*_siegeEngineUnitType < 1) { + + return; + } + + _counterTill6 = 0; + + do { + + if (*_siegeEngineUnitType + == DAT_SkirmishDefinedData::instance.SiegeEngineUnitType[_counterTill6]) { + + _requiredEngineers + = DAT_SkirmishDefinedData::instance.SiegeEngineTypeRequiredEngineers[_counterTill6]; + + _siegeEngineType = (MappersEnum)DAT_SkirmishDefinedData::instance + .SiegeEngineCommandBuildingTypes[_counterTill6]; + + break; + } + + _counterTill6 = _counterTill6 + 1; + + } while (_counterTill6 < 6); + + if (local_18 < _requiredEngineers) { + + return; + } + + local_18 = local_18 - _requiredEngineers; + + _selectionID2 = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addEngineersToSelection, this)( + playerID, _requiredEngineers); + + if (_selectionID2 == 0) { + + return; + } + + _currentAttackWave = (short)DAT_GameState::instance.playerDataArray[playerID].currentAttackWave; + + DAT_TribesState::instance.tribes[_selectionID2].tribeType = OpenSHC::AI::Tribes::AITT_ENGINEERS; + + DAT_TribesState::instance.tribes[_selectionID2].tribeBehaviorType + = OpenSHC::Map::Units::STBT_0x410_SIEGE_EQUIPMENT_CONSTRUCTION; + + DAT_TribesState::instance.tribes[_selectionID2].attackWave = _currentAttackWave; + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func:: + placeSiegeTentOrTunnelAtSuitableLocationAndAssignEngineers, + DAT_TroopValueState::ptr)( + _selectionID2, _siegeEngineType, (uint)((int)(60)), (UnitInstructionType)((int)(15))); + + _counterTill8 = _counterTill8 + 1; + + _siegeEngineUnitType = _siegeEngineUnitType + 1; + + } while (_counterTill8 < 8); + } + } + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/useAITribe_0xe_toPlaceTunnels.cpp b/src/OpenSHC/AI/AICState/useAITribe_0xe_toPlaceTunnels.cpp new file mode 100644 index 00000000..3f47b327 --- /dev/null +++ b/src/OpenSHC/AI/AICState/useAITribe_0xe_toPlaceTunnels.cpp @@ -0,0 +1,80 @@ +#include "OpenSHC/AI/AICState.func.hpp" +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/Map/Units/TroopValueState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/Tribes/AITribeType.hpp" +#include "OpenSHC/Commands/MappersEnum.hpp" +#include "OpenSHC/Map/Units/SomeTribeBehaviorType.hpp" +#include "OpenSHC/Map/Units/UnitInstructionType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIType; + using OpenSHC::AI::Tribes::AITribeType; + using OpenSHC::Commands::MappersEnum; + using OpenSHC::Map::Units::SomeTribeBehaviorType; + using OpenSHC::Map::Units::UnitInstructionType; + + /* + decompilerscript: committed: 2025-01-30 21:57:43.216000 */ + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF180 + void AICState ::useAITribe_0xe_toPlaceTunnels(int playerID) + + { + + int _aiTribeID; + + uint _unitID; + + int _tribeID; + + int _countdown; + + short _size; + + if (((DAT_GameState::instance.playerDataArray[playerID].aiType != OpenSHC::AI::AIT_NULL) + && (_aiTribeID = (int)DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xe], + _aiTribeID != 0)) + && (DAT_TribesState::instance.tribes[_aiTribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xe])) { + + _size = DAT_TribesState::instance.tribes[_aiTribeID].size; + + for (_countdown = (int)_size; (_size != 0 && (0 < _countdown)); _countdown = _countdown + -1) { + + _unitID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::popUnitFromTribe, DAT_TribesState::ptr)(_aiTribeID); + + _tribeID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + + DAT_TribesState::instance.tribes[_tribeID].attackWave + = (short)DAT_GameState::instance.playerDataArray[playerID].currentAttackWave; + + DAT_TribesState::instance.tribes[_tribeID].tribeType = OpenSHC::AI::Tribes::AITT_TUNNELERS; + + DAT_TribesState::instance.tribes[_tribeID].tribeBehaviorType = OpenSHC::Map::Units::STBT_0x415; + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)( + _unitID, _tribeID); + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TroopValueState_Func:: + placeSiegeTentOrTunnelAtSuitableLocationAndAssignEngineers, + DAT_TroopValueState::ptr)(_tribeID, OpenSHC::Commands::M_MAPPER_TUNNEL_CONSTRUCTION, + (uint)((int)(50)), (UnitInstructionType)((int)(21))); + + _size = DAT_TribesState::instance.tribes[_aiTribeID].size; + } + } + + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/wipeAICMemory.cpp b/src/OpenSHC/AI/AICState/wipeAICMemory.cpp new file mode 100644 index 00000000..ed4a0203 --- /dev/null +++ b/src/OpenSHC/AI/AICState/wipeAICMemory.cpp @@ -0,0 +1,18 @@ +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/AI/AICState.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004C6D30 + void AICState::wipeAICMemory() + { + for (int i = 0; i < 20; i++) { + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 676, '\0', &this->aics[i]); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/yieldCowThrow.cpp b/src/OpenSHC/AI/AICState/yieldCowThrow.cpp new file mode 100644 index 00000000..16c993e2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/yieldCowThrow.cpp @@ -0,0 +1,27 @@ +#include "OpenSHC/AI/AICState.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CFF60 + BOOLEnum AICState::yieldCowThrow(int playerID) + { + int* _pCounter; + int const _ai = DAT_GameState::instance.playerDataArray[playerID].aiType; + if ((((_ai != AITA_NULL) && (DAT_GameState::instance.mapAndTime.noCowThrowing == 0)) + && (this->aics[_ai - 1].CowThrowInterval != 0)) + && ((0 < DAT_GameState::instance.playerDataArray[playerID].counter + && (_pCounter = &DAT_GameState::instance.playerDataArray[playerID].aiCowThrowCounter, + *_pCounter = *_pCounter + 1, + (int)this->aics[_ai - 1].CowThrowInterval + < DAT_GameState::instance.playerDataArray[playerID].aiCowThrowCounter)))) { + DAT_GameState::instance.playerDataArray[playerID].aiCowThrowCounter = 0; + return TRUE; + } + return FALSE; + } +} +}