Skip to content

Commit 73a2ac7

Browse files
authored
[Vanilla Enhancement] Customize size for mind controlled unit (#2018)
- Mind controlled targets can have size of control, like passengers in transport. - 被心灵控制的单位现在可以设置大小,就像乘客在载具中那样. In `rulesmd.ini`: ```ini [SOMETECHNO] ; TechnoType MindControl.IgnoreSize=true ; boolean MindControlSize=1 ; integer ```
1 parent 3617dfc commit 73a2ac7

File tree

11 files changed

+174
-24
lines changed

11 files changed

+174
-24
lines changed

CREDITS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ This page lists all the individual contributions to the project by their author.
477477
- Fix the bug that vehicle survivor can spawn on wrong position when transport has been destroyed
478478
- Fix the bug that if object has been removed from LogicClass in Update(), next object will be skip
479479
- Fix the bug that weapon cannot used to intercept on gound bullet if it's projectile has `AG=no`
480+
- Customize size for mind controlled unit
480481
- **Apollo** - Translucent SHP drawing patches
481482
- **ststl**:
482483
- Customizable `ShowTimer` priority of superweapons

YRpp

docs/New-or-Enhanced-Logics.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,13 +1819,16 @@ Please notice that if the object is a unit which carries passengers, they will n
18191819
*Multiple Mind Control unit auto-releases the first victim in [Fantasy ADVENTURE](https://www.moddb.com/mods/fantasy-adventure)*
18201820

18211821
- Mind controllers now can have the upper limit of the control distance. Tag values greater than 0 will activate this feature.
1822+
- Mind controlled targets can have size of control, like passengers in transport.
18221823
- Mind controllers now can decide which house can see the link drawn between itself and the controlled units.
18231824
- 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.
18241825

18251826
In `rulesmd.ini`:
18261827
```ini
18271828
[SOMETECHNO] ; TechnoType
18281829
MindControlRangeLimit=-1.0 ; floating point value
1830+
MindControl.IgnoreSize=true ; boolean
1831+
MindControlSize=1 ; integer
18291832
MindControlLink.VisibleToHouse=all ; Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
18301833
MultiMindControl.ReleaseVictim=false ; boolean
18311834
```

docs/Whats-New.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ New:
482482
- [Toggle to exclude technos from base center calculations](New-or-Enhanced-Logics.md#exclusion-from-base-center-calculations) (by Starkku)
483483
- Weapons now support `AttackFriendlies` and `AttackCursorOnFriendlies` (by FlyStar)
484484
- Attack non-threatening structures extensions (by FlyStar)
485+
- Customize size for mind controlled unit (by NetsuNegi)
485486
486487
Vanilla fixes:
487488
- Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya)

src/Ext/CaptureManager/Body.cpp

Lines changed: 119 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,112 @@
11
#include "Body.h"
22

33
#include <Ext/Techno/Body.h>
4+
#include <Utilities/AresHelper.h>
5+
6+
int CaptureManagerExt::GetControlledTotalSize(CaptureManagerClass* pManager)
7+
{
8+
int totalSize = 0;
9+
10+
for (const auto pNode : pManager->ControlNodes)
11+
{
12+
if (const auto pTechno = pNode->Unit)
13+
totalSize += TechnoExt::ExtMap.Find(pTechno)->TypeExtData->MindControlSize;
14+
}
15+
16+
return totalSize;
17+
}
18+
19+
struct DummyExtHere
20+
{
21+
char _[0x9C];
22+
bool DriverKilled;
23+
};
24+
25+
struct DummyTypeExtHere
26+
{
27+
char _[0x131];
28+
bool Vet_PsionicsImmune;
29+
bool __[0x6];
30+
bool Elite_PsionicsImmune;
31+
};
432

533
bool CaptureManagerExt::CanCapture(CaptureManagerClass* pManager, TechnoClass* pTarget)
634
{
7-
if (pManager->MaxControlNodes == 1)
8-
return pManager->CanCapture(pTarget);
35+
// target exists and doesn't belong to capturing player
36+
if (!pTarget)
37+
return false;
38+
39+
if (pManager->MaxControlNodes <= 0)
40+
return false;
941

10-
const auto pTechnoTypeExt = TechnoExt::ExtMap.Find(pManager->Owner)->TypeExtData;
11-
if (pTechnoTypeExt->MultiMindControl_ReleaseVictim)
42+
const auto pOwner = pManager->Owner;
43+
44+
if (pTarget->Owner == pOwner->Owner)
45+
return false;
46+
47+
const auto pTargetType = pTarget->GetTechnoType();
48+
49+
// generally not capturable
50+
if (pTargetType->ImmuneToPsionics)
51+
return false;
52+
53+
if (AresHelper::CanUseAres)
1254
{
13-
// I hate Ares' completely rewritten things - secsome
14-
pManager->MaxControlNodes += 1;
15-
const bool result = pManager->CanCapture(pTarget);
16-
pManager->MaxControlNodes -= 1;
17-
return result;
55+
const auto pTargetTypeExt_Ares = reinterpret_cast<DummyTypeExtHere*>(pTargetType->align_2FC);
56+
57+
switch (pTarget->Veterancy.GetRemainingLevel())
58+
{
59+
case Rank::Elite:
60+
if (pTargetTypeExt_Ares->Elite_PsionicsImmune)
61+
return false;
62+
63+
case Rank::Veteran:
64+
if (pTargetTypeExt_Ares->Vet_PsionicsImmune)
65+
return false;
66+
67+
default:
68+
break;
69+
}
1870
}
1971

20-
return pManager->CanCapture(pTarget);
72+
// disallow capturing bunkered units
73+
if (pTarget->BunkerLinkedItem && pTarget->WhatAmI() == AbstractType::Unit)
74+
return false;
75+
76+
if (pTarget->IsMindControlled() || pTarget->MindControlledByHouse)
77+
return false;
78+
79+
// free slot? (move on if infinite or single slot which will be freed if used)
80+
if (!pManager->InfiniteMindControl && pManager->MaxControlNodes != 1)
81+
{
82+
const auto pOwnerTypeExt = TechnoExt::ExtMap.Find(pOwner)->TypeExtData;
83+
84+
if (!pOwnerTypeExt->MindControl_IgnoreSize)
85+
{
86+
const int totalSize = CaptureManagerExt::GetControlledTotalSize(pManager);
87+
const int available = pOwnerTypeExt->MultiMindControl_ReleaseVictim ? pManager->MaxControlNodes : pManager->MaxControlNodes - totalSize;
88+
89+
if (TechnoTypeExt::ExtMap.Find(pTargetType)->MindControlSize > available)
90+
return false;
91+
}
92+
else
93+
{
94+
if (pManager->ControlNodes.Count >= pManager->MaxControlNodes && !pOwnerTypeExt->MultiMindControl_ReleaseVictim)
95+
return false;
96+
}
97+
}
98+
99+
// currently disallowed
100+
const auto mission = pTarget->CurrentMission;
101+
102+
if (pTarget->IsIronCurtained() || mission == Mission::Selling || mission == Mission::Construction)
103+
return false;
104+
105+
// driver killed. has no mind.
106+
if (AresHelper::CanUseAres && reinterpret_cast<DummyExtHere*>(*(uintptr_t*)((char*)pTarget + 0x154))->DriverKilled)
107+
return false;
108+
109+
return true;
21110
}
22111

23112
bool CaptureManagerExt::FreeUnit(CaptureManagerClass* pManager, TechnoClass* pTarget, bool silent)
@@ -68,7 +157,7 @@ bool CaptureManagerExt::FreeUnit(CaptureManagerClass* pManager, TechnoClass* pTa
68157
}
69158

70159
bool CaptureManagerExt::CaptureUnit(CaptureManagerClass* pManager, TechnoClass* pTarget,
71-
bool bRemoveFirst, AnimTypeClass* pControlledAnimType, bool silent, int threatDelay)
160+
bool removeFirst, AnimTypeClass* pControlledAnimType, bool silent, int threatDelay)
72161
{
73162
if (CaptureManagerExt::CanCapture(pManager, pTarget))
74163
{
@@ -78,10 +167,26 @@ bool CaptureManagerExt::CaptureUnit(CaptureManagerClass* pManager, TechnoClass*
78167
if (!pManager->InfiniteMindControl)
79168
{
80169
if (pManager->MaxControlNodes == 1 && pManager->ControlNodes.Count == 1)
170+
{
81171
CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit);
82-
else if (pManager->ControlNodes.Count == pManager->MaxControlNodes)
83-
if (bRemoveFirst)
84-
CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit);
172+
}
173+
else if (pManager->ControlNodes.Count > 0 && removeFirst)
174+
{
175+
const auto pOwnerTypeExt = TechnoTypeExt::ExtMap.Find(pManager->Owner->GetTechnoType());
176+
177+
if (pOwnerTypeExt->MindControl_IgnoreSize)
178+
{
179+
if (pManager->ControlNodes.Count == pManager->MaxControlNodes)
180+
CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit);
181+
}
182+
else
183+
{
184+
const auto pTargetTypeExt = TechnoTypeExt::ExtMap.Find(pTarget->GetTechnoType());
185+
186+
while (pManager->ControlNodes.Count && pTargetTypeExt->MindControlSize > pManager->MaxControlNodes - CaptureManagerExt::GetControlledTotalSize(pManager))
187+
CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit);
188+
}
189+
}
85190
}
86191

87192
auto const pControlNode = GameCreate<ControlNode>();

src/Ext/CaptureManager/Body.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
class CaptureManagerExt
88
{
99
public:
10+
static int GetControlledTotalSize(CaptureManagerClass* pManager);
11+
1012
static bool CanCapture(CaptureManagerClass* pManager, TechnoClass* pTarget);
1113
static bool FreeUnit(CaptureManagerClass* pManager, TechnoClass* pTarget, bool silent = false);
12-
static bool CaptureUnit(CaptureManagerClass* pManager, TechnoClass* pTarget, bool bRemoveFirst,
14+
static bool CaptureUnit(CaptureManagerClass* pManager, TechnoClass* pTarget, bool removeFirst,
1315
AnimTypeClass* pControlledAnimType = RulesClass::Instance->ControlledAnimationType, bool silent = false, int threatDelay = 0);
1416
static bool CaptureUnit(CaptureManagerClass* pManager, AbstractClass* pTechno,
1517
AnimTypeClass* pControlledAnimType = RulesClass::Instance->ControlledAnimationType, int threatDelay = 0);

src/Ext/CaptureManager/Hooks.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,44 @@ DEFINE_HOOK(0x471FF0, CaptureManagerClass_FreeUnit, 0x8)
2222
return 0x472006;
2323
}
2424

25-
DEFINE_HOOK(0x6FCB34, TechnoClass_CanFire_CanCapture, 0x6)
25+
DEFINE_HOOK(0x471C90, CaptureManagerClass_CanCapture, 0x6)
2626
{
27-
GET(TechnoClass*, pThis, ESI);
28-
GET(TechnoClass*, pTarget, EBP);
27+
GET(CaptureManagerClass*, pThis, ECX);
28+
GET_STACK(TechnoClass*, pTarget, 0x4);
29+
30+
R->AL(CaptureManagerExt::CanCapture(pThis, pTarget));
31+
32+
return 0x471D39;
33+
}
34+
35+
static int __fastcall _GetControlledCount(CaptureManagerClass* pThis)
36+
{
37+
const auto pOwnerTypeExt = TechnoExt::ExtMap.Find(pThis->Owner)->TypeExtData;
38+
39+
if (!pOwnerTypeExt->MindControl_IgnoreSize)
40+
return CaptureManagerExt::GetControlledTotalSize(pThis);
41+
42+
return pThis->ControlNodes.Count;
43+
}
44+
DEFINE_FUNCTION_JUMP(LJMP, 0x4722D0, _GetControlledCount)
2945

30-
R->AL(CaptureManagerExt::CanCapture(pThis->CaptureManager, pTarget));
46+
DEFINE_HOOK(0x4726C7, CaptureManagerClass_IsOverloading_ControlledCount, 0x6)
47+
{
48+
enum { ContinueCheck = 0x4726D1, ReturnFalse = 0x4726E4 };
49+
50+
GET(CaptureManagerClass*, pThis, ECX);
51+
52+
return pThis->GetControlledCount() > pThis->MaxControlNodes ? ContinueCheck : ReturnFalse;
53+
}
54+
55+
DEFINE_HOOK(0x4722AA, CaptureManagerClass_CannotControlAnyMore_ControlledCount, 0x6)
56+
{
57+
enum { SkipGameCode = 0x4722B5 };
58+
59+
GET(CaptureManagerClass*, pThis, ECX);
3160

32-
return 0x6FCB40;
61+
R->AL(pThis->GetControlledCount() >= pThis->MaxControlNodes);
62+
return SkipGameCode;
3363
}
3464

3565
DEFINE_HOOK(0x519F71, InfantryClass_UpdatePosition_BeforeBuildingChangeHouse, 0x6)
@@ -104,7 +134,7 @@ static void __fastcall CaptureManagerClass_Overload_AI(CaptureManagerClass* pThi
104134
return;
105135

106136
int nCurIdx = 0;
107-
int const nNodeCount = pThis->ControlNodes.Count;
137+
int const nNodeCount = pThis->GetControlledCount();
108138

109139
for (int i = 0; i < (int)(OverloadCount.size()); ++i)
110140
{

src/Ext/Techno/Body.Update.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,7 @@ void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType)
985985
}
986986
else if (pOldTypeExt->Convert_ResetMindControl)
987987
{
988-
if (!infiniteCapture && pCaptureManager->ControlNodes.Count > maxCapture)
988+
if (!infiniteCapture && pCaptureManager->GetControlledCount() > maxCapture)
989989
{
990990
// Remove excess nodes.
991991
for (int i = pCaptureManager->ControlNodes.Count - 1; i >= maxCapture; --i)

src/Ext/Techno/Body.Visuals.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType,
534534
if (!pCaptureManager)
535535
return;
536536

537-
value = pCaptureManager->ControlNodes.Count;
537+
value = pCaptureManager->GetControlledCount();
538538
maxValue = pCaptureManager->MaxControlNodes;
539539
break;
540540
}

src/Ext/TechnoType/Body.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,8 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
745745
this->RadarJamAffect.Read(exINI, pSection, "RadarJamAffect");
746746
this->RadarJamIgnore.Read(exINI, pSection, "RadarJamIgnore");
747747
this->MindControlRangeLimit.Read(exINI, pSection, "MindControlRangeLimit");
748+
this->MindControl_IgnoreSize.Read(exINI, pSection, "MindControl.IgnoreSize");
749+
this->MindControlSize.Read(exINI, pSection, "MindControlSize");
748750
this->MindControlLink_VisibleToHouse.Read(exINI, pSection, "MindControlLink.VisibleToHouse");
749751
this->FactoryPlant_Multiplier.Read(exINI, pSection, "FactoryPlant.Multiplier");
750752

@@ -1406,6 +1408,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
14061408
.Process(this->UIDescription)
14071409
.Process(this->LowSelectionPriority)
14081410
.Process(this->MindControlRangeLimit)
1411+
.Process(this->MindControl_IgnoreSize)
1412+
.Process(this->MindControlSize)
14091413
.Process(this->MindControlLink_VisibleToHouse)
14101414
.Process(this->FactoryPlant_Multiplier)
14111415

0 commit comments

Comments
 (0)