added forge addon
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 21s
Create tag and build when new code gets to main / Test (push) Successful in 6m56s
Create tag and build when new code gets to main / Export (push) Successful in 9m3s

This commit is contained in:
2026-02-08 15:16:01 +01:00
parent 2b74c9e70c
commit c4be97e0de
163 changed files with 6975 additions and 141 deletions

View File

@@ -0,0 +1,34 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
[Tool]
public partial class AttributeEditorPlugin : EditorInspectorPlugin
{
public override bool _CanHandle(GodotObject @object)
{
return @object is Resources.ForgeModifier || @object is Resources.ForgeCue;
}
public override bool _ParseProperty(
GodotObject @object,
Variant.Type type,
string name,
PropertyHint hintType,
string hintString,
PropertyUsageFlags usageFlags,
bool wide)
{
if (name == "Attribute" || name == "CapturedAttribute" || name == "MagnitudeAttribute")
{
AddPropertyEditor(name, new AttributeEditorProperty());
return true;
}
return false;
}
}
#endif

View File

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

View File

@@ -0,0 +1,101 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
[Tool]
public partial class AttributeEditorProperty : EditorProperty
{
private const int ButtonSize = 26;
private const int PopupSize = 300;
private Label _label = null!;
public override void _Ready()
{
Texture2D dropdownIcon = EditorInterface.Singleton
.GetEditorTheme()
.GetIcon("GuiDropdown", "EditorIcons");
var hbox = new HBoxContainer();
_label = new Label { Text = "None", SizeFlagsHorizontal = SizeFlags.ExpandFill };
var button = new Button { Icon = dropdownIcon, CustomMinimumSize = new Vector2(ButtonSize, 0) };
hbox.AddChild(_label);
hbox.AddChild(button);
AddChild(hbox);
var popup = new Popup { Size = new Vector2I(PopupSize, PopupSize) };
var tree = new Tree
{
HideRoot = true,
AnchorRight = 1,
AnchorBottom = 1,
};
popup.AddChild(tree);
var bg = new StyleBoxFlat
{
BgColor = EditorInterface.Singleton
.GetEditorTheme()
.GetColor("dark_color_2", "Editor"),
};
tree.AddThemeStyleboxOverride("panel", bg);
AddChild(popup);
BuildAttributeTree(tree);
button.Pressed += () =>
{
Window win = GetWindow();
popup.Position = (Vector2I)button.GlobalPosition
+ win.Position
- new Vector2I(PopupSize - ButtonSize, -30);
popup.Popup();
};
tree.ItemActivated += () =>
{
TreeItem item = tree.GetSelected();
if (item?.HasMeta("attribute_path") != true)
{
return;
}
var fullPath = item.GetMeta("attribute_path").AsString();
_label.Text = fullPath;
EmitChanged(GetEditedProperty(), fullPath);
popup.Hide();
};
}
public override void _UpdateProperty()
{
var value = GetEditedObject().Get(GetEditedProperty()).AsString();
_label.Text = string.IsNullOrEmpty(value) ? "None" : value;
}
private static void BuildAttributeTree(Tree tree)
{
TreeItem root = tree.CreateItem();
foreach (var attributeSet in EditorUtils.GetAttributeSetOptions())
{
TreeItem setItem = tree.CreateItem(root);
setItem.SetText(0, attributeSet);
setItem.Collapsed = true;
foreach (var attribute in EditorUtils.GetAttributeOptions(attributeSet))
{
TreeItem attributeItem = tree.CreateItem(setItem);
var attributePath = $"{attributeSet}.{attribute}";
attributeItem.SetText(0, attribute);
attributeItem.SetMeta("attribute_path", attributePath);
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,74 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Linq;
using System.Reflection;
using Gamesmiths.Forge.Attributes;
using Gamesmiths.Forge.Godot.Nodes;
using Godot;
using Godot.Collections;
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
[Tool]
public partial class AttributeSetClassEditorProperty : EditorProperty
{
private OptionButton _optionButton = null!;
public override void _Ready()
{
_optionButton = new OptionButton();
AddChild(_optionButton);
_optionButton.AddItem("Select AttributeSet Class");
foreach (var option in EditorUtils.GetAttributeSetOptions())
{
_optionButton.AddItem(option);
}
_optionButton.ItemSelected += x =>
{
var className = _optionButton.GetItemText((int)x);
EmitChanged(GetEditedProperty(), className);
GodotObject obj = GetEditedObject();
if (obj is not null)
{
var dict = new Dictionary<string, AttributeValues>();
var assembly = Assembly.GetAssembly(typeof(ForgeAttributeSet));
Type? targetType = System.Array.Find(assembly?.GetTypes() ?? [], x => x.Name == className);
if (targetType is not null)
{
System.Collections.Generic.IEnumerable<PropertyInfo> attrProps = targetType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.PropertyType == typeof(EntityAttribute));
foreach (PropertyInfo? pi in attrProps)
{
dict[pi.Name] = new AttributeValues(0, 0, int.MaxValue);
}
}
EmitChanged("InitialAttributeValues", dict);
}
};
}
public override void _UpdateProperty()
{
GodotObject obj = GetEditedObject();
StringName property = GetEditedProperty();
var val = obj.Get(property).AsString();
for (var i = 0; i < _optionButton.GetItemCount(); i++)
{
if (_optionButton.GetItemText(i) == val)
{
_optionButton.Selected = i;
break;
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,43 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using Gamesmiths.Forge.Godot.Nodes;
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
[Tool]
public partial class AttributeSetInspectorPlugin : EditorInspectorPlugin
{
private PackedScene? _inspectorScene;
public override bool _CanHandle(GodotObject @object)
{
return @object is ForgeAttributeSet;
}
public override bool _ParseProperty(
GodotObject @object,
Variant.Type type,
string name,
PropertyHint hintType,
string hintString,
PropertyUsageFlags usageFlags,
bool wide)
{
if (name == "AttributeSetClass")
{
AddPropertyEditor(name, new AttributeSetClassEditorProperty());
return true;
}
if (name == "InitialAttributeValues")
{
AddPropertyEditor(name, new AttributeSetValuesEditorProperty());
return true;
}
return false;
}
}
#endif

View File

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

View File

@@ -0,0 +1,171 @@
// Copyright © Gamesmiths Guild.
#if TOOLS
using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Gamesmiths.Forge.Attributes;
using Gamesmiths.Forge.Godot.Nodes;
using Godot;
using Godot.Collections;
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
[Tool]
public partial class AttributeSetValuesEditorProperty : EditorProperty
{
public override void _Ready()
{
var attributesRoot = new VBoxContainer { Name = "AttributesRoot" };
AddChild(attributesRoot);
SetBottomEditor(attributesRoot);
}
public override void _UpdateProperty()
{
VBoxContainer attributesRoot = GetNodeOrNull<VBoxContainer>("AttributesRoot");
if (attributesRoot is null)
{
return;
}
FreeAllChildren(attributesRoot);
if (GetEditedObject() is not ForgeAttributeSet obj
|| string.IsNullOrEmpty(obj.AttributeSetClass)
|| obj.InitialAttributeValues is null)
{
return;
}
var className = obj.AttributeSetClass;
var assembly = Assembly.GetAssembly(typeof(ForgeAttributeSet));
Type? targetType = System.Array.Find(assembly?.GetTypes() ?? [], x => x.Name == className);
if (targetType is null)
{
return;
}
System.Collections.Generic.IEnumerable<PropertyInfo> attributeProperties = targetType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.PropertyType == typeof(EntityAttribute));
foreach (var attributeName in attributeProperties.Select(x => x.Name))
{
var groupVBox = new VBoxContainer();
groupVBox.AddChild(AttributeHeader(attributeName));
AttributeValues value = obj.InitialAttributeValues.TryGetValue(attributeName, out AttributeValues? v)
? v
: new AttributeValues(0, 0, int.MaxValue);
SpinBox spinDefault = CreateSpinBox(value.Min, value.Max, value.Default);
SpinBox spinMin = CreateSpinBox(int.MinValue, value.Max, value.Min);
SpinBox spinMax = CreateSpinBox(value.Min, int.MaxValue, value.Max);
groupVBox.AddChild(AttributeFieldRow("Default", spinDefault));
groupVBox.AddChild(AttributeFieldRow("Min", spinMin));
groupVBox.AddChild(AttributeFieldRow("Max", spinMax));
spinDefault.ValueChanged += x =>
{
UpdateAndEmit(obj, attributeName, (int)x, (int)spinMin.Value, (int)spinMax.Value);
};
spinMin.ValueChanged += x =>
{
spinDefault.MinValue = x;
spinMax.MinValue = x;
UpdateAndEmit(obj, attributeName, (int)spinDefault.Value, (int)x, (int)spinMax.Value);
};
spinMax.ValueChanged += x =>
{
spinDefault.MaxValue = x;
spinMin.MaxValue = x;
UpdateAndEmit(obj, attributeName, (int)spinDefault.Value, (int)spinMin.Value, (int)x);
};
attributesRoot.AddChild(groupVBox);
}
}
private static PanelContainer AttributeHeader(string text)
{
var headerPanel = new PanelContainer
{
CustomMinimumSize = new Vector2(0, 28),
};
var style = new StyleBoxFlat
{
BgColor = new Color(0.16f, 0.17f, 0.20f),
};
headerPanel.AddThemeStyleboxOverride("panel", style);
var label = new Label
{
Text = text,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
SizeFlagsHorizontal = (SizeFlags)(int)SizeFlags.ExpandFill,
CustomMinimumSize = new Vector2(0, 22),
AutowrapMode = TextServer.AutowrapMode.Off,
};
headerPanel.AddChild(label);
return headerPanel;
}
private static HBoxContainer AttributeFieldRow(string label, SpinBox spinBox)
{
var hbox = new HBoxContainer();
hbox.AddChild(new Label
{
Text = label,
CustomMinimumSize = new Vector2(80, 0),
SizeFlagsHorizontal = SizeFlags.ExpandFill,
});
hbox.AddChild(spinBox);
return hbox;
}
private static SpinBox CreateSpinBox(int min, int max, int value)
{
return new SpinBox
{
MinValue = min,
MaxValue = max,
Value = value,
SizeFlagsHorizontal = SizeFlags.ExpandFill,
};
}
private static void FreeAllChildren(Node node)
{
for (var i = node.GetChildCount() - 1; i >= 0; i--)
{
node.GetChild(i).QueueFree();
}
}
private void UpdateAndEmit(ForgeAttributeSet obj, string name, int def, int min, int max)
{
Debug.Assert(obj.InitialAttributeValues is not null, "InitialAttributeValues should not be null here.");
var dict = new Dictionary<string, AttributeValues>(obj.InitialAttributeValues)
{
[name] = new AttributeValues(def, min, max),
};
EmitChanged(nameof(ForgeAttributeSet.InitialAttributeValues), dict);
}
}
#endif

View File

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

View File

@@ -0,0 +1,29 @@
// Copyright © Gamesmiths Guild.
using Godot;
namespace Gamesmiths.Forge.Godot.Editor.Attributes;
[Tool]
public partial class AttributeValues : Resource
{
[Export]
public int Default { get; set; }
[Export]
public int Min { get; set; }
[Export]
public int Max { get; set; }
public AttributeValues()
{
}
public AttributeValues(int @default, int min, int max)
{
Default = @default;
Min = min;
Max = max;
}
}

View File

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