com.wallstop-studios.unity-helpers 2.0.3 → 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/Core/Helper/UnityMainThreadDispatcher.cs +4 -2
- package/Runtime/Tags/Attribute.cs +144 -24
- package/Runtime/Tags/AttributeEffect.cs +278 -11
- package/Runtime/Tags/AttributeModification.cs +59 -29
- package/Runtime/Tags/AttributeUtilities.cs +465 -0
- 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 +564 -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/Runtime/Tags/TagHandler.cs +375 -21
- 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/AttributeUtilitiesTests.cs +245 -0
- package/Tests/Runtime/Tags/CosmeticAndCollisionTests.cs +1 -1
- package/Tests/Runtime/Tags/EffectBehaviorTests.cs +184 -0
- package/Tests/Runtime/Tags/EffectBehaviorTests.cs.meta +3 -0
- package/Tests/Runtime/Tags/EffectHandlerTests.cs +809 -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/Tests/Runtime/Tags/TagHandlerTests.cs +130 -6
- 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
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
namespace WallstopStudios.UnityHelpers.Tests.Tags
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using NUnit.Framework;
|
|
5
|
+
using UnityEngine;
|
|
6
|
+
using WallstopStudios.UnityHelpers.Tags;
|
|
7
|
+
using Attribute = WallstopStudios.UnityHelpers.Tags.Attribute;
|
|
8
|
+
|
|
9
|
+
[TestFixture]
|
|
10
|
+
public sealed class AttributeTests : AttributeTagsTestBase
|
|
11
|
+
{
|
|
12
|
+
[SetUp]
|
|
13
|
+
public void SetUp()
|
|
14
|
+
{
|
|
15
|
+
ResetEffectHandleId();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
[Test]
|
|
19
|
+
public void CurrentValueReflectsBaseValueWhenUnmodified()
|
|
20
|
+
{
|
|
21
|
+
Attribute attribute = new(12f);
|
|
22
|
+
Assert.AreEqual(12f, attribute.CurrentValue);
|
|
23
|
+
Assert.AreEqual(12f, attribute.BaseValue);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
[Test]
|
|
27
|
+
public void ApplyAttributeModificationWithoutHandleMutatesBase()
|
|
28
|
+
{
|
|
29
|
+
Attribute attribute = new(10f);
|
|
30
|
+
AttributeModification modification = new()
|
|
31
|
+
{
|
|
32
|
+
attribute = "health",
|
|
33
|
+
action = ModificationAction.Addition,
|
|
34
|
+
value = 5f,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
attribute.ApplyAttributeModification(modification);
|
|
38
|
+
Assert.AreEqual(15f, attribute.BaseValue);
|
|
39
|
+
Assert.AreEqual(15f, attribute.CurrentValue);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
[Test]
|
|
43
|
+
public void ApplyAndRemoveAttributeModificationWithHandleRecalculates()
|
|
44
|
+
{
|
|
45
|
+
Attribute attribute = new(100f);
|
|
46
|
+
AttributeModification addition = new()
|
|
47
|
+
{
|
|
48
|
+
attribute = "health",
|
|
49
|
+
action = ModificationAction.Addition,
|
|
50
|
+
value = 25f,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
AttributeEffect effect = Track(ScriptableObject.CreateInstance<AttributeEffect>());
|
|
54
|
+
effect.name = "Buff";
|
|
55
|
+
EffectHandle handle = EffectHandle.CreateInstance(effect);
|
|
56
|
+
|
|
57
|
+
attribute.ApplyAttributeModification(addition, handle);
|
|
58
|
+
Assert.AreEqual(125f, attribute.CurrentValue);
|
|
59
|
+
Assert.AreEqual(100f, attribute.BaseValue);
|
|
60
|
+
|
|
61
|
+
bool removed = attribute.RemoveAttributeModification(handle);
|
|
62
|
+
Assert.IsTrue(removed);
|
|
63
|
+
Assert.AreEqual(100f, attribute.CurrentValue);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
[Test]
|
|
67
|
+
public void ApplyAttributeModificationWithMultiplicationExecutesInOrder()
|
|
68
|
+
{
|
|
69
|
+
Attribute attribute = new(10f);
|
|
70
|
+
AttributeEffect effect = Track(ScriptableObject.CreateInstance<AttributeEffect>());
|
|
71
|
+
effect.name = "Stacking";
|
|
72
|
+
EffectHandle handle = EffectHandle.CreateInstance(effect);
|
|
73
|
+
|
|
74
|
+
attribute.ApplyAttributeModification(
|
|
75
|
+
new AttributeModification
|
|
76
|
+
{
|
|
77
|
+
attribute = "health",
|
|
78
|
+
action = ModificationAction.Addition,
|
|
79
|
+
value = 5f,
|
|
80
|
+
},
|
|
81
|
+
handle
|
|
82
|
+
);
|
|
83
|
+
attribute.ApplyAttributeModification(
|
|
84
|
+
new AttributeModification
|
|
85
|
+
{
|
|
86
|
+
attribute = "health",
|
|
87
|
+
action = ModificationAction.Multiplication,
|
|
88
|
+
value = 2f,
|
|
89
|
+
},
|
|
90
|
+
handle
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
Assert.AreEqual(30f, attribute.CurrentValue);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
[Test]
|
|
97
|
+
public void AttributeEqualsSupportsFloatComparisons()
|
|
98
|
+
{
|
|
99
|
+
Attribute attribute = new(7.25f);
|
|
100
|
+
Assert.IsTrue(attribute.Equals(7.25f));
|
|
101
|
+
Assert.IsTrue(attribute.Equals((double)7.25f));
|
|
102
|
+
Assert.IsFalse(attribute.Equals(7.5f));
|
|
103
|
+
Assert.AreEqual("7.25", attribute.ToString());
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[Test]
|
|
107
|
+
public void ClearCacheForcesRecalculation()
|
|
108
|
+
{
|
|
109
|
+
Attribute attribute = new(10f);
|
|
110
|
+
AttributeModification addition = new()
|
|
111
|
+
{
|
|
112
|
+
attribute = "health",
|
|
113
|
+
action = ModificationAction.Addition,
|
|
114
|
+
value = 5f,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
attribute.ApplyAttributeModification(addition);
|
|
118
|
+
Assert.AreEqual(15f, attribute.CurrentValue);
|
|
119
|
+
|
|
120
|
+
attribute.ClearCache();
|
|
121
|
+
Assert.AreEqual(15f, attribute.CurrentValue);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
[Test]
|
|
125
|
+
public void AddProducesHandleAndAppliesAddition()
|
|
126
|
+
{
|
|
127
|
+
Attribute attribute = new(10f);
|
|
128
|
+
|
|
129
|
+
EffectHandle handle = attribute.Add(5f);
|
|
130
|
+
Assert.AreEqual(15f, attribute.CurrentValue);
|
|
131
|
+
Assert.AreEqual(1L, handle.id);
|
|
132
|
+
|
|
133
|
+
bool removed = attribute.RemoveAttributeModification(handle);
|
|
134
|
+
Assert.IsTrue(removed);
|
|
135
|
+
Assert.AreEqual(10f, attribute.CurrentValue);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[Test]
|
|
139
|
+
public void SubtractStacksAsNegativeAddition()
|
|
140
|
+
{
|
|
141
|
+
Attribute attribute = new(20f);
|
|
142
|
+
|
|
143
|
+
EffectHandle addition = attribute.Add(5f);
|
|
144
|
+
EffectHandle subtraction = attribute.Subtract(8f);
|
|
145
|
+
EffectHandle multiplier = attribute.Multiply(2f);
|
|
146
|
+
|
|
147
|
+
Assert.AreEqual(34f, attribute.CurrentValue);
|
|
148
|
+
|
|
149
|
+
bool subtractionRemoved = attribute.RemoveAttributeModification(subtraction);
|
|
150
|
+
Assert.IsTrue(subtractionRemoved);
|
|
151
|
+
Assert.AreEqual(50f, attribute.CurrentValue);
|
|
152
|
+
|
|
153
|
+
attribute.RemoveAttributeModification(addition);
|
|
154
|
+
attribute.RemoveAttributeModification(multiplier);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
[Test]
|
|
158
|
+
public void DivideAppliesReciprocalMultiplication()
|
|
159
|
+
{
|
|
160
|
+
Attribute attribute = new(12f);
|
|
161
|
+
|
|
162
|
+
EffectHandle addition = attribute.Add(6f);
|
|
163
|
+
EffectHandle division = attribute.Divide(3f);
|
|
164
|
+
|
|
165
|
+
Assert.AreEqual(6f, attribute.CurrentValue);
|
|
166
|
+
|
|
167
|
+
bool divisionRemoved = attribute.RemoveAttributeModification(division);
|
|
168
|
+
Assert.IsTrue(divisionRemoved);
|
|
169
|
+
Assert.AreEqual(18f, attribute.CurrentValue);
|
|
170
|
+
|
|
171
|
+
attribute.RemoveAttributeModification(addition);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
[Test]
|
|
175
|
+
public void DivideThrowsWhenValueIsZero()
|
|
176
|
+
{
|
|
177
|
+
Attribute attribute = new(10f);
|
|
178
|
+
Assert.Throws<ArgumentException>(() => attribute.Divide(0f));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
[Test]
|
|
182
|
+
public void ArithmeticHelpersThrowWhenValueIsNotFinite()
|
|
183
|
+
{
|
|
184
|
+
Attribute attribute = new(5f);
|
|
185
|
+
|
|
186
|
+
Assert.Throws<ArgumentException>(() => attribute.Add(float.NaN));
|
|
187
|
+
Assert.Throws<ArgumentException>(() => attribute.Subtract(float.PositiveInfinity));
|
|
188
|
+
Assert.Throws<ArgumentException>(() => attribute.Multiply(float.NegativeInfinity));
|
|
189
|
+
Assert.Throws<ArgumentException>(() => attribute.Divide(float.PositiveInfinity));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -80,6 +80,112 @@ namespace WallstopStudios.UnityHelpers.Tests.Tags
|
|
|
80
80
|
Assert.IsTrue(entity.HasAnyTag(list));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
[UnityTest]
|
|
84
|
+
public IEnumerator GetActiveTagsExtensionHandlesBuffers()
|
|
85
|
+
{
|
|
86
|
+
GameObject entity = CreateTrackedGameObject("Tagged", typeof(TagHandler));
|
|
87
|
+
yield return null;
|
|
88
|
+
TagHandler handler = entity.GetComponent<TagHandler>();
|
|
89
|
+
handler.ApplyTag("Buff");
|
|
90
|
+
handler.ApplyTag("Shield");
|
|
91
|
+
|
|
92
|
+
List<string> initial = entity.GetActiveTags();
|
|
93
|
+
CollectionAssert.AreEquivalent(new[] { "Buff", "Shield" }, initial);
|
|
94
|
+
|
|
95
|
+
List<string> reusable = new() { "Sentinel" };
|
|
96
|
+
List<string> reused = entity.GetActiveTags(reusable);
|
|
97
|
+
Assert.AreSame(reusable, reused);
|
|
98
|
+
CollectionAssert.AreEquivalent(new[] { "Buff", "Shield" }, reused);
|
|
99
|
+
Assert.IsFalse(reused.Contains("Sentinel"));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
[UnityTest]
|
|
103
|
+
public IEnumerator GetHandlesWithTagExtensionCreatesBuffers()
|
|
104
|
+
{
|
|
105
|
+
GameObject entity = CreateTrackedGameObject("Entity", typeof(TestAttributesComponent));
|
|
106
|
+
yield return null;
|
|
107
|
+
EffectHandler effectHandler = entity.GetComponent<EffectHandler>();
|
|
108
|
+
|
|
109
|
+
AttributeEffect effect = CreateEffect(
|
|
110
|
+
"Buff",
|
|
111
|
+
e =>
|
|
112
|
+
{
|
|
113
|
+
e.effectTags.Add("Buff");
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
EffectHandle handle = effectHandler.ApplyEffect(effect).Value;
|
|
117
|
+
|
|
118
|
+
List<EffectHandle> handles = entity.GetHandlesWithTag("Buff");
|
|
119
|
+
Assert.AreEqual(1, handles.Count);
|
|
120
|
+
Assert.AreEqual(handle, handles[0]);
|
|
121
|
+
|
|
122
|
+
List<EffectHandle> reusable = new() { EffectHandle.CreateInstance(effect) };
|
|
123
|
+
List<EffectHandle> reused = entity.GetHandlesWithTag("Buff", reusable);
|
|
124
|
+
Assert.AreSame(reusable, reused);
|
|
125
|
+
Assert.AreEqual(1, reused.Count);
|
|
126
|
+
Assert.AreEqual(handle, reused[0]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
[Test]
|
|
130
|
+
public void AttributeEffectQueryHelpersReturnExpectedResults()
|
|
131
|
+
{
|
|
132
|
+
AttributeEffect effect = CreateEffect(
|
|
133
|
+
"Query",
|
|
134
|
+
e =>
|
|
135
|
+
{
|
|
136
|
+
e.effectTags.Add("Buff");
|
|
137
|
+
e.effectTags.Add("Shield");
|
|
138
|
+
e.modifications.Add(
|
|
139
|
+
new AttributeModification
|
|
140
|
+
{
|
|
141
|
+
attribute = nameof(TestAttributesComponent.health),
|
|
142
|
+
action = ModificationAction.Addition,
|
|
143
|
+
value = 5f,
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
e.modifications.Add(
|
|
147
|
+
new AttributeModification
|
|
148
|
+
{
|
|
149
|
+
attribute = nameof(TestAttributesComponent.armor),
|
|
150
|
+
action = ModificationAction.Addition,
|
|
151
|
+
value = 3f,
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
e.modifications.Add(
|
|
155
|
+
new AttributeModification
|
|
156
|
+
{
|
|
157
|
+
attribute = nameof(TestAttributesComponent.health),
|
|
158
|
+
action = ModificationAction.Multiplication,
|
|
159
|
+
value = 1.1f,
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
Assert.IsTrue(effect.HasTag("Buff"));
|
|
166
|
+
Assert.IsFalse(effect.HasTag("Missing"));
|
|
167
|
+
|
|
168
|
+
HashSet<string> query = new() { "Missing", "Shield" };
|
|
169
|
+
Assert.IsTrue(effect.HasAnyTag(query));
|
|
170
|
+
IReadOnlyList<string> list = new List<string> { "Other", "Buff" };
|
|
171
|
+
Assert.IsTrue(effect.HasAnyTag(list));
|
|
172
|
+
List<string> none = new() { "Missing" };
|
|
173
|
+
Assert.IsFalse(effect.HasAnyTag(none));
|
|
174
|
+
|
|
175
|
+
Assert.IsTrue(effect.ModifiesAttribute(nameof(TestAttributesComponent.health)));
|
|
176
|
+
Assert.IsTrue(effect.ModifiesAttribute(nameof(TestAttributesComponent.armor)));
|
|
177
|
+
Assert.IsFalse(effect.ModifiesAttribute("Speed"));
|
|
178
|
+
|
|
179
|
+
List<AttributeModification> buffer = new();
|
|
180
|
+
effect.GetModifications(nameof(TestAttributesComponent.health), buffer);
|
|
181
|
+
Assert.AreEqual(2, buffer.Count);
|
|
182
|
+
Assert.AreEqual(nameof(TestAttributesComponent.health), buffer[0].attribute);
|
|
183
|
+
Assert.AreEqual(nameof(TestAttributesComponent.health), buffer[1].attribute);
|
|
184
|
+
|
|
185
|
+
effect.GetModifications("Speed", buffer);
|
|
186
|
+
Assert.AreEqual(0, buffer.Count);
|
|
187
|
+
}
|
|
188
|
+
|
|
83
189
|
[UnityTest]
|
|
84
190
|
public IEnumerator ApplyEffectAddsAttributesCosmeticsAndTags()
|
|
85
191
|
{
|
|
@@ -370,6 +476,145 @@ namespace WallstopStudios.UnityHelpers.Tests.Tags
|
|
|
370
476
|
Assert.IsFalse(entity.HasTag("Buff"));
|
|
371
477
|
}
|
|
372
478
|
|
|
479
|
+
[UnityTest]
|
|
480
|
+
public IEnumerator ExtensionHelpersBridgeTagAndEffectQueries()
|
|
481
|
+
{
|
|
482
|
+
GameObject entity = CreateTrackedGameObject("Entity", typeof(TestAttributesComponent));
|
|
483
|
+
yield return null;
|
|
484
|
+
|
|
485
|
+
AttributeEffect effect = CreateEffect(
|
|
486
|
+
"Utility",
|
|
487
|
+
e =>
|
|
488
|
+
{
|
|
489
|
+
e.duration = 0.3f;
|
|
490
|
+
e.resetDurationOnReapplication = true;
|
|
491
|
+
e.effectTags.Add("Buff");
|
|
492
|
+
e.modifications.Add(
|
|
493
|
+
new AttributeModification
|
|
494
|
+
{
|
|
495
|
+
attribute = nameof(TestAttributesComponent.health),
|
|
496
|
+
action = ModificationAction.Addition,
|
|
497
|
+
value = 5f,
|
|
498
|
+
}
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
);
|
|
502
|
+
|
|
503
|
+
List<string> tagsBuffer = new();
|
|
504
|
+
List<EffectHandle> handlesBuffer = new();
|
|
505
|
+
|
|
506
|
+
Assert.IsFalse(entity.HasAllTags(new[] { "Buff" }));
|
|
507
|
+
EffectHandle? handle = entity.ApplyEffect(effect);
|
|
508
|
+
Assert.IsTrue(handle.HasValue);
|
|
509
|
+
|
|
510
|
+
Assert.IsTrue(entity.HasAllTags(new[] { "Buff" }));
|
|
511
|
+
Assert.IsFalse(entity.HasAllTags(new[] { "Buff", "Missing" }));
|
|
512
|
+
Assert.IsTrue(entity.HasNoneOfTags(new[] { "Missing" }));
|
|
513
|
+
List<string> none = new() { "Buff" };
|
|
514
|
+
Assert.IsFalse(entity.HasNoneOfTags(none));
|
|
515
|
+
|
|
516
|
+
Assert.IsTrue(entity.TryGetTagCount("Buff", out int tagCount));
|
|
517
|
+
Assert.AreEqual(1, tagCount);
|
|
518
|
+
|
|
519
|
+
Assert.AreNotEqual(0, entity.GetActiveTags(tagsBuffer).Count);
|
|
520
|
+
CollectionAssert.Contains(tagsBuffer, "Buff");
|
|
521
|
+
|
|
522
|
+
Assert.AreNotEqual(0, entity.GetHandlesWithTag("Buff", handlesBuffer).Count);
|
|
523
|
+
Assert.AreEqual(1, handlesBuffer.Count);
|
|
524
|
+
|
|
525
|
+
Assert.IsTrue(entity.IsEffectActive(effect));
|
|
526
|
+
Assert.AreEqual(1, entity.GetEffectStackCount(effect));
|
|
527
|
+
|
|
528
|
+
List<EffectHandle> activeEffects = entity.GetActiveEffects(new List<EffectHandle>());
|
|
529
|
+
Assert.AreEqual(1, activeEffects.Count);
|
|
530
|
+
|
|
531
|
+
EffectHandle activeHandle = handlesBuffer[0];
|
|
532
|
+
Assert.IsTrue(entity.TryGetRemainingDuration(activeHandle, out float remaining));
|
|
533
|
+
Assert.Greater(remaining, 0f);
|
|
534
|
+
|
|
535
|
+
yield return null;
|
|
536
|
+
Assert.IsTrue(entity.TryGetRemainingDuration(activeHandle, out float beforeEnsure));
|
|
537
|
+
|
|
538
|
+
EffectHandle? ensured = entity.EnsureHandle(effect);
|
|
539
|
+
Assert.IsTrue(ensured.HasValue);
|
|
540
|
+
Assert.AreEqual(activeHandle, ensured.Value);
|
|
541
|
+
Assert.IsTrue(entity.TryGetRemainingDuration(activeHandle, out float afterEnsure));
|
|
542
|
+
Assert.Greater(afterEnsure, beforeEnsure);
|
|
543
|
+
|
|
544
|
+
yield return null;
|
|
545
|
+
Assert.IsTrue(entity.TryGetRemainingDuration(activeHandle, out float beforeNoRefresh));
|
|
546
|
+
EffectHandle? ensuredNoRefresh = entity.EnsureHandle(effect, refreshDuration: false);
|
|
547
|
+
Assert.IsTrue(ensuredNoRefresh.HasValue);
|
|
548
|
+
Assert.AreEqual(activeHandle, ensuredNoRefresh.Value);
|
|
549
|
+
Assert.IsTrue(entity.TryGetRemainingDuration(activeHandle, out float afterNoRefresh));
|
|
550
|
+
Assert.LessOrEqual(afterNoRefresh, beforeNoRefresh);
|
|
551
|
+
|
|
552
|
+
Assert.IsTrue(entity.RefreshEffect(activeHandle));
|
|
553
|
+
|
|
554
|
+
entity.RemoveAllEffects();
|
|
555
|
+
tagsBuffer.Clear();
|
|
556
|
+
Assert.AreEqual(0, entity.GetActiveTags(tagsBuffer).Count);
|
|
557
|
+
Assert.IsEmpty(tagsBuffer);
|
|
558
|
+
handlesBuffer.Clear();
|
|
559
|
+
Assert.AreEqual(0, entity.GetHandlesWithTag("Buff", handlesBuffer).Count);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
[UnityTest]
|
|
563
|
+
public IEnumerator ApplyEffectsNoAllocAppendsToProvidedHandlesBuffer()
|
|
564
|
+
{
|
|
565
|
+
GameObject entity = CreateTrackedGameObject("Entity", typeof(TestAttributesComponent));
|
|
566
|
+
yield return null;
|
|
567
|
+
|
|
568
|
+
List<AttributeEffect> effects = new() { CreateEffect("BuffA"), CreateEffect("BuffB") };
|
|
569
|
+
|
|
570
|
+
EffectHandle sentinel = EffectHandle.CreateInstance(CreateEffect("Sentinel"));
|
|
571
|
+
List<EffectHandle> handles = new() { sentinel };
|
|
572
|
+
entity.ApplyEffectsNoAlloc(effects, handles);
|
|
573
|
+
|
|
574
|
+
Assert.AreEqual(effects.Count + 1, handles.Count);
|
|
575
|
+
Assert.AreEqual(sentinel, handles[0]);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
[UnityTest]
|
|
579
|
+
public IEnumerator GetActiveEffectsClearsBufferBeforePopulation()
|
|
580
|
+
{
|
|
581
|
+
GameObject entity = CreateTrackedGameObject("Entity", typeof(TestAttributesComponent));
|
|
582
|
+
yield return null;
|
|
583
|
+
EffectHandler handler = entity.GetComponent<EffectHandler>();
|
|
584
|
+
|
|
585
|
+
AttributeEffect effect = CreateEffect("Tracked");
|
|
586
|
+
EffectHandle handle = handler.ApplyEffect(effect).Value;
|
|
587
|
+
|
|
588
|
+
EffectHandle sentinel = EffectHandle.CreateInstance(CreateEffect("Sentinel"));
|
|
589
|
+
List<EffectHandle> buffer = new() { sentinel };
|
|
590
|
+
List<EffectHandle> populated = entity.GetActiveEffects(buffer);
|
|
591
|
+
Assert.AreSame(buffer, populated);
|
|
592
|
+
Assert.AreEqual(1, populated.Count);
|
|
593
|
+
Assert.AreEqual(handle, populated[0]);
|
|
594
|
+
Assert.AreNotEqual(sentinel, populated[0]);
|
|
595
|
+
yield return null;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
[UnityTest]
|
|
599
|
+
public IEnumerator RefreshEffectIgnorePolicyExtensionRefreshesWhenDisallowed()
|
|
600
|
+
{
|
|
601
|
+
GameObject entity = CreateTrackedGameObject("Entity", typeof(TestAttributesComponent));
|
|
602
|
+
yield return null;
|
|
603
|
+
AttributeEffect effect = CreateEffect(
|
|
604
|
+
"Policy",
|
|
605
|
+
e =>
|
|
606
|
+
{
|
|
607
|
+
e.duration = 0.2f;
|
|
608
|
+
e.resetDurationOnReapplication = false;
|
|
609
|
+
}
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
EffectHandle handle = entity.ApplyEffect(effect).Value;
|
|
613
|
+
yield return null;
|
|
614
|
+
Assert.IsFalse(entity.RefreshEffect(handle));
|
|
615
|
+
Assert.IsTrue(entity.RefreshEffect(handle, ignoreReapplicationPolicy: true));
|
|
616
|
+
}
|
|
617
|
+
|
|
373
618
|
[UnityTest]
|
|
374
619
|
public IEnumerator ApplyEffectsNoAllocSkipsNullTarget()
|
|
375
620
|
{
|
|
@@ -111,7 +111,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Tags
|
|
|
111
111
|
yield return null;
|
|
112
112
|
Assert.IsFalse(collider.enabled);
|
|
113
113
|
|
|
114
|
-
handler.RemoveTag(CollisionSenses.CollisionDisabledTag
|
|
114
|
+
handler.RemoveTag(CollisionSenses.CollisionDisabledTag);
|
|
115
115
|
yield return null;
|
|
116
116
|
Assert.IsTrue(collider.enabled);
|
|
117
117
|
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
namespace WallstopStudios.UnityHelpers.Tests.Tags
|
|
2
|
+
{
|
|
3
|
+
using System.Collections;
|
|
4
|
+
using NUnit.Framework;
|
|
5
|
+
using UnityEngine;
|
|
6
|
+
using UnityEngine.TestTools;
|
|
7
|
+
using WallstopStudios.UnityHelpers.Tags;
|
|
8
|
+
using WallstopStudios.UnityHelpers.Tests.Tags.Helpers;
|
|
9
|
+
|
|
10
|
+
[TestFixture]
|
|
11
|
+
public sealed class EffectBehaviorTests : TagsTestBase
|
|
12
|
+
{
|
|
13
|
+
[SetUp]
|
|
14
|
+
public void SetUp()
|
|
15
|
+
{
|
|
16
|
+
ResetEffectHandleId();
|
|
17
|
+
RecordingEffectBehavior.Reset();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
[UnityTest]
|
|
21
|
+
public IEnumerator LifecycleCallbacksProvideContextData()
|
|
22
|
+
{
|
|
23
|
+
(GameObject entity, EffectHandler handler, _, _) = CreateEntity();
|
|
24
|
+
|
|
25
|
+
PeriodicEffectDefinition periodicDefinition = new()
|
|
26
|
+
{
|
|
27
|
+
name = "Pulse",
|
|
28
|
+
initialDelay = 0f,
|
|
29
|
+
interval = 0.05f,
|
|
30
|
+
maxTicks = 1,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
AttributeEffect effect = CreateEffect(
|
|
34
|
+
"Lifecycle",
|
|
35
|
+
e =>
|
|
36
|
+
{
|
|
37
|
+
e.periodicEffects.Add(periodicDefinition);
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
RecordingEffectBehavior behavior = Track(
|
|
42
|
+
ScriptableObject.CreateInstance<RecordingEffectBehavior>()
|
|
43
|
+
);
|
|
44
|
+
effect.behaviors.Add(behavior);
|
|
45
|
+
|
|
46
|
+
EffectHandle handle = handler.ApplyEffect(effect).Value;
|
|
47
|
+
Assert.AreEqual(1, RecordingEffectBehavior.ApplyCount);
|
|
48
|
+
Assert.AreEqual(
|
|
49
|
+
1,
|
|
50
|
+
RecordingEffectBehavior.ApplyContexts.Count,
|
|
51
|
+
"OnApply should fire immediately."
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
yield return null;
|
|
55
|
+
yield return null;
|
|
56
|
+
|
|
57
|
+
Assert.IsNotEmpty(
|
|
58
|
+
RecordingEffectBehavior.TickContexts,
|
|
59
|
+
"OnTick should run after Update."
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
yield return new WaitForSeconds(0.08f);
|
|
63
|
+
|
|
64
|
+
Assert.AreEqual(
|
|
65
|
+
1,
|
|
66
|
+
RecordingEffectBehavior.PeriodicInvocations.Count,
|
|
67
|
+
"Expected one periodic tick."
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
int removeCountBefore = RecordingEffectBehavior.RemoveCount;
|
|
71
|
+
handler.RemoveEffect(handle);
|
|
72
|
+
Assert.AreEqual(removeCountBefore + 1, RecordingEffectBehavior.RemoveCount);
|
|
73
|
+
Assert.AreEqual(
|
|
74
|
+
1,
|
|
75
|
+
RecordingEffectBehavior.RemoveContexts.Count,
|
|
76
|
+
"OnRemove should fire once."
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
EffectBehaviorContext applyContext = RecordingEffectBehavior.ApplyContexts[0];
|
|
80
|
+
Assert.AreSame(handler, applyContext.handler);
|
|
81
|
+
Assert.AreSame(entity, applyContext.Target);
|
|
82
|
+
Assert.AreEqual(effect, applyContext.Effect);
|
|
83
|
+
Assert.AreEqual(0f, applyContext.deltaTime);
|
|
84
|
+
|
|
85
|
+
EffectBehaviorContext tickContext = RecordingEffectBehavior.TickContexts[0];
|
|
86
|
+
Assert.AreSame(handler, tickContext.handler);
|
|
87
|
+
Assert.AreSame(entity, tickContext.Target);
|
|
88
|
+
Assert.AreEqual(effect, tickContext.Effect);
|
|
89
|
+
Assert.Greater(tickContext.deltaTime, 0f);
|
|
90
|
+
|
|
91
|
+
RecordingEffectBehavior.PeriodicInvocation periodicInvocation =
|
|
92
|
+
RecordingEffectBehavior.PeriodicInvocations[0];
|
|
93
|
+
Assert.AreSame(handler, periodicInvocation.Context.handler);
|
|
94
|
+
Assert.AreSame(entity, periodicInvocation.Context.Target);
|
|
95
|
+
Assert.AreEqual(effect, periodicInvocation.Context.Effect);
|
|
96
|
+
Assert.Greater(periodicInvocation.Context.deltaTime, 0f);
|
|
97
|
+
Assert.AreSame(periodicDefinition, periodicInvocation.TickContext.definition);
|
|
98
|
+
Assert.AreEqual(1, periodicInvocation.TickContext.executedTicks);
|
|
99
|
+
Assert.GreaterOrEqual(periodicInvocation.TickContext.currentTime, 0f);
|
|
100
|
+
|
|
101
|
+
EffectBehaviorContext removeContext = RecordingEffectBehavior.RemoveContexts[0];
|
|
102
|
+
Assert.AreSame(handler, removeContext.handler);
|
|
103
|
+
Assert.AreSame(entity, removeContext.Target);
|
|
104
|
+
Assert.AreEqual(effect, removeContext.Effect);
|
|
105
|
+
Assert.AreEqual(0f, removeContext.deltaTime);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
[UnityTest]
|
|
109
|
+
public IEnumerator PeriodicTickContextTracksExecutedTicksAndTime()
|
|
110
|
+
{
|
|
111
|
+
(GameObject entity, EffectHandler handler, _, _) = CreateEntity();
|
|
112
|
+
|
|
113
|
+
PeriodicEffectDefinition periodicDefinition = new()
|
|
114
|
+
{
|
|
115
|
+
name = "Stacking Pulse",
|
|
116
|
+
initialDelay = 0f,
|
|
117
|
+
interval = 0.05f,
|
|
118
|
+
maxTicks = 3,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
AttributeEffect effect = CreateEffect(
|
|
122
|
+
"Periodic",
|
|
123
|
+
e =>
|
|
124
|
+
{
|
|
125
|
+
e.periodicEffects.Add(periodicDefinition);
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
RecordingEffectBehavior behavior = Track(
|
|
130
|
+
ScriptableObject.CreateInstance<RecordingEffectBehavior>()
|
|
131
|
+
);
|
|
132
|
+
effect.behaviors.Add(behavior);
|
|
133
|
+
|
|
134
|
+
EffectHandle handle = handler.ApplyEffect(effect).Value;
|
|
135
|
+
|
|
136
|
+
yield return new WaitForSeconds(0.18f);
|
|
137
|
+
|
|
138
|
+
Assert.AreEqual(
|
|
139
|
+
3,
|
|
140
|
+
RecordingEffectBehavior.PeriodicInvocations.Count,
|
|
141
|
+
"Expected periodic callbacks for each executed tick."
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
for (int i = 0; i < RecordingEffectBehavior.PeriodicInvocations.Count; ++i)
|
|
145
|
+
{
|
|
146
|
+
RecordingEffectBehavior.PeriodicInvocation invocation =
|
|
147
|
+
RecordingEffectBehavior.PeriodicInvocations[i];
|
|
148
|
+
Assert.AreSame(periodicDefinition, invocation.TickContext.definition);
|
|
149
|
+
Assert.AreEqual(i + 1, invocation.TickContext.executedTicks);
|
|
150
|
+
Assert.Greater(invocation.Context.deltaTime, 0f);
|
|
151
|
+
|
|
152
|
+
if (i > 0)
|
|
153
|
+
{
|
|
154
|
+
float previousTime = RecordingEffectBehavior
|
|
155
|
+
.PeriodicInvocations[i - 1]
|
|
156
|
+
.TickContext
|
|
157
|
+
.currentTime;
|
|
158
|
+
Assert.GreaterOrEqual(invocation.TickContext.currentTime, previousTime);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
handler.RemoveEffect(handle);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private (
|
|
166
|
+
GameObject entity,
|
|
167
|
+
EffectHandler handler,
|
|
168
|
+
TestAttributesComponent attributes,
|
|
169
|
+
TagHandler tags
|
|
170
|
+
) CreateEntity()
|
|
171
|
+
{
|
|
172
|
+
GameObject entity = CreateTrackedGameObject(
|
|
173
|
+
"EffectBehaviorEntity",
|
|
174
|
+
typeof(TestAttributesComponent)
|
|
175
|
+
);
|
|
176
|
+
return (
|
|
177
|
+
entity,
|
|
178
|
+
entity.GetComponent<EffectHandler>(),
|
|
179
|
+
entity.GetComponent<TestAttributesComponent>(),
|
|
180
|
+
entity.GetComponent<TagHandler>()
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|