From 599a1d8a911338e3552b01efc1954e6943c48ad4 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Sun, 27 Dec 2020 11:55:28 +0200 Subject: [PATCH] Game can deal gracefully wth mods removing certain units and technologies --- core/src/com/unciv/logic/GameInfo.kt | 70 +++++++++++-------- .../unciv/logic/civilization/TechManager.kt | 2 + core/src/com/unciv/logic/map/MapUnit.kt | 8 +-- core/src/com/unciv/logic/map/TileInfo.kt | 14 +++- 4 files changed, 57 insertions(+), 37 deletions(-) diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 3839837cd1..fc64e51795 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -262,35 +262,7 @@ class GameInfo { throw UncivShowableException("Missing mods: [$missingMods]") } - // Mods can change, leading to things on the map that are no longer defined in the mod. - // So we remove them so the game doesn't crash when it tries to access them. - for (tile in tileMap.values) { - if (tile.resource != null && !ruleSet.tileResources.containsKey(tile.resource!!)) - tile.resource = null - if (tile.improvement != null && !ruleSet.tileImprovements.containsKey(tile.improvement!!) - && !tile.improvement!!.startsWith("StartingLocation ")) // To not remove the starting locations in GameStarter.startNewGame() - tile.improvement = null - - for (unit in tile.getUnits()) - for (promotion in unit.promotions.promotions.toList()) - if (!ruleSet.unitPromotions.containsKey(promotion)) - unit.promotions.promotions.remove(promotion) - - for (city in civilizations.asSequence().flatMap { it.cities.asSequence() }) { - for (building in city.cityConstructions.builtBuildings.toHashSet()) - if (!ruleSet.buildings.containsKey(building)) - city.cityConstructions.builtBuildings.remove(building) - - // Remove invalid buildings or units from the queue - don't just check buildings and units because it might be a special construction as well - for (construction in city.cityConstructions.constructionQueue.toList()) { - if (!ruleSet.buildings.containsKey(construction) && !ruleSet.units.containsKey(construction) - && !PerpetualConstruction.perpetualConstructionsMap.containsKey(construction)) - city.cityConstructions.constructionQueue.remove(construction) - } - } - - - } + removeMissingModReferences() tileMap.setTransients(ruleSet) @@ -347,6 +319,46 @@ class GameInfo { } } + + // Mods can change, leading to things on the map that are no longer defined in the mod. + // So we remove them so the game doesn't crash when it tries to access them. + private fun removeMissingModReferences() { + for (tile in tileMap.values) { + if (tile.resource != null && !ruleSet.tileResources.containsKey(tile.resource!!)) + tile.resource = null + if (tile.improvement != null && !ruleSet.tileImprovements.containsKey(tile.improvement!!) + && !tile.improvement!!.startsWith("StartingLocation ")) // To not remove the starting locations in GameStarter.startNewGame() + tile.improvement = null + + for (unit in tile.getUnits()) { + if (!ruleSet.units.containsKey(unit.name)) tile.removeUnit(unit) + + for (promotion in unit.promotions.promotions.toList()) + if (!ruleSet.unitPromotions.containsKey(promotion)) + unit.promotions.promotions.remove(promotion) + } + + } + + for (city in civilizations.asSequence().flatMap { it.cities.asSequence() }) { + for (building in city.cityConstructions.builtBuildings.toHashSet()) + if (!ruleSet.buildings.containsKey(building)) + city.cityConstructions.builtBuildings.remove(building) + + // Remove invalid buildings or units from the queue - don't just check buildings and units because it might be a special construction as well + for (construction in city.cityConstructions.constructionQueue.toList()) { + if (!ruleSet.buildings.containsKey(construction) && !ruleSet.units.containsKey(construction) + && !PerpetualConstruction.perpetualConstructionsMap.containsKey(construction)) + city.cityConstructions.constructionQueue.remove(construction) + } + } + for (civinfo in civilizations) { + for (tech in civinfo.tech.techsResearched.toList()) + if (!ruleSet.technologies.containsKey(tech)) + civinfo.tech.techsResearched.remove(tech) + } + } + private fun changeBuildingName(cityConstructions: CityConstructions, oldBuildingName: String, newBuildingName: String) { if (cityConstructions.builtBuildings.contains(oldBuildingName)) { cityConstructions.builtBuildings.remove(oldBuildingName) diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index a183c74b90..ce0e72c590 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -18,6 +18,7 @@ import kotlin.math.min class TechManager { @Transient lateinit var civInfo: CivilizationInfo + /** This is the Transient list of Technologies */ @Transient var researchedTechnologies = ArrayList() @Transient @@ -42,6 +43,7 @@ class TechManager { /** For calculating Great Scientist yields - see https://civilization.fandom.com/wiki/Great_Scientist_(Civ5) */ var scienceOfLast8Turns = IntArray(8) { 0 } var scienceFromResearchAgreements = 0 + /** This is the lit of strings, which is serialized */ var techsResearched = HashSet() /** When moving towards a certain tech, the user doesn't have to manually pick every one. */ diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 033afa460e..52864239e9 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -495,13 +495,7 @@ class MapUnit { .forEach { unit -> unit.destroy() } } - fun removeFromTile() { - when { - type.isAirUnit() -> currentTile.airUnits.remove(this) - type.isCivilian() -> getTile().civilianUnit = null - else -> getTile().militaryUnit = null - } - } + fun removeFromTile() = currentTile.removeUnit(this) fun moveThroughTile(tile: TileInfo) { if (tile.improvement == Constants.ancientRuins && civInfo.isMajorCiv()) diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 11430af6f6..4a357cd17d 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -483,7 +483,19 @@ open class TileInfo { } fun stripUnits() { - for (unit in this.getUnits()) unit.removeFromTile() + for (unit in this.getUnits()) removeUnit(unit) + } + + + /** If the unit isn't in the ruleset we can't even know what type of unit this is! So check each place + * This works with no transients so can be called from gameInfo.setTransients with no fear + */ + fun removeUnit(mapUnit: MapUnit){ + when { + airUnits.contains(mapUnit) -> airUnits.remove(mapUnit) + civilianUnit == mapUnit -> civilianUnit = null + else -> militaryUnit = null + } } fun startWorkingOnImprovement(improvement: TileImprovement, civInfo: CivilizationInfo) {