Skip to content

Commit 76a8f44

Browse files
committed
Fixes to AE CumulativeAnims logic and minor AE optimizations
- 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
1 parent afedd4a commit 76a8f44

File tree

10 files changed

+157
-46
lines changed

10 files changed

+157
-46
lines changed

docs/New-or-Enhanced-Logics.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ This page describes all the engine features that are either new and introduced b
2525
- `Animation.TemporalAction` determines what happens to the animation when the attached object is under effect of `Temporal=true` Warhead.
2626
- `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.
2727
- `Animation.HideIfAttachedWith` contains list of other AttachEffectTypes that if attached to same techno as the current one, will hide this effect's animation.
28-
- `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.
28+
- `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.
29+
- `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.
2930
- Attached effect can fire off a weapon when expired / removed / object dies by setting `ExpireWeapon`.
3031
- `ExpireWeapon.TriggerOn` determines the exact conditions upon which the weapon is fired, defaults to `expire` which means only if the effect naturally expires.
3132
- `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.
@@ -90,6 +91,7 @@ Animation.TemporalAction=None ; AttachedAnimFlag (None, Hides,
9091
Animation.UseInvokerAsOwner=false ; boolean
9192
Animation.HideIfAttachedWith= ; List of AttachEffectTypes
9293
CumulativeAnimations= ; list of animations
94+
CumulativeAnimations.RestartOnChange=true ; boolean
9395
ExpireWeapon= ; WeaponType
9496
ExpireWeapon.TriggerOn=expire ; List of expire weapon trigger condition enumeration (none|expire|remove|death|all)
9597
ExpireWeapon.CumulativeOnlyOnce=false ; boolean

src/Ext/Anim/Body.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "Body.h"
22

3+
#include <GameOptionsClass.h>
4+
35
#include <Ext/AnimType/Body.h>
46
#include <Ext/House/Body.h>
57
#include <Ext/WarheadType/Body.h>
@@ -130,6 +132,54 @@ void AnimExt::VeinAttackAI(AnimClass* pAnim)
130132
}
131133
}
132134

135+
// Changes type of anim in similar fashion to Next.
136+
void AnimExt::ChangeAnimType(AnimClass* pAnim, AnimTypeClass* pNewType, bool resetLoops, bool restart)
137+
{
138+
double percentThrough = pAnim->Animation.Value / static_cast<double>(pAnim->Type->End);
139+
140+
if (pNewType->End == -1)
141+
{
142+
pNewType->End = pNewType->GetImage()->Frames;
143+
144+
if (pNewType->Shadow)
145+
pNewType->End /= 2;
146+
}
147+
148+
if (pNewType->LoopEnd == -1)
149+
{
150+
pNewType->LoopEnd = pNewType->End;
151+
}
152+
153+
pAnim->Type = pNewType;
154+
155+
if (resetLoops)
156+
pAnim->RemainingIterations = static_cast<byte>(pNewType->LoopCount);
157+
158+
pAnim->Accum = 0;
159+
pAnim->UnableToContinue = false;
160+
pAnim->Reverse = pNewType->Reverse;
161+
162+
int rate = pNewType->Rate;
163+
164+
if (pNewType->RandomRate.Min || pNewType->RandomRate.Max)
165+
rate = ScenarioClass::Instance->Random.RandomRanged(pNewType->RandomRate.Min, pNewType->RandomRate.Max);
166+
167+
if (pNewType->Normalized)
168+
rate = GameOptionsClass::Instance->GetAnimSpeed(rate);
169+
170+
pAnim->Animation.Start(rate, pNewType->Reverse ? -1 : 1);
171+
172+
if (restart)
173+
{
174+
pAnim->Animation.Value = pNewType->Reverse ? pNewType->End : pNewType->Start;
175+
pAnim->Start();
176+
}
177+
else
178+
{
179+
pAnim->Animation.Value = static_cast<int>(pNewType->End * percentThrough);
180+
}
181+
}
182+
133183
void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWakeAnim, Iterator<AnimTypeClass*> splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage,
134184
CellClass* pCell, CoordStruct nLocation, bool heightFlag, bool isMeteor, bool warheadDetonate, bool explodeOnWater, bool splashAnimsPickRandom)
135185
{

src/Ext/Anim/Body.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,8 @@ class AnimExt
7272

7373
static bool SetAnimOwnerHouseKind(AnimClass* pAnim, HouseClass* pInvoker, HouseClass* pVictim, bool defaultToVictimOwner = true, bool defaultToInvokerOwner = false);
7474
static HouseClass* GetOwnerHouse(AnimClass* pAnim, HouseClass* pDefaultOwner = nullptr);
75-
7675
static void VeinAttackAI(AnimClass* pAnim);
77-
76+
static void ChangeAnimType(AnimClass* pAnim, AnimTypeClass* pNewType, bool resetLoops, bool restart);
7877
static void HandleDebrisImpact(AnimTypeClass* pExpireAnim, AnimTypeClass* pWakeAnim, Iterator<AnimTypeClass*> splashAnims, HouseClass* pOwner, WarheadTypeClass* pWarhead, int nDamage,
7978
CellClass* pCell, CoordStruct nLocation, bool heightFlag, bool isMeteor, bool warheadDetonate, bool explodeOnWater, bool splashAnimsPickRandom);
8079

src/Ext/Techno/Body.Update.cpp

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,8 @@ void TechnoExt::ExtData::UpdateAttachEffects()
836836
if (pType->HasTint())
837837
markForRedraw = true;
838838

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

841842
if (pType->ExpireWeapon && (pType->ExpireWeapon_TriggerOn & ExpireWeaponCondition::Expire) != ExpireWeaponCondition::None)
842843
{
@@ -949,31 +950,45 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects()
949950
pThis->MarkForRedraw();
950951
}
951952

952-
// Updates state of AttachEffects of same cumulative type on techno, (which one is first active instance existing, if any), kills animations if needed.
953-
void TechnoExt::ExtData::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType)
953+
// Updates CumulativeAnimations AE's on techno.
954+
void TechnoExt::ExtData::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved)
954955
{
955-
if (!pAttachEffectType || !pAttachEffectType->Cumulative)
956-
return;
957-
958-
bool foundFirst = false;
956+
AttachEffectClass* pAELargestDuration = nullptr;
957+
AttachEffectClass* pAEWithAnim = nullptr;
958+
int duration = 0;
959959

960960
for (auto const& attachEffect : this->AttachedEffects)
961961
{
962-
if (attachEffect->GetType() != pAttachEffectType || !attachEffect->IsActive())
962+
if (attachEffect->GetType() != pAttachEffectType)
963963
continue;
964964

965-
if (!foundFirst)
965+
if (attachEffect->HasCumulativeAnim)
966966
{
967-
foundFirst = true;
968-
attachEffect->IsFirstCumulativeInstance = true;
967+
pAEWithAnim = attachEffect.get();
969968
}
970-
else
969+
else if (attachEffect->CanShowAnim())
971970
{
972-
attachEffect->IsFirstCumulativeInstance = false;
971+
int currentDuration = attachEffect->GetRemainingDuration();
972+
973+
if (currentDuration < 0 || currentDuration > duration)
974+
{
975+
pAELargestDuration = attachEffect.get();
976+
duration = currentDuration;
977+
}
973978
}
979+
}
980+
981+
if (pAEWithAnim)
982+
{
983+
pAEWithAnim->UpdateCumulativeAnim();
974984

975-
if (pAttachEffectType->CumulativeAnimations.size() > 0)
976-
attachEffect->KillAnim();
985+
if (pRemoved == pAEWithAnim)
986+
{
987+
pAEWithAnim->HasCumulativeAnim = false;
988+
989+
if (pAELargestDuration)
990+
pAELargestDuration->TransferCumulativeAnim(pAEWithAnim);
991+
}
977992
}
978993
}
979994

src/Ext/Techno/Body.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class TechnoExt
100100
void UpdateTypeData(TechnoTypeClass* currentType);
101101
void UpdateLaserTrails();
102102
void UpdateAttachEffects();
103-
void UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType);
103+
void UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved = nullptr);
104104
void RecalculateStatMultipliers();
105105
void UpdateTemporal();
106106
void UpdateMindControlAnim();

src/New/Entity/AttachEffectClass.cpp

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ AttachEffectClass::AttachEffectClass()
1616
, Duration { 0 }
1717
, CurrentDelay { 0 }
1818
, NeedsDurationRefresh { false }
19-
, IsFirstCumulativeInstance { false }
19+
, HasCumulativeAnim { false }
2020
{
2121
this->HasInitialized = false;
2222
AttachEffectClass::Array.emplace_back(this);
@@ -35,7 +35,7 @@ AttachEffectClass::AttachEffectClass(AttachEffectTypeClass* pType, TechnoClass*
3535
, IsOnline { true }
3636
, IsCloaked { false }
3737
, NeedsDurationRefresh { false }
38-
, IsFirstCumulativeInstance { false }
38+
, HasCumulativeAnim { false }
3939
{
4040
this->HasInitialized = false;
4141

@@ -154,7 +154,7 @@ void AttachEffectClass::AI()
154154
this->CloakCheck();
155155
this->OnlineCheck();
156156

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

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

169169
this->CloakCheck();
170170

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

174174
if (this->Animation)
@@ -212,7 +212,7 @@ void AttachEffectClass::AnimCheck()
212212
{
213213
this->IsAnimHidden = false;
214214

215-
if (!this->Animation && (!this->IsUnderTemporal || this->Type->Animation_TemporalAction != AttachedAnimFlag::Hides))
215+
if (!this->Animation && this->CanShowAnim())
216216
this->CreateAnim();
217217
}
218218
}
@@ -290,7 +290,7 @@ void AttachEffectClass::CreateAnim()
290290

291291
if (this->Type->Cumulative && this->Type->CumulativeAnimations.size() > 0)
292292
{
293-
if (!this->IsFirstCumulativeInstance)
293+
if (!this->HasCumulativeAnim)
294294
return;
295295

296296
int count = TechnoExt::ExtMap.Find(this->Techno)->GetAttachedEffectCumulativeCount(this->Type);
@@ -328,6 +328,44 @@ void AttachEffectClass::KillAnim()
328328
}
329329
}
330330

331+
void AttachEffectClass::UpdateCumulativeAnim()
332+
{
333+
if (!this->HasCumulativeAnim || !this->Animation)
334+
return;
335+
336+
int count = TechnoExt::ExtMap.Find(this->Techno)->GetAttachedEffectCumulativeCount(this->Type);
337+
338+
if (count < 1)
339+
{
340+
this->KillAnim();
341+
return;
342+
}
343+
344+
auto const pAnimType = this->Type->GetCumulativeAnimation(count);
345+
346+
if (this->Animation->Type != pAnimType)
347+
AnimExt::ChangeAnimType(this->Animation, pAnimType, false, this->Type->CumulativeAnimations_RestartOnChange);
348+
}
349+
350+
void AttachEffectClass::TransferCumulativeAnim(AttachEffectClass* pSource)
351+
{
352+
if (!pSource || !pSource->Animation)
353+
return;
354+
355+
this->KillAnim();
356+
this->Animation = pSource->Animation;
357+
this->HasCumulativeAnim = true;
358+
pSource->Animation = nullptr;
359+
pSource->HasCumulativeAnim = false;
360+
}
361+
362+
bool AttachEffectClass::CanShowAnim() const
363+
{
364+
return (!this->IsUnderTemporal || this->Type->Animation_TemporalAction != AttachedAnimFlag::Hides)
365+
&& (this->IsOnline || this->Type->Animation_OfflineAction != AttachedAnimFlag::Hides)
366+
&& !this->IsCloaked && !this->IsInTunnel && !this->IsAnimHidden;
367+
}
368+
331369
void AttachEffectClass::SetAnimationTunnelState(bool visible)
332370
{
333371
if (!this->IsInTunnel && !visible)
@@ -346,7 +384,9 @@ void AttachEffectClass::RefreshDuration(int durationOverride)
346384
if (this->Type->Animation_ResetOnReapply)
347385
{
348386
this->KillAnim();
349-
this->CreateAnim();
387+
388+
if (this->CanShowAnim())
389+
this->CreateAnim();
350390
}
351391
}
352392

@@ -362,11 +402,6 @@ bool AttachEffectClass::ResetIfRecreatable()
362402
return true;
363403
}
364404

365-
bool AttachEffectClass::IsSelfOwned() const
366-
{
367-
return this->Source == this->Techno;
368-
}
369-
370405
bool AttachEffectClass::HasExpired() const
371406
{
372407
return this->IsSelfOwned() && this->Delay >= 0 ? false : !this->Duration;
@@ -435,11 +470,6 @@ bool AttachEffectClass::IsFromSource(TechnoClass* pInvoker, AbstractClass* pSour
435470
return pInvoker == this->Invoker && pSource == this->Source;
436471
}
437472

438-
AttachEffectTypeClass* AttachEffectClass::GetType() const
439-
{
440-
return this->Type;
441-
}
442-
443473
#pragma region StaticFunctions_AttachDetachTransfer
444474

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

535-
if (pType->Cumulative)
565+
if (pType->Cumulative && pType->CumulativeAnimations.size() > 0)
536566
pTargetExt->UpdateCumulativeAttachEffects(pType);
537567
}
538568
}
@@ -612,7 +642,12 @@ AttachEffectClass* AttachEffectClass::CreateAndAttach(AttachEffectTypeClass* pTy
612642
else
613643
{
614644
targetAEs.push_back(std::make_unique<AttachEffectClass>(pType, pTarget, pInvokerHouse, pInvoker, pSource, durationOverride, delay, initialDelay, recreationDelay));
615-
return targetAEs.back().get();
645+
auto const pAE = targetAEs.back().get();
646+
647+
if (!currentTypeCount && pType->Cumulative && pType->CumulativeAnimations.size() > 0)
648+
pAE->HasCumulativeAnim = true;
649+
650+
return pAE;
616651
}
617652

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

676-
if (count && pType->Cumulative)
677-
pTargetExt->UpdateCumulativeAttachEffects(pType);
678-
679711
detachedCount += count;
680712
index++;
681713
}
@@ -760,6 +792,10 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass
760792
expireWeapons.push_back(pType->ExpireWeapon);
761793
}
762794

795+
796+
if (pType->Cumulative && pType->CumulativeAnimations.size() > 0)
797+
pTargetExt->UpdateCumulativeAttachEffects(pType, attachEffect);
798+
763799
if (attachEffect->ResetIfRecreatable())
764800
{
765801
++it;
@@ -872,6 +908,7 @@ bool AttachEffectClass::Serialize(T& Stm)
872908
.Process(this->IsCloaked)
873909
.Process(this->HasInitialized)
874910
.Process(this->NeedsDurationRefresh)
911+
.Process(this->HasCumulativeAnim)
875912
.Success();
876913
}
877914

src/New/Entity/AttachEffectClass.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@ class AttachEffectClass
1717
void AI();
1818
void AI_Temporal();
1919
void KillAnim();
20+
void CreateAnim();
21+
void UpdateCumulativeAnim();
22+
void TransferCumulativeAnim(AttachEffectClass* pSource);
23+
bool CanShowAnim() const;
2024
void SetAnimationTunnelState(bool visible);
21-
AttachEffectTypeClass* GetType() const;
25+
AttachEffectTypeClass* GetType() const { return this->Type; }
26+
int GetRemainingDuration() const { return this->Duration; }
2227
void RefreshDuration(int durationOverride = 0);
2328
bool ResetIfRecreatable();
24-
bool IsSelfOwned() const;
29+
bool IsSelfOwned() const { return this->Source == this->Techno; }
2530
bool HasExpired() const;
2631
bool AllowedToBeActive() const;
2732
bool IsActive() const;
@@ -62,7 +67,6 @@ class AttachEffectClass
6267
void OnlineCheck();
6368
void CloakCheck();
6469
void AnimCheck();
65-
void CreateAnim();
6670

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

9599
public:
96-
bool IsFirstCumulativeInstance;
100+
bool HasCumulativeAnim;
97101
};
98102

99103
// Container for TechnoClass-specific AttachEffect fields.

0 commit comments

Comments
 (0)