From 7bd633c4b4f1b3e22ecc3cb05632c2415f6254b6 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 25 Jan 2025 15:21:18 -0600 Subject: [PATCH] Keep track of the currently researched tech and let the user change it This PR also loads this information from save files. #392 --- C7/Game.cs | 5 +++++ C7/UIElements/Advisors/Advisors.cs | 15 ++++++++----- C7/UIElements/Advisors/ScienceAdvisor.cs | 24 +++++++++++++++----- C7Engine/EntryPoints/MessageToEngine.cs | 28 ++++++++++++++++++++++++ C7Engine/EntryPoints/MessageToUI.cs | 2 ++ C7GameData/ImportCiv3.cs | 7 +++--- C7GameData/Player.cs | 5 ++++- C7GameData/Save/SavePlayer.cs | 7 +++++- 8 files changed, 76 insertions(+), 17 deletions(-) diff --git a/C7/Game.cs b/C7/Game.cs index ca318c64..4ede59fe 100644 --- a/C7/Game.cs +++ b/C7/Game.cs @@ -208,6 +208,11 @@ public void processEngineMessages(GameData gameData) { setSelectedUnit(CurrentlySelectedUnit); } break; + case MsgUpdateUiAfterTechSelection mUUATS: + // F6 is the science advisor. + // TODO: Move the F* key strings to a set of constants/enum. + EmitSignal(SignalName.ShowSpecificAdvisor, "F6"); + break; } } } diff --git a/C7/UIElements/Advisors/Advisors.cs b/C7/UIElements/Advisors/Advisors.cs index cde57582..f3730615 100644 --- a/C7/UIElements/Advisors/Advisors.cs +++ b/C7/UIElements/Advisors/Advisors.cs @@ -54,13 +54,16 @@ private void OnShowSpecificAdvisor(string advisorType) { this.Show(); } if (advisorType.Equals("F6")) { - if (scienceAdvisor == null) { - scienceAdvisor = new ScienceAdvisor(); - advisors.Add(scienceAdvisor); - AddChild(scienceAdvisor); - } else { - scienceAdvisor.Show(); + // TODO: What's the best way to refresh the tech tree UI without + // adding too many children? + if (scienceAdvisor != null) { + RemoveChild(scienceAdvisor); + scienceAdvisor = null; } + + scienceAdvisor = new ScienceAdvisor(); + advisors.Add(scienceAdvisor); + AddChild(scienceAdvisor); this.Show(); } } diff --git a/C7/UIElements/Advisors/ScienceAdvisor.cs b/C7/UIElements/Advisors/ScienceAdvisor.cs index 91644dc4..44a89635 100644 --- a/C7/UIElements/Advisors/ScienceAdvisor.cs +++ b/C7/UIElements/Advisors/ScienceAdvisor.cs @@ -5,9 +5,15 @@ using System.Collections.Generic; public partial class ScienceAdvisor : TextureRect { + private ImageTexture AncientBackground; + private ImageTexture MiddleBackground; + private ImageTexture IndustrialBackground; + private ImageTexture ModernBackground; + // Called when the node enters the scene tree for the first time. public override void _Ready() { this.CreateUI(); + this.DrawTechTree(); } private void CreateUI() { @@ -16,10 +22,10 @@ private void CreateUI() { // // science_industrial_new is used as the industrial tech tree is // different from vanilla civ3. - ImageTexture AncientBackground = Util.LoadTextureFromPCX("Art/Advisors/science_ancient.pcx"); - ImageTexture MiddleBackground = Util.LoadTextureFromPCX("Art/Advisors/science_middle.pcx"); - ImageTexture IndustrialBackground = Util.LoadTextureFromPCX("Art/Advisors/science_industrial_new.pcx"); - ImageTexture ModernBackground = Util.LoadTextureFromPCX("Art/Advisors/science_modern.pcx"); + AncientBackground = Util.LoadTextureFromPCX("Art/Advisors/science_ancient.pcx"); + MiddleBackground = Util.LoadTextureFromPCX("Art/Advisors/science_middle.pcx"); + IndustrialBackground = Util.LoadTextureFromPCX("Art/Advisors/science_industrial_new.pcx"); + ModernBackground = Util.LoadTextureFromPCX("Art/Advisors/science_modern.pcx"); // TODO: Age-based background. Only use Ancient for now. // TODO: Consider moving this to an advisor utility, since we're copying @@ -53,11 +59,13 @@ private void CreateUI() { GoBackButton.SetPosition(new Vector2(952, 720)); AddChild(GoBackButton); GoBackButton.Pressed += ReturnToMenu; + } + void DrawTechTree() { using (UIGameDataAccess gameDataAccess = new()) { List techs = gameDataAccess.gameData.techs; Player player = gameDataAccess.gameData.GetHumanPlayers()[0]; - List knownTechs = player.knownTechs; + HashSet knownTechs = player.knownTechs; // Set the tech background based on the player's era. if (player.eraCivilopediaName == "ERAS_Ancient_Times") { @@ -75,10 +83,11 @@ private void CreateUI() { continue; } - // TODO: track the currently researched tech for kInProgress. TechBox.TechState techState = TechBox.TechState.kBlocked; if (knownTechs.Contains(tech.id)) { techState = TechBox.TechState.kKnown; + } else if (player.currentlyResearchedTech == tech.id) { + techState = TechBox.TechState.kInProgress; } else { bool prereqsKnown = true; foreach (Tech prereq in tech.Prerequisites) { @@ -92,6 +101,9 @@ private void CreateUI() { TechBox techButton = new(tech, techState); techButton.SetPosition(new Vector2(tech.X, tech.Y)); + techButton.Pressed += () => { + new MsgChooseResearch(tech.id).send(); + }; AddChild(techButton); } } diff --git a/C7Engine/EntryPoints/MessageToEngine.cs b/C7Engine/EntryPoints/MessageToEngine.cs index e5fc959d..82c5b022 100644 --- a/C7Engine/EntryPoints/MessageToEngine.cs +++ b/C7Engine/EntryPoints/MessageToEngine.cs @@ -126,6 +126,34 @@ public override void process() { } } + public class MsgChooseResearch : MessageToEngine { + private ID techId; + public MsgChooseResearch(ID techId) { + this.techId = techId; + } + + public override void process() { + Player player = EngineStorage.gameData.GetHumanPlayers()[0]; + if (player.currentlyResearchedTech == techId) { + return; + } + Tech requestedTech = EngineStorage.gameData.techs.Find(t => t.id == techId); + + // Ensure this is an eligible tech to research. + // + // TODO: do a topological sort to allow a queue of techs to study. + foreach (Tech prereq in requestedTech.Prerequisites) { + if (!player.knownTechs.Contains(prereq.id)) { + return; + } + } + + // Start researching this tech and update the UI. + player.currentlyResearchedTech = requestedTech.id; + new MsgUpdateUiAfterTechSelection().send(); + } + } + public class MsgEndTurn : MessageToEngine { private ILogger log = Log.ForContext(); diff --git a/C7Engine/EntryPoints/MessageToUI.cs b/C7Engine/EntryPoints/MessageToUI.cs index 2fc7dc8c..4babd181 100644 --- a/C7Engine/EntryPoints/MessageToUI.cs +++ b/C7Engine/EntryPoints/MessageToUI.cs @@ -40,6 +40,8 @@ public class MsgStartTurn : MessageToUI { } public class MsgUpdateUiAfterMove : MessageToUI { } + public class MsgUpdateUiAfterTechSelection : MessageToUI { } + public class MsgCityDestroyed : MessageToUI { public City city; diff --git a/C7GameData/ImportCiv3.cs b/C7GameData/ImportCiv3.cs index da246a61..dfe614f5 100644 --- a/C7GameData/ImportCiv3.cs +++ b/C7GameData/ImportCiv3.cs @@ -400,9 +400,10 @@ private void ImportSavLeaders() { /*cityNameIndex=*/leader.FoundedCities, /*era=*/theBiq.Eras[leader.Era].CivilopediaEntry); - - // TODO: store non-negative values of leader.Researching, - // which indexes into save.Techs. + // Record what the player is currently researching. + if (leader.Researching > -1) { + player.currentlyResearchedTech = save.Techs[leader.Researching].id; + } // Record any techs that this player knows. for (int k = 0; k < savData.KnownTechFlags.Length; ++k) { diff --git a/C7GameData/Player.cs b/C7GameData/Player.cs index 5b4c885d..f2255a87 100644 --- a/C7GameData/Player.cs +++ b/C7GameData/Player.cs @@ -24,7 +24,10 @@ public class Player { public List strategicPriorityData = new List(); // The list of techs known by this player. - public List knownTechs = new(); + public HashSet knownTechs = new(); + + // The tech the player is currently researching. + public ID currentlyResearchedTech; // The civilopedia name of the era this player is in. // diff --git a/C7GameData/Save/SavePlayer.cs b/C7GameData/Save/SavePlayer.cs index 532388cf..e252cc19 100644 --- a/C7GameData/Save/SavePlayer.cs +++ b/C7GameData/Save/SavePlayer.cs @@ -15,7 +15,10 @@ public class SavePlayer { public List tileKnowledge = new List(); // The list of techs known by this player. - public List knownTechs = new(); + public HashSet knownTechs = new(); + + // The tech the player is currently researching. + public ID currentlyResearchedTech; // The civilopedia name of the era this player is in. // @@ -36,6 +39,7 @@ public Player ToPlayer(GameMap map, List civilizations) { cityNameIndex = cityNameIndex, tileKnowledge = new TileKnowledge(), knownTechs = knownTechs, + currentlyResearchedTech = currentlyResearchedTech, eraCivilopediaName = eraCivilopediaName, }; foreach (TileLocation tile in tileKnowledge) { @@ -64,6 +68,7 @@ public SavePlayer(Player player) { tileKnowledge = player.tileKnowledge.AllKnownTiles().ConvertAll(tile => new TileLocation(tile)); turnsUntilPriorityReevaluation = player.turnsUntilPriorityReevaluation; knownTechs = player.knownTechs; + currentlyResearchedTech = player.currentlyResearchedTech; eraCivilopediaName = player.eraCivilopediaName; } }