Replicated the weapon flying tick setup using resources
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 26s
Create tag and build when new code gets to main / Export (push) Successful in 5m42s

This commit is contained in:
2026-04-07 16:32:26 +02:00
parent cc7cb90041
commit 1d856fd937
145 changed files with 12943 additions and 109 deletions

View File

@@ -0,0 +1,36 @@
// Copyright © Gamesmiths Guild.
using Godot;
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// Represents a connection between two nodes in the Statescript graph.
/// </summary>
[Tool]
public partial class StatescriptConnection : Resource
{
/// <summary>
/// Gets or sets the source node id.
/// </summary>
[Export]
public string FromNode { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the source port index.
/// </summary>
[Export]
public int OutputPort { get; set; }
/// <summary>
/// Gets or sets the destination node id.
/// </summary>
[Export]
public string ToNode { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the destination port index.
/// </summary>
[Export]
public int InputPort { get; set; }
}

View File

@@ -0,0 +1 @@
uid://c3w8w5eh6qxub

View File

@@ -0,0 +1,75 @@
// Copyright © Gamesmiths Guild.
using Godot;
using Godot.Collections;
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// Resource representing a complete Statescript graph. Contains all nodes and their connections.
/// </summary>
[Tool]
[GlobalClass]
[Icon("uid://b6yrjb46fluw3")]
public partial class StatescriptGraph : Resource
{
/// <summary>
/// Gets or sets the display name for this graph.
/// </summary>
[Export]
public string StatescriptName { get; set; } = "New Statescript";
/// <summary>
/// Gets or sets the nodes in this graph.
/// </summary>
[Export]
public Array<StatescriptNode> Nodes { get; set; } = [];
/// <summary>
/// Gets or sets the connections between nodes in this graph.
/// </summary>
[Export]
public Array<StatescriptConnection> Connections { get; set; } = [];
/// <summary>
/// Gets or sets the graph variable definitions.
/// </summary>
[Export]
public Array<StatescriptGraphVariable> Variables { get; set; } = [];
/// <summary>
/// Gets or sets the scroll offset of the graph editor when this graph was last saved.
/// </summary>
[Export]
public Vector2 ScrollOffset { get; set; }
/// <summary>
/// Gets or sets the zoom level of the graph editor when this graph was last saved.
/// </summary>
[Export]
public float Zoom { get; set; } = 1.0f;
/// <summary>
/// Ensures the graph has an Entry node. Called when the graph is first created or loaded.
/// </summary>
public void EnsureEntryNode()
{
foreach (StatescriptNode node in Nodes)
{
if (node.NodeType == StatescriptNodeType.Entry)
{
return;
}
}
var entryNode = new StatescriptNode
{
NodeId = "entry",
Title = "Entry",
NodeType = StatescriptNodeType.Entry,
PositionOffset = new Vector2(100, 200),
};
Nodes.Add(entryNode);
}
}

View File

@@ -0,0 +1 @@
uid://fj6yp4h5mkl6

View File

@@ -0,0 +1,46 @@
// Copyright © Gamesmiths Guild.
using Godot;
using Godot.Collections;
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// Resource representing a single graph variable definition, including name, type, initial value, and whether it is an
/// array variable.
/// </summary>
[Tool]
public partial class StatescriptGraphVariable : Resource
{
/// <summary>
/// Gets or sets the name of this variable.
/// </summary>
[Export]
public string VariableName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the type of this variable.
/// </summary>
[Export]
public StatescriptVariableType VariableType { get; set; } = StatescriptVariableType.Int;
/// <summary>
/// Gets or sets a value indicating whether this is an array variable.
/// </summary>
[Export]
public bool IsArray { get; set; }
/// <summary>
/// Gets or sets the initial value of this variable, stored as a Godot variant.
/// For non-array variables, this is a single value. Ignored when <see cref="IsArray"/> is true.
/// </summary>
[Export]
public Variant InitialValue { get; set; }
/// <summary>
/// Gets or sets the initial values for array variables.
/// Each element is stored as a Godot variant. Only used when <see cref="IsArray"/> is true.
/// </summary>
[Export]
public Array<Variant> InitialArrayValues { get; set; } = [];
}

View File

@@ -0,0 +1 @@
uid://doh71xlxr3vqs

View File

@@ -0,0 +1,91 @@
// Copyright © Gamesmiths Guild.
using Godot;
using Godot.Collections;
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// The type of a Statescript node.
/// </summary>
public enum StatescriptNodeType
{
/// <summary>
/// Entry node: single output port. One per graph, cannot be removed. Color: Blue.
/// </summary>
Entry = 0,
/// <summary>
/// Exit node: single input port. Optional, can have multiple. Color: Blue.
/// </summary>
Exit = 1,
/// <summary>
/// Action node: one input, one output. Executes an instant action. Color: Green.
/// </summary>
Action = 2,
/// <summary>
/// Condition node: one input, two outputs (true/false). Color: Yellow.
/// </summary>
Condition = 3,
/// <summary>
/// State node: two inputs (input/abort), multiple outputs (OnActivate, OnDeactivate, OnAbort, Subgraph, +
/// custom). Color: Red.
/// </summary>
State = 4,
}
/// <summary>
/// Resource representing a single node within a Statescript graph.
/// </summary>
[Tool]
public partial class StatescriptNode : Resource
{
/// <summary>
/// Gets or sets the unique identifier for this node within the graph.
/// </summary>
[Export]
public string NodeId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the display title for this node.
/// </summary>
[Export]
public string Title { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the type of this node.
/// </summary>
[Export]
public StatescriptNodeType NodeType { get; set; }
/// <summary>
/// Gets or sets the fully qualified runtime type name of the concrete node class from the Forge library.
/// Empty for Entry and Exit nodes which are handled specially.
/// </summary>
[Export]
public string RuntimeTypeName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the position of this node in the graph editor.
/// </summary>
[Export]
public Vector2 PositionOffset { get; set; }
/// <summary>
/// Gets or sets additional custom data for extended node implementations.
/// </summary>
/// <remarks>
/// Keys are constructor parameter names; values are the serialized parameter values.
/// </remarks>
[Export]
public Dictionary<string, Variant> CustomData { get; set; } = [];
/// <summary>
/// Gets or sets the property bindings for this node's input properties and output variables.
/// </summary>
[Export]
public Array<StatescriptNodeProperty> PropertyBindings { get; set; } = [];
}

View File

@@ -0,0 +1 @@
uid://6gpuevsxrj1i

View File

@@ -0,0 +1,30 @@
// Copyright © Gamesmiths Guild.
using Godot;
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// Resource representing a single node property binding. Stores the resolver resource reference.
/// </summary>
[Tool]
public partial class StatescriptNodeProperty : Resource
{
/// <summary>
/// Gets or sets the direction of this property (input or output).
/// </summary>
[Export]
public StatescriptPropertyDirection Direction { get; set; }
/// <summary>
/// Gets or sets the index of this property in the node's InputProperties or OutputVariables array.
/// </summary>
[Export]
public int PropertyIndex { get; set; }
/// <summary>
/// Gets or sets the resolver resource for this property.
/// </summary>
[Export]
public StatescriptResolverResource? Resolver { get; set; }
}

View File

@@ -0,0 +1 @@
uid://dlo2ir5oqf48y

View File

@@ -0,0 +1,19 @@
// Copyright © Gamesmiths Guild.
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// Indicates the direction of a node property binding.
/// </summary>
public enum StatescriptPropertyDirection
{
/// <summary>
/// An input property that feeds a value into the node.
/// </summary>
Input = 0,
/// <summary>
/// An output variable that the node writes a value to.
/// </summary>
Output = 1,
}

View File

@@ -0,0 +1 @@
uid://cxxeqnxesm87m

View File

@@ -0,0 +1,65 @@
// Copyright © Gamesmiths Guild.
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// Base resource for all Statescript property resolvers. Each resolver type derives from this and implements the
/// binding methods to wire serialized editor data into the core runtime graph.
/// </summary>
/// <remarks>
/// Subclasses must override <see cref="ResolverTypeId"/> to return a unique string that matches the corresponding
/// editor's <c>ResolverTypeId</c>. This enables automatic discovery and matching without hardcoded registrations.
/// </remarks>
[Tool]
[GlobalClass]
public partial class StatescriptResolverResource : Resource
{
/// <summary>
/// Gets the unique type identifier for this resolver, used to match serialized resources to their corresponding
/// editor. Subclasses must override this to return a non-empty string that matches their editor's
/// <c>ResolverTypeId</c>.
/// </summary>
public virtual string ResolverTypeId => string.Empty;
/// <summary>
/// Binds this resolver as an input property on a runtime node. Implementations should register any necessary
/// variable or property definitions on the graph and call <see cref="ForgeNode.BindInput"/> with the appropriate
/// name.
/// </summary>
/// <param name="graph">The runtime graph being built.</param>
/// <param name="runtimeNode">The runtime node to bind the input on.</param>
/// <param name="nodeId">The serialized node identifier, used for generating unique property names.</param>
/// <param name="index">The zero-based index of the input property to bind.</param>
public virtual void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
}
/// <summary>
/// Binds this resolver as an output variable on a runtime node. Implementations should call
/// <see cref="ForgeNode.BindOutput(byte, StringKey, VariableScope)"/> with the appropriate variable name.
/// </summary>
/// <param name="runtimeNode">The runtime node to bind the output on.</param>
/// <param name="index">The zero-based index of the output variable to bind.</param>
public virtual void BindOutput(ForgeNode runtimeNode, byte index)
{
}
/// <summary>
/// Creates an <see cref="IPropertyResolver"/> from this resolver resource. Used when this resolver appears as a
/// nested operand inside another resolver (e.g., left/right side of a comparison).
/// </summary>
/// <param name="graph">The runtime graph being built, used for looking up variable type information.</param>
/// <returns>The property resolver instance, or a default zero-value resolver if the resource is not configured.
/// </returns>
public virtual IPropertyResolver BuildResolver(Graph graph)
{
return new VariantResolver(default, typeof(int));
}
}

View File

@@ -0,0 +1 @@
uid://bmlwmm5o5prqv

View File

@@ -0,0 +1,66 @@
// Copyright © Gamesmiths Guild.
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// Enumerates the supported variable types for Statescript graph variables and node properties.
/// Maps directly to the types supported by Forge's <see cref="Forge.Statescript.Variant128"/>.
/// </summary>
public enum StatescriptVariableType
{
/// <summary>Boolean value.</summary>
Bool = 0,
/// <summary>Unsigned 8-bit integer.</summary>
Byte = 1,
/// <summary>Signed 8-bit integer.</summary>
SByte = 2,
/// <summary>Unicode character.</summary>
#pragma warning disable CA1720 // Identifier contains type name
Char = 3,
/// <summary>128-bit decimal.</summary>
Decimal = 4,
/// <summary>64-bit floating point.</summary>
Double = 5,
/// <summary>32-bit floating point.</summary>
Float = 6,
/// <summary>Signed 32-bit integer.</summary>
Int = 7,
/// <summary>Unsigned 32-bit integer.</summary>
UInt = 8,
/// <summary>Signed 64-bit integer.</summary>
Long = 9,
/// <summary>Unsigned 64-bit integer.</summary>
ULong = 10,
/// <summary>Signed 16-bit integer.</summary>
Short = 11,
/// <summary>Unsigned 16-bit integer.</summary>
UShort = 12,
#pragma warning restore CA1720 // Identifier contains type name
/// <summary>2D vector (float x, float y).</summary>
Vector2 = 13,
/// <summary>3D vector (float x, float y, float z).</summary>
Vector3 = 14,
/// <summary>4D vector (float x, float y, float z, float w).</summary>
Vector4 = 15,
/// <summary>Plane (normal + distance).</summary>
Plane = 16,
/// <summary>Quaternion rotation.</summary>
Quaternion = 17,
}

View File

@@ -0,0 +1 @@
uid://cl2gru8twj1mx

View File

@@ -0,0 +1,204 @@
// Copyright © Gamesmiths Guild.
using System;
using System.Collections.Generic;
using Gamesmiths.Forge.Statescript;
using GodotPlane = Godot.Plane;
using GodotQuaternion = Godot.Quaternion;
using GodotVariant = Godot.Variant;
using GodotVector2 = Godot.Vector2;
using GodotVector3 = Godot.Vector3;
using GodotVector4 = Godot.Vector4;
using SysVector2 = System.Numerics.Vector2;
using SysVector3 = System.Numerics.Vector3;
using SysVector4 = System.Numerics.Vector4;
namespace Gamesmiths.Forge.Godot.Resources.Statescript;
/// <summary>
/// Provides conversion utilities between <see cref="StatescriptVariableType"/>, <see cref="Type"/>, and Godot
/// <see cref="GodotVariant"/> for use in the editor and at graph build time.
/// </summary>
public static class StatescriptVariableTypeConverter
{
private static readonly Dictionary<StatescriptVariableType, Type> _typeMap = new()
{
[StatescriptVariableType.Bool] = typeof(bool),
[StatescriptVariableType.Byte] = typeof(byte),
[StatescriptVariableType.SByte] = typeof(sbyte),
[StatescriptVariableType.Char] = typeof(char),
[StatescriptVariableType.Decimal] = typeof(decimal),
[StatescriptVariableType.Double] = typeof(double),
[StatescriptVariableType.Float] = typeof(float),
[StatescriptVariableType.Int] = typeof(int),
[StatescriptVariableType.UInt] = typeof(uint),
[StatescriptVariableType.Long] = typeof(long),
[StatescriptVariableType.ULong] = typeof(ulong),
[StatescriptVariableType.Short] = typeof(short),
[StatescriptVariableType.UShort] = typeof(ushort),
[StatescriptVariableType.Vector2] = typeof(SysVector2),
[StatescriptVariableType.Vector3] = typeof(SysVector3),
[StatescriptVariableType.Vector4] = typeof(SysVector4),
[StatescriptVariableType.Plane] = typeof(System.Numerics.Plane),
[StatescriptVariableType.Quaternion] = typeof(System.Numerics.Quaternion),
};
private static readonly Dictionary<Type, StatescriptVariableType> _reverseTypeMap = [];
static StatescriptVariableTypeConverter()
{
foreach (KeyValuePair<StatescriptVariableType, Type> kvp in _typeMap)
{
_reverseTypeMap[kvp.Value] = kvp.Key;
}
}
/// <summary>
/// Gets all supported variable type values.
/// </summary>
/// <returns>All values of <see cref="StatescriptVariableType"/>.</returns>
public static StatescriptVariableType[] GetAllTypes()
{
return (StatescriptVariableType[])Enum.GetValues(typeof(StatescriptVariableType));
}
/// <summary>
/// Converts a <see cref="StatescriptVariableType"/> to the corresponding <see cref="Type"/>.
/// </summary>
/// <param name="variableType">The variable type enum value.</param>
/// <returns>The corresponding CLR type.</returns>
public static Type ToSystemType(StatescriptVariableType variableType)
{
return _typeMap[variableType];
}
/// <summary>
/// Tries to find the <see cref="StatescriptVariableType"/> for the given <see cref="Type"/>.
/// </summary>
/// <param name="type">The CLR type to look up.</param>
/// <param name="variableType">The corresponding variable type if found.</param>
/// <returns><see langword="true"/> if a matching variable type was found.</returns>
public static bool TryFromSystemType(Type type, out StatescriptVariableType variableType)
{
return _reverseTypeMap.TryGetValue(type, out variableType);
}
/// <summary>
/// Checks whether the given <see cref="Type"/> is compatible with the specified variable type.
/// </summary>
/// <remarks>
/// For <see cref="Variant128"/> (wildcard type), all types are compatible. Otherwise, strict type matching is used
/// with no implicit numeric conversions.
/// </remarks>
/// <param name="expectedType">The expected type from the node declaration.</param>
/// <param name="variableType">The variable type to check.</param>
/// <returns><see langword="true"/> if the types are compatible.</returns>
public static bool IsCompatible(Type expectedType, StatescriptVariableType variableType)
{
if (expectedType == typeof(Variant128))
{
return true;
}
Type actualType = ToSystemType(variableType);
return expectedType == actualType;
}
/// <summary>
/// Creates a default <see cref="GodotVariant"/> for the given variable type.
/// </summary>
/// <param name="variableType">The variable type.</param>
/// <returns>A Godot variant containing the default value.</returns>
public static GodotVariant CreateDefaultGodotVariant(StatescriptVariableType variableType)
{
return variableType switch
{
StatescriptVariableType.Bool => GodotVariant.From(false),
StatescriptVariableType.Byte => GodotVariant.From(0),
StatescriptVariableType.SByte => GodotVariant.From(0),
StatescriptVariableType.Char => GodotVariant.From(0),
StatescriptVariableType.Decimal => GodotVariant.From(0.0),
StatescriptVariableType.Double => GodotVariant.From(0.0),
StatescriptVariableType.Float => GodotVariant.From(0.0f),
StatescriptVariableType.Int => GodotVariant.From(0),
StatescriptVariableType.UInt => GodotVariant.From(0),
StatescriptVariableType.Long => GodotVariant.From(0L),
StatescriptVariableType.ULong => GodotVariant.From(0L),
StatescriptVariableType.Short => GodotVariant.From(0),
StatescriptVariableType.UShort => GodotVariant.From(0),
StatescriptVariableType.Vector2 => GodotVariant.From(GodotVector2.Zero),
StatescriptVariableType.Vector3 => GodotVariant.From(GodotVector3.Zero),
StatescriptVariableType.Vector4 => GodotVariant.From(GodotVector4.Zero),
StatescriptVariableType.Plane => GodotVariant.From(new GodotPlane(0, 1, 0, 0)),
StatescriptVariableType.Quaternion => GodotVariant.From(GodotQuaternion.Identity),
_ => GodotVariant.From(0),
};
}
/// <summary>
/// Converts a Godot variant value to a Forge <see cref="Variant128"/> based on the variable type.
/// </summary>
/// <param name="godotValue">The Godot variant value.</param>
/// <param name="variableType">The variable type that determines interpretation.</param>
/// <returns>The corresponding <see cref="Variant128"/>.</returns>
public static Variant128 GodotVariantToForge(GodotVariant godotValue, StatescriptVariableType variableType)
{
return variableType switch
{
StatescriptVariableType.Bool => new Variant128(godotValue.AsBool()),
StatescriptVariableType.Byte => new Variant128((byte)godotValue.AsInt32()),
StatescriptVariableType.SByte => new Variant128((sbyte)godotValue.AsInt32()),
StatescriptVariableType.Char => new Variant128((char)godotValue.AsInt32()),
StatescriptVariableType.Decimal => new Variant128((decimal)godotValue.AsDouble()),
StatescriptVariableType.Double => new Variant128(godotValue.AsDouble()),
StatescriptVariableType.Float => new Variant128(godotValue.AsSingle()),
StatescriptVariableType.Int => new Variant128(godotValue.AsInt32()),
StatescriptVariableType.UInt => new Variant128((uint)godotValue.AsInt64()),
StatescriptVariableType.Long => new Variant128(godotValue.AsInt64()),
StatescriptVariableType.ULong => new Variant128((ulong)godotValue.AsInt64()),
StatescriptVariableType.Short => new Variant128((short)godotValue.AsInt32()),
StatescriptVariableType.UShort => new Variant128((ushort)godotValue.AsInt32()),
StatescriptVariableType.Vector2 => ToForgeVector2(godotValue.AsVector2()),
StatescriptVariableType.Vector3 => ToForgeVector3(godotValue.AsVector3()),
StatescriptVariableType.Vector4 => ToForgeVector4(godotValue.AsVector4()),
StatescriptVariableType.Plane => ToForgePlane(godotValue.AsPlane()),
StatescriptVariableType.Quaternion => ToForgeQuaternion(godotValue.AsQuaternion()),
_ => default,
};
}
/// <summary>
/// Gets the display name for a variable type.
/// </summary>
/// <param name="variableType">The variable type.</param>
/// <returns>A human-readable name for the type.</returns>
public static string GetDisplayName(StatescriptVariableType variableType)
{
return variableType.ToString();
}
private static Variant128 ToForgeVector2(GodotVector2 v)
{
return new Variant128(new SysVector2(v.X, v.Y));
}
private static Variant128 ToForgeVector3(GodotVector3 v)
{
return new Variant128(new SysVector3(v.X, v.Y, v.Z));
}
private static Variant128 ToForgeVector4(GodotVector4 v)
{
return new Variant128(new SysVector4(v.X, v.Y, v.Z, v.W));
}
private static Variant128 ToForgePlane(GodotPlane p)
{
return new Variant128(new System.Numerics.Plane(p.Normal.X, p.Normal.Y, p.Normal.Z, p.D));
}
private static Variant128 ToForgeQuaternion(GodotQuaternion q)
{
return new Variant128(new System.Numerics.Quaternion(q.X, q.Y, q.Z, q.W));
}
}

View File

@@ -0,0 +1 @@
uid://dsewjg4nt7kao

View File

@@ -0,0 +1 @@
uid://cssjv42gccy3n

View File

@@ -0,0 +1,105 @@
// Copyright © Gamesmiths Guild.
using System;
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
/// <summary>
/// Resolver resource that binds a node property to a field declared by an <see cref="IActivationDataProvider"/>.
/// </summary>
/// <remarks>
/// At build time the resolver defines a graph variable for the field so that the data binder can write to it, and binds
/// the node input to that variable. At runtime the value is read from the graph's variables after the data binder has
/// populated them.
/// </remarks>
[Tool]
[GlobalClass]
public partial class ActivationDataResolverResource : StatescriptResolverResource
{
/// <inheritdoc/>
public override string ResolverTypeId => "ActivationData";
/// <summary>
/// Gets or sets the class name of the <see cref="IActivationDataProvider"/> implementation that declares the field.
/// </summary>
[Export]
public string ProviderClassName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the name of the activation data field to bind to.
/// </summary>
[Export]
public string FieldName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the expected type of the activation data field.
/// </summary>
[Export]
public StatescriptVariableType FieldType { get; set; } = StatescriptVariableType.Int;
/// <inheritdoc/>
public override void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
if (string.IsNullOrEmpty(ProviderClassName))
{
GD.PushError(
$"Statescript: Activation Data resolver on node '{nodeId}' (input {index}) " +
"has no provider selected. Select a provider and field in the graph editor.");
return;
}
if (string.IsNullOrEmpty(FieldName))
{
GD.PushError(
$"Statescript: Activation Data resolver on node '{nodeId}' (input {index}) " +
$"has provider '{ProviderClassName}' but no field selected. " +
"Select a field in the graph editor.");
return;
}
Type clrType = StatescriptVariableTypeConverter.ToSystemType(FieldType);
var variableName = new StringKey(FieldName);
// Define the variable so the data binder's SetVar call succeeds at runtime.
// Check if the variable is already defined to avoid duplicates when multiple nodes bind the same field.
var alreadyDefined = false;
foreach (VariableDefinition existing in graph.VariableDefinitions.VariableDefinitions)
{
if (existing.Name == variableName)
{
alreadyDefined = true;
break;
}
}
if (!alreadyDefined)
{
graph.VariableDefinitions.VariableDefinitions.Add(
new VariableDefinition(variableName, default, clrType));
}
runtimeNode.BindInput(index, variableName);
}
/// <inheritdoc/>
public override IPropertyResolver BuildResolver(Graph graph)
{
if (string.IsNullOrEmpty(ProviderClassName) || string.IsNullOrEmpty(FieldName))
{
GD.PushError(
"Statescript: Activation Data resolver has incomplete configuration " +
$"(provider: '{ProviderClassName}', field: '{FieldName}'). " +
"The resolver will return a default value.");
return new VariantResolver(default, typeof(int));
}
Type clrType = StatescriptVariableTypeConverter.ToSystemType(FieldType);
return new VariableResolver(new StringKey(FieldName), clrType);
}
}

View File

@@ -0,0 +1 @@
uid://dcshceitj3lc

View File

@@ -0,0 +1,56 @@
// Copyright © Gamesmiths Guild.
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
/// <summary>
/// Resolver resource that reads a value from a Forge entity attribute at runtime.
/// </summary>
[Tool]
[GlobalClass]
public partial class AttributeResolverResource : StatescriptResolverResource
{
/// <inheritdoc/>
public override string ResolverTypeId => "Attribute";
/// <summary>
/// Gets or sets the attribute set class name.
/// </summary>
[Export]
public string AttributeSetClass { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the attribute name within the attribute set.
/// </summary>
[Export]
public string AttributeName { get; set; } = string.Empty;
/// <inheritdoc/>
public override void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
if (string.IsNullOrEmpty(AttributeSetClass) || string.IsNullOrEmpty(AttributeName))
{
return;
}
var attributeKey = new StringKey($"{AttributeSetClass}.{AttributeName}");
var propertyName = new StringKey($"__attr_{nodeId}_{index}");
graph.VariableDefinitions.DefineProperty(propertyName, new AttributeResolver(attributeKey));
runtimeNode.BindInput(index, propertyName);
}
/// <inheritdoc/>
public override IPropertyResolver BuildResolver(Graph graph)
{
var attributeKey = new StringKey($"{AttributeSetClass}.{AttributeName}");
return new AttributeResolver(attributeKey);
}
}

View File

@@ -0,0 +1 @@
uid://cyiritjnyt65r

View File

@@ -0,0 +1,64 @@
// Copyright © Gamesmiths Guild.
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
/// <summary>
/// Resolver resource that compares two nested numeric resolvers and produces a boolean result.
/// </summary>
[Tool]
[GlobalClass]
public partial class ComparisonResolverResource : StatescriptResolverResource
{
/// <inheritdoc/>
public override string ResolverTypeId => "Comparison";
/// <summary>
/// Gets or sets the left-hand operand resolver.
/// </summary>
[Export]
public StatescriptResolverResource? Left { get; set; }
/// <summary>
/// Gets or sets the comparison operation.
/// </summary>
[Export]
public ComparisonOperation Operation { get; set; }
/// <summary>
/// Gets or sets the right-hand operand resolver.
/// </summary>
[Export]
public StatescriptResolverResource? Right { get; set; }
/// <inheritdoc/>
public override void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
IPropertyResolver comparisonResolver = BuildResolver(graph);
var propertyName = new StringKey($"__cmp_{nodeId}_{index}");
graph.VariableDefinitions.DefineProperty(propertyName, comparisonResolver);
runtimeNode.BindInput(index, propertyName);
}
/// <inheritdoc/>
public override IPropertyResolver BuildResolver(Graph graph)
{
IPropertyResolver leftResolver = Left?.BuildResolver(graph)
?? new VariantResolver(default, typeof(int));
IPropertyResolver rightResolver = Right?.BuildResolver(graph)
?? new VariantResolver(default, typeof(int));
var operation = (ComparisonOperation)(byte)Operation;
return new ComparisonResolver(leftResolver, operation, rightResolver);
}
}

View File

@@ -0,0 +1 @@
uid://c3hb4x0majsfa

View File

@@ -0,0 +1,38 @@
// Copyright © Gamesmiths Guild.
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
/// <summary>
/// Resolver resource that reads the ability activation magnitude from the <see cref="GraphContext.ActivationContext"/>
/// at runtime. Produces a <see langword="float"/> value.
/// </summary>
[Tool]
[GlobalClass]
public partial class MagnitudeResolverResource : StatescriptResolverResource
{
/// <inheritdoc/>
public override string ResolverTypeId => "Magnitude";
/// <inheritdoc/>
public override void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
var propertyName = new StringKey($"__mag_{nodeId}_{index}");
graph.VariableDefinitions.DefineProperty(propertyName, new MagnitudeResolver());
runtimeNode.BindInput(index, propertyName);
}
/// <inheritdoc/>
public override IPropertyResolver BuildResolver(Graph graph)
{
return new MagnitudeResolver();
}
}

View File

@@ -0,0 +1 @@
uid://88e4ahqgwac6

View File

@@ -0,0 +1,83 @@
// Copyright © Gamesmiths Guild.
using System;
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
/// <summary>
/// Resolver resource that binds a node property to an entity's shared variable by name. At runtime the value is read
/// from the <see cref="GraphContext.SharedVariables"/> bag, which is populated from the entity's
/// <see cref="ForgeSharedVariableSet"/>.
/// </summary>
[Tool]
[GlobalClass]
public partial class SharedVariableResolverResource : StatescriptResolverResource
{
/// <inheritdoc/>
public override string ResolverTypeId => "SharedVariable";
/// <summary>
/// Gets or sets the resource path of the <see cref="ForgeSharedVariableSet"/> that defines the variable.
/// </summary>
[Export]
public string SharedVariableSetPath { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the name of the shared variable to bind to.
/// </summary>
[Export]
public string VariableName { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the expected type of the shared variable.
/// </summary>
[Export]
public StatescriptVariableType VariableType { get; set; } = StatescriptVariableType.Int;
/// <inheritdoc/>
public override void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
if (string.IsNullOrEmpty(VariableName))
{
return;
}
Type clrType = StatescriptVariableTypeConverter.ToSystemType(VariableType);
var propertyName = new StringKey($"__shared_{nodeId}_{index}");
graph.VariableDefinitions.DefineProperty(
propertyName,
new SharedVariableResolver(new StringKey(VariableName), clrType));
runtimeNode.BindInput(index, propertyName);
}
/// <inheritdoc/>
public override void BindOutput(ForgeNode runtimeNode, byte index)
{
if (string.IsNullOrEmpty(VariableName))
{
return;
}
runtimeNode.BindOutput(index, new StringKey(VariableName), VariableScope.Shared);
}
/// <inheritdoc/>
public override IPropertyResolver BuildResolver(Graph graph)
{
if (string.IsNullOrEmpty(VariableName))
{
return new VariantResolver(default, typeof(int));
}
Type clrType = StatescriptVariableTypeConverter.ToSystemType(VariableType);
return new SharedVariableResolver(new StringKey(VariableName), clrType);
}
}

View File

@@ -0,0 +1 @@
uid://v0ukqap4vj40

View File

@@ -0,0 +1,56 @@
// Copyright © Gamesmiths Guild.
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Godot.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
/// <summary>
/// Resolver resource that checks whether the owner entity has a given tag, resolving to a boolean value at runtime.
/// </summary>
[Tool]
[GlobalClass]
public partial class TagResolverResource : StatescriptResolverResource
{
/// <inheritdoc/>
public override string ResolverTypeId => "Tag";
/// <summary>
/// Gets or sets the tag string to check for (e.g., "Status.Burning").
/// </summary>
[Export]
public string Tag { get; set; } = string.Empty;
/// <inheritdoc/>
public override void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
if (string.IsNullOrEmpty(Tag))
{
return;
}
var tag = Tags.Tag.RequestTag(ForgeManagers.Instance.TagsManager, Tag);
var propertyName = new StringKey($"__tag_{nodeId}_{index}");
graph.VariableDefinitions.DefineProperty(propertyName, new TagResolver(tag));
runtimeNode.BindInput(index, propertyName);
}
/// <inheritdoc/>
public override IPropertyResolver BuildResolver(Graph graph)
{
if (string.IsNullOrEmpty(Tag))
{
return new VariantResolver(new Variant128(false), typeof(bool));
}
var tag = Tags.Tag.RequestTag(ForgeManagers.Instance.TagsManager, Tag);
return new TagResolver(tag);
}
}

View File

@@ -0,0 +1 @@
uid://bmkcmsbydtode

View File

@@ -0,0 +1,85 @@
// Copyright © Gamesmiths Guild.
using System;
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
/// <summary>
/// Resolver resource that binds a node property to a graph variable by name.
/// </summary>
[Tool]
[GlobalClass]
public partial class VariableResolverResource : StatescriptResolverResource
{
/// <inheritdoc/>
public override string ResolverTypeId => "Variable";
/// <summary>
/// Gets or sets the name of the graph variable to bind to.
/// </summary>
[Export]
public string VariableName { get; set; } = string.Empty;
/// <inheritdoc/>
public override void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
if (string.IsNullOrEmpty(VariableName))
{
return;
}
runtimeNode.BindInput(index, new StringKey(VariableName));
}
/// <inheritdoc/>
public override void BindOutput(ForgeNode runtimeNode, byte index)
{
if (string.IsNullOrEmpty(VariableName))
{
return;
}
runtimeNode.BindOutput(index, new StringKey(VariableName));
}
/// <inheritdoc/>
public override IPropertyResolver BuildResolver(Graph graph)
{
if (string.IsNullOrEmpty(VariableName))
{
return new VariantResolver(default, typeof(int));
}
Type? variableType = FindGraphVariableType(graph, VariableName);
return new VariableResolver(new StringKey(VariableName), variableType ?? typeof(int));
}
private static Type? FindGraphVariableType(Graph graph, string variableName)
{
var key = new StringKey(variableName);
foreach (VariableDefinition def in graph.VariableDefinitions.VariableDefinitions)
{
if (def.Name == key)
{
return def.ValueType;
}
}
foreach (ArrayVariableDefinition definition in graph.VariableDefinitions.ArrayVariableDefinitions)
{
if (definition.Name == key)
{
return definition.ElementType;
}
}
return null;
}
}

View File

@@ -0,0 +1 @@
uid://bso385kpryjl

View File

@@ -0,0 +1,91 @@
// Copyright © Gamesmiths Guild.
using System;
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Statescript.Properties;
using Godot;
using Godot.Collections;
using ForgeNode = Gamesmiths.Forge.Statescript.Node;
namespace Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
/// <summary>
/// Resolver resource that holds a constant (inline) value for a node property.
/// </summary>
[Tool]
[GlobalClass]
public partial class VariantResolverResource : StatescriptResolverResource
{
/// <inheritdoc/>
public override string ResolverTypeId => "Variant";
/// <summary>
/// Gets or sets the constant value. Used when <see cref="IsArray"/> is <see langword="false"/>.
/// </summary>
[Export]
public Variant Value { get; set; }
/// <summary>
/// Gets or sets the type interpretation for the value.
/// </summary>
[Export]
public StatescriptVariableType ValueType { get; set; } = StatescriptVariableType.Int;
/// <summary>
/// Gets or sets a value indicating whether this resolver holds an array of values.
/// </summary>
[Export]
public bool IsArray { get; set; }
/// <summary>
/// Gets or sets the array values. Used when <see cref="IsArray"/> is <see langword="true"/>.
/// </summary>
[Export]
public Array<Variant> ArrayValues { get; set; } = [];
/// <summary>
/// Gets or sets a value indicating whether the array section is expanded in the editor.
/// </summary>
[Export]
public bool IsArrayExpanded { get; set; }
/// <inheritdoc/>
public override void BindInput(Graph graph, ForgeNode runtimeNode, string nodeId, byte index)
{
var propertyName = new StringKey($"__const_{nodeId}_{index}");
if (IsArray)
{
var values = new Variant128[ArrayValues.Count];
for (var i = 0; i < ArrayValues.Count; i++)
{
values[i] = StatescriptVariableTypeConverter.GodotVariantToForge(ArrayValues[i], ValueType);
}
Type clrType = StatescriptVariableTypeConverter.ToSystemType(ValueType);
graph.VariableDefinitions.ArrayVariableDefinitions.Add(
new ArrayVariableDefinition(propertyName, values, clrType));
}
else
{
Variant128 value = StatescriptVariableTypeConverter.GodotVariantToForge(Value, ValueType);
Type clrType = StatescriptVariableTypeConverter.ToSystemType(ValueType);
graph.VariableDefinitions.PropertyDefinitions.Add(
new PropertyDefinition(propertyName, new VariantResolver(value, clrType)));
}
runtimeNode.BindInput(index, propertyName);
}
/// <inheritdoc/>
public override IPropertyResolver BuildResolver(Graph graph)
{
Variant128 value = StatescriptVariableTypeConverter.GodotVariantToForge(Value, ValueType);
Type clrType = StatescriptVariableTypeConverter.ToSystemType(ValueType);
return new VariantResolver(value, clrType);
}
}

View File

@@ -0,0 +1 @@
uid://db2rt0n3jtytf