From 566a9529ef3ef9a9df5066d9b3f8f2e59d1c70f9 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Sun, 20 Dec 2020 22:55:36 +0200 Subject: [PATCH] Un-hardcoded some settler checks --- core/src/com/unciv/Constants.kt | 2 - .../logic/automation/WorkerAutomation.kt | 62 ++++++++++--------- core/src/com/unciv/models/ruleset/Unique.kt | 20 +++--- .../com/unciv/ui/cityscreen/CityStatsTable.kt | 12 ++-- 4 files changed, 51 insertions(+), 45 deletions(-) diff --git a/core/src/com/unciv/Constants.kt b/core/src/com/unciv/Constants.kt index 4c11f1583f..010e14c873 100644 --- a/core/src/com/unciv/Constants.kt +++ b/core/src/com/unciv/Constants.kt @@ -67,8 +67,6 @@ object Constants { const val disabled = "disabled" const val enabled = "enabled" - const val scienceConversionEffect = "Production to science conversion in cities increased by 33%" - const val ancientEra = "Ancient era" const val classicalEra = "Classical era" const val medievalEra = "Medieval era" diff --git a/core/src/com/unciv/logic/automation/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/WorkerAutomation.kt index 788801f87d..884bc4543a 100644 --- a/core/src/com/unciv/logic/automation/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/WorkerAutomation.kt @@ -60,7 +60,7 @@ class WorkerAutomation(val unit: MapUnit) { } - private fun tryConnectingCities(unit: MapUnit):Boolean { // returns whether we actually did anything + private fun tryConnectingCities(unit: MapUnit): Boolean { // returns whether we actually did anything //Player can choose not to auto-build roads & railroads. if (unit.civInfo.isPlayerCivilization() && !UncivGame.Current.settings.autoBuildingRoads) return false @@ -68,15 +68,17 @@ class WorkerAutomation(val unit: MapUnit) { val targetRoad = unit.civInfo.tech.getBestRoadAvailable() val citiesThatNeedConnecting = unit.civInfo.cities.asSequence() - .filter { it.population.population>3 && !it.isCapital() && !it.isBeingRazed //City being razed should not be connected. - && !it.cityStats.isConnectedToCapital(targetRoad) - // Cities that are too far away make the caReach() calculations devastatingly long - && it.getCenterTile().aerialDistanceTo(unit.getTile()) < 20 } - if(citiesThatNeedConnecting.none()) return false // do nothing. + .filter { + it.population.population > 3 && !it.isCapital() && !it.isBeingRazed //City being razed should not be connected. + && !it.cityStats.isConnectedToCapital(targetRoad) + // Cities that are too far away make the caReach() calculations devastatingly long + && it.getCenterTile().aerialDistanceTo(unit.getTile()) < 20 + } + if (citiesThatNeedConnecting.none()) return false // do nothing. val citiesThatNeedConnectingBfs = citiesThatNeedConnecting .sortedBy { it.getCenterTile().aerialDistanceTo(unit.civInfo.getCapital().getCenterTile()) } - .map { city -> BFS(city.getCenterTile()){it.isLand && unit.movement.canPassThrough(it)} } + .map { city -> BFS(city.getCenterTile()) { it.isLand && unit.movement.canPassThrough(it) } } val connectedCities = unit.civInfo.cities .filter { it.isCapital() || it.cityStats.isConnectedToCapital(targetRoad) }.map { it.getCenterTile() } @@ -84,7 +86,7 @@ class WorkerAutomation(val unit: MapUnit) { // Since further away cities take longer to get to and - most importantly - the canReach() to them is very long, // we order cities by their closeness to the worker first, and then check for each one whether there's a viable path // it can take to an existing connected city. - for(bfs in citiesThatNeedConnectingBfs) { + for (bfs in citiesThatNeedConnectingBfs) { while (bfs.tilesToCheck.isNotEmpty()) { bfs.nextStep() for (city in connectedCities) @@ -124,7 +126,7 @@ class WorkerAutomation(val unit: MapUnit) { (it.civilianUnit == null || it == currentTile) && tileCanBeImproved(it, unit.civInfo) && it.getTilesInDistance(2) - .none { it.isCityCenter() && it.getCity()!!.civInfo.isAtWarWith(unit.civInfo) } + .none { it.isCityCenter() && it.getCity()!!.civInfo.isAtWarWith(unit.civInfo) } } .sortedByDescending { getPriority(it, unit.civInfo) } @@ -177,7 +179,7 @@ class WorkerAutomation(val unit: MapUnit) { } private fun chooseImprovement(tile: TileInfo, civInfo: CivilizationInfo): TileImprovement? { - val improvementStringForResource : String ?= when { + val improvementStringForResource: String? = when { tile.resource == null || !tile.hasViewableResource(civInfo) -> null tile.terrainFeature == Constants.marsh && !isImprovementOnFeatureAllowed(tile, civInfo) -> "Remove Marsh" tile.terrainFeature == "Fallout" && !isImprovementOnFeatureAllowed(tile, civInfo) -> "Remove Fallout" // for really mad modders @@ -188,20 +190,20 @@ class WorkerAutomation(val unit: MapUnit) { val tileImprovements = civInfo.gameInfo.ruleSet.tileImprovements val uniqueImprovement = tileImprovements.values - .firstOrNull { it.uniqueTo==civInfo.civName} + .firstOrNull { it.uniqueTo == civInfo.civName } val improvementString = when { tile.improvementInProgress != null -> tile.improvementInProgress improvementStringForResource != null && tileImprovements.containsKey(improvementStringForResource) - && tileImprovements[improvementStringForResource]!!.turnsToBuild!=0 -> improvementStringForResource + && tileImprovements[improvementStringForResource]!!.turnsToBuild != 0 -> improvementStringForResource tile.containsGreatImprovement() -> null tile.containsUnfinishedGreatImprovement() -> null // Defence is more important that civilian improvements // While AI sucks in strategical placement of forts, allow a human does it manually - !civInfo.isPlayerCivilization() && evaluateFortPlacement(tile,civInfo,false) -> Constants.fort + !civInfo.isPlayerCivilization() && evaluateFortPlacement(tile, civInfo, false) -> Constants.fort // I think we can assume that the unique improvement is better - uniqueImprovement!=null && tile.canBuildImprovement(uniqueImprovement,civInfo) -> uniqueImprovement.name + uniqueImprovement != null && tile.canBuildImprovement(uniqueImprovement, civInfo) -> uniqueImprovement.name tile.terrainFeature == "Fallout" -> "Remove Fallout" tile.terrainFeature == Constants.marsh -> "Remove Marsh" @@ -209,7 +211,7 @@ class WorkerAutomation(val unit: MapUnit) { tile.terrainFeature == "Oasis" -> null tile.terrainFeature == Constants.forest -> "Lumber mill" tile.isHill() -> "Mine" - tile.baseTerrain in listOf(Constants.grassland,Constants.desert,Constants.plains) -> "Farm" + tile.baseTerrain in listOf(Constants.grassland, Constants.desert, Constants.plains) -> "Farm" tile.isAdjacentToFreshwater -> "Farm" tile.baseTerrain in listOf(Constants.tundra, Constants.snow) -> Constants.tradingPost else -> null @@ -217,6 +219,7 @@ class WorkerAutomation(val unit: MapUnit) { if (improvementString == null) return null return unit.civInfo.gameInfo.ruleSet.tileImprovements[improvementString] // For mods, the tile improvement may not exist, so don't assume. } + private fun isImprovementOnFeatureAllowed(tile: TileInfo, civInfo: CivilizationInfo): Boolean { // routine assumes the caller ensured that terrainFeature and resource are both present val resourceImprovementName = tile.getTileResource().improvement @@ -226,8 +229,7 @@ class WorkerAutomation(val unit: MapUnit) { return resourceImprovement.resourceTerrainAllow.contains(tile.terrainFeature!!) } - private fun isAcceptableTileForFort(tile: TileInfo, civInfo: CivilizationInfo): Boolean - { + private fun isAcceptableTileForFort(tile: TileInfo, civInfo: CivilizationInfo): Boolean { if (tile.isCityCenter() // don't build fort in the city || !tile.isLand // don't build fort in the water || tile.improvement == Constants.fort // don't build fort if it is already here @@ -246,13 +248,14 @@ class WorkerAutomation(val unit: MapUnit) { !isAcceptableTileForFort(tile, civInfo)) return false // if this place is not perfect, let's see if there is a better one - val nearestTiles = tile.getTilesInDistance(2).filter{it.owningCity?.civInfo == civInfo}.toList() + val nearestTiles = tile.getTilesInDistance(2).filter { it.owningCity?.civInfo == civInfo }.toList() for (closeTile in nearestTiles) { // don't build forts too close to the cities if (closeTile.isCityCenter()) return false // don't build forts too close to other forts - if (closeTile.improvement == Constants.fort || closeTile.improvement == Constants.citadel - || closeTile.improvementInProgress == Constants.fort) return false + if (closeTile.improvement != null + && closeTile.getTileImprovement()!!.uniqueObjects.any { it.placeholderText == "Gives a defensive bonus of []%" } + || closeTile.improvementInProgress != Constants.fort) return false // there is another better tile for the fort if (!tile.isHill() && closeTile.isHill() && isAcceptableTileForFort(closeTile, civInfo)) return false @@ -263,24 +266,25 @@ class WorkerAutomation(val unit: MapUnit) { // no potential enemies if (enemyCivs.isEmpty()) return false - val threatMapping : (CivilizationInfo) -> Int = { + val threatMapping: (CivilizationInfo) -> Int = { // the war is already a good nudge to build forts (if (civInfo.isAtWarWith(it)) 20 else 0) + - // let's check also the force of the enemy + // let's check also the force of the enemy when (Automation.threatAssessment(civInfo, it)) { - ThreatLevel.VeryLow -> 1 // do not build forts - ThreatLevel.Low -> 6 // too close, let's build until it is late - ThreatLevel.Medium -> 10 - ThreatLevel.High -> 15 // they are strong, let's built until they reach us - ThreatLevel.VeryHigh -> 20 - } } + ThreatLevel.VeryLow -> 1 // do not build forts + ThreatLevel.Low -> 6 // too close, let's build until it is late + ThreatLevel.Medium -> 10 + ThreatLevel.High -> 15 // they are strong, let's built until they reach us + ThreatLevel.VeryHigh -> 20 + } + } val enemyCivsIsCloseEnough = enemyCivs.filter { NextTurnAutomation.getMinDistanceBetweenCities(civInfo, it) <= threatMapping(it) } // no threat, let's not build fort if (enemyCivsIsCloseEnough.isEmpty()) return false // make list of enemy cities as sources of threat val enemyCities = mutableListOf() - enemyCivsIsCloseEnough.forEach { enemyCities.addAll(it.cities.map { city -> city.getCenterTile() } ) } + enemyCivsIsCloseEnough.forEach { enemyCities.addAll(it.cities.map { city -> city.getCenterTile() }) } // find closest enemy city val closestEnemyCity = enemyCities.minBy { it.aerialDistanceTo(tile) }!! diff --git a/core/src/com/unciv/models/ruleset/Unique.kt b/core/src/com/unciv/models/ruleset/Unique.kt index 6ec7d6043a..eccca4c50c 100644 --- a/core/src/com/unciv/models/ruleset/Unique.kt +++ b/core/src/com/unciv/models/ruleset/Unique.kt @@ -36,22 +36,24 @@ class UniqueMap:HashMap>() { // Buildings, techs and policies can have 'triggered' effects object UniqueTriggerActivation { - fun triggerCivwideUnique(unique: Unique, civInfo: CivilizationInfo, cityInfo:CityInfo?=null) { - val chosenCity = if(cityInfo!=null) cityInfo else civInfo.cities.firstOrNull { it.isCapital() } + fun triggerCivwideUnique(unique: Unique, civInfo: CivilizationInfo, cityInfo: CityInfo? = null) { + val chosenCity = if (cityInfo != null) cityInfo else civInfo.cities.firstOrNull { it.isCapital() } when (unique.placeholderText) { "Free [] appears" -> { val unitName = unique.params[0] - if (chosenCity != null && (unitName != Constants.settler || !civInfo.isOneCityChallenger())) + val unit = civInfo.gameInfo.ruleSet.units[unitName] + if (chosenCity != null && unit != null && (!unit.uniques.contains("Founds a new city") || !civInfo.isOneCityChallenger())) civInfo.addUnit(unitName, chosenCity) } "[] free [] units appear" -> { val unitName = unique.params[1] - if (chosenCity!=null && (unitName != Constants.settler || !civInfo.isOneCityChallenger())) + val unit = civInfo.gameInfo.ruleSet.units[unitName] + if (chosenCity != null && unit != null && (!unit.uniques.contains("Founds a new city") || !civInfo.isOneCityChallenger())) for (i in 1..unique.params[0].toInt()) civInfo.addUnit(unitName, chosenCity) } // spectators get all techs at start of game, and if (in a mod) a tech gives a free policy, the game stucks on the policy picker screen - "Free Social Policy" -> if(!civInfo.isSpectator()) civInfo.policies.freePolicies++ + "Free Social Policy" -> if (!civInfo.isSpectator()) civInfo.policies.freePolicies++ "Empire enters golden age" -> civInfo.goldenAges.enterGoldenAge() "Free Great Person" -> { @@ -84,9 +86,11 @@ object UniqueTriggerActivation { val promotion = unique.params[1] for (unit in civInfo.getCivUnits()) if (unit.matchesFilter(filter) - || (civInfo.gameInfo.ruleSet.unitPromotions.values.any { it.name == promotion - && unit.type.name in it.unitTypes })) - unit.promotions.addPromotion(promotion, isFree = true)} + || (civInfo.gameInfo.ruleSet.unitPromotions.values.any { + it.name == promotion && unit.type.name in it.unitTypes + })) + unit.promotions.addPromotion(promotion, isFree = true) + } } } diff --git a/core/src/com/unciv/ui/cityscreen/CityStatsTable.kt b/core/src/com/unciv/ui/cityscreen/CityStatsTable.kt index da010952ff..071521dfd9 100644 --- a/core/src/com/unciv/ui/cityscreen/CityStatsTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityStatsTable.kt @@ -18,11 +18,11 @@ class CityStatsTable(val cityScreen: CityScreen): Table() { init { pad(2f) - background = ImageGetter.getBackground(colorFromRGB(194,180,131)) + background = ImageGetter.getBackground(colorFromRGB(194, 180, 131)) innerTable.pad(5f) innerTable.defaults().pad(2f) - innerTable.background = ImageGetter.getBackground(Color.BLACK.cpy().apply { a=0.8f }) + innerTable.background = ImageGetter.getBackground(Color.BLACK.cpy().apply { a = 0.8f }) add(innerTable).fill() } @@ -31,8 +31,8 @@ class CityStatsTable(val cityScreen: CityScreen): Table() { innerTable.clear() val ministatsTable = Table() - for(stat in cityInfo.cityStats.currentCityStats.toHashMap()) { - if(stat.key == Stat.Happiness || stat.key == Stat.Faith) continue + for (stat in cityInfo.cityStats.currentCityStats.toHashMap()) { + if (stat.key == Stat.Happiness || stat.key == Stat.Faith) continue ministatsTable.add(ImageGetter.getStatIcon(stat.key.name)).size(20f).padRight(5f) ministatsTable.add(round(stat.value).toInt().toString().toLabel()).padRight(10f) } @@ -59,9 +59,9 @@ class CityStatsTable(val cityScreen: CityScreen): Table() { } else { "Stopped expansion".tr() } - if (cityInfo.expansion.chooseNewTileToOwn()!=null) + if (cityInfo.expansion.chooseNewTileToOwn() != null) turnsToExpansionString += " (" + cityInfo.expansion.cultureStored + "/" + - cityInfo.expansion.getCultureToNextTile() + ")" + cityInfo.expansion.getCultureToNextTile() + ")" var turnsToPopString = when {