Skip to content

Commit

Permalink
Fixes to AE CumulativeAnims logic and minor AE optimizations
Browse files Browse the repository at this point in the history
- Rework CumulativeAnimations to work in more robust manner, add option to not restart playback on animation type change
- Fix a potential edge case with Animation.ResetOnReapply not checking if the AE is eligible to display animation
- Move animation display eligibility checks to a separate function
- Inline some small functions
  • Loading branch information
Starkku committed Sep 23, 2024
1 parent afedd4a commit 76a8f44
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 46 deletions.
2 changes: 1 addition & 1 deletion YRpp
4 changes: 3 additions & 1 deletion docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ This page describes all the engine features that are either new and introduced b
- `Animation.TemporalAction` determines what happens to the animation when the attached object is under effect of `Temporal=true` Warhead.
- `Animation.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 animation's owner & invoker instead of the object the effect is attached to.
- `Animation.HideIfAttachedWith` contains list of other AttachEffectTypes that if attached to same techno as the current one, will hide this effect's animation.
- `CumulativeAnimations` can be used to declare a list of animations used for `Cumulative=true` types instead of `Animation`. An animation is picked from the list in order matching the number of active instances of the type on the object, with last listed animation used if number is higher than the number of listed animations. This animation is only displayed once, on the first active instance of the effect found attached and is updated and restarted if the number of active instances changed.
- `CumulativeAnimations` can be used to declare a list of animations used for `Cumulative=true` types instead of `Animation`. An animation is picked from the list in order matching the number of active instances of the type on the object, with last listed animation used if number is higher than the number of listed animations. This animation is only displayed once and is transferred from the effect to another of same type (specifically one with longest remaining duration), if such exists, upon expiration or removal. Note that because `Cumulative.MaxCount` limits the number of effects of same type that can be applied this can cause animations to 'flicker' here as effects expire before new ones can be applied in some circumstances.
- `CumulativeAnimations.RestartOnChange` determines if the animation playback is restarted when the type of animation changes, if not then playback resumes at frame at same position relative to the animation's length.
- Attached effect can fire off a weapon when expired / removed / object dies by setting `ExpireWeapon`.
- `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.
Expand Down Expand Up @@ -90,6 +91,7 @@ Animation.TemporalAction=None ; AttachedAnimFlag (None, Hides,
Animation.UseInvokerAsOwner=false ; boolean
Animation.HideIfAttachedWith= ; List of AttachEffectTypes
CumulativeAnimations= ; list of animations
CumulativeAnimations.RestartOnChange=true ; boolean
ExpireWeapon= ; WeaponType
ExpireWeapon.TriggerOn=expire ; List of expire weapon trigger condition enumeration (none|expire|remove|death|all)
ExpireWeapon.CumulativeOnlyOnce=false ; boolean
Expand Down
50 changes: 50 additions & 0 deletions src/Ext/Anim/Body.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "Body.h"

#include <GameOptionsClass.h>

#include <Ext/AnimType/Body.h>
#include <Ext/House/Body.h>
#include <Ext/WarheadType/Body.h>
Expand Down Expand Up @@ -130,6 +132,54 @@ void AnimExt::VeinAttackAI(AnimClass* pAnim)
}
}

// Changes type of anim in similar fashion to Next.
void AnimExt::ChangeAnimType(AnimClass* pAnim, AnimTypeClass* pNewType, bool resetLoops, bool restart)
{
double percentThrough = pAnim->Animation.Value / static_cast<double>(pAnim->Type->End);

if (pNewType->End == -1)
{
pNewType->End = pNewType->GetImage()->Frames;

if (pNewType->Shadow)
pNewType->End /= 2;
}

if (pNewType->LoopEnd == -1)
{
pNewType->LoopEnd = pNewType->End;
}

pAnim->Type = pNewType;

if (resetLoops)
pAnim->RemainingIterations = static_cast<byte>(pNewType->LoopCount);

pAnim->Accum = 0;
pAnim->UnableToContinue = false;
pAnim->Reverse = pNewType->Reverse;

int rate = pNewType->Rate;

if (pNewType->RandomRate.Min || pNewType->RandomRate.Max)
rate = ScenarioClass::Instance->Random.RandomRanged(pNewType->RandomRate.Min, pNewType->RandomRate.Max);

if (pNewType->Normalized)
rate = GameOptionsClass::Instance->GetAnimSpeed(rate);

pAnim->Animation.Start(rate, pNewType->Reverse ? -1 : 1);

if (restart)
{
pAnim->Animation.Value = pNewType->Reverse ? pNewType->End : pNewType->Start;
pAnim->Start();
}
else
{
pAnim->Animation.Value = static_cast<int>(pNewType->End * percentThrough);
}
}

void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWakeAnim, Iterator<AnimTypeClass*> splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage,
CellClass* pCell, CoordStruct nLocation, bool heightFlag, bool isMeteor, bool warheadDetonate, bool explodeOnWater, bool splashAnimsPickRandom)
{
Expand Down
3 changes: 1 addition & 2 deletions src/Ext/Anim/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ class AnimExt

static bool SetAnimOwnerHouseKind(AnimClass* pAnim, HouseClass* pInvoker, HouseClass* pVictim, bool defaultToVictimOwner = true, bool defaultToInvokerOwner = false);
static HouseClass* GetOwnerHouse(AnimClass* pAnim, HouseClass* pDefaultOwner = nullptr);

static void VeinAttackAI(AnimClass* pAnim);

static void ChangeAnimType(AnimClass* pAnim, AnimTypeClass* pNewType, bool resetLoops, bool restart);
static void HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWakeAnim, Iterator<AnimTypeClass*> splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage,
CellClass* pCell, CoordStruct nLocation, bool heightFlag, bool isMeteor, bool warheadDetonate, bool explodeOnWater, bool splashAnimsPickRandom);

Expand Down
45 changes: 30 additions & 15 deletions src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,8 @@ void TechnoExt::ExtData::UpdateAttachEffects()
if (pType->HasTint())
markForRedraw = true;

this->UpdateCumulativeAttachEffects(attachEffect->GetType());
if (pType->Cumulative && pType->CumulativeAnimations.size() > 0)
this->UpdateCumulativeAttachEffects(attachEffect->GetType(), attachEffect);

if (pType->ExpireWeapon && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Expire) != ExpireWeaponCondition::None)
{
Expand Down Expand Up @@ -949,31 +950,45 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects()
pThis->MarkForRedraw();
}

// Updates state of AttachEffects of same cumulative type on techno, (which one is first active instance existing, if any), kills animations if needed.
void TechnoExt::ExtData::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType)
// Updates CumulativeAnimations AE's on techno.
void TechnoExt::ExtData::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved)
{
if (!pAttachEffectType || !pAttachEffectType->Cumulative)
return;

bool foundFirst = false;
AttachEffectClass* pAELargestDuration = nullptr;
AttachEffectClass* pAEWithAnim = nullptr;
int duration = 0;

for (auto const& attachEffect : this->AttachedEffects)
{
if (attachEffect->GetType() != pAttachEffectType || !attachEffect->IsActive())
if (attachEffect->GetType() != pAttachEffectType)
continue;

if (!foundFirst)
if (attachEffect->HasCumulativeAnim)
{
foundFirst = true;
attachEffect->IsFirstCumulativeInstance = true;
pAEWithAnim = attachEffect.get();
}
else
else if (attachEffect->CanShowAnim())
{
attachEffect->IsFirstCumulativeInstance = false;
int currentDuration = attachEffect->GetRemainingDuration();

if (currentDuration < 0 || currentDuration > duration)
{
pAELargestDuration = attachEffect.get();
duration = currentDuration;
}
}
}

if (pAEWithAnim)
{
pAEWithAnim->UpdateCumulativeAnim();

if (pAttachEffectType->CumulativeAnimations.size() > 0)
attachEffect->KillAnim();
if (pRemoved == pAEWithAnim)
{
pAEWithAnim->HasCumulativeAnim = false;

if (pAELargestDuration)
pAELargestDuration->TransferCumulativeAnim(pAEWithAnim);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class TechnoExt
void UpdateTypeData(TechnoTypeClass* currentType);
void UpdateLaserTrails();
void UpdateAttachEffects();
void UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType);
void UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved = nullptr);
void RecalculateStatMultipliers();
void UpdateTemporal();
void UpdateMindControlAnim();
Expand Down
81 changes: 59 additions & 22 deletions src/New/Entity/AttachEffectClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ AttachEffectClass::AttachEffectClass()
, Duration { 0 }
, CurrentDelay { 0 }
, NeedsDurationRefresh { false }
, IsFirstCumulativeInstance { false }
, HasCumulativeAnim { false }
{
this->HasInitialized = false;
AttachEffectClass::Array.emplace_back(this);
Expand All @@ -35,7 +35,7 @@ AttachEffectClass::AttachEffectClass(AttachEffectTypeClass* pType, TechnoClass*
, IsOnline { true }
, IsCloaked { false }
, NeedsDurationRefresh { false }
, IsFirstCumulativeInstance { false }
, HasCumulativeAnim { false }
{
this->HasInitialized = false;

Expand Down Expand Up @@ -154,7 +154,7 @@ void AttachEffectClass::AI()
this->CloakCheck();
this->OnlineCheck();

if (!this->Animation && !this->IsUnderTemporal && this->IsOnline && !this->IsCloaked && !this->IsInTunnel && !this->IsAnimHidden)
if (!this->Animation && this->CanShowAnim())
this->CreateAnim();

this->AnimCheck();
Expand All @@ -168,7 +168,7 @@ void AttachEffectClass::AI_Temporal()

this->CloakCheck();

if (!this->Animation && this->Type->Animation_TemporalAction != AttachedAnimFlag::Hides && this->IsOnline && !this->IsCloaked && !this->IsInTunnel && !this->IsAnimHidden)
if (!this->Animation && this->CanShowAnim())
this->CreateAnim();

if (this->Animation)
Expand Down Expand Up @@ -212,7 +212,7 @@ void AttachEffectClass::AnimCheck()
{
this->IsAnimHidden = false;

if (!this->Animation && (!this->IsUnderTemporal || this->Type->Animation_TemporalAction != AttachedAnimFlag::Hides))
if (!this->Animation && this->CanShowAnim())
this->CreateAnim();
}
}
Expand Down Expand Up @@ -290,7 +290,7 @@ void AttachEffectClass::CreateAnim()

if (this->Type->Cumulative && this->Type->CumulativeAnimations.size() > 0)
{
if (!this->IsFirstCumulativeInstance)
if (!this->HasCumulativeAnim)
return;

int count = TechnoExt::ExtMap.Find(this->Techno)->GetAttachedEffectCumulativeCount(this->Type);
Expand Down Expand Up @@ -328,6 +328,44 @@ void AttachEffectClass::KillAnim()
}
}

void AttachEffectClass::UpdateCumulativeAnim()
{
if (!this->HasCumulativeAnim || !this->Animation)
return;

int count = TechnoExt::ExtMap.Find(this->Techno)->GetAttachedEffectCumulativeCount(this->Type);

if (count < 1)
{
this->KillAnim();
return;
}

auto const pAnimType = this->Type->GetCumulativeAnimation(count);

if (this->Animation->Type != pAnimType)
AnimExt::ChangeAnimType(this->Animation, pAnimType, false, this->Type->CumulativeAnimations_RestartOnChange);
}

void AttachEffectClass::TransferCumulativeAnim(AttachEffectClass* pSource)
{
if (!pSource || !pSource->Animation)
return;

this->KillAnim();
this->Animation = pSource->Animation;
this->HasCumulativeAnim = true;
pSource->Animation = nullptr;
pSource->HasCumulativeAnim = false;
}

bool AttachEffectClass::CanShowAnim() const
{
return (!this->IsUnderTemporal || this->Type->Animation_TemporalAction != AttachedAnimFlag::Hides)
&& (this->IsOnline || this->Type->Animation_OfflineAction != AttachedAnimFlag::Hides)
&& !this->IsCloaked && !this->IsInTunnel && !this->IsAnimHidden;
}

void AttachEffectClass::SetAnimationTunnelState(bool visible)
{
if (!this->IsInTunnel && !visible)
Expand All @@ -346,7 +384,9 @@ void AttachEffectClass::RefreshDuration(int durationOverride)
if (this->Type->Animation_ResetOnReapply)
{
this->KillAnim();
this->CreateAnim();

if (this->CanShowAnim())
this->CreateAnim();
}
}

Expand All @@ -362,11 +402,6 @@ bool AttachEffectClass::ResetIfRecreatable()
return true;
}

bool AttachEffectClass::IsSelfOwned() const
{
return this->Source == this->Techno;
}

bool AttachEffectClass::HasExpired() const
{
return this->IsSelfOwned() && this->Delay >= 0 ? false : !this->Duration;
Expand Down Expand Up @@ -435,11 +470,6 @@ bool AttachEffectClass::IsFromSource(TechnoClass* pInvoker, AbstractClass* pSour
return pInvoker == this->Invoker && pSource == this->Source;
}

AttachEffectTypeClass* AttachEffectClass::GetType() const
{
return this->Type;
}

#pragma region StaticFunctions_AttachDetachTransfer

/// <summary>
Expand Down Expand Up @@ -532,7 +562,7 @@ int AttachEffectClass::Attach(std::vector<AttachEffectTypeClass*> const& types,
if (pType->HasTint())
markForRedraw = true;

if (pType->Cumulative)
if (pType->Cumulative && pType->CumulativeAnimations.size() > 0)
pTargetExt->UpdateCumulativeAttachEffects(pType);
}
}
Expand Down Expand Up @@ -612,7 +642,12 @@ AttachEffectClass* AttachEffectClass::CreateAndAttach(AttachEffectTypeClass* pTy
else
{
targetAEs.push_back(std::make_unique<AttachEffectClass>(pType, pTarget, pInvokerHouse, pInvoker, pSource, durationOverride, delay, initialDelay, recreationDelay));
return targetAEs.back().get();
auto const pAE = targetAEs.back().get();

if (!currentTypeCount && pType->Cumulative && pType->CumulativeAnimations.size() > 0)
pAE->HasCumulativeAnim = true;

return pAE;
}

return nullptr;
Expand Down Expand Up @@ -673,9 +708,6 @@ int AttachEffectClass::Detach(std::vector<AttachEffectTypeClass*> const& types,
if (count && pType->HasTint())
markForRedraw = true;

if (count && pType->Cumulative)
pTargetExt->UpdateCumulativeAttachEffects(pType);

detachedCount += count;
index++;
}
Expand Down Expand Up @@ -760,6 +792,10 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass
expireWeapons.push_back(pType->ExpireWeapon);
}


if (pType->Cumulative && pType->CumulativeAnimations.size() > 0)
pTargetExt->UpdateCumulativeAttachEffects(pType, attachEffect);

if (attachEffect->ResetIfRecreatable())
{
++it;
Expand Down Expand Up @@ -872,6 +908,7 @@ bool AttachEffectClass::Serialize(T& Stm)
.Process(this->IsCloaked)
.Process(this->HasInitialized)
.Process(this->NeedsDurationRefresh)
.Process(this->HasCumulativeAnim)
.Success();
}

Expand Down
12 changes: 8 additions & 4 deletions src/New/Entity/AttachEffectClass.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ class AttachEffectClass
void AI();
void AI_Temporal();
void KillAnim();
void CreateAnim();
void UpdateCumulativeAnim();
void TransferCumulativeAnim(AttachEffectClass* pSource);
bool CanShowAnim() const;
void SetAnimationTunnelState(bool visible);
AttachEffectTypeClass* GetType() const;
AttachEffectTypeClass* GetType() const { return this->Type; }
int GetRemainingDuration() const { return this->Duration; }
void RefreshDuration(int durationOverride = 0);
bool ResetIfRecreatable();
bool IsSelfOwned() const;
bool IsSelfOwned() const { return this->Source == this->Techno; }
bool HasExpired() const;
bool AllowedToBeActive() const;
bool IsActive() const;
Expand Down Expand Up @@ -62,7 +67,6 @@ class AttachEffectClass
void OnlineCheck();
void CloakCheck();
void AnimCheck();
void CreateAnim();

static AttachEffectClass* CreateAndAttach(AttachEffectTypeClass* pType, TechnoClass* pTarget, std::vector<std::unique_ptr<AttachEffectClass>>& targetAEs,
HouseClass* pInvokerHouse, TechnoClass* pInvoker, AbstractClass* pSource, int durationOverride = 0, int delay = 0, int initialDelay = 0, int recreationDelay = -1);
Expand Down Expand Up @@ -93,7 +97,7 @@ class AttachEffectClass
bool NeedsDurationRefresh;

public:
bool IsFirstCumulativeInstance;
bool HasCumulativeAnim;
};

// Container for TechnoClass-specific AttachEffect fields.
Expand Down
Loading

0 comments on commit 76a8f44

Please sign in to comment.