// 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("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 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(obj.InitialAttributeValues) { [name] = new AttributeValues(def, min, max), }; EmitChanged(nameof(ForgeAttributeSet.InitialAttributeValues), dict); } } #endif