Skip to content

Commit 08e7df7

Browse files
Add more comments
1 parent c3c7ce7 commit 08e7df7

442 files changed

Lines changed: 14042 additions & 205 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.githooks/pre-commit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ set -euo pipefail
2020

2121
# dotnet format mode: whitespace or full.
2222
# whitespace is faster for commit-time checks while still using dotnet format.
23-
: "${PRECOMMIT_FORMAT_MODE:=whitespace}"
23+
: "${PRECOMMIT_FORMAT_MODE:=full}"
2424

2525
# true: run targeted builds for changed project areas.
2626
: "${PRECOMMIT_ENABLE_BUILD_GODOTUTILS:=true}"

Template.GodotUtils/Debugging/JsonExceptionHandler.cs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,45 +15,41 @@ public static class JsonExceptionHandler
1515
private const int ContextLinesAfter = 8;
1616

1717
/// <summary>
18-
/// Prints a formatted JSON parsing error with context.
18+
/// Prints a formatted JSON parsing error with nearby source context when line data is available.
1919
/// </summary>
20+
/// <param name="ex">JSON exception describing the parse failure.</param>
21+
/// <param name="jsonText">Original JSON source text.</param>
22+
/// <param name="path">Path of the JSON resource being parsed.</param>
2023
public static void Handle(JsonException ex, string jsonText, string path)
2124
{
22-
// Extract relevant information from the exception
2325
long? lineNumber = ex.LineNumber;
2426

27+
// Prefer context-rich output when the parser reports a source line number.
2528
if (lineNumber.HasValue)
2629
{
27-
// Split the JSON into lines
2830
string[] lines = jsonText.Split('\n');
2931

30-
// Get the problematic line
3132
int lineIndex = (int)lineNumber.Value;
3233
lineIndex = Math.Clamp(lineIndex, 0, Math.Max(0, lines.Length - 1));
3334
string problematicLine = lines[lineIndex];
3435

35-
// Determine the range of lines to display
3636
int startLine = Math.Max(0, lineIndex - ContextLinesBefore);
3737
int endLine = Math.Min(lines.Length, lineIndex + ContextLinesAfter);
3838

39-
// Create the error message
4039
StringBuilder errorMessage = new();
4140

4241
errorMessage.AppendLine($"ERROR: Failed to parse {Path.GetFileName(path)}");
4342
errorMessage.AppendLine();
4443
errorMessage.AppendLine($"{ex.Message}");
4544
errorMessage.AppendLine();
4645

47-
// Add the lines before the problematic line
4846
for (int i = startLine; i < lineIndex; i++)
4947
{
5048
errorMessage.AppendLine(lines[i]);
5149
}
5250

53-
// Add the problematic line with the caret indicating the error position
5451
errorMessage.AppendLine($"{problematicLine} <--- Syntax error could be on this line or the next line");
5552

56-
// Add the lines after the problematic line
5753
for (int i = lineIndex + 1; i < endLine; i++)
5854
{
5955
errorMessage.AppendLine(lines[i]);

Template.GodotUtils/Debugging/ParamValidator.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@ namespace GodotUtils.Debugging;
1111
public static class ParamValidator
1212
{
1313
/// <summary>
14-
/// Throws when <paramref name="obj"/> is null and disables the node.
14+
/// Validates an object argument and disables the node before throwing when the value is null.
1515
/// </summary>
16+
/// <param name="node">Node to disable when validation fails.</param>
17+
/// <param name="obj">Argument value to validate.</param>
18+
/// <param name="paramName">Name of the validated argument.</param>
19+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="obj"/> is null.</exception>
1620
public static void ThrowIfNull(Node node, object obj, [CallerArgumentExpression(nameof(obj))] string paramName = "")
1721
{
1822
ArgumentNullException.ThrowIfNull(node, nameof(node));
1923

24+
// Disable processing before throwing so invalid nodes stop running immediately.
2025
if (obj == null)
2126
{
2227
Script script = node.GetScript().As<Script>();

Template.GodotUtils/Deprecated/EventManager.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace GodotUtils.Deprecated;
77
/// <summary>
88
/// Legacy event manager for dispatching events by enum key.
99
/// </summary>
10+
/// <typeparam name="TEvent">Enum key type used to group and dispatch listeners.</typeparam>
1011
// This class was created to attempt to simplify the process of creating C# events for gamedev.
1112
//
1213
// ########### Example #1 ###########
@@ -40,6 +41,9 @@ public class EventManager<TEvent> where TEvent : notnull
4041
/// <summary>
4142
/// Adds a listener that receives raw argument arrays.
4243
/// </summary>
44+
/// <param name="eventType">Event key to subscribe to.</param>
45+
/// <param name="action">Callback invoked with all notify arguments.</param>
46+
/// <param name="id">Optional identifier used for grouped removal.</param>
4347
public void AddListener(TEvent eventType, Action<object[]> action, string id = "")
4448
{
4549
AddListenerInternal(eventType, action, id);
@@ -48,6 +52,10 @@ public void AddListener(TEvent eventType, Action<object[]> action, string id = "
4852
/// <summary>
4953
/// Adds a typed listener that receives the first argument as <typeparamref name="T"/>.
5054
/// </summary>
55+
/// <typeparam name="T">Expected type of the first notify argument.</typeparam>
56+
/// <param name="eventType">Event key to subscribe to.</param>
57+
/// <param name="action">Typed callback invoked with the first notify argument.</param>
58+
/// <param name="id">Optional identifier used for grouped removal.</param>
5159
public void AddListener<T>(TEvent eventType, Action<T> action, string id = "")
5260
{
5361
AddListenerInternal(eventType, WrapAction(action), id);
@@ -56,13 +64,17 @@ public void AddListener<T>(TEvent eventType, Action<T> action, string id = "")
5664
/// <summary>
5765
/// Removes all listeners of type <paramref name="eventType"/> with the provided id.
5866
/// </summary>
67+
/// <param name="eventType">Event key whose listeners should be filtered.</param>
68+
/// <param name="id">Identifier used to match listener entries for removal.</param>
5969
public void RemoveListeners(TEvent eventType, string id = "")
6070
{
71+
// Removing from an unknown event type is treated as a usage error.
6172
if (!_eventListeners.TryGetValue(eventType, out List<Listener>? listeners))
6273
throw new InvalidOperationException($"Tried to remove listener of event type '{eventType}' from an event type that has not even been defined yet");
6374

6475
for (int i = listeners.Count - 1; i >= 0; i--)
6576
{
77+
// Remove all listener entries that match the target id.
6678
if (listeners[i].Id == id)
6779
listeners.RemoveAt(i);
6880
}
@@ -79,21 +91,32 @@ public void RemoveAllListeners()
7991
/// <summary>
8092
/// Notifies all listeners for the provided event type.
8193
/// </summary>
94+
/// <param name="eventType">Event key to dispatch.</param>
95+
/// <param name="args">Arguments passed through to each listener callback.</param>
8296
public void Notify(TEvent eventType, params object[] args)
8397
{
98+
// Unknown event types simply have no listeners to notify.
8499
if (!_eventListeners.TryGetValue(eventType, out List<Listener>? value))
85100
{
86101
return;
87102
}
88103

104+
// Iterate snapshot to avoid collection-modified issues during callbacks.
89105
foreach (Listener listener in value.ToList()) // if ToList() is not here then issue #137 will occur
90106
{
91107
((Action<object[]>)listener.Action)(args);
92108
}
93109
}
94110

111+
/// <summary>
112+
/// Registers a raw listener for the given event type, creating the listener list when needed.
113+
/// </summary>
114+
/// <param name="eventType">Event key used to group listeners.</param>
115+
/// <param name="action">Listener callback that receives the notify argument array.</param>
116+
/// <param name="id">Optional listener identifier used by removal APIs.</param>
95117
private void AddListenerInternal(TEvent eventType, Action<object[]> action, string id)
96118
{
119+
// Lazily create listener bucket for first subscription of an event type.
97120
if (!_eventListeners.TryGetValue(eventType, out List<Listener>? listeners))
98121
{
99122
listeners = [];
@@ -103,10 +126,17 @@ private void AddListenerInternal(TEvent eventType, Action<object[]> action, stri
103126
listeners.Add(new Listener(action, id));
104127
}
105128

129+
/// <summary>
130+
/// Wraps a typed listener so it can be invoked by the raw object-array dispatch pipeline.
131+
/// </summary>
132+
/// <typeparam name="T">Expected argument type at index zero.</typeparam>
133+
/// <param name="action">Typed listener callback.</param>
134+
/// <returns>Adapter callback compatible with raw listener storage.</returns>
106135
private static Action<object[]> WrapAction<T>(Action<T> action)
107136
{
108137
return args =>
109138
{
139+
// Typed listeners consume only the first argument when available.
110140
if (args == null || args.Length == 0)
111141
return;
112142

@@ -118,6 +148,8 @@ private static Action<object[]> WrapAction<T>(Action<T> action)
118148
/// <summary>
119149
/// Stores a listener delegate and identifier.
120150
/// </summary>
151+
/// <param name="action">Callback delegate to invoke during event dispatch.</param>
152+
/// <param name="id">Optional identifier used for listener removal filters.</param>
121153
public class Listener(dynamic action, string id)
122154
{
123155
/// <summary>

Template.GodotUtils/Extensions/AnimatedSprite2DExtensions.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public static class AnimatedSprite2DExtensions
1010
/// <summary>
1111
/// Plays an animation immediately without the usual switch delay.
1212
/// </summary>
13+
/// <param name="sprite">Animated sprite to update.</param>
14+
/// <param name="anim">Animation name to play.</param>
1315
public static void InstantPlay(this AnimatedSprite2D sprite, string anim)
1416
{
1517
sprite.Animation = anim;
@@ -19,16 +21,21 @@ public static void InstantPlay(this AnimatedSprite2D sprite, string anim)
1921
/// <summary>
2022
/// Plays an animation immediately at the provided frame.
2123
/// </summary>
24+
/// <param name="sprite">Animated sprite to update.</param>
25+
/// <param name="anim">Animation name to play.</param>
26+
/// <param name="frame">Frame index to set before playback starts.</param>
2227
public static void InstantPlay(this AnimatedSprite2D sprite, string anim, int frame)
2328
{
2429
sprite.Animation = anim;
2530

2631
int frameCount = sprite.SpriteFrames.GetFrameCount(anim);
2732

33+
// Apply requested frame only when index is within animation bounds.
2834
if (frameCount - 1 >= frame)
2935
{
3036
sprite.Frame = frame;
3137
}
38+
// Log out-of-range frame selection for debugging.
3239
else
3340
{
3441
GD.Print($"The frame '{frame}' specified for {sprite.Name} is " +
@@ -41,6 +48,8 @@ public static void InstantPlay(this AnimatedSprite2D sprite, string anim, int fr
4148
/// <summary>
4249
/// Plays an animation starting at a random frame.
4350
/// </summary>
51+
/// <param name="sprite">Animated sprite to update.</param>
52+
/// <param name="anim">Optional animation name, or empty to use the current animation.</param>
4453
public static void PlayRandom(this AnimatedSprite2D sprite, string anim = "")
4554
{
4655
string resolvedAnim = ResolveAnimation(sprite, anim);
@@ -51,6 +60,9 @@ public static void PlayRandom(this AnimatedSprite2D sprite, string anim = "")
5160
/// <summary>
5261
/// Gets the unscaled size of the first frame.
5362
/// </summary>
63+
/// <param name="sprite">Animated sprite that owns the animation frames.</param>
64+
/// <param name="anim">Optional animation name, or empty to use the current animation.</param>
65+
/// <returns>Width and height of frame zero before node scaling.</returns>
5466
public static Vector2 GetSize(this AnimatedSprite2D sprite, string anim = "")
5567
{
5668
string resolvedAnim = ResolveAnimation(sprite, anim);
@@ -63,6 +75,9 @@ public static Vector2 GetSize(this AnimatedSprite2D sprite, string anim = "")
6375
/// <summary>
6476
/// Gets the scaled size of the first frame.
6577
/// </summary>
78+
/// <param name="sprite">Animated sprite that owns the animation frames.</param>
79+
/// <param name="anim">Optional animation name, or empty to use the current animation.</param>
80+
/// <returns>Width and height of frame zero after node scaling.</returns>
6681
public static Vector2 GetScaledSize(this AnimatedSprite2D sprite, string anim = "")
6782
{
6883
Vector2 size = sprite.GetSize(anim);
@@ -73,6 +88,9 @@ public static Vector2 GetScaledSize(this AnimatedSprite2D sprite, string anim =
7388
/// <summary>
7489
/// Gets the visible pixel size after trimming transparent borders.
7590
/// </summary>
91+
/// <param name="sprite">Animated sprite that owns the animation frames.</param>
92+
/// <param name="anim">Optional animation name, or empty to use the current animation.</param>
93+
/// <returns>Visible pixel width and height after transparency trimming.</returns>
7694
public static Vector2 GetPixelSize(this AnimatedSprite2D sprite, string anim = "")
7795
{
7896
string resolvedAnim = ResolveAnimation(sprite, anim);
@@ -82,6 +100,9 @@ public static Vector2 GetPixelSize(this AnimatedSprite2D sprite, string anim = "
82100
/// <summary>
83101
/// Gets the visible pixel width after trimming transparent columns.
84102
/// </summary>
103+
/// <param name="sprite">Animated sprite that owns the animation frames.</param>
104+
/// <param name="anim">Optional animation name, or empty to use the current animation.</param>
105+
/// <returns>Visible width in pixels after transparency trimming and scaling.</returns>
85106
public static int GetPixelWidth(this AnimatedSprite2D sprite, string anim = "")
86107
{
87108
string resolvedAnim = ResolveAnimation(sprite, anim);
@@ -98,6 +119,9 @@ public static int GetPixelWidth(this AnimatedSprite2D sprite, string anim = "")
98119
/// <summary>
99120
/// Gets the visible pixel height after trimming transparent rows.
100121
/// </summary>
122+
/// <param name="sprite">Animated sprite that owns the animation frames.</param>
123+
/// <param name="anim">Optional animation name, or empty to use the current animation.</param>
124+
/// <returns>Visible height in pixels after transparency trimming and scaling.</returns>
101125
public static int GetPixelHeight(this AnimatedSprite2D sprite, string anim = "")
102126
{
103127
string resolvedAnim = ResolveAnimation(sprite, anim);
@@ -114,6 +138,9 @@ public static int GetPixelHeight(this AnimatedSprite2D sprite, string anim = "")
114138
/// <summary>
115139
/// Gets the offset from the bottom to the first opaque pixel.
116140
/// </summary>
141+
/// <param name="sprite">Animated sprite that owns the animation frames.</param>
142+
/// <param name="anim">Optional animation name, or empty to use the current animation.</param>
143+
/// <returns>Pixel distance from the frame bottom to the first opaque center pixel.</returns>
117144
public static int GetPixelBottomY(this AnimatedSprite2D sprite, string anim = "")
118145
{
119146
string resolvedAnim = ResolveAnimation(sprite, anim);
@@ -125,6 +152,7 @@ public static int GetPixelBottomY(this AnimatedSprite2D sprite, string anim = ""
125152

126153
for (int y = size.Y - 1; y >= 0; y--)
127154
{
155+
// Stop once an opaque pixel is reached in center column.
128156
if (img.GetPixel(size.X / 2, y).A != 0)
129157
{
130158
break;
@@ -136,11 +164,24 @@ public static int GetPixelBottomY(this AnimatedSprite2D sprite, string anim = ""
136164
return diff;
137165
}
138166

167+
/// <summary>
168+
/// Returns the requested animation name or falls back to the sprite's current animation.
169+
/// </summary>
170+
/// <param name="sprite">Animated sprite that provides the fallback animation.</param>
171+
/// <param name="anim">Requested animation name.</param>
172+
/// <returns>Resolved animation name to use for frame lookups.</returns>
139173
private static string ResolveAnimation(AnimatedSprite2D sprite, string anim)
140174
{
141175
return string.IsNullOrWhiteSpace(anim) ? sprite.Animation : anim;
142176
}
143177

178+
/// <summary>
179+
/// Gets the first frame image for an animation and outputs its dimensions.
180+
/// </summary>
181+
/// <param name="sprite">Animated sprite that owns the frame set.</param>
182+
/// <param name="anim">Animation name used for frame retrieval.</param>
183+
/// <param name="size">Resolved frame image dimensions.</param>
184+
/// <returns>Image for frame zero of the resolved animation.</returns>
144185
private static Image GetFrameImage(AnimatedSprite2D sprite, string anim, out Vector2I size)
145186
{
146187
Texture2D tex = sprite.SpriteFrames.GetFrameTexture(anim, 0);

Template.GodotUtils/Extensions/AnimationTreeExtensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ public static class AnimationTreeExtensions
1010
/// <summary>
1111
/// Sets a condition briefly and auto-resets after 0.1 seconds.
1212
/// </summary>
13+
/// <param name="tree">Animation tree to update.</param>
14+
/// <param name="path">Condition parameter path segment.</param>
15+
/// <param name="value">Condition value to set briefly.</param>
1316
public static void SetCondition(this AnimationTree tree, StringName path, bool value)
1417
{
1518
tree.SetParam($"conditions/{path}", value);
@@ -22,6 +25,9 @@ public static void SetCondition(this AnimationTree tree, StringName path, bool v
2225
/// <summary>
2326
/// Sets the blend position of a BlendSpace1D by name.
2427
/// </summary>
28+
/// <param name="tree">Animation tree to update.</param>
29+
/// <param name="name">Blend space parameter name.</param>
30+
/// <param name="value">Blend position value.</param>
2531
public static void SetBlendSpace1DPosition(this AnimationTree tree, StringName name, float value)
2632
{
2733
tree.SetParam($"{name}/blend_position", value);
@@ -30,6 +36,9 @@ public static void SetBlendSpace1DPosition(this AnimationTree tree, StringName n
3036
/// <summary>
3137
/// Sets a parameter value on the animation tree.
3238
/// </summary>
39+
/// <param name="tree">Animation tree to update.</param>
40+
/// <param name="path">Parameter path segment under <c>parameters/</c>.</param>
41+
/// <param name="value">Value to assign.</param>
3342
public static void SetParam(this AnimationTree tree, StringName path, Variant value)
3443
{
3544
tree.Set($"parameters/{path}", value);
@@ -38,6 +47,9 @@ public static void SetParam(this AnimationTree tree, StringName path, Variant va
3847
/// <summary>
3948
/// Gets a parameter value from the animation tree.
4049
/// </summary>
50+
/// <param name="tree">Animation tree to read.</param>
51+
/// <param name="path">Parameter path segment under <c>parameters/</c>.</param>
52+
/// <returns>Resolved parameter value.</returns>
4153
public static Variant GetParam(this AnimationTree tree, StringName path)
4254
{
4355
return tree.Get($"parameters/{path}");
@@ -46,6 +58,9 @@ public static Variant GetParam(this AnimationTree tree, StringName path)
4658
/// <summary>
4759
/// Gets a condition value from the animation tree.
4860
/// </summary>
61+
/// <param name="tree">Animation tree to read.</param>
62+
/// <param name="path">Condition path segment under <c>conditions/</c>.</param>
63+
/// <returns>Resolved condition value.</returns>
4964
public static bool GetCondition(this AnimationTree tree, StringName path)
5065
{
5166
return (bool)tree.GetParam($"conditions/{path}");
@@ -54,6 +69,8 @@ public static bool GetCondition(this AnimationTree tree, StringName path)
5469
/// <summary>
5570
/// Gets the state machine playback controller.
5671
/// </summary>
72+
/// <param name="tree">Animation tree to read.</param>
73+
/// <returns>State machine playback controller.</returns>
5774
public static AnimationNodeStateMachinePlayback GetStateMachine(this AnimationTree tree)
5875
{
5976
return tree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>();

Template.GodotUtils/Extensions/Area2DExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public static class Area2DExtensions
1010
/// <summary>
1111
/// Sets Monitoring using deferred property updates.
1212
/// </summary>
13+
/// <param name="area">Area to update.</param>
14+
/// <param name="enabled">Target monitoring state.</param>
1315
public static void SetMonitoringDeferred(this Area2D area, bool enabled)
1416
{
1517
area.SetDeferred(Area2D.PropertyName.Monitoring, enabled);
@@ -18,6 +20,8 @@ public static void SetMonitoringDeferred(this Area2D area, bool enabled)
1820
/// <summary>
1921
/// Sets Monitorable using deferred property updates.
2022
/// </summary>
23+
/// <param name="area">Area to update.</param>
24+
/// <param name="enabled">Target monitorable state.</param>
2125
public static void SetMonitorableDeferred(this Area2D area, bool enabled)
2226
{
2327
area.SetDeferred(Area2D.PropertyName.Monitorable, enabled);

0 commit comments

Comments
 (0)