Un-hardcoded some settler checks

This commit is contained in:
Yair Morgenstern
2020-12-20 22:55:36 +02:00
parent 585be1b2be
commit 566a9529ef
4 changed files with 51 additions and 45 deletions

View File

@ -67,8 +67,6 @@ object Constants {
const val disabled = "disabled" const val disabled = "disabled"
const val enabled = "enabled" const val enabled = "enabled"
const val scienceConversionEffect = "Production to science conversion in cities increased by 33%"
const val ancientEra = "Ancient era" const val ancientEra = "Ancient era"
const val classicalEra = "Classical era" const val classicalEra = "Classical era"
const val medievalEra = "Medieval era" const val medievalEra = "Medieval era"

View File

@ -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. //Player can choose not to auto-build roads & railroads.
if (unit.civInfo.isPlayerCivilization() && !UncivGame.Current.settings.autoBuildingRoads) if (unit.civInfo.isPlayerCivilization() && !UncivGame.Current.settings.autoBuildingRoads)
return false return false
@ -68,15 +68,17 @@ class WorkerAutomation(val unit: MapUnit) {
val targetRoad = unit.civInfo.tech.getBestRoadAvailable() val targetRoad = unit.civInfo.tech.getBestRoadAvailable()
val citiesThatNeedConnecting = unit.civInfo.cities.asSequence() val citiesThatNeedConnecting = unit.civInfo.cities.asSequence()
.filter { it.population.population>3 && !it.isCapital() && !it.isBeingRazed //City being razed should not be connected. .filter {
it.population.population > 3 && !it.isCapital() && !it.isBeingRazed //City being razed should not be connected.
&& !it.cityStats.isConnectedToCapital(targetRoad) && !it.cityStats.isConnectedToCapital(targetRoad)
// Cities that are too far away make the caReach() calculations devastatingly long // Cities that are too far away make the caReach() calculations devastatingly long
&& it.getCenterTile().aerialDistanceTo(unit.getTile()) < 20 } && it.getCenterTile().aerialDistanceTo(unit.getTile()) < 20
if(citiesThatNeedConnecting.none()) return false // do nothing. }
if (citiesThatNeedConnecting.none()) return false // do nothing.
val citiesThatNeedConnectingBfs = citiesThatNeedConnecting val citiesThatNeedConnectingBfs = citiesThatNeedConnecting
.sortedBy { it.getCenterTile().aerialDistanceTo(unit.civInfo.getCapital().getCenterTile()) } .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 val connectedCities = unit.civInfo.cities
.filter { it.isCapital() || it.cityStats.isConnectedToCapital(targetRoad) }.map { it.getCenterTile() } .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, // 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 // 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. // it can take to an existing connected city.
for(bfs in citiesThatNeedConnectingBfs) { for (bfs in citiesThatNeedConnectingBfs) {
while (bfs.tilesToCheck.isNotEmpty()) { while (bfs.tilesToCheck.isNotEmpty()) {
bfs.nextStep() bfs.nextStep()
for (city in connectedCities) for (city in connectedCities)
@ -177,7 +179,7 @@ class WorkerAutomation(val unit: MapUnit) {
} }
private fun chooseImprovement(tile: TileInfo, civInfo: CivilizationInfo): TileImprovement? { private fun chooseImprovement(tile: TileInfo, civInfo: CivilizationInfo): TileImprovement? {
val improvementStringForResource : String ?= when { val improvementStringForResource: String? = when {
tile.resource == null || !tile.hasViewableResource(civInfo) -> null tile.resource == null || !tile.hasViewableResource(civInfo) -> null
tile.terrainFeature == Constants.marsh && !isImprovementOnFeatureAllowed(tile, civInfo) -> "Remove Marsh" tile.terrainFeature == Constants.marsh && !isImprovementOnFeatureAllowed(tile, civInfo) -> "Remove Marsh"
tile.terrainFeature == "Fallout" && !isImprovementOnFeatureAllowed(tile, civInfo) -> "Remove Fallout" // for really mad modders 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 tileImprovements = civInfo.gameInfo.ruleSet.tileImprovements
val uniqueImprovement = tileImprovements.values val uniqueImprovement = tileImprovements.values
.firstOrNull { it.uniqueTo==civInfo.civName} .firstOrNull { it.uniqueTo == civInfo.civName }
val improvementString = when { val improvementString = when {
tile.improvementInProgress != null -> tile.improvementInProgress tile.improvementInProgress != null -> tile.improvementInProgress
improvementStringForResource != null && tileImprovements.containsKey(improvementStringForResource) improvementStringForResource != null && tileImprovements.containsKey(improvementStringForResource)
&& tileImprovements[improvementStringForResource]!!.turnsToBuild!=0 -> improvementStringForResource && tileImprovements[improvementStringForResource]!!.turnsToBuild != 0 -> improvementStringForResource
tile.containsGreatImprovement() -> null tile.containsGreatImprovement() -> null
tile.containsUnfinishedGreatImprovement() -> null tile.containsUnfinishedGreatImprovement() -> null
// Defence is more important that civilian improvements // Defence is more important that civilian improvements
// While AI sucks in strategical placement of forts, allow a human does it manually // 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 // 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 == "Fallout" -> "Remove Fallout"
tile.terrainFeature == Constants.marsh -> "Remove Marsh" tile.terrainFeature == Constants.marsh -> "Remove Marsh"
@ -209,7 +211,7 @@ class WorkerAutomation(val unit: MapUnit) {
tile.terrainFeature == "Oasis" -> null tile.terrainFeature == "Oasis" -> null
tile.terrainFeature == Constants.forest -> "Lumber mill" tile.terrainFeature == Constants.forest -> "Lumber mill"
tile.isHill() -> "Mine" 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.isAdjacentToFreshwater -> "Farm"
tile.baseTerrain in listOf(Constants.tundra, Constants.snow) -> Constants.tradingPost tile.baseTerrain in listOf(Constants.tundra, Constants.snow) -> Constants.tradingPost
else -> null else -> null
@ -217,6 +219,7 @@ class WorkerAutomation(val unit: MapUnit) {
if (improvementString == null) return null if (improvementString == null) return null
return unit.civInfo.gameInfo.ruleSet.tileImprovements[improvementString] // For mods, the tile improvement may not exist, so don't assume. 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 { private fun isImprovementOnFeatureAllowed(tile: TileInfo, civInfo: CivilizationInfo): Boolean {
// routine assumes the caller ensured that terrainFeature and resource are both present // routine assumes the caller ensured that terrainFeature and resource are both present
val resourceImprovementName = tile.getTileResource().improvement val resourceImprovementName = tile.getTileResource().improvement
@ -226,8 +229,7 @@ class WorkerAutomation(val unit: MapUnit) {
return resourceImprovement.resourceTerrainAllow.contains(tile.terrainFeature!!) 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 if (tile.isCityCenter() // don't build fort in the city
|| !tile.isLand // don't build fort in the water || !tile.isLand // don't build fort in the water
|| tile.improvement == Constants.fort // don't build fort if it is already here || 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 !isAcceptableTileForFort(tile, civInfo)) return false
// if this place is not perfect, let's see if there is a better one // 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) { for (closeTile in nearestTiles) {
// don't build forts too close to the cities // don't build forts too close to the cities
if (closeTile.isCityCenter()) return false if (closeTile.isCityCenter()) return false
// don't build forts too close to other forts // don't build forts too close to other forts
if (closeTile.improvement == Constants.fort || closeTile.improvement == Constants.citadel if (closeTile.improvement != null
|| closeTile.improvementInProgress == Constants.fort) return false && 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 // there is another better tile for the fort
if (!tile.isHill() && closeTile.isHill() && if (!tile.isHill() && closeTile.isHill() &&
isAcceptableTileForFort(closeTile, civInfo)) return false isAcceptableTileForFort(closeTile, civInfo)) return false
@ -263,7 +266,7 @@ class WorkerAutomation(val unit: MapUnit) {
// no potential enemies // no potential enemies
if (enemyCivs.isEmpty()) return false if (enemyCivs.isEmpty()) return false
val threatMapping : (CivilizationInfo) -> Int = { val threatMapping: (CivilizationInfo) -> Int = {
// the war is already a good nudge to build forts // the war is already a good nudge to build forts
(if (civInfo.isAtWarWith(it)) 20 else 0) + (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
@ -273,14 +276,15 @@ class WorkerAutomation(val unit: MapUnit) {
ThreatLevel.Medium -> 10 ThreatLevel.Medium -> 10
ThreatLevel.High -> 15 // they are strong, let's built until they reach us ThreatLevel.High -> 15 // they are strong, let's built until they reach us
ThreatLevel.VeryHigh -> 20 ThreatLevel.VeryHigh -> 20
} } }
}
val enemyCivsIsCloseEnough = enemyCivs.filter { NextTurnAutomation.getMinDistanceBetweenCities(civInfo, it) <= threatMapping(it) } val enemyCivsIsCloseEnough = enemyCivs.filter { NextTurnAutomation.getMinDistanceBetweenCities(civInfo, it) <= threatMapping(it) }
// no threat, let's not build fort // no threat, let's not build fort
if (enemyCivsIsCloseEnough.isEmpty()) return false if (enemyCivsIsCloseEnough.isEmpty()) return false
// make list of enemy cities as sources of threat // make list of enemy cities as sources of threat
val enemyCities = mutableListOf<TileInfo>() val enemyCities = mutableListOf<TileInfo>()
enemyCivsIsCloseEnough.forEach { enemyCities.addAll(it.cities.map { city -> city.getCenterTile() } ) } enemyCivsIsCloseEnough.forEach { enemyCities.addAll(it.cities.map { city -> city.getCenterTile() }) }
// find closest enemy city // find closest enemy city
val closestEnemyCity = enemyCities.minBy { it.aerialDistanceTo(tile) }!! val closestEnemyCity = enemyCities.minBy { it.aerialDistanceTo(tile) }!!

View File

@ -36,22 +36,24 @@ class UniqueMap:HashMap<String, ArrayList<Unique>>() {
// Buildings, techs and policies can have 'triggered' effects // Buildings, techs and policies can have 'triggered' effects
object UniqueTriggerActivation { object UniqueTriggerActivation {
fun triggerCivwideUnique(unique: Unique, civInfo: CivilizationInfo, cityInfo:CityInfo?=null) { fun triggerCivwideUnique(unique: Unique, civInfo: CivilizationInfo, cityInfo: CityInfo? = null) {
val chosenCity = if(cityInfo!=null) cityInfo else civInfo.cities.firstOrNull { it.isCapital() } val chosenCity = if (cityInfo != null) cityInfo else civInfo.cities.firstOrNull { it.isCapital() }
when (unique.placeholderText) { when (unique.placeholderText) {
"Free [] appears" -> { "Free [] appears" -> {
val unitName = unique.params[0] 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) civInfo.addUnit(unitName, chosenCity)
} }
"[] free [] units appear" -> { "[] free [] units appear" -> {
val unitName = unique.params[1] 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()) for (i in 1..unique.params[0].toInt())
civInfo.addUnit(unitName, chosenCity) 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 // 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" -> "Empire enters golden age" ->
civInfo.goldenAges.enterGoldenAge() civInfo.goldenAges.enterGoldenAge()
"Free Great Person" -> { "Free Great Person" -> {
@ -84,9 +86,11 @@ object UniqueTriggerActivation {
val promotion = unique.params[1] val promotion = unique.params[1]
for (unit in civInfo.getCivUnits()) for (unit in civInfo.getCivUnits())
if (unit.matchesFilter(filter) if (unit.matchesFilter(filter)
|| (civInfo.gameInfo.ruleSet.unitPromotions.values.any { it.name == promotion || (civInfo.gameInfo.ruleSet.unitPromotions.values.any {
&& unit.type.name in it.unitTypes })) it.name == promotion && unit.type.name in it.unitTypes
unit.promotions.addPromotion(promotion, isFree = true)} }))
unit.promotions.addPromotion(promotion, isFree = true)
}
} }
} }

View File

@ -18,11 +18,11 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
init { init {
pad(2f) pad(2f)
background = ImageGetter.getBackground(colorFromRGB(194,180,131)) background = ImageGetter.getBackground(colorFromRGB(194, 180, 131))
innerTable.pad(5f) innerTable.pad(5f)
innerTable.defaults().pad(2f) 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() add(innerTable).fill()
} }
@ -31,8 +31,8 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
innerTable.clear() innerTable.clear()
val ministatsTable = Table() val ministatsTable = Table()
for(stat in cityInfo.cityStats.currentCityStats.toHashMap()) { for (stat in cityInfo.cityStats.currentCityStats.toHashMap()) {
if(stat.key == Stat.Happiness || stat.key == Stat.Faith) continue if (stat.key == Stat.Happiness || stat.key == Stat.Faith) continue
ministatsTable.add(ImageGetter.getStatIcon(stat.key.name)).size(20f).padRight(5f) ministatsTable.add(ImageGetter.getStatIcon(stat.key.name)).size(20f).padRight(5f)
ministatsTable.add(round(stat.value).toInt().toString().toLabel()).padRight(10f) ministatsTable.add(round(stat.value).toInt().toString().toLabel()).padRight(10f)
} }
@ -59,7 +59,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
} else { } else {
"Stopped expansion".tr() "Stopped expansion".tr()
} }
if (cityInfo.expansion.chooseNewTileToOwn()!=null) if (cityInfo.expansion.chooseNewTileToOwn() != null)
turnsToExpansionString += " (" + cityInfo.expansion.cultureStored + "/" + turnsToExpansionString += " (" + cityInfo.expansion.cultureStored + "/" +
cityInfo.expansion.getCultureToNextTile() + ")" cityInfo.expansion.getCultureToNextTile() + ")"