Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
9adb0fe
Initial commit
FS-21 Dec 2, 2022
bb8171e
I forgot to merge the unclassified category at the end of the analysis
FS-21 Dec 2, 2022
66b8a55
Cleaned code
FS-21 Dec 3, 2022
355c682
.
FS-21 Dec 3, 2022
5dc2862
.
FS-21 Dec 3, 2022
ceb07ad
.
FS-21 Dec 3, 2022
f1f4369
Added documentation
FS-21 Dec 3, 2022
f0b9a56
What's new and credits file changes
FS-21 Dec 3, 2022
ee5ca9d
trailing space
FS-21 Dec 3, 2022
45b0456
FS-21 Jan 11, 2023
9712713
Updated documentation
FS-21 Jan 11, 2023
08691d5
Secret Lab support for the AI and bug fixes
FS-21 Feb 7, 2023
c287773
FS-21 Feb 8, 2023
ee04ad6
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Feb 10, 2023
57a9487
Fix compilation after the develop merge
FS-21 Feb 10, 2023
c0dfeda
Merge remote-tracking branch 'origin/develop' into feature/new-ai-tea…
FS-21 Feb 12, 2023
36e08b4
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Feb 18, 2023
035abe8
crash fix
FS-21 Feb 18, 2023
9cc658d
Code improvements and additions
FS-21 May 26, 2023
28e715b
fixed the object type I changed by mistake
FS-21 May 26, 2023
809e022
Comment a debug block
FS-21 May 26, 2023
4b1f4f6
Merge remote-tracking branch 'origin/develop' into feature/new-ai-tea…
FS-21 May 26, 2023
7e17960
Point to latest YRpp
FS-21 May 26, 2023
03d6c43
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Jul 22, 2023
e12d733
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Sep 23, 2023
44174cb
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Sep 25, 2023
2687a95
Clear some vectors before filling with elements
FS-21 Dec 13, 2023
8477602
ParentCountry support
FS-21 Dec 22, 2023
1190d4f
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Mar 10, 2024
3352951
Merge remote-tracking branch 'origin/develop' into feature/new-ai-tea…
FS-21 Mar 10, 2024
cd17116
Merge remote-tracking branch 'origin/develop' into feature/new-ai-tea…
FS-21 Mar 10, 2024
0a8284f
Fix compilation
FS-21 Mar 11, 2024
8558823
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Mar 12, 2024
14ffad9
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Apr 29, 2024
5a2fa0f
Some prerequisites Fixes
FS-21 Apr 29, 2024
149a718
Moved code into its own file
FS-21 Apr 29, 2024
7e5f461
Moved code into its own file
FS-21 Apr 29, 2024
2280ae2
Moved code into the new file
FS-21 Apr 29, 2024
6731097
Fix rare crash and tweaks
FS-21 May 1, 2024
429d957
Added priority to defensive teams
FS-21 May 11, 2024
fc2d405
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Jul 12, 2024
b520f60
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Jul 14, 2024
3f2cf24
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Aug 29, 2024
5c0e5fd
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Aug 30, 2024
6d47382
Bug Fixes and some Trigger conditions implementations
FS-21 Oct 26, 2024
2aa5859
merge from develop
FS-21 Oct 26, 2024
06cf870
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Oct 27, 2024
7147769
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Oct 28, 2024
f74c069
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Dec 9, 2024
f129cb4
Merge remote-tracking branch 'origin/develop' into feature/new-ai-tea…
FS-21 Feb 18, 2025
9f2a84e
Fixes & tweaks
FS-21 Feb 18, 2025
15c5e97
Fix
FS-21 Feb 18, 2025
cd8a81f
Tweak
FS-21 Feb 20, 2025
0ca0c9b
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 May 19, 2025
d9a1bd1
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 May 23, 2025
5d6532c
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 May 29, 2025
c7ba0ef
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 May 30, 2025
af01816
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Jun 7, 2025
c6d4df3
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Jun 8, 2025
cc7778e
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Jul 5, 2025
bd950d0
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Aug 7, 2025
3a6f2e1
Fix AI in single player campaigns
FS-21 Aug 10, 2025
b497b0a
Fix crash
FS-21 Aug 11, 2025
0b35fe1
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Sep 2, 2025
f18e11d
small improvements
FS-21 Oct 30, 2025
e9d9d4d
Code tweaks & fixes
FS-21 Dec 9, 2025
27451c5
Merge remote-tracking branch 'origin/develop' into feature/new-ai-tea…
FS-21 Dec 9, 2025
01067ae
Unify and optimize HouseExt::IsAvailableToHouse and PrerequisitesMet
FS-21 Aug 28, 2026
4727c12
Eliminate ownedBuildings map and use pHouse->ActiveBuildingTypes in P…
FS-21 Aug 28, 2026
b4461db
Optimize PrerequisiteTheaters to DWORD bitmask across TechnoType
FS-21 Aug 28, 2026
cc49ea0
Add stolen tech checks and correct Prerequisite.Lists boolean logic i…
FS-21 Aug 29, 2026
e6aa412
Add BuildLimit lifetime checks and Phobos BuildLimitGroup in Prerequi…
FS-21 Aug 29, 2026
d286456
Add general support for Prerequisite<Name>Alternate technos in Generi…
FS-21 Aug 29, 2026
b20b8a0
Normalize prerequisite system with building upgrades, nested generic …
FS-21 Aug 29, 2026
a9efc10
Optimize AI Team Selector data structures, eliminate temporary vector…
FS-21 Aug 29, 2026
84b6cf3
Refactor AI Team Selector weight discrete distribution and eliminate …
FS-21 Aug 29, 2026
4e608a7
Add dedicated Infantry category to AI Team Selector and fix fallback …
FS-21 Aug 29, 2026
bbd6231
Optimize AI Team Selector with O(1) active team type counting, robust…
FS-21 Aug 29, 2026
33e4e99
Clean up code comments in AI Team Selector
FS-21 Aug 29, 2026
355b7a7
Filter dead and destroying objects early in AI Team Selector map scan…
FS-21 Aug 29, 2026
ea59bf0
Refactor AI trigger candidate loop with early exits, switch jump tabl…
FS-21 Aug 29, 2026
e97a7d6
Simplify Autocreate and Recruiter validation logic with early exits
FS-21 Aug 29, 2026
501dac8
Unify roulette wheel selection to eliminate duplicated selection loops
FS-21 Aug 29, 2026
c056c79
Optimize trigger preload with difficulty filtering and single-pass tr…
FS-21 Aug 29, 2026
48320ef
Filter strictly ground-only offensive triggers when no enemy targets …
FS-21 Aug 29, 2026
e1a1634
Revert "Filter strictly ground-only offensive triggers when no enemy …
FS-21 Aug 29, 2026
0208b24
Fix maxTeamsLimit variable passed in TotalAITeamCap log message
FS-21 Aug 29, 2026
0b1f7ef
Merge develop into feature/new-ai-team-selector
FS-21 Aug 29, 2026
b78dc9f
Refactor trigger condition evaluation into TeamExt::EvaluateTriggerCo…
FS-21 Aug 30, 2026
b8e69ad
comments
FS-21 Aug 30, 2026
eced11e
Add NewTeamsSelector.VIPWeight tag supporting custom thresholds and d…
FS-21 Aug 30, 2026
9f28fce
Merge branch 'develop' into feature/new-ai-team-selector
FS-21 Aug 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ This page lists all the individual contributions to the project by their author.
- Unit & infantry auto-conversion on ammo change
- Restore the ScriptType action#24 `Play speech` from Tiberian Sun
- Modify ammo on impact
- New AI teams selector
- **Starkku**:
- Misc. minor bugfixes & improvements
- AI script actions:
Expand Down
3 changes: 3 additions & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<ClCompile Include="src\Ext\Event\Body.cpp" />
<!-- src\Ext\House -->
<ClCompile Include="src\Ext\House\Body.cpp" />
<ClCompile Include="src\Ext\House\Body.Prerequisites.cpp" />
<ClCompile Include="src\Ext\House\Hooks.AINavalProduction.cpp" />
<ClCompile Include="src\Ext\House\Hooks.cpp" />
<ClCompile Include="src\Ext\House\Hooks.ForceEnemy.cpp" />
Expand All @@ -128,6 +129,7 @@
<!-- src\Ext\Scenario -->
<ClCompile Include="src\Ext\Scenario\Body.cpp" />
<ClCompile Include="src\Ext\Scenario\Hooks.cpp" />
<ClCompile Include="src\Ext\Scenario\Hooks.NewTeamsSelector.cpp" />
<ClCompile Include="src\Ext\Scenario\Hooks.Variables.cpp" />
<ClCompile Include="src\Ext\Scenario\Hooks.Waypoints.cpp" />
<!-- src\Ext\Script -->
Expand Down Expand Up @@ -159,6 +161,7 @@
<ClCompile Include="src\Ext\TAction\Hooks.cpp" />
<!-- src\Ext\Team -->
<ClCompile Include="src\Ext\Team\Body.cpp" />
<ClCompile Include="src\Ext\Team\Body.NewTeamsSelector.cpp" />
<ClCompile Include="src\Ext\Team\Hooks.cpp" />
<!-- src\Ext\TeamType -->
<ClCompile Include="src\Ext\TeamType\Body.cpp" />
Expand Down
76 changes: 76 additions & 0 deletions docs/AI-Scripting-and-Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,82 @@ ShowBriefing=true ; boolean
- You can use `NextScenario` and `AltNextScenario` to specify the map names required to enter a new campaign, thereby forcing the game to proceed to the next campaign.
- Now, setting a local variable named `<Alternate Next Scenario>` will also trigger `AltNextScenario`.

## New AI Teams Selector

- New AI system for selecting valid triggers in multiplayer randomly. Unlike the original method this one checks prerequisites and take care of other details.
- It can split the valid triggers into 5 categories: ground vehicles, infantry, air, naval & mixed categories. If set, AI picks a random trigger in a random category.
- Categories can have different chance probabilities. It can be set globally or customized per house. By default each category has a 20% chance to be selected.
- `NewTeamsSelector.MergeUnclassifiedCategoryWith` can be used for merging the mixed category (units are from different categories) into one of the main categories.
- In case of picking a category without valid triggers exist a fallback mode that allow picking a trigger from all valid triggers like if categories were disabled.
- If `Autocreate=yes` AI will care about all units prerequisites so if the house's tech tree is incomplete for the trigger it gets discarded. It understands Ares tags like `Prerequisite.RequiredTheaters`, `Prerequisite.Negative`, `Prerequisite.Lists` & `Generic prerequisites` section.
- `NewTeamsSelector.VIPWeight` controls the VIP trigger threshold. If a trigger meets this threshold, AI discards non-VIP candidates and picks exclusively from VIP triggers with equal chance (categories are bypassed in VIP mode).
- Defaults to `5000.0` (vanilla behavior).
- Setting `NewTeamsSelector.VIPWeight=-1` enables dynamic VIP mode where each trigger becomes VIP when its `Weight_Current >= Weight_Maximum`.
- Setting `NewTeamsSelector.VIPWeight=0` disables VIP mode completely.
- Units can override the category using `ConsideredVehicle` and `ConsideredNaval` boolean tags.
- AI is able to use unlocked units in captured Secret Labs.

In `rulesmd.ini`:
```ini
[AI]
NewTeamsSelector=false ; boolean
NewTeamsSelector.SplitTriggersByCategory=true ; boolean
NewTeamsSelector.EnableFallback=false ; boolean
NewTeamsSelector.GroundCategoryPercentage=0.20 ; floating point value, percents or absolute
NewTeamsSelector.InfantryCategoryPercentage=0.20 ; floating point value, percents or absolute
NewTeamsSelector.NavalCategoryPercentage=0.20 ; floating point value, percents or absolute
NewTeamsSelector.AirCategoryPercentage=0.20 ; floating point value, percents or absolute
NewTeamsSelector.UnclassifiedCategoryPercentage=0.20 ; floating point value, percents or absolute
NewTeamsSelector.MergeUnclassifiedCategoryWith=-1 ; Integer - Ground: 1, Air: 2, Naval: 3, Infantry: 4
NewTeamsSelector.VIPWeight=5000.0 ; floating point value, -1 for dynamic max weight, 0 to disable

[SOMEHOUSE] ; HouseType
NewTeamsSelector.MergeUnclassifiedCategoryWith= ; Integer - Ground: 1, Air: 2, Naval: 3, Infantry: 4
NewTeamsSelector.UnclassifiedCategoryPercentage= ; floating point value, percents or absolute
NewTeamsSelector.GroundCategoryPercentage= ; floating point value, percents or absolute
NewTeamsSelector.InfantryCategoryPercentage= ; floating point value, percents or absolute
NewTeamsSelector.AirCategoryPercentage= ; floating point value, percents or absolute
NewTeamsSelector.NavalCategoryPercentage= ; floating point value, percents or absolute
NewTeamsSelector.VIPWeight= ; floating point value, -1 for dynamic max weight, 0 to disable

[SOMETECHNO] ; TechnoType
ConsideredNaval= ; boolean
ConsideredVehicle= ; boolean
```

- Modified slightly AI Trigger conditions 0 (enemy owns ...), 1 (house owns ...) and 7 (civilian owns ...) and added 10 new conditions (from 8 to 17) that check lists of objects instead of only 1 unit. The first 3 were modified because the objects counters weren't updated in real-time. The possible modified and new cases are:

| *Condition* | *List of objects* | *Description* |
| :---------: | :---------------: | :---------------------------------------------: |
| 0 (changed) | No | Count objects of the main enemy. If AI house doesn't have a main enemy It will search in all enemies. In vanilla YR if house doesn't have a main enemy this condition fail |
| 1 (changed) | No | Count own objects |
| 7 (changed) | No | Count civilian objects |
| 8 | No | Count enemy objects. It will search in all enemies |
| 9 | Yes | Count enemy objects. If AI house doesn't have a main enemy It will search in all enemies. |
| 10 | Yes | Count own objects |
| 11 | Yes | Count civilian objects |
| 12 | Yes | Count enemy objects. It will search in all enemies |
| 13 | Yes | Count objects from allies |
| 14 | Yes | Count enemy objects. If AI house doesn't have a main enemy It will search in all enemies. It uses `AND` comparator so each object must obey the same comparations |
| 15 | Yes | Count own objects. It uses `AND` comparator so each object must obey the same comparations |
| 16 | Yes | Count civilian objects. It uses `AND` comparator so each object must obey the same comparations |
| 17 | Yes | Count enemy objects. It will search in all enemies. It uses `AND` comparator so each object must obey the same comparations |
| 18 | No | Count structures with `BridgeRepairHut=yes` linked with destroyed bridges |
| 19 | No | Count structures with `BridgeRepairHut=yes` linked with undamaged bridges |

- Some trigger conditions need to specify a 0-based list from `[AITargetTypes]`. The index of the list is written in hexadecimal and in little endian format. The value must be written at the end of the trigger:
`00000000000000000000000000000000000000000000000000000000AABBCCDD`
For example: list 0 is written 00 (put it in AA), list 1 is written 01 (put it in AA), list 10 is written 0A (put it in AA), 255 is 0xFF (put it in AA) and 256 is 0x0001 (put it in AABB), etc.
- The *`AITargetTypes` index#* values are obtained in the new `AITargetTypes` section that must be declared in `rulesmd.ini`:

In `rulesmd.ini`:
```ini
[AITargetTypes] ; List of TechnoType lists
0=SOMETECHNOTYPE,SOMEOTHERTECHNOTYPE,SAMPLETECHNOTYPE
1=ANOTHERTECHNOTYPE,YETANOTHERTECHNOTYPE
; ...
```

## Script Actions

### `10000-10999` Ingame Actions
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,7 @@ HideShakeEffects=false ; boolean
- Customizable ROF random delay (by Starkku)
- Animation with `Tiled=yes` now supports `CustomPalette` (by ststl)
- Toggleable `DieSound` when grinding (by Trsdy)
- New AI teams selector (by FS-21)
- Shields can inherit Techno ArmorType (by Starkku)
- Income money flying-string display when harvesters or slaves are docking to refineries or when spies steal credits (by Trsdy)
- Allow random crates to be generated only on lands (by Trsdy)
Expand Down
118 changes: 118 additions & 0 deletions src/Ext/Building/Body.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "Body.h"

#include <BitFont.h>
#include <Utilities/EnumFunctions.h>
#include <Ext/House/Body.h>
#include <Misc/FlyingStrings.h>
#include <Utilities/AresHelper.h>

Expand Down Expand Up @@ -442,6 +444,121 @@ const std::vector<CellStruct> BuildingExt::GetFoundationCells(BuildingClass* con
return foundationCells;
}

// Assigns a secret production option to the AI building (Ares doesn't handle the AI case).
void BuildingExt::UpdateSecretLabAI()
{
auto pThis = this->OwnerObject();
auto pOwner = pThis->Owner;

if (!pOwner || pOwner->Type->MultiplayPassive || pOwner->IsControlledByHuman())
return;

auto pType = pThis->Type;

// Fixed item, no need to randomize
if (pType->SecretInfantry || pType->SecretUnit || pType->SecretBuilding)
return;

auto pDataType = BuildingTypeExt::Fetch(pType);

// If Secret Lab already picked a techno and isn't allowed to recalculate it again the function ends
if (this->SecretLab_Placed && (!pDataType || !pDataType->Secret_RecalcOnCapture))
return;

DynamicVectorClass<TechnoTypeClass*> validCandidates;
DynamicVectorClass<TechnoTypeClass*> possibleCandidates;

if (pDataType && pDataType->PossibleBoons.size() > 0)
{
for (const auto& boon : pDataType->PossibleBoons)
{
possibleCandidates.AddItem(boon);
}
}
else
{
for (const auto& boon : RulesClass::Instance->SecretInfantry)
{
possibleCandidates.AddItem(boon);
}

for (const auto& boon : RulesClass::Instance->SecretUnits)
{
possibleCandidates.AddItem(boon);
}

for (const auto& boon : RulesClass::Instance->SecretBuildings)
{
possibleCandidates.AddItem(boon);
}
}

if (possibleCandidates.Count > 0)
{
for (const auto& boon : possibleCandidates)
{
auto pExt = TechnoTypeExt::Fetch(boon);
bool isRequiredHouse = true; // Default value if Secret.RequiredHouses isn't declared

if (pExt && pExt->Secret_RequiredHouses.size() > 0)
isRequiredHouse = false;

if (pExt)
{
for (const auto houseId : pExt->Secret_RequiredHouses)
{
int houseIdx = HouseTypeClass::FindIndex(houseId.c_str());
if (houseIdx < 0)
continue;

if (pOwner->Type->ArrayIndex == houseIdx)
{
bool canBeBuilt = HouseExt::PrerequisitesMet(pOwner, boon, true);

if (canBeBuilt)
{
isRequiredHouse = true;
break;
}
}
}

bool isForbiddenHouse = false; // Default value if Secret.ForbiddenHouses isn't declared

for (const auto houseId : pExt->Secret_ForbiddenHouses)
{
int houseIdx = HouseTypeClass::FindIndex(houseId.c_str());
if (houseIdx < 0)
continue;

if (pOwner->Type->ArrayIndex == houseIdx)
{
isForbiddenHouse = true;
break;
}
}

if (isRequiredHouse && !isForbiddenHouse)
validCandidates.AddItem(boon);
}
}
}

// pick one of all eligible items
if (validCandidates.Count > 0)
{
auto result = validCandidates[ScenarioClass::Instance->Random.RandomRanged(0, validCandidates.Count - 1)];
Debug::Log("[Secret Lab AI] rolled %s for %s\n", result->ID, pType->ID);
pThis->SecretProduction = result;
this->SecretLab_Placed = true;
}
else
{
Debug::Log("[Secret Lab AI] %s has no boons applicable to country [%s]!\n",
pType->ID, pOwner->Type->ID);
}
}

WeaponStruct* BuildingExt::GetLaserWeapon(BuildingClass* pThis)
{
auto const pExt = BuildingExt::Fetch(pThis);
Expand Down Expand Up @@ -595,6 +712,7 @@ void BuildingExt::Serialize(T& Stm)
.Process(this->GrindingWeapon_LastFiredFrame)
.Process(this->GrindingWeapon_AccumulatedCredits)
.Process(this->CurrentAirFactory)
.Process(this->SecretLab_Placed)
.Process(this->AccumulatedIncome)
.Process(this->CurrentLaserWeaponIndex)
.Process(this->PoweredUpToLevel)
Expand Down
4 changes: 3 additions & 1 deletion src/Ext/Building/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class BuildingExt final : public TechnoExt, public Detach::Listener<BuildingClas
int GrindingWeapon_LastFiredFrame;
int GrindingWeapon_AccumulatedCredits;
BuildingClass* CurrentAirFactory;
bool SecretLab_Placed;
int AccumulatedIncome;
std::optional<int> CurrentLaserWeaponIndex;
int PoweredUpToLevel; // Distinct from UpgradeLevel, and set to highest PowersUpToLevel out of applied upgrades regardless of how many are currently applied to this building.
Expand All @@ -39,6 +40,7 @@ class BuildingExt final : public TechnoExt, public Detach::Listener<BuildingClas
, GrindingWeapon_LastFiredFrame { 0 }
, GrindingWeapon_AccumulatedCredits { 0 }
, CurrentAirFactory { nullptr }
, SecretLab_Placed { false }
, AccumulatedIncome { 0 }
, CurrentLaserWeaponIndex {}
, PoweredUpToLevel { 0 }
Expand Down Expand Up @@ -67,6 +69,7 @@ class BuildingExt final : public TechnoExt, public Detach::Listener<BuildingClas
void ApplyPoweredKillSpawns();
bool HasSuperWeapon(int index) const;
bool HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefore);
void UpdateSecretLabAI();
void UpdatePrimaryFactoryAI();

virtual ~BuildingExt() = default;
Expand Down Expand Up @@ -124,4 +127,3 @@ class BuildingExt final : public TechnoExt, public Detach::Listener<BuildingClas
static int GetTurretFrame(BuildingClass* pThis);
static bool BuildingOnline(BuildingClass* pThis);
};

13 changes: 13 additions & 0 deletions src/Ext/Building/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,19 @@ DEFINE_HOOK(0x4AE95E, DisplayClass_sub_4AE750_DisallowBuildingNonAttackPlanning,

#pragma endregion

DEFINE_HOOK(0x445F80, BuildingClass_GrandOpening_UpdateSecretLabAI, 0x5)
{
GET(BuildingClass*, pThis, ECX);

if (pThis->Type->SecretLab && !pThis->Owner->IsControlledByHuman())
{
auto pExt = BuildingExt::Fetch(pThis);
pExt->UpdateSecretLabAI();
}

return 0;
}

DEFINE_HOOK(0x4400F9, BuildingClass_AI_UpdateOverpower, 0x6)
{
enum { SkipGameCode = 0x44019D };
Expand Down
18 changes: 18 additions & 0 deletions src/Ext/BuildingType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,22 @@ void BuildingTypeExt::LoadFromINIFile(CCINIClass* const pINI)

this->RevealToAll_Radius.Read(exINI, pSection, "RevealToAll.Radius");

this->Secret_RecalcOnCapture.Read(exINI, pSection, "SecretLab.GenerateOnCapture");

// Secret.Boons contains a list of TechnoTypeClass IDs
const char* key = "SecretLab.PossibleBoons";
char* context = nullptr;
pINI->ReadString(pSection, key, "", Phobos::readBuffer);

for (char* cur = strtok_s(Phobos::readBuffer, Phobos::readDelims, &context); cur; cur = strtok_s(nullptr, Phobos::readDelims, &context))
{
int index = TechnoTypeClass::FindIndex(cur);
if (index != -1)
this->PossibleBoons.push_back(TechnoTypeClass::Array.GetItem(index));
}

key = nullptr;

if (pThis->NumberOfDocks > 0)
{
std::optional<DirType> empty;
Expand Down Expand Up @@ -439,6 +455,8 @@ void BuildingTypeExt::Serialize(T& Stm)
.Process(this->ConsideredVehicle)
.Process(this->ZShapePointMove_OnBuildup)
.Process(this->SellBuildupLength)
.Process(this->Secret_RecalcOnCapture)
.Process(this->PossibleBoons)
.Process(this->AircraftDockingDirs)
.Process(this->AircraftDockingDir_DefaultToPoseDir)
.Process(this->FactoryPlant_AllowTypes)
Expand Down
8 changes: 7 additions & 1 deletion src/Ext/BuildingType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ class BuildingTypeExt final : public TechnoTypeExt

Valueable<bool> IsAnimDelayedBurst;

Valueable<bool> Secret_RecalcOnCapture;
NullableVector<TechnoTypeClass*> PossibleBoons;

std::vector<std::optional<DirType>> AircraftDockingDirs;
Nullable<bool> AircraftDockingDir_DefaultToPoseDir;
Nullable<bool> AircraftDockingDir_DefaultToPoseDir;

ValueableVector<TechnoTypeClass*> FactoryPlant_AllowTypes;
ValueableVector<TechnoTypeClass*> FactoryPlant_DisallowTypes;
Expand Down Expand Up @@ -263,6 +266,9 @@ class BuildingTypeExt final : public TechnoTypeExt
// Ares 0.E
, Tunnel { false }

, Secret_RecalcOnCapture { false }
, PossibleBoons {}

// Ares 3.0
, UnitSell {}
{ }
Expand Down
Loading
Loading