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 @@ -469,6 +469,7 @@ This page lists all the individual contributions to the project by their author.
- Fix 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`
- Fix a bug where units can be promoted when created via trigger actions even if they have `Trainable=false`
- Fix the bug that ai will try to product aircraft even the airport has no free dock for it
- Customize size for mind controlled unit
- **Apollo** - Translucent SHP drawing patches
- **ststl**:
- Customizable `ShowTimer` priority of superweapons
Expand Down
2 changes: 1 addition & 1 deletion YRpp
Submodule YRpp updated 1 files
+2 −0 CaptureManagerClass.h
3 changes: 3 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1797,13 +1797,16 @@ Please notice that if the object is a unit which carries passengers, they will n
*Multiple Mind Control unit auto-releases the first victim in [Fantasy ADVENTURE](https://www.moddb.com/mods/fantasy-adventure)*

- Mind controllers now can have the upper limit of the control distance. Tag values greater than 0 will activate this feature.
- Mind controlled targets can have size of control, like passengers in transport.
- Mind controllers now can decide which house can see the link drawn between itself and the controlled units.
- Mind controllers with multiple controlling slots can now release the first controlled unit when they have reached the control limit and are ordered to control a new target.

In `rulesmd.ini`:
```ini
[SOMETECHNO] ; TechnoType
MindControlRangeLimit=-1.0 ; floating point value
MindControl.IgnoreSize=true ; boolean
MindControlSize=1 ; integer
MindControlLink.VisibleToHouse=all ; Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
MultiMindControl.ReleaseVictim=false ; boolean
```
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ New:
- AutoDeath upon ownership change (by Ollerus)
- [Script Action 14004 for forcing all new actions to target only the main owner's enemy](AI-Scripting-and-Mapping.md#force-global-onlytargethouseenemy-value-in-teams-for-new-attack-move-actions-introduced-by-phobos) (by FS-21)
- [Allow merging AOE damage to buildings into one](New-or-Enhanced-Logics.md#allow-merging-aoe-damage-to-buildings-into-one) (by CrimRecya)
- Customize size for mind controlled unit (by NetsuNegi)

Vanilla fixes:
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)
Expand Down
133 changes: 119 additions & 14 deletions src/Ext/CaptureManager/Body.cpp
Original file line number Diff line number Diff line change
@@ -1,23 +1,112 @@
#include "Body.h"

#include <Ext/Techno/Body.h>
#include <Utilities/AresHelper.h>

int CaptureManagerExt::GetControlledTotalSize(CaptureManagerClass* pManager)
{
int totalSize = 0;

for (const auto pNode : pManager->ControlNodes)
{
if (const auto pTechno = pNode->Unit)
totalSize += TechnoTypeExt::ExtMap.Find(pTechno->GetTechnoType())->MindControlSize;
}

return totalSize;
}

struct DummyExtHere
{
char _[0x9C];
bool DriverKilled;
};

struct DummyTypeExtHere
{
char _[0x131];
bool Vet_PsionicsImmune;
bool __[0x6];
bool Elite_PsionicsImmune;
};

bool CaptureManagerExt::CanCapture(CaptureManagerClass* pManager, TechnoClass* pTarget)
{
if (pManager->MaxControlNodes == 1)
return pManager->CanCapture(pTarget);
// target exists and doesn't belong to capturing player
if (!pTarget)
return false;

if (pManager->MaxControlNodes <= 0)
return false;

const auto pTechnoTypeExt = TechnoExt::ExtMap.Find(pManager->Owner)->TypeExtData;
if (pTechnoTypeExt->MultiMindControl_ReleaseVictim)
const auto pOwner = pManager->Owner;

if (pTarget->Owner == pOwner->Owner)
return false;

const auto pTargetType = pTarget->GetTechnoType();

// generally not capturable
if (pTargetType->ImmuneToPsionics)
return false;

if (AresHelper::CanUseAres)
{
// I hate Ares' completely rewritten things - secsome
pManager->MaxControlNodes += 1;
const bool result = pManager->CanCapture(pTarget);
pManager->MaxControlNodes -= 1;
return result;
const auto pTargetTypeExt_Ares = reinterpret_cast<DummyTypeExtHere*>(pTargetType->align_2FC);

switch (pTarget->Veterancy.GetRemainingLevel())
{
case Rank::Elite:
if (pTargetTypeExt_Ares->Elite_PsionicsImmune)
return false;

case Rank::Veteran:
if (pTargetTypeExt_Ares->Vet_PsionicsImmune)
return false;

default:
break;
}
}

return pManager->CanCapture(pTarget);
// disallow capturing bunkered units
if (pTarget->BunkerLinkedItem && pTarget->WhatAmI() == AbstractType::Unit)
return false;

if (pTarget->IsMindControlled() || pTarget->MindControlledByHouse)
return false;

// free slot? (move on if infinite or single slot which will be freed if used)
if (!pManager->InfiniteMindControl && pManager->MaxControlNodes != 1)
{
const auto pOwnerTypeExt = TechnoTypeExt::ExtMap.Find(pOwner->GetTechnoType());

if (!pOwnerTypeExt->MindControl_IgnoreSize)
{
const int totalSize = CaptureManagerExt::GetControlledTotalSize(pManager);
const int available = pOwnerTypeExt->MultiMindControl_ReleaseVictim ? pManager->MaxControlNodes : pManager->MaxControlNodes - totalSize;

if (TechnoTypeExt::ExtMap.Find(pTargetType)->MindControlSize > available)
return false;
}
else
{
if (pManager->ControlNodes.Count >= pManager->MaxControlNodes && !pOwnerTypeExt->MultiMindControl_ReleaseVictim)
return false;
}
}

// currently disallowed
const auto mission = pTarget->CurrentMission;

if (pTarget->IsIronCurtained() || mission == Mission::Selling || mission == Mission::Construction)
return false;

// driver killed. has no mind.
if (AresHelper::CanUseAres && reinterpret_cast<DummyExtHere*>(*(uintptr_t*)((char*)pTarget + 0x154))->DriverKilled)
return false;

return true;
}

bool CaptureManagerExt::FreeUnit(CaptureManagerClass* pManager, TechnoClass* pTarget, bool silent)
Expand Down Expand Up @@ -68,7 +157,7 @@ bool CaptureManagerExt::FreeUnit(CaptureManagerClass* pManager, TechnoClass* pTa
}

bool CaptureManagerExt::CaptureUnit(CaptureManagerClass* pManager, TechnoClass* pTarget,
bool bRemoveFirst, AnimTypeClass* pControlledAnimType, bool silent, int threatDelay)
bool removeFirst, AnimTypeClass* pControlledAnimType, bool silent, int threatDelay)
{
if (CaptureManagerExt::CanCapture(pManager, pTarget))
{
Expand All @@ -78,10 +167,26 @@ bool CaptureManagerExt::CaptureUnit(CaptureManagerClass* pManager, TechnoClass*
if (!pManager->InfiniteMindControl)
{
if (pManager->MaxControlNodes == 1 && pManager->ControlNodes.Count == 1)
{
CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit);
else if (pManager->ControlNodes.Count == pManager->MaxControlNodes)
if (bRemoveFirst)
CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit);
}
else if (pManager->ControlNodes.Count > 0 && removeFirst)
{
const auto pOwnerTypeExt = TechnoTypeExt::ExtMap.Find(pManager->Owner->GetTechnoType());

if (pOwnerTypeExt->MindControl_IgnoreSize)
{
if (pManager->ControlNodes.Count == pManager->MaxControlNodes)
CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit);
}
else
{
const auto pTargetTypeExt = TechnoTypeExt::ExtMap.Find(pTarget->GetTechnoType());

while (pManager->ControlNodes.Count && pTargetTypeExt->MindControlSize > pManager->MaxControlNodes - CaptureManagerExt::GetControlledTotalSize(pManager))
CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit);
}
}
}

auto const pControlNode = GameCreate<ControlNode>();
Expand Down
4 changes: 3 additions & 1 deletion src/Ext/CaptureManager/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
class CaptureManagerExt
{
public:
static int GetControlledTotalSize(CaptureManagerClass* pManager);

static bool CanCapture(CaptureManagerClass* pManager, TechnoClass* pTarget);
static bool FreeUnit(CaptureManagerClass* pManager, TechnoClass* pTarget, bool silent = false);
static bool CaptureUnit(CaptureManagerClass* pManager, TechnoClass* pTarget, bool bRemoveFirst,
static bool CaptureUnit(CaptureManagerClass* pManager, TechnoClass* pTarget, bool removeFirst,
AnimTypeClass* pControlledAnimType = RulesClass::Instance->ControlledAnimationType, bool silent = false, int threatDelay = 0);
static bool CaptureUnit(CaptureManagerClass* pManager, AbstractClass* pTechno,
AnimTypeClass* pControlledAnimType = RulesClass::Instance->ControlledAnimationType, int threatDelay = 0);
Expand Down
42 changes: 36 additions & 6 deletions src/Ext/CaptureManager/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,44 @@ DEFINE_HOOK(0x471FF0, CaptureManagerClass_FreeUnit, 0x8)
return 0x472006;
}

DEFINE_HOOK(0x6FCB34, TechnoClass_CanFire_CanCapture, 0x6)
DEFINE_HOOK(0x471C90, CaptureManagerClass_CanCapture, 0x6)
{
GET(TechnoClass*, pThis, ESI);
GET(TechnoClass*, pTarget, EBP);
GET(CaptureManagerClass*, pThis, ECX);
GET_STACK(TechnoClass*, pTarget, 0x4);

R->AL(CaptureManagerExt::CanCapture(pThis, pTarget));

return 0x471D39;
}

static int __fastcall _GetControlledCount(CaptureManagerClass* pThis)
{
const auto pOwnerTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Owner->GetTechnoType());

if (!pOwnerTypeExt->MindControl_IgnoreSize)
return CaptureManagerExt::GetControlledTotalSize(pThis);

return pThis->ControlNodes.Count;
}
DEFINE_FUNCTION_JUMP(LJMP, 0x4722D0, _GetControlledCount)

R->AL(CaptureManagerExt::CanCapture(pThis->CaptureManager, pTarget));
DEFINE_HOOK(0x4726C7, CaptureManagerClass_IsOverloading_ControlledCount, 0x6)
{
enum { ContinueCheck = 0x4726D1, ReturnFalse = 0x4726E4 };

GET(CaptureManagerClass*, pThis, ECX);

return pThis->GetControlledCount() > pThis->MaxControlNodes ? ContinueCheck : ReturnFalse;
}

DEFINE_HOOK(0x4722AA, CaptureManagerClass_CannotControlAnyMore_ControlledCount, 0x6)
{
enum { SkipGameCode = 0x4722B5 };

GET(CaptureManagerClass*, pThis, ECX);

return 0x6FCB40;
R->AL(pThis->GetControlledCount() >= pThis->MaxControlNodes);
return SkipGameCode;
}

DEFINE_HOOK(0x519F71, InfantryClass_UpdatePosition_BeforeBuildingChangeHouse, 0x6)
Expand Down Expand Up @@ -107,7 +137,7 @@ static void __fastcall CaptureManagerClass_Overload_AI(CaptureManagerClass* pThi
return;

int nCurIdx = 0;
int const nNodeCount = pThis->ControlNodes.Count;
int const nNodeCount = pThis->GetControlledCount();

for (int i = 0; i < (int)(OverloadCount.size()); ++i)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType)
}
else if (pOldTypeExt->Convert_ResetMindControl)
{
if (!infiniteCapture && pCaptureManager->ControlNodes.Count > maxCapture)
if (!infiniteCapture && pCaptureManager->GetControlledCount() > maxCapture)
{
// Remove excess nodes.
for (int i = pCaptureManager->ControlNodes.Count - 1; i >= maxCapture; --i)
Expand Down
2 changes: 1 addition & 1 deletion src/Ext/Techno/Body.Visuals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType,
if (!pCaptureManager)
return;

value = pCaptureManager->ControlNodes.Count;
value = pCaptureManager->GetControlledCount();
maxValue = pCaptureManager->MaxControlNodes;
break;
}
Expand Down
4 changes: 4 additions & 0 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->RadarJamAffect.Read(exINI, pSection, "RadarJamAffect");
this->RadarJamIgnore.Read(exINI, pSection, "RadarJamIgnore");
this->MindControlRangeLimit.Read(exINI, pSection, "MindControlRangeLimit");
this->MindControl_IgnoreSize.Read(exINI, pSection, "MindControl.IgnoreSize");
this->MindControlSize.Read(exINI, pSection, "MindControlSize");
this->MindControlLink_VisibleToHouse.Read(exINI, pSection, "MindControlLink.VisibleToHouse");
this->FactoryPlant_Multiplier.Read(exINI, pSection, "FactoryPlant.Multiplier");

Expand Down Expand Up @@ -1385,6 +1387,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->UIDescription)
.Process(this->LowSelectionPriority)
.Process(this->MindControlRangeLimit)
.Process(this->MindControl_IgnoreSize)
.Process(this->MindControlSize)
.Process(this->MindControlLink_VisibleToHouse)
.Process(this->FactoryPlant_Multiplier)

Expand Down
4 changes: 4 additions & 0 deletions src/Ext/TechnoType/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class TechnoTypeExt
Nullable<int> DesignatorRange;
Valueable<float> FactoryPlant_Multiplier;
Valueable<Leptons> MindControlRangeLimit;
Valueable<bool> MindControl_IgnoreSize;
Valueable<int> MindControlSize;
Valueable<AffectedHouse> MindControlLink_VisibleToHouse;

std::unique_ptr<InterceptorTypeClass> InterceptorType;
Expand Down Expand Up @@ -471,6 +473,8 @@ class TechnoTypeExt
, DesignatorRange { }
, FactoryPlant_Multiplier { 1.0 }
, MindControlRangeLimit {}
, MindControl_IgnoreSize { true }
, MindControlSize { 1 }
, MindControlLink_VisibleToHouse{ AffectedHouse::All }

, InterceptorType { nullptr }
Expand Down