Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -284,6 +284,7 @@ This page lists all the individual contributions to the project by their author.
- Owner change during buildup bugfix
- Subterranean harvester pathfinding fix
- Toggle to exclude technos from base center calculations
- AI superweapon targeting improvements
- **Morton (MortonPL)**:
- `XDrawOffset` for animations
- Shield passthrough & absorption
Expand Down
1 change: 1 addition & 0 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
- Fixed a bug introduced by Ares where building types that have `UndeploysInto` cannot display `AltCameo` or `AltCameoPCX` even when you infiltrate enemy buildings with `Factory=UnitType`.
- Fixed the issue that technos cannot spawn survivors due to non-probabilistic reasons when the tech type was destroyed.
- Fixed the bug that vehicle survivor can spawn on wrong position when transport has been destroyed.
- `SW.AITargeting=PsychicDominator` superweapons now ignore `Insignificant=true` as well as owned by `MultiplayPassive=true` house targets.

## Newly added global settings

Expand Down
18 changes: 18 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,24 @@ In `rulesmd.ini`:
AISuperWeaponDelay= ; integer, game frames
```

### AI Superweapon targeting customizations

- There are several customizations available for AI superweapon targeting, currently restricted to controlling Ares' `SW.AITargeting` modes.
- For `SW.AITargeting=PsychicDominator`:
- If `SW.AITargeting.PsyDom.SkipChecks` is set to true targets with `ImmuneToPsionics=yes` will be considered and `SW.AITargeting.PsyDom.AllowAir` and `SW.AITargeting.PsyDom.AllowInvulnerable` will be available to customize whether or not targets in air or those affected by Iron Curtain/Force Shield, respectively, will be considered.
- `SW.AIRequiresTarget` and `SW.AIRequiresHouse` will be considered if explicitly set, with latter overriding previously hardcoded check to only allow enemy house targets to be considered.
- `SW.AITargeting.PsyDom.AllowTypes` and `SW.AITargeting.PsyDom.DisallowTypes` can be used to white/blacklist specific TechnoTypes.

In `rulesmd.ini`:
```ini
[SOMESW] ; SuperWeaponType
SW.AITargeting.PsyDom.SkipChecks=false ; boolean
SW.AITargeting.PsyDom.AllowAir=false ; boolean
SW.AITargeting.PsyDom.AllowInvulnerable=false ; boolean
SW.AITargeting.PsyDom.AllowTypes= ; List of TechnoTypes
SW.AITargeting.PsyDom.DisallowTypes= ; List of TechnoTypes
```

### Convert TechnoType

- Warheads can now change TechnoTypes of affected units to other Types in the same category (infantry to infantry, vehicles to vehicles, aircraft to aircraft).
Expand Down
2 changes: 2 additions & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,8 @@ Fixes / interactions with other extensions:
- Fixed a bug introduced by Ares where building types that have `UndeploysInto` cannot display `AltCameo` or `AltCameoPCX` even when you infiltrate enemy buildings with `Factory=UnitType` (by NetsuNegi)
- Fixed the issue that technos cannot spawn survivors due to non-probabilistic reasons when the tech type was destroyed (by NetsuNegi)
- Fixed the bug that vehicle survivor can spawn on wrong position when transport has been destroyed (by NetsuNegi)
- `SW.AITargeting=PsychicDominator` superweapons now ignore `Insignificant=true` as well as owned by `MultiplayPassive=true` house targets (by Starkku)
- [Additional customizations for certain AI superweapon targeting modes](New-or-Enhanced-Logics.md#ai-superweapon-targeting-customizations) (by Starkku)
```

### 0.4.0.1
Expand Down
15 changes: 15 additions & 0 deletions src/Ext/SWType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "NewSWType/NewSWType.h"

SWTypeExt::ExtContainer SWTypeExt::ExtMap;
SuperClass* SWTypeExt::CurrentAIEvaluatedSW = nullptr;

void SWTypeExt::ExtData::Initialize()
{
Expand All @@ -24,6 +25,11 @@ void SWTypeExt::ExtData::Serialize(T& Stm)
.Process(this->EVA_Impatient)
.Process(this->EVA_InsufficientFunds)
.Process(this->EVA_SelectTarget)
.Process(this->SW_AITargeting_PsyDom_SkipChecks)
.Process(this->SW_AITargeting_PsyDom_AllowAir)
.Process(this->SW_AITargeting_PsyDom_AllowInvulnerable)
.Process(this->SW_AITargeting_PsyDom_AllowTypes)
.Process(this->SW_AITargeting_PsyDom_DisallowTypes)
.Process(this->SW_UseAITargeting)
.Process(this->SW_AutoFire)
.Process(this->SW_ManualFire)
Expand All @@ -43,6 +49,8 @@ void SWTypeExt::ExtData::Serialize(T& Stm)
.Process(this->SW_PostDependent)
.Process(this->SW_MaxCount)
.Process(this->SW_Shots)
.Process(this->SW_AIRequiresTarget)
.Process(this->SW_AIRequiresHouse)
.Process(this->Message_CannotFire)
.Process(this->Message_InsufficientFunds)
.Process(this->Message_ColorScheme)
Expand Down Expand Up @@ -109,6 +117,11 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->EVA_Impatient.Read(exINI, pSection, "EVA.Impatient");
this->EVA_InsufficientFunds.Read(exINI, pSection, "EVA.InsufficientFunds");
this->EVA_SelectTarget.Read(exINI, pSection, "EVA.SelectTarget");
this->SW_AITargeting_PsyDom_SkipChecks.Read(exINI, pSection, "SW.AITargeting.PsyDom.SkipChecks");
this->SW_AITargeting_PsyDom_AllowAir.Read(exINI, pSection, "SW.AITargeting.PsyDom.AllowAir");
this->SW_AITargeting_PsyDom_AllowInvulnerable.Read(exINI, pSection, "SW.AITargeting.PsyDom.AllowInvulnerable");
this->SW_AITargeting_PsyDom_AllowTypes.Read(exINI, pSection, "SW.AITargeting.PsyDom.AllowTypes");
this->SW_AITargeting_PsyDom_DisallowTypes.Read(exINI, pSection, "SW.AITargeting.PsyDom.DisallowTypes");
this->SW_UseAITargeting.Read(exINI, pSection, "SW.UseAITargeting");
this->SW_AutoFire.Read(exINI, pSection, "SW.AutoFire");
this->SW_ManualFire.Read(exINI, pSection, "SW.ManualFire");
Expand All @@ -128,6 +141,8 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->SW_PostDependent.Read(exINI, pSection, "SW.PostDependent");
this->SW_MaxCount.Read(exINI, pSection, "SW.MaxCount");
this->SW_Shots.Read(exINI, pSection, "SW.Shots");
this->SW_AIRequiresTarget.Read(exINI, pSection, "SW.AIRequiresTarget");
this->SW_AIRequiresHouse.Read(exINI, pSection, "SW.AIRequiresHouse");

this->Message_CannotFire.Read(exINI, pSection, "Message.CannotFire");
this->Message_InsufficientFunds.Read(exINI, pSection, "Message.InsufficientFunds");
Expand Down
17 changes: 17 additions & 0 deletions src/Ext/SWType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class SWTypeExt
ValueableIdx<VoxClass> EVA_InsufficientFunds;
ValueableIdx<VoxClass> EVA_SelectTarget;
Valueable<bool> SW_UseAITargeting;
Valueable<bool> SW_AITargeting_PsyDom_SkipChecks;
Valueable<bool> SW_AITargeting_PsyDom_AllowAir;
Valueable<bool> SW_AITargeting_PsyDom_AllowInvulnerable;
ValueableVector<TechnoTypeClass*> SW_AITargeting_PsyDom_AllowTypes;
ValueableVector<TechnoTypeClass*> SW_AITargeting_PsyDom_DisallowTypes;
Valueable<bool> SW_AutoFire;
Valueable<bool> SW_ManualFire;
Valueable<bool> SW_ShowCameo;
Expand All @@ -35,6 +40,8 @@ class SWTypeExt
Valueable<double> SW_RangeMinimum;
Valueable<double> SW_RangeMaximum;
Valueable<int> SW_Shots;
Nullable<AffectedTarget> SW_AIRequiresTarget;
Nullable<AffectedHouse> SW_AIRequiresHouse;

DWORD SW_RequiredHouses;
DWORD SW_ForbiddenHouses;
Expand Down Expand Up @@ -112,6 +119,11 @@ class SWTypeExt
, EVA_Impatient { -1 }
, EVA_InsufficientFunds { -1 }
, EVA_SelectTarget { -1 }
, SW_AITargeting_PsyDom_SkipChecks { false }
, SW_AITargeting_PsyDom_AllowAir { false }
, SW_AITargeting_PsyDom_AllowInvulnerable { false }
, SW_AITargeting_PsyDom_AllowTypes {}
, SW_AITargeting_PsyDom_DisallowTypes {}
, SW_UseAITargeting { false }
, SW_AutoFire { false }
, SW_ManualFire { true }
Expand All @@ -131,6 +143,8 @@ class SWTypeExt
, SW_PostDependent {}
, SW_MaxCount { -1 }
, SW_Shots { -1 }
, SW_AIRequiresTarget {}
, SW_AIRequiresHouse {}
, Message_CannotFire {}
, Message_InsufficientFunds {}
, Message_ColorScheme { -1 }
Expand Down Expand Up @@ -231,12 +245,15 @@ class SWTypeExt
~ExtContainer();
};

static SuperClass* CurrentAIEvaluatedSW;

static void FireSuperWeaponExt(SuperClass* pSW, const CellStruct& cell);

static ExtContainer ExtMap;
static bool LoadGlobals(PhobosStreamReader& Stm);
static bool SaveGlobals(PhobosStreamWriter& Stm);

static bool Activate(SuperClass* pSuper, CellStruct cell, bool isPlayer);
static bool EligibleTargetForPsyDomSW(TechnoClass* pTechno);

};
18 changes: 18 additions & 0 deletions src/Ext/SWType/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,21 @@ DEFINE_HOOK(0x6AC67A, SidebarClass_6AC5F0_TabIndex, 0x5)

DEFINE_JUMP(LJMP, 0x6A8D07, 0x6A8D17) // Skip tabIndex check
#pragma endregion

// Only used if Ares is not available.
DEFINE_HOOK(0x509952, HouseClass_SuperWeapon_AI_Set, 0x6)
{
GET(SuperClass*, pSuper, EAX);

SWTypeExt::CurrentAIEvaluatedSW = pSuper;

return 0;
}

// Only used if Ares is not available.
DEFINE_HOOK(0x509AD2, HouseClass_SuperWeapon_AI_Unset, 0x6)
{
SWTypeExt::CurrentAIEvaluatedSW = nullptr;

return 0;
}
52 changes: 52 additions & 0 deletions src/Ext/SWType/SWHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,55 @@ void SWTypeExt::ExtData::PrintMessage(const CSFText& message, HouseClass* pFirer
// print the message
MessageListClass::Instance.PrintMessage(message, RulesClass::Instance->MessageDelay, color);
}

// Replaces Ares Psychic Dominator SW target eligibility checks if Ares is present.
bool SWTypeExt::EligibleTargetForPsyDomSW(TechnoClass* pTechno)
{
auto const pTechnoType = pTechno->GetTechnoType();

// Always ignore Insignificant or civilian-house owned targets - change from Ares and vanilla behaviour.
if (pTechnoType->Insignificant || pTechno->Owner->Type->MultiplayPassive)
return false;

if (auto const pSuper = SWTypeExt::CurrentAIEvaluatedSW)
{
auto const pTypeExt = SWTypeExt::ExtMap.Find(pSuper->Type);
auto const pOwner = pSuper->Owner;
auto const pTargetHouse = pTechno->Owner;

// If SW.AIRequiresHouse is explicitly set use that instead of restricting to enemies only.
if (pTypeExt->SW_AIRequiresHouse.isset())
{
if (!EnumFunctions::CanTargetHouse(pTypeExt->SW_AIRequiresHouse, pOwner, pTargetHouse))
return false;
}
else if (pOwner->IsAlliedWith(pTargetHouse))
{
return false;
}

// If SW.AIRequiresTarget is explicitly set check that here as well.
if (pTypeExt->SW_AIRequiresTarget.isset() && !EnumFunctions::IsTechnoEligible(pTechno, pTypeExt->SW_AIRequiresTarget))
return false;

if (pTypeExt->SW_AITargeting_PsyDom_AllowTypes.size() > 0 && !pTypeExt->SW_AITargeting_PsyDom_AllowTypes.Contains(pTechnoType))
return false;

if (pTypeExt->SW_AITargeting_PsyDom_DisallowTypes.size() > 0 && pTypeExt->SW_AITargeting_PsyDom_DisallowTypes.Contains(pTechnoType))
return false;

// Skip normal MC immunity etc. checks and only check air & invulnerability separately with toggles to turn them off.
if (pTypeExt->SW_AITargeting_PsyDom_SkipChecks)
{
if (pTechno->IsInAir() && !pTypeExt->SW_AITargeting_PsyDom_AllowAir)
return false;

if (pTechno->IsIronCurtained() && !pTypeExt->SW_AITargeting_PsyDom_AllowInvulnerable)
return false;

return true;
}
}

return pTechno->CanBePermaMindControlled();
}
33 changes: 33 additions & 0 deletions src/Misc/Hooks.Ares.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
#include <Ext/Building/Body.h>
#include <Ext/Sidebar/Body.h>
#include <Ext/EBolt/Body.h>
#include <Ext/SWType/Body.h>

#include <New/Entity/Ares/RadarJammerClass.h>
#include <Utilities/AresFunctions.h>

// Remember that we still don't fix Ares "issues" a priori. Extensions as well.
// Patches presented here are exceptions rather that the rule. They must be short, concise and correct.
Expand Down Expand Up @@ -61,6 +63,19 @@ static bool __fastcall CameoIsVeteran(TechnoTypeClass** pTypeExt_Ares, void*, Ho
return TechnoTypeExt::ExtMap.Find(*pTypeExt_Ares)->CameoIsVeteran(pHouse);
}

static bool __stdcall PickSuperWeaponTarget(SuperClass* pSuper, bool isFromEvent)
{
SWTypeExt::CurrentAIEvaluatedSW = pSuper;
bool retVal = AresFunctions::PickSuperWeaponTarget(pSuper, isFromEvent);
SWTypeExt::CurrentAIEvaluatedSW = nullptr;
return retVal;
}

static bool __fastcall CanBePermaMindControlled(TechnoClass* pTechno)
{
return SWTypeExt::EligibleTargetForPsyDomSW(pTechno);
}

_GET_FUNCTION_ADDRESS(RadarJammerClass::Update, AresRadarJammerClass_Update_GetAddr)

void Apply_Ares3_0_Patches()
Expand Down Expand Up @@ -124,6 +139,15 @@ void Apply_Ares3_0_Patches()

// Redirect Ares's TechnoTypeExt::ExtData::CameoIsElite() to our implementation:
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x3D800, &CameoIsVeteran);

// Redirect Ares' top-level SW target picker to our wrapper.
Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x38E6E, &PickSuperWeaponTarget);

// Redirect Ares' Psychic Dominator target evaluation checks to our wrapper.
Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x38177, &CanBePermaMindControlled);

// Skip checking house alliances in Ares' Psychic Dominator target evaluation checks, check them elsewhere later.
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x38137, AresHelper::AresBaseAddress + 0x38175);
}

void Apply_Ares3_0p1_Patches()
Expand Down Expand Up @@ -189,4 +213,13 @@ void Apply_Ares3_0p1_Patches()

// Redirect Ares's TechnoTypeExt::ExtData::CameoIsElite() to our implementation:
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x3E210, &CameoIsVeteran);

// Redirect Ares' top-level SW target picker to our wrapper.
Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x3990E, &PickSuperWeaponTarget);

// Redirect Ares' Psychic Dominator target evaluation checks to our wrapper.
Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x38C17, &CanBePermaMindControlled);

// Skip checks in Ares' Psychic Dominator target evaluation checks, check them elsewhere later.
Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x38BD7, AresHelper::AresBaseAddress + 0x38C15);
}
5 changes: 5 additions & 0 deletions src/Utilities/AresAddressInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ decltype(AresFunctions::CreateAresEBolt) AresFunctions::CreateAresEBolt = nullpt
decltype(AresFunctions::SpawnSurvivors) AresFunctions::SpawnSurvivors = nullptr;
decltype(AresFunctions::ReverseEngineer) AresFunctions::ReverseEngineer = nullptr;
decltype(AresFunctions::IsTargetConstraintsEligible) AresFunctions::IsTargetConstraintsEligible = nullptr;
decltype(AresFunctions::PickSuperWeaponTarget) AresFunctions::PickSuperWeaponTarget = nullptr;
std::function<AresSWTypeExtData* (SuperWeaponTypeClass*)> AresFunctions::SWTypeExtMap_Find;
PhobosMap<ObjectClass*, AlphaShapeClass*>* AresFunctions::AlphaExtMap = nullptr;

Expand Down Expand Up @@ -36,6 +37,8 @@ void AresFunctions::InitAres3_0()

NOTE_ARES_FUN(IsTargetConstraintsEligible, 0x032110);

NOTE_ARES_FUN(PickSuperWeaponTarget, 0x0382D0);

NOTE_ARES_FUN(_SWTypeExtMapFind, 0x57C70);
NOTE_ARES_FUN(_SWTypeExtMap, 0xC1C54);
SWTypeExtMap_Find = [](SuperWeaponTypeClass* swt) { return _SWTypeExtMapFind(_SWTypeExtMap, swt); };
Expand Down Expand Up @@ -66,6 +69,8 @@ void AresFunctions::InitAres3_0p1()

NOTE_ARES_FUN(IsTargetConstraintsEligible, 0x032AF0);

NOTE_ARES_FUN(PickSuperWeaponTarget, 0x038D70);

NOTE_ARES_FUN(_SWTypeExtMapFind, 0x58900);
NOTE_ARES_FUN(_SWTypeExtMap, 0xC2C50);
SWTypeExtMap_Find = [](SuperWeaponTypeClass* swt) { return _SWTypeExtMapFind(_SWTypeExtMap, swt); };
Expand Down
2 changes: 2 additions & 0 deletions src/Utilities/AresFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class AresFunctions

static bool(__thiscall* IsTargetConstraintsEligible)(void*, HouseClass*, bool);

static bool(__stdcall* PickSuperWeaponTarget)(SuperClass*, bool);

static std::function<AresSWTypeExtData* (SuperWeaponTypeClass*)> SWTypeExtMap_Find;

static PhobosMap<ObjectClass*, AlphaShapeClass*>* AlphaExtMap;
Expand Down