Ares
Functions

Hooks.Prism.cpp File Reference

#include "Body.h"
#include "../BuildingType/Body.h"
#include "../WeaponType/Body.h"
#include <BulletClass.h>
#include <LaserDrawClass.h>

Functions

 DEFINE_HOOK (44B2FE, BuildingClass_Mi_Attack_IsPrism, 6)
 DEFINE_HOOK (447FAE, BuildingClass_GetObjectActivityState, 6)
 DEFINE_HOOK (4503F0, BuildingClass_Update_Prism, 9)
 DEFINE_HOOK (44ABD0, BuildingClass_FireLaser, 5)
 DEFINE_HOOK (4424EF, PrismForward_BuildingDestroyed, 6)
 DEFINE_HOOK (447113, PrismForward_BuildingSold, 6)
 DEFINE_HOOK (448277, PrismForward_BuildingChangeOwner, 5)
 DEFINE_HOOK (71AF76, PrismForward_BuildingWarped, 9)
 DEFINE_HOOK (70FD9A, PrismForward_BuildingDrain, 6)
 DEFINE_HOOK (454B3D, PrismForward_BuildingPowerDown, 6)
 DEFINE_HOOK (44EBF0, PrismForward_BuildingRemoved, 5)

Function Documentation

DEFINE_HOOK ( 44B2FE  ,
BuildingClass_Mi_Attack_IsPrism  ,
 
)
{
        GET(BuildingClass *, B, ESI);
        GET(int, idxWeapon, EBP); //which weapon was chosen to attack the target with
        R->EAX<BuildingTypeClass *>(B->Type);

        enum { IsPrism = 0x44B310, IsNotPrism = 0x44B630, IsCustomPrism = 0x44B6D6};

        BuildingTypeClass *pMasterType = B->Type;
        BuildingTypeExt::ExtData *pMasterTypeData = BuildingTypeExt::ExtMap.Find(pMasterType);

        if (pMasterTypeData->PrismForwarding.Enabled == BuildingTypeExt::cPrismForwarding::YES
                || pMasterTypeData->PrismForwarding.Enabled == BuildingTypeExt::cPrismForwarding::ATTACK) {

                BuildingExt::ExtData *pMasterData = BuildingExt::ExtMap.Find(B);

                if (B->PrismStage == pcs_Idle) {
                        B->PrismStage = pcs_Master;
                        B->DelayBeforeFiring = B->Type->DelayedFireDelay;

                        B->PrismTargetCoords.X = 0;
                        if (pMasterType->get_Secondary()) {
                                if (B->IsOverpowered) {
                                        B->PrismTargetCoords.X = 1;
                                }
                        }

                        B->PrismTargetCoords.Y = B->PrismTargetCoords.Z = 0;
                        pMasterData->PrismForwarding.ModifierReserve = 0.0;
                        pMasterData->PrismForwarding.DamageReserve = 0;

                        int LongestChain = 0;

                        //set up slaves
                        int NetworkSize = 0;
                        int stage = 0;

                        //when it reaches zero we can't acquire any more slaves
                        while (BuildingTypeExt::cPrismForwarding::AcquireSlaves_MultiStage(B, B, stage++, 0, &NetworkSize, &LongestChain) != 0) {}

                        //now we have all the towers we know the longest chain, and can set all the towers' charge delays
                        BuildingTypeExt::cPrismForwarding::SetChargeDelay(B, LongestChain);

                } else if (B->PrismStage == pcs_Slave) {
                        //a slave tower is changing into a master tower at the last second
                        B->PrismStage = pcs_Master;
                        B->PrismTargetCoords.X = 0;
                        if (pMasterType->get_Secondary()) {
                                if (B->IsOverpowered) {
                                        B->PrismTargetCoords.X = 1;
                                }
                        }
                        B->PrismTargetCoords.Y = B->PrismTargetCoords.Z = 0;
                        pMasterData->PrismForwarding.ModifierReserve = 0.0;
                        pMasterData->PrismForwarding.DamageReserve = 0;
                        pMasterData->PrismForwarding.SupportTarget = NULL;

                }

                return IsCustomPrism; //always custom, the new code is a complete rewrite of the old code

        }

        return IsNotPrism;
}
DEFINE_HOOK ( 44EBF0  ,
PrismForward_BuildingRemoved  ,
 
)
{
        GET(BuildingClass *, B, ECX);
        BuildingTypeExt::cPrismForwarding::RemoveFromNetwork(B, true);
        return 0;
}
DEFINE_HOOK ( 454B3D  ,
PrismForward_BuildingPowerDown  ,
 
)
{
        GET(BuildingClass *, B, ESI);
        // this building just realised it needs to go offline
        // it unregistered itself from powered unit controls but hasn't done anything else yet
        BuildingTypeExt::cPrismForwarding::RemoveFromNetwork(B, true);
        return 0;
}
DEFINE_HOOK ( 70FD9A  ,
PrismForward_BuildingDrain  ,
 
)
{
        GET(TechnoClass *, Drainer, ESI);
        GET(TechnoClass *, Drainee, EDI);
        if(Drainee->DrainingMe != Drainer) { // else we're already being drained, nothing to do
                if (BuildingClass * B = specific_cast<BuildingClass *>(Drainee)) {
                        BuildingTypeExt::cPrismForwarding::RemoveFromNetwork(B, true);
                }
        }
        return 0;
}
DEFINE_HOOK ( 71AF76  ,
PrismForward_BuildingWarped  ,
 
)
                                                    {
        GET(TechnoClass *, T, EDI);
        if (BuildingClass * B = specific_cast<BuildingClass *>(T)) {
                BuildingTypeExt::cPrismForwarding::RemoveFromNetwork(B, true);
        }
        return 0;
}
DEFINE_HOOK ( 448277  ,
PrismForward_BuildingChangeOwner  ,
 
)
{
        GET(BuildingClass *, B, ESI);
        GET_STACK(HouseClass *, newOwner, 0x5C);
        
        HouseClass * oldOwner = B->Owner;

        if (newOwner != oldOwner) {
                BuildingTypeClass *pType = B->Type;
                BuildingTypeExt::ExtData *pTypeData = BuildingTypeExt::ExtMap.Find(pType);

                if (pTypeData->PrismForwarding.ToAllies) {
                        BuildingClass *LastTarget = B;
                        BuildingClass *FirstTarget = NULL;
                        while (LastTarget) {
                                BuildingExt::ExtData *pData = BuildingExt::ExtMap.Find(LastTarget);
                                BuildingClass *NextTarget = pData->PrismForwarding.SupportTarget;
                                if (!FirstTarget) {
                                        if(!NextTarget) {
                                                //no first target so either this is a master tower, an idle tower, or not a prism tower at all
                                                //no need to remove
                                                return 0;
                                        }
                                        FirstTarget = NextTarget;
                                }

                                if (!NextTarget) {
                                        //LastTarget is now the master (firing) tower
                                        if (newOwner->IsAlliedWith(LastTarget->Owner) && newOwner->IsAlliedWith(FirstTarget->Owner)) {
                                                //alliances check out so this slave tower can keep on charging.
                                                return 0;
                                        }
                                }
                                LastTarget = NextTarget;
                        }
                }
                //if we reach this point then the alliance checks have failed
                BuildingTypeExt::cPrismForwarding::RemoveFromNetwork(B, false); //false because animation should continue / slave is busy but won't now fire
                
        }

        return 0;
}
DEFINE_HOOK ( 447113  ,
PrismForward_BuildingSold  ,
 
)
{
        GET(BuildingClass *, B, ESI);
        BuildingTypeExt::cPrismForwarding::RemoveFromNetwork(B, true);
        return 0;
}
DEFINE_HOOK ( 4424EF  ,
PrismForward_BuildingDestroyed  ,
 
)
{
        GET(BuildingClass *, B, ESI);
        BuildingTypeExt::cPrismForwarding::RemoveFromNetwork(B, true);
        return 0;
}
DEFINE_HOOK ( 44ABD0  ,
BuildingClass_FireLaser  ,
 
)
{
        GET(BuildingClass *, B, ECX);
        LEA_STACK(CoordStruct *, pTargetXYZ, 0x4);

        BuildingTypeClass *pType = B->Type;
        BuildingTypeExt::ExtData *pTypeData = BuildingTypeExt::ExtMap.Find(pType);

        CoordStruct SourceXYZ, Base = {0, 0, 0};
        B->GetFLH(&SourceXYZ, 0, Base);

        ColorStruct blank(0, 0, 0);

        int idxSupport = -1;
        if (B->Veterancy.IsElite()) {
                idxSupport = pTypeData->PrismForwarding.EliteSupportWeaponIndex;
        } else {
                idxSupport = pTypeData->PrismForwarding.SupportWeaponIndex;
        }

        WeaponTypeClass * supportWeapon = NULL;
        if (idxSupport != -1) {
                supportWeapon = pType->get_Weapon(idxSupport);
        }
        LaserDrawClass * LaserBeam = NULL;
        if (supportWeapon) {
                WeaponTypeExt::ExtData *supportWeaponData = WeaponTypeExt::ExtMap.Find(supportWeapon);
                //IsLaser
                if (supportWeapon->IsLaser) {
                        if (supportWeapon->IsHouseColor) {
                                GAME_ALLOC(LaserDrawClass, LaserBeam, SourceXYZ, *pTargetXYZ, B->Owner->LaserColor, blank, blank, supportWeapon->LaserDuration);
                        } else {
                                GAME_ALLOC(LaserDrawClass, LaserBeam, SourceXYZ, *pTargetXYZ,
                                        supportWeapon->LaserInnerColor, supportWeapon->LaserOuterColor, supportWeapon->LaserOuterSpread,
                                        supportWeapon->LaserDuration);
                        }
                        if(LaserBeam) {
                                LaserBeam->IsHouseColor = supportWeapon->IsHouseColor;
                                //basic thickness (intensity additions are added later)
                                if (supportWeaponData->Laser_Thickness == -1) {
                                        LaserBeam->Thickness = 3; //original default
                                } else {
                                        LaserBeam->Thickness = supportWeaponData->Laser_Thickness;
                                }
                        }
                }
                //IsRadBeam
                if (supportWeapon->IsRadBeam) {
                        RadBeam* supportRadBeam = RadBeam::Allocate(RadBeamType::RadBeam);
                        if (supportRadBeam) {
                                supportRadBeam->SetCoordsSource(&SourceXYZ);
                                supportRadBeam->SetCoordsTarget(pTargetXYZ);
                                if (supportWeaponData->Beam_IsHouseColor) {
                                        supportRadBeam->Color = B->Owner->LaserColor;
                                } else {
                                        supportRadBeam->Color = supportWeaponData->Beam_Color;
                                }
                                supportRadBeam->Period = supportWeaponData->Beam_Duration;
                                supportRadBeam->Amplitude = supportWeaponData->Beam_Amplitude;
                                //Debug::DumpObj((byte *)supportRadBeam, sizeof(RadBeam));
                        }
                }
                //IsElectricBolt
                if (supportWeapon->IsElectricBolt) {
                        EBolt* supportEBolt;
                        GAME_ALLOC(EBolt, supportEBolt);
                        if (supportEBolt) {
                                supportEBolt->AlternateColor = supportWeapon->IsAlternateColor;
                                WeaponTypeExt::BoltExt[supportEBolt] = WeaponTypeExt::ExtMap.Find(supportWeapon);
                                supportEBolt->Fire(SourceXYZ, *pTargetXYZ, 0); //messing with 3rd arg seems to make bolts more jumpy, and parts of them disappear
                        }
                }
                //Report
                if(supportWeapon->Report.Count > 0) {
                        int ReportIndex = ScenarioClass::Instance->Random.RandomRanged(0, supportWeapon->Report.Count - 1);
                        int SoundArrayIndex = supportWeapon->Report.GetItem(ReportIndex);
                        if(SoundArrayIndex != -1) {
                                VocClass::PlayAt(SoundArrayIndex, &SourceXYZ, NULL);
                        }
                }
                //ROF
                B->ReloadTimer.Start(supportWeapon->ROF);
        } else {
                //just the default support beam
                GAME_ALLOC(LaserDrawClass, LaserBeam, SourceXYZ, *pTargetXYZ, B->Owner->LaserColor, blank, blank, RulesClass::Instance->PrismSupportDuration);
                if(LaserBeam) {
                        LaserBeam->IsHouseColor = true;
                        LaserBeam->Thickness = 3;
                }
                B->ReloadTimer.Start(RulesClass::Instance->PrismSupportDelay);
        }

        //Intensity adjustment for LaserBeam
        if (LaserBeam) {
                if (pTypeData->PrismForwarding.Intensity > 0) {
                        BuildingExt::ExtData *pData = BuildingExt::ExtMap.Find(B);
                        LaserBeam->Thickness += (pTypeData->PrismForwarding.Intensity * (B->SupportingPrisms -1));
                }
        }

        B->SupportingPrisms = 0; //not sure why Westwood set this here. We're setting it in BuildingClass_Update_Prism

        return 0x44ACE2;
}
DEFINE_HOOK ( 4503F0  ,
BuildingClass_Update_Prism  ,
 
)
{
        GET(BuildingClass *, pThis, ECX);
        if(int PrismStage = pThis->PrismStage) {
                BuildingExt::ExtData *pData = BuildingExt::ExtMap.Find(pThis);
                if (pData->PrismForwarding.PrismChargeDelay <= 0) {
                        --pThis->DelayBeforeFiring;
                        if(pThis->DelayBeforeFiring <= 0) {
                                if(PrismStage == pcs_Slave) {
                                        if (BuildingClass *pTarget = pData->PrismForwarding.SupportTarget) {
                                                BuildingExt::ExtData *pTargetData = BuildingExt::ExtMap.Find(pTarget);
                                                BuildingTypeClass *pType = pThis->Type;
                                                BuildingTypeExt::ExtData *pTypeData = BuildingTypeExt::ExtMap.Find(pType);
                                                //slave firing
                                                pTargetData->PrismForwarding.ModifierReserve +=
                                                        (pTypeData->PrismForwarding.SupportModifier.Get() + pData->PrismForwarding.ModifierReserve);
                                                pTargetData->PrismForwarding.DamageReserve +=
                                                        (pTypeData->PrismForwarding.DamageAdd.Get()  + pData->PrismForwarding.DamageReserve);
                                                pThis->FireLaser(pThis->PrismTargetCoords);

                                        }
                                }
                                if(PrismStage == pcs_Master) {
                                        if(ObjectClass *Target = pThis->Target) {
                                                if(pThis->GetFireError(Target, pThis->PrismTargetCoords.X, true) == FireError::OK) {
                                                        if(BulletClass *LaserBeam = pThis->Fire(Target, pThis->PrismTargetCoords.X)) {
                                                                BuildingTypeClass *pType = pThis->Type;
                                                                BuildingTypeExt::ExtData *pTypeData = BuildingTypeExt::ExtMap.Find(pType);

                                                                //apparently this is divided by 256 elsewhere
                                                                LaserBeam->DamageMultiplier = ((pData->PrismForwarding.ModifierReserve + 100) * 256) / 100;
                                                                LaserBeam->Health += pTypeData->PrismForwarding.DamageAdd.Get()  + pData->PrismForwarding.DamageReserve;
                                                        }
                                                }
                                        }
                                }
                                //This tower's job is done. Go idle.
                                pData->PrismForwarding.ModifierReserve = 0.0;
                                pData->PrismForwarding.DamageReserve = 0;
                                pData->PrismForwarding.Senders.Clear();
                                pThis->SupportingPrisms = 0; //Ares sets this to the longest backward chain
                                pData->PrismForwarding.SupportTarget = NULL;
                                pThis->PrismStage = pcs_Idle;
                        }
                } else {
                        //still in delayed charge so not actually charging yet
                        --pData->PrismForwarding.PrismChargeDelay;
                        if (pData->PrismForwarding.PrismChargeDelay <= 0) {
                                //now it's time to start charging
                                if (pThis->Type->BuildingAnim[BuildingAnimSlot::Special].Anim[0]) { //only if it actually has a special anim
                                        pThis->DestroyNthAnim(BuildingAnimSlot::Active);
                                        pThis->PlayNthAnim(BuildingAnimSlot::Special);
                                }
                        }
                }
        }
        return 0x4504E2;
}
DEFINE_HOOK ( 447FAE  ,
BuildingClass_GetObjectActivityState  ,
 
)
{
        GET(BuildingClass *, B, ESI);
        enum { BusyCharging = 0x447FB8, NotBusyCharging = 0x447FC3};

        if(B->DelayBeforeFiring > 0) {
                //if this is a slave prism tower, then it might still be able to become a master tower at this time
                BuildingTypeClass *pType = B->Type;
                BuildingTypeExt::ExtData *pTypeData = BuildingTypeExt::ExtMap.Find(pType);
                if (pTypeData->PrismForwarding.Enabled == BuildingTypeExt::cPrismForwarding::YES
                                || pTypeData->PrismForwarding.Enabled == BuildingTypeExt::cPrismForwarding::ATTACK) {
                        //is a prism tower
                        if (B->PrismStage == pcs_Slave && pTypeData->PrismForwarding.BreakSupport) {
                                return NotBusyCharging;
                        }
                }
                return BusyCharging;
        }
        return NotBusyCharging;
}
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines