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

@@ -19,7 +19,7 @@ public partial class ForgeAbilityData : Resource
private AbilityInstancingPolicy _instancingPolicy;
[Export]
public string Name { get; set; } = string.Empty;
public string Name { get; set; } = "New Ability";
[Export]
public AbilityInstancingPolicy InstancingPolicy

View File

@@ -0,0 +1,102 @@
// Copyright © Gamesmiths Guild.
using System;
using System.Linq;
using Gamesmiths.Forge.Abilities;
using Gamesmiths.Forge.Godot.Core;
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Gamesmiths.Forge.Godot.Resources.Statescript.Resolvers;
using Gamesmiths.Forge.Statescript;
using Godot;
namespace Gamesmiths.Forge.Godot.Resources.Abilities;
/// <summary>
/// A <see cref="ForgeAbilityBehavior"/> implementation that creates a <see cref="GraphAbilityBehavior"/> from a
/// serialized <see cref="StatescriptGraph"/> resource. The graph is built once and cached, then shared across all
/// ability instances using the Flyweight pattern. Each <see cref="GraphAbilityBehavior"/> creates its own
/// <see cref="GraphProcessor"/> with independent <see cref="GraphContext"/> state.
/// </summary>
/// <remarks>
/// If any node in the graph uses an <see cref="ActivationDataResolverResource"/>, the behavior automatically detects
/// the associated <see cref="IActivationDataProvider"/> implementation and produces a
/// <see cref="GraphAbilityBehavior{TData}"/> with a data binder that maps activation data fields into graph variables.
/// When no activation data resolver is present, a plain <see cref="GraphAbilityBehavior"/> (without data
/// support) is created.
/// </remarks>
[Tool]
[GlobalClass]
[Icon("uid://b6yrjb46fluw3")]
public partial class StatescriptAbilityBehavior : ForgeAbilityBehavior
{
private Graph? _cachedGraph;
private IActivationDataProvider? _cachedProvider;
private bool _providerResolved;
/// <summary>
/// Gets or sets the Statescript graph resource that defines the ability's behavior.
/// </summary>
[Export]
public StatescriptGraph? Statescript { get; set; }
/// <inheritdoc/>
public override IAbilityBehavior GetBehavior()
{
if (Statescript is null)
{
GD.PushError("StatescriptAbilityBehavior: Statescript is null.");
throw new InvalidOperationException("StatescriptAbilityBehavior requires a valid Statescript assigned.");
}
_cachedGraph ??= StatescriptGraphBuilder.Build(Statescript);
if (!_providerResolved)
{
_cachedProvider = FindActivationDataProvider(Statescript);
_providerResolved = true;
}
if (_cachedProvider is not null)
{
return _cachedProvider.CreateBehavior(_cachedGraph);
}
return new GraphAbilityBehavior(_cachedGraph);
}
private static IActivationDataProvider? FindActivationDataProvider(StatescriptGraph graph)
{
foreach (StatescriptNode node in graph.Nodes)
{
foreach (StatescriptNodeProperty binding in node.PropertyBindings)
{
if (binding.Resolver is ActivationDataResolverResource { ProviderClassName.Length: > 0 } resolver)
{
return InstantiateProvider(resolver.ProviderClassName);
}
}
}
return null;
}
private static IActivationDataProvider? InstantiateProvider(string className)
{
Type? type = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.FirstOrDefault(
x => typeof(IActivationDataProvider).IsAssignableFrom(x)
&& !x.IsAbstract
&& !x.IsInterface
&& x.Name == className);
if (type is null)
{
return null;
}
return Activator.CreateInstance(type) as IActivationDataProvider;
}
}

View File

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