|
| 1 | +#include "Body.h" |
| 2 | + |
| 3 | +#include <HouseClass.h> |
| 4 | + |
| 5 | +#include "Ext/Rules/Body.h" |
| 6 | + |
| 7 | +// AIConstructionYard Hook #1 -> sub_740810 - Check number of construction yard before deploy. |
| 8 | +DEFINE_HOOK(0x740A11, UnitClass_Mission_Guard_AIAutoDeployMCV, 0x6) |
| 9 | +{ |
| 10 | + enum { SkipGameCode = 0x740A50 }; |
| 11 | + |
| 12 | + GET(UnitClass*, pMCV, ESI); |
| 13 | + |
| 14 | + return (!RulesExt::Global()->AIAutoDeployMCV && pMCV->Owner->NumConYards > 0) ? SkipGameCode : 0; |
| 15 | +} |
| 16 | + |
| 17 | +// AIConstructionYard Hook #2 -> sub_7393C0 - Skip useless base center setting. |
| 18 | +DEFINE_HOOK(0x739889, UnitClass_TryToDeploy_AISetBaseCenter, 0x6) |
| 19 | +{ |
| 20 | + enum { SkipGameCode = 0x73992B }; |
| 21 | + |
| 22 | + GET(UnitClass*, pMCV, EBP); |
| 23 | + |
| 24 | + return (!RulesExt::Global()->AISetBaseCenter && pMCV->Owner->NumConYards > 1) ? SkipGameCode : 0; |
| 25 | +} |
| 26 | + |
| 27 | +// AIConstructionYard Hook #3 -> sub_4FD500 - Update better base center. |
| 28 | +DEFINE_HOOK(0x4FD538, HouseClass_AIHouseUpdate_CheckAIBaseCenter, 0x7) |
| 29 | +{ |
| 30 | + if (RulesExt::Global()->AIBiasSpawnCell && !SessionClass::IsCampaign()) |
| 31 | + { |
| 32 | + GET(HouseClass*, pAI, EBX); |
| 33 | + |
| 34 | + if (const auto count = pAI->ConYards.Count) |
| 35 | + { |
| 36 | + const auto wayPoint = pAI->GetSpawnPosition(); |
| 37 | + |
| 38 | + if (wayPoint != -1) |
| 39 | + { |
| 40 | + const auto center = ScenarioClass::Instance->GetWaypointCoords(wayPoint); |
| 41 | + auto newCenter = center; |
| 42 | + double distanceSquared = 131072.0; |
| 43 | + |
| 44 | + for (int i = 0; i < count; ++i) |
| 45 | + { |
| 46 | + if (const auto pBuilding = pAI->ConYards.GetItem(i)) |
| 47 | + { |
| 48 | + if (pBuilding->IsAlive && pBuilding->Health > 0 && !pBuilding->InLimbo) |
| 49 | + { |
| 50 | + const auto newDistanceSquared = pBuilding->GetMapCoords().DistanceFromSquared(center); |
| 51 | + |
| 52 | + if (newDistanceSquared < distanceSquared) |
| 53 | + { |
| 54 | + distanceSquared = newDistanceSquared; |
| 55 | + newCenter = pBuilding->GetMapCoords(); |
| 56 | + } |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + if (newCenter != center) |
| 62 | + { |
| 63 | + pAI->BaseSpawnCell = newCenter; |
| 64 | + pAI->Base.Center = newCenter; |
| 65 | + } |
| 66 | + } |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + return 0; |
| 71 | +} |
| 72 | + |
| 73 | +// AIConstructionYard Hook #4-1 -> sub_443C60 - Prohibit AI from building construction yard and clean up invalid walls nodes. |
| 74 | +DEFINE_HOOK(0x4451F8, BuildingClass_KickOutUnit_CleanUpAIBuildingSpace, 0x6) |
| 75 | +{ |
| 76 | + enum { CanNotBuild = 0x4454E6, BuildFailed = 0x445696 }; |
| 77 | + |
| 78 | + GET(BaseNodeClass* const, pBaseNode, EBX); |
| 79 | + GET(BuildingClass* const, pBuilding, EDI); |
| 80 | + GET(const CellStruct, topLeftCell, EDX); |
| 81 | + |
| 82 | + const auto pBuildingType = pBuilding->Type; |
| 83 | + |
| 84 | + // Prohibit AI from building construction yard |
| 85 | + if (RulesExt::Global()->AIForbidConYard && pBuildingType->ConstructionYard) |
| 86 | + { |
| 87 | + if (pBaseNode) |
| 88 | + { |
| 89 | + pBaseNode->Placed = true; |
| 90 | + pBaseNode->Attempts = 0; |
| 91 | + } |
| 92 | + |
| 93 | + return BuildFailed; |
| 94 | + } |
| 95 | + |
| 96 | + // Clean up invalid walls nodes |
| 97 | + if (RulesExt::Global()->AICleanWallNode && pBuildingType->Wall) |
| 98 | + { |
| 99 | + auto notValidWallNode = [topLeftCell]() |
| 100 | + { |
| 101 | + const auto pCell = MapClass::Instance.GetCellAt(topLeftCell); |
| 102 | + |
| 103 | + for (int i = 0; i < 8; ++i) |
| 104 | + { |
| 105 | + if (const auto pAdjBuilding = pCell->GetNeighbourCell(static_cast<FacingType>(i))->GetBuilding()) |
| 106 | + { |
| 107 | + if (pAdjBuilding->Type->ProtectWithWall) |
| 108 | + return false; |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + return true; |
| 113 | + }; |
| 114 | + |
| 115 | + if (notValidWallNode()) |
| 116 | + return CanNotBuild; |
| 117 | + } |
| 118 | + |
| 119 | + return 0; |
| 120 | +} |
| 121 | + |
| 122 | +// AIConstructionYard Hook #4-2 -> sub_42EB50 - Prohibit AI from building construction yard. |
| 123 | +DEFINE_HOOK(0x42EB8E, BaseClass_GetBaseNodeIndex_CheckValidBaseNode, 0x6) |
| 124 | +{ |
| 125 | + enum { Valid = 0x42EBC3, Invalid = 0x42EBAE }; |
| 126 | + |
| 127 | + GET(BaseClass* const, pBase, ESI); |
| 128 | + GET(BaseNodeClass* const, pBaseNode, EAX); |
| 129 | + |
| 130 | + if (RulesExt::Global()->AIForbidConYard && pBaseNode->Placed) |
| 131 | + { |
| 132 | + const auto index = pBaseNode->BuildingTypeIndex; |
| 133 | + |
| 134 | + if (index >= 0 && index < BuildingTypeClass::Array.Count && BuildingTypeClass::Array.Items[index]->ConstructionYard) |
| 135 | + return Invalid; |
| 136 | + } |
| 137 | + |
| 138 | + return reinterpret_cast<bool(__thiscall*)(HouseClass*, BaseNodeClass*)>(0x50CAD0)(pBase->Owner, pBaseNode) ? Valid : Invalid; |
| 139 | +} |
| 140 | + |
| 141 | +// AIConstructionYard Hook #4-3 -> sub_7393C0 - Prohibit AI from building construction yard. |
| 142 | +DEFINE_HOOK(0x7397F4, UnitClass_TryToDeploy_SkipSetShouldRebuild, 0x7) |
| 143 | +{ |
| 144 | + enum { SkipRebuildFlag = 0x7397FB }; |
| 145 | + |
| 146 | + GET(BuildingClass* const, pBuilding, EBX); |
| 147 | + |
| 148 | + return (pBuilding->Type->ConstructionYard && RulesExt::Global()->AIForbidConYard) ? SkipRebuildFlag : 0; |
| 149 | +} |
| 150 | + |
| 151 | +// AIConstructionYard Hook #4-4 -> sub_440580 - Prohibit AI from building construction yard. |
| 152 | +DEFINE_HOOK(0x440B7A, BuildingClass_Unlimbo_SkipSetShouldRebuild, 0x7) |
| 153 | +{ |
| 154 | + enum { SkipRebuildFlag = 0x440B81 }; |
| 155 | + |
| 156 | + GET(BuildingClass* const, pBuilding, ESI); |
| 157 | + |
| 158 | + return (pBuilding->Type->ConstructionYard && RulesExt::Global()->AIForbidConYard) ? SkipRebuildFlag : 0; |
| 159 | +} |
| 160 | + |
| 161 | +// AIConstructionYard Hook #5-1 -> sub_588570 - Only expand walls on nodes. |
| 162 | +DEFINE_HOOK(0x5885D1, MapClass_BuildingToFirestormWall_SkipExtraWalls, 0x6) |
| 163 | +{ |
| 164 | + enum { NextDirection = 0x588730 }; |
| 165 | + |
| 166 | + GET_STACK(const HouseClass* const, pHouse, STACK_OFFSET(0x38, 0x8)); |
| 167 | + GET_STACK(const int, count, STACK_OFFSET(0x38, -0x24)); |
| 168 | + |
| 169 | + if (pHouse->IsControlledByHuman() || !RulesExt::Global()->AINodeWallsOnly || count) |
| 170 | + return 0; |
| 171 | + |
| 172 | + GET(const CellStruct, cell, EBX); |
| 173 | + GET(const BuildingTypeClass* const, pType, EBP); |
| 174 | + |
| 175 | + const auto index = pType->ArrayIndex; |
| 176 | + const auto& nodes = pHouse->Base.BaseNodes; |
| 177 | + |
| 178 | + for (const auto& pNode : nodes) |
| 179 | + { |
| 180 | + if (pNode.MapCoords == cell && pNode.BuildingTypeIndex == index) |
| 181 | + return 0; |
| 182 | + } |
| 183 | + |
| 184 | + return NextDirection; |
| 185 | +} |
| 186 | + |
| 187 | +// AIConstructionYard Hook #5-2 -> sub_588750 - Only expand walls on nodes. |
| 188 | +DEFINE_HOOK(0x5887C1, MapClass_BuildingToWall_SkipExtraWalls, 0x6) |
| 189 | +{ |
| 190 | + enum { NextDirection = 0x588935 }; |
| 191 | + |
| 192 | + GET_STACK(const HouseClass* const, pHouse, STACK_OFFSET(0x3C, 0x8)); |
| 193 | + GET_STACK(const int, count, STACK_OFFSET(0x3C, -0x2C)); |
| 194 | + |
| 195 | + if (pHouse->IsControlledByHuman() || !RulesExt::Global()->AINodeWallsOnly || count) |
| 196 | + return 0; |
| 197 | + |
| 198 | + GET(const CellStruct, cell, EDX); |
| 199 | + GET(const BuildingTypeClass* const, pType, EDI); |
| 200 | + |
| 201 | + const auto index = pType->ArrayIndex; |
| 202 | + const auto& nodes = pHouse->Base.BaseNodes; |
| 203 | + |
| 204 | + for (const auto& pNode : nodes) |
| 205 | + { |
| 206 | + if (pNode.MapCoords == cell && pNode.BuildingTypeIndex == index) |
| 207 | + return 0; |
| 208 | + } |
| 209 | + |
| 210 | + return NextDirection; |
| 211 | +} |
0 commit comments