com.wallstop-studios.unity-helpers 2.0.2 → 2.0.4
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/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +4 -2
- package/Runtime/Tags/AttributeEffect.cs +167 -3
- package/Runtime/Tags/AttributeUtilities.cs +465 -0
- package/Runtime/Tags/AttributesComponent.cs +14 -3
- package/Runtime/Tags/EffectHandler.cs +179 -0
- package/Runtime/Tags/TagHandler.cs +375 -21
- package/Tests/Runtime/Tags/AttributeUtilitiesTests.cs +245 -0
- package/Tests/Runtime/Tags/CosmeticAndCollisionTests.cs +1 -1
- package/Tests/Runtime/Tags/EffectHandlerTests.cs +191 -0
- package/Tests/Runtime/Tags/TagHandlerTests.cs +130 -6
- package/package.json +1 -1
|
@@ -109,6 +109,14 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
109
109
|
/// <param name="target">The Unity Object (GameObject or Component) to check.</param>
|
|
110
110
|
/// <param name="effectTag">The tag to check for.</param>
|
|
111
111
|
/// <returns><c>true</c> if the target has a TagHandler with the specified tag; otherwise, <c>false</c>.</returns>
|
|
112
|
+
/// <example>
|
|
113
|
+
/// <code>
|
|
114
|
+
/// if (player.HasTag("Stunned"))
|
|
115
|
+
/// {
|
|
116
|
+
/// DisableMovement();
|
|
117
|
+
/// }
|
|
118
|
+
/// </code>
|
|
119
|
+
/// </example>
|
|
112
120
|
public static bool HasTag(this Object target, string effectTag)
|
|
113
121
|
{
|
|
114
122
|
if (target == null)
|
|
@@ -126,6 +134,15 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
126
134
|
/// <param name="target">The Unity Object (GameObject or Component) to check.</param>
|
|
127
135
|
/// <param name="effectTags">The collection of tags to check for.</param>
|
|
128
136
|
/// <returns><c>true</c> if the target has any of the specified tags; otherwise, <c>false</c>.</returns>
|
|
137
|
+
/// <example>
|
|
138
|
+
/// <code>
|
|
139
|
+
/// string[] crowdControlTags = { "Stunned", "Frozen", "KnockedDown" };
|
|
140
|
+
/// if (player.HasAnyTag(crowdControlTags))
|
|
141
|
+
/// {
|
|
142
|
+
/// ShowCrowdControlUI();
|
|
143
|
+
/// }
|
|
144
|
+
/// </code>
|
|
145
|
+
/// </example>
|
|
129
146
|
public static bool HasAnyTag(this Object target, IEnumerable<string> effectTags)
|
|
130
147
|
{
|
|
131
148
|
if (target == null)
|
|
@@ -143,6 +160,7 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
143
160
|
/// <param name="target">The Unity Object (GameObject or Component) to check.</param>
|
|
144
161
|
/// <param name="effectTags">The list of tags to check for.</param>
|
|
145
162
|
/// <returns><c>true</c> if the target has any of the specified tags; otherwise, <c>false</c>.</returns>
|
|
163
|
+
/// <remarks>Equivalent to <see cref="HasAnyTag(UnityEngine.Object,System.Collections.Generic.IEnumerable{string})"/> but optimized for indexable lists.</remarks>
|
|
146
164
|
public static bool HasAnyTag(this Object target, IReadOnlyList<string> effectTags)
|
|
147
165
|
{
|
|
148
166
|
if (target == null)
|
|
@@ -154,6 +172,186 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
154
172
|
&& tagHandler.HasAnyTag(effectTags);
|
|
155
173
|
}
|
|
156
174
|
|
|
175
|
+
/// <summary>
|
|
176
|
+
/// Extension method to check if a Unity Object has all of the specified tags.
|
|
177
|
+
/// </summary>
|
|
178
|
+
/// <param name="target">The Unity Object (GameObject or Component) to check.</param>
|
|
179
|
+
/// <param name="effectTags">The collection of tags that must all be active.</param>
|
|
180
|
+
/// <returns><c>true</c> if all tags are active; otherwise, <c>false</c>.</returns>
|
|
181
|
+
/// <example>
|
|
182
|
+
/// <code>
|
|
183
|
+
/// string[] stealthRequirements = { "Invisible", "Silenced" };
|
|
184
|
+
/// if (player.HasAllTags(stealthRequirements))
|
|
185
|
+
/// {
|
|
186
|
+
/// EnableBackstabBonus();
|
|
187
|
+
/// }
|
|
188
|
+
/// </code>
|
|
189
|
+
/// </example>
|
|
190
|
+
public static bool HasAllTags(this Object target, IEnumerable<string> effectTags)
|
|
191
|
+
{
|
|
192
|
+
if (target == null)
|
|
193
|
+
{
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return target.TryGetComponent(out TagHandler tagHandler)
|
|
198
|
+
&& tagHandler.HasAllTags(effectTags);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// <summary>
|
|
202
|
+
/// Extension method to check if a Unity Object has all of the specified tags.
|
|
203
|
+
/// </summary>
|
|
204
|
+
/// <param name="target">The Unity Object (GameObject or Component) to check.</param>
|
|
205
|
+
/// <param name="effectTags">The list of tags that must all be active.</param>
|
|
206
|
+
/// <returns><c>true</c> if all tags are active; otherwise, <c>false</c>.</returns>
|
|
207
|
+
/// <remarks>Equivalent to <see cref="HasAllTags(UnityEngine.Object,System.Collections.Generic.IEnumerable{string})"/> but optimized for indexable lists.</remarks>
|
|
208
|
+
public static bool HasAllTags(this Object target, IReadOnlyList<string> effectTags)
|
|
209
|
+
{
|
|
210
|
+
if (target == null)
|
|
211
|
+
{
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return target.TryGetComponent(out TagHandler tagHandler)
|
|
216
|
+
&& tagHandler.HasAllTags(effectTags);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/// <summary>
|
|
220
|
+
/// Extension method to determine whether none of the specified tags are active on the target.
|
|
221
|
+
/// </summary>
|
|
222
|
+
/// <param name="target">The Unity Object (GameObject or Component) to check.</param>
|
|
223
|
+
/// <param name="effectTags">The collection of tags to inspect.</param>
|
|
224
|
+
/// <returns><c>true</c> if no tags in the collection are active; otherwise, <c>false</c>.</returns>
|
|
225
|
+
public static bool HasNoneOfTags(this Object target, IEnumerable<string> effectTags)
|
|
226
|
+
{
|
|
227
|
+
if (target == null)
|
|
228
|
+
{
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return !target.TryGetComponent(out TagHandler tagHandler)
|
|
233
|
+
|| tagHandler.HasNoneOfTags(effectTags);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/// <summary>
|
|
237
|
+
/// Extension method to determine whether none of the specified tags are active on the target.
|
|
238
|
+
/// </summary>
|
|
239
|
+
/// <param name="target">The Unity Object (GameObject or Component) to check.</param>
|
|
240
|
+
/// <param name="effectTags">The list of tags to inspect.</param>
|
|
241
|
+
/// <returns><c>true</c> if no tags in the collection are active; otherwise, <c>false</c>.</returns>
|
|
242
|
+
public static bool HasNoneOfTags(this Object target, IReadOnlyList<string> effectTags)
|
|
243
|
+
{
|
|
244
|
+
if (target == null)
|
|
245
|
+
{
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return !target.TryGetComponent(out TagHandler tagHandler)
|
|
250
|
+
|| tagHandler.HasNoneOfTags(effectTags);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/// <summary>
|
|
254
|
+
/// Attempts to retrieve the active count for a specific tag on the target.
|
|
255
|
+
/// </summary>
|
|
256
|
+
/// <param name="target">The Unity Object (GameObject or Component) to inspect.</param>
|
|
257
|
+
/// <param name="effectTag">The tag whose count should be retrieved.</param>
|
|
258
|
+
/// <param name="count">
|
|
259
|
+
/// When this method returns, contains the tag count (cast to <see cref="int"/>) if available; otherwise, zero.
|
|
260
|
+
/// </param>
|
|
261
|
+
/// <returns><c>true</c> if the target has a <see cref="TagHandler"/> and the tag is tracked; otherwise, <c>false</c>.</returns>
|
|
262
|
+
/// <example>
|
|
263
|
+
/// <code>
|
|
264
|
+
/// if (target.TryGetTagCount("Bleeding", out int stacks) && stacks >= 5)
|
|
265
|
+
/// {
|
|
266
|
+
/// ApplyMajorWoundPenalty();
|
|
267
|
+
/// }
|
|
268
|
+
/// </code>
|
|
269
|
+
/// </example>
|
|
270
|
+
public static bool TryGetTagCount(this Object target, string effectTag, out int count)
|
|
271
|
+
{
|
|
272
|
+
count = 0;
|
|
273
|
+
if (target == null)
|
|
274
|
+
{
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return target.TryGetComponent(out TagHandler tagHandler)
|
|
279
|
+
&& tagHandler.TryGetTagCount(effectTag, out count);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/// <summary>
|
|
283
|
+
/// Retrieves the active tags on the target into an optional buffer.
|
|
284
|
+
/// </summary>
|
|
285
|
+
/// <param name="target">The Unity Object (GameObject or Component) to inspect.</param>
|
|
286
|
+
/// <param name="buffer">
|
|
287
|
+
/// Optional buffer to populate. When <c>null</c>, a new list is created. The buffer is cleared before population.
|
|
288
|
+
/// </param>
|
|
289
|
+
/// <returns>The populated buffer of active tags. The buffer is empty when no tags are present or no handler exists.</returns>
|
|
290
|
+
/// <example>
|
|
291
|
+
/// <code>
|
|
292
|
+
/// List<string> activeTags = target.GetActiveTags(_tagBuffer);
|
|
293
|
+
/// if (activeTags.Contains("Invisible"))
|
|
294
|
+
/// {
|
|
295
|
+
/// EnableDetectionShader();
|
|
296
|
+
/// }
|
|
297
|
+
/// </code>
|
|
298
|
+
/// </example>
|
|
299
|
+
public static List<string> GetActiveTags(this Object target, List<string> buffer = null)
|
|
300
|
+
{
|
|
301
|
+
buffer ??= new List<string>();
|
|
302
|
+
buffer.Clear();
|
|
303
|
+
if (target == null)
|
|
304
|
+
{
|
|
305
|
+
return buffer;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (!target.TryGetComponent(out TagHandler tagHandler))
|
|
309
|
+
{
|
|
310
|
+
return buffer;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return tagHandler.GetActiveTags(buffer);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/// <summary>
|
|
317
|
+
/// Retrieves all effect handles that contributed a specific tag on the target.
|
|
318
|
+
/// </summary>
|
|
319
|
+
/// <param name="target">The Unity Object (GameObject or Component) to inspect.</param>
|
|
320
|
+
/// <param name="effectTag">The tag to query.</param>
|
|
321
|
+
/// <param name="buffer">
|
|
322
|
+
/// Optional buffer to populate. When <c>null</c>, a new list is created. The buffer is cleared before population.
|
|
323
|
+
/// </param>
|
|
324
|
+
/// <returns>The populated buffer of effect handles whose effects contain <paramref name="effectTag"/>.</returns>
|
|
325
|
+
/// <example>
|
|
326
|
+
/// <code>
|
|
327
|
+
/// List<EffectHandle> taggedHandles = target.GetHandlesWithTag("Burning", _handleBuffer);
|
|
328
|
+
/// foreach (EffectHandle handle in taggedHandles)
|
|
329
|
+
/// {
|
|
330
|
+
/// target.RefreshEffect(handle);
|
|
331
|
+
/// }
|
|
332
|
+
/// </code>
|
|
333
|
+
/// </example>
|
|
334
|
+
public static List<EffectHandle> GetHandlesWithTag(
|
|
335
|
+
this Object target,
|
|
336
|
+
string effectTag,
|
|
337
|
+
List<EffectHandle> buffer = null
|
|
338
|
+
)
|
|
339
|
+
{
|
|
340
|
+
buffer ??= new List<EffectHandle>();
|
|
341
|
+
buffer.Clear();
|
|
342
|
+
if (target == null)
|
|
343
|
+
{
|
|
344
|
+
return buffer;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (!target.TryGetComponent(out TagHandler tagHandler))
|
|
348
|
+
{
|
|
349
|
+
return buffer;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return tagHandler.GetHandlesWithTag(effectTag, buffer);
|
|
353
|
+
}
|
|
354
|
+
|
|
157
355
|
/// <summary>
|
|
158
356
|
/// Extension method to apply an effect to a Unity Object.
|
|
159
357
|
/// Automatically adds an EffectHandler component if one doesn't exist.
|
|
@@ -161,6 +359,17 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
161
359
|
/// <param name="target">The Unity Object (GameObject or Component) to apply the effect to.</param>
|
|
162
360
|
/// <param name="attributeEffect">The effect to apply.</param>
|
|
163
361
|
/// <returns>An EffectHandle for non-instant effects, or null for instant effects.</returns>
|
|
362
|
+
/// <example>
|
|
363
|
+
/// <code>
|
|
364
|
+
/// EffectHandle? handle = enemy.ApplyEffect(poisonEffect);
|
|
365
|
+
/// if (!handle.HasValue)
|
|
366
|
+
/// {
|
|
367
|
+
/// // Instant effects do not return a handle
|
|
368
|
+
/// return;
|
|
369
|
+
/// }
|
|
370
|
+
/// activeHandles.Add(handle.Value);
|
|
371
|
+
/// </code>
|
|
372
|
+
/// </example>
|
|
164
373
|
public static EffectHandle? ApplyEffect(this Object target, AttributeEffect attributeEffect)
|
|
165
374
|
{
|
|
166
375
|
if (target == null)
|
|
@@ -172,6 +381,17 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
172
381
|
return effectHandler.ApplyEffect(attributeEffect);
|
|
173
382
|
}
|
|
174
383
|
|
|
384
|
+
/// <summary>
|
|
385
|
+
/// Applies a list of effects to the target without allocating a result collection.
|
|
386
|
+
/// </summary>
|
|
387
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
388
|
+
/// <param name="attributeEffects">The list of effects to apply.</param>
|
|
389
|
+
/// <remarks>Effects are applied sequentially; instant effects still return <c>null</c> handles internally.</remarks>
|
|
390
|
+
/// <example>
|
|
391
|
+
/// <code>
|
|
392
|
+
/// AttributeUtilities.ApplyEffectsNoAlloc(player, _precomputedEffects);
|
|
393
|
+
/// </code>
|
|
394
|
+
/// </example>
|
|
175
395
|
public static void ApplyEffectsNoAlloc(
|
|
176
396
|
this Object target,
|
|
177
397
|
List<AttributeEffect> attributeEffects
|
|
@@ -193,6 +413,12 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
193
413
|
}
|
|
194
414
|
}
|
|
195
415
|
|
|
416
|
+
/// <summary>
|
|
417
|
+
/// Applies a sequence of effects to the target without allocations, iterating any enumerable.
|
|
418
|
+
/// </summary>
|
|
419
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
420
|
+
/// <param name="attributeEffects">The enumerable of effects to apply.</param>
|
|
421
|
+
/// <remarks>Use when you are streaming effects from a generator or LINQ query.</remarks>
|
|
196
422
|
public static void ApplyEffectsNoAlloc(
|
|
197
423
|
this Object target,
|
|
198
424
|
IEnumerable<AttributeEffect> attributeEffects
|
|
@@ -210,6 +436,19 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
210
436
|
}
|
|
211
437
|
}
|
|
212
438
|
|
|
439
|
+
/// <summary>
|
|
440
|
+
/// Applies a list of effects to the target and collects any returned handles into the supplied buffer.
|
|
441
|
+
/// </summary>
|
|
442
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
443
|
+
/// <param name="attributeEffects">The list of effects to apply.</param>
|
|
444
|
+
/// <param name="effectHandles">Buffer that receives non-<c>null</c> handles. The buffer is not cleared automatically.</param>
|
|
445
|
+
/// <example>
|
|
446
|
+
/// <code>
|
|
447
|
+
/// _handles.Clear();
|
|
448
|
+
/// target.ApplyEffectsNoAlloc(burstEffects, _handles);
|
|
449
|
+
/// // _handles now contains handles for duration and infinite effects.
|
|
450
|
+
/// </code>
|
|
451
|
+
/// </example>
|
|
213
452
|
public static void ApplyEffectsNoAlloc(
|
|
214
453
|
this Object target,
|
|
215
454
|
List<AttributeEffect> attributeEffects,
|
|
@@ -232,6 +471,18 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
232
471
|
}
|
|
233
472
|
}
|
|
234
473
|
|
|
474
|
+
/// <summary>
|
|
475
|
+
/// Applies a list of effects to the target and returns the collected handles.
|
|
476
|
+
/// </summary>
|
|
477
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
478
|
+
/// <param name="attributeEffects">The list of effects to apply.</param>
|
|
479
|
+
/// <returns>A list containing handles for every duration or infinite effect that was applied.</returns>
|
|
480
|
+
/// <example>
|
|
481
|
+
/// <code>
|
|
482
|
+
/// List<EffectHandle> handles = player.ApplyEffects(bossOpeners);
|
|
483
|
+
/// _activeBossEffects.AddRange(handles);
|
|
484
|
+
/// </code>
|
|
485
|
+
/// </example>
|
|
235
486
|
public static List<EffectHandle> ApplyEffects(
|
|
236
487
|
this Object target,
|
|
237
488
|
List<AttributeEffect> attributeEffects
|
|
@@ -242,6 +493,20 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
242
493
|
return handles;
|
|
243
494
|
}
|
|
244
495
|
|
|
496
|
+
/// <summary>
|
|
497
|
+
/// Removes a previously applied effect by handle.
|
|
498
|
+
/// </summary>
|
|
499
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
500
|
+
/// <param name="effectHandle">The handle returned by <see cref="ApplyEffect(UnityEngine.Object, AttributeEffect)"/>.</param>
|
|
501
|
+
/// <example>
|
|
502
|
+
/// <code>
|
|
503
|
+
/// if (_slowHandle.HasValue)
|
|
504
|
+
/// {
|
|
505
|
+
/// enemy.RemoveEffect(_slowHandle.Value);
|
|
506
|
+
/// _slowHandle = null;
|
|
507
|
+
/// }
|
|
508
|
+
/// </code>
|
|
509
|
+
/// </example>
|
|
245
510
|
public static void RemoveEffect(this Object target, EffectHandle effectHandle)
|
|
246
511
|
{
|
|
247
512
|
if (target == null)
|
|
@@ -255,6 +520,17 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
255
520
|
}
|
|
256
521
|
}
|
|
257
522
|
|
|
523
|
+
/// <summary>
|
|
524
|
+
/// Removes a collection of effect handles from the target.
|
|
525
|
+
/// </summary>
|
|
526
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
527
|
+
/// <param name="effectHandles">Handles to remove. The list is iterated as-is.</param>
|
|
528
|
+
/// <example>
|
|
529
|
+
/// <code>
|
|
530
|
+
/// target.RemoveEffects(_queuedDispels);
|
|
531
|
+
/// _queuedDispels.Clear();
|
|
532
|
+
/// </code>
|
|
533
|
+
/// </example>
|
|
258
534
|
public static void RemoveEffects(this Object target, List<EffectHandle> effectHandles)
|
|
259
535
|
{
|
|
260
536
|
if (target == null || effectHandles.Count <= 0)
|
|
@@ -271,6 +547,16 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
271
547
|
}
|
|
272
548
|
}
|
|
273
549
|
|
|
550
|
+
/// <summary>
|
|
551
|
+
/// Removes every active effect from the target.
|
|
552
|
+
/// </summary>
|
|
553
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
554
|
+
/// <example>
|
|
555
|
+
/// <code>
|
|
556
|
+
/// // Cleanse all effects when respawning the character
|
|
557
|
+
/// character.RemoveAllEffects();
|
|
558
|
+
/// </code>
|
|
559
|
+
/// </example>
|
|
274
560
|
public static void RemoveAllEffects(this Object target)
|
|
275
561
|
{
|
|
276
562
|
if (target == null)
|
|
@@ -284,6 +570,185 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
284
570
|
}
|
|
285
571
|
}
|
|
286
572
|
|
|
573
|
+
/// <summary>
|
|
574
|
+
/// Determines whether the specified effect is currently active on the target.
|
|
575
|
+
/// </summary>
|
|
576
|
+
/// <param name="target">The Unity Object (GameObject or Component) to inspect.</param>
|
|
577
|
+
/// <param name="attributeEffect">The effect to query.</param>
|
|
578
|
+
/// <returns><c>true</c> if the effect is active; otherwise, <c>false</c>.</returns>
|
|
579
|
+
/// <example>
|
|
580
|
+
/// <code>
|
|
581
|
+
/// if (!enemy.IsEffectActive(slowEffect))
|
|
582
|
+
/// {
|
|
583
|
+
/// enemy.ApplyEffect(slowEffect);
|
|
584
|
+
/// }
|
|
585
|
+
/// </code>
|
|
586
|
+
/// </example>
|
|
587
|
+
public static bool IsEffectActive(this Object target, AttributeEffect attributeEffect)
|
|
588
|
+
{
|
|
589
|
+
if (target == null)
|
|
590
|
+
{
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return target.TryGetComponent(out EffectHandler effectHandler)
|
|
595
|
+
&& effectHandler.IsEffectActive(attributeEffect);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/// <summary>
|
|
599
|
+
/// Retrieves the number of active handles for the specified effect on the target.
|
|
600
|
+
/// </summary>
|
|
601
|
+
/// <param name="target">The Unity Object (GameObject or Component) to inspect.</param>
|
|
602
|
+
/// <param name="attributeEffect">The effect to count.</param>
|
|
603
|
+
/// <returns>The number of active handles; zero when inactive.</returns>
|
|
604
|
+
/// <example>
|
|
605
|
+
/// <code>
|
|
606
|
+
/// int stacks = target.GetEffectStackCount(bleedEffect);
|
|
607
|
+
/// bleedStacksLabel.text = stacks.ToString();
|
|
608
|
+
/// </code>
|
|
609
|
+
/// </example>
|
|
610
|
+
public static int GetEffectStackCount(this Object target, AttributeEffect attributeEffect)
|
|
611
|
+
{
|
|
612
|
+
if (target == null)
|
|
613
|
+
{
|
|
614
|
+
return 0;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return target.TryGetComponent(out EffectHandler effectHandler)
|
|
618
|
+
? effectHandler.GetEffectStackCount(attributeEffect)
|
|
619
|
+
: 0;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/// <summary>
|
|
623
|
+
/// Copies all active effect handles on the target into the provided buffer.
|
|
624
|
+
/// </summary>
|
|
625
|
+
/// <param name="target">The Unity Object (GameObject or Component) to inspect.</param>
|
|
626
|
+
/// <param name="buffer">
|
|
627
|
+
/// Optional buffer to populate. When <c>null</c>, a new list is created. The buffer is cleared before population.
|
|
628
|
+
/// </param>
|
|
629
|
+
/// <returns>The populated buffer containing every active effect handle.</returns>
|
|
630
|
+
/// <example>
|
|
631
|
+
/// <code>
|
|
632
|
+
/// List<EffectHandle> handles = target.GetActiveEffects(_handleBuffer);
|
|
633
|
+
/// foreach (EffectHandle handle in handles)
|
|
634
|
+
/// {
|
|
635
|
+
/// Debug.Log(handle);
|
|
636
|
+
/// }
|
|
637
|
+
/// </code>
|
|
638
|
+
/// </example>
|
|
639
|
+
public static List<EffectHandle> GetActiveEffects(
|
|
640
|
+
this Object target,
|
|
641
|
+
List<EffectHandle> buffer = null
|
|
642
|
+
)
|
|
643
|
+
{
|
|
644
|
+
buffer ??= new List<EffectHandle>();
|
|
645
|
+
buffer.Clear();
|
|
646
|
+
if (target == null)
|
|
647
|
+
{
|
|
648
|
+
return buffer;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (!target.TryGetComponent(out EffectHandler effectHandler))
|
|
652
|
+
{
|
|
653
|
+
return buffer;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return effectHandler.GetActiveEffects(buffer);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/// <summary>
|
|
660
|
+
/// Attempts to retrieve the remaining duration for the specified effect handle on the target.
|
|
661
|
+
/// </summary>
|
|
662
|
+
/// <param name="target">The Unity Object (GameObject or Component) to inspect.</param>
|
|
663
|
+
/// <param name="effectHandle">The handle to query.</param>
|
|
664
|
+
/// <param name="remainingDuration">When this method returns, contains the remaining time if available; otherwise, zero.</param>
|
|
665
|
+
/// <returns><c>true</c> if a duration was found; otherwise, <c>false</c>.</returns>
|
|
666
|
+
public static bool TryGetRemainingDuration(
|
|
667
|
+
this Object target,
|
|
668
|
+
EffectHandle effectHandle,
|
|
669
|
+
out float remainingDuration
|
|
670
|
+
)
|
|
671
|
+
{
|
|
672
|
+
remainingDuration = 0f;
|
|
673
|
+
if (target == null)
|
|
674
|
+
{
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return target.TryGetComponent(out EffectHandler effectHandler)
|
|
679
|
+
&& effectHandler.TryGetRemainingDuration(effectHandle, out remainingDuration);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/// <summary>
|
|
683
|
+
/// Ensures an effect handle exists on the target, adding an EffectHandler when necessary.
|
|
684
|
+
/// </summary>
|
|
685
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
686
|
+
/// <param name="attributeEffect">The effect to apply or refresh.</param>
|
|
687
|
+
/// <returns>An active handle for the effect, or <c>null</c> for instant effects.</returns>
|
|
688
|
+
public static EffectHandle? EnsureHandle(
|
|
689
|
+
this Object target,
|
|
690
|
+
AttributeEffect attributeEffect
|
|
691
|
+
)
|
|
692
|
+
{
|
|
693
|
+
return EnsureHandle(target, attributeEffect, refreshDuration: true);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/// <summary>
|
|
697
|
+
/// Ensures an effect handle exists on the target, adding an EffectHandler when necessary.
|
|
698
|
+
/// </summary>
|
|
699
|
+
/// <param name="target">The Unity Object (GameObject or Component) to modify.</param>
|
|
700
|
+
/// <param name="attributeEffect">The effect to apply or refresh.</param>
|
|
701
|
+
/// <param name="refreshDuration">
|
|
702
|
+
/// When <c>true</c>, attempts to refresh the duration of an existing handle if the effect supports it.
|
|
703
|
+
/// </param>
|
|
704
|
+
/// <returns>An active handle for the effect, or <c>null</c> for instant effects.</returns>
|
|
705
|
+
public static EffectHandle? EnsureHandle(
|
|
706
|
+
this Object target,
|
|
707
|
+
AttributeEffect attributeEffect,
|
|
708
|
+
bool refreshDuration
|
|
709
|
+
)
|
|
710
|
+
{
|
|
711
|
+
if (target == null)
|
|
712
|
+
{
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
EffectHandler effectHandler = target.GetGameObject().GetOrAddComponent<EffectHandler>();
|
|
717
|
+
return effectHandler.EnsureHandle(attributeEffect, refreshDuration);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/// <summary>
|
|
721
|
+
/// Attempts to refresh the duration of an effect handle on the target.
|
|
722
|
+
/// </summary>
|
|
723
|
+
/// <param name="target">The Unity Object (GameObject or Component) to inspect.</param>
|
|
724
|
+
/// <param name="effectHandle">The handle to refresh.</param>
|
|
725
|
+
/// <param name="ignoreReapplicationPolicy">
|
|
726
|
+
/// When <c>true</c>, refreshes the duration even if the effect disallows reapplication resets.
|
|
727
|
+
/// </param>
|
|
728
|
+
/// <returns><c>true</c> if the duration was refreshed; otherwise, <c>false</c>.</returns>
|
|
729
|
+
/// <example>
|
|
730
|
+
/// <code>
|
|
731
|
+
/// if (!target.RefreshEffect(handle))
|
|
732
|
+
/// {
|
|
733
|
+
/// target.RefreshEffect(handle, ignoreReapplicationPolicy: true);
|
|
734
|
+
/// }
|
|
735
|
+
/// </code>
|
|
736
|
+
/// </example>
|
|
737
|
+
public static bool RefreshEffect(
|
|
738
|
+
this Object target,
|
|
739
|
+
EffectHandle effectHandle,
|
|
740
|
+
bool ignoreReapplicationPolicy = false
|
|
741
|
+
)
|
|
742
|
+
{
|
|
743
|
+
if (target == null)
|
|
744
|
+
{
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return target.TryGetComponent(out EffectHandler effectHandler)
|
|
749
|
+
&& effectHandler.RefreshEffect(effectHandle, ignoreReapplicationPolicy);
|
|
750
|
+
}
|
|
751
|
+
|
|
287
752
|
public static Dictionary<string, FieldInfo> GetAttributeFields(Type type)
|
|
288
753
|
{
|
|
289
754
|
return AttributeFields.GetOrAdd(
|
|
@@ -48,7 +48,7 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
48
48
|
/// </summary>
|
|
49
49
|
public event Action<string, float, float> OnAttributeModified;
|
|
50
50
|
|
|
51
|
-
private
|
|
51
|
+
private Dictionary<string, Func<object, Attribute>> _attributeFieldGetters;
|
|
52
52
|
private readonly HashSet<EffectHandle> _effectHandles;
|
|
53
53
|
|
|
54
54
|
[SiblingComponent]
|
|
@@ -62,7 +62,6 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
62
62
|
/// </summary>
|
|
63
63
|
protected AttributesComponent()
|
|
64
64
|
{
|
|
65
|
-
_attributeFieldGetters = AttributeUtilities.GetOptimizedAttributeFields(GetType());
|
|
66
65
|
_effectHandles = new HashSet<EffectHandle>();
|
|
67
66
|
}
|
|
68
67
|
|
|
@@ -72,6 +71,7 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
72
71
|
/// </summary>
|
|
73
72
|
protected virtual void Awake()
|
|
74
73
|
{
|
|
74
|
+
EnsureAttributeFieldGettersInitialized();
|
|
75
75
|
this.AssignSiblingComponents();
|
|
76
76
|
_effectHandler.Register(this);
|
|
77
77
|
}
|
|
@@ -217,8 +217,9 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
protected bool TryGetAttribute(string attributeName, out Attribute attribute)
|
|
221
221
|
{
|
|
222
|
+
EnsureAttributeFieldGettersInitialized();
|
|
222
223
|
if (
|
|
223
224
|
!_attributeFieldGetters.TryGetValue(
|
|
224
225
|
attributeName,
|
|
@@ -233,5 +234,15 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
233
234
|
attribute = getter(this);
|
|
234
235
|
return true;
|
|
235
236
|
}
|
|
237
|
+
|
|
238
|
+
protected void EnsureAttributeFieldGettersInitialized()
|
|
239
|
+
{
|
|
240
|
+
if (_attributeFieldGetters != null)
|
|
241
|
+
{
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
_attributeFieldGetters = AttributeUtilities.GetOptimizedAttributeFields(GetType());
|
|
246
|
+
}
|
|
236
247
|
}
|
|
237
248
|
}
|