From 032836b9ec3eacde154f1b5d289c7ae1a8bfdff6 Mon Sep 17 00:00:00 2001 From: Nicola Date: Fri, 8 May 2026 11:45:46 +0200 Subject: [PATCH] Improved research display and added ResearchState. --- Scripts/Helpers/GameData.cs | 2 +- Scripts/Helpers/ResourceLoader.cs | 6 +- Scripts/Research/Research.cs | 10 +- Scripts/Research/ResearchList.cs | 194 +++++++++++++++++++++++--- Scripts/Research/ResearchState.cs | 7 + Scripts/Research/ResearchState.cs.uid | 1 + 6 files changed, 191 insertions(+), 29 deletions(-) create mode 100644 Scripts/Research/ResearchState.cs create mode 100644 Scripts/Research/ResearchState.cs.uid diff --git a/Scripts/Helpers/GameData.cs b/Scripts/Helpers/GameData.cs index 3a36efc..58a68bc 100644 --- a/Scripts/Helpers/GameData.cs +++ b/Scripts/Helpers/GameData.cs @@ -19,7 +19,7 @@ public partial class GameData public static float tileWidth = 6; public static float tileHeight = 4; public static SortedDictionary availableItems = ResourceLoader.LoadItems(); - public static Dictionary availableResearch = ResourceLoader.LoadResearch(); + public static Dictionary availableResearch = ResourceLoader.LoadResearch(); //--- PLAYER ADJUSTABLE VALUES --- //Color used in primary objects (e.g. Robots) diff --git a/Scripts/Helpers/ResourceLoader.cs b/Scripts/Helpers/ResourceLoader.cs index 3b51029..39965d5 100644 --- a/Scripts/Helpers/ResourceLoader.cs +++ b/Scripts/Helpers/ResourceLoader.cs @@ -102,18 +102,18 @@ public partial class ResourceLoader return result; } - public static Dictionary LoadResearch() + public static Dictionary LoadResearch() { FileAccess file = FileAccess.Open("res://Assets/Research.json", FileAccess.ModeFlags.Read); string json = file.GetAsText(); - Dictionary result = new(); + Dictionary result = new(); List researches = JsonSerializer.Deserialize>(json); foreach (ResearchData research in researches) { - result.Add(research.Id, research); + result.Add(research.Id, new Research(research)); } return result; diff --git a/Scripts/Research/Research.cs b/Scripts/Research/Research.cs index 0a4c33c..c818120 100644 --- a/Scripts/Research/Research.cs +++ b/Scripts/Research/Research.cs @@ -3,9 +3,15 @@ using Godot; public class Research { public ResearchData data; - public bool isResearched = false; public double elapsedResearchTime = 0; public bool paidResources = false; + public ResearchState state; + + public Research(ResearchData data) + { + this.data = data; + state = ResearchState.UNDEFINED; + } public ResearchResult Execute(double delta) { @@ -20,7 +26,7 @@ public class Research elapsedResearchTime += delta; if (elapsedResearchTime >= data.CraftTime) { - isResearched = true; + state = ResearchState.RESEARCHED; return ResearchResult.FINISHED; } return ResearchResult.RESEARCHING; diff --git a/Scripts/Research/ResearchList.cs b/Scripts/Research/ResearchList.cs index 2700722..e2ef7f1 100644 --- a/Scripts/Research/ResearchList.cs +++ b/Scripts/Research/ResearchList.cs @@ -1,49 +1,118 @@ +using System.Collections.Generic; +using System.Linq; using Godot; +using Godot.Collections; public partial class ResearchList : PanelContainer { - [Export] GraphEdit researchGraph; + [Export] private GraphEdit researchGraph; + + private const float StartXDivisor = 32f; + private const float StartYDivisor = 4f; + private const float NodeSpacingMultiplier = 10f; + + private List reloadKeys = new List(); public override void _Ready() { - foreach (ResearchData research in GameData.availableResearch.Values) + RecalculateResearchStates(); + SetupGraph(); + } + + public void SetupGraph() + { + reloadKeys = new List(); + ClearGraph(); + CreateResearchNodes(); + CreateResearchConnections(); + } + + private void ClearGraph() + { + foreach (Dictionary connection in researchGraph.GetConnectionList()) { - researchGraph.AddChild(CreateItemNode(research.Id, research.Texture)); + researchGraph.DisconnectNode( + connection["from_node"].AsStringName(), + (int)connection["from_port"], + connection["to_node"].AsStringName(), + (int)connection["to_port"] + ); } - foreach (ResearchData research in GameData.availableResearch.Values) + + foreach (Node child in researchGraph.GetChildren()) { + if (child is GraphNode) + { + researchGraph.RemoveChild(child); + child.QueueFree(); + } + } + } + + private void CreateResearchNodes() + { + foreach (Research research in GameData.availableResearch.Values) + { + GD.Print(research.state); + GraphNode node = CreateResearchNode( + research.data.Id, + research.data.Texture, + research.state + ); + + researchGraph.AddChild(node); + } + } + + private void CreateResearchConnections() + { + foreach (Research research in GameData.availableResearch.Values) + { + string prerequisite = research.data.Research; + string current = research.data.Id; + + if (string.IsNullOrEmpty(prerequisite)) + continue; + + if (!researchGraph.HasNode(prerequisite) || !researchGraph.HasNode(current)) + continue; + researchGraph.ConnectNode( - research.Research, - 0, - research.Id, - 0 - ); + prerequisite, + 0, + current, + 0 + ); } } - public override void _Process(double delta) + private GraphNode CreateResearchNode(string id, string texturePath, ResearchState state) { + Texture2D texture = GD.Load(texturePath); + Color stateColor = GetColorByState(state); - } - - private GraphNode CreateItemNode(string id, string texturePath) - { - Vector2 viewportSize = GetViewport().GetVisibleRect().Size; TextureRect icon = new TextureRect { - Texture = GD.Load(texturePath), - StretchMode = TextureRect.StretchModeEnum.KeepAspectCentered + Texture = texture, + StretchMode = TextureRect.StretchModeEnum.KeepAspectCentered, + CustomMinimumSize = new Vector2(64, 64), + SelfModulate = stateColor }; - //Step 1: Place the node in the visible field for the player - Vector2 nodePosition = new Vector2(viewportSize.X / 32, viewportSize.Y / 4); - //Step 2: Modify position by nodeIndex - nodePosition = nodePosition + new Vector2(GameData.availableResearch.Count * -icon.Texture.GetWidth() + researchGraph.GetChildCount() * icon.Texture.GetWidth() * 10, 0); + + Button button = new Button + { + Text = "Research", + Disabled = state != ResearchState.AVAILABLE + }; + + button.Pressed += () => OnResearchPressed(id); GraphNode node = new GraphNode { Name = id, Title = id, - PositionOffset = nodePosition + PositionOffset = GetNodePosition(texture), + SelfModulate = stateColor }; node.SetSlot( @@ -57,7 +126,86 @@ public partial class ResearchList : PanelContainer ); node.AddChild(icon); + node.AddChild(button); return node; } -} + + private Vector2 GetNodePosition(Texture2D texture) + { + Vector2 viewportSize = GetViewport().GetVisibleRect().Size; + + float textureWidth = texture?.GetWidth() ?? 64f; + + Vector2 startPosition = new Vector2( + viewportSize.X / StartXDivisor, + viewportSize.Y / StartYDivisor + ); + + float xOffset = + GameData.availableResearch.Count * -textureWidth + + researchGraph.GetChildCount() * textureWidth * NodeSpacingMultiplier; + + return startPosition + new Vector2(xOffset, 0); + } + + private void OnResearchPressed(string id) + { + GameData.availableResearch[id].state = ResearchState.RESEARCHED; + RecalculateResearchStates(); + SetupGraph(); + } + + private void RecalculateResearchStates() + { + bool changedState; + foreach (string key in GameData.availableResearch.Keys) + { + changedState = false; + if (reloadKeys.Contains(key)) continue; + reloadKeys.Add(key); + + //Already researched + if (GameData.availableResearch[key].state == ResearchState.RESEARCHED) + { + changedState = true; + } + + //No previous research needed + if (GameData.availableResearch[key].data.Research.Length <= 0) + { + GameData.availableResearch[key].state = ResearchState.RESEARCHED; + changedState = true; + } + + //Previous research is unlocked + if (!changedState && GameData.availableResearch[GameData.availableResearch[key].data.Research].state == ResearchState.RESEARCHED) + { + GameData.availableResearch[key].state = ResearchState.AVAILABLE; + changedState = true; + } + + if (!changedState) + { + //All others are locked + GameData.availableResearch[key].state = ResearchState.LOCKED; + } + + if (reloadKeys.Count != GameData.availableResearch.Keys.Count) + { + RecalculateResearchStates(); + } + } + } + + private Color GetColorByState(ResearchState state) + { + return state switch + { + ResearchState.AVAILABLE => Colors.White, + ResearchState.LOCKED => new Color(0.6f, 0.6f, 0.6f, 0.5f), + ResearchState.RESEARCHED => new Color(0.7f, 1f, 0.7f, 1f), + _ => Colors.Red + }; + } +} \ No newline at end of file diff --git a/Scripts/Research/ResearchState.cs b/Scripts/Research/ResearchState.cs new file mode 100644 index 0000000..84cd46f --- /dev/null +++ b/Scripts/Research/ResearchState.cs @@ -0,0 +1,7 @@ +public enum ResearchState +{ + UNDEFINED, + RESEARCHED, + AVAILABLE, + LOCKED +} \ No newline at end of file diff --git a/Scripts/Research/ResearchState.cs.uid b/Scripts/Research/ResearchState.cs.uid new file mode 100644 index 0000000..26dec14 --- /dev/null +++ b/Scripts/Research/ResearchState.cs.uid @@ -0,0 +1 @@ +uid://b57yhucbav37c