Skip to content

Commit

Permalink
Two skirmish AI behavior dehardcoded and a minor improvement (#1350)
Browse files Browse the repository at this point in the history
### Skirmish AI behavior dehardcode

- In vanilla, there is a hardcoded behavior that when an skirmish AI
player has no factory and has not taken damage for a while, it will sell
its buildings and set its units to hunt. This can be customized now.
- `[General]->AISellAllOnLastLegs` and `[General]->AIAllInOnLastLegs`
control whether the AI will act selling and hunting respectively.
- `[General]->AISellAllDelay` defines a timer, it will only work if
`[General]->AISellAllOnLastLegs` is set to `true`. When the first time
the AI reaches the trigger condition of vanilla behavior, the timer
starts and prevents the selling behavior from happening until the timer
is expired.
- You can use these flags to make the AIs "all in" before they are
defeated.
- Another hardcoded behavior is that, when the AI deploys a MCV, it will
regroup all of its forces to that place. This can be toggle off now.
  - `[General]->RegroupWhenMCVDeploy` controls this behavior.

In `rulesmd.ini`:
```ini
[General]
AISellAllOnLastLegs=true   ; boolean
AISellAllDelay=0           ; integer, number of frames
AIAllInOnLastLegs=true     ; boolean
RegroupWhenMCVDeploy=true  ; boolean
```
### Global setting for `[Country House]->RepairBaseNodes` and
`[Basic]->MCVRedeploys`

- Self-evident.

---------

Co-authored-by: MortonPL <[email protected]>
Co-authored-by: Kerbiter <[email protected]>
  • Loading branch information
3 people authored Sep 26, 2024
1 parent fca5e1f commit efbcf18
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ This page lists all the individual contributions to the project by their author.
- Build limit group enhancement
- Customizable rocker amplitude
- **handama** - AI script action to jump back to previous script
- **TaranDahl (航味麻酱)**
- Skirmish AI "sell all buildings and set all technos to hunt" behavior dehardcode
- Skirmish AI "gather when MCV deploy" behavior dehardcode
- Global value of `RepairBaseNodes` and `MCVRedeploys`
- **Ares developers**
- YRpp and Syringe which are used, save/load, project foundation and generally useful code from Ares
- unfinished RadTypes code
Expand Down
21 changes: 17 additions & 4 deletions docs/AI-Scripting-and-Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@ This page describes all AI scripting and mapping related additions and changes i

### Base node repairing

- In singleplayer campaign missions you can now decide whether AI can repair the base nodes / buildings delivered by SW (Ares) by setting `RepairBaseNodes`.
- In singleplayer campaign missions you can now decide whether AI can repair the base nodes / buildings delivered by SW (Ares).
- You can control it globally by setting `[General]->RepairBaseNodes` in rulesmd.ini, or locally by setting the flag with same name in `[Some House]` in certain map file. The global one will be overriden if the local one is set.

In rulesmd.ini:
```ini
[General]
RepairBaseNodes=false,false,false ; list of 3 booleans indicating whether AI repair basenodes in Easy / Normal / Difficult game diffculty.
```

In map file:
```ini
[Country House]
RepairBaseNodes=false,false,false ; list of 3 booleans indicating whether AI repair basenodes in Easy / Normal / Difficult game diffculty.
RepairBaseNodes= ; list of 3 booleans indicating whether AI repair basenodes in Easy / Normal / Difficult game diffculty.
```

### Default loading screen and briefing offsets
Expand All @@ -45,12 +52,18 @@ DefaultLS800BkgdPal= ; filename - including the .pal extension

### MCV redeploying

- You can now decide whether MCV can redeploy in singleplayer campaign missions by setting `MCVRedeploys`. Overrides `[MultiplayerDialogSettings]`->`MCVRedeploys` only in singleplayer campaign missions.
- You can now decide whether MCV can redeploy in singleplayer campaign missions by setting `[Basic]->MCVRedeploys` in map file. Overrides `[MultiplayerDialogSettings]`->`MCVRedeploys` only in singleplayer campaign missions. You can also define this globally by setting `[General]->MCVRedeploysInCampaign` in rulesmd.ini. The flag defined in map file has higher priority.

In rulesmd.ini:
```ini
[General]
MCVRedeploysInCampaign=false ; boolean
```

In map file:
```ini
[Basic]
MCVRedeploys=false ; boolean
MCVRedeploys= ; boolean
```

### Set par times and related string labels in missionmd.ini
Expand Down
18 changes: 18 additions & 0 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,24 @@ NoWobbles=false ; boolean
`CruiseHeight` is for `JumpjetHeight`, `WobblesPerSecond` is for `JumpjetWobbles`, `WobbleDeviation` is for `JumpjetDeviation`, and `Acceleration` is for `JumpjetAccel`. All other corresponding keys just simply have no Jumpjet prefix.
```

### Skirmish AI behavior dehardcode

- In vanilla, there is a hardcoded behavior that when an skirmish AI player has no factory and has not taken damage for a while, it will sell its buildings and set its units to hunt. This can be customized now.
- `[General]->AIFireSale` and `[General]->AIAllToHunt` control whether the AI will act selling and hunting respectively.
- `[General]->AIFireSaleDelay` defines a timer, it will only work if `[General]->AIFireSale` is set to `true`. When the first time the AI reaches the trigger condition of vanilla behavior, the timer starts and prevents the selling behavior from happening until the timer is expired.
- You can use these flags to make the AIs "all in" before they are defeated.
- Another hardcoded behavior is that, when the AI deploys a MCV, it will gather all of its forces to that place. This can be toggle off now.
- `[General]->GatherWhenMCVDeploy` controls this behavior.

In `rulesmd.ini`:
```ini
[General]
AIFireSale=true ; boolean
AIFireSaleDelay=0 ; integer, number of frames
AIAllToHunt=true ; boolean
GatherWhenMCVDeploy=true ; boolean
```

### Subterranean unit travel height

- It is now possible to control the height at which units with subterranean (Tunnel) `Locomotor` travel, globally or per TechnoType.
Expand Down
2 changes: 2 additions & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,8 @@ New:
- Animated (non-tiberium spawning) TerrainTypes (by Starkku)
- Toggleable passenger killing for Explodes=true units (by Starkku)
- New condition for automatic self-destruction logic when TechnoTypes exist/don't exist (by FlyStar)
- Skirmish AI "sell all buildings and set all technos to hunt" behavior dehardcode (by TaranDahl/航味麻酱)
- Skirmish AI "gather when MCV deploy" behavior dehardcode (by TaranDahl/航味麻酱)
Vanilla fixes:
- Fixed laser drawing code to allow for thicker lasers in house color draw mode (by Kerbiter, ChrisLv_CN)
Expand Down
13 changes: 12 additions & 1 deletion src/Ext/House/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,12 +610,22 @@ void HouseExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
ValueableVector<bool> readBaseNodeRepairInfo;
readBaseNodeRepairInfo.Read(exINI, pSection, "RepairBaseNodes");
size_t nWritten = readBaseNodeRepairInfo.size();
size_t nWrittenGlobal = RulesExt::Global()->RepairBaseNodes.size();
if ( nWrittenGlobal > 0)
{
for (size_t i = 0; i < 3; i++)
{
this->RepairBaseNodes[i] = RulesExt::Global()->RepairBaseNodes[i < nWrittenGlobal ? i : nWrittenGlobal - 1];
}
}

if (nWritten > 0)
{
for (size_t i = 0; i < 3; i++)
{
this->RepairBaseNodes[i] = readBaseNodeRepairInfo[i < nWritten ? i : nWritten - 1];
}
}

}

// =============================
Expand Down Expand Up @@ -646,6 +656,7 @@ void HouseExt::ExtData::Serialize(T& Stm)
.Process(this->NumWarFactories_NonMFB)
.Process(this->NumConYards_NonMFB)
.Process(this->NumShipyards_NonMFB)
.Process(this->AIFireSaleDelayTimer)
;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Ext/House/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class HouseExt
BuildingClass* Factory_AircraftType;

CDTimerClass AISuperWeaponDelayTimer;
CDTimerClass AIFireSaleDelayTimer;

//Read from INI
bool RepairBaseNodes[3];
Expand Down Expand Up @@ -75,6 +76,7 @@ class HouseExt
, NumWarFactories_NonMFB { 0 }
, NumConYards_NonMFB { 0 }
, NumShipyards_NonMFB { 0 }
, AIFireSaleDelayTimer {}
{ }

bool OwnsLimboDeliveredBuilding(BuildingClass* pBuilding);
Expand Down
32 changes: 32 additions & 0 deletions src/Ext/House/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,35 @@ DEFINE_HOOK(0x500910, HouseClass_GetFactoryCount, 0x5)

return SkipGameCode;
}

// Sell all and all in.
DEFINE_HOOK(0x4FD8F7, HouseClass_UpdateAI_OnLastLegs, 0x10)
{
enum { ret = 0x4FD907 };

GET(HouseClass*, pThis, EBX);

auto const pRules = RulesExt::Global();

if (pRules->AIFireSale)
{
auto const pExt = HouseExt::ExtMap.Find(pThis);

if (pRules->AIFireSaleDelay <= 0 || !pExt ||
pExt->AIFireSaleDelayTimer.Completed())
{
pThis->Fire_Sale();
}
else if (!pExt->AIFireSaleDelayTimer.HasStarted())
{
pExt->AIFireSaleDelayTimer.Start(pRules->AIFireSaleDelay);
}
}

if (pRules->AIAllToHunt)
{
pThis->All_To_Hunt();
}

return ret;
}
13 changes: 13 additions & 0 deletions src/Ext/Rules/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,13 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)

this->UseFixedVoxelLighting.Read(exINI, GameStrings::AudioVisual, "UseFixedVoxelLighting");

this->GatherWhenMCVDeploy.Read(exINI, GameStrings::General, "GatherWhenMCVDeploy");
this->AIFireSale.Read(exINI, GameStrings::General, "AIFireSale");
this->AIFireSaleDelay.Read(exINI, GameStrings::General, "AIFireSaleDelay");
this->AIAllToHunt.Read(exINI, GameStrings::General, "AIAllToHunt");
this->RepairBaseNodes.Read(exINI, GameStrings::General, "RepairBaseNodes");
this->MCVRedeploysInCampaign.Read(exINI, GameStrings::General, "MCVRedeploysInCampaign");

// Section AITargetTypes
int itemsCount = pINI->GetKeyCount("AITargetTypes");
for (int i = 0; i < itemsCount; ++i)
Expand Down Expand Up @@ -363,6 +370,12 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->VoxelLightSource)
// .Process(this->VoxelShadowLightSource)
.Process(this->UseFixedVoxelLighting)
.Process(this->GatherWhenMCVDeploy)
.Process(this->AIFireSale)
.Process(this->AIFireSaleDelay)
.Process(this->AIAllToHunt)
.Process(this->RepairBaseNodes)
.Process(this->MCVRedeploysInCampaign)
;
}

Expand Down
13 changes: 13 additions & 0 deletions src/Ext/Rules/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,13 @@ class RulesExt
// Nullable<Vector3D<float>> VoxelShadowLightSource;
Valueable<bool> UseFixedVoxelLighting;

Valueable<bool> GatherWhenMCVDeploy;
Valueable<bool> AIFireSale;
Valueable<int> AIFireSaleDelay;
Valueable<bool> AIAllToHunt;
ValueableVector<bool> RepairBaseNodes;
Valueable<bool> MCVRedeploysInCampaign;

ExtData(RulesClass* OwnerObject) : Extension<RulesClass>(OwnerObject)
, Storage_TiberiumIndex { -1 }
, InfantryGainSelfHealCap {}
Expand Down Expand Up @@ -259,6 +266,12 @@ class RulesExt
, VoxelLightSource { }
// , VoxelShadowLightSource { }
, UseFixedVoxelLighting { false }
, GatherWhenMCVDeploy { true }
, AIFireSale { true }
, AIFireSaleDelay { 0 }
, AIAllToHunt { true }
, RepairBaseNodes {}
, MCVRedeploysInCampaign { false }
{ }

virtual ~ExtData() = default;
Expand Down
2 changes: 1 addition & 1 deletion src/Ext/Scenario/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ void ScenarioExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
{
Nullable<bool> SP_MCVRedeploy;
SP_MCVRedeploy.Read(exINI, GameStrings::Basic, GameStrings::MCVRedeploys);
GameModeOptionsClass::Instance->MCVRedeploy = SP_MCVRedeploy.Get(false);
GameModeOptionsClass::Instance->MCVRedeploy = SP_MCVRedeploy.Get(RulesExt::Global()->MCVRedeploysInCampaign);

CCINIClass* pINI_MISSIONMD = CCINIClass::LoadINIFile(GameStrings::MISSIONMD_INI);
auto const scenarioName = pThis->FileName;
Expand Down
10 changes: 10 additions & 0 deletions src/Ext/Techno/Hooks.Misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,13 @@ DEFINE_HOOK(0x6B7600, SpawnManagerClass_AI_InitDestination, 0x6)

return R->Origin() == 0x6B7600 ? SkipGameCode1 : SkipGameCode2;
}

// I must not regroup my forces.
DEFINE_HOOK(0x739920, UnitClass_TryToDeploy_DisableRegroupAtNewConYard, 0x6)
{
enum { SkipRegroup = 0x73992B, DoNotSkipRegroup = 0 };

auto const pRules = RulesExt::Global();

return pRules->GatherWhenMCVDeploy ? DoNotSkipRegroup : SkipRegroup;
}

0 comments on commit efbcf18

Please sign in to comment.