diff --git a/C7/Game.cs b/C7/Game.cs index 092497b0..e47705c8 100644 --- a/C7/Game.cs +++ b/C7/Game.cs @@ -6,6 +6,7 @@ using Serilog; using C7Engine.Pathing; using System.Collections.Generic; +using C7Engine.AI; public partial class Game : Node2D { [Signal] public delegate void TurnStartedEventHandler(); @@ -105,6 +106,17 @@ public override void _Ready() { Util.setModPath(scenarioSearchPath); log.Debug("RelativeModPath ", scenarioSearchPath); return Util.Civ3MediaPath("Text/PediaIcons.txt"); + }, (City city, CitizenType ct) => { + // Provide tile assignments for scenarios, which don't specify + // this in the BIC file. + for (int i = 0; i < city.size; ++i) { + CityResident newResident = new() { + citizenType = ct, + nationality = city.owner.civilization, + city = city + }; + CityTileAssignmentAI.AssignNewCitizenToTile(newResident); + } }); // Spawns engine thread Global.ResetLoadGamePath(); diff --git a/C7Engine/EntryPoints/CreateGame.cs b/C7Engine/EntryPoints/CreateGame.cs index dcb80abb..1baee8fe 100644 --- a/C7Engine/EntryPoints/CreateGame.cs +++ b/C7Engine/EntryPoints/CreateGame.cs @@ -12,12 +12,14 @@ public class CreateGame { * quickly. By keeping all the client-callable APIs in the EntryPoints folder, * hopefully it won't be too much of a goose hunt to refactor it later if we decide to do so. **/ - public static Player createGame(string loadFilePath, string defaultBicPath, Func getPediaIconsPath) { + public static Player createGame(string loadFilePath, string defaultBicPath, + Func getPediaIconsPath, + Action assignScenarioResidents) { EngineStorage.createThread(); EngineStorage.gameDataMutex.WaitOne(); SaveGame save = SaveManager.LoadSave(loadFilePath, defaultBicPath, getPediaIconsPath); - GameData gameData = save.ToGameData(); + GameData gameData = save.ToGameData(assignScenarioResidents); EngineStorage.gameData = gameData; diff --git a/C7GameData/Save/SaveCity.cs b/C7GameData/Save/SaveCity.cs index 5987d711..0f5ee86e 100644 --- a/C7GameData/Save/SaveCity.cs +++ b/C7GameData/Save/SaveCity.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace C7GameData.Save { @@ -43,7 +44,12 @@ public SaveCity(City city) { }); } - public City ToCity(GameMap gameMap, List players, List unitPrototypes, List civilizations, List citizenTypes) { + public City ToCity(GameMap gameMap, + List players, + List unitPrototypes, + List civilizations, + List citizenTypes, + Action assignScenarioResidents) { City city = new City{ id = id, location = gameMap.tileAt(location.X, location.Y), @@ -70,6 +76,15 @@ public City ToCity(GameMap gameMap, List players, List un foreach (CityResident cr in city.residents) { cr.tileWorked.personWorkingTile = cr; } + + // Scenarios don't specify the citizens of each city, only the city + // size. So we need to do that assignment now. This requires using + // the tile assignment AI, which due to dependency reasons we can't + // access directly, so we do this via a lambda. + if (city.residents.Count == 0) { + assignScenarioResidents(city, citizenTypes.Find(x => x.IsDefaultCitizen)); + } + return city; } } diff --git a/C7GameData/Save/SaveGame.cs b/C7GameData/Save/SaveGame.cs index 65cc0197..85a86141 100644 --- a/C7GameData/Save/SaveGame.cs +++ b/C7GameData/Save/SaveGame.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; using System.IO; @@ -86,7 +87,7 @@ private void populateGameDataTileUnitsAndCities(GameData data) { // TODO: GameData should store Civilizations, otherwise the round trip from // SaveGame to GameData and back loses Civilization instances that are not // assigned to a player. - public GameData ToGameData() { + public GameData ToGameData(Action assignScenarioResidents) { // copy data without references GameData data = new GameData{ seed = Seed, @@ -113,7 +114,7 @@ public GameData ToGameData() { }); // cities require game map for location and players for city owner - data.cities = Cities.ConvertAll(city => city.ToCity(data.map, data.players, UnitPrototypes, Civilizations, CitizenTypes)); + data.cities = Cities.ConvertAll(city => city.ToCity(data.map, data.players, UnitPrototypes, Civilizations, CitizenTypes, assignScenarioResidents)); // Once cities are known, players can reference cities. data.players.ForEach(player => { diff --git a/C7GameDataTests/SaveTest.cs b/C7GameDataTests/SaveTest.cs index 83e3fa45..ea22a2ee 100644 --- a/C7GameDataTests/SaveTest.cs +++ b/C7GameDataTests/SaveTest.cs @@ -61,7 +61,9 @@ public void SimpleSave() { SaveGame saveNeverGameData = SaveGame.Load(developerSave); saveNeverGameData.Save(outputNeverGameDataPath); - GameData gameData = saveNeverGameData.ToGameData(); + GameData gameData = saveNeverGameData.ToGameData((City c, CitizenType ct) => { + // We don't care about scenario tile assignment here. + }); SaveGame saveWasGameData = SaveGame.FromGameData(gameData); saveWasGameData.Save(outputWasGameDataPath); @@ -110,7 +112,9 @@ public async void LoadSampleSaves() { }); Assert.Null(ex); ex = Record.Exception(() => { - gd = game.ToGameData(); + gd = game.ToGameData((City c, CitizenType ct) => { + // We don't care about scenario tile assignment here. + }); }); Assert.Null(ex); Assert.NotNull(game); @@ -162,7 +166,9 @@ public void CheckScenariosInCiv3Subfolder(string subfolder) { }); Assert.True(ex == null, name + ": " + ex?.ToString()); ex = Record.Exception(() => { - gd = game.ToGameData(); + gd = game.ToGameData((City c, CitizenType ct) => { + // We don't care about scenario tile assignment here. + }); }); Assert.True(ex == null, name + ":" + ex?.ToString()); Assert.NotNull(game);