Step 1 of resolving #6186 freshwater crisis - unify adjacency to a single function, only rivers are outliers (#6219)

This commit is contained in:
Yair Morgenstern
2022-02-25 13:39:34 +02:00
committed by GitHub
parent 8cb73ac91b
commit 78f0ad768a
5 changed files with 21 additions and 23 deletions

View File

@ -21,12 +21,12 @@ object Constants {
const val forest = "Forest" const val forest = "Forest"
const val jungle = "Jungle" const val jungle = "Jungle"
const val ice = "Ice" const val ice = "Ice"
const val floodPlains = "Flood plains"
val vegetation = arrayOf(forest, jungle) val vegetation = arrayOf(forest, jungle)
val sea = arrayOf(ocean, coast) val sea = arrayOf(ocean, coast)
const val freshWater = "Fresh water"
const val barbarianEncampment = "Barbarian encampment" const val barbarianEncampment = "Barbarian encampment"
const val peaceTreaty = "Peace Treaty" const val peaceTreaty = "Peace Treaty"

View File

@ -51,14 +51,6 @@ open class TileInfo {
@Transient @Transient
var isOcean = false var isOcean = false
// This will be called often - farm can be built on Hill and tundra if adjacent to fresh water
// and farms on adjacent to fresh water tiles will have +1 additional Food after researching Civil Service
@delegate:Transient
val isAdjacentToFreshwater: Boolean by lazy {
matchesTerrainFilter("River") || matchesTerrainFilter("Fresh water")
|| neighbors.any { it.matchesTerrainFilter("Fresh water") }
}
var militaryUnit: MapUnit? = null var militaryUnit: MapUnit? = null
var civilianUnit: MapUnit? = null var civilianUnit: MapUnit? = null
var airUnits = ArrayList<MapUnit>() var airUnits = ArrayList<MapUnit>()
@ -398,7 +390,7 @@ open class TileInfo {
.sumOf { it.params[0].toInt() } .sumOf { it.params[0].toInt() }
} }
if (isAdjacentToRiver()) fertility += 1 if (isAdjacentToRiver()) fertility += 1
if (isAdjacentToFreshwater) fertility += 1 // meaning total +2 for river if (isAdjacentTo(Constants.freshWater)) fertility += 1 // meaning total +2 for river
if (checkCoasts && isCoastalTile()) fertility += 2 if (checkCoasts && isCoastalTile()) fertility += 2
return fertility return fertility
} }
@ -435,8 +427,8 @@ open class TileInfo {
// Freshwater and non-freshwater cannot be moved to matchesUniqueFilter since that creates an endless feedback. // Freshwater and non-freshwater cannot be moved to matchesUniqueFilter since that creates an endless feedback.
// If you're attempting that, check that it works! // If you're attempting that, check that it works!
// Edit: It seems to have been moved? // Edit: It seems to have been moved?
|| unique.params[1] == "Fresh water" && isAdjacentToFreshwater || unique.params[1] == Constants.freshWater && isAdjacentTo(Constants.freshWater)
|| unique.params[1] == "non-fresh water" && !isAdjacentToFreshwater || unique.params[1] == "non-fresh water" && !isAdjacentTo(Constants.freshWater)
) )
stats.add(unique.stats) stats.add(unique.stats)
} }
@ -496,10 +488,12 @@ open class TileInfo {
} }
} }
// This should be the only adjacency function
fun isAdjacentTo(terrainFilter:String): Boolean { fun isAdjacentTo(terrainFilter:String): Boolean {
if (terrainFilter == "Fresh water" && isAdjacentToFreshwater) return true // Rivers are odd, as they aren't technically part of any specific tile but still count towards adjacency
return if (terrainFilter == "River") isAdjacentToRiver() if (terrainFilter == "River") return isAdjacentToRiver()
else neighbors.any { neighbor -> neighbor.matchesFilter(terrainFilter) } if (terrainFilter == Constants.freshWater && isAdjacentToRiver()) return true
return neighbors.any { neighbor -> neighbor.matchesFilter(terrainFilter) }
} }
/** Without regards to what CivInfo it is, a lot of the checks are just for the improvement on the tile. /** Without regards to what CivInfo it is, a lot of the checks are just for the improvement on the tile.
@ -528,14 +522,14 @@ open class TileInfo {
// Tiles with no terrains, and no turns to build, are like great improvements - they're placeable // Tiles with no terrains, and no turns to build, are like great improvements - they're placeable
improvement.terrainsCanBeBuiltOn.isEmpty() && improvement.turnsToBuild == 0 && isLand -> true improvement.terrainsCanBeBuiltOn.isEmpty() && improvement.turnsToBuild == 0 && isLand -> true
improvement.terrainsCanBeBuiltOn.contains(topTerrain.name) -> true improvement.terrainsCanBeBuiltOn.contains(topTerrain.name) -> true
improvement.uniqueObjects.filter { it.type == UniqueType.MustBeNextTo }.any { improvement.getMatchingUniques(UniqueType.MustBeNextTo).any {
!isAdjacentTo(it.params[0]) !isAdjacentTo(it.params[0])
} -> false } -> false
!isWater && RoadStatus.values().any { it.name == improvement.name && it > roadStatus } -> true !isWater && RoadStatus.values().any { it.name == improvement.name && it > roadStatus } -> true
improvement.name == roadStatus.removeAction -> true improvement.name == roadStatus.removeAction -> true
topTerrain.unbuildable && !improvement.isAllowedOnFeature(topTerrain.name) -> false topTerrain.unbuildable && !improvement.isAllowedOnFeature(topTerrain.name) -> false
// DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests. // DO NOT reverse this &&. isAdjacentToFreshwater() is a lazy which calls a function, and reversing it breaks the tests.
improvement.hasUnique(UniqueType.ImprovementBuildableByFreshWater) && isAdjacentToFreshwater -> true improvement.hasUnique(UniqueType.ImprovementBuildableByFreshWater) && isAdjacentTo(Constants.freshWater) -> true
// If an unique of this type exists, we want all to match (e.g. Hill _and_ Forest would be meaningful). // If an unique of this type exists, we want all to match (e.g. Hill _and_ Forest would be meaningful).
improvement.getMatchingUniques(UniqueType.CanOnlyBeBuiltOnTile).let { improvement.getMatchingUniques(UniqueType.CanOnlyBeBuiltOnTile).let {
@ -574,7 +568,7 @@ open class TileInfo {
"Water resource" -> isWater && observingCiv != null && hasViewableResource(observingCiv) "Water resource" -> isWater && observingCiv != null && hasViewableResource(observingCiv)
"Natural Wonder" -> naturalWonder != null "Natural Wonder" -> naturalWonder != null
"Featureless" -> terrainFeatures.isEmpty() "Featureless" -> terrainFeatures.isEmpty()
"Fresh Water" -> isAdjacentToFreshwater "Fresh Water" -> isAdjacentTo(Constants.freshWater)
else -> { else -> {
if (terrainFeatures.contains(filter)) return true if (terrainFeatures.contains(filter)) return true
if (getAllTerrains().any { it.hasUnique(filter) }) return true if (getAllTerrains().any { it.hasUnique(filter) }) return true

View File

@ -2,6 +2,7 @@ package com.unciv.logic.map.mapgenerator
import com.badlogic.gdx.math.Rectangle import com.badlogic.gdx.math.Rectangle
import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.math.Vector2
import com.unciv.Constants
import com.unciv.logic.HexMath import com.unciv.logic.HexMath
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.logic.map.* import com.unciv.logic.map.*
@ -391,7 +392,7 @@ class MapRegions (val ruleset: Ruleset){
evaluateTileForStart(tile) evaluateTileForStart(tile)
if (tile.isAdjacentToRiver()) if (tile.isAdjacentToRiver())
riverTiles.add(tile.position) riverTiles.add(tile.position)
else if (tile.isCoastalTile() || tile.isAdjacentToFreshwater) else if (tile.isCoastalTile() || tile.isAdjacentTo(Constants.freshWater))
wetTiles.add(tile.position) wetTiles.add(tile.position)
else else
dryTiles.add(tile.position) dryTiles.add(tile.position)
@ -422,7 +423,7 @@ class MapRegions (val ruleset: Ruleset){
evaluateTileForStart(tile) evaluateTileForStart(tile)
if (tile.isAdjacentToRiver()) if (tile.isAdjacentToRiver())
riverTiles.add(tile.position) riverTiles.add(tile.position)
else if (tile.isCoastalTile() || tile.isAdjacentToFreshwater) else if (tile.isCoastalTile() || tile.isAdjacentTo(Constants.freshWater))
wetTiles.add(tile.position) wetTiles.add(tile.position)
else else
dryTiles.add(tile.position) dryTiles.add(tile.position)
@ -504,7 +505,7 @@ class MapRegions (val ruleset: Ruleset){
// If terrible, try adding a hill to a dry flat tile // If terrible, try adding a hill to a dry flat tile
if (innerProduction == 0 || (innerProduction < 2 && outerProduction < 8) || (minorCiv && innerProduction < 4)) { if (innerProduction == 0 || (innerProduction < 2 && outerProduction < 8) || (minorCiv && innerProduction < 4)) {
val hillSpot = startTile.neighbors val hillSpot = startTile.neighbors
.filter { it.isLand && it.terrainFeatures.isEmpty() && !it.isAdjacentToFreshwater && !it.isImpassible() } .filter { it.isLand && it.terrainFeatures.isEmpty() && !it.isAdjacentTo(Constants.freshWater) && !it.isImpassible() }
.toList().randomOrNull() .toList().randomOrNull()
val hillEquivalent = ruleset.terrains.values val hillEquivalent = ruleset.terrains.values
.firstOrNull { it.type == TerrainType.TerrainFeature && it.production >= 2 && !it.hasUnique(UniqueType.RareFeature) }?.name .firstOrNull { it.type == TerrainType.TerrainFeature && it.production >= 2 && !it.hasUnique(UniqueType.RareFeature) }?.name

View File

@ -170,6 +170,8 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
UniqueType.ConditionalInTiles -> UniqueType.ConditionalInTiles ->
relevantTile?.matchesFilter(condition.params[0], state.civInfo) == true relevantTile?.matchesFilter(condition.params[0], state.civInfo) == true
UniqueType.ConditionalInTilesNot ->
relevantTile?.matchesFilter(condition.params[0], state.civInfo) == false
UniqueType.ConditionalFightingInTiles -> UniqueType.ConditionalFightingInTiles ->
state.attackedTile?.matchesFilter(condition.params[0], state.civInfo) == true state.attackedTile?.matchesFilter(condition.params[0], state.civInfo) == true
UniqueType.ConditionalInTilesAnd -> UniqueType.ConditionalInTilesAnd ->

View File

@ -3,6 +3,7 @@ package com.unciv.models.translations
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.Array import com.badlogic.gdx.utils.Array
import com.unciv.Constants
import com.unciv.JsonParser import com.unciv.JsonParser
import com.unciv.models.metadata.BaseRuleset import com.unciv.models.metadata.BaseRuleset
import com.unciv.models.ruleset.* import com.unciv.models.ruleset.*
@ -241,7 +242,7 @@ object TranslationFileWriter {
val tileFilterMap = ruleset.terrains.keys.toMutableSet().apply { addAll(sequenceOf( val tileFilterMap = ruleset.terrains.keys.toMutableSet().apply { addAll(sequenceOf(
"Friendly Land", "Friendly Land",
"Foreign Land", "Foreign Land",
"Fresh water", Constants.freshWater,
"non-fresh water", "non-fresh water",
"Open Terrain", "Open Terrain",
"Rough Terrain", "Rough Terrain",