diff --git a/CREDITS.md b/CREDITS.md index c2b30b3bd6..eee4bafb46 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -455,6 +455,9 @@ This page lists all the individual contributions to the project by their author. - Customize the country displayed in `Sight` - The parachute animation supports `ShouldUseCellDrawer=no` to remove the hard-coded requirement to use the owner's color palette, while inheriting the animation owner's current country - New `NoAmmoWeapons` tag supporting multiple weapon types + - Fix an issue in Ares where vehicles would occasionally continue to fire and move even after the driver had been killed + - New weapon filter tag `CanTarget.DriverKilled` + - Ares' Chrono Prisons / Abductors Expansion - **NetsuNegi**: - Forbidding parallel AI queues by type - Jumpjet crash speed fix when crashing onto building diff --git a/YRpp b/YRpp index 8468aab5ea..46fe722939 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 8468aab5eab6c2057532ed095e36f639a37564fc +Subproject commit 46fe722939c34b2e67a1385231c374721ca77f79 diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 496ef77389..1606107b0a 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -392,6 +392,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Allowed customizing the default value of `[Warhead] -> PreventScatter` via `[CombatDamage] -> Warhead.PreventScatter`. - Allowed `SW.ShowCameo` and `SW.ManualFire` to work independently of `SW.AutoFire`. - Fixed the bug that Ares tunnel-type buildings cannot unload via the Deploy hotkey or command bar button. +- Fixed an issue in Ares where vehicles would occasionally continue to fire and move even after the driver had been killed. ## Newly added global settings @@ -3386,6 +3387,19 @@ AmbientDamage.Warhead= ; WarheadType AmbientDamage.IgnoreTarget= ; boolean, default to [General] -> AmbientDamage.IgnoreTarget ``` +### Ares' Chrono Prisons / Abductors Expansion + +- When Ares brings a vehicle—whose driver has been killed—into the prison and changes its current country of affiliation, the vehicle, upon being released, will be unable to fire, move, or be recaptured by an engineer. Therefore, two new tags have been added to handle this situation (requires at least Ares 3.0). + - `Abductor.ChangeOwner.ResetDriverKilled` allows you to reset the driver's status after capturing a vehicle whose driver has been killed. Determine whether to retain the status of a driver who has been killed based on the `MultiplayPassive` setting in the country where the capturing player is currently located. + - `Abductor.ChangeOwner.IgnoreDriverKilled` prevents the current country of a vehicle whose driver has been killed from being changed; the effect is equivalent to `Abductor.ChangeOwner=no`. + +In `rulesmd.ini`: +```ini +[SOMEWEAPON] ; WeaponType +Abductor.ChangeOwner.ResetDriverKilled=true ; boolean +Abductor.ChangeOwner.IgnoreDriverKilled=false ; boolean +``` + ### Can attack allies - Weapons now support `AttackFriendlies` and `AttackCursorOnFriendlies`. They override the firer's `AttackFriendlies` and `AttackCursorOnFriendlies`. diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 6416ba1e66..82f87585a1 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -3721,6 +3721,17 @@ CanTarget.IronCurtained= ; boolean, default to [CombatDamage] -> CanT AutoTarget.IronCurtained= ; boolean, default to [CombatDamage] -> AutoTarget.IronCurtained ``` +### Customize whether weapon can target driver-killed vehicles + +- In Ares, you cannot skip certain objectives based on whether a unit's driver is dead; this feature has now been added (requires at least Ares 3.0). + - `CanTarget.DriverKilled` allows attacks only against units whose drivers have been killed. + +In `rulesmd.ini`: +```ini +[SOMEWEAPON] ; WeaponType +CanTarget.DriverKilled=false ; boolean +``` + ### Delayed firing - It is possible to have any weapon fire with a delay by setting `DelayedFire.Duration` on a WeaponType - it supports a single integer or two comma-separated ones for a random range to pick value from. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 29bf7c10ce..8805a43468 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -2050,6 +2050,7 @@ HideShakeEffects=false ; boolean - [More convenient vanilla action script target specification](AI-Scripting-and-Mapping.md#more-convenient-vanilla-action-script-target-specification) (by NetsuNegi) - Global default value for `ShouldUseCellDrawer` (by Noble_Fish) - [New `NoAmmoWeapons` tag supporting multiple weapon types](New-or-Enhanced-Logics.md#no-ammo-weapons) (by FlyStar) +- New weapon filter tag `CanTarget.DriverKilled` (by FlyStar) #### Vanilla fixes: - Fixed the bug where a building with `Factory=BuildingType` owned by the AI did not play `ProductionAnim` when placing a produced building (by Noble_Fish) @@ -2079,6 +2080,8 @@ HideShakeEffects=false ; boolean - Allowed `SW.ShowCameo` and `SW.ManualFire` to work independently of `SW.AutoFire` (by Noble_Fish) - [Added a global default value for `KeepAlive`](Fixed-or-Improved-Logics.md#add-a-global-default-value-for-keepalive) (by FlyStar) - Fixed the bug that Ares tunnel-type buildings cannot unload via the Deploy hotkey or command bar button (by Noble_Fish) +- Fixed an issue in Ares where vehicles would occasionally continue to fire and move even after the driver had been killed (by FlyStar) +- Ares' Chrono Prisons / Abductors Expansion (by FlyStar) ``` ### 0.5 diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 085c7d0c1d..3586db2657 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1077,6 +1077,10 @@ bool __fastcall TechnoExt::ApplyKillDriver(TechnoClass** pData, void*, HouseClas pThis->SetTarget(nullptr); pThis->SetDestination(nullptr, false); + // Thank you to NetsuNegi for the guidance. + if (!pThis->IsAttackedByLocomotor) + pThis->StopMoving(); + auto pTag = pThis->AttachedTag; if (pTag) @@ -1163,6 +1167,18 @@ FireError TechnoExt::GetFireErrorIgnoreDisableWeapons(TechnoClass* pThis, Abstra return fireError; } +bool TechnoExt::DriverKilled(TechnoClass* pThis) +{ + // Determine whether the driver was killed. + return AresHelper::CanUseAres && reinterpret_cast(pThis->align_154)->DriverKilled; +} + +Mission TechnoExt::DriverKilledMission(TechnoClass* pThis, Mission mission) +{ + // After the driver is killed, the vehicle will no longer perform any missions other than `Harmless`. + return TechnoExt::DriverKilled(pThis) ? Mission::Harmless : mission; +} + // ============================= // load / save diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index b61e24f908..dfd02f8d8d 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -273,6 +273,9 @@ class TechnoExt : public RadioExt, public Detach::Listener static bool EjectSurvivor(FootClass* pSurvivor, CoordStruct coords, bool select); static bool __fastcall ApplyKillDriver(TechnoClass** pData, void*, HouseClass* pToHouse, TechnoClass* pKiller, bool resetVeterancy); + static bool DriverKilled(TechnoClass* pThis); + static Mission DriverKilledMission(TechnoClass* pThis, Mission mission); + // WeaponHelpers.cpp static int PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, AbstractClass* pTarget, int weaponIndexOne, int weaponIndexTwo, bool allowFallback = true, bool allowAAFallback = true); static void FireWeaponAtSelf(TechnoClass* pThis, WeaponTypeClass* pWeaponType); diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 5328d008d1..41d5fd1adc 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -359,6 +359,10 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6) GET_STACK(AbstractClass*, pTarget, STACK_OFFSET(0x20, 0x4)); GET(TechnoClass*, pTargetTechno, EBP); + // Drivers are prohibited from continuing to fire after being killed. + if (TechnoExt::DriverKilled(pThis)) + return CannotFire; + // Checking for nullptr is not required here, since the game has already executed them before calling the hook -- Belonit const auto pWH = pWeapon->Warhead; const auto pWHExt = WarheadTypeExt::Fetch(pWH); @@ -416,7 +420,8 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6) || !EnumFunctions::CanTargetHouse(pWeaponExt->CanTargetHouses, pThis->Owner, pTargetTechno->Owner) || !pWeaponExt->IsHealthInThreshold(pTargetTechno) || !pWeaponExt->IsVeterancyInThreshold(pTargetTechno) - || !pWeaponExt->HasRequiredAttachedEffects(pTargetTechno, pThis)) + || !pWeaponExt->HasRequiredAttachedEffects(pTargetTechno, pThis) + || (pWeaponExt->CanTarget_DriverKilled && !TechnoExt::DriverKilled(pTargetTechno))) { return CannotFire; } diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 079b006cfa..c43ae6a660 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -2620,3 +2620,52 @@ DEFINE_HOOK(0x5F5B03, ObjectClass_SpawnParachuted_ShouldUseCellDrawer, 0x7) R->EAX(pThis->WhatAmI()); return Continue; } + +// After the driver is killed, the vehicle will no longer perform any missions other than `Harmless`. +#pragma region DriverKilledFix + +DEFINE_HOOK_AGAIN(0x708412, TechnoClass_BeAttacked_DriverKilled, 0x6) // Unit +DEFINE_HOOK(0x7081DC, TechnoClass_BeAttacked_DriverKilled, 0x6) // Infantry +{ + GET(FootClass*, pThis, ESI); + + if (TechnoExt::DriverKilled(pThis)) + { + R->AL(false); + return R->Origin() + 0x6; + } + + return 0; +} + +static void __fastcall TechnoClass_Guard_ForceMission_DriverKilled(TechnoClass* pThis, void*, Mission mission) +{ + pThis->ForceMission(TechnoExt::DriverKilledMission(pThis, mission)); +} + +DEFINE_FUNCTION_JUMP(CALL6, 0x70F87B, TechnoClass_Guard_ForceMission_DriverKilled) + +static bool __fastcall UnitClass_EnterIdleMode_QueueMission_DriverKilled(UnitClass* pThis, void*, Mission mission, bool start_mission) +{ + return pThis->QueueMission(TechnoExt::DriverKilledMission(pThis, mission), start_mission); +} + +DEFINE_FUNCTION_JUMP(CALL6, 0x738D1B, UnitClass_EnterIdleMode_QueueMission_DriverKilled) + +// If you encounter a problem that you really can't solve, you can restore it here. +/* +static bool __fastcall UnitClass_QueueMission(UnitClass* pThis, void*, Mission mission, bool start_mission) +{ + return pThis->MissionClass::QueueMission(TechnoExt::DriverKilledMission(pThis, mission), start_mission); +} + +static void __fastcall UnitClass_ForceMission(UnitClass* pThis, void*, Mission mission) +{ + pThis->MissionClass::ForceMission(TechnoExt::DriverKilledMission(pThis, mission)); +} + +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5E58, UnitClass_QueueMission) +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5E60, UnitClass_ForceMission) +*/ + +#pragma endregion diff --git a/src/Ext/Techno/WeaponHelpers.cpp b/src/Ext/Techno/WeaponHelpers.cpp index 8f9512f88f..2087715766 100644 --- a/src/Ext/Techno/WeaponHelpers.cpp +++ b/src/Ext/Techno/WeaponHelpers.cpp @@ -30,6 +30,8 @@ int TechnoExt::PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, A pTargetCell = pCell; } + const bool targetIsDriverKiiled = pTargetTechno && TechnoExt::DriverKilled(pTargetTechno); + if (!pSecondExt->SkipWeaponPicking) { if (pTargetCell && !EnumFunctions::IsCellEligible(pTargetCell, pSecondExt->CanTarget, true, true)) @@ -41,7 +43,8 @@ int TechnoExt::PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, A || !EnumFunctions::CanTargetHouse(pSecondExt->CanTargetHouses, pThis->Owner, pTargetTechno->Owner) || !pSecondExt->IsHealthInThreshold(pTargetTechno) || !pSecondExt->IsVeterancyInThreshold(pTargetTechno) - || !pSecondExt->HasRequiredAttachedEffects(pTargetTechno, pThis)) + || !pSecondExt->HasRequiredAttachedEffects(pTargetTechno, pThis) + || (pSecondExt->CanTarget_DriverKilled && !targetIsDriverKiiled)) { return weaponIndexOne; } @@ -72,7 +75,8 @@ int TechnoExt::PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, A || !EnumFunctions::CanTargetHouse(pFirstExt->CanTargetHouses, pThis->Owner, pTargetTechno->Owner) || !pFirstExt->IsHealthInThreshold(pTargetTechno) || !pFirstExt->IsVeterancyInThreshold(pTargetTechno) - || !firstAllowedAE) + || !firstAllowedAE + || (pFirstExt->CanTarget_DriverKilled && !targetIsDriverKiiled)) { return weaponIndexTwo; } diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 9a5367fa53..11d26305dd 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -195,6 +195,9 @@ bool UnitExt::CannotMove(UnitClass* pThis) if (pThis->LocomotorSource) return false; + if (TechnoExt::DriverKilled(pThis)) + return true; + const auto pType = pThis->Type; if (pType->Speed == 0) diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index 5d7eb0e645..4ec1bc54d7 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -233,7 +233,7 @@ void WarheadTypeExt::DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, if (this->PenetratesTransport_Level > 0 && damage) this->ApplyPenetratesTransport(pTarget, pOwner, pHouse, coords, damage, distance); - if (this->Taunt && pOwner) + if (this->Taunt && pOwner && !TechnoExt::DriverKilled(pTarget)) pTarget->Override_Mission(Mission::Attack, pOwner, nullptr); if (this->IvanBomb_Detonate) diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index e0124554e6..3a5b75a80a 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -217,12 +217,17 @@ void WeaponTypeExt::LoadFromINIFile(CCINIClass* const pINI) this->CylinderRangefinding.Read(exINI, pSection, "CylinderRangefinding"); this->Anim_Update.Read(exINI, pSection, "Anim.Update"); + this->Abductor_ChangeOwner_ResetDriverKilled.Read(exINI, pSection, "Abductor.ChangeOwner.ResetDriverKilled"); + this->Abductor_ChangeOwner_IgnoreDriverKilled.Read(exINI, pSection, "Abductor.ChangeOwner.IgnoreDriverKilled"); + this->CanTarget_DriverKilled.Read(exINI, pSection, "CanTarget.DriverKilled"); + // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All || this->CanTarget_MaxHealth < 1.0 || this->CanTarget_MinHealth > 0.0 || this->CanTargetVeterancy != AffectedVeterancy::All || this->AttachEffect_RequiredTypes.size() || this->AttachEffect_RequiredGroups.size() - || this->AttachEffect_DisallowedTypes.size() || this->AttachEffect_DisallowedGroups.size()) + || this->AttachEffect_DisallowedTypes.size() || this->AttachEffect_DisallowedGroups.size() + || this->CanTarget_DriverKilled) { this->SkipWeaponPicking = false; } @@ -333,6 +338,9 @@ void WeaponTypeExt::Serialize(T& Stm) .Process(this->AttackNoThreatBuildings) .Process(this->CylinderRangefinding) .Process(this->Anim_Update) + .Process(this->Abductor_ChangeOwner_ResetDriverKilled) + .Process(this->Abductor_ChangeOwner_IgnoreDriverKilled) + .Process(this->CanTarget_DriverKilled) ; }; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 86f32e74fb..3929c6f804 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -125,6 +125,10 @@ class WeaponTypeExt final : public AbstractTypeExt Nullable CylinderRangefinding; + Valueable Abductor_ChangeOwner_ResetDriverKilled; + Valueable Abductor_ChangeOwner_IgnoreDriverKilled; + Valueable CanTarget_DriverKilled; + WeaponTypeExt(WeaponTypeClass* OwnerObject) : AbstractTypeExt(OwnerObject) , DiskLaser_Radius { DiskLaserClass::Radius } , ProjectileRange { Leptons(100000) } @@ -223,6 +227,9 @@ class WeaponTypeExt final : public AbstractTypeExt , AttackNoThreatBuildings {} , CylinderRangefinding {} , Anim_Update {} + , Abductor_ChangeOwner_ResetDriverKilled { true } + , Abductor_ChangeOwner_IgnoreDriverKilled { false } + , CanTarget_DriverKilled { false } { } int GetBurstDelay(int burstIndex) const; diff --git a/src/Misc/Hooks.Ares.cpp b/src/Misc/Hooks.Ares.cpp index 90d6e63a4c..49ed47906d 100644 --- a/src/Misc/Hooks.Ares.cpp +++ b/src/Misc/Hooks.Ares.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -260,6 +261,46 @@ static InfantryTypeClass* __fastcall AresHouseExt_GetCrew(HouseClass** pExt_Ares #pragma endregion +#pragma region AresDriverKilled + +namespace AresDriverKilledTemp +{ + WeaponTypeExt* Ext = nullptr; +} + +static bool __fastcall ApplyAbductor_Init(WeaponTypeClass** pExt_Ares, void*, TechnoClass* pFirer, FootClass* pTarget) +{ + AresDriverKilledTemp::Ext = WeaponTypeExt::Fetch(*pExt_Ares); + const bool result = AresFunctions::ApplyAbductor(pExt_Ares, pFirer, pTarget); + AresDriverKilledTemp::Ext = nullptr; + + return result; +} + +struct DummyExtHere +{ + char _pad0[0x9C]; + bool DriverKilled; +}; + +static bool __fastcall ApplyAbductor_DriverKilled(TechnoClass* pTechno, void*, HouseClass* pHouse, bool announce) +{ + if (TechnoExt::DriverKilled(pTechno)) + { + if (AresDriverKilledTemp::Ext->Abductor_ChangeOwner_IgnoreDriverKilled) + return false; + + auto const pExt_Ares = reinterpret_cast(pTechno->align_154); + + if (AresDriverKilledTemp::Ext->Abductor_ChangeOwner_ResetDriverKilled) + pExt_Ares->DriverKilled = pHouse->IsNeutral(); + } + + return pTechno->SetOwningHouse(pHouse, announce); +} + +#pragma endregion + DEFINE_HOOK(0x440580, BuildingClass_Unlimbo_UnitDeliveryFix, 0x5) { if (UnitDeliveryTemp::Placing) @@ -401,6 +442,11 @@ void Apply_Ares3_0_Patches() // Add a new custom crew for a country. Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x4C836, GET_OFFSET(AresHouseExt_GetCrew)); + + // A unit whose driver has been killed will no longer change sides. + Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x537CC, GET_OFFSET(ApplyAbductor_Init)); + Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x5652E, GET_OFFSET(ApplyAbductor_Init)); + Patch::Apply_CALL6(AresHelper::AresBaseAddress + 0x54DC0, GET_OFFSET(ApplyAbductor_DriverKilled)); } void Apply_Ares3_0p1_Patches() @@ -525,4 +571,9 @@ void Apply_Ares3_0p1_Patches() // Add a new custom crew for a country. Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x4D496, GET_OFFSET(AresHouseExt_GetCrew)); + + // A unit whose driver has been killed will no longer change sides. + Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x5447C, GET_OFFSET(ApplyAbductor_Init)); + Patch::Apply_CALL(AresHelper::AresBaseAddress + 0x571DE, GET_OFFSET(ApplyAbductor_Init)); + Patch::Apply_CALL6(AresHelper::AresBaseAddress + 0x55A70, GET_OFFSET(ApplyAbductor_DriverKilled)); } diff --git a/src/Utilities/AresAddressInit.cpp b/src/Utilities/AresAddressInit.cpp index bbc56e475c..39fe99c0e6 100644 --- a/src/Utilities/AresAddressInit.cpp +++ b/src/Utilities/AresAddressInit.cpp @@ -10,6 +10,7 @@ decltype(AresFunctions::SpawnSurvivors) AresFunctions::SpawnSurvivors = nullptr; decltype(AresFunctions::IsTargetConstraintsEligible) AresFunctions::IsTargetConstraintsEligible = nullptr; decltype(AresFunctions::UnitDeliveryStateMachine_Update) AresFunctions::UnitDeliveryStateMachine_Update = nullptr; decltype(AresFunctions::SetSpotlight) AresFunctions::SetSpotlight = nullptr; +decltype(AresFunctions::ApplyAbductor) AresFunctions::ApplyAbductor = nullptr; decltype(AresFunctions::ApplyPermaMC) AresFunctions::ApplyPermaMC = nullptr; decltype(AresFunctions::DetailsCurrentlyEnabled) AresFunctions::DetailsCurrentlyEnabled = nullptr; decltype(AresFunctions::SendPDPlane) AresFunctions::SendPDPlane = nullptr; @@ -54,6 +55,8 @@ void AresFunctions::InitAres3_0() NOTE_ARES_FUN(SetSpotlight, 0x046420); + NOTE_ARES_FUN(ApplyAbductor, 0x054A80); + NOTE_ARES_FUN(ApplyPermaMC, 0x052CD0); NOTE_ARES_FUN(DetailsCurrentlyEnabled, 0x02A6C0); @@ -106,6 +109,8 @@ void AresFunctions::InitAres3_0p1() NOTE_ARES_FUN(SetSpotlight, 0x046F90); + NOTE_ARES_FUN(ApplyAbductor, 0x055730); + NOTE_ARES_FUN(ApplyPermaMC, 0x053980); NOTE_ARES_FUN(DetailsCurrentlyEnabled, 0x02B1C0); diff --git a/src/Utilities/AresFunctions.h b/src/Utilities/AresFunctions.h index 0a3eb40c49..d8e777e567 100644 --- a/src/Utilities/AresFunctions.h +++ b/src/Utilities/AresFunctions.h @@ -39,6 +39,9 @@ class AresFunctions static void(__thiscall* SetSpotlight)(void*, BuildingLightClass* pSpotlight); + // WeaponTypeExt + static bool(__thiscall* ApplyAbductor)(WeaponTypeClass**, TechnoClass* pFirer, FootClass* pTarget); + // WarheadTypeExt static bool(__thiscall* ApplyPermaMC)(void*, HouseClass* pSourceHouse, AbstractClass* pTarget);