com.wallstop-studios.unity-helpers 2.0.0-rc67 → 2.0.0-rc69

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.
Files changed (33) hide show
  1. package/Runtime/Core/Attributes/ValidateAssignmentAttribute.cs +1 -1
  2. package/Runtime/Core/Helper/Helpers.cs +1 -1
  3. package/Runtime/Core/Helper/Partials/LogHelpers.cs +1 -1
  4. package/Runtime/Core/Helper/Partials/MathHelpers.cs +1 -1
  5. package/Runtime/Core/Helper/Partials/ObjectHelpers.cs +3 -4
  6. package/Runtime/Tags/Attribute.cs +205 -0
  7. package/Runtime/Tags/Attribute.cs.meta +3 -0
  8. package/Runtime/Tags/AttributeEffect.cs +276 -0
  9. package/Runtime/Tags/AttributeEffect.cs.meta +3 -0
  10. package/Runtime/Tags/AttributeModification.cs +51 -0
  11. package/Runtime/Tags/AttributeModification.cs.meta +3 -0
  12. package/Runtime/Tags/AttributeUtilities.cs +208 -0
  13. package/Runtime/Tags/AttributeUtilities.cs.meta +3 -0
  14. package/Runtime/Tags/AttributesComponent.cs +163 -0
  15. package/Runtime/Tags/AttributesComponent.cs.meta +3 -0
  16. package/Runtime/Tags/CosmeticEffectComponent.cs +50 -0
  17. package/Runtime/Tags/CosmeticEffectComponent.cs.meta +3 -0
  18. package/Runtime/Tags/CosmeticEffectData.cs +63 -0
  19. package/Runtime/Tags/CosmeticEffectData.cs.meta +3 -0
  20. package/Runtime/Tags/EffectHandle.cs +63 -0
  21. package/Runtime/Tags/EffectHandle.cs.meta +3 -0
  22. package/Runtime/Tags/EffectHandler.cs +379 -0
  23. package/Runtime/Tags/EffectHandler.cs.meta +3 -0
  24. package/Runtime/Tags/ModificationAction.cs +9 -0
  25. package/Runtime/Tags/ModificationAction.cs.meta +3 -0
  26. package/Runtime/Tags/ModifierDurationType.cs +13 -0
  27. package/Runtime/Tags/ModifierDurationType.cs.meta +3 -0
  28. package/Runtime/{Utils → Tags}/TagHandler.cs +42 -5
  29. package/Runtime/Tags.meta +3 -0
  30. package/Tests/Runtime/DataStructures/SpatialTreeTests.cs +1 -1
  31. package/Tests/Runtime/Helper/ObjectHelperTests.cs +1 -0
  32. package/package.json +1 -1
  33. /package/Runtime/{Utils → Tags}/TagHandler.cs.meta +0 -0
@@ -6,7 +6,7 @@
6
6
  using System.Linq;
7
7
  using System.Reflection;
8
8
  using Extension;
9
- using Helper.Partials;
9
+ using Helper;
10
10
  using JetBrains.Annotations;
11
11
  using Object = UnityEngine.Object;
12
12
 
@@ -20,7 +20,7 @@
20
20
  {
21
21
  private static readonly WaitForEndOfFrame WaitForEndOfFrame = new();
22
22
  private static readonly Dictionary<Type, MethodInfo> AwakeMethodsByType = new();
23
- private static readonly Object LogObject = new() { name = "Wallstop Log Helper" };
23
+ private static readonly Object LogObject = new();
24
24
  private static readonly Dictionary<string, Object> ObjectsByTag = new(
25
25
  StringComparer.Ordinal
26
26
  );
@@ -1,4 +1,4 @@
1
- namespace WallstopStudios.UnityHelpers.Core.Helper.Partials
1
+ namespace WallstopStudios.UnityHelpers.Core.Helper
2
2
  {
3
3
  using Extension;
4
4
  using UnityEngine;
@@ -1,4 +1,4 @@
1
- namespace WallstopStudios.UnityHelpers.Core.Helper.Partials
1
+ namespace WallstopStudios.UnityHelpers.Core.Helper
2
2
  {
3
3
  using UnityEngine;
4
4
 
@@ -2,13 +2,12 @@
2
2
  {
3
3
  using System;
4
4
  using Extension;
5
+ using UnityEditor;
6
+ using UnityEditor.SceneManagement;
5
7
  using UnityEngine;
6
8
  using UnityEngine.SceneManagement;
7
9
  using Object = UnityEngine.Object;
8
- #if UNITY_EDITOR
9
- using UnityEditor;
10
- using UnityEditor.SceneManagement;
11
- #endif
10
+
12
11
  public static partial class Helpers
13
12
  {
14
13
  public static T Find<T>(this Object component, string tag, bool log = true)
@@ -0,0 +1,205 @@
1
+ namespace WallstopStudios.UnityHelpers.Tags
2
+ {
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.ComponentModel;
6
+ using System.Globalization;
7
+ using System.Linq;
8
+ using System.Runtime.Serialization;
9
+ using System.Text.Json.Serialization;
10
+ using Core.Extension;
11
+ using ProtoBuf;
12
+ using UnityEngine;
13
+
14
+ [Serializable]
15
+ [ProtoContract]
16
+ public sealed class Attribute : IEquatable<Attribute>, IEquatable<float>
17
+ {
18
+ public float CurrentValue
19
+ {
20
+ get
21
+ {
22
+ #if UNITY_EDITOR
23
+ /*
24
+ For some reason, there's a bug with loot tables where
25
+ _currentValueCalculated will be true but the current
26
+ value is not calculated, so ignore the flag if we're
27
+ in editor mode, where this happens
28
+ */
29
+ if (Application.isPlaying)
30
+ #endif
31
+ {
32
+ if (_currentValueCalculated)
33
+ {
34
+ return _currentValue;
35
+ }
36
+ }
37
+
38
+ CalculateCurrentValue();
39
+ return _currentValue;
40
+ }
41
+ }
42
+
43
+ public float BaseValue => _baseValue;
44
+
45
+ [SerializeField]
46
+ internal float _baseValue;
47
+
48
+ [SerializeField]
49
+ private float _currentValue;
50
+
51
+ private bool _currentValueCalculated;
52
+
53
+ internal void CalculateCurrentValue()
54
+ {
55
+ float calculatedValue = _baseValue;
56
+ foreach (
57
+ AttributeModification attributeModification in _modifications
58
+ .SelectMany(kvp => kvp.Value)
59
+ .OrderBy(mod => mod.action)
60
+ )
61
+ {
62
+ ApplyAttributeModification(attributeModification, ref calculatedValue);
63
+ }
64
+
65
+ _currentValue = calculatedValue;
66
+ _currentValueCalculated = true;
67
+ }
68
+
69
+ private readonly Dictionary<EffectHandle, List<AttributeModification>> _modifications =
70
+ new();
71
+
72
+ public static implicit operator float(Attribute attribute) => attribute.CurrentValue;
73
+
74
+ public static implicit operator Attribute(float value) => new(value);
75
+
76
+ public Attribute()
77
+ : this(0) { }
78
+
79
+ public Attribute(float value)
80
+ {
81
+ _baseValue = value;
82
+ _currentValueCalculated = false;
83
+ }
84
+
85
+ [JsonConstructor]
86
+ public Attribute(float baseValue, float currentValue)
87
+ {
88
+ _baseValue = baseValue;
89
+ _currentValue = currentValue;
90
+ _currentValueCalculated = true;
91
+ }
92
+
93
+ public void ClearCache()
94
+ {
95
+ _currentValueCalculated = false;
96
+ }
97
+
98
+ public void ApplyAttributeModification(
99
+ AttributeModification attributeModification,
100
+ EffectHandle? handle = null
101
+ )
102
+ {
103
+ // If we don't have a handle, then this is an instant effect, apply it to the base value.
104
+ if (!handle.HasValue)
105
+ {
106
+ ApplyAttributeModification(attributeModification, ref _baseValue);
107
+ }
108
+ else
109
+ {
110
+ _modifications.GetOrAdd(handle.Value).Add(attributeModification);
111
+ }
112
+
113
+ CalculateCurrentValue();
114
+ }
115
+
116
+ public bool RemoveAttributeModification(EffectHandle handle)
117
+ {
118
+ bool removed = _modifications.Remove(handle);
119
+ if (removed)
120
+ {
121
+ CalculateCurrentValue();
122
+ }
123
+
124
+ return removed;
125
+ }
126
+
127
+ [OnDeserialized]
128
+ private void AfterDeserialize(StreamingContext streamingContext)
129
+ {
130
+ ClearCache();
131
+ }
132
+
133
+ [ProtoAfterDeserialization]
134
+ private void AfterProtoDeserialized()
135
+ {
136
+ ClearCache();
137
+ }
138
+
139
+ private static void ApplyAttributeModification(
140
+ AttributeModification attributeModification,
141
+ ref float value
142
+ )
143
+ {
144
+ switch (attributeModification.action)
145
+ {
146
+ case ModificationAction.Addition:
147
+ value += attributeModification.value;
148
+ break;
149
+ case ModificationAction.Multiplication:
150
+ value *= attributeModification.value;
151
+ break;
152
+ case ModificationAction.Override:
153
+ value = attributeModification.value;
154
+ break;
155
+ default:
156
+ throw new InvalidEnumArgumentException(
157
+ nameof(attributeModification.action),
158
+ (int)attributeModification.action,
159
+ typeof(ModificationAction)
160
+ );
161
+ }
162
+ }
163
+
164
+ public bool Equals(Attribute other)
165
+ {
166
+ if (ReferenceEquals(this, other))
167
+ {
168
+ return true;
169
+ }
170
+
171
+ return other != null && CurrentValue.Equals(other.CurrentValue);
172
+ }
173
+
174
+ public override bool Equals(object other)
175
+ {
176
+ switch (other)
177
+ {
178
+ case Attribute attribute:
179
+ return Equals(attribute);
180
+ case float attribute:
181
+ return Equals(attribute);
182
+ case double attribute:
183
+ return Equals((float)attribute);
184
+ default:
185
+ return false;
186
+ }
187
+ }
188
+
189
+ public bool Equals(float other)
190
+ {
191
+ return CurrentValue.Equals(other);
192
+ }
193
+
194
+ public override int GetHashCode()
195
+ {
196
+ // ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
197
+ return base.GetHashCode();
198
+ }
199
+
200
+ public override string ToString()
201
+ {
202
+ return ((float)this).ToString(CultureInfo.InvariantCulture);
203
+ }
204
+ }
205
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 7209237176394eaf99a875c53950e2d0
3
+ timeCreated: 1746412136
@@ -0,0 +1,276 @@
1
+ namespace WallstopStudios.UnityHelpers.Tags
2
+ {
3
+ using System;
4
+ using System.Collections.Generic;
5
+ using System.ComponentModel;
6
+ using System.Linq;
7
+ using System.Text;
8
+ using Core.Extension;
9
+ using Core.Helper;
10
+ using Newtonsoft.Json;
11
+ using Sirenix.OdinInspector;
12
+
13
+ [Serializable]
14
+ public sealed class AttributeEffect :
15
+ #if ODIN_INSPECTOR
16
+ SerializedScriptableObject
17
+ #else
18
+ ScriptableObject
19
+ #endif
20
+ , IEquatable<AttributeEffect>
21
+ {
22
+ public string HumanReadableDescription => BuildDescription();
23
+
24
+ public readonly List<AttributeModification> modifications = new();
25
+
26
+ public ModifierDurationType durationType = ModifierDurationType.Duration;
27
+
28
+ #if ODIN_INSPECTOR
29
+ [ShowIf("@durationType == ModifierDurationType.Duration")]
30
+ #endif
31
+ public float duration;
32
+
33
+ #if ODIN_INSPECTOR
34
+ [ShowIf("@durationType == ModifierDurationType.Duration")]
35
+ #endif
36
+ public bool resetDurationOnReapplication;
37
+
38
+ public List<string> effectTags = new();
39
+
40
+ [JsonIgnore]
41
+ public readonly List<CosmeticEffectData> cosmeticEffects = new();
42
+
43
+ private List<string> CosmeticEffectsForJson =>
44
+ cosmeticEffects
45
+ ?.Select(cosmeticEffectData => cosmeticEffectData.name)
46
+ .ToList(cosmeticEffects.Count) ?? new List<string>(0);
47
+
48
+ public override string ToString()
49
+ {
50
+ return new
51
+ {
52
+ Description = HumanReadableDescription,
53
+ CosmeticEffects = CosmeticEffectsForJson,
54
+ modifications,
55
+ durationType,
56
+ duration,
57
+ tags = effectTags,
58
+ }.ToJson();
59
+ }
60
+
61
+ private string BuildDescription()
62
+ {
63
+ if (modifications == null)
64
+ {
65
+ return nameof(AttributeEffect);
66
+ }
67
+
68
+ StringBuilder descriptionBuilder = new();
69
+ for (int i = 0; i < modifications.Count; ++i)
70
+ {
71
+ AttributeModification modification = modifications[i];
72
+ switch (modification.action)
73
+ {
74
+ case ModificationAction.Addition:
75
+ {
76
+ if (modification.value < 0)
77
+ {
78
+ _ = descriptionBuilder.Append(modification.value);
79
+ _ = descriptionBuilder.Append(' ');
80
+ }
81
+ else if (modification.value == 0)
82
+ {
83
+ continue;
84
+ }
85
+ else
86
+ {
87
+ _ = descriptionBuilder.AppendFormat("+{0} ", modification.value);
88
+ }
89
+
90
+ break;
91
+ }
92
+ case ModificationAction.Multiplication:
93
+ {
94
+ if (modification.value < 1)
95
+ {
96
+ _ = descriptionBuilder.AppendFormat(
97
+ "-{0}% ",
98
+ (1 - modification.value) * 100
99
+ );
100
+ }
101
+ // ReSharper disable once CompareOfFloatsByEqualityOperator
102
+ else if (modification.value == 1)
103
+ {
104
+ continue;
105
+ }
106
+ else
107
+ {
108
+ _ = descriptionBuilder.AppendFormat(
109
+ "+{0}% ",
110
+ (modification.value - 1) * 100
111
+ );
112
+ }
113
+
114
+ break;
115
+ }
116
+ case ModificationAction.Override:
117
+ {
118
+ _ = descriptionBuilder.AppendFormat("{0} ", modification.value);
119
+ break;
120
+ }
121
+ default:
122
+ {
123
+ throw new InvalidEnumArgumentException(
124
+ nameof(modification.value),
125
+ (int)modification.value,
126
+ typeof(ModificationAction)
127
+ );
128
+ }
129
+ }
130
+
131
+ _ = descriptionBuilder.Append(modification.attribute.ToPascalCase(" "));
132
+ if (i < modifications.Count - 1)
133
+ {
134
+ _ = descriptionBuilder.Append(", ");
135
+ }
136
+ }
137
+
138
+ return descriptionBuilder.ToString();
139
+ }
140
+
141
+ // Needed now since most things are based on serialized attribute effects and each unserialization will be a new instance
142
+ public bool Equals(AttributeEffect other)
143
+ {
144
+ if (ReferenceEquals(this, other))
145
+ {
146
+ return true;
147
+ }
148
+
149
+ if (other == null)
150
+ {
151
+ return false;
152
+ }
153
+
154
+ if (!string.Equals(name, other.name))
155
+ {
156
+ return false;
157
+ }
158
+
159
+ if (durationType != other.durationType)
160
+ {
161
+ return false;
162
+ }
163
+
164
+ // ReSharper disable once CompareOfFloatsByEqualityOperator
165
+ if (duration != other.duration)
166
+ {
167
+ return false;
168
+ }
169
+
170
+ if (resetDurationOnReapplication != other.resetDurationOnReapplication)
171
+ {
172
+ return false;
173
+ }
174
+
175
+ if (modifications == null)
176
+ {
177
+ if (other.modifications != null)
178
+ {
179
+ return false;
180
+ }
181
+ }
182
+ else if (other.modifications == null)
183
+ {
184
+ return false;
185
+ }
186
+ else
187
+ {
188
+ if (modifications.Count != other.modifications.Count)
189
+ {
190
+ return false;
191
+ }
192
+
193
+ for (int i = 0; i < modifications.Count; ++i)
194
+ {
195
+ if (modifications[i] != other.modifications[i])
196
+ {
197
+ return false;
198
+ }
199
+ }
200
+ }
201
+
202
+ if (effectTags == null)
203
+ {
204
+ if (other.effectTags != null)
205
+ {
206
+ return false;
207
+ }
208
+ }
209
+ else if (other.effectTags == null)
210
+ {
211
+ return false;
212
+ }
213
+ else
214
+ {
215
+ if (effectTags.Count != other.effectTags.Count)
216
+ {
217
+ return false;
218
+ }
219
+
220
+ for (int i = 0; i < effectTags.Count; ++i)
221
+ {
222
+ if (effectTags[i] != other.effectTags[i])
223
+ {
224
+ return false;
225
+ }
226
+ }
227
+ }
228
+
229
+ if (cosmeticEffects == null)
230
+ {
231
+ if (other.cosmeticEffects != null)
232
+ {
233
+ return false;
234
+ }
235
+ }
236
+ else if (other.cosmeticEffects == null)
237
+ {
238
+ return false;
239
+ }
240
+ else
241
+ {
242
+ if (cosmeticEffects.Count != other.cosmeticEffects.Count)
243
+ {
244
+ return false;
245
+ }
246
+
247
+ for (int i = 0; i < cosmeticEffects.Count; ++i)
248
+ {
249
+ if (!Equals(cosmeticEffects[i], other.cosmeticEffects[i]))
250
+ {
251
+ return false;
252
+ }
253
+ }
254
+ }
255
+
256
+ return true;
257
+ }
258
+
259
+ public override bool Equals(object obj)
260
+ {
261
+ return ReferenceEquals(this, obj) || obj is AttributeEffect other && Equals(other);
262
+ }
263
+
264
+ public override int GetHashCode()
265
+ {
266
+ return Objects.HashCode(
267
+ modifications?.Count,
268
+ durationType,
269
+ duration,
270
+ resetDurationOnReapplication,
271
+ effectTags?.Count,
272
+ cosmeticEffects?.Count
273
+ );
274
+ }
275
+ }
276
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 000e706d0ccd45d8a776c1a9b9d4928b
3
+ timeCreated: 1746411716
@@ -0,0 +1,51 @@
1
+ namespace WallstopStudios.UnityHelpers.Tags
2
+ {
3
+ using System;
4
+ using Core.Extension;
5
+ using Core.Helper;
6
+
7
+ [Serializable]
8
+ public struct AttributeModification : IEquatable<AttributeModification>
9
+ {
10
+ [StringInList(typeof(AttributeUtilities), nameof(AttributeUtilities.GetAllAttributeNames))]
11
+ public string attribute;
12
+
13
+ public ModificationAction action;
14
+ public float value;
15
+
16
+ public override string ToString()
17
+ {
18
+ return this.ToJson();
19
+ }
20
+
21
+ public static bool operator !=(AttributeModification lhs, AttributeModification rhs)
22
+ {
23
+ return !(lhs == rhs);
24
+ }
25
+
26
+ public static bool operator ==(AttributeModification lhs, AttributeModification rhs)
27
+ {
28
+ // ReSharper disable once CompareOfFloatsByEqualityOperator
29
+ return string.Equals(lhs.attribute, rhs.attribute)
30
+ && lhs.action == rhs.action
31
+ && lhs.value == rhs.value;
32
+ }
33
+
34
+ public override bool Equals(object obj)
35
+ {
36
+ return obj is AttributeModification other && Equals(other);
37
+ }
38
+
39
+ public override int GetHashCode()
40
+ {
41
+ return Objects.HashCode(attribute, action, value);
42
+ }
43
+
44
+ public bool Equals(AttributeModification other)
45
+ {
46
+ return string.Equals(attribute, other.attribute, StringComparison.Ordinal)
47
+ && action == other.action
48
+ && value.Equals(other.value);
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: aa8638bb1ef14228b7496f0d7b12262d
3
+ timeCreated: 1746411774