Large diffs are not rendered by default.

@@ -52,6 +52,11 @@ class TechnoExt

bool ShadowDrawnManually;

bool DriverKilled;

int HijackerHealth;
HouseClass* HijackerHouse;

// 305 Radar Jammers
JammerClass* RadarJam;

@@ -68,6 +73,9 @@ class TechnoExt
Survivors_Done (0),
Insignia_Image (NULL),
GarrisonedIn (NULL),
HijackerHealth (-1),
HijackerHouse (NULL),
DriverKilled (false),
EMPSparkleAnim (NULL),
EMPLastMission (mission_None),
ShadowDrawnManually (false),
@@ -97,6 +105,9 @@ class TechnoExt
bool IsOperated();
bool IsPowered();

AresAction::Value GetActionHijack(TechnoClass *pTarget);
bool PerformActionHijack(TechnoClass* pTarget);

unsigned int AlphaFrame(SHPStruct * Image);

bool DrawVisualFX();
@@ -121,7 +132,10 @@ class TechnoExt
static void SpawnSurvivors(FootClass *pThis, TechnoClass *pKiller, bool Select, bool IgnoreDefenses);
static bool EjectSurvivor(FootClass *Survivor, CoordStruct *loc, bool Select);
static void EjectPassengers(FootClass *, signed short);
static void GetPutLocation(CoordStruct const &, CoordStruct &);
static void GetPutLocation(CoordStruct const &, CoordStruct &, int);
static bool EjectRandomly(FootClass*, CoordStruct const &, int, bool);
// If available, removes the hijacker from its victim and creates an InfantryClass instance.
static InfantryClass* RecoverHijacker(FootClass *pThis);

static void StopDraining(TechnoClass *Drainer, TechnoClass *Drainee);

@@ -678,22 +678,239 @@ DEFINE_HOOK(70E2D2, TechnoClass_IronCurtain_Modify, 6) {
return 0;
}

// update the vehicle thief's destination. needed to follow a
// target without the requirement to also enable Thief=yes.
DEFINE_HOOK(5202F9, InfantryClass_UpdateVehicleThief_Check, 6)
{
GET(InfantryClass*, pThis, ESI);

// good old WW checks for Thief. idiots.
if(!pThis->Type->VehicleThief) {
// also allow for drivers, because vehicles may still drive around. usually they are not.
TechnoTypeExt::ExtData* pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type);
if(!pTypeExt->CanDrive) {
return 0x5206A1;
}
}

return 0x52030D;
}

DEFINE_HOOK(5198AD, InfantryClass_UpdatePosition_EnteredGrinder, 6)
// the hijacker is close to the target. capture.
DEFINE_HOOK(5203F7, InfantryClass_UpdateVehicleThief_Hijack, 5)
{
GET(InfantryClass *, Infantry, ESI);
GET(BuildingClass *, Grinder, EBX);
enum {GoOn = 0x5206A1, Stop = 0x520473};

BuildingExt::ExtData *pData = BuildingExt::ExtMap.Find(Grinder);
GET(InfantryClass*, pThis, ESI);
GET(FootClass*, pTarget, EDI);
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);

if(pData->ReverseEngineer(Infantry)) {
if(Infantry->Owner->ControlledByPlayer()) {
VoxClass::Play("EVA_ReverseEngineeredInfantry");
VoxClass::Play("EVA_NewTechnologyAcquired");
bool finalize = pExt->PerformActionHijack(pTarget);
if(finalize) {
// manually deinitialize this infantry
pThis->UnInit();
}
return finalize ? Stop : GoOn;
}

DEFINE_HOOK(51E7BF, InfantryClass_GetCursorOverObject_CanCapture, 6)
{
GET(InfantryClass *, pSelected, EDI);
GET(ObjectClass *, pTarget, ESI);

enum {
Capture = 0x51E84B, // the game will return an Enter cursor no questions asked
DontCapture = 0x51E85A, // the game will assume this is not a VehicleThief and will check for other cursors normally
Select = 0x51E7EF, // select target instead of ordering this
DontMindMe = 0, // the game will check if this is a VehicleThief
} DoWhat = DontMindMe;

if(TechnoClass* pTechno = generic_cast<TechnoClass*>(pTarget)) {
if(pTechno->GetTechnoType()->IsTrain) {
DoWhat = Select;
} else {
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pSelected);
TechnoTypeExt::ExtData* pTypeExt = TechnoTypeExt::ExtMap.Find(pSelected->Type);
if(pSelected->Type->VehicleThief || pTypeExt->CanDrive) {
DoWhat = (pExt->GetActionHijack(pTechno) ? Capture : DontCapture);
}
}
}

return 0;
return DoWhat;
}

// change all the special things infantry do, like vehicle thief, infiltration,
// bridge repair, enter transports or bio reactors, ...
DEFINE_HOOK(519675, InfantryClass_UpdatePosition_BeforeInfantrySpecific, A)
{
// called after FootClass:UpdatePosition has been called and before
// all specific infantry handling takes place.
enum {
Return = 0x51AA01, // skip the original logic
Destroy = 0x51A010, // uninits this infantry and returns
Handle = 0 // resume the original function
} DoWhat = Handle;

GET(InfantryClass*, pThis, ESI);

if(pThis) {
// steal vehicles / reclaim KillDriver'd units using CanDrive
if(pThis->CurrentMission == mission_Capture) {
if(TechnoClass* pDest = generic_cast<TechnoClass*>(pThis->Destination)) {
// this is the possible target we stand on
CellClass* pCell = pThis->GetCell();
TechnoClass* pTarget = pCell->GetUnit(pThis->OnBridge);
if(!pTarget) {
pTarget = pCell->GetAircraft(pThis->OnBridge);
if(!pTarget) {
pTarget = pCell->GetBuilding();
if(pTarget && !pTarget->IsStrange()) {
pTarget = NULL;
}
}
}

// reached its destination?
if(pTarget && pTarget == pDest) {
// reached the target. capture.
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);
bool finalize = pExt->PerformActionHijack(pTarget);
DoWhat = finalize ? Destroy : Return;
}
}
}
}

return DoWhat;
}

DEFINE_HOOK(471C96, CaptureManagerClass_CanCapture, A)
{
// this is a complete rewrite, because it might be easier to change
// this in a central place than spread all over the source code.
enum {
Allowed = 0x471D2E, // this can be captured
Disallowed = 0x471D35 // can't be captured
};

GET(CaptureManagerClass*, pThis, ECX);
GET(TechnoClass*, pTarget, ESI);
TechnoClass* pCapturer = pThis->Owner;

// target exists and doesn't belong to capturing player
if(!pTarget || pTarget->Owner == pCapturer->Owner) {
return Disallowed;
}

// generally not capturable
if(pTarget->GetTechnoType()->ImmuneToPsionics) {
return Disallowed;
}

// disallow capturing bunkered units
if(pTarget->BunkerLinkedItem && pTarget->BunkerLinkedItem->WhatAmI() == abs_Unit) {
return Disallowed;
}

// TODO: extend this for mind-control priorities
if(pTarget->IsMindControlled() || pTarget->MindControlledByHouse) {
return Disallowed;
}

// free slot? (move on if infinite or single slot which will be freed if used)
if(!pThis->InfiniteMindControl && pThis->MaxControlNodes != 1 && pThis->ControlNodes.Count >= pThis->MaxControlNodes) {
return Disallowed;
}

// currently disallowed
eMission mission = pTarget->CurrentMission;
if(pTarget->IsIronCurtained() || mission == mission_Selling || mission == mission_Construction) {
return Disallowed;
}

// driver killed. has no mind.
TechnoExt::ExtData* pTargetExt = TechnoExt::ExtMap.Find(pTarget);
if(pTargetExt->DriverKilled) {
return Disallowed;
}

// passed all tests
return Allowed;
}

DEFINE_HOOK(53C450, TechnoClass_CanBePermaMC, 5)
{
// complete rewrite. used by psychic dominator, ai targeting, etc.
GET(TechnoClass*, pThis, ECX);
BYTE ret = 0;

if(pThis && pThis->WhatAmI() != abs_Building
&& !pThis->IsIronCurtained() && !pThis->IsInAir()) {

TechnoTypeClass* pType = pThis->GetTechnoType();
if(!pType->ImmuneToPsionics && !pType->BalloonHover) {

// KillDriver check
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);
if(!pExt->DriverKilled) {
ret = 1;
}
}
}

R->AL(ret);
return 0x53C4BA;
}

DEFINE_HOOK(73758A, UnitClass_ReceivedRadioCommand_QueryEnterAsPassenger_KillDriver, 6)
{
// prevent units from getting the enter cursor on transports
// with killed drivers.
GET(TechnoClass*, pThis, ESI);
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);
return (pExt->DriverKilled ? 0x73761F : 0);
}

DEFINE_HOOK(41946B, AircraftClass_ReceivedRadioCommand_QueryEnterAsPassenger_KillDriver, 6)
{
// prevent units from getting the enter cursor on transports
// with killed drivers.
GET(TechnoClass*, pThis, ESI);
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);
return (pExt->DriverKilled ? 0x4190DD : 0);
}

DEFINE_HOOK(6F6A58, TechnoClass_DrawHealthBar_HidePips_KillDriver, 6)
{
// prevent player from seeing pips on transports with killed drivers.
GET(TechnoClass*, pThis, ESI);
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);
return (pExt->DriverKilled ? 0x6F6AB6 : 0);
}

DEFINE_HOOK(7087EB, TechnoClass_ShouldRetaliate_KillDriver, 6)
{
// prevent units with killed drivers from retaliating.
GET(TechnoClass*, pThis, ESI);
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);
return (pExt->DriverKilled ? 0x708B17 : 0);
}

DEFINE_HOOK(7091D6, TechnoClass_CanPassiveAquire_KillDriver, 6)
{
// prevent units with killed drivers from looking for victims.
GET(TechnoClass*, pThis, ESI);
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);
return (pExt->DriverKilled ? 0x70927D : 0);
}

DEFINE_HOOK(6F3283, TechnoClass_CanScatter_KillDriver, 8)
{
// prevent units with killed drivers from scattering when attacked.
GET(TechnoClass*, pThis, ESI);
TechnoExt::ExtData* pExt = TechnoExt::ExtMap.Find(pThis);
return (pExt->DriverKilled ? 0x6F32C5 : 0);
}

DEFINE_HOOK(73A1BC, UnitClass_UpdatePosition_EnteredGrinder, 7)
@@ -263,6 +263,13 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(TechnoTypeClass *pThis, CCINIClass

this->VoiceRepair.Read(&exINI, section, "VoiceIFVRepair");

this->HijackerEnterSound.Read(&exINI, section, "VehicleThief.EnterSound");
this->HijackerLeaveSound.Read(&exINI, section, "VehicleThief.LeaveSound");
this->HijackerKillPilots.Read(&exINI, section, "VehicleThief.KillPilots");
this->HijackerBreakMindControl.Read(&exINI, section, "VehicleThief.BreakMindControl");
this->HijackerAllowed.Read(&exINI, section, "VehicleThief.Allowed");
this->HijackerOneTime.Read(&exINI, section, "VehicleThief.OneTime");

this->IC_Modifier = (float)pINI->ReadDouble(section, "IronCurtain.Modifier", this->IC_Modifier);

this->Chronoshift_Allow.Read(&exINI, section, "Chronoshift.Allow");
@@ -99,6 +99,13 @@ class TechnoTypeExt

ValueableIdx<int, VocClass> VoiceRepair;

ValueableIdx<int, VocClass> HijackerEnterSound;
ValueableIdx<int, VocClass> HijackerLeaveSound;
Valueable<int> HijackerKillPilots;
Valueable<bool> HijackerBreakMindControl;
Valueable<bool> HijackerAllowed;
Valueable<bool> HijackerOneTime;

Customizable<UnitTypeClass *> WaterImage;

char CameoPCX[0x20];
@@ -168,6 +175,12 @@ class TechnoTypeExt
MindControlExperienceSelfModifier (0.0F),
MindControlExperienceVictimModifier (1.0F),
VoiceRepair (-1),
HijackerEnterSound (-1),
HijackerLeaveSound (-1),
HijackerKillPilots (0),
HijackerBreakMindControl (true),
HijackerAllowed (true),
HijackerOneTime (false),
WaterImage (NULL),
CanBeReversed (true),
RadarJamRadius (0),
@@ -398,12 +398,23 @@ bool WarheadTypeExt::ExtData::applyKillDriver(BulletClass* Bullet) {
TechnoExt::EjectPassengers(pTarget, -1);
}

// remove the hijacker
pTarget->HijackerInfantryType = -1;

// If this unit is driving under influence, we have to free it first
if(TechnoClass *Controller = pTarget->MindControlledBy) {
if(CaptureManagerClass *MC = Controller->CaptureManager) {
MC->FreeUnit(pTarget);
}
}
pTarget->MindControlledByAUnit = false;
pTarget->MindControlledByHouse = NULL;

// remove the mind-control ring anim
if(pTarget->MindControlRingAnim) {
pTarget->MindControlRingAnim->UnInit();
pTarget->MindControlRingAnim = NULL;
}

// If this unit mind controls stuff, we should free the controllees, since they still belong to the previous owner
if(pTarget->CaptureManager) {
@@ -434,11 +445,15 @@ bool WarheadTypeExt::ExtData::applyKillDriver(BulletClass* Bullet) {
pSlaveManager->Killed(Bullet->Owner);
pSlaveManager->ZeroOutSlaves();
pTarget->SlaveManager->Owner = pTarget;
pTarget->SlaveManager->SuspendWork();
}

TechnoExt::ExtData* TargetExt = TechnoExt::ExtMap.Find(pTarget);
TargetExt->DriverKilled = true;

// Hand over to Civilian/Special house
pTarget->SetOwningHouse(HouseClass::FindByCountryIndex(HouseTypeClass::FindIndexOfName("Special")));
pTarget->QueueMission(mission_Sticky, true);
pTarget->QueueMission(mission_Harmless, true);
return true;
} else {
return false;
@@ -157,4 +157,14 @@ class SuperWeaponFlags {
};
};

class AresAction {
public:
typedef int Value;
enum {
None = 0,
Hijack = 1,
Drive = 2
};
};

#endif