diff --git a/core/src/com/unciv/logic/BackwardCompatibility.kt b/core/src/com/unciv/logic/BackwardCompatibility.kt index 3578754b59..63de26282c 100644 --- a/core/src/com/unciv/logic/BackwardCompatibility.kt +++ b/core/src/com/unciv/logic/BackwardCompatibility.kt @@ -27,6 +27,17 @@ object BackwardCompatibility { fun GameInfo.removeMissingModReferences() { tileMap.removeMissingTerrainModReferences(ruleset) + removeUnitsAndPromotions() + + // Mod decided you can't repair things anymore - get rid of old pillaged improvements + removeOldPillagedImprovements() + + handleMissingReferencesForEachCity() + + removeTechAndPolicies() + } + + private fun GameInfo.removeUnitsAndPromotions() { for (tile in tileMap.values) { for (unit in tile.getUnits()) { if (!ruleset.units.containsKey(unit.name)) tile.removeUnit(unit) @@ -36,8 +47,9 @@ object BackwardCompatibility { unit.promotions.promotions.remove(promotion) } } + } - // Mod decided you can't repair things anymore - get rid of old pillaged improvements + private fun GameInfo.removeOldPillagedImprovements() { if (!ruleset.tileImprovements.containsKey(Constants.repair)) for (tile in tileMap.values) { if (tile.roadIsPillaged) { @@ -49,8 +61,9 @@ object BackwardCompatibility { tile.improvementIsPillaged = false } } + } - + private fun GameInfo.handleMissingReferencesForEachCity() { for (city in civilizations.asSequence().flatMap { it.cities.asSequence() }) { changeBuildingNameIfNotInRuleset(ruleset, city.cityConstructions, "Hanse", "Bank") @@ -63,8 +76,8 @@ object BackwardCompatibility { fun isInvalidConstruction(construction: String) = !ruleset.buildings.containsKey(construction) - && !ruleset.units.containsKey(construction) - && !PerpetualConstruction.perpetualConstructionsMap.containsKey(construction) + && !ruleset.units.containsKey(construction) + && !PerpetualConstruction.perpetualConstructionsMap.containsKey(construction) // 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()) { @@ -76,7 +89,9 @@ object BackwardCompatibility { if (isInvalidConstruction(construction)) city.cityConstructions.inProgressConstructions.remove(construction) } + } + private fun GameInfo.removeTechAndPolicies() { for (civInfo in civilizations) { for (tech in civInfo.tech.techsResearched.toList()) if (!ruleset.technologies.containsKey(tech)) diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 4203fa8af8..038606c66d 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -506,11 +506,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion tileMap.gameInfo = this // [TEMPORARY] Convert old saves to newer ones by moving base rulesets from the mod list to the base ruleset field - val baseRulesetInMods = gameParameters.mods.firstOrNull { RulesetCache[it]?.modOptions?.isBaseRuleset == true } - if (baseRulesetInMods != null) { - gameParameters.baseRuleset = baseRulesetInMods - gameParameters.mods = LinkedHashSet(gameParameters.mods.filter { it != baseRulesetInMods }) - } + convertOldSavesToNewSaves() ruleset = RulesetCache.getComplexRuleset(gameParameters) @@ -562,6 +558,24 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion convertFortify() + updateCivilizationState() + + spaceResources.clear() + spaceResources.addAll(ruleset.buildings.values.filter { it.hasUnique(UniqueType.SpaceshipPart) } + .flatMap { it.getResourceRequirementsPerTurn().keys }) + spaceResources.addAll(ruleset.victories.values.flatMap { it.requiredSpaceshipParts }) + + convertEncampmentData() + barbarians.setTransients(this) + + cityDistances.game = this + + guaranteeUnitPromotions() + + migrateToTileHistory() + } + + private fun updateCivilizationState() { for (civInfo in civilizations.asSequence() // update city-state resource first since the happiness of major civ depends on it. // See issue: https://github.com/yairm210/Unciv/issues/7781 @@ -604,20 +618,14 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion cityInfo.cityStats.update() } } + } - spaceResources.clear() - spaceResources.addAll(ruleset.buildings.values.filter { it.hasUnique(UniqueType.SpaceshipPart) } - .flatMap { it.getResourceRequirementsPerTurn().keys }) - spaceResources.addAll(ruleset.victories.values.flatMap { it.requiredSpaceshipParts }) - - convertEncampmentData() - barbarians.setTransients(this) - - cityDistances.game = this - - guaranteeUnitPromotions() - - migrateToTileHistory() + private fun convertOldSavesToNewSaves() { + val baseRulesetInMods = gameParameters.mods.firstOrNull { RulesetCache[it]?.modOptions?.isBaseRuleset == true } + if (baseRulesetInMods != null) { + gameParameters.baseRuleset = baseRulesetInMods + gameParameters.mods = LinkedHashSet(gameParameters.mods.filter { it != baseRulesetInMods }) + } } //endregion diff --git a/core/src/com/unciv/logic/GameStarter.kt b/core/src/com/unciv/logic/GameStarter.kt index 780d10fd73..5b2e9aef99 100644 --- a/core/src/com/unciv/logic/GameStarter.kt +++ b/core/src/com/unciv/logic/GameStarter.kt @@ -19,6 +19,7 @@ import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache import com.unciv.models.ruleset.unique.StateForConditionals import com.unciv.models.ruleset.unique.UniqueType +import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.stats.Stats import com.unciv.models.translations.equalsPlaceholderText import com.unciv.models.translations.getPlaceholderParameters @@ -365,9 +366,6 @@ object GameStarter { val ruleSet = gameInfo.ruleset val tileMap = gameInfo.tileMap - val startingEra = gameInfo.gameParameters.startingEra - var startingUnits: MutableList - var eraUnitReplacement: String val cityCenterMinStats = sequenceOf(ruleSet.tileImprovements[Constants.cityCenter]) .filterNotNull() @@ -395,81 +393,107 @@ object GameStarter { val startingLocations = getStartingLocations(allCivs, tileMap, landTilesInBigEnoughGroup, startScores) - val settlerLikeUnits = ruleSet.units.filter { - it.value.hasUnique(UniqueType.FoundCity) - } - // no starting units for Barbarians and Spectators + determineStartingUnitsAndLocations(gameInfo, startingLocations, ruleSet) + } + + private fun removeAncientRuinsNearStartingLocation(startingLocation: Tile) { + for (tile in startingLocation.getTilesInDistance(3)) { + if (tile.improvement != null + && tile.getTileImprovement()!!.isAncientRuinsEquivalent() + ) { + tile.changeImprovement(null) // Remove ancient ruins in immediate vicinity + } + } + } + + private fun determineStartingUnitsAndLocations( + gameInfo: GameInfo, + startingLocations: HashMap, + ruleset: Ruleset + ) { + val startingEra = gameInfo.gameParameters.startingEra + val settlerLikeUnits = ruleset.units.filter { it.value.hasUnique(UniqueType.FoundCity) } + for (civ in gameInfo.civilizations.filter { !it.isBarbarian() && !it.isSpectator() }) { val startingLocation = startingLocations[civ]!! - for (tile in startingLocation.getTilesInDistance(3)) { - if (tile.improvement != null - && tile.getTileImprovement()!!.isAncientRuinsEquivalent() - ) { - tile.changeImprovement(null) // Remove ancient ruins in immediate vicinity + removeAncientRuinsNearStartingLocation(startingLocation) + val startingUnits = getStartingUnitsForEraAndDifficulty(civ, gameInfo, ruleset, startingEra) + adjustStartingUnitsForCityStatesAndOneCityChallenge(civ, gameInfo, startingUnits, settlerLikeUnits) + placeStartingUnits(civ, startingLocation, startingUnits, ruleset, ruleset.eras[startingEra]!!.startingMilitaryUnit, settlerLikeUnits) + } + } + + private fun getStartingUnitsForEraAndDifficulty(civ: Civilization, gameInfo: GameInfo, ruleset: Ruleset, startingEra: String): MutableList { + val startingUnits = ruleset.eras[startingEra]!!.getStartingUnits().toMutableList() + + // Add extra units granted by difficulty + startingUnits.addAll(when { + civ.isHuman() -> gameInfo.getDifficulty().playerBonusStartingUnits + civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivBonusStartingUnits + else -> gameInfo.getDifficulty().aiCityStateBonusStartingUnits + }) + + return startingUnits + } + + private fun getEquivalentUnit( + civ: Civilization, + unitParam: String, + ruleset: Ruleset, + eraUnitReplacement: String, + settlerLikeUnits: Map + ): String? { + var unit = unitParam // We want to change it and this is the easiest way to do so + if (unit == Constants.eraSpecificUnit) unit = eraUnitReplacement + if (unit == Constants.settler && Constants.settler !in ruleset.units) { + val buildableSettlerLikeUnits = + settlerLikeUnits.filter { + it.value.isBuildable(civ) + && it.value.isCivilian() } + if (buildableSettlerLikeUnits.isEmpty()) return null // No settlers in this mod + return civ.getEquivalentUnit(buildableSettlerLikeUnits.keys.random()).name + } + if (unit == "Worker" && "Worker" !in ruleset.units) { + val buildableWorkerLikeUnits = ruleset.units.filter { + it.value.hasUnique(UniqueType.BuildImprovements) && + it.value.isBuildable(civ) && it.value.isCivilian() } + if (buildableWorkerLikeUnits.isEmpty()) return null // No workers in this mod + return civ.getEquivalentUnit(buildableWorkerLikeUnits.keys.random()).name + } + return civ.getEquivalentUnit(unit).name + } - fun placeNearStartingPosition(unitName: String) { - civ.units.placeUnitNearTile(startingLocation.position, unitName) - } + private fun adjustStartingUnitsForCityStatesAndOneCityChallenge( + civ: Civilization, + gameInfo: GameInfo, + startingUnits: MutableList, + settlerLikeUnits: Map + ) { + // Adjust starting units for city states + if (civ.isCityState() && !gameInfo.ruleset.modOptions.uniques.contains(ModOptionsConstants.allowCityStatesSpawnUnits)) { + val startingSettlers = startingUnits.filter { settlerLikeUnits.contains(it) } - // Determine starting units based on starting era - startingUnits = ruleSet.eras[startingEra]!!.getStartingUnits().toMutableList() - eraUnitReplacement = ruleSet.eras[startingEra]!!.startingMilitaryUnit + startingUnits.clear() + startingUnits.add(startingSettlers.random()) + } - // Add extra units granted by difficulty - startingUnits.addAll(when { - civ.isHuman() -> gameInfo.getDifficulty().playerBonusStartingUnits - civ.isMajorCiv() -> gameInfo.getDifficulty().aiMajorCivBonusStartingUnits - else -> gameInfo.getDifficulty().aiCityStateBonusStartingUnits - }) + // Adjust starting units for one city challenge + if (civ.playerType == PlayerType.Human && gameInfo.gameParameters.oneCityChallenge) { + val startingSettlers = startingUnits.filter { settlerLikeUnits.contains(it) } + startingUnits.removeAll(startingSettlers) + startingUnits.add(startingSettlers.random()) + } + } - fun getEquivalentUnit(civ: Civilization, unitParam: String): String? { - var unit = unitParam // We want to change it and this is the easiest way to do so - if (unit == Constants.eraSpecificUnit) unit = eraUnitReplacement - if (unit == Constants.settler && Constants.settler !in ruleSet.units) { - val buildableSettlerLikeUnits = - settlerLikeUnits.filter { - it.value.isBuildable(civ) - && it.value.isCivilian() - } - if (buildableSettlerLikeUnits.isEmpty()) return null // No settlers in this mod - return civ.getEquivalentUnit(buildableSettlerLikeUnits.keys.random()).name - } - if (unit == "Worker" && "Worker" !in ruleSet.units) { - val buildableWorkerLikeUnits = ruleSet.units.filter { - it.value.hasUnique(UniqueType.BuildImprovements) && - it.value.isBuildable(civ) && it.value.isCivilian() - } - if (buildableWorkerLikeUnits.isEmpty()) return null // No workers in this mod - return civ.getEquivalentUnit(buildableWorkerLikeUnits.keys.random()).name - } - return civ.getEquivalentUnit(unit).name - } - - // City states should only spawn with one settler regardless of difficulty, but this may be disabled in mods - if (civ.isCityState() && !ruleSet.modOptions.uniques.contains(ModOptionsConstants.allowCityStatesSpawnUnits)) { - val startingSettlers = startingUnits.filter { settlerLikeUnits.contains(it) } - - startingUnits.clear() - startingUnits.add(startingSettlers.random()) - } - - // One city challengers should spawn with one settler only regardless of era and difficulty - if (civ.playerType == PlayerType.Human && gameInfo.gameParameters.oneCityChallenge) { - val startingSettlers = startingUnits.filter { settlerLikeUnits.contains(it) } - - startingUnits.removeAll(startingSettlers) - startingUnits.add(startingSettlers.random()) - } - - for (unit in startingUnits) { - val unitToAdd = getEquivalentUnit(civ, unit) - if (unitToAdd != null) placeNearStartingPosition(unitToAdd) - } + private fun placeStartingUnits(civ: Civilization, startingLocation: Tile, startingUnits: MutableList, ruleset: Ruleset, eraUnitReplacement: String, settlerLikeUnits: Map) { + for (unit in startingUnits) { + val unitToAdd = getEquivalentUnit(civ, unit, ruleset, eraUnitReplacement, settlerLikeUnits) + if (unitToAdd != null) civ.units.placeUnitNearTile(startingLocation.position, unitToAdd) } }