Skip to content

Commit 38dfa4c

Browse files
authored
Have the client independently spawn the resource exhaustion animation to avoid having frames between when the animation begins playing and when the resource's graphic changes (AscensionGameDev#2639)
* Have the client independently spawn the resource exhaustion animation to avoid having frames between when the animation begins playing and when the resource's graphic changes AscensionGameDev#2572 * make the TODO to be an option to show rather than hide the exhausted graphic
1 parent f7d2ec7 commit 38dfa4c

File tree

8 files changed

+166
-52
lines changed

8 files changed

+166
-52
lines changed

Intersect.Client.Core/Entities/Animation.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ namespace Intersect.Client.Entities;
1414

1515
public partial class Animation : IAnimation
1616
{
17+
public event Action<IAnimation>? Disposed;
18+
public event Action<IAnimation>? Finished;
19+
1720
public bool AutoRotate { get; set; }
1821

19-
private bool disposed = false;
22+
private bool _disposed = false;
2023

2124
public bool Hidden { get; set; }
2225

@@ -243,6 +246,8 @@ public void EndDraw()
243246
{
244247
Dispose();
245248
}
249+
250+
Finished?.Invoke(this);
246251
}
247252

248253
static Point RotatePoint(Point pointToRotate, Point centerPoint, double angleInDegrees)
@@ -275,10 +280,7 @@ public void Show()
275280

276281
public void Dispose()
277282
{
278-
if (disposed)
279-
{
280-
return;
281-
}
283+
ObjectDisposedException.ThrowIf(_disposed, this);
282284

283285
lock (Graphics.AnimationLock)
284286
{
@@ -294,17 +296,19 @@ public void Dispose()
294296
}
295297

296298
_ = Graphics.LiveAnimations.Remove(this);
297-
disposed = true;
299+
_disposed = true;
298300
GC.SuppressFinalize(this);
299301
}
302+
303+
Disposed?.Invoke(this);
300304
}
301305

302306
public void DisposeNextDraw()
303307
{
304308
mDisposeNextDraw = true;
305309
}
306310

307-
public bool IsDisposed => disposed;
311+
public bool IsDisposed => _disposed;
308312

309313
public void SetPosition(float worldX, float worldY, int mapx, int mapy, Guid mapId, Direction dir, int z = 0)
310314
{
@@ -322,7 +326,7 @@ public void SetPosition(float worldX, float worldY, int mapx, int mapy, Guid map
322326

323327
public void Update()
324328
{
325-
if (disposed)
329+
if (_disposed)
326330
{
327331
return;
328332
}

Intersect.Client.Core/Entities/Resource.cs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
using Intersect.Client.Framework.GenericClasses;
55
using Intersect.Client.Framework.Maps;
66
using Intersect.Client.General;
7+
using Intersect.Core;
78
using Intersect.Enums;
89
using Intersect.Framework.Core.GameObjects.Resources;
910
using Intersect.GameObjects;
1011
using Intersect.Network.Packets.Server;
12+
using Microsoft.Extensions.Logging;
1113

1214
namespace Intersect.Client.Entities;
1315

@@ -20,7 +22,9 @@ public partial class Resource : Entity, IResource
2022
private bool _waitingForTilesets;
2123

2224
private bool _isDead;
25+
private Guid _descriptorId;
2326
private ResourceDescriptor? _descriptor;
27+
private IAnimation? _activeAnimation;
2428

2529
public Resource(Guid id, ResourceEntityPacket packet) : base(id, packet, EntityType.Resource)
2630
{
@@ -108,14 +112,67 @@ public override void Load(EntityPacket? packet)
108112
return;
109113
}
110114

115+
var wasDead = IsDead;
111116
IsDead = resourceEntityPacket.IsDead;
112117

113-
if (!ResourceDescriptor.TryGet(resourceEntityPacket.ResourceId, out _descriptor))
118+
var descriptorId = resourceEntityPacket.ResourceId;
119+
_descriptorId = descriptorId;
120+
121+
var justDied = !wasDead && resourceEntityPacket.IsDead;
122+
if (!ResourceDescriptor.TryGet(descriptorId, out var descriptor))
114123
{
124+
if (justDied)
125+
{
126+
ApplicationContext.CurrentContext.Logger.LogError(
127+
"Unable to play resource exhaustion animation because resource {EntityId} ({EntityName}) is missing the descriptor ({DescriptorId})",
128+
Id,
129+
Name,
130+
descriptorId
131+
);
132+
}
133+
115134
return;
116135
}
117136

137+
_descriptor = descriptor;
118138
UpdateFromDescriptor(_descriptor);
139+
140+
if (!justDied)
141+
{
142+
return;
143+
}
144+
145+
if (MapInstance is { } mapInstance)
146+
{
147+
var animation = mapInstance.AddTileAnimation(descriptor.AnimationId, X, Y, Direction.Up);
148+
if (animation is { IsDisposed: false })
149+
{
150+
animation.Finished += OnAnimationDisposedOrFinished;
151+
animation.Disposed += OnAnimationDisposedOrFinished;
152+
}
153+
_activeAnimation = animation;
154+
}
155+
else
156+
{
157+
ApplicationContext.CurrentContext.Logger.LogError(
158+
"Unable to play resource exhaustion animation because resource {EntityId} ({EntityName}) has no reference to the map instance for map {MapId}",
159+
Id,
160+
Name,
161+
MapId
162+
);
163+
}
164+
}
165+
166+
private void OnAnimationDisposedOrFinished(IAnimation animation)
167+
{
168+
if (_activeAnimation != animation)
169+
{
170+
return;
171+
}
172+
173+
_activeAnimation = null;
174+
animation.Disposed -= OnAnimationDisposedOrFinished;
175+
animation.Finished -= OnAnimationDisposedOrFinished;
119176
}
120177

121178
private void UpdateFromDescriptor(ResourceDescriptor? descriptor)
@@ -353,9 +410,17 @@ public override void Draw()
353410
return;
354411
}
355412

356-
if (Texture != null)
413+
if (Texture == null)
357414
{
358-
Graphics.DrawGameTexture(Texture, _renderBoundsSrc, _renderBoundsDest, Intersect.Color.White);
415+
return;
359416
}
417+
418+
// TODO: Add an option to show the exhausted sprite until the exhaustion animation is finished, but this is not necessary if the graphics line up like Blinkuz' sample provided to fix #2572
419+
if (_activeAnimation != null)
420+
{
421+
return;
422+
}
423+
424+
Graphics.DrawGameTexture(Texture, _renderBoundsSrc, _renderBoundsDest, Intersect.Color.White);
360425
}
361426
}

Intersect.Client.Core/General/Globals.cs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics.CodeAnalysis;
12
using Intersect.Client.Entities;
23
using Intersect.Client.Entities.Events;
34
using Intersect.Client.Framework.Database;
@@ -9,10 +10,12 @@
910
using Intersect.Client.Items;
1011
using Intersect.Client.Maps;
1112
using Intersect.Client.Plugins.Interfaces;
13+
using Intersect.Core;
1214
using Intersect.Enums;
1315
using Intersect.Framework.Core.GameObjects.Crafting;
1416
using Intersect.GameObjects;
1517
using Intersect.Network.Packets.Server;
18+
using Microsoft.Extensions.Logging;
1619

1720
namespace Intersect.Client.General;
1821

@@ -221,23 +224,37 @@ internal static void OnGameDraw(DrawStates state, IEntity entity, TimeSpan delta
221224
);
222225
}
223226

224-
public static Entity GetEntity(Guid id, EntityType type)
227+
public static bool TryGetEntity(EntityType entityType, Guid entityId, [NotNullWhen(true)] out Entity? entity)
225228
{
226-
if (Entities.ContainsKey(id))
229+
if (!Entities.TryGetValue(entityId, out entity))
227230
{
228-
var entity = Entities[id];
229-
230-
if (!entity.IsDisposed && entity.Type == type)
231-
{
232-
EntitiesToDispose.Remove(entity.Id);
231+
return false;
232+
}
233233

234-
return entity;
235-
}
234+
if (entity.IsDisposed)
235+
{
236+
Entities.Remove(entityId);
237+
entity = null;
238+
return false;
239+
}
236240

241+
if (entity.Type != entityType)
242+
{
243+
ApplicationContext.CurrentContext.Logger.LogError(
244+
"Found instance of {ActualEntityType} registered to {EntityId} but it was expected to be {ExpectedEntityType}",
245+
entity.Type,
246+
entityId,
247+
entityType
248+
);
249+
Entities.Remove(entityId);
237250
entity.Dispose();
238-
Entities.Remove(id);
251+
entity = null;
252+
return false;
239253
}
240254

241-
return default;
255+
EntitiesToDispose.Remove(entityId);
256+
return true;
242257
}
258+
259+
public static Entity? GetEntity(Guid id, EntityType type) => TryGetEntity(type, id, out var entity) ? entity : null;
243260
}

Intersect.Client.Core/Maps/MapInstance.cs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -725,38 +725,60 @@ private void ClearAttributeSounds()
725725
}
726726

727727
//Animations
728-
public void AddTileAnimation(
729-
Guid animId,
728+
public IAnimation? AddTileAnimation(
729+
Guid animationDescriptorId,
730730
int tileX,
731731
int tileY,
732732
Direction dir = Direction.None,
733733
IEntity? owner = null,
734734
AnimationSource source = default
735735
)
736736
{
737-
var animBase = AnimationDescriptor.Get(animId);
738-
if (animBase == null)
737+
if (!AnimationDescriptor.TryGet(animationDescriptorId, out var animationDescriptor))
739738
{
740-
return;
739+
return null;
741740
}
742741

743-
var anim = new MapAnimation(
744-
animBase,
742+
return AddTileAnimation(
743+
animationDescriptor,
744+
tileX,
745+
tileY,
746+
dir,
747+
owner,
748+
source
749+
);
750+
}
751+
752+
public IAnimation? AddTileAnimation(
753+
AnimationDescriptor animationDescriptor,
754+
int tileX,
755+
int tileY,
756+
Direction dir = Direction.None,
757+
IEntity? owner = null,
758+
AnimationSource source = default
759+
)
760+
{
761+
var animationInstance = new MapAnimation(
762+
animationDescriptor,
745763
tileX,
746764
tileY,
747765
dir,
748766
owner as Entity,
749767
source: source
750768
);
751-
LocalAnimations.TryAdd(anim.Id, anim);
752-
anim.SetPosition(
769+
770+
LocalAnimations.TryAdd(animationInstance.Id, animationInstance);
771+
772+
animationInstance.SetPosition(
753773
X + tileX * _tileWidth + _tileHalfWidth,
754774
Y + tileY * _tileHeight + _tileHalfHeight,
755775
tileX,
756776
tileY,
757777
Id,
758778
dir
759779
);
780+
781+
return animationInstance;
760782
}
761783

762784
private void HideActiveAnimations()

Intersect.Client.Core/Networking/PacketHandler.cs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,15 +389,22 @@ public void HandlePacket(IPacketSender packetSender, NpcEntityPacket packet)
389389
//ResourceEntityPacket
390390
public void HandlePacket(IPacketSender packetSender, ResourceEntityPacket packet)
391391
{
392-
var en = Globals.GetEntity(packet.EntityId, EntityType.Resource);
393-
if (en != null)
392+
if (Globals.TryGetEntity(EntityType.Resource, packet.EntityId, out var entity))
394393
{
395-
en.Load(packet);
394+
entity.Load(packet);
396395
}
397396
else
398397
{
399-
var entity = new Resource(packet.EntityId, packet);
400-
Globals.Entities.Add(entity.Id, entity);
398+
entity = new Resource(packet.EntityId, packet);
399+
if (!Globals.Entities.TryAdd(entity.Id, entity))
400+
{
401+
ApplicationContext.CurrentContext.Logger.LogError(
402+
"Failed to register new {EntityType} {EntityId} ({EntityName})",
403+
EntityType.Resource,
404+
packet.EntityId,
405+
packet.Name
406+
);
407+
}
401408
}
402409
}
403410

Intersect.Client.Framework/Entities/IAnimation.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
using Intersect.GameObjects;
21
using Intersect.Enums;
32
using Intersect.Framework.Core.GameObjects.Animations;
43

54
namespace Intersect.Client.Framework.Entities;
65

76
public interface IAnimation : IDisposable
87
{
8+
event Action<IAnimation>? Disposed;
9+
event Action<IAnimation>? Finished;
10+
11+
bool IsDisposed { get; }
912
bool AutoRotate { get; set; }
1013
bool Hidden { get; set; }
1114
bool InfiniteLoop { get; set; }
@@ -16,4 +19,4 @@ public interface IAnimation : IDisposable
1619
void SetDir(Direction dir);
1720
void SetPosition(float worldX, float worldY, int mapx, int mapy, Guid mapId, Direction dir, int z = 0);
1821
void Show();
19-
}
22+
}

Intersect.Client.Framework/Maps/IMapInstance.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public interface IMapInstance
2828
bool IsDisposed { get; }
2929
bool IsLoaded { get; }
3030

31-
void AddTileAnimation(
31+
IAnimation? AddTileAnimation(
3232
Guid animId,
3333
int tileX,
3434
int tileY,
@@ -37,6 +37,15 @@ void AddTileAnimation(
3737
AnimationSource source = default
3838
);
3939

40+
IAnimation? AddTileAnimation(
41+
AnimationDescriptor animationDescriptor,
42+
int tileX,
43+
int tileY,
44+
Direction dir = Direction.None,
45+
IEntity? owner = null,
46+
AnimationSource source = default
47+
);
48+
4049
void CompareEffects(IMapInstance oldMap);
4150
bool InView();
4251
void Load(string json);

0 commit comments

Comments
 (0)