Files
RuinAdventurer/Scripts/UI/Research/ResearchList.cs
T

302 lines
6.8 KiB
C#

using Godot;
using Godot.Collections;
using System.Collections.Generic;
using System.Text;
public partial class ResearchList : PanelContainer
{
[Export] private GraphEdit researchGraph;
private System.Collections.Generic.Dictionary<string, Vector2> nodePositions = new System.Collections.Generic.Dictionary<string, Vector2>();
private bool hasArrangedNodes = false;
private List<Research> currentResearch = new List<Research>();
private List<Research> toDelete = new List<Research>();
public override void _Ready()
{
RecalculateResearchStates();
if (Visible) SetupGraph();
}
public override void _Process(double delta)
{
if(currentResearch.Count > 0) toDelete = new List<Research>();
foreach (Research research in currentResearch)
{
ResearchResult result = research.Execute(delta);
if (result == ResearchResult.FINISHED)
{
toDelete.Add(research);
RecalculateResearchStates();
SetupGraph();
}
else if (result == ResearchResult.FAILED)
{
research.state = ResearchState.AVAILABLE;
toDelete.Add(research);
RecalculateResearchStates();
SetupGraph();
}
}
foreach (Research delete in toDelete)
{
currentResearch.Remove(delete);
}
}
public void SetupGraph()
{
RememberNodePositions();
ClearGraph();
CreateResearchNodes();
CreateResearchConnections();
if (!hasArrangedNodes)
{
researchGraph.ArrangeNodes();
hasArrangedNodes = true;
}
}
private void RememberNodePositions()
{
foreach (Node child in researchGraph.GetChildren())
{
GraphNode node = child as GraphNode;
if (node == null) continue;
nodePositions[node.Name] = node.PositionOffset;
}
}
private void ClearGraph()
{
foreach (Dictionary connection in researchGraph.GetConnectionList())
{
researchGraph.DisconnectNode(
connection["from_node"].AsStringName(),
(int)connection["from_port"],
connection["to_node"].AsStringName(),
(int)connection["to_port"]
);
}
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)
{
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(
prerequisite,
0,
current,
0
);
}
}
private GraphNode CreateResearchNode(string id, string texturePath, ResearchState state)
{
Texture2D texture = GD.Load<Texture2D>(texturePath);
Color stateColor = GetColorByState(state);
string tooltipText = GetResearchTooltip(GameData.availableResearch[id]);
TextureRect icon = new TextureRect
{
Texture = texture,
StretchMode = TextureRect.StretchModeEnum.KeepAspectCentered,
CustomMinimumSize = new Vector2(64, 64),
SelfModulate = stateColor
};
Button button = new Button
{
Text = GetResearchButtonText(GameData.availableResearch[id], state),
Disabled = state != ResearchState.AVAILABLE || !GameData.availableResearch[id].CanStart(),
TooltipText = tooltipText
};
button.Pressed += () => OnResearchPressed(id);
GraphNode node = new GraphNode
{
Name = id,
Title = Research.GetReadableName(id),
SelfModulate = stateColor,
TooltipText = tooltipText
};
if (nodePositions.ContainsKey(id))
{
node.PositionOffset = nodePositions[id];
}
node.SetSlot(
0,
true,
0,
Colors.White,
true,
0,
Colors.White
);
node.AddChild(icon);
node.AddChild(button);
return node;
}
private void OnResearchPressed(string id)
{
Research research = GameData.availableResearch[id];
if (!research.CanStart()) return;
if (currentResearch.Contains(research)) return;
research.state = ResearchState.RESEARCHING;
currentResearch.Add(research);
RecalculateResearchStates();
SetupGraph();
}
private string GetResearchButtonText(Research research, ResearchState state)
{
if (state == ResearchState.RESEARCHED) return "Done";
if (state == ResearchState.RESEARCHING) return "Researching";
if (state == ResearchState.LOCKED) return "Locked";
if (!research.CanStart()) return "Missing items";
return "Research";
}
private string GetResearchTooltip(Research research)
{
StringBuilder tooltip = new StringBuilder(Research.GetReadableName(research.data.Id));
tooltip.AppendLine();
tooltip.AppendLine();
tooltip.AppendLine("Costs:");
if (research.data.Inputs.Count <= 0)
{
tooltip.AppendLine("- None");
}
foreach (Ingredient ingredient in research.data.Inputs)
{
tooltip.Append("- ");
tooltip.Append(ItemData.GetReadableName(ingredient.Item));
tooltip.Append(": ");
tooltip.Append(GameData.inventory.GetItemAmount(ingredient.Item));
tooltip.Append("/");
tooltip.AppendLine(ingredient.Amount.ToString());
}
tooltip.AppendLine();
tooltip.Append("Time: ");
tooltip.AppendLine(research.data.CraftTime.ToString("0"));
if (research.data.Effects == null || research.data.Effects.Count <= 0)
{
return tooltip.ToString();
}
tooltip.AppendLine();
tooltip.AppendLine("Effects:");
foreach (ResearchEffect effect in research.data.Effects)
{
tooltip.Append("- ");
tooltip.AppendLine(effect.GetDisplayText());
}
return tooltip.ToString();
}
private void RecalculateResearchStates()
{
bool changedState = true;
while (changedState)
{
changedState = false;
foreach (Research research in GameData.availableResearch.Values)
{
ResearchState newState = GetUpdatedResearchState(research);
if (research.state == newState) continue;
research.state = newState;
changedState = true;
}
}
}
private ResearchState GetUpdatedResearchState(Research research)
{
if (research.state == ResearchState.RESEARCHED)
{
return ResearchState.RESEARCHED;
}
if (research.data.Research.Length <= 0)
{
return ResearchState.RESEARCHED;
}
if (research.state == ResearchState.RESEARCHING)
{
return ResearchState.RESEARCHING;
}
if (GameData.availableResearch[research.data.Research].state == ResearchState.RESEARCHED)
{
return ResearchState.AVAILABLE;
}
return ResearchState.LOCKED;
}
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),
ResearchState.RESEARCHING => new Color(0.3f, 1f, 0.3f, 1f),
_ => Colors.Red
};
}
}