diff --git a/core/src/com/unciv/logic/automation/unit/CityLocationTileRanker.kt b/core/src/com/unciv/logic/automation/unit/CityLocationTileRanker.kt index e385204309..a29b4608ad 100644 --- a/core/src/com/unciv/logic/automation/unit/CityLocationTileRanker.kt +++ b/core/src/com/unciv/logic/automation/unit/CityLocationTileRanker.kt @@ -4,6 +4,7 @@ import com.unciv.logic.automation.Automation import com.unciv.logic.city.City import com.unciv.logic.civilization.Civilization import com.unciv.logic.civilization.diplomacy.DiplomacyFlags +import com.unciv.logic.map.HexMath import com.unciv.logic.map.mapunit.MapUnit import com.unciv.logic.map.tile.Tile import com.unciv.models.ruleset.tile.ResourceType @@ -21,7 +22,7 @@ object CityLocationTileRanker { /** * Returns a hashmap of tiles to their ranking plus the a the highest value tile and its value */ - fun getBestTilesToFoundCity(unit: MapUnit, distanceToSearch: Int? = null): BestTilesToFoundCity { + fun getBestTilesToFoundCity(unit: MapUnit, distanceToSearch: Int? = null, minimumValue: Float): BestTilesToFoundCity { val range = if (distanceToSearch != null) distanceToSearch else { val distanceFromHome = if (unit.civ.cities.isEmpty()) 0 else unit.civ.cities.minOf { it.getCenterTile().aerialDistanceTo(unit.getTile()) } @@ -39,10 +40,11 @@ object CityLocationTileRanker { val possibleTileLocationsWithRank = possibleCityLocations .map { val tileValue = rankTileToSettle(it, unit.civ, nearbyCities, baseTileMap, uniqueCache) - bestTilesToFoundCity.tileRankMap[it] = tileValue + if (tileValue >= minimumValue) + bestTilesToFoundCity.tileRankMap[it] = tileValue Pair(it, tileValue) - } + }.filter { it.second >= minimumValue } .sortedByDescending { it.second } val bestReachableTile = possibleTileLocationsWithRank.firstOrNull { unit.movement.canReach(it.first) } @@ -85,7 +87,7 @@ object CityLocationTileRanker { // Only count a luxary resource that we don't have yet as unique once val newUniqueLuxuryResources = HashSet() - if (onCoast) tileValue += 15 + if (onCoast) tileValue += 8 if (newCityTile.isAdjacentToRiver()) tileValue += 10 if (newCityTile.terrainHasUnique(UniqueType.FreshWater)) tileValue += 5 // We want to found the city on an oasis because it can't be improved otherwise @@ -93,11 +95,16 @@ object CityLocationTileRanker { // If we build the city on a resource tile, then we can't build any special improvements on it if (newCityTile.resource != null) tileValue -= 4 + var tiles = 0 for (i in 0..3) { for (nearbyTile in newCityTile.getTilesAtDistance(i)) { + tiles++ tileValue += rankTile(nearbyTile, civ, onCoast, newUniqueLuxuryResources, baseTileMap, uniqueCache) } } + + // Placing cities on the edge of the map is bad, we can't even build improvements on them! + tileValue -= (HexMath.getNumberOfTilesInHexagon(3) - tiles) * 3 return tileValue } @@ -111,35 +118,37 @@ object CityLocationTileRanker { // If it is not higher the settler may get stuck when it ranks the same tile differently // as it moves away from the city and doesn't include it in the calculation // and values it higher than when it moves closer to the city - distanceToCity == 6 -> 2f - distanceToCity == 5 -> 5f - distanceToCity == 4 -> 10f - distanceToCity == 3 -> 15f - distanceToCity < 3 -> 25f // Even if it is a mod that lets us settle closer, lets still not do it + distanceToCity == 7 -> 5f // Perfect location, there aren't any unused tiles in between + distanceToCity == 6 -> -4f + distanceToCity == 5 -> -8f + distanceToCity == 4 -> -20f + distanceToCity == 3 -> -25f + distanceToCity < 3 -> -30f // Even if it is a mod that lets us settle closer, lets still not do it else -> 0f } // Bigger cities will expand more so we want to stay away from them // Reduces the chance that we don't settle at the begining distanceToCityModifier *= when { - city.population.population >= 12 -> 4f - city.population.population >= 8 -> 3f - city.population.population >= 3 -> 2f + city.population.population >= 12 -> 2f + city.population.population >= 8 -> 1.5f + city.population.population >= 3 -> 1.2f else -> 1f } // It is worse to settle cities near our own compare to near another civ // Do not settle near our capital unless really necessary // Having a strong capital is esential to constructing wonders - if (city.civ == civ) distanceToCityModifier *= if (city.isCapital()) 5 else 2 - modifier -= distanceToCityModifier + if (city.civ == civ) distanceToCityModifier *= if (city.isCapital()) 2 else 1 + modifier += distanceToCityModifier } return modifier } private fun rankTile(rankTile: Tile, civ: Civilization, onCoast: Boolean, newUniqueLuxuryResources: HashSet, baseTileMap: HashMap, uniqueCache: LocalUniqueCache): Float { + if (rankTile.getCity() != null) return -1f var locationSpecificTileValue = 0f // Don't settle near but not on the coast - if (rankTile.isCoastalTile() && !onCoast) locationSpecificTileValue -= 10 + if (rankTile.isCoastalTile() && !onCoast) locationSpecificTileValue -= 2 // Apply the effect of having a lighthouse, since we can probably assume that we will build it if (onCoast && rankTile.isOcean) locationSpecificTileValue += 1 // Check if there are any new unique luxury resources @@ -154,6 +163,8 @@ object CityLocationTileRanker { if (rankTile.getOwner() != null && rankTile.getOwner() != civ) return 0f var rankTileValue = Automation.rankStatsValue(rankTile.stats.getTileStats(null, civ, uniqueCache), civ) + // We can't build improvements on water tiles without resources + if (!rankTile.isLand) rankTileValue -= 1 if (rankTile.resource != null) { rankTileValue += when (rankTile.tileResource.resourceType) { diff --git a/core/src/com/unciv/logic/automation/unit/SpecificUnitAutomation.kt b/core/src/com/unciv/logic/automation/unit/SpecificUnitAutomation.kt index 3f3018305a..478cd018a8 100644 --- a/core/src/com/unciv/logic/automation/unit/SpecificUnitAutomation.kt +++ b/core/src/com/unciv/logic/automation/unit/SpecificUnitAutomation.kt @@ -107,7 +107,7 @@ object SpecificUnitAutomation { // It's possible that we'll see a tile "over the sea" that's better than the tiles close by, but that's not a reason to abandon the close tiles! // Also this lead to some routing problems, see https://github.com/yairm210/Unciv/issues/3653 - val bestTilesInfo = CityLocationTileRanker.getBestTilesToFoundCity(unit, rangeToSearch) + val bestTilesInfo = CityLocationTileRanker.getBestTilesToFoundCity(unit, rangeToSearch, 50f) var bestCityLocation: Tile? = null if (unit.civ.gameInfo.turns == 0 && unit.civ.cities.isEmpty() && bestTilesInfo.tileRankMap.containsKey(unit.getTile())) { // Special case, we want AI to settle in place on turn 1. @@ -133,7 +133,7 @@ object SpecificUnitAutomation { // If the tile we are currently on is close to the best tile, then lets just settle here instead if (bestTilesInfo.tileRankMap.containsKey(unit.getTile()) - && (bestTilesInfo.bestTile == null || bestTilesInfo.tileRankMap[unit.getTile()]!! >= bestTilesInfo.bestTileRank - 10)) { + && (bestTilesInfo.bestTile == null || bestTilesInfo.tileRankMap[unit.getTile()]!! >= bestTilesInfo.bestTileRank - 2)) { bestCityLocation = unit.getTile() } diff --git a/core/src/com/unciv/ui/screens/worldscreen/WorldMapHolder.kt b/core/src/com/unciv/ui/screens/worldscreen/WorldMapHolder.kt index 0586446dc7..26fc6324e5 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/WorldMapHolder.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/WorldMapHolder.kt @@ -858,7 +858,7 @@ class WorldMapHolder( // Highlight best tiles for city founding if (unit.hasUnique(UniqueType.FoundCity) && UncivGame.Current.settings.showSettlersSuggestedCityLocations) { - CityLocationTileRanker.getBestTilesToFoundCity(unit).tileRankMap.asSequence() + CityLocationTileRanker.getBestTilesToFoundCity(unit, minimumValue = 50f).tileRankMap.asSequence() .filter { it.key.isExplored(unit.civ) }.sortedByDescending { it.value }.take(3).forEach { tileGroups[it.key]!!.layerOverlay.showGoodCityLocationIndicator() }