diff --git a/CREDITS.md b/CREDITS.md index a228e988e5..46a1d936fc 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -466,6 +466,7 @@ This page lists all the individual contributions to the project by their author. - Customize type selection for IFV - Fix the issue that units will goto farest location if target is closer than `MinimumRange` - Fix a bug introduced by Ares where building types that have `UndeploysInto` cannot display `AltCameo` or `AltCameoPCX` even when you infiltrate enemy buildings with `Factory=UnitType` + - Allow customize either `ExpireWeapon` can detonate or not if invoker of AE is dead - **Apollo** - Translucent SHP drawing patches - **ststl**: - Customizable `ShowTimer` priority of superweapons diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 01d26904d5..c93949afd2 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -37,6 +37,7 @@ This page describes all the engine features that are either new and introduced b - `ExpireWeapon.TriggerOn` determines the exact conditions upon which the weapon is fired, defaults to `expire` which means only if the effect naturally expires. - `ExpireWeapon.CumulativeOnlyOnce`, if set to true, makes it so that `Cumulative=true` attached effects only detonate the weapon once period, instead of once per active instance. On `remove` and `expire` condition this means it will only detonate after last instance has expired or been removed. - `ExpireWeapon.UseInvokerAsOwner` can be used to set the house and TechnoType that created the effect (e.g firer of the weapon that applied it) as the weapon's owner & invoker instead of the object the effect is attached to. + - `ExpireWeapon.InvokerMustAlive`, if set to true, this weapon will disabled when invoker is dead. - `Tint.Color` & `Tint.Intensity` can be used to set a color tint effect and additive lighting increase/decrease on the object the effect is attached to, respectively. - `Tint.VisibleToHouses` can be used to control which houses can see the tint effect. - `FirepowerMultiplier`, `ArmorMultiplier`, `SpeedMultiplier` and `ROFMultiplier` can be used to modify the object's firepower, armor strength, movement speed and weapon reload rate, respectively. @@ -115,6 +116,7 @@ ExpireWeapon= ; WeaponType ExpireWeapon.TriggerOn=expire ; List of expire weapon trigger condition enumeration (none|expire|remove|death|discard|all) ExpireWeapon.CumulativeOnlyOnce=false ; boolean ExpireWeapon.UseInvokerAsOwner=false ; boolean +ExpireWeapon.InvokerMustAlive=true ; boolean Tint.Color= ; integer - R,G,B Tint.Intensity= ; floating point value Tint.VisibleToHouses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 34bcbbdcc2..7db4c89b92 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -467,6 +467,7 @@ New: - CellSpread in cylinder shape (by TaranDahl) - CellSpread damage check if victim is in air or on floor (by TaranDahl) - OpenTopped range bonus and damage multiplier customization for passengers (by Ollerus) +- Allow customize either `ExpireWeapon` can detonate or not if invoker of AE is dead (by NetsuNegi) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 6379f7e5d5..f4651fe063 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -1823,7 +1823,7 @@ void TechnoExt::ExtData::UpdateAttachEffects() bool markForRedraw = false; bool altered = false; std::vector>::iterator it; - std::vector> expireWeapons; + std::vector expireWeapons; for (it = this->AttachedEffects.begin(); it != this->AttachedEffects.end(); ) { @@ -1860,14 +1860,9 @@ void TechnoExt::ExtData::UpdateAttachEffects() if (!pType->Cumulative || !pType->ExpireWeapon_CumulativeOnlyOnce || this->GetAttachedEffectCumulativeCount(pType) < 1) { if (pType->ExpireWeapon_UseInvokerAsOwner) - { - if (auto const pInvoker = attachEffect->GetInvoker()) - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pInvoker)); - } + expireWeapons.emplace_back(pType->ExpireWeapon, attachEffect->GetInvoker(), attachEffect->GetInvokerHouse()); else - { - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pThis)); - } + expireWeapons.emplace_back(pType->ExpireWeapon, pThis, pThis->Owner); } } @@ -1894,11 +1889,8 @@ void TechnoExt::ExtData::UpdateAttachEffects() auto const coords = pThis->GetCoords(); - for (auto const& pair : expireWeapons) - { - auto const pInvoker = pair.second; - WeaponTypeExt::DetonateAt(pair.first, coords, pInvoker, pInvoker->Owner, pThis); - } + for (auto const& [pWeapon, pInvoker, pInvokerHouse] : expireWeapons) + WeaponTypeExt::DetonateAt(pWeapon, coords, pInvoker, pInvokerHouse, pThis); } // Updates self-owned (defined on TechnoType) AttachEffects, called on type conversion. @@ -1908,7 +1900,7 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() auto const pTypeExt = this->TypeExtData; auto const pTechnoType = pTypeExt->OwnerObject(); std::vector>::iterator it; - std::vector> expireWeapons; + std::vector expireWeapons; bool altered = false; // Delete ones on old type and not on current. @@ -1927,14 +1919,9 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() if (!pType->Cumulative || !pType->ExpireWeapon_CumulativeOnlyOnce || this->GetAttachedEffectCumulativeCount(pType) < 1) { if (pType->ExpireWeapon_UseInvokerAsOwner) - { - if (auto const pInvoker = attachEffect->GetInvoker()) - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pInvoker)); - } + expireWeapons.emplace_back(pType->ExpireWeapon, attachEffect->GetInvoker(), attachEffect->GetInvokerHouse()); else - { - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pThis)); - } + expireWeapons.emplace_back(pType->ExpireWeapon, pThis, pThis->Owner); } } @@ -1949,11 +1936,8 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() auto const coords = pThis->GetCoords(); - for (auto const& pair : expireWeapons) - { - auto const pInvoker = pair.second; - WeaponTypeExt::DetonateAt(pair.first, coords, pInvoker, pInvoker->Owner, pThis); - } + for (auto const& [pWeapon, pInvoker, pInvokerHouse] : expireWeapons) + WeaponTypeExt::DetonateAt(pWeapon, coords, pInvoker, pInvokerHouse, pThis); // Add new ones. const int count = AttachEffectClass::Attach(pThis, pThis->Owner, pThis, pThis, pTypeExt->AttachEffects); diff --git a/src/Ext/Techno/Hooks.ReceiveDamage.cpp b/src/Ext/Techno/Hooks.ReceiveDamage.cpp index 9e57d97361..08029d5f7c 100644 --- a/src/Ext/Techno/Hooks.ReceiveDamage.cpp +++ b/src/Ext/Techno/Hooks.ReceiveDamage.cpp @@ -287,7 +287,7 @@ DEFINE_HOOK(0x702050, TechnoClass_ReceiveDamage_AttachEffectExpireWeapon, 0x6) auto const pExt = TechnoExt::ExtMap.Find(pThis); std::set cumulativeTypes; - std::vector> expireWeapons; + std::vector expireWeapons; for (auto const& attachEffect : pExt->AttachedEffects) { @@ -301,25 +301,17 @@ DEFINE_HOOK(0x702050, TechnoClass_ReceiveDamage_AttachEffectExpireWeapon, 0x6) cumulativeTypes.insert(pType); if (pType->ExpireWeapon_UseInvokerAsOwner) - { - if (auto const pInvoker = attachEffect->GetInvoker()) - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pInvoker)); - } + expireWeapons.emplace_back(pType->ExpireWeapon, attachEffect->GetInvoker(), attachEffect->GetInvokerHouse()); else - { - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pThis)); - } + expireWeapons.emplace_back(pType->ExpireWeapon, pThis, pThis->Owner); } } } auto const coords = pThis->GetCoords(); - for (auto const& pair : expireWeapons) - { - auto const pInvoker = pair.second; - WeaponTypeExt::DetonateAt(pair.first, coords, pInvoker, pInvoker->Owner, pThis); - } + for (auto const& [pWeapon, pInvoker, pInvokerHouse] : expireWeapons) + WeaponTypeExt::DetonateAt(pWeapon, coords, pInvoker, pInvokerHouse, pThis); return 0; } @@ -378,16 +370,17 @@ DEFINE_HOOK(0x701E18, TechnoClass_ReceiveDamage_ReflectDamage, 0x7) if (pType->ReflectDamage_UseInvokerAsOwner) { auto const pInvoker = attachEffect->GetInvoker(); + const auto pInvokerHouse = pInvoker ? pInvoker->Owner : attachEffect->GetInvokerHouse(); - if (pInvoker && EnumFunctions::CanTargetHouse(pType->ReflectDamage_AffectsHouses, pInvoker->Owner, pSourceHouse)) + if (pInvokerHouse && EnumFunctions::CanTargetHouse(pType->ReflectDamage_AffectsHouses, pInvokerHouse, pSourceHouse)) { auto const pWHExtRef = WarheadTypeExt::ExtMap.Find(pWH); pWHExtRef->Reflected = true; if (pType->ReflectDamage_Warhead_Detonate) - WarheadTypeExt::DetonateAt(pWH, pSource, pInvoker, damage, pInvoker->Owner); + WarheadTypeExt::DetonateAt(pWH, pSource, pInvoker, damage, pInvokerHouse); else - pSource->ReceiveDamage(&damage, 0, pWH, pInvoker, false, false, pInvoker->Owner); + pSource->ReceiveDamage(&damage, 0, pWH, pInvoker, false, false, pInvokerHouse); pWHExtRef->Reflected = false; } diff --git a/src/Ext/Techno/WeaponHelpers.cpp b/src/Ext/Techno/WeaponHelpers.cpp index 9eec2f40cf..1ee7e649e0 100644 --- a/src/Ext/Techno/WeaponHelpers.cpp +++ b/src/Ext/Techno/WeaponHelpers.cpp @@ -285,9 +285,10 @@ void TechnoExt::ApplyRevengeWeapon(TechnoClass* pThis, TechnoClass* pSource, War if (pType->RevengeWeapon_UseInvokerAsOwner) { auto const pInvoker = attachEffect->GetInvoker(); + const auto pInvokerHouse = pInvoker ? pInvoker->Owner : attachEffect->GetInvokerHouse(); - if (pInvoker && EnumFunctions::CanTargetHouse(pType->RevengeWeapon_AffectsHouses, pInvoker->Owner, pSourceOwner)) - WeaponTypeExt::DetonateAt(pType->RevengeWeapon, pSource, pInvoker); + if (pInvokerHouse && EnumFunctions::CanTargetHouse(pType->RevengeWeapon_AffectsHouses, pInvokerHouse, pSourceOwner)) + WeaponTypeExt::DetonateAt(pType->RevengeWeapon, pSource, pInvoker, pInvokerHouse); } else if (EnumFunctions::CanTargetHouse(pType->RevengeWeapon_AffectsHouses, pThisOwner, pSourceOwner)) { diff --git a/src/New/Entity/AttachEffectClass.cpp b/src/New/Entity/AttachEffectClass.cpp index 62717dfa8b..64e7f04a53 100644 --- a/src/New/Entity/AttachEffectClass.cpp +++ b/src/New/Entity/AttachEffectClass.cpp @@ -138,7 +138,11 @@ void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) { if (pTechno == pEffect->Invoker) { - AnnounceInvalidPointer(pEffect->Invoker, ptr); + pEffect->Invoker = nullptr; + + if ((pEffect->Type->DiscardOn & DiscardCondition::InvokerDie) != DiscardCondition::None) + pEffect->ShouldBeDiscarded = true; + count--; if (count <= 0) @@ -867,7 +871,7 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass auto const targetAEs = &pTargetExt->AttachedEffects; std::vector>::iterator it; - std::vector> expireWeapons; + std::vector expireWeapons; for (it = targetAEs->begin(); it != targetAEs->end(); ) { @@ -886,14 +890,9 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass if (!pType->Cumulative || !pType->ExpireWeapon_CumulativeOnlyOnce || stackCount == 1) { if (pType->ExpireWeapon_UseInvokerAsOwner) - { - if (auto const pInvoker = attachEffect->Invoker) - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pInvoker)); - } + expireWeapons.emplace_back(pType->ExpireWeapon, attachEffect->Invoker, attachEffect->InvokerHouse); else - { - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pTarget)); - } + expireWeapons.emplace_back(pType->ExpireWeapon, pTarget, pTarget->Owner); } } @@ -921,11 +920,8 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass auto const coords = pTarget->GetCoords(); - for (auto const& pair : expireWeapons) - { - auto const pInvoker = pair.second; - WeaponTypeExt::DetonateAt(pair.first, coords, pInvoker, pInvoker->Owner, pTarget); - } + for (auto const& [pWeapon, pInvoker, pInvokerHouse] : expireWeapons) + WeaponTypeExt::DetonateAt(pWeapon, coords, pInvoker, pInvokerHouse, pTarget); return detachedCount; } diff --git a/src/New/Entity/AttachEffectClass.h b/src/New/Entity/AttachEffectClass.h index c596d6d709..5486826603 100644 --- a/src/New/Entity/AttachEffectClass.h +++ b/src/New/Entity/AttachEffectClass.h @@ -40,6 +40,7 @@ class AttachEffectClass bool ShouldBeDiscardedNow(); bool IsFromSource(TechnoClass* pInvoker, AbstractClass* pSource) const { return pInvoker == this->Invoker && pSource == this->Source; } TechnoClass* GetInvoker() const { return this->Invoker; } + HouseClass* GetInvokerHouse() const { return this->InvokerHouse; } bool IsActive() const { return this->IsOnline && this->IsActiveIgnorePowered(); } bool IsActiveIgnorePowered() const @@ -138,3 +139,16 @@ struct AttachEffectTechnoProperties , HasCritModifiers { false } { } }; + +struct ExpireWeaponData +{ + WeaponTypeClass* Weapon { nullptr }; + TechnoClass* Owner { nullptr }; + HouseClass* OwnerHouse { nullptr }; + + ExpireWeaponData(WeaponTypeClass* pWeapon, TechnoClass* pOwner, HouseClass* pOwnerHouse) + : Weapon(pWeapon) + , Owner(pOwner) + , OwnerHouse(pOwnerHouse) + { } +}; diff --git a/src/New/Type/AttachEffectTypeClass.cpp b/src/New/Type/AttachEffectTypeClass.cpp index 0b8191e409..69e5fe5398 100644 --- a/src/New/Type/AttachEffectTypeClass.cpp +++ b/src/New/Type/AttachEffectTypeClass.cpp @@ -290,6 +290,10 @@ namespace detail { parsed |= DiscardCondition::Firing; } + else if (!_strcmpi(cur, "invokerdie")) + { + parsed |= DiscardCondition::InvokerDie; + } else { Debug::INIParseFailed(pSection, pKey, cur, "Expected a discard condition type"); diff --git a/src/New/Type/AttachEffectTypeClass.h b/src/New/Type/AttachEffectTypeClass.h index b3d76711d3..47ed1e177e 100644 --- a/src/New/Type/AttachEffectTypeClass.h +++ b/src/New/Type/AttachEffectTypeClass.h @@ -18,7 +18,8 @@ enum class DiscardCondition : unsigned char Drain = 0x8, InRange = 0x10, OutOfRange = 0x20, - Firing = 0x40 + Firing = 0x40, + InvokerDie = 0x80 }; MAKE_ENUM_FLAGS(DiscardCondition);