diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 38342ff07d..7abaabe6bc 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -11,6 +11,7 @@ This page describes all the engine features that are either new and introduced b - If `Duration.ApplyFirepowerMult` set to true, the duration will multiply the invoker's firepower multipliers if it's not negative. Can't reduce duration to below 0 by a negative firepower multiplier. - If `Duration.ApplyArmorMultOnTarget` set to true, the duration will divide the target's armor multipliers if it's not negative. This'll also include `ArmorMultiplier` from its own and ignore `ArmorMultiplier.Allow/DisallowWarheads`. Can't reduce duration to below 0 by a negative armor multiplier. - `Cumulative`, if set to true, allows the same type of effect to be applied on same object multiple times, up to `Cumulative.MaxCount` number or with no limit if `Cumulative.MaxCount` is a negative number. If the target already has `Cumulative.MaxCount` number of the same effect applied on it, trying to attach another will refresh duration of the attached instance with shortest remaining duration. + - If `Cumulative.SimpleStack` also set to true, latter applied effects will be counted as a part of the first one, instead of its own entity. This is better for performance but will make the effect always refresh during a reapplication. - `Powered` controls whether or not the effect is rendered inactive if the object it is attached to is deactivated (`PoweredUnit` or affected by EMP) or on low power. What happens to animation is controlled by `Animation.OfflineAction`. - `DiscardOn` accepts a list of values corresponding to conditions where the attached effect should be discarded. Defaults to `none`, meaning it is never discarded. - `entry`: Discard on exiting the map when entering transports or buildings etc. @@ -70,8 +71,8 @@ This page describes all the engine features that are either new and introduced b - AttachEffectTypes can be attached to objects via Warheads using `AttachEffect.AttachTypes`. - `AttachEffect.DurationOverrides` can be used to override the default durations. Duration matching the position in `AttachTypes` is used for that type, or the last listed duration if not available. - - `AttachEffect.CumulativeRefreshAll` if set to true makes it so that trying to attach `Cumulative=true` effect to a target that already has `Cumulative.MaxCount` amount of effects will refresh duration of all attached effects of the same type instead of only the one with shortest remaining duration. If `AttachEffect.CumulativeRefreshAll.OnAttach` is also set to true, this refresh applies even if the target does not have maximum allowed amount of effects of same type. - - `AttachEffect.CumulativeRefreshSameSourceOnly` controls whether or not trying to apply `Cumulative=true` effect on target requires any existing effects of same type to come from same Warhead by same firer for them to be eligible for duration refresh. + - `AttachEffect.CumulativeRefreshAll` if set to true makes it so that trying to attach `Cumulative=true` effect to a target that already has `Cumulative.MaxCount` amount of effects will refresh duration of all attached effects of the same type instead of only the one with shortest remaining duration. If `AttachEffect.CumulativeRefreshAll.OnAttach` is also set to true, this refresh applies even if the target does not have maximum allowed amount of effects of same type. These toggles don't work on effects with `Cumulative.SimpleStack=true`, which always refresh during a reapplication. + - `AttachEffect.CumulativeRefreshSameSourceOnly` controls whether or not trying to apply `Cumulative=true` effect on target requires any existing effects of same type to come from same Warhead by same firer for them to be eligible for duration refresh. Doesn't work on effects with `Cumulative.SimpleStack=true`. - Attached Effects can be removed from objects by Warheads using `AttachEffect.RemoveTypes` or `AttachEffect.RemoveGroups`. - `AttachEffect.CumulativeRemoveMinCounts` sets minimum number of active instaces per `RemoveTypes`/`RemoveGroups` required for `Cumulative=true` types to be removed. - `AttachEffect.CumulativeRemoveMaxCounts` sets maximum number of active instaces per `RemoveTypes`/`RemoveGroups` for `Cumulative=true` that are removed at once by this Warhead. @@ -94,6 +95,7 @@ Duration=0 ; integer - game frames or ne Duration.ApplyFirepowerMult=false ; boolean Duration.ApplyArmorMultOnTarget=false ; boolean Cumulative=false ; boolean +Cumulative.SimpleStack=false ; boolean Cumulative.MaxCount=-1 ; integer Powered=false ; boolean DiscardOn=none ; List of discard condition enumeration (none|entry|move|stationary|drain|inrange|outofrange) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index bb54d15d5a..5caedb00e5 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -1805,6 +1805,19 @@ void TechnoExt::ExtData::UpdateAttachEffects() std::vector>::iterator it; std::vector> expireWeapons; + auto handleExpireWeapon = [&](WeaponTypeClass* pWeapon, TechnoClass* pTarget, TechnoClass* pInvoker, bool invokerOwner) + { + if (invokerOwner) + { + if (pInvoker) + expireWeapons.emplace_back(pWeapon, pInvoker); + } + else + { + expireWeapons.emplace_back(pWeapon, pTarget); + } + }; + for (it = this->AttachedEffects.begin(); it != this->AttachedEffects.end(); ) { auto const attachEffect = it->get(); @@ -1831,22 +1844,34 @@ void TechnoExt::ExtData::UpdateAttachEffects() if (pType->HasTint()) markForRedraw = true; - if (pType->Cumulative && pType->CumulativeAnimations.size() > 0) + if (pType->Cumulative && !pType->Cumulative_SimpleStack && pType->CumulativeAnimations.size() > 0) this->UpdateCumulativeAttachEffects(attachEffect->GetType(), attachEffect); - if (pType->ExpireWeapon && ((hasExpired && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Expire) != ExpireWeaponCondition::None) + auto const pWeapon = pType->ExpireWeapon; + + if (pWeapon && ((hasExpired && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Expire) != ExpireWeaponCondition::None) || (shouldDiscard && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Discard) != ExpireWeaponCondition::None))) { - if (!pType->Cumulative || !pType->ExpireWeapon_CumulativeOnlyOnce || this->GetAttachedEffectCumulativeCount(pType) < 1) + const bool simpleStack = pType->Cumulative && pType->Cumulative_SimpleStack; + const bool invokerOwner = pType->ExpireWeapon_UseInvokerAsOwner; + auto const pInvoker = attachEffect->GetInvoker(); + + if (!simpleStack || !pType->ExpireWeapon_CumulativeOnlyOnce || this->GetAttachedEffectCumulativeCount(pType) < 1) { - if (pType->ExpireWeapon_UseInvokerAsOwner) + handleExpireWeapon(pWeapon, pThis, pInvoker, invokerOwner); + } + else if (simpleStack) + { + if (pType->ExpireWeapon_CumulativeOnlyOnce) { - if (auto const pInvoker = attachEffect->GetInvoker()) - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pInvoker)); + handleExpireWeapon(pWeapon, pThis, pInvoker, invokerOwner); } else { - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pThis)); + for (int i = 0; i < attachEffect->SimpleStackCount; i++) + { + handleExpireWeapon(pWeapon, pThis, pInvoker, invokerOwner); + } } } } @@ -1891,6 +1916,19 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() std::vector> expireWeapons; bool altered = false; + auto handleExpireWeapon = [&](WeaponTypeClass* pWeapon, TechnoClass* pTarget, TechnoClass* pInvoker, bool invokerOwner) + { + if (invokerOwner) + { + if (pInvoker) + expireWeapons.emplace_back(pWeapon, pInvoker); + } + else + { + expireWeapons.emplace_back(pWeapon, pTarget); + } + }; + // Delete ones on old type and not on current. for (it = this->AttachedEffects.begin(); it != this->AttachedEffects.end(); ) { @@ -1902,18 +1940,30 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() if (remove) { - if (pType->ExpireWeapon && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Expire) != ExpireWeaponCondition::None) + auto const pWeapon = pType->ExpireWeapon; + + if (pWeapon && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Expire) != ExpireWeaponCondition::None) { - if (!pType->Cumulative || !pType->ExpireWeapon_CumulativeOnlyOnce || this->GetAttachedEffectCumulativeCount(pType) < 1) + const bool simpleStack = pType->Cumulative && pType->Cumulative_SimpleStack; + const bool invokerOwner = pType->ExpireWeapon_UseInvokerAsOwner; + auto const pInvoker = attachEffect->GetInvoker(); + + if (!simpleStack || !pType->ExpireWeapon_CumulativeOnlyOnce || this->GetAttachedEffectCumulativeCount(pType) < 1) { - if (pType->ExpireWeapon_UseInvokerAsOwner) + handleExpireWeapon(pWeapon, pThis, pInvoker, invokerOwner); + } + else if (simpleStack) + { + if (pType->ExpireWeapon_CumulativeOnlyOnce) { - if (auto const pInvoker = attachEffect->GetInvoker()) - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pInvoker)); + handleExpireWeapon(pWeapon, pThis, pInvoker, invokerOwner); } else { - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pThis)); + for (int i = 0; i < attachEffect->SimpleStackCount; i++) + { + handleExpireWeapon(pWeapon, pThis, pInvoker, invokerOwner); + } } } } @@ -2012,15 +2062,16 @@ void TechnoExt::ExtData::RecalculateStatMultipliers() continue; auto const type = attachEffect->GetType(); - firepower *= type->FirepowerMultiplier; - speed *= type->SpeedMultiplier; + const int simpleStackCount = type->Cumulative_SimpleStack; + firepower *= std::pow(type->FirepowerMultiplier, simpleStackCount); + speed *= std::pow(type->SpeedMultiplier, simpleStackCount); if (type->ArmorMultiplier != 1.0 && (type->ArmorMultiplier_AllowWarheads.size() > 0 || type->ArmorMultiplier_DisallowWarheads.size() > 0)) hasRestrictedArmorMultipliers = true; else - armor *= type->ArmorMultiplier; + armor *= std::pow(type->ArmorMultiplier, simpleStackCount); - ROF *= type->ROFMultiplier; + ROF *= std::pow(type->ROFMultiplier, simpleStackCount); cloak |= type->Cloakable; forceDecloak |= type->ForceDecloak; disableWeapons |= type->DisableWeapons; diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 121ece2f9a..aa90704ad2 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -498,6 +498,9 @@ bool TechnoExt::ExtData::HasAttachedEffects(std::vector for (auto const& type : attachEffectTypes) { + const bool cumulative = type->Cumulative; + int cumulativeCount = -1; + for (auto const& attachEffect : this->AttachedEffects) { if (attachEffect->GetType() == type && attachEffect->IsActive()) @@ -505,22 +508,42 @@ bool TechnoExt::ExtData::HasAttachedEffects(std::vector if (checkSource && attachEffect->IsFromSource(pInvoker, pSource)) continue; - const unsigned int minSize = minCounts ? minCounts->size() : 0; - const unsigned int maxSize = maxCounts ? maxCounts->size() : 0; - - if (type->Cumulative && (minSize > 0 || maxSize > 0)) + if (cumulative) { - const int cumulativeCount = this->GetAttachedEffectCumulativeCount(type, ignoreSameSource, pInvoker, pSource); + const unsigned int minSize = minCounts ? minCounts->size() : 0; + const unsigned int maxSize = maxCounts ? maxCounts->size() : 0; - if (minSize > 0) - { - if (cumulativeCount < minCounts->at(typeCounter - 1 >= minSize ? minSize - 1 : typeCounter - 1)) - continue; - } - if (maxSize > 0) + if (minSize > 0 || maxSize > 0) { - if (cumulativeCount > maxCounts->at(typeCounter - 1 >= maxSize ? maxSize - 1 : typeCounter - 1)) - continue; + if (cumulativeCount == -1) + { + if (type->Cumulative_SimpleStack) + cumulativeCount = attachEffect->SimpleStackCount; + else + cumulativeCount = this->GetAttachedEffectCumulativeCount(type, ignoreSameSource, pInvoker, pSource); + } + + if (minSize > 0) + { + if (cumulativeCount < minCounts->at(typeCounter - 1 >= minSize ? minSize - 1 : typeCounter - 1)) + { + if (type->Cumulative_SimpleStack) + break; + + continue; + } + } + + if (maxSize > 0) + { + if (cumulativeCount > maxCounts->at(typeCounter - 1 >= maxSize ? maxSize - 1 : typeCounter - 1)) + { + if (type->Cumulative_SimpleStack) + break; + + continue; + } + } } } diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index 2cfbc5f6a5..93eea96d26 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -671,8 +671,8 @@ double WarheadTypeExt::ExtData::GetCritChance(TechnoClass* pFirer) const if (disallowWarheads.size() > 0 && disallowWarheads.Contains(pObject)) continue; - critChance = critChance * Math::max(pType->Crit_Multiplier, 0); - extraChance += pType->Crit_ExtraChance; + critChance = critChance * Math::max(std::pow(pType->Crit_Multiplier, attachEffect->SimpleStackCount), 0); + extraChance += pType->Crit_ExtraChance * attachEffect->SimpleStackCount; } return critChance + extraChance; diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index e3cd9c1d5c..7933b8ff05 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -346,8 +346,8 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF if (type->WeaponRange_DisallowWeapons.size() > 0 && type->WeaponRange_DisallowWeapons.Contains(pThis)) continue; - range = static_cast(range * Math::max(type->WeaponRange_Multiplier, 0.0)); - extraRange += type->WeaponRange_ExtraRange; + range = static_cast(range * Math::max(std::pow(type->WeaponRange_Multiplier, attachEffect->SimpleStackCount), 0.0)); + extraRange += type->WeaponRange_ExtraRange * attachEffect->SimpleStackCount; } range += static_cast(extraRange * Unsorted::LeptonsPerCell); diff --git a/src/New/Entity/AttachEffectClass.cpp b/src/New/Entity/AttachEffectClass.cpp index bcb452cb20..dbc31a8f98 100644 --- a/src/New/Entity/AttachEffectClass.cpp +++ b/src/New/Entity/AttachEffectClass.cpp @@ -23,6 +23,7 @@ AttachEffectClass::AttachEffectClass() , NeedsRecalculateStat { false } , LastDiscardCheckFrame { -1 } , LastDiscardCheckValue { false } + , SimpleStackCount { 1 } { this->HasInitialized = false; AttachEffectClass::Array.emplace_back(this); @@ -48,6 +49,7 @@ AttachEffectClass::AttachEffectClass(AttachEffectTypeClass* pType, TechnoClass* , NeedsRecalculateStat { false } , LastDiscardCheckFrame { -1 } , LastDiscardCheckValue { false } + , SimpleStackCount { 1 } { this->HasInitialized = false; @@ -391,7 +393,7 @@ void AttachEffectClass::CreateAnim() if (!this->HasCumulativeAnim) return; - const int count = TechnoExt::ExtMap.Find(pTechno)->GetAttachedEffectCumulativeCount(pType); + const int count = pType->Cumulative_SimpleStack ? this->SimpleStackCount : TechnoExt::ExtMap.Find(pTechno)->GetAttachedEffectCumulativeCount(pType); pAnimType = pType->GetCumulativeAnimation(count); } else @@ -442,7 +444,7 @@ void AttachEffectClass::UpdateCumulativeAnim() return; const auto pType = this->Type; - const int count = TechnoExt::ExtMap.Find(this->Techno)->GetAttachedEffectCumulativeCount(pType); + const int count = pType->Cumulative_SimpleStack ? this->SimpleStackCount : TechnoExt::ExtMap.Find(this->Techno)->GetAttachedEffectCumulativeCount(pType); if (count < 1) { @@ -639,7 +641,12 @@ int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, T markForRedraw = true; if (pType->Cumulative && pType->CumulativeAnimations.size() > 0) - pTargetExt->UpdateCumulativeAttachEffects(pType); + { + if (!pType->Cumulative_SimpleStack) + pTargetExt->UpdateCumulativeAttachEffects(pType); + else + pAE->UpdateCumulativeAnim(); + } } } } @@ -694,7 +701,8 @@ AttachEffectClass* AttachEffectClass::CreateAndAttach(AttachEffectTypeClass* pTy return nullptr; int currentTypeCount = 0; - const bool cumulative = pType->Cumulative && checkCumulative; + const bool simpleStack = pType->Cumulative && pType->Cumulative_SimpleStack; + const bool cumulative = pType->Cumulative && !pType->Cumulative_SimpleStack && checkCumulative; AttachEffectClass* match = nullptr; std::vector cumulativeMatches; cumulativeMatches.reserve(targetAEs.size()); @@ -709,8 +717,17 @@ AttachEffectClass* AttachEffectClass::CreateAndAttach(AttachEffectTypeClass* pTy if (!cumulative) { - match = attachEffect; - break; + if (simpleStack && (pType->Cumulative_MaxCount < 0 || attachEffect->SimpleStackCount < pType->Cumulative_MaxCount)) + { + attachEffect->SimpleStackCount++; + + if (pType->CumulativeAnimations.size() > 0) + attachEffect->HasCumulativeAnim = true; + } + + attachEffect->RefreshDuration(attachParams.DurationOverride); + AttachEffectTypeClass::HandleEvent(pTarget); + return nullptr; } else if (!attachParams.CumulativeRefreshSameSourceOnly || (attachEffect->Source == pSource && attachEffect->Invoker == pInvoker)) { @@ -722,7 +739,7 @@ AttachEffectClass* AttachEffectClass::CreateAndAttach(AttachEffectTypeClass* pTy } } - if (cumulativeMatches.size() > 0) + if (cumulative) { if (pType->Cumulative_MaxCount >= 0 && currentTypeCount >= pType->Cumulative_MaxCount) { @@ -733,7 +750,7 @@ AttachEffectClass* AttachEffectClass::CreateAndAttach(AttachEffectTypeClass* pTy ae->RefreshDuration(attachParams.DurationOverride); } } - else + else if (match) { match->RefreshDuration(attachParams.DurationOverride); } @@ -750,23 +767,13 @@ AttachEffectClass* AttachEffectClass::CreateAndAttach(AttachEffectTypeClass* pTy } } - if (!cumulative && match) - { - match->RefreshDuration(attachParams.DurationOverride); - AttachEffectTypeClass::HandleEvent(pTarget); - } - else - { - targetAEs.emplace_back(std::make_unique(pType, pTarget, pInvokerHouse, pInvoker, pSource, attachParams.DurationOverride, attachParams.Delay, attachParams.InitialDelay, attachParams.RecreationDelay)); - auto const pAE = targetAEs.back().get(); + targetAEs.emplace_back(std::make_unique(pType, pTarget, pInvokerHouse, pInvoker, pSource, attachParams.DurationOverride, attachParams.Delay, attachParams.InitialDelay, attachParams.RecreationDelay)); + auto const pAE = targetAEs.back().get(); - if (!currentTypeCount && cumulative && pType->CumulativeAnimations.size() > 0) - pAE->HasCumulativeAnim = true; - - return pAE; - } + if (!currentTypeCount && cumulative && pType->CumulativeAnimations.size() > 0) + pAE->HasCumulativeAnim = true; - return nullptr; + return pAE; } /// @@ -865,19 +872,39 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass return 0; auto const pTargetExt = TechnoExt::ExtMap.Find(pTarget); - int detachedCount = 0; + const bool cumulative = pType->Cumulative; + const bool simpleStack = cumulative && pType->Cumulative_SimpleStack; + const bool complexStack = cumulative && !pType->Cumulative_SimpleStack; int stackCount = -1; - if (pType->Cumulative) + if (complexStack) + { stackCount = pTargetExt->GetAttachedEffectCumulativeCount(pType); - if (minCount > 0 && stackCount > -1 && pType->Cumulative && minCount > stackCount) - return 0; + if (minCount > 0 && stackCount > -1 && minCount > stackCount) + return 0; + } + int detachedCount = 0; auto const targetAEs = &pTargetExt->AttachedEffects; + auto const pWeapon = pType->ExpireWeapon; + const bool invokerOwner = pType->ExpireWeapon_UseInvokerAsOwner; std::vector>::iterator it; std::vector> expireWeapons; + auto handleExpireWeapon = [&](TechnoClass* pInvoker) + { + if (invokerOwner) + { + if (pInvoker) + expireWeapons.emplace_back(pWeapon, pInvoker); + } + else + { + expireWeapons.emplace_back(pWeapon, pTarget); + } + }; + for (it = targetAEs->begin(); it != targetAEs->end(); ) { if (maxCount > 0 && detachedCount >= maxCount) @@ -889,24 +916,48 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass { detachedCount++; - if (pType->ExpireWeapon && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Remove) != ExpireWeaponCondition::None) + if (simpleStack) { + detachedCount = attachEffect->SimpleStackCount; + + if (minCount > 0 && minCount > detachedCount) + return 0; + + if (maxCount > 0 && detachedCount > maxCount) + { + detachedCount = maxCount; + attachEffect->SimpleStackCount -= maxCount; + stackCount = attachEffect->SimpleStackCount; + } + } + + if (pWeapon && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Remove) != ExpireWeaponCondition::None) + { + auto const pInvoker = attachEffect->Invoker; + // can't be GetAttachedEffectCumulativeCount(pType) < 2, or inactive AE might make it stack more than once - if (!pType->Cumulative || !pType->ExpireWeapon_CumulativeOnlyOnce || stackCount == 1) + if (!simpleStack || !pType->ExpireWeapon_CumulativeOnlyOnce || stackCount == 1) + { + handleExpireWeapon(pInvoker); + } + else if (simpleStack) // handle ExpireWeapon for Cumulative.SimpleStack { - if (pType->ExpireWeapon_UseInvokerAsOwner) + if (pType->ExpireWeapon_CumulativeOnlyOnce) { - if (auto const pInvoker = attachEffect->Invoker) - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pInvoker)); + if (stackCount <= 0) + handleExpireWeapon(pInvoker); } else { - expireWeapons.push_back(std::make_pair(pType->ExpireWeapon, pTarget)); + for (int i = 0; i < detachedCount; i++) + { + handleExpireWeapon(pInvoker); + } } } } - if (pType->Cumulative && pType->CumulativeAnimations.size() > 0) + if (complexStack && pType->CumulativeAnimations.size() > 0) pTargetExt->UpdateCumulativeAttachEffects(pType, attachEffect); if (attachEffect->ResetIfRecreatable()) @@ -917,7 +968,7 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass it = targetAEs->erase(it); - if (!pType->Cumulative) + if (!cumulative) break; stackCount--; @@ -1049,6 +1100,7 @@ bool AttachEffectClass::Serialize(T& Stm) .Process(this->LastActiveStat) .Process(this->LaserTrail) .Process(this->NeedsRecalculateStat) + .Process(this->SimpleStackCount) .Success(); } diff --git a/src/New/Entity/AttachEffectClass.h b/src/New/Entity/AttachEffectClass.h index c596d6d709..021d2c5e6d 100644 --- a/src/New/Entity/AttachEffectClass.h +++ b/src/New/Entity/AttachEffectClass.h @@ -101,6 +101,7 @@ class AttachEffectClass bool HasCumulativeAnim; bool ShouldBeDiscarded; bool NeedsRecalculateStat; + int SimpleStackCount; }; // Container for TechnoClass-specific AttachEffect fields. diff --git a/src/New/Type/AttachEffectTypeClass.cpp b/src/New/Type/AttachEffectTypeClass.cpp index c4a4d0c208..5e6a75ab28 100644 --- a/src/New/Type/AttachEffectTypeClass.cpp +++ b/src/New/Type/AttachEffectTypeClass.cpp @@ -52,16 +52,6 @@ std::vector AttachEffectTypeClass::GetTypesFromGroups(co return std::vector(types.begin(), types.end()); } -AnimTypeClass* AttachEffectTypeClass::GetCumulativeAnimation(int cumulativeCount) const -{ - if (cumulativeCount < 0 || this->CumulativeAnimations.size() < 1) - return nullptr; - - int index = static_cast(cumulativeCount) >= this->CumulativeAnimations.size() ? this->CumulativeAnimations.size() - 1 : cumulativeCount - 1; - - return this->CumulativeAnimations.at(index); -} - void AttachEffectTypeClass::HandleEvent(TechnoClass* pTarget) { if (const auto pTag = pTarget->AttachedTag) @@ -105,6 +95,7 @@ void AttachEffectTypeClass::LoadFromINI(CCINIClass* pINI) this->Duration_ApplyFirepowerMult.Read(exINI, pSection, "Duration.ApplyFirepowerMult"); this->Duration_ApplyArmorMultOnTarget.Read(exINI, pSection, "Duration.ApplyArmorMultOnTarget"); this->Cumulative.Read(exINI, pSection, "Cumulative"); + this->Cumulative_SimpleStack.Read(exINI, pSection, "SimpleStack"); this->Cumulative_MaxCount.Read(exINI, pSection, "Cumulative.MaxCount"); this->Powered.Read(exINI, pSection, "Powered"); this->DiscardOn.Read(exINI, pSection, "DiscardOn"); @@ -184,6 +175,7 @@ void AttachEffectTypeClass::Serialize(T& Stm) .Process(this->Duration_ApplyFirepowerMult) .Process(this->Duration_ApplyArmorMultOnTarget) .Process(this->Cumulative) + .Process(this->Cumulative_SimpleStack) .Process(this->Cumulative_MaxCount) .Process(this->Powered) .Process(this->DiscardOn) diff --git a/src/New/Type/AttachEffectTypeClass.h b/src/New/Type/AttachEffectTypeClass.h index 95a7fa216d..ab8848b531 100644 --- a/src/New/Type/AttachEffectTypeClass.h +++ b/src/New/Type/AttachEffectTypeClass.h @@ -46,6 +46,7 @@ class AttachEffectTypeClass final : public Enumerable Valueable Duration_ApplyFirepowerMult; Valueable Duration_ApplyArmorMultOnTarget; Valueable Cumulative; + Valueable Cumulative_SimpleStack; Valueable Cumulative_MaxCount; Valueable Powered; Valueable DiscardOn; @@ -109,6 +110,7 @@ class AttachEffectTypeClass final : public Enumerable , Duration_ApplyFirepowerMult { false } , Duration_ApplyArmorMultOnTarget { false } , Cumulative { false } + , Cumulative_SimpleStack { false } , Cumulative_MaxCount { -1 } , Powered { false } , DiscardOn { DiscardCondition::None } @@ -174,7 +176,16 @@ class AttachEffectTypeClass final : public Enumerable bool HasGroup(const std::string& groupID) const; bool HasGroups(const std::vector& groupIDs, bool requireAll) const; - AnimTypeClass* GetCumulativeAnimation(int cumulativeCount) const; + + AnimTypeClass* GetCumulativeAnimation(int cumulativeCount) const + { + if (cumulativeCount < 0 || this->CumulativeAnimations.size() < 1) + return nullptr; + + const int index = static_cast(cumulativeCount) >= this->CumulativeAnimations.size() ? this->CumulativeAnimations.size() - 1 : cumulativeCount - 1; + + return this->CumulativeAnimations.at(index); + } void LoadFromINI(CCINIClass* pINI); void LoadFromStream(PhobosStreamReader& Stm);