Index: Ext/Techno/Body.h =================================================================== --- Ext/Techno/Body.h (revision 454) +++ Ext/Techno/Body.h (working copy) @@ -43,6 +43,8 @@ BuildingClass *GarrisonedIn; // when infantry garrisons a building, we need a fast way to find said building when damage forwarding kills it + AnimClass *EMPSparkleAnim; + ExtData(const DWORD Canary, TT* const OwnerObject) : Extension(Canary, OwnerObject), idxSlot_Wave (0), idxSlot_Beam (0), @@ -50,7 +52,8 @@ idxSlot_Parasite(0), Survivors_Done (0), Insignia_Image (NULL), - GarrisonedIn (NULL) + GarrisonedIn (NULL), + EMPSparkleAnim (NULL) { this->CloakSkipTimer.Stop(); }; Index: Ext/WarheadType/Body.cpp =================================================================== --- Ext/WarheadType/Body.cpp (revision 454) +++ Ext/WarheadType/Body.cpp (working copy) @@ -1,6 +1,7 @@ #include "Body.h" #include #include "../../Enum/ArmorTypes.h" +#include "../Techno/Body.h" #include #include @@ -25,10 +26,10 @@ hash_ionExt WarheadTypeExt::IonExt; -hash_technoExt WarheadTypeExt::TechnoExt; - WarheadTypeClass * WarheadTypeExt::Temporal_WH = NULL; +WarheadTypeClass * WarheadTypeExt::EMP_WH = NULL; + void WarheadTypeExt::ExtData::LoadFromINIFile(WarheadTypeClass *pThis, CCINIClass *pINI) { const char * section = pThis->ID; @@ -79,7 +80,6 @@ void Container::InvalidatePointer(void *ptr) { AnnounceInvalidPointerMap(WarheadTypeExt::IonExt, ptr); - AnnounceInvalidPointerMap(WarheadTypeExt::TechnoExt, ptr); AnnounceInvalidPointer(WarheadTypeExt::Temporal_WH, ptr); } @@ -169,13 +169,13 @@ if (this->EMP_Duration) { CellStruct cellCoords = MapClass::Instance->GetCellAt(coords)->MapCoords; - WarheadTypeExt::TechnoExt[source] = this; + // the cap is saved with the ExtData of TechnoExt. + WarheadTypeExt::EMP_WH = this->AttachedToObject; + // create the pulse. EMPulseClass *placeholder; GAME_ALLOC(EMPulseClass, placeholder, cellCoords, int(this->AttachedToObject->CellSpread), this->EMP_Duration, source); - - WarheadTypeExt::TechnoExt.erase(source); } } Index: Ext/WarheadType/Body.h =================================================================== --- Ext/WarheadType/Body.h (revision 454) +++ Ext/WarheadType/Body.h (working copy) @@ -105,10 +105,10 @@ static WarheadTypeClass *Temporal_WH; + static WarheadTypeClass *EMP_WH; + static hash_map IonExt; - static hash_map TechnoExt; - static void applyRipples(WarheadTypeClass * pWH, CoordStruct* coords) { if(auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH)) { pWHExt->applyRipples(coords); @@ -130,8 +130,21 @@ } } static void applyOccupantDamage(BulletClass *); + + static void createEMPulse(EMPulseClass *, TechnoClass *); + static void disableEMPEffect(TechnoClass *); + +private: + static bool isEMPTypeImmune(TechnoClass *, HouseClass *); + static bool isEMPImmune(TechnoClass *, HouseClass *); + static bool canAffectTarget(TechnoClass *, HouseClass *, WarheadTypeClass *); + static bool canBeEMPAffected(TechnoClass *, HouseClass *); + static bool shouldEMPHouse(HouseClass *, HouseClass *, WarheadTypeExt::ExtData *); + static int getCappedDuration(int, int, int); + static void updateRadarBlackout(TechnoClass *); + static bool enableEMPEffect(TechnoClass *, ObjectClass *); + static bool verbose; }; typedef hash_map hash_ionExt; -typedef hash_map hash_technoExt; #endif Index: Ext/WarheadType/Hooks.EMP.cpp =================================================================== --- Ext/WarheadType/Hooks.EMP.cpp (revision 454) +++ Ext/WarheadType/Hooks.EMP.cpp (working copy) @@ -1,14 +1,16 @@ #include "Body.h" -#include "../TechnoType//Body.h" -bool Verbose = true; +#include "../Techno/Body.h" +#include "../TechnoType/Body.h" -bool IsTypeImmune(TechnoClass * Unit, HouseClass * SourceHouse) { - // If houses differ, TypeImmune does not count. +bool WarheadTypeExt::verbose = true; + +bool WarheadTypeExt::isEMPTypeImmune(TechnoClass * Unit, HouseClass * SourceHouse) { + // if houses differ, TypeImmune does not count. if (Unit->Owner == SourceHouse) { return false; } - // Find an EMP weapon. + // find an emp weapon. TechnoTypeClass *TT = Unit->GetTechnoType(); if (TT->TypeImmune) { for (int i = 0; i < 18; ++i) { @@ -21,7 +23,7 @@ WarheadTypeClass *WarheadType = WeaponType->Warhead; if (WarheadType && WarheadType->EMEffect) { - // This unit can fire emps and type immunity + // this unit can fire emps and type immunity // grants it to never be affected. return true; } @@ -31,7 +33,7 @@ return false; } -bool IsEMPImmune(TechnoClass * Unit, HouseClass * SourceHouse) { +bool WarheadTypeExt::isEMPImmune(TechnoClass * Unit, HouseClass * SourceHouse) { // iron curtained objects can not be emp'd if (Unit->IsIronCurtained()) { return true; @@ -40,33 +42,59 @@ // this can be overridden by a flag on the techno. TechnoTypeExt::ExtData *pData = TechnoTypeExt::ExtMap.Find(Unit->GetTechnoType()); if (pData->ImmuneToEMP) { - if (Verbose) - Debug::Log("[IsEMPImmune] \"%s\" is ImmuneToEMP.\n", Unit->get_ID()); + if (verbose) + Debug::Log("[isEMPImmune] \"%s\" is ImmuneToEMP.\n", Unit->get_ID()); return true; } // ignore if type immune. don't even try. - if (IsTypeImmune(Unit, SourceHouse)) { + if (isEMPTypeImmune(Unit, SourceHouse)) { // This unit can fire emps and type immunity // grants it to never be affected. - if (Verbose) - Debug::Log("[IsEMPImmune] \"%s\" is TypeImmune.\n", Unit->get_ID()); + if (verbose) + Debug::Log("[isEMPImmune] \"%s\" is TypeImmune.\n", Unit->get_ID()); return true; } return false; } -bool CanBeAffected(TechnoClass * Unit, HouseClass * SourceHouse) { +bool WarheadTypeExt::canAffectTarget(TechnoClass * Target, HouseClass * SourceHouse, WarheadTypeClass *WH) { + if (SourceHouse && Target && WH) { + // owner and target house are allied and this warhead + // is set to not hurt any allies. + bool alliedWithTarget = SourceHouse->IsAlliedWith(Target->Owner); + if (alliedWithTarget && !WarheadTypeExt::EMP_WH->AffectsAllies) { + if (verbose) + Debug::Log("[isEMPImmune] \"%s\" does not AffectAllies.\n", WH->ID); + return false; + } + + // this warhead's pulse is designed to fly around + // enemy units. useful for healing. + WarheadTypeExt::ExtData *pWHdata = WarheadTypeExt::ExtMap.Find(WH); + if (!alliedWithTarget && !pWHdata->AffectsEnemies) { + if (verbose) + Debug::Log("[isEMPImmune] \"%s\" does not AffectEnemies.\n", WH->ID); + return false; + } + } + + return true; +} + +bool WarheadTypeExt::canBeEMPAffected(TechnoClass * Unit, HouseClass * SourceHouse) { // check whether this techno can be affected at all - bool isEMPProne = !IsEMPImmune(Unit, SourceHouse); + bool isEMPProne = !isEMPImmune(Unit, SourceHouse); + //&& canAffectTarget(Unit, SourceHouse, WarheadTypeExt::EMP_WH); // unit can be affected. if (isEMPProne) { // buildings are emp prone if they consume power and need it to function if (BuildingClass * VictimBuilding = specific_cast (Unit)) { BuildingTypeClass * VictimBuildingType = VictimBuilding->Type; - isEMPProne = (VictimBuildingType->Powered && (VictimBuildingType->PowerDrain > 0)); + isEMPProne = ((VictimBuildingType->Powered && (VictimBuildingType->PowerDrain > 0)) + || VictimBuildingType->TogglePower); // may have a special function. if (VictimBuildingType->Radar || @@ -91,7 +119,7 @@ return isEMPProne; } -int GetCappedDuration(int CurrentValue, int Duration, int Cap) { +int WarheadTypeExt::getCappedDuration(int CurrentValue, int Duration, int Cap) { // Usually, the new duration is just added. int ProposedDuration = CurrentValue + Duration; @@ -114,28 +142,28 @@ } } -void UpdateRadarBlackout(TechnoClass * Techno) { +void WarheadTypeExt::updateRadarBlackout(TechnoClass * Techno) { if (BuildingClass * Building = specific_cast(Techno)) { if (!Building->Type->InvisibleInGame) { if (Building->Type->Radar) { - if (Verbose) - Debug::Log("[UpdateRadarBlackout] Before: %d, %d\n", + if (verbose) + Debug::Log("[updateRadarBlackout] Before: %d, %d\n", Building->EMPLockRemaining, Building->Owner->RadarBlackoutTimer.TimeLeft); if (Building->EMPLockRemaining > 0) { Building->Owner->CreateRadarOutage(Building->EMPLockRemaining); } else { - Building->Owner->RadarBlackout = 1; // trigger the radar outage check + Building->Owner->RadarBlackoutTimer.TimeLeft = 1; // trigger the radar outage check } - if (Verbose) - Debug::Log("[UpdateRadarBlackout] After: =%d\n", + if (verbose) + Debug::Log("[updateRadarBlackout] After: %d\n", Building->Owner->RadarBlackoutTimer.TimeLeft); } } } } -bool EnableEMPEffect(TechnoClass * Victim, ObjectClass * Souce) { +bool WarheadTypeExt::enableEMPEffect(TechnoClass * Victim, ObjectClass * Souce) { if (BuildingClass * Building = specific_cast(Victim)) { Building->DisableStuff(); if (Building->Type->Radar) { @@ -161,66 +189,35 @@ return false; } -void DisableEMPEffect(TechnoClass * Victim) { +void WarheadTypeExt::disableEMPEffect(TechnoClass * Victim) { if (BuildingClass * Building = specific_cast(Victim)) { if (!Building->Type->InvisibleInGame) { Building->EnableStuff(); if (Building->Type->Radar) { - UpdateRadarBlackout(Building); + updateRadarBlackout(Building); } } } else { if (FootClass * Foot = generic_cast(Victim)) { Foot->Locomotor->Power_On(); } - for (int i = 0; i < AnimClass::Array->Count; ++i) { - if (AnimClass *Anim = AnimClass::Array->GetItem(i)) { - if (Anim->OwnerObject == Victim) { - if (Anim->Type == RulesClass::Instance->EMPulseSparkles) { - Anim->RemainingIterations = 0; // basically "you don't need to show up anymore" - } - } - } + TechnoExt::ExtData *pData = TechnoExt::ExtMap.Find(Victim); + if (pData && pData->EMPSparkleAnim) { + pData->EMPSparkleAnim->RemainingIterations = 0; // basically "you don't need to show up anymore" } } } -bool EMPSparkleExists(TechnoClass * Victim) { - for (int i = 0; i < AnimClass::Array->Count; ++i) { - if (AnimClass *Anim = AnimClass::Array->GetItem(i)) { - if (Anim->OwnerObject == Victim) { - if (Anim->Type == RulesClass::Instance->EMPulseSparkles) { - return true; - } - } - } - } - - return false; -} - -DEFINE_HOOK(4C575E, EMPulseClass_CyborgCheck, 7) { - GET(TechnoClass *, curVictim, ESI); - return curVictim->GetTechnoType()->Cyborg_ ? 0x4C577A : 0; -} - -DEFINE_HOOK(5240BD, CyborgParsingMyArse, 7) { - return 0x5240C4; -} - -// completely new implementation of the EMPulse. -DEFINE_HOOK(4C54E0, EMPulseClass_Initialize, 6) { - GET(EMPulseClass *, pThis, ECX); - GET_STACK(TechnoClass *, pGenerator, 0x4); - +void WarheadTypeExt::createEMPulse(EMPulseClass *pThis, TechnoClass *pGenerator) { + // fill the gaps HouseClass *pHouse = (pGenerator ? pGenerator->Owner : NULL); - // Get cap. - WarheadTypeExt::ExtData *pData = WarheadTypeExt::TechnoExt[pGenerator]; - int Cap = (pData ? pData->EMP_Cap : -1); + // get cap and other extended properties. + WarheadTypeExt::ExtData *pWH = WarheadTypeExt::ExtMap.Find(WarheadTypeExt::EMP_WH); + int Cap = (pWH ? pWH->EMP_Cap : -1); - if (Verbose) - Debug::Log("[EMPulseClass_Initialize] Duration: %d\n", pThis->Duration); + if (WarheadTypeExt::verbose) + Debug::Log("[createEMPulse] Duration: %d\n", pThis->Duration); CellStruct cellCoords = MapClass::Instance->GetCellAt(&pThis->BaseCoords)->MapCoords; int countCells = CellSpread::NumCells(pThis->Spread); @@ -230,57 +227,74 @@ CellClass *c = MapClass::Instance->GetCellAt(&tmpCell); for (ObjectClass *curObj = c->GetContent(); curObj; curObj = curObj->NextObject) { if (TechnoClass * curTechno = generic_cast (curObj)) { - if (Verbose) - Debug::Log("[EMPulseClass_Initialize] Step 1: %s => %s\n", + if (WarheadTypeExt::verbose) + Debug::Log("[createEMPulse] Step 1: %s => %s\n", (pGenerator ? pGenerator->get_ID() : NULL), curTechno->get_ID()); - if (CanBeAffected(curTechno, pHouse)) { - if (Verbose) - Debug::Log("[EMPulseClass_Initialize] Step 2: %s\n", + if (WarheadTypeExt::canBeEMPAffected(curTechno, pHouse)) { + if (WarheadTypeExt::verbose) + Debug::Log("[createEMPulse] Step 2: %s\n", curTechno->get_ID()); // get the new capped value int oldValue = curTechno->EMPLockRemaining; - int newValue = GetCappedDuration(oldValue, pThis->Duration, Cap); + int newValue = WarheadTypeExt::getCappedDuration(oldValue, pThis->Duration, Cap); - if (Verbose) - Debug::Log("[EMPulseClass_Initialize] Step 3: %d\n", + if (WarheadTypeExt::verbose) + Debug::Log("[createEMPulse] Step 3: %d\n", newValue); // can not be less than zero curTechno->EMPLockRemaining = max(0, newValue); - if (Verbose) - Debug::Log("[EMPulseClass_Initialize] Step 4: %d\n", + if (WarheadTypeExt::verbose) + Debug::Log("[createEMPulse] Step 4: %d\n", newValue); // newly de-paralyzed if ((oldValue > 0) && (curTechno->EMPLockRemaining <= 0)) { - if (Verbose) - Debug::Log("[EMPulseClass_Initialize] Step 5a\n"); - DisableEMPEffect( curTechno); + if (WarheadTypeExt::verbose) + Debug::Log("[createEMPulse] Step 5a\n"); + WarheadTypeExt::disableEMPEffect( curTechno); } else if ((oldValue <= 0) && (curTechno->EMPLockRemaining > 0)) { // newly paralyzed unit - if (Verbose) - Debug::Log("[EMPulseClass_Initialize] Step 5b\n"); - if (EnableEMPEffect(curTechno, pGenerator)) { + if (WarheadTypeExt::verbose) + Debug::Log("[createEMPulse] Step 5b\n"); + if (WarheadTypeExt::enableEMPEffect(curTechno, pGenerator)) { continue; } // Set the animation. - if (!EMPSparkleExists(curTechno)) { - AnimClass *EMPSparkles; - GAME_ALLOC(AnimClass, EMPSparkles, RulesClass::Instance->EMPulseSparkles, &curTechno->Location); - EMPSparkles->RemainingIterations = -1; - EMPSparkles->SetOwnerObject(curTechno); + TechnoExt::ExtData *pData = TechnoExt::ExtMap.Find(curTechno); + if (!pData->EMPSparkleAnim) { + GAME_ALLOC(AnimClass, pData->EMPSparkleAnim, RulesClass::Instance->EMPulseSparkles, &curTechno->Location); + pData->EMPSparkleAnim->RemainingIterations = -1; + pData->EMPSparkleAnim->SetOwnerObject(curTechno); } - } else { + } else if (oldValue != newValue) { // At least update the radar, if this is one. - UpdateRadarBlackout( curTechno); + WarheadTypeExt::updateRadarBlackout(curTechno); } } } } } +} +DEFINE_HOOK(4C575E, EMPulseClass_CyborgCheck, 7) { + GET(TechnoClass *, curVictim, ESI); + return curVictim->GetTechnoType()->Cyborg_ ? 0x4C577A : 0; +} + +DEFINE_HOOK(5240BD, CyborgParsingMyArse, 7) { + return 0x5240C4; +} + +// completely new implementation of the EMPulse. +DEFINE_HOOK(4C54E0, EMPulseClass_Initialize, 6) { + GET(EMPulseClass *, pThis, ECX); + GET_STACK(TechnoClass *, pGenerator, 0x4); + + WarheadTypeExt::createEMPulse(pThis, pGenerator); + // skip old function entirely. return 0x4C58B6; } @@ -295,7 +309,7 @@ --pThis->EMPLockRemaining; if (!pThis->EMPLockRemaining) { // the forced vacation just ended. - DisableEMPEffect(pThis); + WarheadTypeExt::disableEMPEffect(pThis); } }