Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 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
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 @@ -149,6 +149,7 @@ This page lists all the individual contributions to the project by their author.
- Warhead activation target health thresholds enhancements
- Event 606: AttachEffect is attaching to a Techno
- Linked superweapons
- New AI teams selector
- **Starkku**:
- Misc. minor bugfixes & improvements
- AI script actions:
Expand Down
5 changes: 5 additions & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,16 @@
<ClCompile Include="src\Ext\Bullet\Trajectories\PhobosTrajectory.cpp" />
<ClCompile Include="src\Ext\Bullet\Trajectories\StraightTrajectory.cpp" />
<ClCompile Include="src\Ext\Bullet\Trajectories\ParabolaTrajectory.cpp" />
<ClCompile Include="src\Ext\HouseType\Body.cpp" />
<ClCompile Include="src\Ext\CaptureManager\Hooks.cpp" />
<ClCompile Include="src\Ext\CaptureManager\Body.cpp" />
<ClCompile Include="src\Ext\House\Body.Prerequisites.cpp" />
<ClCompile Include="src\Ext\OverlayType\Body.cpp" />
<ClCompile Include="src\Ext\OverlayType\Hooks.cpp" />
<ClCompile Include="src\Ext\ParticleType\Body.cpp" />
<ClCompile Include="src\Ext\ParticleType\Hooks.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\Sidebar\Body.cpp" />
<ClCompile Include="src\Ext\Sidebar\Hooks.cpp" />
Expand All @@ -79,6 +82,7 @@
<ClCompile Include="src\Ext\TAction\Body.cpp" />
<ClCompile Include="src\Ext\TAction\Hooks.cpp" />
<ClCompile Include="src\Ext\Team\Body.cpp" />
<ClCompile Include="src\Ext\Team\Body.NewTeamsSelector.cpp" />
<ClCompile Include="src\Ext\Team\Hooks.cpp" />
<ClCompile Include="src\Ext\Techno\Hooks.Pips.cpp" />
<ClCompile Include="src\Ext\Techno\Body.Visuals.cpp" />
Expand Down Expand Up @@ -240,6 +244,7 @@
<ClInclude Include="src\Ext\Bullet\Trajectories\ParabolaTrajectory.h" />
<ClInclude Include="src\Ext\OverlayType\Body.h" />
<ClInclude Include="src\Ext\ParticleType\Body.h" />
<ClInclude Include="src\Ext\HouseType\Body.h" />
<ClInclude Include="src\Ext\Sidebar\Body.h" />
<ClInclude Include="src\Ext\Anim\Body.h" />
<ClInclude Include="src\Ext\Surface\Body.h" />
Expand Down
2 changes: 1 addition & 1 deletion YRpp
Submodule YRpp updated 1 files
+3 −3 ScenarioClass.h
69 changes: 69 additions & 0 deletions docs/AI-Scripting-and-Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,75 @@ In `RA2MD.INI`:
ShowBriefing=true ; boolean
```

## 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 4 categories: ground, 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 25% 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 understand Ares tags like `Prerequisite.RequiredTheaters`, `Prerequisite.Negative`, `Prerequisite.Lists` & `Generic prerequisites` section.
- If it finds a trigger with 5000 current probability weight then discard valid triggers all and start searching all valid triggers with weight 5000. AI will pick 1 randomly and decrease by 1 the current weight of the selected trigger (so if nothing happens in the next teams selection loop it won't appear in this special list). Under this scenario categories are disabled.
- Units can override the category using `ConsideredVehicle` and `ConsideredNaval` boolean tags.
- AI is be 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.25 ; floating point value, percents or absolute
NewTeamsSelector.NavalCategoryPercentage=0.25 ; floating point value, percents or absolute
NewTeamsSelector.AirCategoryPercentage=0.25 ; floating point value, percents or absolute
NewTeamsSelector.UnclassifiedCategoryPercentage=0.25 ; floating point value, percents or absolute
NewTeamsSelector.MergeUnclassifiedCategoryWith=-1 ; Integer - Ground: 1, Air: 2, Naval: 3

[SOMEHOUSE] ; HouseType
NewTeamsSelector.MergeUnclassifiedCategoryWith= ; boolean
NewTeamsSelector.UnclassifiedCategoryPercentage= ; floating point value, percents or absolute
NewTeamsSelector.GroundCategoryPercentage= ; floating point value, percents or absolute
NewTeamsSelector.AirCategoryPercentage ; floating point value, percents or absolute
NewTeamsSelector.NavalCategoryPercentage ; floating point value, percents or absolute

[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 @@ -462,6 +462,7 @@ New:
- 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
121 changes: 121 additions & 0 deletions src/Ext/Building/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <BitFont.h>

#include <Utilities/EnumFunctions.h>
#include <Ext/House/Body.h>

BuildingExt::ExtContainer BuildingExt::ExtMap;

Expand Down Expand Up @@ -451,6 +452,125 @@ 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::ExtData::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::ExtMap.Find(pType);

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

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

if (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)
{
std::map<BuildingTypeClass*, int> ownedBuildings;

for (auto building : pOwner->Buildings)
{
++ownedBuildings[building->Type];
}

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

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

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, ownedBuildings, 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);
}
}

// =============================
// load / save

Expand All @@ -466,6 +586,7 @@ void BuildingExt::ExtData::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: 4 additions & 0 deletions src/Ext/Building/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class BuildingExt
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 @@ -48,6 +49,7 @@ class BuildingExt
, GrindingWeapon_LastFiredFrame { 0 }
, GrindingWeapon_AccumulatedCredits { 0 }
, CurrentAirFactory { nullptr }
, SecretLab_Placed { false }
, AccumulatedIncome { 0 }
, CurrentLaserWeaponIndex {}
, PoweredUpToLevel { 0 }
Expand All @@ -58,6 +60,8 @@ class BuildingExt
void ApplyPoweredKillSpawns();
bool HasSuperWeapon(int index, bool withUpgrades) const;
bool HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefore);
void UpdateSecretLabAI();

void UpdatePrimaryFactoryAI();
virtual ~ExtData() = default;

Expand Down
13 changes: 13 additions & 0 deletions src/Ext/Building/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,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::ExtMap.Find(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 @@ -195,6 +195,22 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->BunkerWallsDownSound.Read(exINI, pSection, "BunkerWallsDownSound");
this->BuildingRepairedSound.Read(exINI, pSection, "BuildingRepairedSound");

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)
{
this->AircraftDockingDirs.clear();
Expand Down Expand Up @@ -309,6 +325,8 @@ void BuildingTypeExt::ExtData::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->FactoryPlant_AllowTypes)
.Process(this->FactoryPlant_DisallowTypes)
Expand Down
5 changes: 5 additions & 0 deletions src/Ext/BuildingType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class BuildingTypeExt

Valueable<bool> IsAnimDelayedBurst;

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

std::vector<std::optional<DirType>> AircraftDockingDirs;

ValueableVector<TechnoTypeClass*> FactoryPlant_AllowTypes;
Expand Down Expand Up @@ -136,6 +139,8 @@ class BuildingTypeExt
, ConsideredVehicle {}
, ZShapePointMove_OnBuildup { false }
, SellBuildupLength { 23 }
, Secret_RecalcOnCapture { false }
, PossibleBoons {}
, AircraftDockingDirs {}
, FactoryPlant_AllowTypes {}
, FactoryPlant_DisallowTypes {}
Expand Down
Loading