com.wallstop-studios.unity-helpers 2.0.4 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Docs/DATA_STRUCTURES.md +7 -7
- package/Docs/EFFECTS_SYSTEM.md +836 -8
- package/Docs/EFFECTS_SYSTEM_TUTORIAL.md +77 -18
- package/Docs/HULLS.md +2 -2
- package/Docs/RANDOM_PERFORMANCE.md +1 -1
- package/Docs/REFLECTION_HELPERS.md +1 -1
- package/Docs/RELATIONAL_COMPONENTS.md +51 -6
- package/Docs/SERIALIZATION.md +1 -1
- package/Docs/SINGLETONS.md +2 -2
- package/Docs/SPATIAL_TREES_2D_GUIDE.md +3 -3
- package/Docs/SPATIAL_TREES_3D_GUIDE.md +3 -3
- package/Docs/SPATIAL_TREE_SEMANTICS.md +7 -7
- package/Editor/CustomDrawers/WShowIfPropertyDrawer.cs +131 -41
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +175 -18
- package/README.md +17 -3
- package/Runtime/Tags/Attribute.cs +144 -24
- package/Runtime/Tags/AttributeEffect.cs +119 -16
- package/Runtime/Tags/AttributeModification.cs +59 -29
- package/Runtime/Tags/AttributesComponent.cs +20 -0
- package/Runtime/Tags/EffectBehavior.cs +171 -0
- package/Runtime/Tags/EffectBehavior.cs.meta +4 -0
- package/Runtime/Tags/EffectHandle.cs +5 -0
- package/Runtime/Tags/EffectHandler.cs +385 -39
- package/Runtime/Tags/EffectStackKey.cs +79 -0
- package/Runtime/Tags/EffectStackKey.cs.meta +4 -0
- package/Runtime/Tags/PeriodicEffectDefinition.cs +102 -0
- package/Runtime/Tags/PeriodicEffectDefinition.cs.meta +4 -0
- package/Runtime/Tags/PeriodicEffectRuntimeState.cs +40 -0
- package/Runtime/Tags/PeriodicEffectRuntimeState.cs.meta +4 -0
- package/Samples~/DI - Zenject/README.md +0 -2
- package/Tests/Editor/Attributes/WShowIfPropertyDrawerTests.cs +285 -0
- package/Tests/Editor/Attributes/WShowIfPropertyDrawerTests.cs.meta +11 -0
- package/Tests/Editor/Core/Attributes/RelationalComponentAssignerTests.cs +2 -2
- package/Tests/Editor/Utils/ScriptableObjectSingletonTests.cs +41 -0
- package/Tests/Runtime/Serialization/JsonSerializationTest.cs +4 -3
- package/Tests/Runtime/Tags/AttributeEffectTests.cs +135 -0
- package/Tests/Runtime/Tags/AttributeEffectTests.cs.meta +3 -0
- package/Tests/Runtime/Tags/AttributeModificationTests.cs +137 -0
- package/Tests/Runtime/Tags/AttributeTests.cs +192 -0
- package/Tests/Runtime/Tags/AttributeTests.cs.meta +3 -0
- package/Tests/Runtime/Tags/EffectBehaviorTests.cs +184 -0
- package/Tests/Runtime/Tags/EffectBehaviorTests.cs.meta +3 -0
- package/Tests/Runtime/Tags/EffectHandlerTests.cs +618 -0
- package/Tests/Runtime/Tags/Helpers/RecordingEffectBehavior.cs +89 -0
- package/Tests/Runtime/Tags/Helpers/RecordingEffectBehavior.cs.meta +4 -0
- package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +92 -0
- package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs.meta +3 -0
- package/package.json +1 -1
- package/scripts/lint-doc-links.ps1 +156 -11
- package/Tests/Runtime/Tags/AttributeDataTests.cs +0 -312
- package/node_modules.meta +0 -8
- /package/Tests/Runtime/Tags/{AttributeDataTests.cs.meta → AttributeModificationTests.cs.meta} +0 -0
|
@@ -67,6 +67,9 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
67
67
|
List<CosmeticEffectData>
|
|
68
68
|
> _instancedCosmeticEffects = new();
|
|
69
69
|
|
|
70
|
+
private readonly Dictionary<EffectStackKey, List<EffectHandle>> _handlesByStackKey = new();
|
|
71
|
+
private readonly Dictionary<long, EffectStackKey> _stackKeyByHandleId = new();
|
|
72
|
+
|
|
70
73
|
// Stores expiration time of duration effects (We store by Id because it's much cheaper to iterate Guids than it is EffectHandles
|
|
71
74
|
private readonly Dictionary<long, float> _effectExpirations = new();
|
|
72
75
|
private readonly Dictionary<long, EffectHandle> _effectHandlesById = new();
|
|
@@ -74,6 +77,9 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
74
77
|
// Used only to save allocations in Update()
|
|
75
78
|
private readonly List<long> _expiredEffectIds = new();
|
|
76
79
|
private readonly List<EffectHandle> _appliedEffects = new();
|
|
80
|
+
private readonly Dictionary<long, List<PeriodicEffectRuntimeState>> _periodicEffectStates =
|
|
81
|
+
new();
|
|
82
|
+
private readonly Dictionary<long, List<EffectBehavior>> _behaviorsByHandleId = new();
|
|
77
83
|
|
|
78
84
|
private bool _initialized;
|
|
79
85
|
|
|
@@ -118,51 +124,109 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
118
124
|
/// </remarks>
|
|
119
125
|
public EffectHandle? ApplyEffect(AttributeEffect effect)
|
|
120
126
|
{
|
|
121
|
-
|
|
127
|
+
if (effect == null)
|
|
128
|
+
{
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
122
131
|
|
|
123
|
-
if (effect.durationType
|
|
132
|
+
if (effect.durationType == ModifierDurationType.Instant)
|
|
124
133
|
{
|
|
125
|
-
if (effect
|
|
134
|
+
if (RequiresHandle(effect))
|
|
126
135
|
{
|
|
127
|
-
|
|
136
|
+
this.LogWarn(
|
|
137
|
+
$"Effect {effect:json} defines periodic or behaviour data but is Instant. These features require a Duration or Infinite effect."
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
InternalApplyEffect(effect);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
EffectStackKey stackKey = effect.GetStackKey();
|
|
146
|
+
List<EffectHandle> existingHandles = TryGetStackHandles(stackKey);
|
|
147
|
+
|
|
148
|
+
switch (effect.stackingMode)
|
|
149
|
+
{
|
|
150
|
+
case EffectStackingMode.Ignore:
|
|
151
|
+
{
|
|
152
|
+
if (existingHandles is { Count: > 0 })
|
|
128
153
|
{
|
|
129
|
-
|
|
154
|
+
return existingHandles[0];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case EffectStackingMode.Refresh:
|
|
160
|
+
{
|
|
161
|
+
if (existingHandles is { Count: > 0 })
|
|
162
|
+
{
|
|
163
|
+
EffectHandle handle = existingHandles[0];
|
|
164
|
+
InternalApplyEffect(handle);
|
|
165
|
+
return handle;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case EffectStackingMode.Replace:
|
|
171
|
+
{
|
|
172
|
+
if (existingHandles is { Count: > 0 })
|
|
173
|
+
{
|
|
174
|
+
using PooledResource<List<EffectHandle>> handleBufferResource =
|
|
175
|
+
Buffers<EffectHandle>.List.Get(out List<EffectHandle> handleBuffer);
|
|
176
|
+
handleBuffer.AddRange(existingHandles);
|
|
177
|
+
for (int i = 0; i < handleBuffer.Count; ++i)
|
|
130
178
|
{
|
|
131
|
-
|
|
179
|
+
RemoveEffect(handleBuffer[i]);
|
|
132
180
|
}
|
|
181
|
+
}
|
|
133
182
|
|
|
134
|
-
|
|
135
|
-
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
case EffectStackingMode.Stack:
|
|
186
|
+
{
|
|
187
|
+
if (existingHandles is { Count: > 0 } && effect.maximumStacks > 0)
|
|
188
|
+
{
|
|
189
|
+
while (existingHandles.Count >= effect.maximumStacks)
|
|
136
190
|
{
|
|
137
|
-
|
|
138
|
-
|
|
191
|
+
EffectHandle oldestHandle = existingHandles[0];
|
|
192
|
+
RemoveEffect(oldestHandle);
|
|
139
193
|
}
|
|
140
194
|
}
|
|
141
|
-
}
|
|
142
195
|
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (maybeHandle.HasValue)
|
|
147
|
-
{
|
|
148
|
-
EffectHandle handle = maybeHandle.Value;
|
|
149
|
-
InternalApplyEffect(handle);
|
|
150
|
-
if (
|
|
151
|
-
effect.durationType == ModifierDurationType.Duration
|
|
152
|
-
&& (effect.resetDurationOnReapplication || !_appliedEffects.Contains(handle))
|
|
153
|
-
)
|
|
154
|
-
{
|
|
155
|
-
long handleId = handle.id;
|
|
156
|
-
_effectExpirations[handleId] = Time.time + effect.duration;
|
|
157
|
-
_effectHandlesById[handleId] = handle;
|
|
196
|
+
break;
|
|
158
197
|
}
|
|
159
198
|
}
|
|
160
|
-
|
|
199
|
+
|
|
200
|
+
EffectHandle newHandle = EffectHandle.CreateInstance(effect);
|
|
201
|
+
RegisterStackHandle(stackKey, newHandle);
|
|
202
|
+
InternalApplyEffect(newHandle);
|
|
203
|
+
return newHandle;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private static bool RequiresHandle(AttributeEffect effect)
|
|
207
|
+
{
|
|
208
|
+
return (effect.periodicEffects is { Count: > 0 })
|
|
209
|
+
|| (effect.behaviors is { Count: > 0 });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private List<EffectHandle> TryGetStackHandles(EffectStackKey stackKey)
|
|
213
|
+
{
|
|
214
|
+
_ = _handlesByStackKey.TryGetValue(stackKey, out List<EffectHandle> handles);
|
|
215
|
+
return handles;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
private void RegisterStackHandle(EffectStackKey stackKey, EffectHandle handle)
|
|
219
|
+
{
|
|
220
|
+
long handleId = handle.id;
|
|
221
|
+
_stackKeyByHandleId[handleId] = stackKey;
|
|
222
|
+
|
|
223
|
+
if (!_handlesByStackKey.TryGetValue(stackKey, out List<EffectHandle> handles))
|
|
161
224
|
{
|
|
162
|
-
|
|
225
|
+
handles = new List<EffectHandle>();
|
|
226
|
+
_handlesByStackKey.Add(stackKey, handles);
|
|
163
227
|
}
|
|
164
228
|
|
|
165
|
-
|
|
229
|
+
handles.Add(handle);
|
|
166
230
|
}
|
|
167
231
|
|
|
168
232
|
/// <summary>
|
|
@@ -173,17 +237,57 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
173
237
|
{
|
|
174
238
|
InternalRemoveEffect(handle);
|
|
175
239
|
_ = _appliedEffects.Remove(handle);
|
|
240
|
+
DeregisterHandle(handle);
|
|
176
241
|
}
|
|
177
242
|
|
|
178
243
|
public void RemoveAllEffects()
|
|
179
244
|
{
|
|
180
|
-
|
|
245
|
+
using PooledResource<List<EffectHandle>> handleBufferResource =
|
|
246
|
+
Buffers<EffectHandle>.List.Get(out List<EffectHandle> handleBuffer);
|
|
247
|
+
handleBuffer.AddRange(_appliedEffects);
|
|
248
|
+
foreach (EffectHandle handle in handleBuffer)
|
|
181
249
|
{
|
|
182
|
-
|
|
250
|
+
RemoveEffect(handle);
|
|
183
251
|
}
|
|
184
252
|
_appliedEffects.Clear();
|
|
185
253
|
}
|
|
186
254
|
|
|
255
|
+
private void DeregisterHandle(EffectHandle handle)
|
|
256
|
+
{
|
|
257
|
+
long handleId = handle.id;
|
|
258
|
+
if (_stackKeyByHandleId.TryGetValue(handleId, out EffectStackKey stackKey))
|
|
259
|
+
{
|
|
260
|
+
if (_handlesByStackKey.TryGetValue(stackKey, out List<EffectHandle> handles))
|
|
261
|
+
{
|
|
262
|
+
handles.Remove(handle);
|
|
263
|
+
if (handles.Count == 0)
|
|
264
|
+
{
|
|
265
|
+
_handlesByStackKey.Remove(stackKey);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
_stackKeyByHandleId.Remove(handleId);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
_periodicEffectStates.Remove(handleId);
|
|
273
|
+
|
|
274
|
+
if (_behaviorsByHandleId.Remove(handleId, out List<EffectBehavior> behaviorInstances))
|
|
275
|
+
{
|
|
276
|
+
EffectBehaviorContext context = new(this, handle, 0f);
|
|
277
|
+
for (int i = 0; i < behaviorInstances.Count; ++i)
|
|
278
|
+
{
|
|
279
|
+
EffectBehavior behavior = behaviorInstances[i];
|
|
280
|
+
if (behavior == null)
|
|
281
|
+
{
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
behavior.OnRemove(context);
|
|
286
|
+
Destroy(behavior);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
187
291
|
/// <summary>
|
|
188
292
|
/// Determines whether the specified effect is currently active on this handler.
|
|
189
293
|
/// </summary>
|
|
@@ -396,17 +500,24 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
396
500
|
_appliedEffects.Add(handle);
|
|
397
501
|
}
|
|
398
502
|
|
|
503
|
+
long handleId = handle.id;
|
|
504
|
+
_effectHandlesById[handleId] = handle;
|
|
505
|
+
|
|
399
506
|
AttributeEffect effect = handle.effect;
|
|
400
507
|
if (effect.durationType == ModifierDurationType.Duration)
|
|
401
508
|
{
|
|
402
|
-
if (effect.resetDurationOnReapplication
|
|
509
|
+
if (!exists || effect.resetDurationOnReapplication)
|
|
403
510
|
{
|
|
404
|
-
long handleId = handle.id;
|
|
405
511
|
_effectExpirations[handleId] = Time.time + effect.duration;
|
|
406
|
-
_effectHandlesById[handleId] = handle;
|
|
407
512
|
}
|
|
408
513
|
}
|
|
409
514
|
|
|
515
|
+
if (!exists)
|
|
516
|
+
{
|
|
517
|
+
RegisterPeriodicRuntime(handle);
|
|
518
|
+
RegisterBehaviors(handle);
|
|
519
|
+
}
|
|
520
|
+
|
|
410
521
|
if (!_initialized && _tagHandler == null)
|
|
411
522
|
{
|
|
412
523
|
this.AssignRelationalComponents();
|
|
@@ -435,6 +546,13 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
435
546
|
|
|
436
547
|
private void InternalApplyEffect(AttributeEffect effect)
|
|
437
548
|
{
|
|
549
|
+
if (effect.durationType == ModifierDurationType.Instant && RequiresHandle(effect))
|
|
550
|
+
{
|
|
551
|
+
this.LogWarn(
|
|
552
|
+
$"Effect {effect:json} defines periodic or behaviour data but is Instant. These features require a Duration or Infinite effect."
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
|
|
438
556
|
if (!_initialized && _tagHandler == null)
|
|
439
557
|
{
|
|
440
558
|
this.AssignRelationalComponents();
|
|
@@ -459,6 +577,116 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
459
577
|
}
|
|
460
578
|
}
|
|
461
579
|
|
|
580
|
+
private void RegisterPeriodicRuntime(EffectHandle handle)
|
|
581
|
+
{
|
|
582
|
+
AttributeEffect effect = handle.effect;
|
|
583
|
+
if (effect.periodicEffects is not { Count: > 0 })
|
|
584
|
+
{
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
List<PeriodicEffectRuntimeState> runtimeStates = null;
|
|
589
|
+
float startTime = Time.time;
|
|
590
|
+
|
|
591
|
+
foreach (PeriodicEffectDefinition definition in effect.periodicEffects)
|
|
592
|
+
{
|
|
593
|
+
if (definition == null)
|
|
594
|
+
{
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
(runtimeStates ??= new List<PeriodicEffectRuntimeState>()).Add(
|
|
599
|
+
new PeriodicEffectRuntimeState(definition, startTime)
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (runtimeStates is { Count: > 0 })
|
|
604
|
+
{
|
|
605
|
+
_periodicEffectStates[handle.id] = runtimeStates;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private void RegisterBehaviors(EffectHandle handle)
|
|
610
|
+
{
|
|
611
|
+
AttributeEffect effect = handle.effect;
|
|
612
|
+
if (effect.behaviors is not { Count: > 0 })
|
|
613
|
+
{
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
List<EffectBehavior> instances = null;
|
|
618
|
+
foreach (EffectBehavior behavior in effect.behaviors)
|
|
619
|
+
{
|
|
620
|
+
if (behavior == null)
|
|
621
|
+
{
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
EffectBehavior clone = Instantiate(behavior);
|
|
626
|
+
(instances ??= new List<EffectBehavior>()).Add(clone);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (instances is not { Count: > 0 })
|
|
630
|
+
{
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
EffectBehaviorContext context = new(this, handle, 0f);
|
|
635
|
+
for (int i = 0; i < instances.Count; ++i)
|
|
636
|
+
{
|
|
637
|
+
EffectBehavior instance = instances[i];
|
|
638
|
+
if (instance == null)
|
|
639
|
+
{
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
instance.OnApply(context);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
_behaviorsByHandleId[handle.id] = instances;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
private void ApplyPeriodicTick(
|
|
650
|
+
EffectHandle handle,
|
|
651
|
+
PeriodicEffectRuntimeState runtimeState,
|
|
652
|
+
float currentTime,
|
|
653
|
+
float deltaTime
|
|
654
|
+
)
|
|
655
|
+
{
|
|
656
|
+
PeriodicEffectDefinition definition = runtimeState.definition;
|
|
657
|
+
if (_attributes is { Count: > 0 } && definition.modifications is { Count: > 0 })
|
|
658
|
+
{
|
|
659
|
+
foreach (AttributesComponent attributesComponent in _attributes)
|
|
660
|
+
{
|
|
661
|
+
attributesComponent.ApplyAttributeModifications(definition.modifications, null);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
if (
|
|
666
|
+
_behaviorsByHandleId.TryGetValue(handle.id, out List<EffectBehavior> behaviors)
|
|
667
|
+
&& behaviors.Count > 0
|
|
668
|
+
)
|
|
669
|
+
{
|
|
670
|
+
EffectBehaviorContext context = new(this, handle, deltaTime);
|
|
671
|
+
PeriodicEffectTickContext tickContext = new(
|
|
672
|
+
definition,
|
|
673
|
+
runtimeState.ExecutedTicks,
|
|
674
|
+
currentTime
|
|
675
|
+
);
|
|
676
|
+
|
|
677
|
+
for (int i = 0; i < behaviors.Count; ++i)
|
|
678
|
+
{
|
|
679
|
+
EffectBehavior behavior = behaviors[i];
|
|
680
|
+
if (behavior == null)
|
|
681
|
+
{
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
behavior.OnPeriodicTick(context, tickContext);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
462
690
|
private void InternalApplyCosmeticEffects(EffectHandle handle)
|
|
463
691
|
{
|
|
464
692
|
if (_instancedCosmeticEffects.ContainsKey(handle))
|
|
@@ -511,8 +739,7 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
511
739
|
{
|
|
512
740
|
foreach (CosmeticEffectData cosmeticEffectData in attributeEffect.cosmeticEffects)
|
|
513
741
|
{
|
|
514
|
-
|
|
515
|
-
if (cosmeticEffect == null)
|
|
742
|
+
if (cosmeticEffectData == null)
|
|
516
743
|
{
|
|
517
744
|
this.LogError(
|
|
518
745
|
$"CosmeticEffectData is null for effect {attributeEffect:json}, cannot determine instancing scheme."
|
|
@@ -532,7 +759,7 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
532
759
|
Buffers<CosmeticEffectComponent>.List.Get();
|
|
533
760
|
List<CosmeticEffectComponent> cosmeticEffectsBuffer =
|
|
534
761
|
cosmeticEffectsResource.resource;
|
|
535
|
-
|
|
762
|
+
cosmeticEffectData.GetComponents(cosmeticEffectsBuffer);
|
|
536
763
|
foreach (CosmeticEffectComponent cosmeticComponent in cosmeticEffectsBuffer)
|
|
537
764
|
{
|
|
538
765
|
cosmeticComponent.OnApplyEffect(gameObject);
|
|
@@ -621,6 +848,13 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
621
848
|
}
|
|
622
849
|
|
|
623
850
|
private void Update()
|
|
851
|
+
{
|
|
852
|
+
ProcessEffectExpirations();
|
|
853
|
+
ProcessBehaviorTicks();
|
|
854
|
+
ProcessPeriodicEffects();
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
private void ProcessEffectExpirations()
|
|
624
858
|
{
|
|
625
859
|
if (_effectExpirations.Count <= 0)
|
|
626
860
|
{
|
|
@@ -631,19 +865,131 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
631
865
|
float currentTime = Time.time;
|
|
632
866
|
foreach (KeyValuePair<long, float> entry in _effectExpirations)
|
|
633
867
|
{
|
|
634
|
-
if (entry.Value
|
|
868
|
+
if (entry.Value <= currentTime)
|
|
635
869
|
{
|
|
636
870
|
_expiredEffectIds.Add(entry.Key);
|
|
637
871
|
}
|
|
638
872
|
}
|
|
639
873
|
|
|
640
|
-
|
|
874
|
+
for (int i = 0; i < _expiredEffectIds.Count; ++i)
|
|
641
875
|
{
|
|
876
|
+
long expiredHandleId = _expiredEffectIds[i];
|
|
642
877
|
if (_effectHandlesById.TryGetValue(expiredHandleId, out EffectHandle expiredHandle))
|
|
643
878
|
{
|
|
644
879
|
RemoveEffect(expiredHandle);
|
|
645
880
|
}
|
|
646
881
|
}
|
|
882
|
+
|
|
883
|
+
_expiredEffectIds.Clear();
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
private void ProcessBehaviorTicks()
|
|
887
|
+
{
|
|
888
|
+
if (_behaviorsByHandleId.Count <= 0)
|
|
889
|
+
{
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
using PooledResource<List<long>> behaviorHandleIdsResource = Buffers<long>.List.Get(
|
|
894
|
+
out List<long> behaviorHandleIdsBuffer
|
|
895
|
+
);
|
|
896
|
+
behaviorHandleIdsBuffer.AddRange(_behaviorsByHandleId.Keys);
|
|
897
|
+
float deltaTime = Time.deltaTime;
|
|
898
|
+
|
|
899
|
+
for (int i = 0; i < behaviorHandleIdsBuffer.Count; ++i)
|
|
900
|
+
{
|
|
901
|
+
long handleId = behaviorHandleIdsBuffer[i];
|
|
902
|
+
if (!_effectHandlesById.TryGetValue(handleId, out EffectHandle handle))
|
|
903
|
+
{
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (!_behaviorsByHandleId.TryGetValue(handleId, out List<EffectBehavior> behaviors))
|
|
908
|
+
{
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
EffectBehaviorContext context = new(this, handle, deltaTime);
|
|
913
|
+
for (int j = 0; j < behaviors.Count; ++j)
|
|
914
|
+
{
|
|
915
|
+
EffectBehavior behavior = behaviors[j];
|
|
916
|
+
if (behavior == null)
|
|
917
|
+
{
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
behavior.OnTick(context);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
private void ProcessPeriodicEffects()
|
|
927
|
+
{
|
|
928
|
+
if (_periodicEffectStates.Count <= 0)
|
|
929
|
+
{
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
float currentTime = Time.time;
|
|
934
|
+
float deltaTime = Time.deltaTime;
|
|
935
|
+
using PooledResource<List<long>> periodicRemovalResource = Buffers<long>.List.Get(
|
|
936
|
+
out List<long> periodicRemovalBuffer
|
|
937
|
+
);
|
|
938
|
+
using PooledResource<List<long>> periodHandleIdsResource = Buffers<long>.List.Get(
|
|
939
|
+
out List<long> periodicHandleIdsBuffer
|
|
940
|
+
);
|
|
941
|
+
periodicHandleIdsBuffer.AddRange(_periodicEffectStates.Keys);
|
|
942
|
+
|
|
943
|
+
for (int handleIndex = 0; handleIndex < periodicHandleIdsBuffer.Count; ++handleIndex)
|
|
944
|
+
{
|
|
945
|
+
long handleId = periodicHandleIdsBuffer[handleIndex];
|
|
946
|
+
if (!_effectHandlesById.TryGetValue(handleId, out EffectHandle handle))
|
|
947
|
+
{
|
|
948
|
+
periodicRemovalBuffer.Add(handleId);
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (
|
|
953
|
+
!_periodicEffectStates.TryGetValue(
|
|
954
|
+
handleId,
|
|
955
|
+
out List<PeriodicEffectRuntimeState> runtimes
|
|
956
|
+
)
|
|
957
|
+
)
|
|
958
|
+
{
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
bool hasActive = false;
|
|
963
|
+
|
|
964
|
+
for (int i = 0; i < runtimes.Count; ++i)
|
|
965
|
+
{
|
|
966
|
+
PeriodicEffectRuntimeState runtimeState = runtimes[i];
|
|
967
|
+
if (runtimeState == null)
|
|
968
|
+
{
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
while (runtimeState.TryConsumeTick(currentTime))
|
|
973
|
+
{
|
|
974
|
+
ApplyPeriodicTick(handle, runtimeState, currentTime, deltaTime);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
if (!runtimeState.IsComplete)
|
|
978
|
+
{
|
|
979
|
+
hasActive = true;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if (!hasActive)
|
|
984
|
+
{
|
|
985
|
+
periodicRemovalBuffer.Add(handleId);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
for (int i = 0; i < periodicRemovalBuffer.Count; ++i)
|
|
990
|
+
{
|
|
991
|
+
_periodicEffectStates.Remove(periodicRemovalBuffer[i]);
|
|
992
|
+
}
|
|
647
993
|
}
|
|
648
994
|
}
|
|
649
995
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
namespace WallstopStudios.UnityHelpers.Tags
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
5
|
+
|
|
6
|
+
/// <summary>
|
|
7
|
+
/// Key used to group effect handles for stacking decisions.
|
|
8
|
+
/// </summary>
|
|
9
|
+
internal readonly struct EffectStackKey : IEquatable<EffectStackKey>
|
|
10
|
+
{
|
|
11
|
+
private readonly EffectStackGroup _group;
|
|
12
|
+
private readonly AttributeEffect _effect;
|
|
13
|
+
private readonly string _customKey;
|
|
14
|
+
|
|
15
|
+
private EffectStackKey(EffectStackGroup group, AttributeEffect effect, string customKey)
|
|
16
|
+
{
|
|
17
|
+
_group = group;
|
|
18
|
+
_effect = effect;
|
|
19
|
+
_customKey = customKey;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public static EffectStackKey CreateReference(AttributeEffect effect)
|
|
23
|
+
{
|
|
24
|
+
return new EffectStackKey(EffectStackGroup.Reference, effect, null);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public static EffectStackKey CreateCustom(string customKey)
|
|
28
|
+
{
|
|
29
|
+
return new EffectStackKey(EffectStackGroup.CustomKey, null, customKey);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public bool Equals(EffectStackKey other)
|
|
33
|
+
{
|
|
34
|
+
if (_group != other._group)
|
|
35
|
+
{
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return _group switch
|
|
40
|
+
{
|
|
41
|
+
EffectStackGroup.Reference => ReferenceEquals(_effect, other._effect),
|
|
42
|
+
EffectStackGroup.CustomKey => string.Equals(
|
|
43
|
+
_customKey,
|
|
44
|
+
other._customKey,
|
|
45
|
+
StringComparison.Ordinal
|
|
46
|
+
),
|
|
47
|
+
_ => false,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public override bool Equals(object obj)
|
|
52
|
+
{
|
|
53
|
+
return obj is EffectStackKey other && Equals(other);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public override int GetHashCode()
|
|
57
|
+
{
|
|
58
|
+
return _group switch
|
|
59
|
+
{
|
|
60
|
+
EffectStackGroup.Reference => Objects.HashCode(_group, _effect),
|
|
61
|
+
EffectStackGroup.CustomKey => Objects.HashCode(
|
|
62
|
+
_group,
|
|
63
|
+
_customKey != null ? StringComparer.Ordinal.GetHashCode(_customKey) : 0
|
|
64
|
+
),
|
|
65
|
+
_ => Objects.HashCode(_group),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public static bool operator ==(EffectStackKey left, EffectStackKey right)
|
|
70
|
+
{
|
|
71
|
+
return left.Equals(right);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public static bool operator !=(EffectStackKey left, EffectStackKey right)
|
|
75
|
+
{
|
|
76
|
+
return !(left == right);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|