diff --git a/src/OpenSHC/AI/AICState/Constructor_AICState.cpp b/src/OpenSHC/AI/AICState/Constructor_AICState.cpp new file mode 100644 index 000000000..2eedf70e1 --- /dev/null +++ b/src/OpenSHC/AI/AICState/Constructor_AICState.cpp @@ -0,0 +1,14 @@ +#include "../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 000000000..bdd9f7312 --- /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 000000000..2cd0c7498 --- /dev/null +++ b/src/OpenSHC/AI/AICState/addBuildingToTargetableBuildings.cpp @@ -0,0 +1,30 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Game/GameMode.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 const owner = DAT_BuildingsState::instance.buildings[buildingID].owner; + if (DAT_GameSynchronyState::instance.currentGameMode == Game::GM_SOLITARY) + return; + if (DAT_GameState::instance.playerDataArray[owner].top100TargetableBuildingsTracker >= 100) + return; + if (DAT_BuildingsState::instance.buildings[buildingID].fireDuration != 0) + return; + + DAT_GameState::instance.playerDataArray[owner] + .top100TargetableBuildings[DAT_GameState::instance.playerDataArray[owner].top100TargetableBuildingsTracker] + = buildingID; + DAT_GameState::instance.playerDataArray[owner].top100TargetableBuildingsTracker++; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/addEngineersToSelection.cpp b/src/OpenSHC/AI/AICState/addEngineersToSelection.cpp new file mode 100644 index 000000000..b84a31b9d --- /dev/null +++ b/src/OpenSHC/AI/AICState/addEngineersToSelection.cpp @@ -0,0 +1,44 @@ +#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/Map/Units/UnitType.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 const tribeID + = MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + for (int unitID = 1; unitID < (int)DAT_UnitsState::instance.maxUnitCount; unitID++) { + if (unitCount <= 0) + break; + if (DAT_UnitsState::instance.units[unitID].logicalState != Map::Units::ULS_NORMAL) + continue; + if (DAT_UnitsState::instance.units[unitID].owner != playerID) + continue; + if (DAT_UnitsState::instance.units[unitID].dying != 0) + continue; + if (DAT_UnitsState::instance.units[unitID].unitType != Map::Units::UT_E_ENGINEER) + continue; + if (DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType != 10) + continue; + if (DAT_UnitsState::instance.units[unitID].tribeID != 0) + continue; + if (DAT_UnitsState::instance.units[unitID].state.generic == Map::Units::States::US_STAND_UPUnk) + continue; + + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, tribeID); + unitCount--; + } + return tribeID; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/addUnitToSmallestPatrolTribe.cpp b/src/OpenSHC/AI/AICState/addUnitToSmallestPatrolTribe.cpp new file mode 100644 index 000000000..5df7b0027 --- /dev/null +++ b/src/OpenSHC/AI/AICState/addUnitToSmallestPatrolTribe.cpp @@ -0,0 +1,27 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2730 + void AICState::addUnitToSmallestPatrolTribe(int unitID) + { + int const owner = DAT_UnitsState::instance.units[unitID].owner; + if (DAT_GameState::instance.playerDataArray[owner].aiType == AITA_NULL) + return; + int const aicIndex = DAT_GameState::instance.playerDataArray[owner].aiType - 1; + + DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = 4; + int const tribeID = MACRO_CALL_MEMBER(AICState_Func::getSmallestPatrolTribe, this)( + owner, this->aics[aicIndex].OuterPatrolGroupsCount); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, tribeID); + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiChoiceAttForceRallyPercentage.cpp b/src/OpenSHC/AI/AICState/aiChoiceAttForceRallyPercentage.cpp new file mode 100644 index 000000000..8b592dd2f --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiChoiceAttForceRallyPercentage.cpp @@ -0,0 +1,45 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CEC80 + BOOLEnum AICState::aiChoiceAttForceRallyPercentage(int playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) + return FALSE; + int const aicIndex = aiType - 1; + + int activeTribes = 0; + int rallyingTribes = 0; + for (int i = 0; i < 11; i++) { + int const tribeType = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeType; + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeCount; + for (int j = 0; j < tribeCount; j++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeType + j]; + if (tribeID == 0) + continue; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[j + tribeType]) + continue; + + activeTribes++; + if (DAT_TribesState::instance.tribes[tribeID].percentageMovingUnk < 20) + rallyingTribes++; + } + } + + if (activeTribes <= 0) + return FALSE; + return rallyingTribes * 100 / activeTribes >= this->aics[aicIndex].AttForceRallyPercentage; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiCommandSiegeEngineTribes.cpp b/src/OpenSHC/AI/AICState/aiCommandSiegeEngineTribes.cpp new file mode 100644 index 000000000..5bc2a4b90 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCommandSiegeEngineTribes.cpp @@ -0,0 +1,102 @@ +#include "../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/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.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 namespace OpenSHC::Map::Units; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CFCD0 + void AICState::aiCommandSiegeEngineTribes(int playerID) + { + int shieldTargetTribeIDs[3]; + shieldTargetTribeIDs[0] = 0; + shieldTargetTribeIDs[1] = 0; + shieldTargetTribeIDs[2] = 0; + int targetCount = 0; + for (int i = 186; i < 189; i++) { + int tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[i]; + if (tribeID == 0) + continue; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[i]) + continue; + shieldTargetTribeIDs[targetCount] = tribeID; + targetCount++; + } + + int shieldCount = 0; + for (int unitID = 1; unitID < 2500; unitID++) { + if (DAT_UnitsState::instance.units[unitID].logicalState == ULS_INVISIBLE) + continue; + if (DAT_UnitsState::instance.units[unitID].unitType != UT_S_TOWER + && DAT_UnitsState::instance.units[unitID].unitType != UT_S_BATTERINGRAM + && DAT_UnitsState::instance.units[unitID].unitType != UT_S_SHIELD) + continue; + if (DAT_UnitsState::instance.units[unitID].unknownSiegeTentRelated02 != 3) + continue; + if (DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType == 21) + continue; + + if (DAT_UnitsState::instance.units[unitID].unitType == UT_S_TOWER) { + int tribeID = DAT_UnitsState::instance.units[unitID].tribeID; + if (tribeID == 0) + continue; + if (DAT_TroopValueState::instance.attackInfo.playerInfo[playerID - 1].wide3 != 0) + MACRO_CALL_MEMBER(TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, STBT_0x414); + } else if (DAT_UnitsState::instance.units[unitID].unitType == UT_S_BATTERINGRAM) { + int tribeID = DAT_UnitsState::instance.units[unitID].tribeID; + if (tribeID == 0) + continue; + if (DAT_TroopValueState::instance.attackInfo.playerInfo[playerID - 1].gate2 != 0) + MACRO_CALL_MEMBER(TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, STBT_0x3f6); + else if (DAT_TroopValueState::instance.attackInfo.playerInfo[playerID - 1].wide3 != 0) + MACRO_CALL_MEMBER(TribesState_Func::assignAttackTargetsForTribe, DAT_TribesState::ptr)( + tribeID, STBT_0x413); + } else if (DAT_UnitsState::instance.units[unitID].unitType == UT_S_SHIELD) { + int tribeID = DAT_UnitsState::instance.units[unitID].tribeID; + if (tribeID == 0) + continue; + if (shieldCount >= 3) + continue; + + int const targetTribeID = shieldTargetTribeIDs[shieldCount]; + DAT_TribesState::instance.tribes[tribeID].supportTribeID = targetTribeID; + DAT_TribesState::instance.tribes[tribeID].uid2 = DAT_TribesState::instance.tribes[targetTribeID].uid; + DAT_TribesState::instance.tribes[targetTribeID].tribeID = tribeID; + int const targetUnitID = DAT_TribesState::instance.tribes[targetTribeID].selectionTargetUnitID; + int const tile = DAT_UnitsState::instance.units[targetUnitID].tile; + shieldCount++; + if (tile <= 0) + continue; + + int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + MACRO_CALL_MEMBER(TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)( + tribeID, x, y, 0, 0, Instructions::UMSE_0); + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::calculatePreferredRelativeOrientation, + DAT_DirectionAlgorithmState::ptr)(DAT_UnitsState::instance.units[targetUnitID].x, + DAT_UnitsState::instance.units[targetUnitID].y, x, y); + DAT_TribesState::instance.tribes[tribeID].orientation + = 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 000000000..423f1b41f --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCommandTribe11StandGroundToGate.cpp @@ -0,0 +1,37 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TroopValueState.func.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) + { + int tribeIndex = 11; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) + return; + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex]; + if (tribeID == 0) + return; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex]) + return; + if (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID == 0) + return; + + DAT_TribesState::instance.tribes[tribeID].unitStance = Map::Units::Behavior::USE_STAND_GROUND; + if (MACRO_CALL_MEMBER(Map::Units::TroopValueState_Func::moveTribeToNearbyGatehouse, DAT_TroopValueState::ptr)( + tribeID) + == 0) + MACRO_CALL_MEMBER(Map::Units::TroopValueState_Func::moveTowardsParticularUnits, DAT_TroopValueState::ptr)( + tribeID); + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiCommandTribe13DefensiveWave.cpp b/src/OpenSHC/AI/AICState/aiCommandTribe13DefensiveWave.cpp new file mode 100644 index 000000000..19c90fa5c --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiCommandTribe13DefensiveWave.cpp @@ -0,0 +1,34 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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) + { + int tribeIndex = 13; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) + return; + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex]; + if (tribeID == 0) + return; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex]) + 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/aiFarmChoice.cpp b/src/OpenSHC/AI/AICState/aiFarmChoice.cpp new file mode 100644 index 000000000..93a7ec91b --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiFarmChoice.cpp @@ -0,0 +1,19 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB180 + int AICState::aiFarmChoice(int playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) + return 0; + return (&this->aics[aiType - 1].farm1)[DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex]; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiOfTypeInCurrentGame.cpp b/src/OpenSHC/AI/AICState/aiOfTypeInCurrentGame.cpp new file mode 100644 index 000000000..835f96773 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiOfTypeInCurrentGame.cpp @@ -0,0 +1,25 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0580 + BOOLEnum AICState::aiOfTypeInCurrentGame(int aicIndex) + { + for (int playerID = 1; playerID < 9; playerID++) { + if (DAT_GameSynchronyState::instance.currentAIArray[playerID] == 0) + continue; + if (DAT_GameState::instance.playerDataArray[playerID].lordKilledByPlayerID != 0) + continue; + if (aicIndex != DAT_GameState::instance.playerDataArray[playerID].aiType - 1) + continue; + return TRUE; + } + return FALSE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiRecruitEngineers.cpp b/src/OpenSHC/AI/AICState/aiRecruitEngineers.cpp new file mode 100644 index 000000000..0b73e843c --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRecruitEngineers.cpp @@ -0,0 +1,40 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.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 const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) + return; + if (DAT_GameState::instance.playerDataArray[playerID].isEngineerRequired == FALSE) + return; + if (DAT_GameState::instance.playerDataArray[playerID].canStartSpending == 0) + return; + if (this->aics[aiType - 1].defSiegeEngineGoldThreshold + + DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] + < 30) + return; + int const guildID = DAT_GameState::instance.playerDataArray[playerID].engineersGuild.id; + if (guildID <= 0) + return; + if (DAT_BuildingsState::instance.buildings[guildID].buildingType != Map::Buildings::BT_ENGINEERSGUILD) + return; + + MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::nonEuroRecruit, DAT_UnitsState::ptr)( + Map::Units::UT_E_ENGINEER, guildID, playerID, 0); + DAT_GameState::instance.playerDataArray[playerID].isEngineerRequired = FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/aiRequiresExtraOxtethers.cpp b/src/OpenSHC/AI/AICState/aiRequiresExtraOxtethers.cpp new file mode 100644 index 000000000..6c231cc6f --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiRequiresExtraOxtethers.cpp @@ -0,0 +1,67 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB3A0 + BOOLEnum AICState::aiRequiresExtraOxtethers(int playerID) + { + int quarryID + = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findFirstBuildingIDForPlayerAndType, + DAT_BuildingsState::ptr)(playerID, OpenSHC::Map::Buildings::BT_QUARRY); + int highestQuarryID = 0; + int highestStonePerTether = 0; + if (MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::countBuildingsForPlayer, + DAT_BuildingsState::ptr)(playerID, OpenSHC::Map::Buildings::BT_OXTETHER, 1) + >= 10) + return FALSE; + + DAT_GameState::instance.playerDataArray[playerID].highestLoadedQuarryUnk = 0; + for (; quarryID != 0; + quarryID = MACRO_CALL_MEMBER(OpenSHC::Map::Buildings::BuildingsState_Func::findNextBuildingForPlayerAndType, + DAT_BuildingsState::ptr)(playerID, OpenSHC::Map::Buildings::BT_QUARRY, quarryID)) { + // The (short) is load-bearing: quarryStockpileID is undefined2, and the original sign-extends it. + int const stone = DAT_BuildingsState::instance + .buildings[(short)DAT_BuildingsState::instance.buildings[quarryID].quarryStockpileID] + .resources[Game::Resources::RT_STONE]; + int tetherCount = 0; + for (int i = 0; i < 3; i++) { + int const tetherID = DAT_BuildingsState::instance.buildings[quarryID].quarryLinkedOxTethers[i]; + if (tetherID == 0) + continue; + if (DAT_BuildingsState::instance.buildings[tetherID].buildingType + == OpenSHC::Map::Buildings::BT_OXTETHER + && DAT_BuildingsState::instance.buildings[tetherID].oxtetherLinkedQuarryID == quarryID) + tetherCount++; + else + DAT_BuildingsState::instance.buildings[quarryID].quarryLinkedOxTethers[i] = 0; + } + + if (tetherCount <= 0) { + DAT_GameState::instance.playerDataArray[playerID].highestLoadedQuarryUnk = quarryID; + return TRUE; + } + if (stone / tetherCount > highestStonePerTether) { + highestQuarryID = quarryID; + highestStonePerTether = stone / tetherCount; + } + } + + if (highestStonePerTether > 20) { + DAT_GameState::instance.playerDataArray[playerID].highestLoadedQuarryUnk = highestQuarryID; + return TRUE; + } + return FALSE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/aiUpdateTaxesAndRations.cpp b/src/OpenSHC/AI/AICState/aiUpdateTaxesAndRations.cpp new file mode 100644 index 000000000..26024f866 --- /dev/null +++ b/src/OpenSHC/AI/AICState/aiUpdateTaxesAndRations.cpp @@ -0,0 +1,91 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CAEA0 + void AICState::aiUpdateTaxesAndRations(int playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) + return; + int const aicIndex = aiType - 1; + + int const popularity = DAT_GameState::instance.playerDataArray[playerID].popularity; + if (popularity <= this->aics[aicIndex].criticalPopularity) + DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue = 2; + else if (popularity <= this->aics[aicIndex].lowestPopularity) + DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue = 1; + else if (popularity >= this->aics[aicIndex].highestPopularity) + DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue = 0; + + if (DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue == 2) { + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = 2; + if (DAT_GameState::instance.playerDataArray[playerID].totalFood <= 0) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 0; + else if (DAT_GameState::instance.playerDataArray[playerID].totalFood <= 4) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 2; + else if (DAT_GameState::instance.playerDataArray[playerID].totalFood <= 8) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 3; + else + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 4; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue == 1) { + if (DAT_GameState::instance.playerDataArray[playerID].taxesSetting > this->aics[aicIndex].taxesMin) + DAT_GameState::instance.playerDataArray[playerID].taxesSetting--; + else + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = this->aics[aicIndex].taxesMin; + + if (DAT_GameState::instance.playerDataArray[playerID].totalFood <= 0) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 0; + else if (DAT_GameState::instance.playerDataArray[playerID].totalFood <= 10) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 2; + else + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 3; + if (this->aics[aicIndex].doubleRationsFoodThreshold != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalFood + >= this->aics[aicIndex].doubleRationsFoodThreshold) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 4; + } else if (DAT_GameState::instance.playerDataArray[playerID].aiPopularityDecisionValue == 0) { + if (DAT_GameState::instance.playerDataArray[playerID].taxesSetting < this->aics[aicIndex].taxesMax) { + int lowerTaxesLimit; + if (DAT_GameState::instance.playerDataArray[playerID].previousAvailablePeasants >= 15) + lowerTaxesLimit = 6; + else if (DAT_GameState::instance.playerDataArray[playerID].previousAvailablePeasants >= 10) + lowerTaxesLimit = 5; + else + lowerTaxesLimit = 4; + + if (DAT_GameState::instance.playerDataArray[playerID].storedPopularityPercent <= popularity) + DAT_GameState::instance.playerDataArray[playerID].taxesSetting++; + else if (DAT_GameState::instance.playerDataArray[playerID].taxesSetting > lowerTaxesLimit) + DAT_GameState::instance.playerDataArray[playerID].taxesSetting--; + } else { + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = this->aics[aicIndex].taxesMax; + } + + if (DAT_GameState::instance.playerDataArray[playerID].totalFood <= 10) { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 0; + } else { + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 2; + if (this->aics[aicIndex].doubleRationsFoodThreshold != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalFood + >= this->aics[aicIndex].doubleRationsFoodThreshold) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 4; + } + } + + if (DAT_GameState::instance.playerDataArray[playerID].taxesSetting < 0) + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = 0; + if (DAT_GameState::instance.playerDataArray[playerID].taxesSetting > 11) + DAT_GameState::instance.playerDataArray[playerID].taxesSetting = 11; + if (DAT_GameState::instance.playerDataArray[playerID].rationsSetting < 0) + DAT_GameState::instance.playerDataArray[playerID].rationsSetting = 0; + if (DAT_GameState::instance.playerDataArray[playerID].rationsSetting > 4) + 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 000000000..617ccfeaa --- /dev/null +++ b/src/OpenSHC/AI/AICState/assignRequiredIdleEngineersToNewTribe.cpp @@ -0,0 +1,50 @@ +#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/Map/Units/UnitType.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) + { + int const tribeID + = MACRO_CALL_MEMBER(Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + for (int unitID = 1; unitID < DAT_UnitsState::instance.maxUnitCount; unitID++) { + if (count <= 0) + break; + if (DAT_UnitsState::instance.units[unitID].logicalState != Map::Units::ULS_NORMAL) + continue; + if (DAT_UnitsState::instance.units[unitID].owner != playerID) + continue; + if (DAT_UnitsState::instance.units[unitID].dying) + continue; + if (DAT_UnitsState::instance.units[unitID].unitType != Map::Units::UT_E_ENGINEER) + continue; + if (DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType == 22) + continue; + if (DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType == 10) + continue; + if (DAT_UnitsState::instance.units[unitID].engineerRelatedUnk != 0) + continue; + if (DAT_UnitsState::instance.units[unitID].state.generic != 105 + && DAT_UnitsState::instance.units[unitID].state.generic != 2 + && DAT_UnitsState::instance.units[unitID].state.generic != Map::Units::States::US_IDLEUnk) + continue; + + if (DAT_UnitsState::instance.units[unitID].tribeID != 0) + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::removeUnitFromTribe, DAT_TribesState::ptr)( + unitID, DAT_UnitsState::instance.units[unitID].tribeID); + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, tribeID); + count--; + } + return tribeID; + } +} +} diff --git a/src/OpenSHC/AI/AICState/assignUnitToATribe.cpp b/src/OpenSHC/AI/AICState/assignUnitToATribe.cpp new file mode 100644 index 000000000..252668ad7 --- /dev/null +++ b/src/OpenSHC/AI/AICState/assignUnitToATribe.cpp @@ -0,0 +1,41 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.hpp" +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_TribesState.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2660 + void AICState::assignUnitToATribe(int unitID) + { + int const owner = DAT_UnitsState::instance.units[unitID].owner; + if (DAT_GameState::instance.playerDataArray[owner].aiType == AITA_NULL) + return; + + AIVUnitType aivUnitType; + if (DAT_GameState::instance.playerDataArray[owner].aivCurrentPauseIndex == 1 + && DAT_GameState::instance.playerDataArray[owner] + .aivPauses[DAT_GameState::instance.playerDataArray[owner].aivCurrentPauseIndex] + > 0) + aivUnitType = MACRO_CALL_MEMBER(AICState_Func::getUnitTypeIndexForUnitID, this)(unitID, 1); + else + aivUnitType = MACRO_CALL_MEMBER(AICState_Func::getUnitTypeIndexForUnitID, this)(unitID, 0); + DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType = 1; + int tribeID; + if (DAT_GameState::instance.playerDataArray[owner].aivUnitLocationSlotLocationCount[aivUnitType] <= 0) { + if (MACRO_CALL_MEMBER(AICState_Func::unitIDIsRangedOrArmored, this)(unitID) != 0) + tribeID = MACRO_CALL_MEMBER(AICState_Func::createTribeForUnitType, this)(owner, 0); + else + tribeID = MACRO_CALL_MEMBER(AICState_Func::createTribeForUnitType, this)(owner, 1); + } else { + tribeID = MACRO_CALL_MEMBER(AICState_Func::smallestTribeOfUnitType, this)(owner, aivUnitType); + } + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::addUnitToTribe, DAT_TribesState::ptr)(unitID, tribeID); + } +} +} diff --git a/src/OpenSHC/AI/AICState/checkTribeActivityPercentages.cpp b/src/OpenSHC/AI/AICState/checkTribeActivityPercentages.cpp new file mode 100644 index 000000000..8bf0b8883 --- /dev/null +++ b/src/OpenSHC/AI/AICState/checkTribeActivityPercentages.cpp @@ -0,0 +1,25 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD070 + BOOLEnum AICState::checkTribeActivityPercentages(int tribeID, BOOLEnum ignoreShooting, BOOLEnum includeMoving) + { + if (includeMoving != FALSE && DAT_TribesState::instance.tribes[tribeID].percentageMovingUnk > 10) { + return TRUE; + } + if (DAT_TribesState::instance.tribes[tribeID].percentageShootingUnk > 10 && ignoreShooting != TRUE) { + return TRUE; + } + return DAT_TribesState::instance.tribes[tribeID].percentageAttackingUnk > 10; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/checksAndGenerateAITribesForPlayerIfNotExisting.cpp b/src/OpenSHC/AI/AICState/checksAndGenerateAITribesForPlayerIfNotExisting.cpp new file mode 100644 index 000000000..3d2776878 --- /dev/null +++ b/src/OpenSHC/AI/AICState/checksAndGenerateAITribesForPlayerIfNotExisting.cpp @@ -0,0 +1,43 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CCFB0 + int AICState::checksAndGenerateAITribesForPlayerIfNotExisting(int playerID, int maxAmount, BOOLEnum checkOnly) + { + int offset = 0; + int const baseOffset = DAT_SkirmishDefinedData::instance.AITribeIDOffsetForAIVUnitType[1]; + for (; offset < maxAmount; offset++) { + if (offset >= 10) { + return 0; + } + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[baseOffset + offset]; + if (tribeID == 0 + || DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[baseOffset + offset]) { + if (checkOnly) { + return 1; + } + int const newTribeID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[baseOffset + offset] = (short)newTribeID; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[baseOffset + offset] + = DAT_TribesState::instance.tribes[newTribeID].uid; + return newTribeID; + } + } + return 0; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/clearOutdatedAITribes.cpp b/src/OpenSHC/AI/AICState/clearOutdatedAITribes.cpp new file mode 100644 index 000000000..9559e083f --- /dev/null +++ b/src/OpenSHC/AI/AICState/clearOutdatedAITribes.cpp @@ -0,0 +1,39 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF870 + void AICState::clearOutdatedAITribes(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return; + + for (int i = 0; i < 11; i++) { + if (i == 8) + continue; + + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxBreachTribes[i].tribeCount; + int const tribeType = DAT_SkirmishDefinedData::instance.MaxBreachTribes[i].tribeType; + for (int j = 0; j < tribeCount; j++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeType + j]; + if (tribeID == 0) + continue; + if (DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[j + tribeType]) + continue; + + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeType + j] = 0; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[j + tribeType] = 0; + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/computeNervousness.cpp b/src/OpenSHC/AI/AICState/computeNervousness.cpp new file mode 100644 index 000000000..af63f65c9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/computeNervousness.cpp @@ -0,0 +1,51 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/WindowsHelper/Enums/BOOLEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TroopValueState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3780 + void AICState::computeNervousness(int playerID) + { + int const troopValueNearKeep + = DAT_TroopValueState::instance.attackInfo.playerTotalTroopValueOfTroopsNearKeep[playerID]; + int const enemyTroopValue = DAT_GameState::instance.playerDataArray[playerID].totalEnemyTroopValue; + + BOOLEnum isNotNervous = TRUE; + int margin; + if (enemyTroopValue < 50) + margin = -20; + else if (enemyTroopValue < 100) + margin = 0; + else if (enemyTroopValue < 200) + margin = 20; + else if (enemyTroopValue < 400) + margin = 100; + else if (enemyTroopValue < 600) + margin = 200; + else if (enemyTroopValue < 800) + margin = 300; + else + margin = 500; + + if (troopValueNearKeep > enemyTroopValue + margin) { + isNotNervous = FALSE; + } else if (!DAT_GameState::instance.playerDataArray[playerID].isNotNervousByEnemyTroopValue) { + if (DAT_GameState::instance.playerDataArray[playerID] + .totalEnemyTroopValueByPlayerID[DAT_GameSynchronyState::instance.currentPlayerSlotID] + > troopValueNearKeep) + 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 = isNotNervous; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/countSiegeEngineUnits.cpp b/src/OpenSHC/AI/AICState/countSiegeEngineUnits.cpp new file mode 100644 index 000000000..4cad5f5ed --- /dev/null +++ b/src/OpenSHC/AI/AICState/countSiegeEngineUnits.cpp @@ -0,0 +1,36 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitLogicState.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CFBC0 + int AICState::countSiegeEngineUnits(int playerID) + { + int count = 0; + for (int unitID = 1; unitID < 2500; unitID++) { + if (DAT_UnitsState::instance.units[unitID].logicalState == OpenSHC::Map::Units::ULS_INVISIBLE) + continue; + if (DAT_UnitsState::instance.units[unitID].owner != playerID) + continue; + if (DAT_UnitsState::instance.units[unitID].unknownSiegeTentRelated02 != 3) + continue; + if (DAT_UnitsState::instance.units[unitID].aiUnitBehaviourType == 21) + continue; + + if (DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_S_CATAPULT + || DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_S_TREBUCHET + || DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_S_TOWER + || DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_S_BATTERINGRAM + || DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_S_FBALLISTA) + count++; + } + return count; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/destroyAnyBuildingInCategory.cpp b/src/OpenSHC/AI/AICState/destroyAnyBuildingInCategory.cpp new file mode 100644 index 000000000..644190c5f --- /dev/null +++ b/src/OpenSHC/AI/AICState/destroyAnyBuildingInCategory.cpp @@ -0,0 +1,98 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingLogicalState.hpp" +#include "OpenSHC/Map/Buildings/BuildingType.hpp" + +#include "OpenSHC/Globals/DAT_BuildingsState.hpp" + +namespace OpenSHC { +namespace AI { + + using namespace OpenSHC::Map::Buildings; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0280 + void AICState::destroyAnyBuildingInCategory(int playerID, AINervousDestroyBuildingCategory buildingCategory) + { + for (int buildingID = 1; buildingID < DAT_BuildingsState::instance.maxBuildingsCount; buildingID++) { + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == 0) + continue; + if (DAT_BuildingsState::instance.buildings[buildingID].logicalState == BLS_REMOVE) + continue; + if (DAT_BuildingsState::instance.buildings[buildingID].owner != playerID) + continue; + + bool matches = true; + if (buildingCategory == AINDBC_FEAR_FACTOR_BUILDINGS) + matches = DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_CESSPIT + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_BURNINGSTAKE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_GIBBET + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_DUNGEON + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_STRETCHINGRACK + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_RACKFLOGGING + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_CHOPPINGBLOCK + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_DUNKINGSTOOL + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_DOGCAGE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_STATUE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_SHRINE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_DANCINGBEAR + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_POND + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_GALLOWS + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_STOCKS + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_WITCHHOIST + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_MAYPOLE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_GARDEN; + else if (buildingCategory == AINDBC_WORKSHOPS_AND_RELIGIOUS_BUILDINGS) + matches = DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_FLETCHER + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_BLACKSMITH + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_POLETURNER + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_ARMOURER + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_TANNER + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_INN + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_APOTHECARY + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_CHAPEL + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_CHURCH + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_CATHEDRAL; + else if (buildingCategory == AINDBC_GATEHOUSES_TOWERS_GUILDS) + matches = DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_GATEHOUSELARGE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_GATEHOUSESMALL + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_DRAWBRIDGE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_TOWER1 + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_TOWER2 + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_TOWER3 + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_TOWER4 + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_TOWER5 + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_ENGINEERSGUILD + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_TUNNELERSGUILD + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_OILSMELTER; + else if (buildingCategory == AINDBC_BARRACKS_MERCPOST_STABLES) + matches = DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_MERCENARYPOST + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_BARRACKS + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_STABLES; + else if (buildingCategory == AINDBC_WATER_BUILDINGS) + matches = DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_WELL + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_WATERPOT; + else if (buildingCategory == AINDBC_RESOURCE_BUILDINGS) + matches = DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_OXTETHER + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_IRONMINE + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_PITCHRIG + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_BAKERY + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_BREWERY + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_QUARRY + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_MILL; + else if (buildingCategory == AINDBC_FARMS) + matches = DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_WHEATFARM + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_HOPFARM + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_APPLEFARM + || DAT_BuildingsState::instance.buildings[buildingID].buildingType == BT_DAIRYFARM; + + if (!matches) + continue; + + MACRO_CALL_MEMBER(BuildingsState_Func::giveBackResourceForDestroyedBuilding, DAT_BuildingsState::ptr)( + buildingID, playerID, 50); + MACRO_CALL_MEMBER(BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(buildingID); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/destroyBuildingIfNoWorker.cpp b/src/OpenSHC/AI/AICState/destroyBuildingIfNoWorker.cpp new file mode 100644 index 000000000..9ffa6b35f --- /dev/null +++ b/src/OpenSHC/AI/AICState/destroyBuildingIfNoWorker.cpp @@ -0,0 +1,49 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIVState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/Game/GameMode.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_GameSynchronyState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB940 + BOOLEnum AICState::destroyBuildingIfNoWorker(int buildingID) + { + short const countdown = DAT_BuildingsState::instance.buildings[buildingID].unknownTickRelatedValue; + int const owner = DAT_BuildingsState::instance.buildings[buildingID].owner; + if (countdown <= 0) + return FALSE; + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[owner] != -1) + return FALSE; + if (DAT_GameSynchronyState::instance.currentAIArray[owner] == 0) + return FALSE; + if (DAT_GameSynchronyState::instance.currentGameMode == Game::GM_SOLITARY) + return FALSE; + + if (DAT_BuildingsState::instance.buildings[buildingID].workers[0] != 0) { + if (DAT_GameState::instance.mapAndTime.monthTicks == owner) + DAT_BuildingsState::instance.buildings[buildingID].unknownTickRelatedValue = countdown - 1; + return FALSE; + } + if (countdown < 20) + return FALSE; + + MACRO_CALL_MEMBER(AIVState_Func::resetCountdownInHeatMap, DAT_AIVState::ptr)( + (short)DAT_BuildingsState::instance.buildings[buildingID].x, + (short)DAT_BuildingsState::instance.buildings[buildingID].y); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::giveBackResourceForDestroyedBuilding, + DAT_BuildingsState::ptr)(buildingID, owner, 50); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)(buildingID); + return TRUE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/determineAIPlayerAttackRequestResponse.cpp b/src/OpenSHC/AI/AICState/determineAIPlayerAttackRequestResponse.cpp new file mode 100644 index 000000000..1875d0607 --- /dev/null +++ b/src/OpenSHC/AI/AICState/determineAIPlayerAttackRequestResponse.cpp @@ -0,0 +1,37 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D38C0 + BOOLEnum AICState::determineAIPlayerAttackRequestResponse( + int playerID, undefined4 targetPlayerID, int requestedByPlayerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return FALSE; + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + + if (DAT_GameState::instance.playerDataArray[playerID].isNotNervousByEnemyTroopValue) { + MACRO_CALL_MEMBER(AICState_Func::playWillNotAttack1BikFromPlayerToPlayer, this)( + playerID, requestedByPlayerID); + return FALSE; + } + + if (DAT_GameState::instance.playerDataArray[playerID].totalAttackTroops + - DAT_GameState::instance.playerDataArray[playerID].totalAttackingEngineerTroops + < this->aics[aicIndex].AttForceSupportAllyThreshold) { + MACRO_CALL_MEMBER(AICState_Func::playWillNotAttack2BikFromPlayerToPlayer, this)( + playerID, requestedByPlayerID); + return FALSE; + } + + MACRO_CALL_MEMBER(AICState_Func::playWillAttackBikFromPlayerToPlayer, this)(playerID, requestedByPlayerID); + return TRUE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/determineAIPlayerHelp.cpp b/src/OpenSHC/AI/AICState/determineAIPlayerHelp.cpp new file mode 100644 index 000000000..e2d371895 --- /dev/null +++ b/src/OpenSHC/AI/AICState/determineAIPlayerHelp.cpp @@ -0,0 +1,35 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3850 + BOOLEnum AICState::determineAIPlayerHelp(int playerID, int requestedByPlayerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return FALSE; + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + + if (DAT_GameState::instance.playerDataArray[playerID].isNotNervousByEnemyTroopValue) { + MACRO_CALL_MEMBER(AICState_Func::playWillNotHelp1BikFromPlayerToPlayer, this)( + playerID, requestedByPlayerID); + return FALSE; + } + + if (DAT_GameState::instance.playerDataArray[playerID].totalAttackTroops + < this->aics[aicIndex].AttForceSupportAllyThreshold) { + MACRO_CALL_MEMBER(AICState_Func::playWillNotHelp2BikFromPlayerToPlayer, this)( + playerID, requestedByPlayerID); + return FALSE; + } + + MACRO_CALL_MEMBER(AICState_Func::playWillHelpBikFromPlayerToPlayer, this)(playerID, requestedByPlayerID); + return TRUE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/findAttackTribePositionAndPathToDefenses.cpp b/src/OpenSHC/AI/AICState/findAttackTribePositionAndPathToDefenses.cpp new file mode 100644 index 000000000..a2a3ed587 --- /dev/null +++ b/src/OpenSHC/AI/AICState/findAttackTribePositionAndPathToDefenses.cpp @@ -0,0 +1,58 @@ +#include "../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) + { + int x = 0; + int y = 0; + for (int i = 0; i < 11; i++) { + int targetUnitID = 0; + int const tribeType = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeType; + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeCount; + for (int j = 0; j < tribeCount; j++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeType + j]; + if (tribeID == 0) + continue; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[j + tribeType]) + continue; + + targetUnitID = DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID; + x = DAT_UnitsState::instance.units[targetUnitID].x; + y = DAT_UnitsState::instance.units[targetUnitID].y; + break; + } + if (targetUnitID != 0) + break; + } + + DAT_GameState::instance.playerDataArray[playerID].someTile = 0; + if (x == 0) + return; + + int const tile = MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::tracePathToFriendlyDefensesAndReturnTileUnk, + DAT_PathFindingState::ptr)(playerID, x, y); + if (tile <= 0) + return; + + int const tileY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + DAT_GameState::instance.playerDataArray[playerID].someTile = tile; + DAT_GameState::instance.playerDataArray[playerID].someY = tileY; + DAT_GameState::instance.playerDataArray[playerID].someX + = tile - DAT_ViewportRenderState::instance.translationMatrix[tileY].addXgetTile; + } +} +} diff --git a/src/OpenSHC/AI/AICState/generateSiegeCreationInformation.cpp b/src/OpenSHC/AI/AICState/generateSiegeCreationInformation.cpp new file mode 100644 index 000000000..2761ea925 --- /dev/null +++ b/src/OpenSHC/AI/AICState/generateSiegeCreationInformation.cpp @@ -0,0 +1,56 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Game/GameMode.hpp" +#include "OpenSHC/Map/Units/UnitType.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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC420 + void AICState::generateSiegeCreationInformation(int playerID, int buildingID, int unitID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return; + int i = 0; + if (DAT_GameSynchronyState::instance.currentGameMode == OpenSHC::Game::GM_SOLITARY) + return; + + int siegeIndex; + if (DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_S_TREBUCHET) + siegeIndex = 2; + else if (DAT_UnitsState::instance.units[unitID].unitType == OpenSHC::Map::Units::UT_S_FBALLISTA) + siegeIndex = 3; + else + return; + + int const slot = DAT_SkirmishDefinedData::instance.SiegeEngineMetaInfoArray[siegeIndex].slot; + int const locationCount + = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[slot]; + while (i < locationCount) { + int const index = i; + i++; + if (DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlots[slot][index] > 0 + && buildingID + == DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[siegeIndex][index] + .buildingID) { + DAT_GameState::instance.playerDataArray[playerID].aiSiegeCreationInformation[siegeIndex][index].unitID + = unitID; + DAT_GameState::instance.playerDataArray[playerID].aiSiegeCreationInformation[siegeIndex][index].uid + = DAT_UnitsState::instance.units[unitID].uid; + DAT_GameState::instance.playerDataArray[playerID] + .aiSiegeCreationInformation[siegeIndex][index] + .buildingID = 0; + return; + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/getAIBuildInterval.cpp b/src/OpenSHC/AI/AICState/getAIBuildInterval.cpp index b76f6d0d0..2cb13d26b 100644 --- a/src/OpenSHC/AI/AICState/getAIBuildInterval.cpp +++ b/src/OpenSHC/AI/AICState/getAIBuildInterval.cpp @@ -1,4 +1,4 @@ -#include "../AICState.hpp" +#include "../AICState.func.hpp" #include "OpenSHC/AI/AITypeA.hpp" @@ -9,14 +9,11 @@ namespace AI { // FUNCTION: STRONGHOLDCRUSADER 0x004CB120 int AICState::getAIBuildInterval(int playerID) - { - int const aiType = DAT_GameState::ptr->playerDataArray[playerID].aiType; - if (aiType == AITA_NULL) { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) return 0; - } - - return this->aics[(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 000000000..321504c8d --- /dev/null +++ b/src/OpenSHC/AI/AICState/getCurrentDesiredAttackRaidUnitCount.cpp @@ -0,0 +1,25 @@ +#include "../AICState.func.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 (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/getResourceRebuildDelay.cpp b/src/OpenSHC/AI/AICState/getResourceRebuildDelay.cpp new file mode 100644 index 000000000..3c04e8881 --- /dev/null +++ b/src/OpenSHC/AI/AICState/getResourceRebuildDelay.cpp @@ -0,0 +1,19 @@ +#include "../AICState.func.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 000000000..85dcc6f50 --- /dev/null +++ b/src/OpenSHC/AI/AICState/getSmallestPatrolTribe.cpp @@ -0,0 +1,49 @@ +#include "../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 i = 0; + int selectedTribeID = 0; + int selectedIndex = 0; + int lowestSize = 1000; + + for (;; i++) { + if (i >= tribeCount) { + if (selectedTribeID == 0) + return 0; + break; + } + + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[170 + i]; + if (tribeID == 0 + || DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[i + 170]) { + selectedTribeID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + selectedIndex = i; + break; + } + + if (DAT_TribesState::instance.tribes[tribeID].size < lowestSize) { + lowestSize = DAT_TribesState::instance.tribes[tribeID].size; + selectedTribeID = tribeID; + selectedIndex = i; + } + } + + DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[selectedIndex + 170] = selectedTribeID; + DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[selectedIndex + 170] + = DAT_TribesState::instance.tribes[selectedTribeID].uid; + return selectedTribeID; + } +} +} diff --git a/src/OpenSHC/AI/AICState/getTargetableBuildingForPlayerID.cpp b/src/OpenSHC/AI/AICState/getTargetableBuildingForPlayerID.cpp new file mode 100644 index 000000000..e87f7a650 --- /dev/null +++ b/src/OpenSHC/AI/AICState/getTargetableBuildingForPlayerID.cpp @@ -0,0 +1,47 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDBC0 + int AICState::getTargetableBuildingForPlayerID(int playerID, int patrolGroupIndex) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == 0) + return 0; + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + if (this->aics[aicIndex].OuterPatrolGroupsCount <= 0) + return 0; + if (DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker <= 0) + return 0; + + int const step = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker + / this->aics[aicIndex].OuterPatrolGroupsCount; + DAT_GameState::instance.playerDataArray[playerID].someCounter2++; + if (DAT_GameState::instance.playerDataArray[playerID].someCounter2 + >= DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker) + DAT_GameState::instance.playerDataArray[playerID].someCounter2 = 0; + int index = (step * patrolGroupIndex + DAT_GameState::instance.playerDataArray[playerID].someCounter2) + % DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker; + for (int i = 0; i < DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker; i++) { + int const buildingID = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildings[index]; + index++; + if (index >= DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker) + index = 0; + if (buildingID == 0) + continue; + + for (int j = 0; j < 8; j++) { + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType + == DAT_SkirmishDefinedData::instance.BuildingTargetPrioritySet1[j]) + return buildingID; + } + } + return 0; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/getUnitTypeForUnitBehaviourType.cpp b/src/OpenSHC/AI/AICState/getUnitTypeForUnitBehaviourType.cpp new file mode 100644 index 000000000..09528bb70 --- /dev/null +++ b/src/OpenSHC/AI/AICState/getUnitTypeForUnitBehaviourType.cpp @@ -0,0 +1,58 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AIUnitBehaviourType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC250 + UnitType AICState::getUnitTypeForUnitBehaviourType(int playerID, AIUnitBehaviourType unitBehaviourType) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) + return (UnitType)0; + int const aicIndex = aiType - 1; + + if (unitBehaviourType == OpenSHC::AI::AIUBT_ENGINEERING) + return OpenSHC::Map::Units::UT_E_ENGINEER; + if (unitBehaviourType == OpenSHC::AI::AIUBT_DIGGING) + return (UnitType)this->aics[aicIndex].AttDiggingUnit; + if (unitBehaviourType == OpenSHC::AI::AIUBT_ASSASSINATION) + return OpenSHC::Map::Units::UT_A_ASSASSIN; + if (unitBehaviourType == OpenSHC::AI::AIUBT_ATTUNIT2) + return (UnitType)this->aics[aicIndex].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[aicIndex].AttUnitPatrol; + if (unitBehaviourType == OpenSHC::AI::AIUBT_BACKUPING) + return (UnitType)this->aics[aicIndex].AttUnitBackup; + if (unitBehaviourType == OpenSHC::AI::AIUBT_ENGAGING) + return (UnitType)this->aics[aicIndex].AttUnitEngage; + if (unitBehaviourType == OpenSHC::AI::AIUBT_SIEGEDEFENSE) + return (UnitType)this->aics[aicIndex].AttUnitSiegeDef; + if (unitBehaviourType == OpenSHC::AI::AIUBT_ATTUNITMAIN) { + if ((&this->aics[aicIndex] + .AttUnitMain1)[DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex] + == 0) + DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex = 0; + if (DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex >= 4) + DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex = 0; + + int const choice = DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex; + UnitType const unitType = (UnitType)(&this->aics[aicIndex].AttUnitMain1)[choice]; + DAT_GameState::instance.playerDataArray[playerID].aiAttUnitMainChoiceIndex = choice + 1; + return unitType; + } + + return (UnitType)this->aics[aicIndex].AttUnitMain1; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/getUnitTypeIndexForUnitID.cpp b/src/OpenSHC/AI/AICState/getUnitTypeIndexForUnitID.cpp new file mode 100644 index 000000000..f1cfe9a7f --- /dev/null +++ b/src/OpenSHC/AI/AICState/getUnitTypeIndexForUnitID.cpp @@ -0,0 +1,63 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/Map/Units/UnitType.hpp" +#include "OpenSHC/Map/Units/UnitTypeInt.hpp" +#include "OpenSHC/Map/Units/UnitTypeShort.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_SkirmishDefinedData.hpp" +#include "OpenSHC/Globals/DAT_UnitsState.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AITypeA; + using OpenSHC::AI::AIVUnitType; + using OpenSHC::Map::Units::UnitTypeInt; + using OpenSHC::Map::Units::UnitTypeShort; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC390 + AIVUnitType AICState::getUnitTypeIndexForUnitID(int unitID, int allowSlaveSlotForRanged) + { + UnitTypeShort const unitType = DAT_UnitsState::instance.units[unitID].unitType; + int const owner = DAT_UnitsState::instance.units[unitID].owner; + UnitTypeInt effectiveUnitType; + if (unitType == OpenSHC::Map::Units::UT_PEASANT) { + effectiveUnitType = DAT_UnitsState::instance.units[unitID].unitTypeToChangeInto; + } else { + effectiveUnitType = unitType; + } + + // The Caliph fields slaves rather than the ranged units listed below + if (DAT_GameState::instance.playerDataArray[owner].aiType - 1 != OpenSHC::AI::AITA_CALIPH + && allowSlaveSlotForRanged != 0 + && DAT_GameState::instance.playerDataArray[owner].aivUnitLocationSlotLocationCount[0xd] > 0) { + if (effectiveUnitType == OpenSHC::Map::Units::UT_E_ARCHER) { + return OpenSHC::AI::AIVUT_SLAVE; + } + if (effectiveUnitType == OpenSHC::Map::Units::UT_E_XBOW) { + return OpenSHC::AI::AIVUT_SLAVE; + } + if (effectiveUnitType == OpenSHC::Map::Units::UT_A_ARCHER) { + return OpenSHC::AI::AIVUT_SLAVE; + } + if (effectiveUnitType == OpenSHC::Map::Units::UT_A_SLINGER) { + return OpenSHC::AI::AIVUT_SLAVE; + } + if (effectiveUnitType == OpenSHC::Map::Units::UT_A_FIRETHROWER) { + return OpenSHC::AI::AIVUT_SLAVE; + } + } + + for (int someUnitTypeIndex = 0; someUnitTypeIndex < 20; someUnitTypeIndex++) { + if (DAT_SkirmishDefinedData::instance.SomeAIUnitTypeArray[someUnitTypeIndex] == effectiveUnitType) { + return (AIVUnitType)someUnitTypeIndex; + } + } + return OpenSHC::AI::AIVUT_NONE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/giveMoveCommandToSortieUnits.cpp b/src/OpenSHC/AI/AICState/giveMoveCommandToSortieUnits.cpp new file mode 100644 index 000000000..51d41bbb2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/giveMoveCommandToSortieUnits.cpp @@ -0,0 +1,71 @@ +#include "../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/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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD8F0 + void AICState::giveMoveCommandToSortieUnits(int playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) + return; + + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[167]; + int const aicIndex = aiType - 1; + if (tribeID == 0) + return; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[167]) + return; + + if (DAT_TribesState::instance.tribes[tribeID].size + < DAT_GameState::instance.playerDataArray[playerID].totalTroopsType7 - 1) + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::syncBehaviourType7UnitTribes, this)(playerID); + + int const targetUnitID = DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID; + bool sendToKeep = true; + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker <= 0) { + if (DAT_TribesState::instance.tribes[tribeID].size < this->aics[aicIndex].SortieUnitMeleeMin) { + sendToKeep = false; + } else { + int const unitID = DAT_GameState::instance.playerDataArray[playerID].lastEncounteredTroopUnitID; + if (unitID != 0 + && DAT_GameState::instance.playerDataArray[playerID].lastEncounteredTroopUnitUID + == DAT_UnitsState::instance.units[unitID].uid + && MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func:: + calculateCanPlayerUnitsNavigateToAreaFromArea, + DAT_PathFindingState::ptr)(playerID, + (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance.units[targetUnitID].tile], + (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_UnitsState::instance.units[unitID].tile], + 0) + != 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, DAT_UnitsState::instance.units[unitID].x, + DAT_UnitsState::instance.units[unitID].y, 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + sendToKeep = false; + } + } + } + if (sendToKeep) { + 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/hasNoTroopsOrAllAreDiggers.cpp b/src/OpenSHC/AI/AICState/hasNoTroopsOrAllAreDiggers.cpp new file mode 100644 index 000000000..2c69186aa --- /dev/null +++ b/src/OpenSHC/AI/AICState/hasNoTroopsOrAllAreDiggers.cpp @@ -0,0 +1,43 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CFFD0 + BOOLEnum AICState::hasNoTroopsOrAllAreDiggers(int playerID) + { + int totalTroops = DAT_GameState::instance.playerDataArray[playerID].totalAssassinTroops + + DAT_GameState::instance.playerDataArray[playerID].totalUnit2Troops + + DAT_GameState::instance.playerDataArray[playerID].totalUnitPatrolTroops; + int halfOfDiggingTroops = 0; + + if (DAT_GameState::instance.playerDataArray[playerID].aiPlayerState == 6) { + totalTroops += DAT_GameState::instance.playerDataArray[playerID].totalDiggingUnitTroops; + if (DAT_GameState::instance.playerDataArray[playerID].attackTicker > 16) + totalTroops += DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops / 8; + else + totalTroops += DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops; + totalTroops += DAT_GameState::instance.playerDataArray[playerID].totalUnitEngageTroops + + DAT_GameState::instance.playerDataArray[playerID].totalUnitSiegeDefTroops + + DAT_GameState::instance.playerDataArray[playerID].totalMaxDefaultTroops; + } else { + halfOfDiggingTroops = DAT_GameState::instance.playerDataArray[playerID].totalDiggingUnitTroops / 2; + if (DAT_GameState::instance.playerDataArray[playerID].attackTicker > 12) + totalTroops += DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops / 8; + else + totalTroops += DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops; + totalTroops += DAT_GameState::instance.playerDataArray[playerID].totalUnitEngageTroops + + DAT_GameState::instance.playerDataArray[playerID].totalUnitSiegeDefTroops + + DAT_GameState::instance.playerDataArray[playerID].totalMaxDefaultTroops; + } + + if (totalTroops <= 1) + return TRUE; + if (totalTroops <= 4 && halfOfDiggingTroops > totalTroops * 2) + return TRUE; + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/hasNotEnoughSupplies.cpp b/src/OpenSHC/AI/AICState/hasNotEnoughSupplies.cpp new file mode 100644 index 000000000..fd5fac651 --- /dev/null +++ b/src/OpenSHC/AI/AICState/hasNotEnoughSupplies.cpp @@ -0,0 +1,31 @@ +#include "../AICState.func.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 0x004D00A0 + BOOLEnum AICState::hasNotEnoughSupplies(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].marketplace.id == 0 + && DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_WOOD] + < DAT_BuildingsState::instance.buildingCosts[Map::Buildings::BT_MARKETPLACE].requiredWood + && DAT_GameState::instance.playerDataArray[playerID].noLabourerBuildingCount < 1) + return TRUE; + if (DAT_GameState::instance.playerDataArray[playerID].granary.id == 0 + && DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_WOOD] + < DAT_BuildingsState::instance.buildingCosts[Map::Buildings::BT_GRANARY].requiredWood + && DAT_GameState::instance.playerDataArray[playerID].noLabourerBuildingCount <= 1) + return TRUE; + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] <= 0 + && DAT_GameState::instance.playerDataArray[playerID].popularity <= 25 + && DAT_GameState::instance.playerDataArray[playerID].totalFood <= 0) + return TRUE; + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/ifHasTribe0SendUnitsToKeep.cpp b/src/OpenSHC/AI/AICState/ifHasTribe0SendUnitsToKeep.cpp new file mode 100644 index 000000000..c90f9ce3b --- /dev/null +++ b/src/OpenSHC/AI/AICState/ifHasTribe0SendUnitsToKeep.cpp @@ -0,0 +1,22 @@ +#include "../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 const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0]; + if (tribeID <= 0) + return; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0]) + return; + + 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 000000000..428a7bc1d --- /dev/null +++ b/src/OpenSHC/AI/AICState/instructTribe166ToMove.cpp @@ -0,0 +1,60 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Game/GameStateStructures.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_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CD7B0 + void AICState::instructTribe166ToMove(int playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) + return; + int const aicIndex = aiType - 1; + + if (DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01 > 0) + DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01--; + if (DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01 > 50) + DAT_GameState::instance.playerDataArray[playerID].unknownCounter_01 = 50; + + int const tribeID = 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[166]) + return; + + if (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 > 0 && keepEnclosed == FALSE) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(tribeID, playerID); + } else if (DAT_TribesState::instance.tribes[tribeID].size >= this->aics[aicIndex].SortieUnitRangedMin) { + if (DAT_GameState::instance.playerDataArray[playerID].someXPosition <= 0 + || DAT_GameState::instance.playerDataArray[playerID].someYPosition <= 0) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(tribeID, playerID); + } else { + 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, DAT_GameState::instance.playerDataArray[playerID].someXPosition, + 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; + } +} +} diff --git a/src/OpenSHC/AI/AICState/isResourceLargerOrEqualThanMinimumGoodsRequiredAfterTrade.cpp b/src/OpenSHC/AI/AICState/isResourceLargerOrEqualThanMinimumGoodsRequiredAfterTrade.cpp new file mode 100644 index 000000000..4df0de5fc --- /dev/null +++ b/src/OpenSHC/AI/AICState/isResourceLargerOrEqualThanMinimumGoodsRequiredAfterTrade.cpp @@ -0,0 +1,26 @@ +#include "../AICState.func.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) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) + return TRUE; + if (resourceType == Game::Resources::RT_GOLD) + return FALSE; + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[resourceType] + < this->aics[DAT_GameState::instance.playerDataArray[playerID].aiType - 1].minimumGoodsRequiredAfterTrade + + buffer) + return FALSE; + return TRUE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/makeOutpostTribesAttack.cpp b/src/OpenSHC/AI/AICState/makeOutpostTribesAttack.cpp new file mode 100644 index 000000000..2ae660693 --- /dev/null +++ b/src/OpenSHC/AI/AICState/makeOutpostTribesAttack.cpp @@ -0,0 +1,22 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Globals/DAT_TribesState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D3960 + void AICState::makeOutpostTribesAttack() + { + for (int i = 0; i < 1000; i++) { + int const tribeID = this->tribeIDArray[i]; + if (tribeID == 0) + continue; + if (DAT_TribesState::instance.tribes[tribeID].uid != this->tribeUIDArray[i]) + continue; + + MACRO_CALL_MEMBER(AICState_Func::decideOnTribeAttackLocation, this)(tribeID); + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/makeUnitsGoDefensiveAndBackToSomeLocation.cpp b/src/OpenSHC/AI/AICState/makeUnitsGoDefensiveAndBackToSomeLocation.cpp new file mode 100644 index 000000000..01b994742 --- /dev/null +++ b/src/OpenSHC/AI/AICState/makeUnitsGoDefensiveAndBackToSomeLocation.cpp @@ -0,0 +1,47 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CEA50 + void AICState::makeUnitsGoDefensiveAndBackToSomeLocation(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return; + if (DAT_GameState::instance.playerDataArray[playerID].someTile == 0) + return; + + for (int i = 0; i < 11; i++) { + int const tribeType = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeType; + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeCount; + for (int j = 0; j < tribeCount; j++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeType + j]; + if (tribeID == 0) + continue; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[j + tribeType]) + continue; + + int const x = DAT_GameState::instance.playerDataArray[playerID].someX; + DAT_TribesState::instance.tribes[tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_DEFENSIVE; + int const y = DAT_GameState::instance.playerDataArray[playerID].someY; + uint const result = MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, + DAT_TribesState::ptr)(tribeID, x, y, 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + if (result == 0) + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::removeAllTribeUnits, DAT_TribesState::ptr)( + tribeID); + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/mapperIsWoodCuttersHutWhileNoWoodAvailable.cpp b/src/OpenSHC/AI/AICState/mapperIsWoodCuttersHutWhileNoWoodAvailable.cpp new file mode 100644 index 000000000..527d339f6 --- /dev/null +++ b/src/OpenSHC/AI/AICState/mapperIsWoodCuttersHutWhileNoWoodAvailable.cpp @@ -0,0 +1,20 @@ +#include "../AICState.func.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) + { + if (commandBuildingType == Commands::M_MAPPER_WOODSMAN + && DAT_AIVState::instance.mapExtraInfo.totalWoodAvailable <= 0) + return TRUE; + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/moveAttackTribesToLocations.cpp b/src/OpenSHC/AI/AICState/moveAttackTribesToLocations.cpp new file mode 100644 index 000000000..4ef01ddee --- /dev/null +++ b/src/OpenSHC/AI/AICState/moveAttackTribesToLocations.cpp @@ -0,0 +1,47 @@ +#include "../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 { + + // 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 i = 0; i < 11; i++) { + int const tribeType = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeType; + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeCount; + for (int j = 0; j < tribeCount; j++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeType + j]; + if (tribeID == 0) + continue; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[j + tribeType]) + continue; + + 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, + DAT_GameState::instance.mapAndTime.aiTribeMoveDestinationXYPairArray1[playerID * 5][destIndex] + .xOffset, + DAT_GameState::instance.mapAndTime.aiTribeMoveDestinationXYPairArray1[playerID * 5][destIndex] + .yOffset, + 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + destIndex++; + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/moveAttackingUnitsToNextRallyPoint.cpp b/src/OpenSHC/AI/AICState/moveAttackingUnitsToNextRallyPoint.cpp new file mode 100644 index 000000000..ab8dac6e0 --- /dev/null +++ b/src/OpenSHC/AI/AICState/moveAttackingUnitsToNextRallyPoint.cpp @@ -0,0 +1,71 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D2F80 + void AICState::moveAttackingUnitsToNextRallyPoint(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return; + + for (int i = 0; i < 2; i++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[190 + i]; + if (tribeID == 0) + continue; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[i + 190]) + continue; + + DAT_TribesState::instance.tribes[tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + if (MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::allUnitsReachedTheirDestination, + DAT_TribesState::ptr)(tribeID) + == FALSE) + continue; + + DAT_TribesState::instance.tribes[tribeID].unknownAttackRelatedUpdateCounter = 0; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addRallyPointForTribe, this)(tribeID); + + short const rallyPointCount = DAT_TribesState::instance.tribes[tribeID].rallyPointCount; + int rallyPointIndex = DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex + 1; + if (rallyPointIndex >= rallyPointCount) + rallyPointIndex = 0; + + int const x = DAT_TribesState::instance.tribes[tribeID].rallyPointArray[rallyPointIndex][0]; + int const y = DAT_TribesState::instance.tribes[tribeID].rallyPointArray[rallyPointIndex][1]; + int const targetUnitID = DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID; + DAT_TribesState::instance.tribes[tribeID].currentRallyPointIndex = rallyPointIndex; + + if (rallyPointCount <= 0) + continue; + + if (rallyPointCount <= 1) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)(DAT_UnitsState::instance.units[targetUnitID].x, + DAT_UnitsState::instance.units[targetUnitID].y, x, y); + if (DAT_DirectionAlgorithmState::instance.distanceHigh <= 5) + continue; + } else { + if (DAT_UnitsState::instance.units[targetUnitID].closestEnemyMicroDistance <= 96) + continue; + } + + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)( + tribeID, x, y, 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/percentageNonMovingTribesGTEAICSpecified.cpp b/src/OpenSHC/AI/AICState/percentageNonMovingTribesGTEAICSpecified.cpp new file mode 100644 index 000000000..132e9a1be --- /dev/null +++ b/src/OpenSHC/AI/AICState/percentageNonMovingTribesGTEAICSpecified.cpp @@ -0,0 +1,59 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CED90 + BOOLEnum AICState::percentageNonMovingTribesGTEAICSpecified(int playerID) + { + AITypeInt const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) { + return FALSE; + } + + int const aicIndex = aiType - 1; + int tribeTotal = 0; + int notReallyMoving = 0; + + for (int i = 0; i < 11; i++) { + int const tribeIndex = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeType; + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeCount; + + for (int j = 0; j < tribeCount; j++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex + j]; + if (tribeID == 0) { + continue; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex + j]) { + continue; + } + + tribeTotal++; + if (DAT_TribesState::instance.tribes[tribeID].percentageMovingUnk < 20) { + notReallyMoving++; + } + } + } + + if (tribeTotal <= 0) { + return FALSE; + } + + return notReallyMoving * 100 / tribeTotal >= this->aics[aicIndex].AttMovingTribesThreshold; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/playAnger1BikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playAnger1BikFromPlayer.cpp new file mode 100644 index 000000000..35899d699 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playAnger1BikFromPlayer.cpp @@ -0,0 +1,42 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + if (DAT_GameState::instance.mapAndTime.playerTeams[playerID] + == DAT_GameState::instance.mapAndTime.playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID]) { + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID + != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + DWORD now = timeGetTime(); + if (INT_ARRAY_00eb0af0::instance[playerID] != 0 && now - INT_ARRAY_00eb0af0::instance[playerID] < 600000) { + return; + } + + INT_ARRAY_00eb0af0::instance[playerID] = now; + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, aicIndex, 5); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playAnger2BikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playAnger2BikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..17af74870 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playAnger2BikFromPlayerToPlayer.cpp @@ -0,0 +1,37 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) { + return; + } + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown3 != 0) { + return; + } + + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown3 = 96; + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, 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 000000000..0d5236677 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playFriendlyVictoryBikFromPlayer.cpp @@ -0,0 +1,24 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, 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 000000000..7e41253d4 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playGoodsNotSentBikFromPlayer.cpp @@ -0,0 +1,24 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 28); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playGoodsRequestBikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playGoodsRequestBikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..7bfb0fc2a --- /dev/null +++ b/src/OpenSHC/AI/AICState/playGoodsRequestBikFromPlayerToPlayer.cpp @@ -0,0 +1,33 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + if (DAT_GameState::instance.playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .lordKilledByPlayerID + != 0) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 14); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playGoodsSentBikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playGoodsSentBikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..69553606b --- /dev/null +++ b/src/OpenSHC/AI/AICState/playGoodsSentBikFromPlayerToPlayer.cpp @@ -0,0 +1,28 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (!MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID)) { + return; + } + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 29); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playJokeBikFromAIToHuman.cpp b/src/OpenSHC/AI/AICState/playJokeBikFromAIToHuman.cpp new file mode 100644 index 000000000..887485f99 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playJokeBikFromAIToHuman.cpp @@ -0,0 +1,23 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, aiType, 20); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playNervous1BikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playNervous1BikFromPlayer.cpp new file mode 100644 index 000000000..ed4056ca5 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playNervous1BikFromPlayer.cpp @@ -0,0 +1,28 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown5 != 0) { + return; + } + + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown5 = 48; + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 8); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playNoHelpBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playNoHelpBikFromPlayer.cpp new file mode 100644 index 000000000..f21f64e4c --- /dev/null +++ b/src/OpenSHC/AI/AICState/playNoHelpBikFromPlayer.cpp @@ -0,0 +1,24 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 31); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playPlayerKilledBiksFromKilledAndKillerPlayer.cpp b/src/OpenSHC/AI/AICState/playPlayerKilledBiksFromKilledAndKillerPlayer.cpp new file mode 100644 index 000000000..dfefafdbc --- /dev/null +++ b/src/OpenSHC/AI/AICState/playPlayerKilledBiksFromKilledAndKillerPlayer.cpp @@ -0,0 +1,74 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(killerPlayerID) + == 0) { + return; + } + + if (killedPlayerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killerPlayerID, DAT_GameState::instance.playerDataArray[killerPlayerID].aiType - 1, 12); + return; + } + + if (killerPlayerID == DAT_GameSynchronyState::instance.currentPlayerSlotID) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killedPlayerID, DAT_GameState::instance.playerDataArray[killedPlayerID].aiType - 1, 7); + + for (int i = 1; i < 9; i++) { + if (DAT_GameState::instance.playerDataArray[i].aiType == AITA_NULL) { + continue; + } + if (DAT_GameState::instance.mapAndTime.playerTeams[i] + != DAT_GameState::instance.mapAndTime + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID]) { + continue; + } + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(i) + == 0) { + continue; + } + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + i, DAT_GameState::instance.playerDataArray[i].aiType - 1, 17); + } + return; + } + + if (DAT_GameState::instance.mapAndTime.playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID] + == DAT_GameState::instance.mapAndTime.playerTeams[killedPlayerID]) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killedPlayerID, DAT_GameState::instance.playerDataArray[killedPlayerID].aiType - 1, 16); + return; + } + + if (DAT_GameState::instance.mapAndTime.playerTeams[killerPlayerID] + == DAT_GameState::instance.mapAndTime.playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID]) { + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killedPlayerID, DAT_GameState::instance.playerDataArray[killedPlayerID].aiType - 1, 7); + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killerPlayerID, DAT_GameState::instance.playerDataArray[killerPlayerID].aiType - 1, 18); + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killedPlayerID, DAT_GameState::instance.playerDataArray[killedPlayerID].aiType - 1, 7); + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + killerPlayerID, DAT_GameState::instance.playerDataArray[killerPlayerID].aiType - 1, 13); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playRequestHelpBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playRequestHelpBikFromPlayer.cpp new file mode 100644 index 000000000..09f8d0039 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playRequestHelpBikFromPlayer.cpp @@ -0,0 +1,40 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown6 != 0) { + return; + } + + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown6 = 48; + + if (DAT_GameState::instance.mapAndTime.playerTeams[playerID] + != DAT_GameState::instance.mapAndTime.playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID]) { + return; + } + if (DAT_GameState::instance.playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .lordKilledByPlayerID + != 0) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 19); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playTauntToAttackedPlayerBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playTauntToAttackedPlayerBikFromPlayer.cpp new file mode 100644 index 000000000..72ac065f9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playTauntToAttackedPlayerBikFromPlayer.cpp @@ -0,0 +1,42 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + + int bik = 0; + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + if (DAT_GameState::instance.mapAndTime.playerTeams[playerID] + == DAT_GameState::instance.mapAndTime.playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID]) { + bik = 23; + } + + if (DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID + != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + if (bik == 0) { + bik = (SEC_RNG::instance.currentNumber2 & 3) + 1; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, aicIndex, bik); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playTeamWinningBikFromPlayer.cpp b/src/OpenSHC/AI/AICState/playTeamWinningBikFromPlayer.cpp new file mode 100644 index 000000000..291c2e0b9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playTeamWinningBikFromPlayer.cpp @@ -0,0 +1,30 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker != 0) { + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker != 0) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 30); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playThanksBikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playThanksBikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..fb3e9204d --- /dev/null +++ b/src/OpenSHC/AI/AICState/playThanksBikFromPlayerToPlayer.cpp @@ -0,0 +1,44 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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/TIME_LastThanksBikPlaybackTime.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D0A80 + void AICState::playThanksBikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + DAT_GameState::instance.playerDataArray[playerID].aiRequestDelay = this->aics[aicIndex].aiRequestDelay; + + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + if (DAT_GameState::instance.playerDataArray[DAT_GameSynchronyState::instance.currentPlayerSlotID] + .lordKilledByPlayerID + != 0) { + return; + } + + DWORD now = timeGetTime(); + if (TIME_LastThanksBikPlaybackTime::instance != 0 && now - TIME_LastThanksBikPlaybackTime::instance < 1200000) { + return; + } + + TIME_LastThanksBikPlaybackTime::instance = now; + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, aicIndex, 15); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playVictory2BikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playVictory2BikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..def0dfcd2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playVictory2BikFromPlayerToPlayer.cpp @@ -0,0 +1,44 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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) + { + if (DAT_GameState::instance.playerDataArray[targetPlayerID].aiType == AITA_NULL) { + return; + } + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID] == -1) { + return; + } + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(targetPlayerID) + == 0) { + return; + } + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (DAT_GameState::instance.playerDataArray[targetPlayerID].someAiCountdown4 != 0) { + return; + } + + DAT_GameState::instance.playerDataArray[targetPlayerID].someAiCountdown4 = 48; + if (playerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + targetPlayerID, DAT_GameState::instance.playerDataArray[targetPlayerID].aiType - 1, 11); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playWillAttackBikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playWillAttackBikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..39dbbc6b8 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playWillAttackBikFromPlayerToPlayer.cpp @@ -0,0 +1,28 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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 0x004D0F40 + void AICState::playWillAttackBikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x21); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playWillHelpBikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playWillHelpBikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..ce1eaf589 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playWillHelpBikFromPlayerToPlayer.cpp @@ -0,0 +1,28 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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 0x004D0E50 + void AICState::playWillHelpBikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x20); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playWillNotAttack1BikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playWillNotAttack1BikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..4a7691635 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playWillNotAttack1BikFromPlayerToPlayer.cpp @@ -0,0 +1,28 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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 0x004D0EA0 + void AICState::playWillNotAttack1BikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x18); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playWillNotAttack2BikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playWillNotAttack2BikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..77b3b681a --- /dev/null +++ b/src/OpenSHC/AI/AICState/playWillNotAttack2BikFromPlayerToPlayer.cpp @@ -0,0 +1,28 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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 0x004D0EF0 + void AICState::playWillNotAttack2BikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x19); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playWillNotHelp1BikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playWillNotHelp1BikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..33040b8fe --- /dev/null +++ b/src/OpenSHC/AI/AICState/playWillNotHelp1BikFromPlayerToPlayer.cpp @@ -0,0 +1,28 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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 0x004D0DB0 + void AICState::playWillNotHelp1BikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x1a); + } +} +} diff --git a/src/OpenSHC/AI/AICState/playWillNotHelp2BikFromPlayerToPlayer.cpp b/src/OpenSHC/AI/AICState/playWillNotHelp2BikFromPlayerToPlayer.cpp new file mode 100644 index 000000000..9b8ae5f28 --- /dev/null +++ b/src/OpenSHC/AI/AICState/playWillNotHelp2BikFromPlayerToPlayer.cpp @@ -0,0 +1,28 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/UnitsState.func.hpp" +#include "OpenSHC/Rendering/Bink/AIMessageQueue.func.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 0x004D0E00 + void AICState::playWillNotHelp2BikFromPlayerToPlayer(int playerID, int targetPlayerID) + { + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(playerID) == 0) { + return; + } + if (targetPlayerID != DAT_GameSynchronyState::instance.currentPlayerSlotID) { + return; + } + + MACRO_CALL_MEMBER(Rendering::Bink::AIMessageQueue_Func::playBikVideoFromPlayer, DAT_VideoBikQueue::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].aiType - 1, 0x1b); + } +} +} diff --git a/src/OpenSHC/AI/AICState/processAttForceRallyPercentage.cpp b/src/OpenSHC/AI/AICState/processAttForceRallyPercentage.cpp new file mode 100644 index 000000000..e5a72e902 --- /dev/null +++ b/src/OpenSHC/AI/AICState/processAttForceRallyPercentage.cpp @@ -0,0 +1,68 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/AI/AITypeInt.hpp" +#include "OpenSHC/AI/Tribes/AITribeIndex.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 const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) { + return FALSE; + } + + int const aicIndex = aiType - 1; + int tribeTotal = 0; + int notReallyMoving = 0; + + for (int i = 0; i < 11; i++) { + int const tribeIndex = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeType; + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeCount; + if (tribeIndex == OpenSHC::AI::Tribes::AITI_BREACH_BASIC_07_01 + || tribeIndex == OpenSHC::AI::Tribes::AITI_BREACH_10_01 + || tribeIndex == OpenSHC::AI::Tribes::AITI_BREACH_BASIC_05_01) { + continue; + } + + for (int j = 0; j < tribeCount; j++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex + j]; + if (tribeID == 0) { + continue; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex + j]) { + continue; + } + + tribeTotal++; + if (DAT_TribesState::instance.tribes[tribeID].percentageMovingUnk < 20) { + notReallyMoving++; + } + DAT_TribesState::instance.tribes[tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_STAND_GROUND; + } + } + + if (tribeTotal <= 0) { + return FALSE; + } + + return (BOOLEnum)(notReallyMoving * 100 / tribeTotal >= this->aics[aicIndex].AttForceRallyPercentage); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/randomlySelectAttackUnitTypeToRecruit.cpp b/src/OpenSHC/AI/AICState/randomlySelectAttackUnitTypeToRecruit.cpp new file mode 100644 index 000000000..b979de3d4 --- /dev/null +++ b/src/OpenSHC/AI/AICState/randomlySelectAttackUnitTypeToRecruit.cpp @@ -0,0 +1,138 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CC070 + AIUnitBehaviourType AICState::randomlySelectAttackUnitTypeToRecruit(int playerID) + { + AITypeInt const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) { + return OpenSHC::AI::AIUBT_ATTUNITMAIN; + } + + int const attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + int const aicIndex = aiType - 1; + int requiredCount = 0; + + // clear out flags + for (int i = 0; i < 11; i++) { + DAT_SkirmishDefinedData::instance.attackUnitRequired[i].required = 0; + } + + int engineersCount = DAT_GameState::instance.playerDataArray[playerID].currentAttackWave * 4; + if (engineersCount > (int)this->aics[aicIndex].AttMaxEngineers) { + engineersCount = this->aics[aicIndex].AttMaxEngineers; + } + if (this->aics[aicIndex].AttMaxEngineers != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalAttackingEngineerTroops < engineersCount) { + requiredCount = 1; + DAT_SkirmishDefinedData::instance.attackUnitRequired[0].required = 1; + } + + if (this->aics[aicIndex].AttDiggingUnitMax != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalDiggingUnitTroops + < (int)this->aics[aicIndex].AttDiggingUnitMax + && DAT_GameState::instance.playerDataArray[attackedPlayerID].moatsOwned > 5) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[1].required = 1; + } + + if (this->aics[aicIndex].AttMaxAssassins != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalAssassinTroops + < (int)this->aics[aicIndex].AttMaxAssassins) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[2].required = 1; + } + + if (this->aics[aicIndex].AttUnit2Max != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalUnit2Troops + < (int)this->aics[aicIndex].AttUnit2Max) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[3].required = 1; + } + + if (this->aics[aicIndex].AttMaxLaddermen != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalLaddermenTroops + < (int)this->aics[aicIndex].AttMaxLaddermen) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[4].required = 1; + } + + if (this->aics[aicIndex].AttMaxTunnelers != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalTunnelerTroops + < (int)this->aics[aicIndex].AttMaxTunnelers) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[5].required = 1; + } + + if (this->aics[aicIndex].AttUnitPatrolMax != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalUnitPatrolTroops + < (int)this->aics[aicIndex].AttUnitPatrolMax) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[6].required = 1; + } + + if (this->aics[aicIndex].AttUnitBackupMax != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalUnitBackupTroops + < (int)this->aics[aicIndex].AttUnitBackupMax) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[7].required = 1; + } + + if (this->aics[aicIndex].AttUnitEngageMax != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalUnitEngageTroops + < (int)this->aics[aicIndex].AttUnitEngageMax) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[8].required = 1; + } + + if (this->aics[aicIndex].AttUnitSiegeDefMax != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalUnitSiegeDefTroops + < (int)this->aics[aicIndex].AttUnitSiegeDefMax) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[9].required = 1; + } + + if (this->aics[aicIndex].AttMaxDefault != 0 + && DAT_GameState::instance.playerDataArray[playerID].totalMaxDefaultTroops + < (int)this->aics[aicIndex].AttMaxDefault) { + requiredCount++; + DAT_SkirmishDefinedData::instance.attackUnitRequired[10].required = 1; + } + + if (requiredCount <= 0) { + return OpenSHC::AI::AIUBT_ATTUNITMAIN; + } + + int selection = (int)SEC_RNG::instance.currentNumber2 % requiredCount; + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + + for (int i = 0; i < 11; i++) { + if (DAT_SkirmishDefinedData::instance.attackUnitRequired[i].required == 0) { + continue; + } + if (selection == 0) { + return (AIUnitBehaviourType)DAT_SkirmishDefinedData::instance.attackUnitRequired[i].unitBehaviourType; + } + selection--; + } + + 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 000000000..55d493028 --- /dev/null +++ b/src/OpenSHC/AI/AICState/recomputeAIPathCostToAttackedKeep.cpp @@ -0,0 +1,60 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE040 + void AICState::recomputeAIPathCostToAttackedKeep(int playerID, int useBorderDistance) + { + int const attackedPlayerID = DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID; + int const keepArea + = (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_GameState::instance.playerDataArray[playerID].campground.tileEntry]; + + int borderDistance; + if (DAT_GameState::instance.playerDataArray[attackedPlayerID].unknownPathBorderDistanceRelated > 120) { + borderDistance = 98; + } else { + borderDistance + = DAT_GameState::instance.playerDataArray[attackedPlayerID].unknownPathBorderDistanceRelated > 90 ? 70 + : 50; + } + if (useBorderDistance == 0) { + borderDistance = 0; + } + + DAT_TroopValueState::instance.attackInfo.playerInfo[playerID - 1].currentPlayerID_OR_attackedPlayerID_OR_one + = attackedPlayerID; + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 80400, '\0', DAT_TileMapState::instance.AIZoneLayer); + + if (MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, DAT_GameState::ptr)( + playerID, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID) + != FALSE) { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::computeAIZoneLayer, + DAT_PathFindingState::ptr)(DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID, 1, 1); + } else { + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::computeAIZoneLayer, + DAT_PathFindingState::ptr)(DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID, 0, 1); + } + + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkLayerAndAIPathCostLayer, + DAT_PathFindingState::ptr)(110, borderDistance, keepArea, playerID); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/recomputeAIZonerLayer.cpp b/src/OpenSHC/AI/AICState/recomputeAIZonerLayer.cpp new file mode 100644 index 000000000..2478afb94 --- /dev/null +++ b/src/OpenSHC/AI/AICState/recomputeAIZonerLayer.cpp @@ -0,0 +1,31 @@ +#include "../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 { + + // 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', DAT_TileMapState::instance.AIZoneLayer); + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::PathFindingState_Func::computeAIZoneLayer, DAT_PathFindingState::ptr)(1, 2, 1); + 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; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/recomputeAttackAIZone.cpp b/src/OpenSHC/AI/AICState/recomputeAttackAIZone.cpp new file mode 100644 index 000000000..905549bec --- /dev/null +++ b/src/OpenSHC/AI/AICState/recomputeAttackAIZone.cpp @@ -0,0 +1,48 @@ +#include "../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() + { + for (int playerID = 1; playerID < 9; playerID++) { + int fullID = DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID]; + DAT_TroopValueState::instance.attackInfo.playerInfo[playerID - 1].currentPlayerID_OR_attackedPlayerID_OR_one + = playerID; + if (fullID == -1 + && DAT_GameSynchronyState::instance.currentAIArray[playerID] == 0 + // fixme: this comparison looks dumb since fullID is already known to be the value, so a cmp eax eax + // is generated, but we need it for the 100% match. + && fullID == DAT_GameSynchronyState::instance.currentPlayerFullIDArray[playerID]) { + continue; + } + + MACRO_CALL_MEMBER(OpenSHC::IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 80400, '\0', DAT_TileMapState::instance.AIZoneLayer); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::computeAIZoneLayer, + DAT_PathFindingState::ptr)(playerID, 2, 1); + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::updateWalkLayerAndAIPathCostLayer, + DAT_PathFindingState::ptr)(252, 0, 1, playerID); + DAT_GameState::instance.playerDataArray[playerID].unknownPathBorderDistanceRelated + = DAT_PathFindingState::instance.distance; + DAT_TroopValueState::instance.attackInfo.playerInfo[playerID - 1].currentPlayerID_OR_attackedPlayerID_OR_one + = 1; + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/recreateEngineersTribe.cpp b/src/OpenSHC/AI/AICState/recreateEngineersTribe.cpp new file mode 100644 index 000000000..3fd8df74e --- /dev/null +++ b/src/OpenSHC/AI/AICState/recreateEngineersTribe.cpp @@ -0,0 +1,25 @@ +#include "../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) { + return; + } + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0x12]; + if (tribeID != 0) { + if (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/removeOrganismsAndSetMoveDestinationPairs.cpp b/src/OpenSHC/AI/AICState/removeOrganismsAndSetMoveDestinationPairs.cpp new file mode 100644 index 000000000..99d4ce303 --- /dev/null +++ b/src/OpenSHC/AI/AICState/removeOrganismsAndSetMoveDestinationPairs.cpp @@ -0,0 +1,49 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE430 + void AICState::removeOrganismsAndSetMoveDestinationPairs(int playerID) + { + // Clear a tree standing on the target tile + int const organismID + = (short)DAT_TileMapState::instance.OrganismLayer + [DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY] + .addXgetTile + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX]; + if (organismID != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::LandscapeState_Func::removeTree, DAT_LandscapeState::ptr)(organismID); + } + + // Hardcoded exception: the Sultan keeps a smaller distance than all other AIs + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + int const extraDistance = aicIndex == OpenSHC::AI::AIT_SULTAN - 1 ? 8 : 20; + MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findLinkageBasedPathOrWalkRadius, + DAT_PathFindingState::ptr)(DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX, + 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, + DAT_TroopValueState::instance.attackInfo.playerInfo[playerID - 1].someMinimumDistance + extraDistance); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/requestGoods.cpp b/src/OpenSHC/AI/AICState/requestGoods.cpp new file mode 100644 index 000000000..2c10772ed --- /dev/null +++ b/src/OpenSHC/AI/AICState/requestGoods.cpp @@ -0,0 +1,70 @@ +#include "../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) { + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].aiRequestDelay != 0) { + return; + } + // Never request raw farm goods or ale + if (resourceType == OpenSHC::Game::Resources::RT_WHEAT || resourceType == OpenSHC::Game::Resources::RT_HOPS + || resourceType == OpenSHC::Game::Resources::RT_ALE) { + return; + } + // Only request food when almost out of it + if ((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) { + return; + } + // fixme: NOTE: the original performs the exact same food check a second time, it cannot be removed + if ((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) { + return; + } + + // Ask every human teammate that has no pending request from this player + for (int i = 1; i < 9; i++) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[i] == -1) { + continue; + } + if (DAT_GameState::instance.mapAndTime.playerTeams[i] + != DAT_GameState::instance.mapAndTime.playerTeams[playerID]) { + continue; + } + if (MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(i) + == 0) { + continue; + } + if (DAT_GameState::instance.playerDataArray[i].requestedGoodsArray1Unk[playerID] != 0) { + continue; + } + + DAT_GameState::instance.playerDataArray[i].requestedGoodsArray1Unk[playerID] = resourceType; + DAT_GameState::instance.playerDataArray[i].requestedGoodsArray2Unk[playerID] = amount; + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::playGoodsRequestBikFromPlayerToPlayer, this)(playerID, i); + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/selectBuildingFromAListOfBuildingTypes.cpp b/src/OpenSHC/AI/AICState/selectBuildingFromAListOfBuildingTypes.cpp new file mode 100644 index 000000000..96b4f48b3 --- /dev/null +++ b/src/OpenSHC/AI/AICState/selectBuildingFromAListOfBuildingTypes.cpp @@ -0,0 +1,35 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDAB0 + int AICState::selectBuildingFromAListOfBuildingTypes(int playerID) + { + // Pick the targetable building whose type comes first in BuildingTargetPrioritySet1 + int selectedBuildingID = 0; + int bestPriority = 46; + for (int i = 0; i < DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker; i++) { + int const buildingID = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildings[i]; + if (buildingID == 0) { + continue; + } + for (int j = 0; j < bestPriority; j++) { + if ((short)DAT_BuildingsState::instance.buildings[buildingID].buildingType + == DAT_SkirmishDefinedData::instance.BuildingTargetPrioritySet1[j]) { + // Found a better match; lowering the bound also ends this loop + bestPriority = j; + selectedBuildingID = buildingID; + } + } + } + + return selectedBuildingID; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/selectRandomBuildingIDFromListOfBuildingTypes.cpp b/src/OpenSHC/AI/AICState/selectRandomBuildingIDFromListOfBuildingTypes.cpp new file mode 100644 index 000000000..22f900fec --- /dev/null +++ b/src/OpenSHC/AI/AICState/selectRandomBuildingIDFromListOfBuildingTypes.cpp @@ -0,0 +1,25 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CDCA0 + int AICState::selectRandomBuildingIDFromListOfBuildingTypes(int playerID, undefined4 raidTribeIndex) + { + int const count = DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildingsTracker; + if (count <= 0) { + return 0; + } + + int const index = SEC_RNG::instance.currentNumber2 % count; + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + return DAT_GameState::instance.playerDataArray[playerID].top100TargetableBuildings[index]; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sellGoods.cpp b/src/OpenSHC/AI/AICState/sellGoods.cpp index 5ebd21d63..c830f93ea 100644 --- a/src/OpenSHC/AI/AICState/sellGoods.cpp +++ b/src/OpenSHC/AI/AICState/sellGoods.cpp @@ -23,6 +23,5 @@ namespace AI { 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 000000000..81be98e1e --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendAITribe_0xf_toAttackPatrol.cpp @@ -0,0 +1,40 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TroopValueState.func.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 tribeIndex = 15; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) { + return; + } + + // Bug: only tribe 15 is sent, although tribe 16 can also exist if 'AIC: AttUnitPatrolGroupsCount' is 2 + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex]; + if (tribeID == 0) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex]) { + 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::TroopValueState_Func::sendAttackingPatrolTribeToComputedDestination, + DAT_TroopValueState::ptr)(tribeID); + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed.cpp b/src/OpenSHC/AI/AICState/sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed.cpp new file mode 100644 index 000000000..2ee9c8a9e --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed.cpp @@ -0,0 +1,64 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4220 + void AICState::sendCertainUnitTribesToAIVSlotsOrKeepIfNervousAndNotEnclosed(int playerID) + { + BOOLEnum const enclosed = MACRO_CALL_MEMBER( + OpenSHC::Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)(playerID); + + for (int i = 0; i < 6; i++) { + int const count = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount + [DAT_SkirmishDefinedData::instance.MaxAIVLocationForGroundUnits[i].aivUnitType]; + for (int j = 0; j < count; j++) { + int const tribeID + = DAT_GameState::instance.playerDataArray[playerID] + .aiTribeIDs[DAT_SkirmishDefinedData::instance.MaxAIVLocationForGroundUnits[i].tribeArrayOffset + + j]; + if (tribeID != 0 + && DAT_TribesState::instance.tribes[tribeID].uid + == DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs + [DAT_SkirmishDefinedData::instance.MaxAIVLocationForGroundUnits[i].tribeArrayOffset + j]) { + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker <= 0 + || enclosed != FALSE) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeToAIVLocationSlot, this)(tribeID, + (AIVUnitType)DAT_SkirmishDefinedData::instance.MaxAIVLocationForGroundUnits[i].aivUnitType, + j); + } else { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(tribeID, playerID); + } + } + + // NOTE: the original sets the stance even when the tribe is invalid (tribeID may be 0) + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker <= 0 + || 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; + } + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sendOilMenForOilPickupOrToAIVLocations.cpp b/src/OpenSHC/AI/AICState/sendOilMenForOilPickupOrToAIVLocations.cpp new file mode 100644 index 000000000..117a3bda2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendOilMenForOilPickupOrToAIVLocations.cpp @@ -0,0 +1,62 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D44E0 + void AICState::sendOilMenForOilPickupOrToAIVLocations(int playerID) + { + int const oilSmelterID = DAT_GameState::instance.playerDataArray[playerID].oilSmelter.id; + int count = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount[1]; + if (count < 1) { + return; + } + if (count == 10) { + count = 9; + } + + // Oil men tribes start at offset 71 in the tribe ID array + for (int i = 1; i <= count; i++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[70 + i]; + if (tribeID == 0) { + continue; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[70 + i]) { + continue; + } + + if (DAT_UnitsState::instance.units[DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID] + .resourceToDeposit + != 0) { + 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, i - 1); + continue; + } + + // Send for oil duty + if (oilSmelterID != 0) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::relayTribeInstruction, DAT_UnitsState::ptr)( + tribeID, OpenSHC::Map::Units::UIT_CONSTRUCT_SIEGE_EQUIPMENTOIL_DUTYENGINEERRELATED, oilSmelterID, + DAT_BuildingsState::instance.buildings[oilSmelterID].uid, 0); + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sendRangedUnitTribesToAIVSlotsOrKeepIfNervous.cpp b/src/OpenSHC/AI/AICState/sendRangedUnitTribesToAIVSlotsOrKeepIfNervous.cpp new file mode 100644 index 000000000..e22092e3e --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendRangedUnitTribesToAIVSlotsOrKeepIfNervous.cpp @@ -0,0 +1,61 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIVUnitType.hpp" +#include "OpenSHC/AI/AIVUnitTypeInt.hpp" +#include "OpenSHC/AI/Tribes/AIVUnitTypeMaxLocationPair.hpp" +#include "OpenSHC/Map/LogicHelpers/Logic1.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; + using OpenSHC::Map::LogicHelpers::L_KEEP_NON_MANOR_HOUSE; + using OpenSHC::Map::LogicHelpers::L_WALL_OR_GATEHOUSE; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D4130 + void AICState::sendRangedUnitTribesToAIVSlotsOrKeepIfNervous(int playerID) + { + for (int i = 0; i < 5; i++) { + int const count = DAT_GameState::instance.playerDataArray[playerID].aivUnitLocationSlotLocationCount + [DAT_SkirmishDefinedData::instance.MaxAIVLocationForRangedUnits[i].aivUnitType]; + for (int j = 0; j < count; j++) { + int const tribeID + = DAT_GameState::instance.playerDataArray[playerID] + .aiTribeIDs[DAT_SkirmishDefinedData::instance.MaxAIVLocationForRangedUnits[i].tribeArrayOffset + + j]; + if (tribeID == 0) { + continue; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID] + .aiTribeUIDs[DAT_SkirmishDefinedData::instance.MaxAIVLocationForRangedUnits[i].tribeArrayOffset + + j]) { + continue; + } + + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker > 0) { + if ((DAT_TileMapState::instance.LogicLayer[DAT_UnitsState::instance + .units[DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID] + .tile] + & (L_KEEP_NON_MANOR_HOUSE | L_WALL_OR_GATEHOUSE)) + == 0) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(tribeID, playerID); + } + } else { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendTribeToAIVLocationSlot, this)(tribeID, + (AIVUnitType)DAT_SkirmishDefinedData::instance.MaxAIVLocationForRangedUnits[i].aivUnitType, j); + } + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sendTribeAtOffset1ToCampfire.cpp b/src/OpenSHC/AI/AICState/sendTribeAtOffset1ToCampfire.cpp new file mode 100644 index 000000000..ef76334da --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendTribeAtOffset1ToCampfire.cpp @@ -0,0 +1,21 @@ +#include "../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 const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[1]; + if (tribeID <= 0) + return; + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[1]) + return; + 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 000000000..16f064822 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendTribeToAIVLocationSlot.cpp @@ -0,0 +1,48 @@ +#include "../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 const owner = DAT_TribesState::instance.tribes[tribeID].owner; + + // Find the slotIndex-th non-empty slot + int tile = 0; + for (int i = 0; i < 10; i++) { + if (DAT_GameState::instance.playerDataArray[owner].aivUnitLocationSlots[aivUnitType][i] == 0) { + continue; + } + slotIndex--; + if (slotIndex < 0) { + tile = DAT_GameState::instance.playerDataArray[owner].aivUnitLocationSlots[aivUnitType][i]; + break; + } + } + + int const y = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[tile]; + int const x = tile - DAT_ViewportRenderState::instance.translationMatrix[y].addXgetTile; + + if (MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::canNavigateUnitsFromTileToTargetTile, this)(tribeID, tile) + != FALSE) { + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::commandUnitsToLocation, DAT_TribesState::ptr)( + tribeID, x, y, 0); + return; + } + + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToKeep, this)(tribeID, owner); + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendTribeToAttack.cpp b/src/OpenSHC/AI/AICState/sendTribeToAttack.cpp new file mode 100644 index 000000000..a4defde57 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendTribeToAttack.cpp @@ -0,0 +1,39 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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 tribeIndex = 0xbd; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) { + return; + } + + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex]; + if (tribeID == 0) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex]) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID == 0) { + return; + } + + 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 000000000..51d390bf3 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendUnitsToAttackBreachedCastle.cpp @@ -0,0 +1,111 @@ +#include "../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/WindowsHelper/Enums/BOOLEnum.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; + using OpenSHC::WindowsHelper::Enums::BOOLEnum; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D30E0 + BOOLEnum AICState::sendUnitsToAttackBreachedCastle(int attackingPlayerIndex) + { + int vector = 0; + if (DAT_GameState::instance.playerDataArray[attackingPlayerIndex].aiType == OpenSHC::AI::AIT_NULL) { + return TRUE; + } + + BOOLEnum sentUnitsToAttackLord = FALSE; + int const defendingLordID = MACRO_CALL_MEMBER(OpenSHC::Map::Units::UnitsState_Func::getAliveLordForPlayer, + DAT_UnitsState::ptr)(DAT_GameState::instance.playerDataArray[attackingPlayerIndex].attackedPlayerID); + + for (int i = 0; i < 11; i++) { + int const tribeType = DAT_SkirmishDefinedData::instance.MaxBreachTribes[i].tribeType; + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxBreachTribes[i].tribeCount; + if (tribeType == 18 || tribeType == 14 || tribeType == 13) { + continue; + } + + for (int j = 0; j < tribeCount; j++) { + int const tribeID + = DAT_GameState::instance.playerDataArray[attackingPlayerIndex].aiTribeIDs[tribeType + j]; + if (tribeID == 0) { + continue; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[attackingPlayerIndex].aiTribeUIDs[j + tribeType]) { + continue; + } + + int const targetUnitID = (short)DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID; + DAT_TribesState::instance.tribes[tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_DEFENSIVE; + + if (sentUnitsToAttackLord != FALSE || defendingLordID == 0 || tribeType == 186) { + if (MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::giveSomeRaidCommand, this)(tribeID, tribeType) + != 0) { + continue; + } + + if (tribeType == 186) { + 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; + + int attackedPlayerID + = DAT_GameState::instance.playerDataArray[attackingPlayerIndex].attackedPlayerID; + MACRO_CALL_MEMBER( + OpenSHC::Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)(DAT_UnitsState::instance.units[targetUnitID].x, + DAT_UnitsState::instance.units[targetUnitID].y, + DAT_GameState::instance.mapAndTime.attackVectors[attackedPlayerID][vector].x, + DAT_GameState::instance.mapAndTime.attackVectors[attackedPlayerID][vector].y); + + if (DAT_DirectionAlgorithmState::instance.distanceHigh > 4) { + attackedPlayerID + = DAT_GameState::instance.playerDataArray[attackingPlayerIndex].attackedPlayerID; + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, + DAT_TribesState::ptr)(tribeID, + DAT_GameState::instance.mapAndTime.attackVectors[attackedPlayerID][vector].x, + DAT_GameState::instance.mapAndTime.attackVectors[attackedPlayerID][vector].y, 0, 0, + OpenSHC::Map::Units::Instructions::UMSE_0); + } + vector += 1; + } else { + // Send units to attack lord + int const lordY = DAT_UnitsState::instance.units[defendingLordID].y; + int const lordX = DAT_UnitsState::instance.units[defendingLordID].x; + if (MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::giveTribeMoveInstruction, DAT_TribesState::ptr)( + tribeID, lordX, lordY, 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0) + == 0) { + return FALSE; + } + sentUnitsToAttackLord = TRUE; + } + } + } + + return TRUE; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/sendUnitsToCampfire.cpp b/src/OpenSHC/AI/AICState/sendUnitsToCampfire.cpp new file mode 100644 index 000000000..a5b115838 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendUnitsToCampfire.cpp @@ -0,0 +1,25 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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 (DAT_GameState::instance.playerDataArray[playerID].campground.id <= 0) { + return; + } + + 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 000000000..3582df1eb --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendUnitsToKeep.cpp @@ -0,0 +1,25 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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 const keepID = DAT_GameState::instance.playerDataArray[playerID].keep.id; + if (keepID <= 0) { + return; + } + + MACRO_CALL_MEMBER(Map::Units::TribesState_Func::commandUnitsToLocation, DAT_TribesState::ptr)(tribeID, + (short)DAT_BuildingsState::instance.buildings[keepID].x + 3, + (short)DAT_BuildingsState::instance.buildings[keepID].y + 4, 0); + } +} +} diff --git a/src/OpenSHC/AI/AICState/sendUnitsToPlayerIDsKeepIfPossible.cpp b/src/OpenSHC/AI/AICState/sendUnitsToPlayerIDsKeepIfPossible.cpp new file mode 100644 index 000000000..06dc5d207 --- /dev/null +++ b/src/OpenSHC/AI/AICState/sendUnitsToPlayerIDsKeepIfPossible.cpp @@ -0,0 +1,80 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE5F0 + void AICState::sendUnitsToPlayerIDsKeepIfPossible(int playerID1, int playerID2) + { + uint const campfireX = DAT_GameState::instance.playerDataArray[playerID1].campground.xEntry; + uint const campfireY = DAT_GameState::instance.playerDataArray[playerID1].campground.yEntry; + + uint gatehouseX; + uint gatehouseY; + if (MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, DAT_GameState::ptr)( + playerID1, playerID2) + == FALSE) { + uint const gatehouseTile + = MACRO_CALL_MEMBER(OpenSHC::Map::Navigation::PathFindingState_Func::findOwnedGatehouse, + DAT_PathFindingState::ptr)(playerID2, campfireX, campfireY); + if (gatehouseTile != 0) { + gatehouseY = DAT_ViewportRenderState::instance.tileTranslationMatrix_YComponent[gatehouseTile]; + gatehouseX + = gatehouseTile - DAT_ViewportRenderState::instance.translationMatrix[gatehouseY].addXgetTile; + } else { + DAT_GameState::instance.playerDataArray[playerID1].requestStateUnk = 0; + return; + } + } + + for (int i = 0; i < 11; i++) { + int const tribeTypeStart = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeType; + int const tribeCount = DAT_SkirmishDefinedData::instance.MaxAttackTribes1[i].tribeCount; + for (int j = 0; j < tribeCount; j++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID1].aiTribeIDs[tribeTypeStart + j]; + if (tribeID == 0) { + continue; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID1].aiTribeUIDs[j + tribeTypeStart]) { + continue; + } + + DAT_TribesState::instance.tribes[tribeID].unitStance = OpenSHC::Map::Units::Behavior::USE_AGGRESSIVE; + + if (MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::canNavigateFromKeepToKeep, + DAT_GameState::ptr)(playerID1, playerID2) + != FALSE) { + if (tribeTypeStart == 0xbe) { + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::sendUnitsToCampfire, this)(tribeID, playerID2); + } else if (MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::selectionContainsHorses, + DAT_TribesState::ptr)(tribeID) + != 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, gatehouseX, gatehouseY, 0, 0, OpenSHC::Map::Units::Instructions::UMSE_0); + } + } + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_01.cpp b/src/OpenSHC/AI/AICState/setAICParameters_01.cpp new file mode 100644 index 000000000..df3fd43ed --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_01.cpp @@ -0,0 +1,151 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.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 aicIndex) + { + this->aics[aicIndex].flagType = 12; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 0; + this->aics[aicIndex].unknown005 = 0; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 5500; + this->aics[aicIndex].highestPopularity = 7500; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 10; + this->aics[aicIndex].farm1 = 32; + this->aics[aicIndex].populationPerFarm = 6; + this->aics[aicIndex].unknown011 = 20; + this->aics[aicIndex].maxFood = 100; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = -1; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 10; + this->aics[aicIndex].tradeAmountEquipment = 2; + this->aics[aicIndex].aiRequestDelay = 18; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 0; + this->aics[aicIndex].doubleRationsFoodThreshold = 0; + this->aics[aicIndex].maxWood = 30; + this->aics[aicIndex].maxStone = 40; + this->aics[aicIndex].maxResourceOther = 20; + this->aics[aicIndex].maxEquipment = 20; + this->aics[aicIndex].maxBeer = 20; + this->aics[aicIndex].maxResourceVariance = 5; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 8; + this->aics[aicIndex].populationPerQuarry = 18; + this->aics[aicIndex].populationPerIronmine = 0; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 1; + this->aics[aicIndex].maxIronmines = 1; + this->aics[aicIndex].maxWoodcutters = 6; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 6; + this->aics[aicIndex].buildInterval = 1; + this->aics[aicIndex].resourceRebuildDelay = 10; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource02 = 3; + this->aics[aicIndex].sellResource03 = 6; + this->aics[aicIndex].sellResource04 = 7; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource06 = 14; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 20; + this->aics[aicIndex].defWallPatrolGroups = 1; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 200; + this->aics[aicIndex].defSiegeEngineBuildDelay = 20; + this->aics[aicIndex].unknown072 = 800; + this->aics[aicIndex].unknown073 = 80; + this->aics[aicIndex].recruitProbDefWeak = 50; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 1; + this->aics[aicIndex].RecruitIntervalStrong = 1; + this->aics[aicIndex].SortieUnitRanged = 22; + this->aics[aicIndex].SortieUnitMelee = 24; + this->aics[aicIndex].DefDiggingUnit = 24; + this->aics[aicIndex].OuterPatrolGroupsCount = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 10; + this->aics[aicIndex].DefUnit1 = 24; + this->aics[aicIndex].DefUnit2 = 24; + this->aics[aicIndex].DefUnit3 = 22; + this->aics[aicIndex].DefUnit4 = 22; + this->aics[aicIndex].RaidUnitsBase = 10; + this->aics[aicIndex].RaidUnit1 = 24; + this->aics[aicIndex].RaidUnit2 = 24; + this->aics[aicIndex].RaidUnit3 = 22; + this->aics[aicIndex].recruitProbDefDefault = 0; + this->aics[aicIndex].recruitProbDefStrong = 0; + this->aics[aicIndex].recruitProbRaidWeak = 50; + this->aics[aicIndex].recruitProbRaidDefault = 50; + this->aics[aicIndex].recruitProbRaidStrong = 0; + this->aics[aicIndex].recruitProbAttackWeak = 0; + this->aics[aicIndex].recruitProbAttackDefault = 50; + this->aics[aicIndex].recruitProbAttackStrong = 100; + this->aics[aicIndex].SortieUnitRangedMin = 5; + this->aics[aicIndex].SortieUnitMeleeMin = 6; + this->aics[aicIndex].DefDiggingUnitMax = 0; + this->aics[aicIndex].DefWalls = 20; + this->aics[aicIndex].DefTotal = 30; + this->aics[aicIndex].OuterPatrolGroupsMove = 0; + this->aics[aicIndex].RaidUnitsRandom = 15; + this->aics[aicIndex].HarassingSiegeEnginesMax = 0; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 3; + this->aics[aicIndex].AttForceBase = 20; + this->aics[aicIndex].AttForceRandom = 10; + this->aics[aicIndex].AttForceSupportAllyThreshold = 15; + this->aics[aicIndex].AttForceRallyPercentage = 50; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 0; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 25; + this->aics[aicIndex].AttCoordinationPreference = 20; + this->aics[aicIndex].SiegeEngine1 = 0; + this->aics[aicIndex].CowThrowInterval = 1000; + this->aics[aicIndex].AttMaxEngineers = 0; + this->aics[aicIndex].AttDiggingUnit = 24; + this->aics[aicIndex].AttDiggingUnitMax = 20; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 22; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 22; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 22; + this->aics[aicIndex].AttUnitBackupMax = 16; + this->aics[aicIndex].AttUnitBackupGroupsCount = 0; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 24; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 24; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 1; + this->aics[aicIndex].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 000000000..13e52f862 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_02.cpp @@ -0,0 +1,149 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.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 aicIndex) + { + this->aics[aicIndex].flagType = 12; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 0; + this->aics[aicIndex].unknown005 = 0; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 8000; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 11; + this->aics[aicIndex].farm1 = 32; + this->aics[aicIndex].populationPerFarm = 5; + this->aics[aicIndex].unknown011 = 15; + this->aics[aicIndex].maxFood = 150; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = 20; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 5; + this->aics[aicIndex].tradeAmountEquipment = 6; + this->aics[aicIndex].aiRequestDelay = 12; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 10; + this->aics[aicIndex].doubleRationsFoodThreshold = 80; + this->aics[aicIndex].maxWood = 48; + this->aics[aicIndex].maxStone = 40; + this->aics[aicIndex].maxResourceOther = 15; + this->aics[aicIndex].maxEquipment = 12; + this->aics[aicIndex].maxBeer = 5; + this->aics[aicIndex].maxResourceVariance = 5; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 5; + this->aics[aicIndex].populationPerQuarry = 15; + this->aics[aicIndex].populationPerIronmine = 0; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 2; + this->aics[aicIndex].maxIronmines = 1; + this->aics[aicIndex].maxWoodcutters = 10; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 10; + this->aics[aicIndex].buildInterval = 2; + this->aics[aicIndex].resourceRebuildDelay = 12; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource02 = 3; + this->aics[aicIndex].sellResource03 = 6; + this->aics[aicIndex].sellResource04 = 7; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource06 = 14; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 2; + this->aics[aicIndex].defWallPatrolGroups = 4; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 500; + this->aics[aicIndex].defSiegeEngineBuildDelay = 20; + this->aics[aicIndex].unknown072 = 400; + this->aics[aicIndex].unknown073 = 60; + this->aics[aicIndex].recruitProbDefWeak = 70; + this->aics[aicIndex].recruitProbDefDefault = 30; + this->aics[aicIndex].recruitProbDefStrong = 20; + this->aics[aicIndex].recruitProbRaidWeak = 30; + this->aics[aicIndex].recruitProbRaidDefault = 40; + this->aics[aicIndex].recruitProbRaidStrong = 40; + this->aics[aicIndex].recruitProbAttackWeak = 0; + this->aics[aicIndex].recruitProbAttackDefault = 30; + this->aics[aicIndex].recruitProbAttackStrong = 40; + this->aics[aicIndex].RecruitIntervalWeak = 0; + this->aics[aicIndex].RecruitInterval = 1; + this->aics[aicIndex].RecruitIntervalStrong = 1; + this->aics[aicIndex].SortieUnitRangedMin = 2; + this->aics[aicIndex].SortieUnitRanged = 22; + this->aics[aicIndex].SortieUnitMeleeMin = 8; + this->aics[aicIndex].SortieUnitMelee = 24; + this->aics[aicIndex].DefDiggingUnitMax = 20; + this->aics[aicIndex].DefDiggingUnit = 24; + this->aics[aicIndex].DefWalls = 50; + this->aics[aicIndex].DefTotal = 80; + this->aics[aicIndex].OuterPatrolGroupsCount = 3; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 2; + this->aics[aicIndex].DefUnit1 = 22; + this->aics[aicIndex].RaidUnitsBase = 30; + this->aics[aicIndex].RaidUnitsRandom = 10; + this->aics[aicIndex].RaidUnit1 = 71; + this->aics[aicIndex].RaidUnit2 = 71; + this->aics[aicIndex].RaidUnit3 = 71; + this->aics[aicIndex].RaidUnit4 = 72; + this->aics[aicIndex].HarassingSiegeEnginesMax = 1; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 3; + this->aics[aicIndex].AttForceBase = 30; + this->aics[aicIndex].AttForceRandom = 10; + this->aics[aicIndex].AttForceSupportAllyThreshold = 30; + this->aics[aicIndex].AttForceRallyPercentage = 50; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 0; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 25; + this->aics[aicIndex].AttCoordinationPreference = 40; + this->aics[aicIndex].SiegeEngine1 = 39; + this->aics[aicIndex].CowThrowInterval = 1000; + this->aics[aicIndex].AttMaxEngineers = 2; + this->aics[aicIndex].AttDiggingUnit = 24; + this->aics[aicIndex].AttDiggingUnitMax = 20; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 22; + this->aics[aicIndex].AttUnit2Max = 20; + this->aics[aicIndex].AttMaxLaddermen = 10; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 72; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 70; + this->aics[aicIndex].AttUnitBackupMax = 50; + this->aics[aicIndex].AttUnitBackupGroupsCount = 1; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 22; + this->aics[aicIndex].AttUnitSiegeDefMax = 30; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 24; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 1; + this->aics[aicIndex].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 000000000..244e6ec73 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_03.cpp @@ -0,0 +1,149 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.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 aicIndex) + { + this->aics[aicIndex].flagType = 12; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 0; + this->aics[aicIndex].unknown005 = 0; + this->aics[aicIndex].criticalPopularity = 5000; + this->aics[aicIndex].lowestPopularity = 7000; + this->aics[aicIndex].highestPopularity = 8500; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 11; + this->aics[aicIndex].farm1 = 33; + this->aics[aicIndex].populationPerFarm = 4; + this->aics[aicIndex].unknown011 = 20; + this->aics[aicIndex].maxFood = 400; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = 10; + this->aics[aicIndex].minimumBread = 10; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 5; + this->aics[aicIndex].tradeAmountEquipment = 4; + this->aics[aicIndex].aiRequestDelay = 12; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 100; + this->aics[aicIndex].doubleRationsFoodThreshold = 60; + this->aics[aicIndex].maxWood = 70; + this->aics[aicIndex].maxStone = 40; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 10; + this->aics[aicIndex].maxBeer = 5; + this->aics[aicIndex].maxResourceVariance = 5; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 6; + this->aics[aicIndex].populationPerQuarry = 25; + this->aics[aicIndex].populationPerIronmine = 10; + this->aics[aicIndex].populationPerPitchrig = 20; + this->aics[aicIndex].maxQuarries = 2; + this->aics[aicIndex].maxIronmines = 4; + this->aics[aicIndex].maxWoodcutters = 8; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 12; + this->aics[aicIndex].buildInterval = 3; + this->aics[aicIndex].resourceRebuildDelay = 50; + this->aics[aicIndex].blacksmithSetting = 21; + this->aics[aicIndex].fletcherSetting = 18; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource02 = 3; + this->aics[aicIndex].sellResource04 = 7; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource06 = 14; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource08 = 17; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 10; + this->aics[aicIndex].defWallPatrolGroups = 4; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 100; + this->aics[aicIndex].defSiegeEngineBuildDelay = 20; + this->aics[aicIndex].unknown072 = 200; + this->aics[aicIndex].unknown073 = 40; + this->aics[aicIndex].recruitProbDefWeak = 100; + this->aics[aicIndex].recruitProbDefDefault = 60; + this->aics[aicIndex].recruitProbDefStrong = 30; + this->aics[aicIndex].recruitProbRaidWeak = 0; + this->aics[aicIndex].recruitProbRaidDefault = 0; + this->aics[aicIndex].recruitProbRaidStrong = 0; + this->aics[aicIndex].recruitProbAttackWeak = 0; + this->aics[aicIndex].recruitProbAttackDefault = 40; + this->aics[aicIndex].recruitProbAttackStrong = 70; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 1; + this->aics[aicIndex].RecruitIntervalStrong = 4; + this->aics[aicIndex].SortieUnitRangedMin = 4; + this->aics[aicIndex].SortieUnitRanged = 23; + this->aics[aicIndex].SortieUnitMeleeMin = 5; + this->aics[aicIndex].SortieUnitMelee = 26; + this->aics[aicIndex].DefDiggingUnitMax = 0; + this->aics[aicIndex].DefDiggingUnit = 24; + this->aics[aicIndex].DefWalls = 30; + this->aics[aicIndex].DefTotal = 40; + this->aics[aicIndex].OuterPatrolGroupsCount = 2; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 20; + this->aics[aicIndex].DefUnit1 = 23; + this->aics[aicIndex].DefUnit2 = 23; + this->aics[aicIndex].RaidUnitsBase = 10; + this->aics[aicIndex].RaidUnitsRandom = 5; + this->aics[aicIndex].DefUnit3 = 23; + this->aics[aicIndex].RaidUnit1 = 26; + this->aics[aicIndex].HarassingSiegeEnginesMax = 2; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 3; + this->aics[aicIndex].AttForceBase = 10; + this->aics[aicIndex].AttForceRandom = 10; + this->aics[aicIndex].AttForceSupportAllyThreshold = 10; + this->aics[aicIndex].AttForceRallyPercentage = 60; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 5; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 15; + this->aics[aicIndex].AttCoordinationPreference = 20; + this->aics[aicIndex].CowThrowInterval = 30; + this->aics[aicIndex].SiegeEngine1 = 39; + this->aics[aicIndex].SiegeEngine2 = 39; + this->aics[aicIndex].SiegeEngine3 = 39; + this->aics[aicIndex].SiegeEngine4 = 59; + this->aics[aicIndex].AttMaxEngineers = 10; + this->aics[aicIndex].AttDiggingUnit = 26; + this->aics[aicIndex].AttDiggingUnitMax = 10; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 23; + this->aics[aicIndex].AttUnitBackupMax = 5; + this->aics[aicIndex].AttUnitBackupGroupsCount = 1; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 26; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 26; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 2; + this->aics[aicIndex].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 000000000..a8678896b --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_04.cpp @@ -0,0 +1,150 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.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 aicIndex) + { + this->aics[aicIndex].flagType = 13; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4500; + this->aics[aicIndex].lowestPopularity = 6000; + this->aics[aicIndex].highestPopularity = 9000; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 11; + this->aics[aicIndex].farm1 = 33; + this->aics[aicIndex].farm2 = 32; + this->aics[aicIndex].farm3 = 31; + this->aics[aicIndex].farm4 = 32; + this->aics[aicIndex].populationPerFarm = 5; + this->aics[aicIndex].unknown011 = 12; + this->aics[aicIndex].maxFood = 200; + this->aics[aicIndex].minimumApples = 5; + this->aics[aicIndex].minimumCheese = 5; + this->aics[aicIndex].minimumBread = 5; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 10; + this->aics[aicIndex].tradeAmountEquipment = 4; + this->aics[aicIndex].aiRequestDelay = 20; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 40; + this->aics[aicIndex].doubleRationsFoodThreshold = 30; + this->aics[aicIndex].maxWood = 60; + this->aics[aicIndex].maxStone = 50; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 10; + this->aics[aicIndex].maxBeer = 15; + this->aics[aicIndex].maxResourceVariance = 5; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 4; + this->aics[aicIndex].populationPerQuarry = 9; + this->aics[aicIndex].populationPerIronmine = 13; + this->aics[aicIndex].populationPerPitchrig = 15; + this->aics[aicIndex].maxQuarries = 6; + this->aics[aicIndex].maxIronmines = 3; + this->aics[aicIndex].maxWoodcutters = 12; + this->aics[aicIndex].maxPitchrigs = 2; + this->aics[aicIndex].maxFarms = 10; + this->aics[aicIndex].buildInterval = 3; + this->aics[aicIndex].resourceRebuildDelay = 20; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 18; + this->aics[aicIndex].poleturnerSetting = 20; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].defWallPatrolRallyTime = 4; + this->aics[aicIndex].defWallPatrolGroups = 3; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 250; + this->aics[aicIndex].defSiegeEngineBuildDelay = 2; + this->aics[aicIndex].unknown072 = 100; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 90; + this->aics[aicIndex].recruitProbDefDefault = 60; + this->aics[aicIndex].recruitProbDefStrong = 30; + this->aics[aicIndex].recruitProbRaidWeak = 10; + this->aics[aicIndex].recruitProbRaidDefault = 30; + this->aics[aicIndex].recruitProbRaidStrong = 10; + this->aics[aicIndex].recruitProbAttackWeak = 0; + this->aics[aicIndex].recruitProbAttackDefault = 10; + this->aics[aicIndex].recruitProbAttackStrong = 60; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 4; + this->aics[aicIndex].RecruitIntervalStrong = 4; + this->aics[aicIndex].SortieUnitRangedMin = 8; + this->aics[aicIndex].SortieUnitRanged = 70; + this->aics[aicIndex].SortieUnitMeleeMin = 6; + this->aics[aicIndex].SortieUnitMelee = 25; + this->aics[aicIndex].DefDiggingUnitMax = 8; + this->aics[aicIndex].DefDiggingUnit = 25; + this->aics[aicIndex].DefWalls = 80; + this->aics[aicIndex].DefTotal = 100; + this->aics[aicIndex].OuterPatrolGroupsCount = 4; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 3; + this->aics[aicIndex].DefUnit1 = 23; + this->aics[aicIndex].DefUnit2 = 23; + this->aics[aicIndex].RaidUnitsRandom = 20; + this->aics[aicIndex].DefUnit3 = 22; + this->aics[aicIndex].DefUnit4 = 22; + this->aics[aicIndex].RaidUnitsBase = 5; + this->aics[aicIndex].RaidUnit1 = 74; + this->aics[aicIndex].RaidUnit2 = 74; + this->aics[aicIndex].HarassingSiegeEnginesMax = 4; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 5; + this->aics[aicIndex].AttForceBase = 40; + this->aics[aicIndex].AttForceRandom = 15; + this->aics[aicIndex].AttForceSupportAllyThreshold = 30; + this->aics[aicIndex].AttForceRallyPercentage = 80; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 12; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 5; + this->aics[aicIndex].AttCoordinationPreference = 50; + this->aics[aicIndex].CowThrowInterval = 8; + this->aics[aicIndex].SiegeEngine1 = 39; + this->aics[aicIndex].SiegeEngine2 = 40; + this->aics[aicIndex].SiegeEngine3 = 77; + this->aics[aicIndex].SiegeEngine4 = 40; + this->aics[aicIndex].AttMaxEngineers = 12; + this->aics[aicIndex].AttDiggingUnit = 25; + this->aics[aicIndex].AttDiggingUnitMax = 20; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 23; + this->aics[aicIndex].AttUnitBackupMax = 30; + this->aics[aicIndex].AttUnitBackupGroupsCount = 3; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 27; + this->aics[aicIndex].AttUnitSiegeDefMax = 5; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 1; + this->aics[aicIndex].AttUnitMain1 = 27; + this->aics[aicIndex].AttUnitMain2 = 25; + this->aics[aicIndex].AttUnitMain3 = 25; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 3; + this->aics[aicIndex].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 000000000..f96fb8e31 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_05.cpp @@ -0,0 +1,149 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.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 aicIndex) + { + this->aics[aicIndex].flagType = 10; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 0; + this->aics[aicIndex].unknown005 = 0; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 8000; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 12; + this->aics[aicIndex].farm1 = 31; + this->aics[aicIndex].farm2 = 30; + this->aics[aicIndex].farm3 = 30; + this->aics[aicIndex].farm4 = 32; + this->aics[aicIndex].populationPerFarm = 3; + this->aics[aicIndex].unknown011 = 20; + this->aics[aicIndex].maxFood = 50; + this->aics[aicIndex].minimumApples = 5; + this->aics[aicIndex].minimumCheese = 5; + this->aics[aicIndex].minimumBread = 5; + this->aics[aicIndex].minimumWheat = 5; + this->aics[aicIndex].minimumHop = 5; + this->aics[aicIndex].tradeAmountFood = 10; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 40; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 20; + this->aics[aicIndex].doubleRationsFoodThreshold = 20; + this->aics[aicIndex].maxWood = 60; + this->aics[aicIndex].maxStone = 50; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 5; + this->aics[aicIndex].maxBeer = 10; + this->aics[aicIndex].maxResourceVariance = 2; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 6; + this->aics[aicIndex].populationPerQuarry = 10; + this->aics[aicIndex].populationPerIronmine = 12; + this->aics[aicIndex].populationPerPitchrig = 20; + this->aics[aicIndex].maxQuarries = 4; + this->aics[aicIndex].maxIronmines = 4; + this->aics[aicIndex].maxWoodcutters = 14; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 15; + this->aics[aicIndex].buildInterval = 2; + this->aics[aicIndex].resourceRebuildDelay = 40; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 18; + this->aics[aicIndex].poleturnerSetting = 20; + this->aics[aicIndex].defWallPatrolRallyTime = 3; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 20; + this->aics[aicIndex].defSiegeEngineBuildDelay = 8; + this->aics[aicIndex].sellResource08 = 17; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].defWallPatrolGroups = 4; + this->aics[aicIndex].unknown072 = 100; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 100; + this->aics[aicIndex].recruitProbDefDefault = 34; + this->aics[aicIndex].recruitProbDefStrong = 25; + this->aics[aicIndex].recruitProbRaidWeak = 0; + this->aics[aicIndex].recruitProbRaidDefault = 33; + this->aics[aicIndex].recruitProbRaidStrong = 25; + this->aics[aicIndex].recruitProbAttackWeak = 0; + this->aics[aicIndex].recruitProbAttackDefault = 33; + this->aics[aicIndex].recruitProbAttackStrong = 50; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 1; + this->aics[aicIndex].RecruitIntervalStrong = 1; + this->aics[aicIndex].SortieUnitRangedMin = 10; + this->aics[aicIndex].SortieUnitRanged = 70; + this->aics[aicIndex].SortieUnitMeleeMin = 7; + this->aics[aicIndex].SortieUnitMelee = 75; + this->aics[aicIndex].DefDiggingUnitMax = 4; + this->aics[aicIndex].DefDiggingUnit = 71; + this->aics[aicIndex].DefWalls = 50; + this->aics[aicIndex].DefTotal = 70; + this->aics[aicIndex].OuterPatrolGroupsCount = 2; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 10; + this->aics[aicIndex].DefUnit1 = 75; + this->aics[aicIndex].DefUnit2 = 70; + this->aics[aicIndex].RaidUnitsBase = 10; + this->aics[aicIndex].RaidUnitsRandom = 5; + this->aics[aicIndex].DefUnit3 = 70; + this->aics[aicIndex].DefUnit4 = 70; + this->aics[aicIndex].DefUnit5 = 76; + this->aics[aicIndex].RaidUnit1 = 74; + this->aics[aicIndex].HarassingSiegeEnginesMax = 5; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 3; + this->aics[aicIndex].AttForceBase = 50; + this->aics[aicIndex].AttForceRandom = 10; + this->aics[aicIndex].AttForceSupportAllyThreshold = 8; + this->aics[aicIndex].AttForceRallyPercentage = 80; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 4; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 1; + this->aics[aicIndex].AttCoordinationPreference = 60; + this->aics[aicIndex].CowThrowInterval = 2000; + this->aics[aicIndex].SiegeEngine1 = 77; + this->aics[aicIndex].SiegeEngine2 = 77; + this->aics[aicIndex].SiegeEngine3 = 39; + this->aics[aicIndex].SiegeEngine4 = 39; + this->aics[aicIndex].AttMaxEngineers = 10; + this->aics[aicIndex].AttDiggingUnit = 71; + this->aics[aicIndex].AttDiggingUnitMax = 20; + this->aics[aicIndex].AttMaxAssassins = 5; + this->aics[aicIndex].AttUnit2 = 74; + this->aics[aicIndex].AttUnit2Max = 20; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 74; + this->aics[aicIndex].AttUnitPatrolMax = 20; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 1; + this->aics[aicIndex].AttUnitBackup = 70; + this->aics[aicIndex].AttUnitBackupMax = 20; + this->aics[aicIndex].AttUnitBackupGroupsCount = 3; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 76; + this->aics[aicIndex].AttUnitSiegeDef = 76; + this->aics[aicIndex].AttUnitSiegeDefMax = 6; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 1; + this->aics[aicIndex].AttUnitMain1 = 75; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 3; + this->aics[aicIndex].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 000000000..af8feecbf --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_06.cpp @@ -0,0 +1,150 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C8110 + void AICState::setAICParameters_06(int aicIndex) + { + this->aics[aicIndex].flagType = 10; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 0; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 9000; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 12; + this->aics[aicIndex].farm1 = 32; + this->aics[aicIndex].farm2 = 31; + this->aics[aicIndex].farm3 = 32; + this->aics[aicIndex].populationPerFarm = 5; + this->aics[aicIndex].unknown011 = 12; + this->aics[aicIndex].maxFood = 100; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = -1; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 10; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 12; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 50; + this->aics[aicIndex].doubleRationsFoodThreshold = 50; + this->aics[aicIndex].maxWood = 50; + this->aics[aicIndex].maxStone = 50; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 5; + this->aics[aicIndex].maxBeer = 15; + this->aics[aicIndex].maxResourceVariance = 5; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 10; + this->aics[aicIndex].populationPerIronmine = 12; + this->aics[aicIndex].populationPerPitchrig = 7; + this->aics[aicIndex].maxQuarries = 2; + this->aics[aicIndex].maxIronmines = 1; + this->aics[aicIndex].maxWoodcutters = 8; + this->aics[aicIndex].maxPitchrigs = 10; + this->aics[aicIndex].maxFarms = 10; + this->aics[aicIndex].buildInterval = 3; + this->aics[aicIndex].resourceRebuildDelay = 60; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 18; + this->aics[aicIndex].poleturnerSetting = 20; + this->aics[aicIndex].defWallPatrolRallyTime = 5; + this->aics[aicIndex].defWallPatrolGroups = 2; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 120; + this->aics[aicIndex].defSiegeEngineBuildDelay = 8; + this->aics[aicIndex].sellResource03 = 6; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource08 = 17; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].unknown072 = 100; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 80; + this->aics[aicIndex].recruitProbDefDefault = 40; + this->aics[aicIndex].recruitProbDefStrong = 40; + this->aics[aicIndex].recruitProbRaidWeak = 20; + this->aics[aicIndex].recruitProbRaidDefault = 40; + this->aics[aicIndex].recruitProbRaidStrong = 30; + this->aics[aicIndex].recruitProbAttackWeak = 0; + this->aics[aicIndex].recruitProbAttackDefault = 20; + this->aics[aicIndex].recruitProbAttackStrong = 30; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 0; + this->aics[aicIndex].RecruitIntervalStrong = 0; + this->aics[aicIndex].SortieUnitRangedMin = 7; + this->aics[aicIndex].SortieUnitRanged = 70; + this->aics[aicIndex].SortieUnitMeleeMin = 5; + this->aics[aicIndex].SortieUnitMelee = 75; + this->aics[aicIndex].DefDiggingUnitMax = 4; + this->aics[aicIndex].DefDiggingUnit = 71; + this->aics[aicIndex].DefWalls = 60; + this->aics[aicIndex].DefTotal = 60; + this->aics[aicIndex].OuterPatrolGroupsCount = 2; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 10; + this->aics[aicIndex].DefUnit1 = 76; + this->aics[aicIndex].DefUnit2 = 70; + this->aics[aicIndex].RaidUnitsBase = 30; + this->aics[aicIndex].RaidUnitsRandom = 30; + this->aics[aicIndex].DefUnit3 = 70; + this->aics[aicIndex].DefUnit4 = 70; + this->aics[aicIndex].RaidUnit1 = 71; + this->aics[aicIndex].HarassingSiegeEnginesMax = 2; + this->aics[aicIndex].HarassingSiegeEngine1 = 358; + this->aics[aicIndex].HarassingSiegeEngine2 = 190; + this->aics[aicIndex].RaidRetargetDelay = 3; + this->aics[aicIndex].AttForceBase = 15; + this->aics[aicIndex].AttForceRandom = 10; + this->aics[aicIndex].AttForceSupportAllyThreshold = 70; + this->aics[aicIndex].AttForceRallyPercentage = 100; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 2; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 10; + this->aics[aicIndex].AttCoordinationPreference = 50; + this->aics[aicIndex].CowThrowInterval = 7; + this->aics[aicIndex].SiegeEngine1 = 77; + this->aics[aicIndex].SiegeEngine2 = 77; + this->aics[aicIndex].SiegeEngine3 = 77; + this->aics[aicIndex].AttMaxEngineers = 6; + this->aics[aicIndex].AttDiggingUnit = 71; + this->aics[aicIndex].AttDiggingUnitMax = 20; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 75; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 74; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 74; + this->aics[aicIndex].AttUnitBackupMax = 40; + this->aics[aicIndex].AttUnitBackupGroupsCount = 2; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 70; + this->aics[aicIndex].AttUnitSiegeDefMax = 10; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 1; + this->aics[aicIndex].AttUnitMain1 = 75; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 5; + this->aics[aicIndex].TargetChoice = OpenSHC::AI::AIC::AITC_BALANCED; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_07.cpp b/src/OpenSHC/AI/AICState/setAICParameters_07.cpp new file mode 100644 index 000000000..0f7a127fc --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_07.cpp @@ -0,0 +1,147 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C8530 + void AICState::setAICParameters_07(int aicIndex) + { + this->aics[aicIndex].flagType = 10; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 0; + this->aics[aicIndex].unknown005 = 0; + this->aics[aicIndex].criticalPopularity = 5000; + this->aics[aicIndex].lowestPopularity = 6000; + this->aics[aicIndex].highestPopularity = 9000; + this->aics[aicIndex].taxesMin = 1; + this->aics[aicIndex].taxesMax = 8; + this->aics[aicIndex].farm1 = 0; + this->aics[aicIndex].populationPerFarm = 100; + this->aics[aicIndex].unknown011 = 20; + this->aics[aicIndex].maxFood = 500; + this->aics[aicIndex].minimumApples = 20; + this->aics[aicIndex].minimumCheese = -1; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 10; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 30; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 0; + this->aics[aicIndex].doubleRationsFoodThreshold = 0; + this->aics[aicIndex].maxWood = 30; + this->aics[aicIndex].maxStone = 20; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 5; + this->aics[aicIndex].maxBeer = 20; + this->aics[aicIndex].maxResourceVariance = 5; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 2; + this->aics[aicIndex].populationPerQuarry = 5; + this->aics[aicIndex].populationPerIronmine = 0; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 2; + this->aics[aicIndex].maxIronmines = 1; + this->aics[aicIndex].maxWoodcutters = 15; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 6; + this->aics[aicIndex].buildInterval = 5; + this->aics[aicIndex].resourceRebuildDelay = 80; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource02 = 3; + this->aics[aicIndex].sellResource03 = 6; + this->aics[aicIndex].sellResource04 = 7; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource06 = 14; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource08 = 17; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 10; + this->aics[aicIndex].defWallPatrolGroups = 2; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 200; + this->aics[aicIndex].defSiegeEngineBuildDelay = 20; + this->aics[aicIndex].unknown072 = 800; + this->aics[aicIndex].unknown073 = 80; + this->aics[aicIndex].recruitProbDefStrong = 30; + this->aics[aicIndex].recruitProbRaidWeak = 10; + this->aics[aicIndex].recruitProbRaidStrong = 30; + this->aics[aicIndex].recruitProbAttackDefault = 30; + this->aics[aicIndex].DefDiggingUnitMax = 2; + this->aics[aicIndex].OuterPatrolGroupsCount = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 10; + this->aics[aicIndex].RaidUnitsRandom = 10; + this->aics[aicIndex].recruitProbDefWeak = 70; + this->aics[aicIndex].recruitProbDefDefault = 50; + this->aics[aicIndex].recruitProbRaidDefault = 20; + this->aics[aicIndex].recruitProbAttackWeak = 20; + this->aics[aicIndex].recruitProbAttackStrong = 40; + this->aics[aicIndex].RecruitIntervalWeak = 8; + this->aics[aicIndex].RecruitInterval = 8; + this->aics[aicIndex].RecruitIntervalStrong = 4; + this->aics[aicIndex].SortieUnitRangedMin = 0; + this->aics[aicIndex].SortieUnitRanged = 72; + this->aics[aicIndex].SortieUnitMeleeMin = 3; + this->aics[aicIndex].SortieUnitMelee = 75; + this->aics[aicIndex].DefDiggingUnit = 71; + this->aics[aicIndex].DefWalls = 40; + this->aics[aicIndex].DefTotal = 50; + this->aics[aicIndex].OuterPatrolGroupsMove = 0; + this->aics[aicIndex].DefUnit1 = 72; + this->aics[aicIndex].DefUnit2 = 72; + this->aics[aicIndex].DefUnit3 = 75; + this->aics[aicIndex].RaidUnitsBase = 20; + this->aics[aicIndex].RaidUnit1 = 72; + this->aics[aicIndex].HarassingSiegeEnginesMax = 0; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 3; + this->aics[aicIndex].AttForceBase = 10; + this->aics[aicIndex].AttForceRandom = 20; + this->aics[aicIndex].AttForceSupportAllyThreshold = 5; + this->aics[aicIndex].AttForceRallyPercentage = 30; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 0; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 40; + this->aics[aicIndex].AttCoordinationPreference = 20; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 0; + this->aics[aicIndex].AttMaxEngineers = 0; + this->aics[aicIndex].AttDiggingUnit = 0; + this->aics[aicIndex].AttDiggingUnitMax = 0; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 70; + this->aics[aicIndex].AttUnitBackupMax = 120; + this->aics[aicIndex].AttUnitBackupGroupsCount = 1; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 0; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 75; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 1; + this->aics[aicIndex].TargetChoice = OpenSHC::AI::AIC::AITC_CLOSEST; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_08.cpp b/src/OpenSHC/AI/AICState/setAICParameters_08.cpp new file mode 100644 index 000000000..f040008a6 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_08.cpp @@ -0,0 +1,149 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + // FUNCTION: STRONGHOLDCRUSADER 0x004C8920 + void AICState::setAICParameters_08(int aicIndex) + { + this->aics[aicIndex].flagType = 13; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 0; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 8000; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 10; + this->aics[aicIndex].farm1 = 33; + this->aics[aicIndex].farm2 = 32; + this->aics[aicIndex].farm3 = 32; + this->aics[aicIndex].populationPerFarm = 4; + this->aics[aicIndex].unknown011 = 12; + this->aics[aicIndex].maxFood = 200; + this->aics[aicIndex].minimumApples = 5; + this->aics[aicIndex].minimumCheese = 5; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 5; + this->aics[aicIndex].tradeAmountEquipment = 2; + this->aics[aicIndex].aiRequestDelay = 22; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 10; + this->aics[aicIndex].doubleRationsFoodThreshold = 40; + this->aics[aicIndex].maxWood = 50; + this->aics[aicIndex].maxStone = 60; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 10; + this->aics[aicIndex].maxBeer = 10; + this->aics[aicIndex].maxResourceVariance = 4; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 10; + this->aics[aicIndex].populationPerIronmine = 10; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 3; + this->aics[aicIndex].maxIronmines = 4; + this->aics[aicIndex].maxWoodcutters = 10; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 15; + this->aics[aicIndex].buildInterval = 4; + this->aics[aicIndex].resourceRebuildDelay = 20; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 20; + this->aics[aicIndex].sellResource02 = 3; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource06 = 14; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].defWallPatrolRallyTime = 8; + this->aics[aicIndex].defWallPatrolGroups = 4; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 200; + this->aics[aicIndex].defSiegeEngineBuildDelay = 2; + this->aics[aicIndex].unknown072 = 100; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 90; + this->aics[aicIndex].recruitProbDefDefault = 30; + this->aics[aicIndex].recruitProbDefStrong = 10; + this->aics[aicIndex].recruitProbRaidWeak = 0; + this->aics[aicIndex].recruitProbRaidDefault = 0; + this->aics[aicIndex].recruitProbRaidStrong = 0; + this->aics[aicIndex].recruitProbAttackWeak = 10; + this->aics[aicIndex].recruitProbAttackDefault = 70; + this->aics[aicIndex].recruitProbAttackStrong = 90; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 1; + this->aics[aicIndex].RecruitIntervalStrong = 1; + this->aics[aicIndex].SortieUnitRangedMin = 6; + this->aics[aicIndex].SortieUnitRanged = 22; + this->aics[aicIndex].SortieUnitMeleeMin = 6; + this->aics[aicIndex].SortieUnitMelee = 25; + this->aics[aicIndex].DefDiggingUnitMax = 0; + this->aics[aicIndex].DefDiggingUnit = 25; + this->aics[aicIndex].DefWalls = 20; + this->aics[aicIndex].DefTotal = 40; + this->aics[aicIndex].OuterPatrolGroupsCount = 2; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 5; + this->aics[aicIndex].DefUnit1 = 22; + this->aics[aicIndex].DefUnit2 = 22; + this->aics[aicIndex].RaidUnitsBase = 10; + this->aics[aicIndex].RaidUnitsRandom = 4; + this->aics[aicIndex].DefUnit3 = 25; + this->aics[aicIndex].RaidUnit1 = 22; + this->aics[aicIndex].HarassingSiegeEnginesMax = 2; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 5; + this->aics[aicIndex].AttForceBase = 20; + this->aics[aicIndex].AttForceRandom = 5; + this->aics[aicIndex].AttForceSupportAllyThreshold = 10; + this->aics[aicIndex].AttForceRallyPercentage = 10; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 20; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 2; + this->aics[aicIndex].AttCoordinationPreference = 10; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 39; + this->aics[aicIndex].SiegeEngine2 = 39; + this->aics[aicIndex].SiegeEngine3 = 39; + this->aics[aicIndex].SiegeEngine4 = 59; + this->aics[aicIndex].SiegeEngine5 = 40; + this->aics[aicIndex].SiegeEngine6 = 40; + this->aics[aicIndex].AttMaxEngineers = 16; + this->aics[aicIndex].AttDiggingUnit = 25; + this->aics[aicIndex].AttDiggingUnitMax = 6; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 3; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 22; + this->aics[aicIndex].AttUnitBackupMax = 20; + this->aics[aicIndex].AttUnitBackupGroupsCount = 2; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 27; + this->aics[aicIndex].AttUnitSiegeDefMax = 4; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 2; + this->aics[aicIndex].AttUnitMain1 = 27; + this->aics[aicIndex].AttUnitMain2 = 25; + this->aics[aicIndex].AttUnitMain3 = 25; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 3; + this->aics[aicIndex].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 000000000..c7c59a899 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_09.cpp @@ -0,0 +1,146 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + // FUNCTION: STRONGHOLDCRUSADER 0x004C8D20 + void AICState::setAICParameters_09(int aicIndex) + { + this->aics[aicIndex].flagType = 13; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 5500; + this->aics[aicIndex].highestPopularity = 9000; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 10; + this->aics[aicIndex].farm1 = 33; + this->aics[aicIndex].farm2 = 32; + this->aics[aicIndex].farm3 = 31; + this->aics[aicIndex].populationPerFarm = 4; + this->aics[aicIndex].unknown011 = 12; + this->aics[aicIndex].maxFood = 200; + this->aics[aicIndex].minimumApples = 5; + this->aics[aicIndex].minimumCheese = 5; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 10; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 22; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 10; + this->aics[aicIndex].doubleRationsFoodThreshold = 40; + this->aics[aicIndex].maxWood = 60; + this->aics[aicIndex].maxStone = 60; + this->aics[aicIndex].maxResourceOther = 15; + this->aics[aicIndex].maxEquipment = 20; + this->aics[aicIndex].maxBeer = 10; + this->aics[aicIndex].maxResourceVariance = 4; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 12; + this->aics[aicIndex].populationPerIronmine = 10; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 4; + this->aics[aicIndex].maxIronmines = 5; + this->aics[aicIndex].maxWoodcutters = 12; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 12; + this->aics[aicIndex].buildInterval = 4; + this->aics[aicIndex].resourceRebuildDelay = 30; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = -999; + this->aics[aicIndex].poleturnerSetting = 20; + this->aics[aicIndex].sellResource04 = 7; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].defWallPatrolRallyTime = 8; + this->aics[aicIndex].defWallPatrolGroups = 2; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 200; + this->aics[aicIndex].defSiegeEngineBuildDelay = 2; + this->aics[aicIndex].unknown072 = 100; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 100; + this->aics[aicIndex].recruitProbDefDefault = 50; + this->aics[aicIndex].recruitProbDefStrong = 20; + this->aics[aicIndex].recruitProbRaidWeak = 0; + this->aics[aicIndex].recruitProbRaidDefault = 20; + this->aics[aicIndex].recruitProbRaidStrong = 30; + this->aics[aicIndex].recruitProbAttackWeak = 0; + this->aics[aicIndex].recruitProbAttackDefault = 30; + this->aics[aicIndex].recruitProbAttackStrong = 50; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 4; + this->aics[aicIndex].RecruitIntervalStrong = 4; + this->aics[aicIndex].SortieUnitRangedMin = 12; + this->aics[aicIndex].SortieUnitRanged = 22; + this->aics[aicIndex].SortieUnitMeleeMin = 4; + this->aics[aicIndex].SortieUnitMelee = 27; + this->aics[aicIndex].DefDiggingUnitMax = 10; + this->aics[aicIndex].DefDiggingUnit = 22; + this->aics[aicIndex].DefWalls = 50; + this->aics[aicIndex].DefTotal = 80; + this->aics[aicIndex].OuterPatrolGroupsCount = 2; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 10; + this->aics[aicIndex].DefUnit1 = 22; + this->aics[aicIndex].DefUnit2 = 22; + this->aics[aicIndex].RaidUnitsBase = 4; + this->aics[aicIndex].DefUnit3 = 23; + this->aics[aicIndex].RaidUnitsRandom = 0; + this->aics[aicIndex].RaidUnit1 = 28; + this->aics[aicIndex].HarassingSiegeEnginesMax = 2; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 6; + this->aics[aicIndex].AttForceBase = 30; + this->aics[aicIndex].AttForceRandom = 4; + this->aics[aicIndex].AttForceSupportAllyThreshold = 20; + this->aics[aicIndex].AttForceRallyPercentage = 50; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 20; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 5; + this->aics[aicIndex].AttCoordinationPreference = 50; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 40; + this->aics[aicIndex].SiegeEngine2 = 40; + this->aics[aicIndex].SiegeEngine3 = 40; + this->aics[aicIndex].SiegeEngine4 = 40; + this->aics[aicIndex].SiegeEngine5 = 40; + this->aics[aicIndex].SiegeEngine6 = 40; + this->aics[aicIndex].AttMaxEngineers = 18; + this->aics[aicIndex].AttDiggingUnit = 22; + this->aics[aicIndex].AttDiggingUnitMax = 20; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 23; + this->aics[aicIndex].AttUnitPatrolMax = 20; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 1; + this->aics[aicIndex].AttUnitBackup = 22; + this->aics[aicIndex].AttUnitBackupMax = 30; + this->aics[aicIndex].AttUnitBackupGroupsCount = 2; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 23; + this->aics[aicIndex].AttUnitSiegeDefMax = 10; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 1; + this->aics[aicIndex].AttUnitMain1 = 27; + this->aics[aicIndex].AttUnitMain2 = 27; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 4; + this->aics[aicIndex].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 000000000..bb6654eb7 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_10.cpp @@ -0,0 +1,147 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C9120 + void AICState::setAICParameters_10(int aicIndex) + { + this->aics[aicIndex].flagType = 13; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 8000; + this->aics[aicIndex].taxesMin = 2; + this->aics[aicIndex].taxesMax = 10; + this->aics[aicIndex].farm1 = 32; + this->aics[aicIndex].farm2 = 32; + this->aics[aicIndex].populationPerFarm = 5; + this->aics[aicIndex].unknown011 = 10; + this->aics[aicIndex].maxFood = 100; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = 10; + this->aics[aicIndex].minimumBread = 10; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 20; + this->aics[aicIndex].tradeAmountEquipment = 2; + this->aics[aicIndex].aiRequestDelay = 12; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 10; + this->aics[aicIndex].doubleRationsFoodThreshold = 50; + this->aics[aicIndex].maxWood = 60; + this->aics[aicIndex].maxStone = 60; + this->aics[aicIndex].maxResourceOther = 15; + this->aics[aicIndex].maxEquipment = 6; + this->aics[aicIndex].maxBeer = 10; + this->aics[aicIndex].maxResourceVariance = 4; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 0; + this->aics[aicIndex].populationPerIronmine = 7; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 1; + this->aics[aicIndex].maxIronmines = 3; + this->aics[aicIndex].maxWoodcutters = 10; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 8; + this->aics[aicIndex].buildInterval = 4; + this->aics[aicIndex].resourceRebuildDelay = 60; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource02 = 3; + this->aics[aicIndex].sellResource04 = 7; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource06 = 14; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].defWallPatrolRallyTime = 2; + this->aics[aicIndex].defWallPatrolGroups = 3; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 100; + this->aics[aicIndex].defSiegeEngineBuildDelay = 4; + this->aics[aicIndex].unknown072 = 200; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 70; + this->aics[aicIndex].recruitProbDefDefault = 40; + this->aics[aicIndex].recruitProbDefStrong = 20; + this->aics[aicIndex].recruitProbRaidWeak = 20; + this->aics[aicIndex].recruitProbRaidDefault = 40; + this->aics[aicIndex].recruitProbRaidStrong = 20; + this->aics[aicIndex].recruitProbAttackWeak = 10; + this->aics[aicIndex].recruitProbAttackDefault = 20; + this->aics[aicIndex].recruitProbAttackStrong = 60; + this->aics[aicIndex].RecruitIntervalWeak = 4; + this->aics[aicIndex].RecruitInterval = 4; + this->aics[aicIndex].RecruitIntervalStrong = 4; + this->aics[aicIndex].SortieUnitRangedMin = 2; + this->aics[aicIndex].SortieUnitRanged = 22; + this->aics[aicIndex].SortieUnitMeleeMin = 20; + this->aics[aicIndex].SortieUnitMelee = 24; + this->aics[aicIndex].DefDiggingUnitMax = 4; + this->aics[aicIndex].DefDiggingUnit = 24; + this->aics[aicIndex].DefWalls = 4; + this->aics[aicIndex].DefTotal = 20; + this->aics[aicIndex].OuterPatrolGroupsCount = 4; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 2; + this->aics[aicIndex].DefUnit1 = 28; + this->aics[aicIndex].DefUnit2 = 22; + this->aics[aicIndex].RaidUnitsBase = 4; + this->aics[aicIndex].RaidUnitsRandom = 4; + this->aics[aicIndex].DefUnit3 = 22; + this->aics[aicIndex].DefUnit4 = 22; + this->aics[aicIndex].DefUnit5 = 22; + this->aics[aicIndex].DefUnit6 = 22; + this->aics[aicIndex].RaidUnit1 = 28; + this->aics[aicIndex].HarassingSiegeEnginesMax = 1; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 4; + this->aics[aicIndex].AttForceBase = 10; + this->aics[aicIndex].AttForceRandom = 20; + this->aics[aicIndex].AttForceSupportAllyThreshold = 40; + this->aics[aicIndex].AttForceRallyPercentage = 40; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 0; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 5; + this->aics[aicIndex].AttCoordinationPreference = 70; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 0; + this->aics[aicIndex].AttMaxEngineers = 0; + this->aics[aicIndex].AttDiggingUnit = 24; + this->aics[aicIndex].AttDiggingUnitMax = 60; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 24; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 0; + this->aics[aicIndex].AttUnitBackupMax = 0; + this->aics[aicIndex].AttUnitBackupGroupsCount = 0; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 0; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 24; + this->aics[aicIndex].AttUnitMain2 = 24; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 2; + this->aics[aicIndex].TargetChoice = OpenSHC::AI::AIC::AITC_BALANCED; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_11.cpp b/src/OpenSHC/AI/AICState/setAICParameters_11.cpp new file mode 100644 index 000000000..8f4dcb309 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_11.cpp @@ -0,0 +1,145 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + // FUNCTION: STRONGHOLDCRUSADER 0x004C94F0 + void AICState::setAICParameters_11(int aicIndex) + { + this->aics[aicIndex].flagType = 10; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4500; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 8000; + this->aics[aicIndex].taxesMin = 2; + this->aics[aicIndex].taxesMax = 12; + this->aics[aicIndex].farm1 = 30; + this->aics[aicIndex].populationPerFarm = 5; + this->aics[aicIndex].unknown011 = 10; + this->aics[aicIndex].maxFood = 100; + this->aics[aicIndex].minimumApples = -1; + this->aics[aicIndex].minimumCheese = -1; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = 2; + this->aics[aicIndex].minimumHop = -1; + this->aics[aicIndex].tradeAmountFood = 20; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 22; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 30; + this->aics[aicIndex].doubleRationsFoodThreshold = 50; + this->aics[aicIndex].maxWood = 60; + this->aics[aicIndex].maxStone = 40; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 4; + this->aics[aicIndex].maxBeer = 10; + this->aics[aicIndex].maxResourceVariance = 4; + this->aics[aicIndex].recruitGoldThreshold = 100; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 6; + this->aics[aicIndex].populationPerIronmine = 12; + this->aics[aicIndex].populationPerPitchrig = 6; + this->aics[aicIndex].maxQuarries = 3; + this->aics[aicIndex].maxIronmines = 1; + this->aics[aicIndex].maxWoodcutters = 10; + this->aics[aicIndex].maxPitchrigs = 4; + this->aics[aicIndex].maxFarms = 8; + this->aics[aicIndex].buildInterval = 3; + this->aics[aicIndex].resourceRebuildDelay = 50; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource02 = 3; + this->aics[aicIndex].sellResource03 = 6; + this->aics[aicIndex].sellResource06 = 14; + this->aics[aicIndex].sellResource08 = 17; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 2; + this->aics[aicIndex].defWallPatrolGroups = 3; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 100; + this->aics[aicIndex].defSiegeEngineBuildDelay = 4; + this->aics[aicIndex].unknown072 = 200; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 70; + this->aics[aicIndex].recruitProbDefDefault = 30; + this->aics[aicIndex].recruitProbDefStrong = 20; + this->aics[aicIndex].recruitProbRaidWeak = 0; + this->aics[aicIndex].recruitProbRaidDefault = 20; + this->aics[aicIndex].recruitProbRaidStrong = 20; + this->aics[aicIndex].recruitProbAttackWeak = 30; + this->aics[aicIndex].recruitProbAttackDefault = 50; + this->aics[aicIndex].recruitProbAttackStrong = 60; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 1; + this->aics[aicIndex].RecruitIntervalStrong = 1; + this->aics[aicIndex].SortieUnitRangedMin = 6; + this->aics[aicIndex].SortieUnitRanged = 74; + this->aics[aicIndex].SortieUnitMeleeMin = 10; + this->aics[aicIndex].SortieUnitMelee = 75; + this->aics[aicIndex].DefDiggingUnitMax = 4; + this->aics[aicIndex].DefDiggingUnit = 71; + this->aics[aicIndex].DefWalls = 40; + this->aics[aicIndex].DefTotal = 40; + this->aics[aicIndex].OuterPatrolGroupsCount = 1; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 2; + this->aics[aicIndex].DefUnit1 = 70; + this->aics[aicIndex].DefUnit2 = 76; + this->aics[aicIndex].RaidUnitsBase = 30; + this->aics[aicIndex].RaidUnitsRandom = 10; + this->aics[aicIndex].RaidUnit1 = 74; + this->aics[aicIndex].HarassingSiegeEnginesMax = 4; + this->aics[aicIndex].HarassingSiegeEngine1 = 358; + this->aics[aicIndex].RaidRetargetDelay = 4; + this->aics[aicIndex].AttForceBase = 40; + this->aics[aicIndex].AttForceRandom = 5; + this->aics[aicIndex].AttForceSupportAllyThreshold = 30; + this->aics[aicIndex].AttForceRallyPercentage = 80; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 4; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 5; + this->aics[aicIndex].AttCoordinationPreference = 90; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 40; + this->aics[aicIndex].SiegeEngine2 = 77; + this->aics[aicIndex].SiegeEngine3 = 77; + this->aics[aicIndex].SiegeEngine4 = 77; + this->aics[aicIndex].AttMaxEngineers = 9; + this->aics[aicIndex].AttDiggingUnit = 71; + this->aics[aicIndex].AttDiggingUnitMax = 15; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 74; + this->aics[aicIndex].AttUnitBackupMax = 80; + this->aics[aicIndex].AttUnitBackupGroupsCount = 1; + this->aics[aicIndex].AttUnitEngageMax = 10; + this->aics[aicIndex].AttUnitEngage = 76; + this->aics[aicIndex].AttUnitSiegeDef = 0; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 75; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 2; + this->aics[aicIndex].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 000000000..8b658e7c9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_12.cpp @@ -0,0 +1,146 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + // FUNCTION: STRONGHOLDCRUSADER 0x004C9900 + void AICState::setAICParameters_12(int aicIndex) + { + this->aics[aicIndex].flagType = 10; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4500; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 9000; + this->aics[aicIndex].taxesMin = 2; + this->aics[aicIndex].taxesMax = 12; + this->aics[aicIndex].farm1 = 30; + this->aics[aicIndex].farm2 = 32; + this->aics[aicIndex].farm3 = 30; + this->aics[aicIndex].farm4 = 31; + this->aics[aicIndex].populationPerFarm = 4; + this->aics[aicIndex].unknown011 = 10; + this->aics[aicIndex].maxFood = 80; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = -1; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = 2; + this->aics[aicIndex].minimumHop = 2; + this->aics[aicIndex].tradeAmountFood = 20; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 12; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 10; + this->aics[aicIndex].doubleRationsFoodThreshold = 40; + this->aics[aicIndex].maxWood = 60; + this->aics[aicIndex].maxStone = 60; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 4; + this->aics[aicIndex].maxBeer = 10; + this->aics[aicIndex].maxResourceVariance = 4; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 6; + this->aics[aicIndex].populationPerIronmine = 10; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 4; + this->aics[aicIndex].maxIronmines = 3; + this->aics[aicIndex].maxWoodcutters = 10; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 16; + this->aics[aicIndex].buildInterval = 2; + this->aics[aicIndex].resourceRebuildDelay = 40; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 2; + this->aics[aicIndex].defWallPatrolGroups = 3; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 100; + this->aics[aicIndex].defSiegeEngineBuildDelay = 4; + this->aics[aicIndex].unknown072 = 200; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 80; + this->aics[aicIndex].recruitProbDefDefault = 40; + this->aics[aicIndex].recruitProbDefStrong = 20; + this->aics[aicIndex].recruitProbRaidWeak = 10; + this->aics[aicIndex].recruitProbRaidDefault = 20; + this->aics[aicIndex].recruitProbRaidStrong = 30; + this->aics[aicIndex].recruitProbAttackWeak = 10; + this->aics[aicIndex].recruitProbAttackDefault = 40; + this->aics[aicIndex].recruitProbAttackStrong = 50; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 0; + this->aics[aicIndex].RecruitIntervalStrong = 0; + this->aics[aicIndex].SortieUnitRangedMin = 4; + this->aics[aicIndex].SortieUnitRanged = 70; + this->aics[aicIndex].SortieUnitMeleeMin = 20; + this->aics[aicIndex].SortieUnitMelee = 75; + this->aics[aicIndex].DefDiggingUnitMax = 4; + this->aics[aicIndex].DefDiggingUnit = 22; + this->aics[aicIndex].DefWalls = 50; + this->aics[aicIndex].DefTotal = 50; + this->aics[aicIndex].OuterPatrolGroupsCount = 1; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 2; + this->aics[aicIndex].DefUnit1 = 22; + this->aics[aicIndex].DefUnit2 = 70; + this->aics[aicIndex].RaidUnitsBase = 30; + this->aics[aicIndex].RaidUnitsRandom = 10; + this->aics[aicIndex].RaidUnit1 = 75; + this->aics[aicIndex].HarassingSiegeEnginesMax = 3; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].HarassingSiegeEngine2 = 190; + this->aics[aicIndex].RaidRetargetDelay = 4; + this->aics[aicIndex].HarassingSiegeEngine3 = 358; + this->aics[aicIndex].AttForceBase = 30; + this->aics[aicIndex].AttForceRandom = 10; + this->aics[aicIndex].AttForceSupportAllyThreshold = 20; + this->aics[aicIndex].AttForceRallyPercentage = 80; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 0; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 5; + this->aics[aicIndex].AttCoordinationPreference = 80; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 39; + this->aics[aicIndex].SiegeEngine2 = 39; + this->aics[aicIndex].SiegeEngine3 = 61; + this->aics[aicIndex].AttMaxEngineers = 6; + this->aics[aicIndex].AttDiggingUnit = 22; + this->aics[aicIndex].AttDiggingUnitMax = 15; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 74; + this->aics[aicIndex].AttUnitBackupMax = 80; + this->aics[aicIndex].AttUnitBackupGroupsCount = 1; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 0; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 75; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 2; + this->aics[aicIndex].TargetChoice = OpenSHC::AI::AIC::AITC_GOLD; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_13.cpp b/src/OpenSHC/AI/AICState/setAICParameters_13.cpp new file mode 100644 index 000000000..d9b40524d --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_13.cpp @@ -0,0 +1,142 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + // FUNCTION: STRONGHOLDCRUSADER 0x004C9CF0 + void AICState::setAICParameters_13(int aicIndex) + { + this->aics[aicIndex].flagType = 10; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4500; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 9000; + this->aics[aicIndex].taxesMin = 2; + this->aics[aicIndex].taxesMax = 12; + this->aics[aicIndex].farm1 = 30; + this->aics[aicIndex].farm2 = 30; + this->aics[aicIndex].farm3 = 30; + this->aics[aicIndex].farm4 = 31; + this->aics[aicIndex].populationPerFarm = 3; + this->aics[aicIndex].unknown011 = 10; + this->aics[aicIndex].maxFood = 100; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = -1; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = 5; + this->aics[aicIndex].minimumHop = 5; + this->aics[aicIndex].tradeAmountFood = 10; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 12; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 30; + this->aics[aicIndex].doubleRationsFoodThreshold = 30; + this->aics[aicIndex].maxWood = 50; + this->aics[aicIndex].maxStone = 40; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 4; + this->aics[aicIndex].maxBeer = 10; + this->aics[aicIndex].maxResourceVariance = 4; + this->aics[aicIndex].recruitGoldThreshold = 100; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 20; + this->aics[aicIndex].populationPerIronmine = 10; + this->aics[aicIndex].populationPerPitchrig = 15; + this->aics[aicIndex].maxQuarries = 1; + this->aics[aicIndex].maxIronmines = 2; + this->aics[aicIndex].maxWoodcutters = 12; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 16; + this->aics[aicIndex].buildInterval = 1; + this->aics[aicIndex].resourceRebuildDelay = 20; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource03 = 6; + this->aics[aicIndex].sellResource08 = 17; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 2; + this->aics[aicIndex].defWallPatrolGroups = 3; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 100; + this->aics[aicIndex].defSiegeEngineBuildDelay = 4; + this->aics[aicIndex].unknown072 = 200; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 80; + this->aics[aicIndex].recruitProbDefDefault = 50; + this->aics[aicIndex].recruitProbDefStrong = 20; + this->aics[aicIndex].recruitProbRaidWeak = 0; + this->aics[aicIndex].recruitProbRaidDefault = 20; + this->aics[aicIndex].recruitProbRaidStrong = 30; + this->aics[aicIndex].recruitProbAttackWeak = 20; + this->aics[aicIndex].recruitProbAttackDefault = 30; + this->aics[aicIndex].recruitProbAttackStrong = 50; + this->aics[aicIndex].RecruitIntervalWeak = 8; + this->aics[aicIndex].RecruitInterval = 4; + this->aics[aicIndex].RecruitIntervalStrong = 1; + this->aics[aicIndex].SortieUnitRangedMin = 5; + this->aics[aicIndex].SortieUnitRanged = 70; + this->aics[aicIndex].SortieUnitMeleeMin = 15; + this->aics[aicIndex].SortieUnitMelee = 70; + this->aics[aicIndex].DefDiggingUnitMax = 60; + this->aics[aicIndex].DefDiggingUnit = 71; + this->aics[aicIndex].DefWalls = 80; + this->aics[aicIndex].DefTotal = 80; + this->aics[aicIndex].OuterPatrolGroupsCount = 1; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 2; + this->aics[aicIndex].DefUnit1 = 70; + this->aics[aicIndex].RaidUnitsBase = 5; + this->aics[aicIndex].RaidUnitsRandom = 5; + this->aics[aicIndex].RaidUnit1 = 73; + this->aics[aicIndex].HarassingSiegeEnginesMax = 0; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 4; + this->aics[aicIndex].AttForceBase = 40; + this->aics[aicIndex].AttForceRandom = 0; + this->aics[aicIndex].AttForceSupportAllyThreshold = 10; + this->aics[aicIndex].AttForceRallyPercentage = 50; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 0; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 5; + this->aics[aicIndex].AttCoordinationPreference = 50; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 39; + this->aics[aicIndex].AttMaxEngineers = 0; + this->aics[aicIndex].AttDiggingUnit = 71; + this->aics[aicIndex].AttDiggingUnitMax = 20; + this->aics[aicIndex].AttMaxAssassins = 40; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 74; + this->aics[aicIndex].AttUnitBackupMax = 0; + this->aics[aicIndex].AttUnitBackupGroupsCount = 0; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 0; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 73; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 4; + this->aics[aicIndex].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 000000000..551c1a0f1 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_14.cpp @@ -0,0 +1,150 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CA0C0 + void AICState::setAICParameters_14(int aicIndex) + { + this->aics[aicIndex].flagType = 13; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 6000; + this->aics[aicIndex].highestPopularity = 9000; + this->aics[aicIndex].taxesMin = 3; + this->aics[aicIndex].taxesMax = 11; + this->aics[aicIndex].farm1 = 32; + this->aics[aicIndex].farm2 = 31; + this->aics[aicIndex].farm3 = 33; + this->aics[aicIndex].populationPerFarm = 4; + this->aics[aicIndex].unknown011 = 10; + this->aics[aicIndex].maxFood = 300; + this->aics[aicIndex].minimumApples = 20; + this->aics[aicIndex].minimumCheese = 20; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = 2; + this->aics[aicIndex].tradeAmountFood = 10; + this->aics[aicIndex].tradeAmountEquipment = 2; + this->aics[aicIndex].aiRequestDelay = 40; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 40; + this->aics[aicIndex].doubleRationsFoodThreshold = 40; + this->aics[aicIndex].maxWood = 50; + this->aics[aicIndex].maxStone = 50; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 10; + this->aics[aicIndex].maxBeer = 10; + this->aics[aicIndex].maxResourceVariance = 5; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 10; + this->aics[aicIndex].populationPerIronmine = 12; + this->aics[aicIndex].populationPerPitchrig = 15; + this->aics[aicIndex].maxQuarries = 3; + this->aics[aicIndex].maxIronmines = 6; + this->aics[aicIndex].maxWoodcutters = 10; + this->aics[aicIndex].maxPitchrigs = 4; + this->aics[aicIndex].maxFarms = 10; + this->aics[aicIndex].buildInterval = 3; + this->aics[aicIndex].resourceRebuildDelay = 8; + this->aics[aicIndex].blacksmithSetting = 21; + this->aics[aicIndex].fletcherSetting = 18; + this->aics[aicIndex].poleturnerSetting = 20; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource08 = 17; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 8; + this->aics[aicIndex].defWallPatrolGroups = 2; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 200; + this->aics[aicIndex].defSiegeEngineBuildDelay = 4; + this->aics[aicIndex].unknown072 = 200; + this->aics[aicIndex].unknown073 = 30; + this->aics[aicIndex].recruitProbDefWeak = 100; + this->aics[aicIndex].recruitProbDefDefault = 50; + this->aics[aicIndex].recruitProbDefStrong = 30; + this->aics[aicIndex].recruitProbRaidWeak = 0; + this->aics[aicIndex].recruitProbRaidDefault = 30; + this->aics[aicIndex].recruitProbRaidStrong = 50; + this->aics[aicIndex].recruitProbAttackWeak = 0; + this->aics[aicIndex].recruitProbAttackDefault = 20; + this->aics[aicIndex].recruitProbAttackStrong = 20; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 4; + this->aics[aicIndex].RecruitIntervalStrong = 4; + this->aics[aicIndex].SortieUnitRangedMin = 10; + this->aics[aicIndex].SortieUnitRanged = 23; + this->aics[aicIndex].SortieUnitMeleeMin = 10; + this->aics[aicIndex].SortieUnitMelee = 26; + this->aics[aicIndex].DefDiggingUnitMax = 4; + this->aics[aicIndex].DefDiggingUnit = 26; + this->aics[aicIndex].DefWalls = 40; + this->aics[aicIndex].DefTotal = 50; + this->aics[aicIndex].OuterPatrolGroupsCount = 1; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 5; + this->aics[aicIndex].DefUnit1 = 23; + this->aics[aicIndex].DefUnit2 = 23; + this->aics[aicIndex].RaidUnitsBase = 10; + this->aics[aicIndex].RaidUnitsRandom = 4; + this->aics[aicIndex].DefUnit3 = 23; + this->aics[aicIndex].DefUnit4 = 76; + this->aics[aicIndex].DefUnit5 = 73; + this->aics[aicIndex].RaidUnit1 = 26; + this->aics[aicIndex].HarassingSiegeEnginesMax = 5; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].HarassingSiegeEngine2 = 358; + this->aics[aicIndex].RaidRetargetDelay = 4; + this->aics[aicIndex].AttForceBase = 50; + this->aics[aicIndex].AttForceRandom = 20; + this->aics[aicIndex].AttForceSupportAllyThreshold = 50; + this->aics[aicIndex].AttForceRallyPercentage = 80; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 5; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 8; + this->aics[aicIndex].AttCoordinationPreference = 50; + this->aics[aicIndex].CowThrowInterval = 6; + this->aics[aicIndex].SiegeEngine1 = 39; + this->aics[aicIndex].SiegeEngine2 = 39; + this->aics[aicIndex].SiegeEngine3 = 77; + this->aics[aicIndex].SiegeEngine4 = 77; + this->aics[aicIndex].SiegeEngine5 = 77; + this->aics[aicIndex].AttMaxEngineers = 10; + this->aics[aicIndex].AttDiggingUnit = 71; + this->aics[aicIndex].AttDiggingUnitMax = 80; + this->aics[aicIndex].AttMaxAssassins = 10; + this->aics[aicIndex].AttUnit2 = 26; + this->aics[aicIndex].AttUnit2Max = 10; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 76; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 23; + this->aics[aicIndex].AttUnitBackupMax = 50; + this->aics[aicIndex].AttUnitBackupGroupsCount = 1; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 26; + this->aics[aicIndex].AttUnitSiegeDefMax = 6; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 1; + this->aics[aicIndex].AttUnitMain1 = 26; + this->aics[aicIndex].AttUnitMain2 = 26; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 3; + this->aics[aicIndex].TargetChoice = OpenSHC::AI::AIC::AITC_CLOSEST; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setAICParameters_15.cpp b/src/OpenSHC/AI/AICState/setAICParameters_15.cpp new file mode 100644 index 000000000..b75bf7c2e --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_15.cpp @@ -0,0 +1,142 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIC/AITargetChoice.hpp" + +namespace OpenSHC { +namespace AI { + + using OpenSHC::AI::AIC::AITargetChoice; + // FUNCTION: STRONGHOLDCRUSADER 0x004CA4D0 + void AICState::setAICParameters_15(int aicIndex) + { + this->aics[aicIndex].flagType = 13; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4000; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 8000; + this->aics[aicIndex].taxesMin = 2; + this->aics[aicIndex].taxesMax = 10; + this->aics[aicIndex].farm1 = 33; + this->aics[aicIndex].farm2 = 31; + this->aics[aicIndex].farm3 = 33; + this->aics[aicIndex].farm4 = 33; + this->aics[aicIndex].populationPerFarm = 5; + this->aics[aicIndex].unknown011 = 10; + this->aics[aicIndex].maxFood = 100; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = 10; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = -1; + this->aics[aicIndex].minimumHop = 5; + this->aics[aicIndex].tradeAmountFood = 20; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 12; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 0; + this->aics[aicIndex].doubleRationsFoodThreshold = 80; + this->aics[aicIndex].maxWood = 60; + this->aics[aicIndex].maxStone = 50; + this->aics[aicIndex].maxResourceOther = 12; + this->aics[aicIndex].maxEquipment = 10; + this->aics[aicIndex].maxBeer = 6; + this->aics[aicIndex].maxResourceVariance = 4; + this->aics[aicIndex].recruitGoldThreshold = 200; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 7; + this->aics[aicIndex].populationPerIronmine = 7; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 1; + this->aics[aicIndex].maxIronmines = 3; + this->aics[aicIndex].maxWoodcutters = 8; + this->aics[aicIndex].maxPitchrigs = 1; + this->aics[aicIndex].maxFarms = 9; + this->aics[aicIndex].buildInterval = 3; + this->aics[aicIndex].resourceRebuildDelay = 40; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource04 = 7; + this->aics[aicIndex].sellResource05 = 9; + this->aics[aicIndex].sellResource07 = 16; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].defWallPatrolRallyTime = 2; + this->aics[aicIndex].defWallPatrolGroups = 3; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 100; + this->aics[aicIndex].defSiegeEngineBuildDelay = 4; + this->aics[aicIndex].unknown072 = 200; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 50; + this->aics[aicIndex].recruitProbDefDefault = 30; + this->aics[aicIndex].recruitProbDefStrong = 20; + this->aics[aicIndex].recruitProbRaidWeak = 40; + this->aics[aicIndex].recruitProbRaidDefault = 40; + this->aics[aicIndex].recruitProbRaidStrong = 30; + this->aics[aicIndex].recruitProbAttackWeak = 10; + this->aics[aicIndex].recruitProbAttackDefault = 30; + this->aics[aicIndex].recruitProbAttackStrong = 50; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 1; + this->aics[aicIndex].RecruitIntervalStrong = 4; + this->aics[aicIndex].SortieUnitRangedMin = 4; + this->aics[aicIndex].SortieUnitRanged = 22; + this->aics[aicIndex].SortieUnitMeleeMin = 4; + this->aics[aicIndex].SortieUnitMelee = 28; + this->aics[aicIndex].DefDiggingUnitMax = 4; + this->aics[aicIndex].DefDiggingUnit = 22; + this->aics[aicIndex].DefWalls = 30; + this->aics[aicIndex].DefTotal = 40; + this->aics[aicIndex].OuterPatrolGroupsCount = 1; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 2; + this->aics[aicIndex].DefUnit1 = 22; + this->aics[aicIndex].RaidUnitsBase = 16; + this->aics[aicIndex].RaidUnitsRandom = 4; + this->aics[aicIndex].RaidUnit1 = 28; + this->aics[aicIndex].HarassingSiegeEnginesMax = 1; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].RaidRetargetDelay = 4; + this->aics[aicIndex].AttForceBase = 10; + this->aics[aicIndex].AttForceRandom = 20; + this->aics[aicIndex].AttForceSupportAllyThreshold = 10; + this->aics[aicIndex].AttForceRallyPercentage = 50; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 0; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 5; + this->aics[aicIndex].AttCoordinationPreference = 70; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 40; + this->aics[aicIndex].SiegeEngine2 = 40; + this->aics[aicIndex].AttMaxEngineers = 6; + this->aics[aicIndex].AttDiggingUnit = 22; + this->aics[aicIndex].AttDiggingUnitMax = 20; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 22; + this->aics[aicIndex].AttUnitBackupMax = 30; + this->aics[aicIndex].AttUnitBackupGroupsCount = 3; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 0; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 27; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 2; + this->aics[aicIndex].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 000000000..b6836e4e7 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAICParameters_16.cpp @@ -0,0 +1,144 @@ +#include "../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 aicIndex) + { + this->aics[aicIndex].flagType = 13; + this->aics[aicIndex].unknown002 = 0; + this->aics[aicIndex].unknown001 = 0; + this->aics[aicIndex].unknown003 = 0; + this->aics[aicIndex].unknown004 = 1; + this->aics[aicIndex].unknown005 = 1; + this->aics[aicIndex].criticalPopularity = 4500; + this->aics[aicIndex].lowestPopularity = 5000; + this->aics[aicIndex].highestPopularity = 9000; + this->aics[aicIndex].taxesMin = 2; + this->aics[aicIndex].taxesMax = 12; + this->aics[aicIndex].farm1 = 31; + this->aics[aicIndex].farm2 = 30; + this->aics[aicIndex].farm3 = 30; + this->aics[aicIndex].populationPerFarm = 4; + this->aics[aicIndex].unknown011 = 10; + this->aics[aicIndex].maxFood = 100; + this->aics[aicIndex].minimumApples = 10; + this->aics[aicIndex].minimumCheese = -1; + this->aics[aicIndex].minimumBread = -1; + this->aics[aicIndex].minimumWheat = 2; + this->aics[aicIndex].minimumHop = 2; + this->aics[aicIndex].tradeAmountFood = 5; + this->aics[aicIndex].tradeAmountEquipment = 0; + this->aics[aicIndex].aiRequestDelay = 12; + this->aics[aicIndex].minimumGoodsRequiredAfterTrade = 10; + this->aics[aicIndex].doubleRationsFoodThreshold = 40; + this->aics[aicIndex].maxWood = 60; + this->aics[aicIndex].maxStone = 40; + this->aics[aicIndex].maxResourceOther = 10; + this->aics[aicIndex].maxEquipment = 4; + this->aics[aicIndex].maxBeer = 5; + this->aics[aicIndex].maxResourceVariance = 4; + this->aics[aicIndex].recruitGoldThreshold = 50; + this->aics[aicIndex].populationPerWoodcutter = 7; + this->aics[aicIndex].populationPerQuarry = 6; + this->aics[aicIndex].populationPerIronmine = 10; + this->aics[aicIndex].populationPerPitchrig = 0; + this->aics[aicIndex].maxQuarries = 4; + this->aics[aicIndex].maxIronmines = 2; + this->aics[aicIndex].maxWoodcutters = 10; + this->aics[aicIndex].maxPitchrigs = 0; + this->aics[aicIndex].maxFarms = 12; + this->aics[aicIndex].buildInterval = 2; + this->aics[aicIndex].resourceRebuildDelay = 40; + this->aics[aicIndex].blacksmithSetting = 22; + this->aics[aicIndex].fletcherSetting = 17; + this->aics[aicIndex].poleturnerSetting = 19; + this->aics[aicIndex].sellResource03 = 6; + this->aics[aicIndex].sellResource09 = 18; + this->aics[aicIndex].sellResource10 = 19; + this->aics[aicIndex].sellResource11 = 20; + this->aics[aicIndex].sellResource12 = 21; + this->aics[aicIndex].sellResource13 = 22; + this->aics[aicIndex].sellResource14 = 23; + this->aics[aicIndex].sellResource15 = 24; + this->aics[aicIndex].defWallPatrolRallyTime = 2; + this->aics[aicIndex].defWallPatrolGroups = 3; + this->aics[aicIndex].defSiegeEngineGoldThreshold = 100; + this->aics[aicIndex].defSiegeEngineBuildDelay = 4; + this->aics[aicIndex].unknown072 = 200; + this->aics[aicIndex].unknown073 = 20; + this->aics[aicIndex].recruitProbDefWeak = 80; + this->aics[aicIndex].recruitProbDefDefault = 40; + this->aics[aicIndex].recruitProbDefStrong = 20; + this->aics[aicIndex].recruitProbRaidWeak = 10; + this->aics[aicIndex].RecruitIntervalWeak = 1; + this->aics[aicIndex].RecruitInterval = 1; + this->aics[aicIndex].RecruitIntervalStrong = 1; + this->aics[aicIndex].SortieUnitRanged = 22; + this->aics[aicIndex].DefDiggingUnit = 22; + this->aics[aicIndex].OuterPatrolGroupsCount = 1; + this->aics[aicIndex].OuterPatrolGroupsMove = 1; + this->aics[aicIndex].OuterPatrolRallyDelay = 2; + this->aics[aicIndex].DefUnit1 = 22; + this->aics[aicIndex].recruitProbRaidDefault = 20; + this->aics[aicIndex].recruitProbRaidStrong = 30; + this->aics[aicIndex].recruitProbAttackWeak = 10; + this->aics[aicIndex].recruitProbAttackDefault = 40; + this->aics[aicIndex].recruitProbAttackStrong = 50; + this->aics[aicIndex].SortieUnitRangedMin = 4; + this->aics[aicIndex].SortieUnitMeleeMin = 20; + this->aics[aicIndex].SortieUnitMelee = 37; + this->aics[aicIndex].DefDiggingUnitMax = 4; + this->aics[aicIndex].DefWalls = 30; + this->aics[aicIndex].DefTotal = 30; + this->aics[aicIndex].RaidUnitsBase = 30; + this->aics[aicIndex].RaidUnitsRandom = 10; + this->aics[aicIndex].RaidUnit1 = 37; + this->aics[aicIndex].HarassingSiegeEnginesMax = 5; + this->aics[aicIndex].RaidRetargetDelay = 4; + this->aics[aicIndex].HarassingSiegeEngine1 = 190; + this->aics[aicIndex].AttForceBase = 50; + this->aics[aicIndex].AttForceRandom = 10; + this->aics[aicIndex].AttForceSupportAllyThreshold = 40; + this->aics[aicIndex].AttForceRallyPercentage = 80; + this->aics[aicIndex].AttMovingTribesThreshold = 100; + this->aics[aicIndex].AttAssaultDelay = 0; + this->aics[aicIndex].AttUnitPatrolRecommandDelay = 5; + this->aics[aicIndex].AttCoordinationPreference = 80; + this->aics[aicIndex].CowThrowInterval = 10000; + this->aics[aicIndex].SiegeEngine1 = 39; + this->aics[aicIndex].SiegeEngine2 = 39; + this->aics[aicIndex].SiegeEngine3 = 39; + this->aics[aicIndex].SiegeEngine4 = 39; + this->aics[aicIndex].AttMaxEngineers = 8; + this->aics[aicIndex].AttDiggingUnit = 22; + this->aics[aicIndex].AttDiggingUnitMax = 10; + this->aics[aicIndex].AttMaxAssassins = 0; + this->aics[aicIndex].AttUnit2 = 0; + this->aics[aicIndex].AttUnit2Max = 0; + this->aics[aicIndex].AttMaxLaddermen = 0; + this->aics[aicIndex].AttMaxTunnelers = 0; + this->aics[aicIndex].AttUnitPatrol = 0; + this->aics[aicIndex].AttUnitPatrolMax = 0; + this->aics[aicIndex].AttUnitPatrolGroupsCount = 0; + this->aics[aicIndex].AttUnitBackup = 22; + this->aics[aicIndex].AttUnitBackupMax = 40; + this->aics[aicIndex].AttUnitBackupGroupsCount = 1; + this->aics[aicIndex].AttUnitEngageMax = 0; + this->aics[aicIndex].AttUnitEngage = 0; + this->aics[aicIndex].AttUnitSiegeDef = 0; + this->aics[aicIndex].AttUnitSiegeDefMax = 0; + this->aics[aicIndex].AttUnitSiegeDefGroupsCount = 0; + this->aics[aicIndex].AttUnitMain1 = 37; + this->aics[aicIndex].AttMaxDefault = 200; + this->aics[aicIndex].AttMainGroupsCount = 2; + this->aics[aicIndex].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 000000000..8e9c2562f --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAttackCoordinationLevel.cpp @@ -0,0 +1,33 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Random/RNG.func.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) { + return; + } + int willingnessPercentage = this->aics[aiType - 1].AttCoordinationPreference + + DAT_GameState::instance.playerDataArray[playerID].currentAttackWave * 4; + if (90 < willingnessPercentage) { + willingnessPercentage = 90; + } + int const rngMod100 = 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 = rngMod100 < willingnessPercentage; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setAttackPrimeInfo.cpp b/src/OpenSHC/AI/AICState/setAttackPrimeInfo.cpp new file mode 100644 index 000000000..545fa5f44 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setAttackPrimeInfo.cpp @@ -0,0 +1,25 @@ +#include "../AICState.func.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 const tile = DAT_GameState::instance.playerDataArray[playerID].shortestDistanceTile; + DAT_TroopValueState::instance.attackInfo.startZone = (char)DAT_TileMapState::instance.AIZoneLayer[tile]; + DAT_TroopValueState::instance.attackInfo.startCon = (short)DAT_TileMapState::instance.PathConnectionLayer[tile]; + DAT_TroopValueState::instance.attackInfo.zoneSize + = DAT_PathFindingState::instance.zoneSizesArray[DAT_TroopValueState::instance.attackInfo.startCon]; + DAT_TroopValueState::instance.attackInfo.unknownOne_0x20f90 = 1; + DAT_TroopValueState::instance.attackInfo.keepCon + = (short)DAT_TileMapState::instance + .PathConnectionLayer[DAT_GameState::instance.playerDataArray[attackedPlayerID].campground.tileEntry]; + } +} +} diff --git a/src/OpenSHC/AI/AICState/setCurrentAttackStrength.cpp b/src/OpenSHC/AI/AICState/setCurrentAttackStrength.cpp new file mode 100644 index 000000000..d73ebde3b --- /dev/null +++ b/src/OpenSHC/AI/AICState/setCurrentAttackStrength.cpp @@ -0,0 +1,38 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Random/RNG.func.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) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) + return; + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + int const randomPercent = SEC_RNG::instance.currentNumber2 % 100; + MACRO_CALL_MEMBER(Random::RNG_Func::nextRandomNumber2, SEC_RNG::ptr)(); + + DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength + = (this->aics[aicIndex].AttForceRandom * randomPercent) / 100; + DAT_GameState::instance.playerDataArray[playerID].currentAttackWave++; + + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] > 10000) { + DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength + += DAT_GameState::instance.playerDataArray[playerID].currentAttackWave * 7; + } else { + DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength + += DAT_GameState::instance.playerDataArray[playerID].currentAttackWave * 5; + } + + if (DAT_GameState::instance.playerDataArray[playerID].currentWaveRandomAttackingStrength > 200) { + 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 000000000..b35e4d414 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setEightTribe0xc0TribesToAggressiveAndAttack.cpp @@ -0,0 +1,39 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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) { + return; + } + + for (int i = 0; i < 8; i++) { + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[i + 0xc0]; + if (tribeID == 0) { + continue; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[i + 0xc0]) { + continue; + } + if (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID == 0) { + continue; + } + 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/setFoodBuyPlan.cpp b/src/OpenSHC/AI/AICState/setFoodBuyPlan.cpp index d556d1026..780b9a3d4 100644 --- a/src/OpenSHC/AI/AICState/setFoodBuyPlan.cpp +++ b/src/OpenSHC/AI/AICState/setFoodBuyPlan.cpp @@ -9,14 +9,16 @@ namespace OpenSHC { namespace AI { + using OpenSHC::AI::AITypeA; + using OpenSHC::Game::Resources::ResourceType; + // FUNCTION: STRONGHOLDCRUSADER 0x004CB060 void AICState::setFoodBuyPlan(int playerID) { - int const aiType = DAT_GameState::ptr->playerDataArray[playerID].aiType; // Early return if not an AI player (human player has aiType == 0) - if (aiType == AITA_NULL) { + if (aiType == AITypeA::AITA_NULL) { return; } @@ -28,36 +30,36 @@ namespace AI { // Apples: special handling with >= 0 check (allows -1 to disable) int const minimumApples = this->aics[aiConfigIndex].minimumApples; int* currentResources = DAT_GameState::ptr->playerDataArray[playerID].currentResources; - if (minimumApples >= 0 && currentResources[Game::Resources::RT_APPLE] < minimumApples) { - DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_APPLE] + if (minimumApples >= 0 && currentResources[ResourceType::RT_APPLE] < minimumApples) { + DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_APPLE] = this->aics[aiConfigIndex].tradeAmountFood; } // Cheese: standard check with > 0 int const minimumCheese = this->aics[aiConfigIndex].minimumCheese; - if (minimumCheese > 0 && currentResources[Game::Resources::RT_CHEESE] < minimumCheese) { - DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_CHEESE] + if (minimumCheese > 0 && currentResources[ResourceType::RT_CHEESE] < minimumCheese) { + DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_CHEESE] = this->aics[aiConfigIndex].tradeAmountFood; } // Bread: standard check with > 0 int const minimumBread = this->aics[aiConfigIndex].minimumBread; - if (minimumBread > 0 && currentResources[Game::Resources::RT_BREAD] < minimumBread) { - DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_BREAD] + if (minimumBread > 0 && currentResources[ResourceType::RT_BREAD] < minimumBread) { + DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_BREAD] = this->aics[aiConfigIndex].tradeAmountFood; } // Wheat: standard check with > 0 int const minimumWheat = this->aics[aiConfigIndex].minimumWheat; - if (minimumWheat > 0 && currentResources[Game::Resources::RT_WHEAT] < minimumWheat) { - DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_WHEAT] + if (minimumWheat > 0 && currentResources[ResourceType::RT_WHEAT] < minimumWheat) { + DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_WHEAT] = this->aics[aiConfigIndex].tradeAmountFood; } // Hops: standard check with > 0 int const minimumHop = this->aics[aiConfigIndex].minimumHop; - if (minimumHop > 0 && currentResources[Game::Resources::RT_HOPS] < minimumHop) { - DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[Game::Resources::RT_HOPS] + if (minimumHop > 0 && currentResources[ResourceType::RT_HOPS] < minimumHop) { + DAT_GameState::ptr->playerDataArray[playerID].resourcesToAcquireArray[ResourceType::RT_HOPS] = 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 000000000..9ab0b2c85 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setInitialAIC.cpp @@ -0,0 +1,45 @@ +#include "../AICState.func.hpp" + +namespace OpenSHC { +namespace AI { + + // 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); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setNextFarmTypeToBuild.cpp b/src/OpenSHC/AI/AICState/setNextFarmTypeToBuild.cpp new file mode 100644 index 000000000..80c64fcce --- /dev/null +++ b/src/OpenSHC/AI/AICState/setNextFarmTypeToBuild.cpp @@ -0,0 +1,26 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AITypeA.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB1B0 + int AICState::setNextFarmTypeToBuild(int playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == AITA_NULL) + return 0; + int const aicIndex = aiType - 1; + + DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex++; + if (DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex >= 8) + DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex = 0; + if ((&this->aics[aicIndex].farm1)[DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex] == 0) + DAT_GameState::instance.playerDataArray[playerID].aiFarmIndex = 0; + return (&this->aics[aicIndex].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 000000000..18a10f4d9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/setNextMoveLocationForUnits.cpp @@ -0,0 +1,38 @@ +#include "../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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CE3B0 + void AICState::setNextMoveLocationForUnits(int playerID) + { + int const organismID + = (short)DAT_TileMapState::instance.OrganismLayer + [DAT_ViewportRenderState::instance + .translationMatrix[DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY] + .addXgetTile + + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX]; + if (organismID != 0) { + MACRO_CALL_MEMBER(Map::LandscapeState_Func::removeTree, DAT_LandscapeState::ptr)(organismID); + } + + MACRO_CALL_MEMBER(Map::Navigation::PathFindingState_Func::findLinkageBasedPathOrWalkRadius, + DAT_PathFindingState::ptr)(DAT_GameState::instance.playerDataArray[playerID].shortestDistanceX, + DAT_GameState::instance.playerDataArray[playerID].shortestDistanceY, -1, -1, 4000, FALSE); + MACRO_CALL_MEMBER(Map::Navigation::PathFindingState_Func::setDestinationPairsBasedOnPreviousSearch, + DAT_PathFindingState::ptr)( + playerID - 1, DAT_GameState::instance.playerDataArray[playerID].attackedPlayerID); + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setTargetForTribeBasedOnTargetChoiceLogic.cpp b/src/OpenSHC/AI/AICState/setTargetForTribeBasedOnTargetChoiceLogic.cpp new file mode 100644 index 000000000..10ebf610c --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTargetForTribeBasedOnTargetChoiceLogic.cpp @@ -0,0 +1,106 @@ +#include "../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/Game/Resources/ResourceType.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 { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CACB0 + void AICState::setTargetForTribeBasedOnTargetChoiceLogic(int owner, int tribe) + { + if (DAT_GameState::instance.playerDataArray[owner].aiType == 0) { + return; + } + + int const aicIndex = DAT_GameState::instance.playerDataArray[owner].aiType - 1; + + int targetPlayer = 0; + int minDistance = 10000; + int maxGold = 0; + int minPoints = 1000000; + int const ownerKeepX = DAT_GameState::instance.playerDataArray[owner].campground.xEntry; + int const ownerKeepY = DAT_GameState::instance.playerDataArray[owner].campground.yEntry; + for (int player = 1; player < 9; player++) { + if (DAT_GameState::instance.mapAndTime.playerTeams[player] + == DAT_GameState::instance.mapAndTime.playerTeams[owner]) { + continue; + } + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(player) + == 0) { + continue; + } + + MACRO_CALL_MEMBER(Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)(DAT_GameState::instance.playerDataArray[player].campground.xEntry, + DAT_GameState::instance.playerDataArray[player].campground.yEntry, ownerKeepX, ownerKeepY); + + if (this->aics[aicIndex].TargetChoice == AIC::AITC_PLAYER) { + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[player] != -1 + && DAT_DirectionAlgorithmState::instance.distanceHigh <= minDistance) { + minDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + targetPlayer = player; + } + } else if (this->aics[aicIndex].TargetChoice == AIC::AITC_CLOSEST) { + if (DAT_DirectionAlgorithmState::instance.distanceHigh <= minDistance) { + minDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + targetPlayer = player; + } + } else if (this->aics[aicIndex].TargetChoice == AIC::AITC_GOLD) { + if (DAT_GameState::instance.playerDataArray[player].currentResources[Game::Resources::RT_GOLD] + >= maxGold) { + maxGold + = DAT_GameState::instance.playerDataArray[player].currentResources[Game::Resources::RT_GOLD]; + targetPlayer = player; + } + } else if (this->aics[aicIndex].TargetChoice == AIC::AITC_BALANCED) { + int const points = DAT_GameState::instance.playerDataArray[player].currentPopulation * 5 + + DAT_GameState::instance.playerDataArray[player].totalTroopValue + + DAT_GameState::instance.playerDataArray[player].currentResources[Game::Resources::RT_GOLD] / 100 + + DAT_DirectionAlgorithmState::instance.distanceHigh * 2; + if (points <= minPoints) { + minPoints = points; + targetPlayer = player; + } + } + } + + if (targetPlayer == 0) { + // Fallback: pick the closest enemy with a living lord + for (int player = 1; player < 9; player++) { + if (DAT_GameState::instance.mapAndTime.playerTeams[player] + == DAT_GameState::instance.mapAndTime.playerTeams[owner]) { + continue; + } + if (MACRO_CALL_MEMBER(Map::Units::UnitsState_Func::getAliveLordForPlayer, DAT_UnitsState::ptr)(player) + == 0) { + continue; + } + + MACRO_CALL_MEMBER(Map::Navigation::DirectionAlgorithmState_Func::setAxisBasedDistanceResult, + DAT_DirectionAlgorithmState::ptr)(DAT_GameState::instance.playerDataArray[player].campground.xEntry, + DAT_GameState::instance.playerDataArray[player].campground.yEntry, ownerKeepX, ownerKeepY); + if (DAT_DirectionAlgorithmState::instance.distanceHigh <= minDistance) { + minDistance = DAT_DirectionAlgorithmState::instance.distanceHigh; + targetPlayer = player; + } + } + + if (targetPlayer == 0) { + return; + } + } + + DAT_TribesState::instance.tribes[tribe].targetPlayer_outpost = (short)targetPlayer; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/setTribe0x11ToAggressiveAndAttackIfConditionMet.cpp b/src/OpenSHC/AI/AICState/setTribe0x11ToAggressiveAndAttackIfConditionMet.cpp new file mode 100644 index 000000000..b3375206b --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTribe0x11ToAggressiveAndAttackIfConditionMet.cpp @@ -0,0 +1,43 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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 playerID) + { + int tribeIndex = 17; + if (DAT_TroopValueState::instance.attackInfo.playerInfo[playerID - 1].moat2 <= 0) { + return; + } + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) { + return; + } + + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex]; + if (tribeID == 0) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex]) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID == 0) { + return; + } + + 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 000000000..fe31982db --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTribe0xCtoAggressiveAndAttack.cpp @@ -0,0 +1,39 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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 tribeIndex = 12; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) { + return; + } + + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex]; + if (tribeID == 0) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex]) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID == 0) { + return; + } + + 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 000000000..ee402e6ec --- /dev/null +++ b/src/OpenSHC/AI/AICState/setTribe0xbToAggressiveAndAttack.cpp @@ -0,0 +1,39 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Units/TribesState.func.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 tribeIndex = 11; + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) { + return; + } + + int const tribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[tribeIndex]; + if (tribeID == 0) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[tribeIndex]) { + return; + } + if (DAT_TribesState::instance.tribes[tribeID].selectionTargetUnitID == 0) { + return; + } + + 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/setupWorkshopProductionType.cpp b/src/OpenSHC/AI/AICState/setupWorkshopProductionType.cpp new file mode 100644 index 000000000..04905465e --- /dev/null +++ b/src/OpenSHC/AI/AICState/setupWorkshopProductionType.cpp @@ -0,0 +1,85 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AIType.hpp" +#include "OpenSHC/Game/Resources/ResourceType.hpp" +#include "OpenSHC/Game/Resources/ResourceTypeShort.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::Game::Resources::ResourceTypeShort; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CBE30 + void AICState::setupWorkshopProductionType(int playerID, int buildingID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AIT_NULL) { + return; + } + + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == Map::Buildings::BT_BLACKSMITH) { + int setting = this->aics[aicIndex].blacksmithSetting; + if (setting == -999) { + int itemType = MACRO_CALL_MEMBER( + Map::Buildings::BuildingsState_Func::chooseProductionItemType, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, Map::Buildings::BT_BLACKSMITH); + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = (ResourceTypeShort)itemType; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = (ResourceTypeShort)itemType; + return; + } + if (setting == Game::Resources::RT_SWORD) { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = Game::Resources::RT_SWORD; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = Game::Resources::RT_SWORD; + return; + } + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = Game::Resources::RT_MACE; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = Game::Resources::RT_MACE; + return; + } + + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == Map::Buildings::BT_POLETURNER) { + int setting = this->aics[aicIndex].poleturnerSetting; + if (setting == -999) { + int itemType = MACRO_CALL_MEMBER( + Map::Buildings::BuildingsState_Func::chooseProductionItemType, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, Map::Buildings::BT_POLETURNER); + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = (ResourceTypeShort)itemType; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = (ResourceTypeShort)itemType; + return; + } + if (setting == Game::Resources::RT_SPEAR) { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = Game::Resources::RT_SPEAR; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = Game::Resources::RT_SPEAR; + return; + } + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = Game::Resources::RT_PIKE; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = Game::Resources::RT_PIKE; + return; + } + + if (DAT_BuildingsState::instance.buildings[buildingID].buildingType == Map::Buildings::BT_FLETCHER) { + int setting = this->aics[aicIndex].fletcherSetting; + if (setting == -999) { + int itemType = MACRO_CALL_MEMBER( + Map::Buildings::BuildingsState_Func::chooseProductionItemType, DAT_BuildingsState::ptr)( + DAT_BuildingsState::instance.buildings[buildingID].owner, Map::Buildings::BT_FLETCHER); + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = (ResourceTypeShort)itemType; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = (ResourceTypeShort)itemType; + return; + } + if (setting == Game::Resources::RT_BOW) { + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = Game::Resources::RT_BOW; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = Game::Resources::RT_BOW; + return; + } + DAT_BuildingsState::instance.buildings[buildingID].producedItemType = Game::Resources::RT_CROSSBOW; + DAT_BuildingsState::instance.buildings[buildingID].producedItemTypeNext = Game::Resources::RT_CROSSBOW; + } + } +} +} diff --git a/src/OpenSHC/AI/AICState/shareGoldAmongTeamMembers.cpp b/src/OpenSHC/AI/AICState/shareGoldAmongTeamMembers.cpp new file mode 100644 index 000000000..dd8fa8225 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shareGoldAmongTeamMembers.cpp @@ -0,0 +1,74 @@ +// disable deprecation warnings for strcpy +#pragma warning(disable : 4996) + +#include "../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/Resources/ResourceType.hpp" +#include "OpenSHC/string-literals.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" +#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp" +#include "OpenSHC/Globals/DAT_TextManagerObject.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004D05D0 + void AICState::shareGoldAmongTeamMembers(int playerID, int gold) + { + int candidateCount = 0; + int candidates[9]; + for (int i = 1; i < 9; i++) { + if ((DAT_GameSynchronyState::instance.currentAIArray[i] != 0 + || DAT_GameSynchronyState::instance.currentPlayerFullIDArray[i] != -1) + && i != playerID + && DAT_GameState::instance.mapAndTime.playerTeams[i] + == DAT_GameState::instance.mapAndTime.playerTeams[playerID] + && DAT_GameState::instance.playerDataArray[i].lordKilledByPlayerID == 0) { + candidates[candidateCount] = i; + candidateCount++; + } + } + + if (candidateCount == 0) { + return; + } + + int const goldPerPlayer = gold / candidateCount; + if (goldPerPlayer == 0) { + return; + } + + for (int j = 0; j < candidateCount; j++) { + int const targetPlayer = candidates[j]; + if (DAT_GameSynchronyState::instance.currentPlayerFullIDArray[targetPlayer] != -1) { + // "Goods received from ally" + strcpy(DAT_GameSynchronyState::instance.receivedChatMessage, + MACRO_CALL_MEMBER(Text::TextManager_Func::getTextStringInGroupAtOffset, DAT_TextManagerObject::ptr)( + DE::SHCDE::TEXT_ALLIES2, 1)); + MACRO_CALL_MEMBER(Synchrony::GameSynchronyState_Func::addChatMessageToDisplayList, + DAT_GameSynchronyState::ptr)(targetPlayer, 0); + + // "Gold" + MACRO_CALL(OS_Func::_sprintf)(DAT_GameSynchronyState::instance.receivedChatMessage, s__d__s___005a7674, + goldPerPlayer, + MACRO_CALL_MEMBER(Text::TextManager_Func::getTextStringInGroupAtOffset, DAT_TextManagerObject::ptr)( + DE::SHCDE::TEXT_GOODS, Game::Resources::RT_GOLD)); + MACRO_CALL_MEMBER(Synchrony::GameSynchronyState_Func::addChatMessageToDisplayList, + DAT_GameSynchronyState::ptr)(targetPlayer, playerID); + } + + DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_GOLD] + -= goldPerPlayer; + DAT_GameState::instance.playerDataArray[targetPlayer].currentResources[Game::Resources::RT_GOLD] + += goldPerPlayer; + } + return; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/shouldDestroyWoodCutterUnk.cpp b/src/OpenSHC/AI/AICState/shouldDestroyWoodCutterUnk.cpp new file mode 100644 index 000000000..8df98c639 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldDestroyWoodCutterUnk.cpp @@ -0,0 +1,53 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/AI/AIVState.func.hpp" +#include "OpenSHC/Map/Buildings/BuildingsState.func.hpp" +#include "OpenSHC/AI/AITypeA.hpp" +#include "OpenSHC/Game/Resources/ResourceType.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) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == AITA_NULL) { + return FALSE; + } + + int buildingID; + if (DAT_AIVState::instance.mapExtraInfo.totalWoodAvailable <= 0) { + buildingID = MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::findFirstBuildingIDForPlayerAndType, + DAT_BuildingsState::ptr)(playerID, Map::Buildings::BT_WOODCUTTERSHUT); + } else { + if (DAT_GameState::instance.playerDataArray[playerID].currentResources[Game::Resources::RT_WOOD] <= 20) { + return FALSE; + } + if (DAT_GameState::instance.playerDataArray[playerID].noLabourerBuildingCount < 2) { + return FALSE; + } + if (DAT_GameState::instance.playerDataArray[playerID].someCount10 < 25) { + return FALSE; + } + buildingID = DAT_GameState::instance.playerDataArray[playerID].someCount11; + } + + if (buildingID != 0) { + MACRO_CALL_MEMBER(AIVState_Func::resetCountdownInHeatMap, DAT_AIVState::ptr)( + (short)DAT_BuildingsState::instance.buildings[buildingID].x, + (short)DAT_BuildingsState::instance.buildings[buildingID].y); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::giveBackResourceForDestroyedBuilding, + DAT_BuildingsState::ptr)(buildingID, playerID, 50); + MACRO_CALL_MEMBER(Map::Buildings::BuildingsState_Func::destroyBuilding, DAT_BuildingsState::ptr)( + buildingID); + return TRUE; + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/shouldNotBeBuildingArmoury.cpp b/src/OpenSHC/AI/AICState/shouldNotBeBuildingArmoury.cpp new file mode 100644 index 000000000..817ca72e2 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBeBuildingArmoury.cpp @@ -0,0 +1,19 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004ECA60 + BOOLEnum AICState::shouldNotBeBuildingArmoury(int 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 000000000..4abf99ecc --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBeBuildingGranary.cpp @@ -0,0 +1,19 @@ +#include "../AICState.func.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 000000000..1de3299a9 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBeBuildingHovel.cpp @@ -0,0 +1,37 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB5B0 + BOOLEnum AICState::shouldNotBeBuildingHovel(int playerID, Commands::MappersEnum mapper) + { + if (mapper != Commands::M_MAPPER_HOVEL) { + return FALSE; + } + + int const populationCap = DAT_GameState::instance.playerDataArray[playerID].populationCap; + if (populationCap <= 12) { + return FALSE; + } + + if (DAT_GameState::instance.playerDataArray[playerID].currentPopulation < populationCap) { + 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 000000000..4011c4bc0 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBuildKillingPits.cpp @@ -0,0 +1,19 @@ +#include "../AICState.func.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 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 000000000..93acb3655 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldNotBuildMarketArmoryGranaryBarracksMercenarypost.cpp @@ -0,0 +1,30 @@ +#include "../AICState.func.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 mapper) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiNervousActionsTracker > 0) { + // nervous: reject market/mercenarypost/barracks/armory types + if (mapper != Commands::M_MAPPER_TRADEPOST && mapper != Commands::M_MAPPER_BARRACKS_EURO + && mapper != Commands::M_MAPPER_BARRACKS_ARAB && mapper != Commands::M_MAPPER_ARMOURY) { + return mapper != Commands::M_MAPPER_GRANARY; + } + } else { + // not nervous: only reject if destroy tracker > 0 and not marketplace + if (DAT_GameState::instance.playerDataArray[playerID].aiBuildingDestroyChoiceTracker > 0 + && mapper != Commands::M_MAPPER_TRADEPOST) { + return mapper != 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 000000000..a7533fc12 --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldWaitWithBuildingAPositiveFearFactor.cpp @@ -0,0 +1,51 @@ +#include "../AICState.func.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 mapper) + { + switch (mapper) { + 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 000000000..29be1ab5e --- /dev/null +++ b/src/OpenSHC/AI/AICState/shouldWaitWithBuildingNegativeFearFactor.cpp @@ -0,0 +1,40 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Commands/MappersEnum.hpp" + +#include "OpenSHC/Globals/DAT_GameState.hpp" + +namespace OpenSHC { +namespace AI { + + // FUNCTION: STRONGHOLDCRUSADER 0x004CB610 + BOOLEnum AICState::shouldWaitWithBuildingNegativeFearFactor(int playerID, Commands::MappersEnum mapper) + { + switch (mapper) { + 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[playerID].popularity < 5000) { + return TRUE; + } + DAT_GameState::instance.playerDataArray[playerID].aivNegativeFearFactorDelay++; + if (DAT_GameState::instance.playerDataArray[playerID].aivNegativeFearFactorDelay < 100) { + return TRUE; + } + DAT_GameState::instance.playerDataArray[playerID].aivNegativeFearFactorDelay = 0; + return FALSE; + } + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/someChatMessageSelection.cpp b/src/OpenSHC/AI/AICState/someChatMessageSelection.cpp new file mode 100644 index 000000000..efcd33cac --- /dev/null +++ b/src/OpenSHC/AI/AICState/someChatMessageSelection.cpp @@ -0,0 +1,113 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D10B0 + void AICState::someChatMessageSelection() + { + int candidates[9]; + int candidateCount; + int total; + int i; + int playerID; + + if ((MACRO_CALL_MEMBER(OpenSHC::Game::GameCore_Func::getAreWeInAInGameMenu, DAT_GameCore::ptr)() != FALSE) + && (DAT_GameSynchronyState::instance.currentGameMode == OpenSHC::Game::GM_SKIRMISH_SINGLE_PLAYER)) { + + if (this->aiTauntResponseTimeWindow == 0) { + return; + } + + if (timeGetTime() - this->aiTauntResponseTimeWindow <= 2000) { + return; + } + + candidateCount = 0; + // Players 1..8, unrolled 4 at a time. The outer loop has to step by 4 and the inner 4 can be + // left to the compiler, which unrolls them into the same instructions as four written-out + // copies. A plain 1..8 loop does not work (0.92 -> 0.67, and the same for every spelling of + // it): MSVC unrolls all 8 and, with the trip count known, constant-folds every index into an + // absolute address and an immediate. The original keeps the index in a register, so its + // source had an induction variable surviving the unroll - which is what stepping by 4 gives. + for (playerID = 1; playerID < 9; playerID += 4) { + for (int offset = 0; offset < 4; offset++) { + if (DAT_GameSynchronyState::instance.currentAIArray[playerID + offset] != 0 + && DAT_GameState::instance.mapAndTime.playerTeams[playerID + offset] + != DAT_GameState::instance.mapAndTime + .playerTeams[DAT_GameSynchronyState::instance.currentPlayerSlotID]) { + candidates[candidateCount] = playerID + offset; + candidateCount++; + } + } + } + + if (candidateCount > 0) { + total = 0; + // NOTE: 'i = total' keeps the (redundant) loop entry check of the original. + for (i = total; i < candidateCount; i++) { + total += DAT_SkirmishDefinedData::instance + .MaxBreachTribes[DAT_GameState::instance.playerDataArray[candidates[i]].aiType + + OpenSHC::AI::AIT_FREDERICK] + .tribeType; + } + + if (total != 0) { + int roll = (int)SEC_RNG::instance.currentNumber1; + MACRO_CALL_MEMBER(OpenSHC::Random::RNG_Func::nextRandomNumber1, SEC_RNG::ptr)(); + roll = roll % total; + + for (i = 0; i < candidateCount; i++) { + playerID = candidates[i]; + roll -= DAT_SkirmishDefinedData::instance + .MaxBreachTribes[DAT_GameState::instance.playerDataArray[playerID].aiType + + OpenSHC::AI::AIT_FREDERICK] + .tribeType; + if (roll < 0) { + 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_ChatTauntOrMessage = -2 + - DAT_SkirmishDefinedData::instance + .MaxBreachTribes[DAT_GameState::instance.playerDataArray[playerID].aiType + + OpenSHC::AI::AIT_FREDERICK] + .tribeCount; + DAT_GameSynchronyState::instance.DAT_GameCommandParam0 = playerID; + MACRO_CALL_MEMBER(OpenSHC::Synchrony::GameSynchronyState_Func::queueCommand, + DAT_GameSynchronyState::ptr)(OpenSHC::Commands::GCT_TAUNT_OR_CHAT); + break; + } + } + } + } + } + + this->aiTauntResponseTimeWindow = 0; + } + +} +} diff --git a/src/OpenSHC/AI/AICState/teamIsWeakRelativeToEnemy.cpp b/src/OpenSHC/AI/AICState/teamIsWeakRelativeToEnemy.cpp new file mode 100644 index 000000000..a4ccb2dbf --- /dev/null +++ b/src/OpenSHC/AI/AICState/teamIsWeakRelativeToEnemy.cpp @@ -0,0 +1,53 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Game/GameStateStructures.func.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 alreadyNervous) + { + // strength of the own team in percent of the enemy strength + int relativeStrength; + if (DAT_GameState::instance.playerDataArray[playerID].totalEnemyTroopValue == 0) { + relativeStrength = 100; + } else { + relativeStrength = DAT_GameState::instance.playerDataArray[playerID].totalTeamTroopValue * 100 + / DAT_GameState::instance.playerDataArray[playerID].totalEnemyTroopValue; + } + DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy = relativeStrength; + + // the team counts as weak below a threshold that is lower with an enclosed keep + // and much higher during the first 12000 ticks + if (alreadyNervous) { + if (MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)(playerID)) { + if (DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy >= 70) + return FALSE; + } else if ((int)DAT_GameCore::instance.mapTimeInTicks < 12000) { + if (DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy >= 300) + return FALSE; + } else if (DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy >= 100) { + return FALSE; + } + } else { + // without enemy ranged troops there is no reason to become nervous + if (DAT_GameState::instance.playerDataArray[playerID].totalEnemyRangedTroopValue < 100) + return FALSE; + if (MACRO_CALL_MEMBER(Game::GameStateStructures_Func::checkKeepEnclosed, DAT_GameState::ptr)(playerID)) { + if (DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy >= 50) + return FALSE; + } else if ((int)DAT_GameCore::instance.mapTimeInTicks < 12000) { + if (DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy >= 250) + return FALSE; + } else if (DAT_GameState::instance.playerDataArray[playerID].relativeStrengthOfTeamComparedToEnemy >= 75) { + return FALSE; + } + } + return TRUE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/unitIDIsRangedOrArmored.cpp b/src/OpenSHC/AI/AICState/unitIDIsRangedOrArmored.cpp new file mode 100644 index 000000000..ad660c736 --- /dev/null +++ b/src/OpenSHC/AI/AICState/unitIDIsRangedOrArmored.cpp @@ -0,0 +1,27 @@ +#include "../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 + BOOLEnum AICState::unitIDIsRangedOrArmored(int unitID) + { + int const unitType = DAT_UnitsState::instance.units[unitID].unitType; + + for (int i = 0; i < 7; i++) { + if (DAT_SkirmishDefinedData::instance.RangedAndArmoredUnits[i] == unitType) { + return TRUE; + } + } + + return FALSE; + } +} +} diff --git a/src/OpenSHC/AI/AICState/updateAIBehaviour.cpp b/src/OpenSHC/AI/AICState/updateAIBehaviour.cpp new file mode 100644 index 000000000..87177170b --- /dev/null +++ b/src/OpenSHC/AI/AICState/updateAIBehaviour.cpp @@ -0,0 +1,94 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/Map/TileMapState.func.hpp" +#include "OpenSHC/AI/AIRecruitUnitChoice.hpp" +#include "OpenSHC/AI/AIType.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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004D53E0 + void AICState::updateAIBehaviour() + { + if (DAT_GameState::instance.mapAndTime.dayTicks == 10) + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::makeOutpostTribesAttack, this)(); + + // every AI gets its own tick of the day, week and month, so the work is spread out + for (int playerID = 1; playerID < 9; playerID++) { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + continue; + if (DAT_GameState::instance.playerDataArray[playerID].playerDeathRelated != 0) + continue; + if (DAT_GameState::instance.playerDataArray[playerID].lordKilledByPlayerID != 0) + continue; + int const aicIndex = DAT_GameState::instance.playerDataArray[playerID].aiType - 1; + + if (DAT_GameState::instance.mapAndTime.dayTicks == playerID) { + if (DAT_GameState::instance.mapAndTime.skirmishNoRushTicks == 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.skirmishNoRushTicks == 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); + + // on the month tick the strength update happens below, with the nervousness handling enabled + if (DAT_GameState::instance.mapAndTime.monthTicks != playerID) + MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::updateAIStrengthState, this)(playerID, 0); + + if (DAT_GameState::instance.playerDataArray[playerID].aiRequestDelay != 0) + DAT_GameState::instance.playerDataArray[playerID].aiRequestDelay--; + if (DAT_GameState::instance.playerDataArray[playerID].nervousBikCountdown != 0) + DAT_GameState::instance.playerDataArray[playerID].nervousBikCountdown--; + if (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown3 != 0) + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown3--; + if (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown4 != 0) + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown4--; + if (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown5 != 0) + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown5--; + if (DAT_GameState::instance.playerDataArray[playerID].someAiCountdown6 != 0) + DAT_GameState::instance.playerDataArray[playerID].someAiCountdown6--; + } + + 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 (DAT_GameState::instance.playerDataArray[playerID].aiRecruitUnitChoiceState + == OpenSHC::AI::AIRUC_RAIDING) { + DAT_GameState::instance.playerDataArray[playerID].canStartSpending = 1; + } else { + // only spend on recruiting while gold is above the AIC's threshold + DAT_GameState::instance.playerDataArray[playerID].canStartSpending + = DAT_GameState::instance.playerDataArray[playerID].currentResources[0xf] + > this->aics[aicIndex].recruitGoldThreshold; + } + } + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/updateTribeLocations.cpp b/src/OpenSHC/AI/AICState/updateTribeLocations.cpp new file mode 100644 index 000000000..cbb774fb4 --- /dev/null +++ b/src/OpenSHC/AI/AICState/updateTribeLocations.cpp @@ -0,0 +1,58 @@ +#include "../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 const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + + if (aiType == OpenSHC::AI::AIT_NULL) { + return; + } + + DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeWaitCounter + = DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeWaitCounter + 1; + + if (DAT_GameState::instance.playerDataArray[playerID].defWallPatrolRallyTimeWaitCounter + >= this->aics[aiType - 1].defWallPatrolRallyTime) { + + 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); + } + +} +} 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 000000000..7532eefcf --- /dev/null +++ b/src/OpenSHC/AI/AICState/useAITribe_0x12_toPlaceSiegeTentsAndAssignEngineers.cpp @@ -0,0 +1,103 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CEFD0 + void AICState::useAITribe_0x12_toPlaceSiegeTentsAndAssignEngineers(int playerID) + { + int const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType == OpenSHC::AI::AIT_NULL) + return; + int const aicIndex = aiType - 1; + int const aiTribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0x12]; + if (aiTribeID == 0) + return; + if (DAT_TribesState::instance.tribes[aiTribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0x12]) + return; + + int availableEngineers = DAT_TribesState::instance.tribes[aiTribeID].size; + int requiredEngineers; + int totalRequiredEngineers = 0; + + for (int i = 0; i < 8; i++) { + if ((&this->aics[aicIndex].SiegeEngine1)[i] <= 0) + break; + for (int j = 0; j < 6; j++) { + if ((&this->aics[aicIndex].SiegeEngine1)[i] + == DAT_SkirmishDefinedData::instance.SiegeEngineUnitType[j]) { + requiredEngineers = DAT_SkirmishDefinedData::instance.SiegeEngineTypeRequiredEngineers[j]; + break; + } + } + if (totalRequiredEngineers + requiredEngineers > availableEngineers) + break; + totalRequiredEngineers += requiredEngineers; + } + + if (totalRequiredEngineers <= 0) + return; + + for (; DAT_TribesState::instance.tribes[aiTribeID].size != 0; totalRequiredEngineers--) { + if (totalRequiredEngineers <= 0) + break; + MACRO_CALL_MEMBER(OpenSHC::Map::Units::TribesState_Func::popUnitFromTribe, DAT_TribesState::ptr)(aiTribeID); + } + + MappersEnum siegeEngineType; + for (int i = 0; i < 8; i++) { + if ((&this->aics[aicIndex].SiegeEngine1)[i] <= 0) + return; + for (int j = 0; j < 6; j++) { + if ((&this->aics[aicIndex].SiegeEngine1)[i] + == DAT_SkirmishDefinedData::instance.SiegeEngineUnitType[j]) { + requiredEngineers = DAT_SkirmishDefinedData::instance.SiegeEngineTypeRequiredEngineers[j]; + siegeEngineType = (MappersEnum)DAT_SkirmishDefinedData::instance.SiegeEngineCommandBuildingTypes[j]; + break; + } + } + if (requiredEngineers > availableEngineers) + return; + availableEngineers -= requiredEngineers; + + int const tribeID = MACRO_CALL_MEMBER(OpenSHC::AI::AICState_Func::addEngineersToSelection, this)( + playerID, requiredEngineers); + if (tribeID == 0) + return; + + 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; + DAT_TribesState::instance.tribes[tribeID].attackWave + = DAT_GameState::instance.playerDataArray[playerID].currentAttackWave; + + MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TroopValueState_Func::placeSiegeTentOrTunnelAtSuitableLocationAndAssignEngineers, + DAT_TroopValueState::ptr)(tribeID, siegeEngineType, 60, (UnitInstructionType)15); + } + } + +} +} 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 000000000..7cdba06e5 --- /dev/null +++ b/src/OpenSHC/AI/AICState/useAITribe_0xe_toPlaceTunnels.cpp @@ -0,0 +1,67 @@ +#include "../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; + + // FUNCTION: STRONGHOLDCRUSADER 0x004CF180 + void AICState::useAITribe_0xe_toPlaceTunnels(int playerID) + { + if (DAT_GameState::instance.playerDataArray[playerID].aiType == OpenSHC::AI::AIT_NULL) + return; + int const aiTribeID = DAT_GameState::instance.playerDataArray[playerID].aiTribeIDs[0xe]; + if (aiTribeID == 0) + return; + if (DAT_TribesState::instance.tribes[aiTribeID].uid + != DAT_GameState::instance.playerDataArray[playerID].aiTribeUIDs[0xe]) + return; + int countdown = DAT_TribesState::instance.tribes[aiTribeID].size; + + while (DAT_TribesState::instance.tribes[aiTribeID].size != 0) { + if (countdown <= 0) + break; + + int const unitID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::popUnitFromTribe, DAT_TribesState::ptr)(aiTribeID); + // Yes this line has to be right here for the code to match. + countdown--; + + int const tribeID = MACRO_CALL_MEMBER( + OpenSHC::Map::Units::TribesState_Func::createTribeForPlayer, DAT_TribesState::ptr)(playerID); + + DAT_TribesState::instance.tribes[tribeID].attackWave + = 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, 50, (UnitInstructionType)21); + } + } + +} +} diff --git a/src/OpenSHC/AI/AICState/wipeAICMemory.cpp b/src/OpenSHC/AI/AICState/wipeAICMemory.cpp new file mode 100644 index 000000000..a63c87e1d --- /dev/null +++ b/src/OpenSHC/AI/AICState/wipeAICMemory.cpp @@ -0,0 +1,19 @@ +#include "../AICState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.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)( + sizeof(this->aics[i]), '\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 000000000..5129c6cc1 --- /dev/null +++ b/src/OpenSHC/AI/AICState/yieldCowThrow.cpp @@ -0,0 +1,30 @@ +#include "../AICState.func.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 const aiType = DAT_GameState::instance.playerDataArray[playerID].aiType; + if (aiType != AITA_NULL) { + int const aicIndex = aiType - 1; + if (DAT_GameState::instance.mapAndTime.skirmishNoCowThrowing == 0 + && this->aics[aicIndex].CowThrowInterval != 0 + && DAT_GameState::instance.playerDataArray[playerID].counter > 0) { + DAT_GameState::instance.playerDataArray[playerID].aiCowThrowCounter++; + if (DAT_GameState::instance.playerDataArray[playerID].aiCowThrowCounter + > this->aics[aicIndex].CowThrowInterval) { + DAT_GameState::instance.playerDataArray[playerID].aiCowThrowCounter = 0; + return TRUE; + } + } + } + return FALSE; + } +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index 238a0fe8a..ef3cb11e6 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -34157,57 +34157,57 @@ SHC_3BB0A8C1_0x004C6CD0 | 0.0% | Pending SHC_3BB0A8C1_0x004C6D30 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C6D60 | 0.0% | Pending +SHC_3BB0A8C1_0x004C6D60 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C7120 | 0.0% | Pending +SHC_3BB0A8C1_0x004C7120 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C7520 | 0.0% | Pending +SHC_3BB0A8C1_0x004C7520 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C7910 | 0.0% | Pending +SHC_3BB0A8C1_0x004C7910 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C7D20 | 0.0% | Pending +SHC_3BB0A8C1_0x004C7D20 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C8110 | 0.0% | Pending +SHC_3BB0A8C1_0x004C8110 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C8530 | 0.0% | Pending +SHC_3BB0A8C1_0x004C8530 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C8920 | 0.0% | Pending +SHC_3BB0A8C1_0x004C8920 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C8D20 | 0.0% | Pending +SHC_3BB0A8C1_0x004C8D20 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C9120 | 0.0% | Pending +SHC_3BB0A8C1_0x004C9120 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C94F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004C94F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C9900 | 0.0% | Pending +SHC_3BB0A8C1_0x004C9900 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004C9CF0 | 0.0% | Pending +SHC_3BB0A8C1_0x004C9CF0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CA0C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CA0C0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CA4D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CA4D0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CA8A0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CA8A0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CAC70 | 0.0% | Pending -SHC_3BB0A8C1_0x004CACB0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CACB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CAD10 | 0.0% | Pending SHC_3BB0A8C1_0x004CAE20 | 0.0% | Pending -SHC_3BB0A8C1_0x004CAEA0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CAEA0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CB060 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CB120 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CB150 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB150 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CB180 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB180 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CB1B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB1B0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CB220 | 0.0% | Pending @@ -34215,15 +34215,15 @@ SHC_3BB0A8C1_0x004CB290 | 0.0% | Pending SHC_3BB0A8C1_0x004CB330 | 0.0% | Pending -SHC_3BB0A8C1_0x004CB3A0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB3A0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CB4D0 | 0.0% | Pending SHC_3BB0A8C1_0x004CB540 | 0.0% | Pending -SHC_3BB0A8C1_0x004CB5B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB5B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CB610 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB610 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CB66C | 0.0% | Pending @@ -34501,7 +34501,7 @@ SHC_3BB0A8C1_0x004CB6FA | 0.0% | Pending SHC_3BB0A8C1_0x004CB6FB | 0.0% | Pending -SHC_3BB0A8C1_0x004CB700 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB700 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CB754 | 0.0% | Pending @@ -34847,33 +34847,33 @@ SHC_3BB0A8C1_0x004CB804 | 0.0% | Pending SHC_3BB0A8C1_0x004CB810 | 0.0% | Pending -SHC_3BB0A8C1_0x004CB890 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB890 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CB940 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB940 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CB9F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CB9F0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CBA50 | 0.0% | Pending -SHC_3BB0A8C1_0x004CBE30 | 0.0% | Pending +SHC_3BB0A8C1_0x004CBE30 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CBFA0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CC000 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CC070 | 0.0% | Pending +SHC_3BB0A8C1_0x004CC070 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CC250 | 0.0% | Pending +SHC_3BB0A8C1_0x004CC250 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CC390 | 0.0% | Pending +SHC_3BB0A8C1_0x004CC390 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CC420 | 0.0% | Pending +SHC_3BB0A8C1_0x004CC420 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CC520 | 0.0% | Pending +SHC_3BB0A8C1_0x004CC520 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CC5A0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CC5A0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CC680 | 0.0% | Pending +SHC_3BB0A8C1_0x004CC680 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CC6B0 | 0.0% | Pending @@ -34883,13 +34883,13 @@ SHC_3BB0A8C1_0x004CC7B0 | 0.0% | Pending SHC_3BB0A8C1_0x004CC840 | 0.0% | Pending -SHC_3BB0A8C1_0x004CC8D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CC8D0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CC910 | 0.0% | Pending SHC_3BB0A8C1_0x004CC990 | 0.0% | Pending -SHC_3BB0A8C1_0x004CCAF0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CCAF0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CCBE0 | 0.0% | Pending @@ -35261,13 +35261,13 @@ SHC_3BB0A8C1_0x004CCFA0 | 0.0% | Pending SHC_3BB0A8C1_0x004CCFA1 | 0.0% | Pending -SHC_3BB0A8C1_0x004CCFB0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CCFB0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CD070 | 0.0% | Pending +SHC_3BB0A8C1_0x004CD070 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CD0C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CD0C0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CD110 | 0.0% | Pending +SHC_3BB0A8C1_0x004CD110 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CD160 | 0.0% | Pending @@ -35279,47 +35279,47 @@ SHC_3BB0A8C1_0x004CD560 | 0.0% | Pending SHC_3BB0A8C1_0x004CD690 | 0.0% | Pending -SHC_3BB0A8C1_0x004CD7B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CD7B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CD8F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CD8F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CDA50 | 0.0% | Pending +SHC_3BB0A8C1_0x004CDA50 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CDAB0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CDAB0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CDB20 | 0.0% | Pending -SHC_3BB0A8C1_0x004CDBC0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CDBC0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CDCA0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CDCA0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CDCF0 | 0.0% | Pending -SHC_3BB0A8C1_0x004CDE60 | 0.0% | Pending +SHC_3BB0A8C1_0x004CDE60 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CDF10 | 0.0% | Pending -SHC_3BB0A8C1_0x004CDFD0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CDFD0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CE040 | 0.0% | Pending +SHC_3BB0A8C1_0x004CE040 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CE110 | 0.0% | Pending +SHC_3BB0A8C1_0x004CE110 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CE1A0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CE1A0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CE200 | 0.0% | Pending SHC_3BB0A8C1_0x004CE270 | 0.0% | Pending -SHC_3BB0A8C1_0x004CE3B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CE3B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CE430 | 0.0% | Pending +SHC_3BB0A8C1_0x004CE430 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CE4D0 | 0.0% | Pending SHC_3BB0A8C1_0x004CE530 | 0.0% | Pending -SHC_3BB0A8C1_0x004CE5F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CE5F0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CE660 | 0.0% | Pending @@ -35327,57 +35327,57 @@ SHC_3BB0A8C1_0x004CE6B0 | 0.0% | Pending SHC_3BB0A8C1_0x004CE790 | 0.0% | Pending -SHC_3BB0A8C1_0x004CE830 | 0.0% | Pending +SHC_3BB0A8C1_0x004CE830 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CE860 | 0.0% | Pending SHC_3BB0A8C1_0x004CE890 | 0.0% | Pending -SHC_3BB0A8C1_0x004CE930 | 0.0% | Pending +SHC_3BB0A8C1_0x004CE930 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CEA50 | 0.0% | Pending +SHC_3BB0A8C1_0x004CEA50 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CEB50 | 0.0% | Pending SHC_3BB0A8C1_0x004CEB80 | 0.0% | Pending -SHC_3BB0A8C1_0x004CEC80 | 0.0% | Pending +SHC_3BB0A8C1_0x004CEC80 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CED90 | 0.0% | Pending +SHC_3BB0A8C1_0x004CED90 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CEEA0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CEEA0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CEFD0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CEFD0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CF060 | 0.0% | Pending -SHC_3BB0A8C1_0x004CF180 | 0.0% | Pending +SHC_3BB0A8C1_0x004CF180 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CF270 | 0.0% | Pending +SHC_3BB0A8C1_0x004CF270 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CF2E0 | 0.0% | Pending -SHC_3BB0A8C1_0x004CF400 | 0.0% | Pending +SHC_3BB0A8C1_0x004CF400 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CF470 | 0.0% | Pending +SHC_3BB0A8C1_0x004CF470 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CF4F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CF4F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CF560 | 0.0% | Pending +SHC_3BB0A8C1_0x004CF560 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CF5E0 | 100.0% | Reimplemented without for-loop +SHC_3BB0A8C1_0x004CF5E0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CF660 | 100.0% | Reimplemented without for-loop -SHC_3BB0A8C1_0x004CF6E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CF6E0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CF750 | 0.0% | Pending +SHC_3BB0A8C1_0x004CF750 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CF7C0 | 100.0% | Reimplemented, for-loop didn't work SHC_3BB0A8C1_0x004CF7F0 | 0.0% | Pending -SHC_3BB0A8C1_0x004CF870 | 100.0% | Reimplemented, for-loop didn't work +SHC_3BB0A8C1_0x004CF870 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004CF920 | 0.0% | Pending @@ -35385,79 +35385,79 @@ SHC_3BB0A8C1_0x004CFA10 | 0.0% | Pending SHC_3BB0A8C1_0x004CFB20 | 0.0% | Pending -SHC_3BB0A8C1_0x004CFBC0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CFBC0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CFCD0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CFCD0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CFF60 | 0.0% | Pending +SHC_3BB0A8C1_0x004CFF60 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004CFFD0 | 0.0% | Pending +SHC_3BB0A8C1_0x004CFFD0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D00A0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D00A0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D0110 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0190 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0190 | 100.0% | Reimplemented, compiler didn't optimize one tail -SHC_3BB0A8C1_0x004D0280 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0280 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0520 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0520 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0580 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0580 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D05D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D05D0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D0720 | 0.0% | Pending -SHC_3BB0A8C1_0x004D07C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D07C0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0830 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0830 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D08B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D08B0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D0910 | 0.0% | Pending -SHC_3BB0A8C1_0x004D0A20 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0A20 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0A80 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0A80 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D0B10 | 0.0% | Pending -SHC_3BB0A8C1_0x004D0B70 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0B70 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0BB0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0BB0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0C00 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0C00 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0C70 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0C70 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0CF0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0CF0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0D40 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0D40 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0DB0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0DB0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0E00 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0E00 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0E50 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0E50 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0EA0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0EA0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0EF0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0EF0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0F40 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0F40 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0F90 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0F90 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D0FC0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D0FC0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D1000 | 0.0% | Pending +SHC_3BB0A8C1_0x004D1000 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D1050 | 0.0% | Pending +SHC_3BB0A8C1_0x004D1050 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D1090 | 0.0% | Pending -SHC_3BB0A8C1_0x004D10B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D10B0 | 100.0% | Reimplemented, one reordered push edi SHC_3BB0A8C1_0x004D11C0 | 0.0% | Pending @@ -35469,7 +35469,7 @@ SHC_3BB0A8C1_0x004D1700 | 0.0% | Pending SHC_3BB0A8C1_0x004D1710 | 0.0% | Pending -SHC_3BB0A8C1_0x004D18B0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D18B0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D1950 | 0.0% | Pending @@ -35477,7 +35477,7 @@ SHC_3BB0A8C1_0x004D1A00 | 0.0% | Pending SHC_3BB0A8C1_0x004D1D60 | 0.0% | Pending -SHC_3BB0A8C1_0x004D1F00 | 100.0% | Reimplemented, for-loop didn't work +SHC_3BB0A8C1_0x004D1F00 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D2000 | 0.0% | Pending @@ -35487,9 +35487,9 @@ SHC_3BB0A8C1_0x004D20C0 | 0.0% | Pending SHC_3BB0A8C1_0x004D2500 | 0.0% | Pending -SHC_3BB0A8C1_0x004D2660 | 0.0% | Pending +SHC_3BB0A8C1_0x004D2660 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D2730 | 0.0% | Pending +SHC_3BB0A8C1_0x004D2730 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D2790 | 100.0% | Reimplemented @@ -35497,27 +35497,27 @@ SHC_3BB0A8C1_0x004D27E0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D2830 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D2870 | 0.0% | Pending +SHC_3BB0A8C1_0x004D2870 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D28B0 | 0.0% | Pending SHC_3BB0A8C1_0x004D2A70 | 0.0% | Pending -SHC_3BB0A8C1_0x004D2E00 | 0.0% | Pending +SHC_3BB0A8C1_0x004D2E00 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D2EC0 | 0.0% | Pending -SHC_3BB0A8C1_0x004D2F80 | 0.0% | Pending +SHC_3BB0A8C1_0x004D2F80 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D30E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D30E0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D3320 | 0.0% | Pending -SHC_3BB0A8C1_0x004D3780 | 0.0% | Pending +SHC_3BB0A8C1_0x004D3780 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D3850 | 0.0% | Pending +SHC_3BB0A8C1_0x004D3850 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D38C0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D38C0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D3940 | 100.0% | Reimplemented @@ -35537,13 +35537,13 @@ SHC_3BB0A8C1_0x004D40B0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D4130 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D4220 | 0.0% | Pending +SHC_3BB0A8C1_0x004D4220 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D4340 | 0.0% | Pending SHC_3BB0A8C1_0x004D4400 | 0.0% | Pending -SHC_3BB0A8C1_0x004D44E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D44E0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D4530 | 0.0% | Pending @@ -35557,7 +35557,7 @@ SHC_3BB0A8C1_0x004D4870 | 0.0% | Pending SHC_3BB0A8C1_0x004D48F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004D4940 | 0.0% | Pending +SHC_3BB0A8C1_0x004D4940 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D49E0 | 0.0% | Pending @@ -35571,7 +35571,7 @@ SHC_3BB0A8C1_0x004D5370 | 0.0% | Pending SHC_3BB0A8C1_0x004D53A0 | 0.0% | Pending -SHC_3BB0A8C1_0x004D53E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004D53E0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004D55D0 | 0.0% | Pending