com.wallstop-studios.unity-helpers 2.0.4 → 2.1.1
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/ILIST_SORTING_PERFORMANCE.md +92 -0
- package/Docs/ILIST_SORTING_PERFORMANCE.md.meta +7 -0
- package/Docs/INDEX.md +10 -1
- package/Docs/Images/random_generators.svg +7 -7
- package/Docs/RANDOM_PERFORMANCE.md +18 -15
- 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_2D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_SEMANTICS.md +7 -7
- package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
- package/Editor/CustomDrawers/WShowIfPropertyDrawer.cs +131 -41
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +175 -18
- package/README.md +42 -18
- package/Runtime/Core/Extension/IListExtensions.cs +720 -12
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
- package/Runtime/Core/Random/AbstractRandom.cs +52 -5
- package/Runtime/Core/Random/DotNetRandom.cs +3 -3
- package/Runtime/Core/Random/FlurryBurstRandom.cs +285 -0
- package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
- package/Runtime/Core/Random/IllusionFlow.cs +3 -3
- package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
- package/Runtime/Core/Random/PcgRandom.cs +6 -6
- package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
- package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
- package/Runtime/Core/Random/RomuDuo.cs +3 -3
- package/Runtime/Core/Random/SplitMix64.cs +3 -3
- package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
- package/Runtime/Core/Random/StormDropRandom.cs +271 -0
- package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
- package/Runtime/Core/Random/UnityRandom.cs +3 -3
- package/Runtime/Core/Random/WyRandom.cs +6 -4
- package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
- package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
- package/Runtime/Tags/Attribute.cs +144 -24
- package/Runtime/Tags/AttributeEffect.cs +119 -16
- package/Runtime/Tags/AttributeMetadataCache.cs +312 -3
- 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/Tags/AttributeMetadataCacheTests.cs +192 -0
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
- package/{node_modules.meta → Tests/Editor/Tags.meta} +1 -1
- package/Tests/Editor/Utils/ScriptableObjectSingletonTests.cs +41 -0
- package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
- package/Tests/Runtime/Helper/ObjectsTests.cs +3 -3
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
- package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
- package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
- package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Serialization/JsonSerializationTest.cs +4 -3
- package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
- package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
- package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
- 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/Tests/Runtime/Tags/{AttributeDataTests.cs.meta → AttributeModificationTests.cs.meta} +0 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
namespace WallstopStudios.UnityHelpers.Core.Random
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Runtime.Serialization;
|
|
5
|
+
using System.Text.Json.Serialization;
|
|
6
|
+
using ProtoBuf;
|
|
7
|
+
using WallstopStudios.UnityHelpers.Core.Extension;
|
|
8
|
+
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
9
|
+
using WallstopStudios.UnityHelpers.Utils;
|
|
10
|
+
|
|
11
|
+
/// <summary>
|
|
12
|
+
/// StormDrop32: a large-state ARX generator inspired by SHISHUA-style buffer mixing, emphasizing long periods and diffusion.
|
|
13
|
+
/// </summary>
|
|
14
|
+
/// <remarks>
|
|
15
|
+
/// <para>
|
|
16
|
+
/// https://github.com/wileylooper/stormdrop
|
|
17
|
+
/// Ported from <c>wileylooper/stormdrop</c>. The 32-bit variant maintains a 1024-element ring buffer and two 32-bit
|
|
18
|
+
/// accumulators. Each step mixes the current index with the accumulators, rotates, and feeds the buffer to provide
|
|
19
|
+
/// high-quality sequences suitable for heavy simulation workloads.
|
|
20
|
+
/// </para>
|
|
21
|
+
/// <para>Pros:</para>
|
|
22
|
+
/// <list type="bullet">
|
|
23
|
+
/// <item><description>Large period and strong diffusion thanks to the 4 KB buffer.</description></item>
|
|
24
|
+
/// <item><description>Deterministic snapshots via <see cref="RandomState"/>.</description></item>
|
|
25
|
+
/// <item><description>Thread-local access available via <see cref="ThreadLocalRandom{T}.Instance"/>.</description></item>
|
|
26
|
+
/// </list>
|
|
27
|
+
/// <para>Cons:</para>
|
|
28
|
+
/// <list type="bullet">
|
|
29
|
+
/// <item><description>Higher per-instance memory compared to smaller generators.</description></item>
|
|
30
|
+
/// <item><description>Not cryptographically secure.</description></item>
|
|
31
|
+
/// </list>
|
|
32
|
+
/// <para>When to use:</para>
|
|
33
|
+
/// <list type="bullet">
|
|
34
|
+
/// <item><description>Procedural workloads needing long non-overlapping streams or large batches.</description></item>
|
|
35
|
+
/// </list>
|
|
36
|
+
/// <para>When not to use:</para>
|
|
37
|
+
/// <list type="bullet">
|
|
38
|
+
/// <item><description>Memory-constrained contexts; prefer smaller-state generators like FlurryBurst.</description></item>
|
|
39
|
+
/// <item><description>Security/adversarial scenarios.</description></item>
|
|
40
|
+
/// </list>
|
|
41
|
+
/// </remarks>
|
|
42
|
+
/// <example>
|
|
43
|
+
/// <code>
|
|
44
|
+
/// using WallstopStudios.UnityHelpers.Core.Random;
|
|
45
|
+
///
|
|
46
|
+
/// StormDropRandom rng = new StormDropRandom(seed: 42u);
|
|
47
|
+
/// float noise = rng.NextFloat();
|
|
48
|
+
/// Vector3 point = rng.NextVector3InSphere(10f); // via RandomExtensions
|
|
49
|
+
/// </code>
|
|
50
|
+
/// </example>
|
|
51
|
+
[Serializable]
|
|
52
|
+
[DataContract]
|
|
53
|
+
[ProtoContract(SkipConstructor = true)]
|
|
54
|
+
public sealed class StormDropRandom
|
|
55
|
+
: AbstractRandom,
|
|
56
|
+
IEquatable<StormDropRandom>,
|
|
57
|
+
IComparable,
|
|
58
|
+
IComparable<StormDropRandom>
|
|
59
|
+
{
|
|
60
|
+
private const uint Increment = 1_111_111_111U;
|
|
61
|
+
private const int ElementCount = 1024;
|
|
62
|
+
private const int ElementMask = ElementCount - 1;
|
|
63
|
+
private const int ElementByteSize = ElementCount * sizeof(uint);
|
|
64
|
+
private const int WarmupRounds = 128;
|
|
65
|
+
|
|
66
|
+
public static StormDropRandom Instance => ThreadLocalRandom<StormDropRandom>.Instance;
|
|
67
|
+
|
|
68
|
+
public override RandomState InternalState
|
|
69
|
+
{
|
|
70
|
+
get
|
|
71
|
+
{
|
|
72
|
+
using PooledResource<byte[]> payloadLease = WallstopArrayPool<byte>.Get(
|
|
73
|
+
ElementByteSize,
|
|
74
|
+
out byte[] buffer
|
|
75
|
+
);
|
|
76
|
+
Buffer.BlockCopy(_elements, 0, buffer, 0, ElementByteSize);
|
|
77
|
+
|
|
78
|
+
ulong state1 = ((ulong)_a << 32) | _b;
|
|
79
|
+
return BuildState(
|
|
80
|
+
state1,
|
|
81
|
+
payload: new ArraySegment<byte>(buffer, 0, ElementByteSize)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
[ProtoMember(6)]
|
|
87
|
+
private uint[] _elements = new uint[ElementCount];
|
|
88
|
+
|
|
89
|
+
[ProtoMember(7)]
|
|
90
|
+
private uint _a;
|
|
91
|
+
|
|
92
|
+
[ProtoMember(8)]
|
|
93
|
+
private uint _b;
|
|
94
|
+
|
|
95
|
+
public StormDropRandom()
|
|
96
|
+
: this(Guid.NewGuid()) { }
|
|
97
|
+
|
|
98
|
+
public StormDropRandom(Guid guid)
|
|
99
|
+
{
|
|
100
|
+
InitializeFromGuid(guid);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public StormDropRandom(uint seed)
|
|
104
|
+
{
|
|
105
|
+
uint seedB = seed ^ 0x9E3779B9U;
|
|
106
|
+
if (seedB == 0)
|
|
107
|
+
{
|
|
108
|
+
seedB = 1U;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
InitializeFromScalars(seed, seedB);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public StormDropRandom(uint seedA, uint seedB)
|
|
115
|
+
{
|
|
116
|
+
InitializeFromScalars(seedA, seedB == 0 ? 1U : seedB);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
[JsonConstructor]
|
|
120
|
+
public StormDropRandom(RandomState internalState)
|
|
121
|
+
{
|
|
122
|
+
_a = (uint)(internalState.State1 >> 32);
|
|
123
|
+
_b = (uint)internalState.State1;
|
|
124
|
+
LoadSerializedElements(internalState._payload);
|
|
125
|
+
RestoreCommonState(internalState);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public override uint NextUint()
|
|
129
|
+
{
|
|
130
|
+
unchecked
|
|
131
|
+
{
|
|
132
|
+
uint index = _b & ElementMask;
|
|
133
|
+
uint mix = (_elements[index] ^ _a) + _b;
|
|
134
|
+
|
|
135
|
+
_a = RotateLeft(_a, 17) ^ _b;
|
|
136
|
+
_b += Increment;
|
|
137
|
+
|
|
138
|
+
_elements[_b & ElementMask] += RotateLeft(mix, 13);
|
|
139
|
+
|
|
140
|
+
return mix;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public override IRandom Copy()
|
|
145
|
+
{
|
|
146
|
+
return new StormDropRandom(InternalState);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
public bool Equals(StormDropRandom other)
|
|
150
|
+
{
|
|
151
|
+
if (other == null)
|
|
152
|
+
{
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (_a != other._a || _b != other._b)
|
|
157
|
+
{
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!_elements.AsSpan().SequenceEqual(other._elements))
|
|
162
|
+
{
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return _cachedGaussian == other._cachedGaussian;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public override bool Equals(object obj)
|
|
170
|
+
{
|
|
171
|
+
return Equals(obj as StormDropRandom);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public int CompareTo(object obj)
|
|
175
|
+
{
|
|
176
|
+
return CompareTo(obj as StormDropRandom);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
public int CompareTo(StormDropRandom other)
|
|
180
|
+
{
|
|
181
|
+
if (other == null)
|
|
182
|
+
{
|
|
183
|
+
return -1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
int comparison = _a.CompareTo(other._a);
|
|
187
|
+
if (comparison != 0)
|
|
188
|
+
{
|
|
189
|
+
return comparison;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
comparison = _b.CompareTo(other._b);
|
|
193
|
+
if (comparison != 0)
|
|
194
|
+
{
|
|
195
|
+
return comparison;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (int i = 0; i < ElementCount; ++i)
|
|
199
|
+
{
|
|
200
|
+
comparison = _elements[i].CompareTo(other._elements[i]);
|
|
201
|
+
if (comparison != 0)
|
|
202
|
+
{
|
|
203
|
+
return comparison;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public override int GetHashCode()
|
|
211
|
+
{
|
|
212
|
+
return Objects.HashCode(_a, _b);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public override string ToString()
|
|
216
|
+
{
|
|
217
|
+
return this.ToJson();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private void InitializeFromGuid(Guid guid)
|
|
221
|
+
{
|
|
222
|
+
(ulong seed0, ulong seed1) = RandomUtilities.GuidToUInt64Pair(guid);
|
|
223
|
+
ulong mixer = seed0 ^ (seed1 << 1) ^ 0x9E3779B97F4A7C15UL;
|
|
224
|
+
InitializeFromMixer(ref mixer);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private void InitializeFromScalars(uint seedA, uint seedB)
|
|
228
|
+
{
|
|
229
|
+
ulong mixer = ((ulong)seedA << 32) | seedB;
|
|
230
|
+
mixer ^= 0xD2B74407B1CE6E93UL;
|
|
231
|
+
InitializeFromMixer(ref mixer);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private void InitializeFromMixer(ref ulong mixer)
|
|
235
|
+
{
|
|
236
|
+
if (_elements == null || _elements.Length != ElementCount)
|
|
237
|
+
{
|
|
238
|
+
_elements = new uint[ElementCount];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for (int i = 0; i < ElementCount; ++i)
|
|
242
|
+
{
|
|
243
|
+
_elements[i] = Mix32(ref mixer);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
_a = Mix32(ref mixer);
|
|
247
|
+
_b = Mix32(ref mixer) | 1U;
|
|
248
|
+
|
|
249
|
+
for (int i = 0; i < WarmupRounds; ++i)
|
|
250
|
+
{
|
|
251
|
+
_ = NextUint();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private void LoadSerializedElements(byte[] payload)
|
|
256
|
+
{
|
|
257
|
+
if (_elements == null || _elements.Length != ElementCount)
|
|
258
|
+
{
|
|
259
|
+
_elements = new uint[ElementCount];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (payload != null && payload.Length >= ElementByteSize)
|
|
263
|
+
{
|
|
264
|
+
Buffer.BlockCopy(payload, 0, _elements, 0, ElementByteSize);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
Array.Clear(_elements, 0, _elements.Length);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -5,9 +5,6 @@ namespace WallstopStudios.UnityHelpers.Core.Random
|
|
|
5
5
|
using System.Text.Json.Serialization;
|
|
6
6
|
using ProtoBuf;
|
|
7
7
|
|
|
8
|
-
[Serializable]
|
|
9
|
-
[DataContract]
|
|
10
|
-
[ProtoContract]
|
|
11
8
|
/// <summary>
|
|
12
9
|
/// An adapter over <c>UnityEngine.Random</c> exposing the <see cref="IRandom"/> interface.
|
|
13
10
|
/// </summary>
|
|
@@ -47,6 +44,9 @@ namespace WallstopStudios.UnityHelpers.Core.Random
|
|
|
47
44
|
/// // Note: calling UnityEngine.Random elsewhere will affect this sequence.
|
|
48
45
|
/// </code>
|
|
49
46
|
/// </example>
|
|
47
|
+
[Serializable]
|
|
48
|
+
[DataContract]
|
|
49
|
+
[ProtoContract]
|
|
50
50
|
public sealed class UnityRandom : AbstractRandom
|
|
51
51
|
{
|
|
52
52
|
public static readonly UnityRandom Instance = new();
|
|
@@ -7,15 +7,14 @@ namespace WallstopStudios.UnityHelpers.Core.Random
|
|
|
7
7
|
using System.Text.Json.Serialization;
|
|
8
8
|
using ProtoBuf;
|
|
9
9
|
|
|
10
|
-
// https://github.com/cocowalla/wyhash-dotnet/blob/master/src/WyHash/WyRng.cs
|
|
11
|
-
[Serializable]
|
|
12
|
-
[DataContract]
|
|
13
|
-
[ProtoContract(SkipConstructor = true)]
|
|
14
10
|
/// <summary>
|
|
15
11
|
/// A wyhash-inspired PRNG variant (WyRandom) leveraging multiply-mix operations for speed and good distribution.
|
|
16
12
|
/// </summary>
|
|
17
13
|
/// <remarks>
|
|
18
14
|
/// <para>
|
|
15
|
+
/// Reference implementation: https://github.com/cocowalla/wyhash-dotnet/blob/master/src/WyHash/WyRng.cs
|
|
16
|
+
/// </para>
|
|
17
|
+
/// <para>
|
|
19
18
|
/// Designed around 64-bit multiply-and-mix steps, this generator is fast and suitable for general-purpose
|
|
20
19
|
/// randomness and hashing-like use cases. It is not a cryptographic hash nor a CSPRNG.
|
|
21
20
|
/// </para>
|
|
@@ -45,6 +44,9 @@ namespace WallstopStudios.UnityHelpers.Core.Random
|
|
|
45
44
|
/// var color = rng.NextColor(); // via RandomExtensions
|
|
46
45
|
/// </code>
|
|
47
46
|
/// </example>
|
|
47
|
+
[Serializable]
|
|
48
|
+
[DataContract]
|
|
49
|
+
[ProtoContract(SkipConstructor = true)]
|
|
48
50
|
public sealed class WyRandom : AbstractRandom
|
|
49
51
|
{
|
|
50
52
|
private const ulong Prime0 = 0xa0761d6478bd642f;
|
|
@@ -5,9 +5,6 @@ namespace WallstopStudios.UnityHelpers.Core.Random
|
|
|
5
5
|
using System.Text.Json.Serialization;
|
|
6
6
|
using ProtoBuf;
|
|
7
7
|
|
|
8
|
-
[Serializable]
|
|
9
|
-
[DataContract]
|
|
10
|
-
[ProtoContract]
|
|
11
8
|
/// <summary>
|
|
12
9
|
/// A classic, extremely fast XorShift PRNG with small state and modest quality.
|
|
13
10
|
/// </summary>
|
|
@@ -53,6 +50,9 @@ namespace WallstopStudios.UnityHelpers.Core.Random
|
|
|
53
50
|
/// var fast = XorShiftRandom.Instance; // per-thread instance
|
|
54
51
|
/// </code>
|
|
55
52
|
/// </example>
|
|
53
|
+
[Serializable]
|
|
54
|
+
[DataContract]
|
|
55
|
+
[ProtoContract]
|
|
56
56
|
public sealed class XorShiftRandom : AbstractRandom
|
|
57
57
|
{
|
|
58
58
|
public static XorShiftRandom Instance => ThreadLocalRandom<XorShiftRandom>.Instance;
|
|
@@ -8,9 +8,6 @@ namespace WallstopStudios.UnityHelpers.Core.Random
|
|
|
8
8
|
using Helper;
|
|
9
9
|
using ProtoBuf;
|
|
10
10
|
|
|
11
|
-
[Serializable]
|
|
12
|
-
[DataContract]
|
|
13
|
-
[ProtoContract]
|
|
14
11
|
/// <summary>
|
|
15
12
|
/// A fast 128-bit state Xoroshiro-based PRNG with good quality and tiny footprint.
|
|
16
13
|
/// </summary>
|
|
@@ -55,6 +52,9 @@ namespace WallstopStudios.UnityHelpers.Core.Random
|
|
|
55
52
|
/// var replay = new XoroShiroRandom(state);
|
|
56
53
|
/// </code>
|
|
57
54
|
/// </example>
|
|
55
|
+
[Serializable]
|
|
56
|
+
[DataContract]
|
|
57
|
+
[ProtoContract]
|
|
58
58
|
public sealed class XoroShiroRandom
|
|
59
59
|
: AbstractRandom,
|
|
60
60
|
IEquatable<XoroShiroRandom>,
|
|
@@ -4,6 +4,7 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
4
4
|
using System.Collections.Generic;
|
|
5
5
|
using System.ComponentModel;
|
|
6
6
|
using System.Globalization;
|
|
7
|
+
using System.Runtime.CompilerServices;
|
|
7
8
|
using System.Runtime.Serialization;
|
|
8
9
|
using System.Text.Json.Serialization;
|
|
9
10
|
using Core.Extension;
|
|
@@ -40,7 +41,6 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
40
41
|
/// </para>
|
|
41
42
|
/// </remarks>
|
|
42
43
|
[Serializable]
|
|
43
|
-
[ProtoContract]
|
|
44
44
|
public sealed class Attribute
|
|
45
45
|
: IEquatable<Attribute>,
|
|
46
46
|
IEquatable<float>,
|
|
@@ -91,6 +91,38 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
91
91
|
|
|
92
92
|
private bool _currentValueCalculated;
|
|
93
93
|
|
|
94
|
+
private readonly Dictionary<EffectHandle, List<AttributeModification>> _modifications =
|
|
95
|
+
new();
|
|
96
|
+
|
|
97
|
+
/// <summary>
|
|
98
|
+
/// Initializes a new instance of the <see cref="Attribute"/> class with a base value of 0.
|
|
99
|
+
/// </summary>
|
|
100
|
+
public Attribute()
|
|
101
|
+
: this(0) { }
|
|
102
|
+
|
|
103
|
+
/// <summary>
|
|
104
|
+
/// Initializes a new instance of the <see cref="Attribute"/> class with the specified base value.
|
|
105
|
+
/// </summary>
|
|
106
|
+
/// <param name="value">The base value for this attribute.</param>
|
|
107
|
+
public Attribute(float value)
|
|
108
|
+
{
|
|
109
|
+
_baseValue = value;
|
|
110
|
+
_currentValueCalculated = false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// <summary>
|
|
114
|
+
/// Initializes a new instance of the <see cref="Attribute"/> class for JSON deserialization.
|
|
115
|
+
/// </summary>
|
|
116
|
+
/// <param name="baseValue">The base value for this attribute.</param>
|
|
117
|
+
/// <param name="currentValue">The cached current value.</param>
|
|
118
|
+
[JsonConstructor]
|
|
119
|
+
public Attribute(float baseValue, float currentValue)
|
|
120
|
+
{
|
|
121
|
+
_baseValue = baseValue;
|
|
122
|
+
_currentValue = currentValue;
|
|
123
|
+
_currentValueCalculated = true;
|
|
124
|
+
}
|
|
125
|
+
|
|
94
126
|
/// <summary>
|
|
95
127
|
/// Recalculates the current value by applying all active modifications to the base value.
|
|
96
128
|
/// Modifications are sorted and applied in order: Addition, Multiplication, then Override.
|
|
@@ -99,8 +131,9 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
99
131
|
{
|
|
100
132
|
float calculatedValue = _baseValue;
|
|
101
133
|
using PooledResource<List<AttributeModification>> modificationBuffer =
|
|
102
|
-
Buffers<AttributeModification>.List.Get(
|
|
103
|
-
|
|
134
|
+
Buffers<AttributeModification>.List.Get(
|
|
135
|
+
out List<AttributeModification> modifications
|
|
136
|
+
);
|
|
104
137
|
foreach (
|
|
105
138
|
KeyValuePair<EffectHandle, List<AttributeModification>> entry in _modifications
|
|
106
139
|
)
|
|
@@ -108,8 +141,7 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
108
141
|
modifications.AddRange(entry.Value);
|
|
109
142
|
}
|
|
110
143
|
|
|
111
|
-
modifications.Sort(
|
|
112
|
-
|
|
144
|
+
modifications.Sort();
|
|
113
145
|
foreach (AttributeModification attributeModification in modifications)
|
|
114
146
|
{
|
|
115
147
|
ApplyAttributeModification(attributeModification, ref calculatedValue);
|
|
@@ -119,9 +151,6 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
119
151
|
_currentValueCalculated = true;
|
|
120
152
|
}
|
|
121
153
|
|
|
122
|
-
private readonly Dictionary<EffectHandle, List<AttributeModification>> _modifications =
|
|
123
|
-
new();
|
|
124
|
-
|
|
125
154
|
/// <summary>
|
|
126
155
|
/// Implicitly converts an Attribute to its current float value.
|
|
127
156
|
/// </summary>
|
|
@@ -137,32 +166,112 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
137
166
|
public static implicit operator Attribute(float value) => new(value);
|
|
138
167
|
|
|
139
168
|
/// <summary>
|
|
140
|
-
///
|
|
169
|
+
/// Applies a temporary additive modification to the attribute.
|
|
141
170
|
/// </summary>
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
/// <param name="value">The amount to add to the attribute's calculated value.</param>
|
|
172
|
+
/// <returns>
|
|
173
|
+
/// An effect handle that can later be supplied to <see cref="RemoveAttributeModification(EffectHandle)"/>
|
|
174
|
+
/// to revoke this addition.
|
|
175
|
+
/// </returns>
|
|
176
|
+
/// <exception cref="ArgumentException">
|
|
177
|
+
/// Thrown when <paramref name="value"/> is not a finite number.
|
|
178
|
+
/// </exception>
|
|
179
|
+
public EffectHandle Add(float value)
|
|
180
|
+
{
|
|
181
|
+
ValidateInput(value);
|
|
182
|
+
|
|
183
|
+
EffectHandle handle = EffectHandle.CreateInstanceInternal();
|
|
184
|
+
AttributeModification modification = new()
|
|
185
|
+
{
|
|
186
|
+
action = ModificationAction.Addition,
|
|
187
|
+
value = value,
|
|
188
|
+
};
|
|
189
|
+
ApplyAttributeModification(modification, handle);
|
|
190
|
+
return handle;
|
|
191
|
+
}
|
|
144
192
|
|
|
145
193
|
/// <summary>
|
|
146
|
-
///
|
|
194
|
+
/// Applies a temporary subtractive modification to the attribute.
|
|
147
195
|
/// </summary>
|
|
148
|
-
/// <param name="value">The
|
|
149
|
-
|
|
196
|
+
/// <param name="value">The amount to subtract from the attribute's calculated value.</param>
|
|
197
|
+
/// <returns>
|
|
198
|
+
/// An effect handle that can later be supplied to <see cref="RemoveAttributeModification(EffectHandle)"/>
|
|
199
|
+
/// to revoke this subtraction.
|
|
200
|
+
/// </returns>
|
|
201
|
+
/// <exception cref="ArgumentException">
|
|
202
|
+
/// Thrown when <paramref name="value"/> is not a finite number.
|
|
203
|
+
/// </exception>
|
|
204
|
+
public EffectHandle Subtract(float value)
|
|
150
205
|
{
|
|
151
|
-
|
|
152
|
-
|
|
206
|
+
ValidateInput(value);
|
|
207
|
+
|
|
208
|
+
EffectHandle handle = EffectHandle.CreateInstanceInternal();
|
|
209
|
+
AttributeModification modification = new()
|
|
210
|
+
{
|
|
211
|
+
action = ModificationAction.Addition,
|
|
212
|
+
// Subtraction is represented as a negative additive modifier to preserve modifier ordering.
|
|
213
|
+
value = -value,
|
|
214
|
+
};
|
|
215
|
+
ApplyAttributeModification(modification, handle);
|
|
216
|
+
return handle;
|
|
153
217
|
}
|
|
154
218
|
|
|
155
219
|
/// <summary>
|
|
156
|
-
///
|
|
220
|
+
/// Applies a temporary division-based modification to the attribute.
|
|
157
221
|
/// </summary>
|
|
158
|
-
/// <param name="
|
|
159
|
-
///
|
|
160
|
-
|
|
161
|
-
|
|
222
|
+
/// <param name="value">
|
|
223
|
+
/// The divisor that will be applied to the attribute's calculated value.
|
|
224
|
+
/// </param>
|
|
225
|
+
/// <returns>
|
|
226
|
+
/// An effect handle that can later be supplied to <see cref="RemoveAttributeModification(EffectHandle)"/>
|
|
227
|
+
/// to revoke this division.
|
|
228
|
+
/// </returns>
|
|
229
|
+
/// <exception cref="ArgumentException">
|
|
230
|
+
/// Thrown when <paramref name="value"/> is zero or not a finite number.
|
|
231
|
+
/// </exception>
|
|
232
|
+
public EffectHandle Divide(float value)
|
|
162
233
|
{
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
234
|
+
ValidateInput(value);
|
|
235
|
+
|
|
236
|
+
if (value == 0f)
|
|
237
|
+
{
|
|
238
|
+
throw new ArgumentException("Cannot divide by zero.", nameof(value));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
EffectHandle handle = EffectHandle.CreateInstanceInternal();
|
|
242
|
+
AttributeModification modification = new()
|
|
243
|
+
{
|
|
244
|
+
action = ModificationAction.Multiplication,
|
|
245
|
+
// Apply division by multiplying by the reciprocal to maintain multiplication ordering guarantees.
|
|
246
|
+
value = 1f / value,
|
|
247
|
+
};
|
|
248
|
+
ApplyAttributeModification(modification, handle);
|
|
249
|
+
return handle;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// <summary>
|
|
253
|
+
/// Applies a temporary multiplicative modification to the attribute.
|
|
254
|
+
/// </summary>
|
|
255
|
+
/// <param name="value">The multiplier to apply to the attribute's calculated value.</param>
|
|
256
|
+
/// <returns>
|
|
257
|
+
/// An effect handle that can later be supplied to <see cref="RemoveAttributeModification(EffectHandle)"/>
|
|
258
|
+
/// to revoke this multiplication.
|
|
259
|
+
/// </returns>
|
|
260
|
+
/// <exception cref="ArgumentException">
|
|
261
|
+
/// Thrown when <paramref name="value"/> is not a finite number.
|
|
262
|
+
/// </exception>
|
|
263
|
+
public EffectHandle Multiply(float value)
|
|
264
|
+
{
|
|
265
|
+
ValidateInput(value);
|
|
266
|
+
|
|
267
|
+
EffectHandle handle = EffectHandle.CreateInstanceInternal();
|
|
268
|
+
AttributeModification modification = new()
|
|
269
|
+
{
|
|
270
|
+
action = ModificationAction.Multiplication,
|
|
271
|
+
value = value,
|
|
272
|
+
};
|
|
273
|
+
ApplyAttributeModification(modification, handle);
|
|
274
|
+
return handle;
|
|
166
275
|
}
|
|
167
276
|
|
|
168
277
|
/// <summary>
|
|
@@ -173,6 +282,17 @@ namespace WallstopStudios.UnityHelpers.Tags
|
|
|
173
282
|
_currentValueCalculated = false;
|
|
174
283
|
}
|
|
175
284
|
|
|
285
|
+
private static void ValidateInput(float value, [CallerMemberName] string caller = null)
|
|
286
|
+
{
|
|
287
|
+
if (!float.IsFinite(value))
|
|
288
|
+
{
|
|
289
|
+
throw new ArgumentException(
|
|
290
|
+
$"Cannot {caller?.ToLowerInvariant()} by infinity or NaN.",
|
|
291
|
+
nameof(value)
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
176
296
|
/// <summary>
|
|
177
297
|
/// Applies an attribute modification to this attribute.
|
|
178
298
|
/// If a handle is provided, the modification is temporary and can be removed.
|