Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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)
Expand Down
85 changes: 68 additions & 17 deletions src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1805,6 +1805,19 @@ void TechnoExt::ExtData::UpdateAttachEffects()
std::vector<std::unique_ptr<AttachEffectClass>>::iterator it;
std::vector<std::pair<WeaponTypeClass*, TechnoClass*>> 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();
Expand All @@ -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);
}
}
}
}
Expand Down Expand Up @@ -1891,6 +1916,19 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects()
std::vector<std::pair<WeaponTypeClass*, TechnoClass*>> 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(); )
{
Expand All @@ -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);
}
}
}
}
Expand Down Expand Up @@ -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;
Expand Down
49 changes: 36 additions & 13 deletions src/Ext/Techno/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,29 +498,52 @@ bool TechnoExt::ExtData::HasAttachedEffects(std::vector<AttachEffectTypeClass*>

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())
{
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;
}
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/Ext/WarheadType/Detonate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/Ext/WeaponType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(range * Math::max(type->WeaponRange_Multiplier, 0.0));
extraRange += type->WeaponRange_ExtraRange;
range = static_cast<int>(range * Math::max(std::pow(type->WeaponRange_Multiplier, attachEffect->SimpleStackCount), 0.0));
extraRange += type->WeaponRange_ExtraRange * attachEffect->SimpleStackCount;
}

range += static_cast<int>(extraRange * Unsorted::LeptonsPerCell);
Expand Down
Loading