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,328 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using Gamesmiths.Forge.Godot.Resources.Statescript;
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Statescript;
public partial class StatescriptGraphEditorDock
{
private void OnFileMenuIdPressed(long id)
{
switch ((int)id)
{
case 0:
ShowNewStatescriptDialog();
break;
case 1:
ShowLoadStatescriptDialog();
break;
case 2:
OnSavePressed();
break;
case 3:
ShowSaveAsDialog();
break;
case 4:
CloseCurrentTab();
break;
}
}
private void ShowNewStatescriptDialog()
{
_newStatescriptDialog?.QueueFree();
_newStatescriptDialog = new AcceptDialog
{
Title = "Create Statescript",
Size = new Vector2I(400, 140),
Exclusive = true,
};
var vBox = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
var pathRow = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
vBox.AddChild(pathRow);
pathRow.AddChild(new Label { Text = "Path:", CustomMinimumSize = new Vector2(50, 0) });
_newStatescriptPathEdit = new LineEdit
{
Text = "res://new_statescript.tres",
SizeFlagsHorizontal = SizeFlags.ExpandFill,
};
pathRow.AddChild(_newStatescriptPathEdit);
_newStatescriptDialog.AddChild(vBox);
_newStatescriptDialog.Confirmed += OnNewStatescriptConfirmed;
AddChild(_newStatescriptDialog);
_newStatescriptDialog.PopupCentered();
}
private void OnNewStatescriptConfirmed()
{
if (_newStatescriptPathEdit is null)
{
return;
}
var path = _newStatescriptPathEdit.Text.Trim();
if (string.IsNullOrEmpty(path))
{
return;
}
if (!path.EndsWith(".tres", System.StringComparison.OrdinalIgnoreCase))
{
path += ".tres";
}
var graph = new StatescriptGraph();
graph.EnsureEntryNode();
graph.StatescriptName = path.GetFile().GetBaseName();
ResourceSaver.Save(graph, path);
EditorInterface.Singleton.GetResourceFilesystem().Scan();
graph = ResourceLoader.Load<StatescriptGraph>(path);
if (graph is not null)
{
OpenGraph(graph);
}
_newStatescriptDialog?.QueueFree();
_newStatescriptDialog = null;
}
private void ShowLoadStatescriptDialog()
{
var dialog = new EditorFileDialog
{
FileMode = FileDialog.FileModeEnum.OpenFile,
Title = "Load Statescript File",
Access = FileDialog.AccessEnum.Resources,
};
dialog.AddFilter("*.tres;StatescriptGraph");
dialog.FileSelected += path =>
{
Resource? graph = ResourceLoader.Load(path);
if (graph is StatescriptGraph statescriptGraph)
{
OpenGraph(statescriptGraph);
}
else
{
GD.PushWarning($"Failed to load StatescriptGraph from: {path}");
}
dialog.QueueFree();
};
dialog.Canceled += dialog.QueueFree;
AddChild(dialog);
dialog.PopupCentered(new Vector2I(700, 500));
}
private void ShowSaveAsDialog()
{
StatescriptGraph? graph = CurrentGraph;
if (graph is null)
{
return;
}
var dialog = new EditorFileDialog
{
FileMode = FileDialog.FileModeEnum.SaveFile,
Title = "Save Statescript As",
Access = FileDialog.AccessEnum.Resources,
};
dialog.AddFilter("*.tres", "Godot Resource");
dialog.FileSelected += path =>
{
if (_graphEdit is not null)
{
graph.ScrollOffset = _graphEdit.ScrollOffset;
graph.Zoom = _graphEdit.Zoom;
SyncVisualNodePositionsToGraph();
SyncConnectionsToCurrentGraph();
}
ResourceSaver.Save(graph, path);
EditorInterface.Singleton.GetResourceFilesystem().Scan();
GD.Print($"Statescript graph saved as: {path}");
StatescriptGraph? savedGraph = ResourceLoader.Load<StatescriptGraph>(path);
if (savedGraph is not null)
{
OpenGraph(savedGraph);
}
dialog.QueueFree();
};
dialog.Canceled += dialog.QueueFree;
AddChild(dialog);
dialog.PopupCentered(new Vector2I(700, 500));
}
private void OnSavePressed()
{
StatescriptGraph? graph = CurrentGraph;
if (graph is null || _graphEdit is null)
{
return;
}
graph.ScrollOffset = _graphEdit.ScrollOffset;
graph.Zoom = _graphEdit.Zoom;
SyncVisualNodePositionsToGraph();
SyncConnectionsToCurrentGraph();
if (string.IsNullOrEmpty(graph.ResourcePath))
{
ShowSaveAsDialog();
return;
}
SaveGraphResource(graph);
GD.Print($"Statescript graph saved: {graph.ResourcePath}");
}
private void OnGraphEditPopupRequest(Vector2 atPosition)
{
if (CurrentGraph is null || _graphEdit is null || _addNodeDialog is null)
{
return;
}
ClearPendingConnection();
Vector2 graphPosition = (_graphEdit.ScrollOffset + atPosition) / _graphEdit.Zoom;
var screenPosition = (Vector2I)(_graphEdit.GetScreenPosition() + atPosition);
_addNodeDialog.ShowAtPosition(graphPosition, screenPosition);
}
private void OnConnectionToEmpty(StringName fromNode, long fromPort, Vector2 releasePosition)
{
if (CurrentGraph is null || _graphEdit is null || _addNodeDialog is null)
{
return;
}
_pendingConnectionNode = fromNode;
_pendingConnectionPort = (int)fromPort;
_pendingConnectionIsOutput = true;
Vector2 graphPosition = (_graphEdit.ScrollOffset + releasePosition) / _graphEdit.Zoom;
var screenPosition = (Vector2I)(_graphEdit.GetScreenPosition() + releasePosition);
_addNodeDialog.ShowAtPosition(graphPosition, screenPosition);
}
private void OnConnectionFromEmpty(StringName toNode, long toPort, Vector2 releasePosition)
{
if (CurrentGraph is null || _graphEdit is null || _addNodeDialog is null)
{
return;
}
_pendingConnectionNode = toNode;
_pendingConnectionPort = (int)toPort;
_pendingConnectionIsOutput = false;
Vector2 graphPosition = (_graphEdit.ScrollOffset + releasePosition) / _graphEdit.Zoom;
var screenPosition = (Vector2I)(_graphEdit.GetScreenPosition() + releasePosition);
_addNodeDialog.ShowAtPosition(graphPosition, screenPosition);
}
private void OnDialogNodeCreationRequested(
StatescriptNodeDiscovery.NodeTypeInfo? typeInfo,
StatescriptNodeType nodeType,
Vector2 position)
{
string newNodeId;
if (typeInfo is not null)
{
newNodeId = AddNodeAtPosition(nodeType, typeInfo.DisplayName, typeInfo.RuntimeTypeName, position);
}
else
{
newNodeId = AddNodeAtPosition(StatescriptNodeType.Exit, "Exit", string.Empty, position);
}
if (_pendingConnectionNode is not null && _graphEdit is not null)
{
if (_pendingConnectionIsOutput)
{
var inputPort = FindFirstEnabledInputPort(newNodeId);
if (inputPort >= 0)
{
OnConnectionRequest(
_pendingConnectionNode,
_pendingConnectionPort,
newNodeId,
inputPort);
}
}
else
{
var outputPort = FindFirstEnabledOutputPort(newNodeId);
if (outputPort >= 0)
{
OnConnectionRequest(
newNodeId,
outputPort,
_pendingConnectionNode,
_pendingConnectionPort);
}
}
ClearPendingConnection();
}
}
private void OnDialogCanceled()
{
ClearPendingConnection();
}
private void ClearPendingConnection()
{
_pendingConnectionNode = null;
_pendingConnectionPort = 0;
_pendingConnectionIsOutput = false;
}
private void OnAddNodeButtonPressed()
{
if (CurrentGraph is null || _graphEdit is null || _addNodeDialog is null || _addNodeButton is null)
{
return;
}
ClearPendingConnection();
var screenPosition = (Vector2I)(_addNodeButton.GetScreenPosition() + new Vector2(0, _addNodeButton.Size.Y));
Vector2 centerPosition = (_graphEdit.ScrollOffset + (_graphEdit.Size / 2)) / _graphEdit.Zoom;
_addNodeDialog.ShowAtPosition(centerPosition, screenPosition);
}
}
#endif