diff --git a/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt index 6477394d0e..8e26c7a027 100644 --- a/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt @@ -387,7 +387,7 @@ class WorkerAutomation( .filter { it.second > 0f } .maxByOrNull { it.second }?.first - val lastTerrain = tile.getLastTerrain() + val lastTerrain = tile.lastTerrain fun isUnbuildableAndRemovable(terrain: Terrain): Boolean = terrain.unbuildable && ruleSet.tileImprovements.containsKey(Constants.remove + terrain.name) diff --git a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt index e1aac6f041..9f0204c454 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/MapGenerator.kt @@ -220,7 +220,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc .firstOrNull { tile.isAdjacentTo(it.params[1]) } ?: continue val terrain = ruleset.terrains[conversionUnique.params[0]] ?: continue - if (!terrain.occursOn.contains(tile.getLastTerrain().name)) continue + if (!terrain.occursOn.contains(tile.lastTerrain.name)) continue if (terrain.type == TerrainType.TerrainFeature) tile.addTerrainFeature(terrain.name) @@ -316,7 +316,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc val suitableTiles = candidateTiles .filterNot { it.baseTerrain == Constants.snow && it.isHill() } .filter { it.resource == null - && resource.terrainsCanBeFoundOn.contains(it.getLastTerrain().name) } + && resource.terrainsCanBeFoundOn.contains(it.lastTerrain.name) } val locations = randomness.chooseSpreadOutLocations(resourcesPerType, suitableTiles, mapRadius) @@ -335,7 +335,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc val suitableTiles = tileMap.values .filterNot { it.baseTerrain == Constants.snow && it.isHill() } .filter { it.resource == null && it.neighbors.none { neighbor -> neighbor.isNaturalWonder() } - && resourcesOfType.any { r -> r.terrainsCanBeFoundOn.contains(it.getLastTerrain().name) } } + && resourcesOfType.any { r -> r.terrainsCanBeFoundOn.contains(it.lastTerrain.name) } } val numberOfResources = tileMap.values.count { it.isLand && !it.isImpassible() } * tileMap.mapParameters.resourceRichness val locations = randomness.chooseSpreadOutLocations(numberOfResources.toInt(), suitableTiles, mapRadius) @@ -344,7 +344,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc for (tile in locations) { val possibleResources = resourcesOfType - .filter { it.terrainsCanBeFoundOn.contains(tile.getLastTerrain().name) } + .filter { it.terrainsCanBeFoundOn.contains(tile.lastTerrain.name) } if (possibleResources.isEmpty()) continue val resourceWithLeastAssignments = possibleResources.minByOrNull { resourceToNumber[it.name]!! }!! resourceToNumber.add(resourceWithLeastAssignments.name, 1) @@ -635,13 +635,13 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc val candidateTerrains = vegetationTerrains.flatMap{ it.occursOn } // Checking it.baseTerrain in candidateTerrains to make sure forest does not spawn on desert hill for (tile in tileMap.values.asSequence().filter { it.baseTerrain in candidateTerrains - && it.getLastTerrain().name in candidateTerrains }) { + && it.lastTerrain.name in candidateTerrains }) { val vegetation = (randomness.getPerlinNoise(tile, vegetationSeed, scale = 3.0, nOctaves = 1) + 1.0) / 2.0 if (vegetation <= tileMap.mapParameters.vegetationRichness) { val possibleVegetation = vegetationTerrains.filter { vegetationTerrain -> - vegetationTerrain.occursOn.contains(tile.getLastTerrain().name) + vegetationTerrain.occursOn.contains(tile.lastTerrain.name) && vegetationTerrain.getMatchingUniques(UniqueType.TileGenerationConditions).none { tile.temperature!! < it.params[0].toDouble() || tile.temperature!! > it.params[1].toDouble() || tile.humidity!! < it.params[2].toDouble() || tile.humidity!! > it.params[3].toDouble() @@ -714,7 +714,7 @@ class MapGenerator(val ruleset: Ruleset, private val coroutineScope: CoroutineSc val candidates = iceEquivalents .filter { it.matches(temperature, 1.0) && - tile.getLastTerrain().name in it.terrain.occursOn + tile.lastTerrain.name in it.terrain.occursOn }.map { it.terrain.name } when (candidates.size) { 1 -> tile.addTerrainFeature(candidates.first()) diff --git a/core/src/com/unciv/logic/map/mapgenerator/MapRegions.kt b/core/src/com/unciv/logic/map/mapgenerator/MapRegions.kt index 17d11b520c..846c0687f8 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/MapRegions.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/MapRegions.kt @@ -646,9 +646,9 @@ class MapRegions (val ruleset: Ruleset){ val validBonuses = ruleset.tileResources.values.filter { it.resourceType == ResourceType.Bonus && it.food >= 1 && - plot.getLastTerrain().name in it.terrainsCanBeFoundOn + plot.lastTerrain.name in it.terrainsCanBeFoundOn } - val goodPlotForOasis = canPlaceOasis && plot.getLastTerrain().name in oasisEquivalent!!.occursOn + val goodPlotForOasis = canPlaceOasis && plot.lastTerrain.name in oasisEquivalent!!.occursOn if (validBonuses.isNotEmpty() || goodPlotForOasis) { if (goodPlotForOasis) { @@ -699,7 +699,7 @@ class MapRegions (val ruleset: Ruleset){ if (plot.resource != null) continue - val bonusToPlace = stoneTypeBonuses.filter { plot.getLastTerrain().name in it.terrainsCanBeFoundOn }.randomOrNull() + val bonusToPlace = stoneTypeBonuses.filter { plot.lastTerrain.name in it.terrainsCanBeFoundOn }.randomOrNull() if (bonusToPlace != null) { plot.resource = bonusToPlace.name stoneNeeded-- @@ -715,7 +715,7 @@ class MapRegions (val ruleset: Ruleset){ val bestImprovementYield = tile.tileMap.ruleset!!.tileImprovements.values .filter { !it.hasUnique(UniqueType.GreatImprovement) && it.uniqueTo == null && - tile.getLastTerrain().name in it.terrainsCanBeBuiltOn } + tile.lastTerrain.name in it.terrainsCanBeBuiltOn } .maxOfOrNull { it[stat] } return baseYield + (bestImprovementYield ?: 0f) } @@ -1334,7 +1334,7 @@ class MapRegions (val ruleset: Ruleset){ continue val weightings = strategicResources.map { if (fallbackStrategic) { - if (tile.getLastTerrain().name in it.terrainsCanBeFoundOn) 1f else 0f + if (tile.lastTerrain.name in it.terrainsCanBeFoundOn) 1f else 0f } else { val uniques = it.getMatchingUniques(UniqueType.MinorDepositWeighting, conditionalTerrain).toList() uniques.sumOf { unique -> unique.params[0].toInt() }.toFloat() @@ -1391,7 +1391,7 @@ class MapRegions (val ruleset: Ruleset){ if(fallbackBonuses && resource.resourceType == ResourceType.Bonus) { // Since we haven't been able to generate any rule-based lists, just generate new ones on the fly // Increase impact to avoid clustering since there is no terrain type stratification. - val fallbackList = tileMap.values.filter { it.getLastTerrain().name in resource.terrainsCanBeFoundOn }.shuffled() + val fallbackList = tileMap.values.filter { it.lastTerrain.name in resource.terrainsCanBeFoundOn }.shuffled() placeResourcesInTiles((20 * bonusMultiplier).toInt(), fallbackList, listOf(resource), 2 + extraImpact, 2 + extraImpact, false) } } @@ -1440,7 +1440,7 @@ class MapRegions (val ruleset: Ruleset){ for (tile in tiles) { val conditionalTerrain = StateForConditionals(attackedTile = tile) if (tile.resource == null && - tile.getLastTerrain().name in resource.terrainsCanBeFoundOn && + tile.lastTerrain.name in resource.terrainsCanBeFoundOn && !tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain) && !resource.hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain) && resource.getMatchingUniques(UniqueType.TileGenerationConditions).none { @@ -1515,7 +1515,7 @@ class MapRegions (val ruleset: Ruleset){ for (tile in tileList) { if (tile.resource != null || (testTerrains && - (tile.getLastTerrain().name !in resourceOptions.first().terrainsCanBeFoundOn || + (tile.lastTerrain.name !in resourceOptions.first().terrainsCanBeFoundOn || resourceOptions.first().hasUnique(UniqueType.NoNaturalGeneration, conditionalTerrain)) ) || tile.getBaseTerrain().hasUnique(UniqueType.BlocksResources, conditionalTerrain)) continue // Can't place here, can't be a fallback tile diff --git a/core/src/com/unciv/logic/map/mapgenerator/NaturalWonderGenerator.kt b/core/src/com/unciv/logic/map/mapgenerator/NaturalWonderGenerator.kt index 8e6a2e39f9..81e42f0f7c 100644 --- a/core/src/com/unciv/logic/map/mapgenerator/NaturalWonderGenerator.kt +++ b/core/src/com/unciv/logic/map/mapgenerator/NaturalWonderGenerator.kt @@ -99,7 +99,7 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration val suitableLocations = tileMap.values.filter { tile-> tile.resource == null && - naturalWonder.occursOn.contains(tile.getLastTerrain().name) && + naturalWonder.occursOn.contains(tile.lastTerrain.name) && naturalWonder.uniqueObjects.all { unique -> when (unique.type) { UniqueType.NaturalWonderNeighborCount -> { @@ -215,7 +215,7 @@ class NaturalWonderGenerator(val ruleset: Ruleset, val randomness: MapGeneration "Land" -> isLand Constants.hill -> isHill() naturalWonder -> true - in allTerrainFeatures -> getLastTerrain().name == filter + in allTerrainFeatures -> lastTerrain.name == filter else -> baseTerrain == filter } diff --git a/core/src/com/unciv/logic/map/mapunit/UnitMovement.kt b/core/src/com/unciv/logic/map/mapunit/UnitMovement.kt index a1a0508d4b..e033a96621 100644 --- a/core/src/com/unciv/logic/map/mapunit/UnitMovement.kt +++ b/core/src/com/unciv/logic/map/mapunit/UnitMovement.kt @@ -68,7 +68,7 @@ class UnitMovement(val unit: MapUnit) { if (unit.cache.ignoresTerrainCost) return 1f + extraCost if (areConnectedByRiver) return 100f // Rivers take the entire turn to cross - val terrainCost = to.getLastTerrain().movementCost.toFloat() + val terrainCost = to.lastTerrain.movementCost.toFloat() if (unit.cache.noTerrainMovementUniques) return terrainCost + extraCost @@ -704,7 +704,7 @@ class UnitMovement(val unit: MapUnit) { // helicopters can pass through impassable tiles like mountains if (!unit.cache.canPassThroughImpassableTiles && !(unit.cache.canEnterIceTiles && tile.terrainFeatures.contains(Constants.ice)) // carthage-like uniques sometimes allow passage through impassible tiles - && !(unit.civ.passThroughImpassableUnlocked && unit.civ.passableImpassables.contains(tile.getLastTerrain().name))) + && !(unit.civ.passThroughImpassableUnlocked && unit.civ.passableImpassables.contains(tile.lastTerrain.name))) return false } if (tile.isLand diff --git a/core/src/com/unciv/logic/map/tile/Tile.kt b/core/src/com/unciv/logic/map/tile/Tile.kt index 5370207941..27e4aec5bf 100644 --- a/core/src/com/unciv/logic/map/tile/Tile.kt +++ b/core/src/com/unciv/logic/map/tile/Tile.kt @@ -96,6 +96,11 @@ open class Tile : IsPartOfGameInfoSerialization { var allTerrains: Sequence = sequenceOf() private set + @Transient + /** Saves a sequence of a list */ + lateinit var lastTerrain: Terrain + private set + @Transient var terrainUniqueMap = UniqueMap() private set @@ -205,13 +210,6 @@ open class Tile : IsPartOfGameInfoSerialization { fun getCity(): City? = owningCity - fun getLastTerrain(): Terrain = when { - terrainFeatures.isNotEmpty() -> ruleset.terrains[terrainFeatures.last()] - ?: getBaseTerrain() // defense against rare edge cases involving baseTerrain Hill deprecation - naturalWonder != null -> getNaturalWonder() - else -> getBaseTerrain() - } - @Transient private var tileResourceCache: TileResource? = null val tileResource: TileResource @@ -257,7 +255,7 @@ open class Tile : IsPartOfGameInfoSerialization { fun isCityCenter(): Boolean = isCityCenterInternal fun isNaturalWonder(): Boolean = naturalWonder != null - fun isImpassible() = getLastTerrain().impassable + fun isImpassible() = lastTerrain.impassable fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!] fun getUnpillagedTileImprovement(): TileImprovement? = if (getUnpillagedImprovement() == null) null else ruleset.tileImprovements[improvement!!] @@ -826,6 +824,13 @@ open class Tile : IsPartOfGameInfoSerialization { val newUniqueMap = UniqueMap() for (terrain in allTerrains) newUniqueMap.addUniques(terrain.uniqueObjects) + + lastTerrain = when { + terrainFeatures.isNotEmpty() -> ruleset.terrains[terrainFeatures.last()] + ?: getBaseTerrain() // defense against rare edge cases involving baseTerrain Hill deprecation + naturalWonder != null -> getNaturalWonder() + else -> getBaseTerrain() + } terrainUniqueMap = newUniqueMap } diff --git a/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt b/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt index a554d4634e..8c80833766 100644 --- a/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt +++ b/core/src/com/unciv/logic/map/tile/TileInfoImprovementFunctions.kt @@ -77,7 +77,7 @@ class TileInfoImprovementFunctions(val tile: Tile) { fun TileImprovement.canBeBuildOnThisUnbuildableTerrain( knownFeatureRemovals: List? = null, ): Boolean { - val topTerrain = tile.getLastTerrain() + val topTerrain = tile.lastTerrain // We can build if we are specifically allowed to build on this terrain if (isAllowedOnFeature(topTerrain.name)) return true @@ -115,7 +115,7 @@ class TileInfoImprovementFunctions(val tile: Tile) { tile.improvement != null && tile.getTileImprovement()!!.hasUnique(UniqueType.Irremovable, stateForConditionals) -> false // Can't build if this terrain is unbuildable, except when we are specifically allowed to - tile.getLastTerrain().unbuildable && !improvement.canBeBuildOnThisUnbuildableTerrain(knownFeatureRemovals) -> false + tile.lastTerrain.unbuildable && !improvement.canBeBuildOnThisUnbuildableTerrain(knownFeatureRemovals) -> false // Can't build if any terrain specifically prevents building this improvement tile.getTerrainMatchingUniques(UniqueType.RestrictedBuildableImprovements, stateForConditionals).any { @@ -146,7 +146,7 @@ class TileInfoImprovementFunctions(val tile: Tile) { // At this point we know this is a normal improvement and that there is no reason not to allow it to be built. // Lastly we check if the improvement may be built on this terrain or resource - improvement.canBeBuiltOn(tile.getLastTerrain().name) -> true + improvement.canBeBuiltOn(tile.lastTerrain.name) -> true tile.isLand && improvement.canBeBuiltOn("Land") -> true tile.isWater && improvement.canBeBuiltOn("Water") -> true // DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests. diff --git a/core/src/com/unciv/ui/screens/pickerscreens/ImprovementPickerScreen.kt b/core/src/com/unciv/ui/screens/pickerscreens/ImprovementPickerScreen.kt index fc17702088..9c4ba1582a 100644 --- a/core/src/com/unciv/ui/screens/pickerscreens/ImprovementPickerScreen.kt +++ b/core/src/com/unciv/ui/screens/pickerscreens/ImprovementPickerScreen.kt @@ -79,8 +79,8 @@ class ImprovementPickerScreen( // clone tileInfo without "top" feature if it could be removed // Keep this copy around for speed val tileWithoutLastTerrain: Tile = tile.clone() - if (Constants.remove + tileWithoutLastTerrain.getLastTerrain().name in ruleSet.tileImprovements) { - tileWithoutLastTerrain.removeTerrainFeature(tileWithoutLastTerrain.getLastTerrain().name) + if (Constants.remove + tileWithoutLastTerrain.lastTerrain.name in ruleSet.tileImprovements) { + tileWithoutLastTerrain.removeTerrainFeature(tileWithoutLastTerrain.lastTerrain.name) } val cityUniqueCache = LocalUniqueCache() @@ -133,7 +133,7 @@ class ImprovementPickerScreen( val proposedSolutions = mutableListOf() if (suggestRemoval) - proposedSolutions.add("${Constants.remove}[${tile.getLastTerrain().name}] first") + proposedSolutions.add("${Constants.remove}[${tile.lastTerrain.name}] first") if (ImprovementBuildingProblem.MissingTech in unbuildableBecause) proposedSolutions.add("Research [${improvement.techRequired}] first") if (ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause)