Generalized building of improvements (#4252)

* Generalized building of improvements

* Readded support for the deprecated unique "Can build improvements on tiles"

* Small code quality changes

* Implemented requested chagnes
This commit is contained in:
Xander Lenstra 2021-06-25 15:35:15 +02:00 committed by GitHub
parent 06b2e7da2f
commit e6850b857a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 156 additions and 113 deletions

View File

@ -7,7 +7,7 @@
"name": "Worker", "name": "Worker",
"unitType": "Civilian", "unitType": "Civilian",
"movement": 2, "movement": 2,
"uniques": ["Can build improvements on tiles"], "uniques": ["Can build [Land] improvements on tiles"],
"cost": 70 "cost": 70
}, },
{ {
@ -372,10 +372,9 @@
"upgradesTo": "Longswordsman", "upgradesTo": "Longswordsman",
"obsoleteTech": "Gunpowder", "obsoleteTech": "Gunpowder",
"requiredResource": "Iron", "requiredResource": "Iron",
"uniques": ["Can construct roads"], "uniques": ["Can build [Road] improvements on tiles", "Can build [Fort] improvements on tiles"],
"hurryCostModifier": 20, "hurryCostModifier": 20,
"attackSound": "metalhit" "attackSound": "metalhit"
// can construct fort (if required for fort tech is researched)
}, },
{ {
"name": "Mohawk Warrior", "name": "Mohawk Warrior",

View File

@ -2,7 +2,10 @@ package com.unciv
object Constants { object Constants {
const val worker = "Worker" const val worker = "Worker"
const val workerUnique = "Can build improvements on tiles" const val canBuildImprovements = "Can build [] improvements on tiles"
// Deprecated as of 3.15.5
const val workerUnique = "Can build improvements on tiles"
//
const val settler = "Settler" const val settler = "Settler"
const val settlerUnique = "Founds a new city" const val settlerUnique = "Founds a new city"

View File

@ -11,8 +11,8 @@ import com.unciv.logic.map.BFS
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.VictoryType import com.unciv.models.ruleset.VictoryType
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
import com.unciv.models.translations.equalsPlaceholderText
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt
import kotlin.math.sqrt import kotlin.math.sqrt
class ConstructionAutomation(val cityConstructions: CityConstructions){ class ConstructionAutomation(val cityConstructions: CityConstructions){
@ -26,8 +26,9 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
.filter { it.isWonder || it.isNationalWonder } .filter { it.isWonder || it.isNationalWonder }
val civUnits = civInfo.getCivUnits() val civUnits = civInfo.getCivUnits()
val militaryUnits = civUnits.count { !it.type.isCivilian()} val militaryUnits = civUnits.count { !it.type.isCivilian() }
val workers = civUnits.count { it.hasUnique(Constants.workerUnique) }.toFloat() // Constants.workerUnique deprecated since 3.15.5
val workers = civUnits.count { (it.hasUnique(Constants.canBuildImprovements) || it.hasUnique(Constants.workerUnique)) && it.type.isCivilian() }.toFloat()
val cities = civInfo.cities.size val cities = civInfo.cities.size
val allTechsAreResearched = civInfo.tech.getNumberOfTechsResearched() >= civInfo.gameInfo.ruleSet.technologies.size val allTechsAreResearched = civInfo.tech.getNumberOfTechsResearched() >= civInfo.gameInfo.ruleSet.technologies.size
@ -68,7 +69,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
addWorkBoatChoice() addWorkBoatChoice()
addMilitaryUnitChoice() addMilitaryUnitChoice()
} }
val production = cityInfo.cityStats.currentCityStats.production val production = cityInfo.cityStats.currentCityStats.production
val theChosenOne: String val theChosenOne: String
@ -138,13 +139,18 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
private fun addWorkerChoice() { private fun addWorkerChoice() {
val workerEquivalents = civInfo.gameInfo.ruleSet.units.values val workerEquivalents = civInfo.gameInfo.ruleSet.units.values
.filter { it.uniques.contains(Constants.workerUnique) && it.isBuildable(cityConstructions) } .filter { it.uniques.any {
// Constants.workerUnique deprecated since 3.15.5
unique -> unique.equalsPlaceholderText(Constants.canBuildImprovements) || unique.equalsPlaceholderText(Constants.workerUnique)
} && it.isBuildable(cityConstructions) }
if (workerEquivalents.isEmpty()) return // for mods with no worker units if (workerEquivalents.isEmpty()) return // for mods with no worker units
if (civInfo.getIdleUnits().any { it.action == Constants.unitActionAutomation && it.hasUnique(Constants.workerUnique) }) // Constants.workerUnique deprecated since 3.15.5
if (civInfo.getIdleUnits().any { it.action == Constants.unitActionAutomation && (it.hasUnique(Constants.canBuildImprovements) || it.hasUnique(Constants.workerUnique)) })
return // If we have automated workers who have no work to do then it's silly to construct new workers. return // If we have automated workers who have no work to do then it's silly to construct new workers.
val citiesCountedTowardsWorkers = min(5, cities) // above 5 cities, extra cities won't make us want more workers val citiesCountedTowardsWorkers = min(5, cities) // above 5 cities, extra cities won't make us want more workers
if (workers < citiesCountedTowardsWorkers * 0.6f && civUnits.none { it.hasUnique(Constants.workerUnique) && it.isIdle() }) { // Constants.workerUnique deprecated since 3.15.5
if (workers < citiesCountedTowardsWorkers * 0.6f && civUnits.none { (it.hasUnique(Constants.canBuildImprovements) || it.hasUnique(Constants.workerUnique)) && it.isIdle() }) {
var modifier = citiesCountedTowardsWorkers / (workers + 0.1f) var modifier = citiesCountedTowardsWorkers / (workers + 0.1f)
if (!cityIsOverAverageProduction) modifier /= 5 // higher production cities will deal with this if (!cityIsOverAverageProduction) modifier /= 5 // higher production cities will deal with this
addChoice(relativeCostEffectiveness, workerEquivalents.minByOrNull { it.cost }!!.name, modifier) addChoice(relativeCostEffectiveness, workerEquivalents.minByOrNull { it.cost }!!.name, modifier)

View File

@ -89,10 +89,11 @@ object UnitAutomation {
if (unit.hasUnique(Constants.settlerUnique)) if (unit.hasUnique(Constants.settlerUnique))
return SpecificUnitAutomation.automateSettlerActions(unit) return SpecificUnitAutomation.automateSettlerActions(unit)
if (unit.hasUnique(Constants.workerUnique)) // Constants.workerUnique deprecated since 3.15.5
if (unit.hasUnique(Constants.canBuildImprovements) || unit.hasUnique(Constants.workerUnique))
return WorkerAutomation(unit).automateWorkerAction() return WorkerAutomation(unit).automateWorkerAction()
if (unit.name == "Work Boats") if (unit.name == "Work Boats") // This is really not modular
return SpecificUnitAutomation.automateWorkBoats(unit) return SpecificUnitAutomation.automateWorkBoats(unit)
if (unit.hasUnique("Bonus for units in 2 tile radius 15%")) if (unit.hasUnique("Bonus for units in 2 tile radius 15%"))

View File

@ -15,11 +15,11 @@ class WorkerAutomation(val unit: MapUnit) {
val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys val enemyUnitsInWalkingDistance = unit.movement.getDistanceToTiles().keys
.filter { UnitAutomation.containsEnemyMilitaryUnit(unit, it) } .filter { UnitAutomation.containsEnemyMilitaryUnit(unit, it) }
if (enemyUnitsInWalkingDistance.isNotEmpty()) return UnitAutomation.runAway(unit) if (enemyUnitsInWalkingDistance.isNotEmpty() && !unit.type.isMilitary()) return UnitAutomation.runAway(unit)
val currentTile = unit.getTile() val currentTile = unit.getTile()
val tileToWork = findTileToWork() val tileToWork = findTileToWork()
if (getPriority(tileToWork, unit.civInfo) < 3) { // building roads is more important if (getPriority(tileToWork, unit.civInfo) < 3) { // building roads is more important
if (tryConnectingCities(unit)) return if (tryConnectingCities(unit)) return
} }
@ -63,7 +63,6 @@ class WorkerAutomation(val unit: MapUnit) {
//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
val targetRoad = unit.civInfo.tech.getBestRoadAvailable() val targetRoad = unit.civInfo.tech.getBestRoadAvailable()
val citiesThatNeedConnecting = unit.civInfo.cities.asSequence() val citiesThatNeedConnecting = unit.civInfo.cities.asSequence()
@ -137,7 +136,7 @@ class WorkerAutomation(val unit: MapUnit) {
return if (selectedTile != null return if (selectedTile != null
&& getPriority(selectedTile, unit.civInfo) > 1 && getPriority(selectedTile, unit.civInfo) > 1
&& (!workableTiles.contains(currentTile) && (!workableTiles.contains(currentTile)
|| getPriority(selectedTile, unit.civInfo) > getPriority(currentTile, unit.civInfo))) || getPriority(selectedTile, unit.civInfo) > getPriority(currentTile, unit.civInfo)))
selectedTile selectedTile
else currentTile else currentTile
} }
@ -152,15 +151,14 @@ class WorkerAutomation(val unit: MapUnit) {
return false return false
if (tile.improvement == null) { if (tile.improvement == null) {
if (tile.improvementInProgress != null) return true if (tile.improvementInProgress != null && unit.canBuildImprovement(tile.getTileImprovementInProgress()!!, tile)) return true
val chosenImprovement = chooseImprovement(tile, civInfo) val chosenImprovement = chooseImprovement(tile, civInfo)
if (chosenImprovement != null && tile.canBuildImprovement(chosenImprovement, civInfo)) return true if (chosenImprovement != null && tile.canBuildImprovement(chosenImprovement, civInfo) && unit.canBuildImprovement(chosenImprovement, tile)) return true
} else if (!tile.containsGreatImprovement() && tile.hasViewableResource(civInfo) } else if (!tile.containsGreatImprovement() && tile.hasViewableResource(civInfo)
&& tile.getTileResource().improvement != tile.improvement && tile.getTileResource().improvement != tile.improvement
&& chooseImprovement(tile, civInfo) // if the chosen improvement is not null and buildable && chooseImprovement(tile, civInfo) // if the chosen improvement is not null and buildable
.let { it != null && tile.canBuildImprovement(it, civInfo) }) .let { it != null && tile.canBuildImprovement(it, civInfo) && unit.canBuildImprovement(it, tile)})
return true return true
return false // couldn't find anything to construct here return false // couldn't find anything to construct here
} }
@ -204,7 +202,9 @@ class WorkerAutomation(val unit: MapUnit) {
// 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)
&& unit.canBuildImprovement(uniqueImprovement, tile) ->
uniqueImprovement.name
tile.terrainFeatures.contains("Fallout") -> "Remove Fallout" tile.terrainFeatures.contains("Fallout") -> "Remove Fallout"
tile.terrainFeatures.contains(Constants.marsh) -> "Remove Marsh" tile.terrainFeatures.contains(Constants.marsh) -> "Remove Marsh"

View File

@ -10,6 +10,7 @@ import com.unciv.logic.civilization.LocationAction
import com.unciv.logic.civilization.NotificationIcon import com.unciv.logic.civilization.NotificationIcon
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.Unique import com.unciv.models.ruleset.Unique
import com.unciv.models.ruleset.tile.TileImprovement
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.ruleset.unit.UnitType import com.unciv.models.ruleset.unit.UnitType
import java.text.DecimalFormat import java.text.DecimalFormat
@ -257,8 +258,13 @@ class MapUnit {
fun isIdle(): Boolean { fun isIdle(): Boolean {
if (currentMovement == 0f) return false if (currentMovement == 0f) return false
if (hasUnique(Constants.workerUnique) && getTile().improvementInProgress != null) return false // Constants.workerUnique deprecated since 3.15.5
if (hasUnique("Can construct roads") && currentTile.improvementInProgress == "Road") return false if (getTile().improvementInProgress != null
&& canBuildImprovement(getTile().getTileImprovementInProgress()!!))
return false
// unique "Can construct roads" deprecated since 3.15.5
if (hasUnique("Can construct roads") && currentTile.improvementInProgress == "Road") return false
//
if (isFortified()) return false if (isFortified()) return false
if (action == Constants.unitActionExplore || isSleeping() if (action == Constants.unitActionExplore || isSleeping()
|| action == Constants.unitActionAutomation || isMoving() || action == Constants.unitActionAutomation || isMoving()
@ -540,12 +546,16 @@ class MapUnit {
fun endTurn() { fun endTurn() {
doAction() doAction()
if (currentMovement > 0 && hasUnique(Constants.workerUnique) if (currentMovement > 0 &&
&& getTile().improvementInProgress != null // Constants.workerUnique deprecated since 3.15.5
) workOnImprovement() getTile().improvementInProgress != null
if (currentMovement > 0 && hasUnique("Can construct roads") && canBuildImprovement(getTile().getTileImprovementInProgress()!!)
&& currentTile.improvementInProgress == "Road"
) workOnImprovement() ) workOnImprovement()
// unique "Can construct roads" deprecated since 3.15.4
if (currentMovement > 0 && hasUnique("Can construct roads")
&& currentTile.improvementInProgress == "Road"
) workOnImprovement()
//
if (currentMovement == getMaxMovement().toFloat() && isFortified()) { if (currentMovement == getMaxMovement().toFloat() && isFortified()) {
val currentTurnsFortified = getFortificationTurns() val currentTurnsFortified = getFortificationTurns()
if (currentTurnsFortified < 2) if (currentTurnsFortified < 2)
@ -913,5 +923,11 @@ class MapUnit {
} }
} }
fun canBuildImprovement(improvement: TileImprovement, tile: TileInfo = currentTile): Boolean {
// Constants.workerUnique deprecated since 3.15.5
val matchingUniques = getMatchingUniques(Constants.canBuildImprovements) + getMatchingUniques(Constants.workerUnique)
return matchingUniques.any { improvement.matchesFilter(it.params[0]) || tile.matchesTerrainFilter(it.params[0]) }
}
//endregion //endregion
} }

View File

@ -154,6 +154,7 @@ open class TileInfo {
fun isImpassible() = getLastTerrain().impassable fun isImpassible() = getLastTerrain().impassable
fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!] fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!]
fun getTileImprovementInProgress(): TileImprovement? = if (improvementInProgress == null) null else ruleset.tileImprovements[improvementInProgress!!]
// This is for performance - since we access the neighbors of a tile ALL THE TIME, // This is for performance - since we access the neighbors of a tile ALL THE TIME,

View File

@ -26,7 +26,9 @@ enum class UnitActionType(val value: String) {
SetUp("Set up"), SetUp("Set up"),
FoundCity("Found city"), FoundCity("Found city"),
ConstructImprovement("Construct improvement"), ConstructImprovement("Construct improvement"),
ConstructRoad("Construct road"), // Deprecated since 3.15.4
ConstructRoad("Construct road"),
//
Create("Create"), Create("Create"),
HurryResearch("Hurry Research"), HurryResearch("Hurry Research"),
StartGoldenAge("Start Golden Age"), StartGoldenAge("Start Golden Age"),

View File

@ -99,7 +99,8 @@ class TileImprovement : NamedStats() {
return when (filter) { return when (filter) {
name -> true name -> true
"All" -> true "All" -> true
"Great Improvement" -> isGreatImprovement() "All Road" -> name == "road" || name == "railroad"
"Great Improvement", "Great" -> isGreatImprovement()
else -> false else -> false
} }
} }

View File

@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.Constants import com.unciv.Constants
import com.unciv.UncivGame import com.unciv.UncivGame
import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.RoadStatus import com.unciv.logic.map.RoadStatus
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.models.ruleset.tile.TileImprovement import com.unciv.models.ruleset.tile.TileImprovement
@ -17,7 +18,7 @@ import com.unciv.ui.utils.*
import com.unciv.ui.utils.StaticTooltip.Companion.addStaticTip import com.unciv.ui.utils.StaticTooltip.Companion.addStaticTip
import kotlin.math.round import kotlin.math.round
class ImprovementPickerScreen(val tileInfo: TileInfo, val onAccept: ()->Unit) : PickerScreen() { class ImprovementPickerScreen(val tileInfo: TileInfo, unit: MapUnit, val onAccept: ()->Unit) : PickerScreen() {
private var selectedImprovement: TileImprovement? = null private var selectedImprovement: TileImprovement? = null
private val gameInfo = tileInfo.tileMap.gameInfo private val gameInfo = tileInfo.tileMap.gameInfo
private val ruleSet = gameInfo.ruleSet private val ruleSet = gameInfo.ruleSet
@ -56,8 +57,9 @@ class ImprovementPickerScreen(val tileInfo: TileInfo, val onAccept: ()->Unit) :
for (improvement in ruleSet.tileImprovements.values) { for (improvement in ruleSet.tileImprovements.values) {
// canBuildImprovement() would allow e.g. great improvements thus we need to exclude them - except cancel // canBuildImprovement() would allow e.g. great improvements thus we need to exclude them - except cancel
if (improvement.turnsToBuild == 0 && improvement.name != Constants.cancelImprovementOrder) continue if (improvement.turnsToBuild == 0 && improvement.name != Constants.cancelImprovementOrder) continue
if (improvement.name == tileInfo.improvement) continue // also checked by canImprovementBeBuiltHere, but after more expensive tests if (improvement.name == tileInfo.improvement) continue // also checked by canImprovementBeBuiltHere, but after more expensive tests
if (!tileInfo.canBuildImprovement(improvement, currentPlayerCiv)) continue if (!tileInfo.canBuildImprovement(improvement, currentPlayerCiv)) continue
if (!unit.canBuildImprovement(improvement)) continue
val improvementButtonTable = Table() val improvementButtonTable = Table()

View File

@ -478,7 +478,12 @@ class WorldScreen(val gameInfo: GameInfo, val viewingCiv:CivilizationInfo) : Cam
displayTutorial(Tutorial.InjuredUnits) { gameInfo.getCurrentPlayerCivilization().getCivUnits().any { it.health < 100 } } displayTutorial(Tutorial.InjuredUnits) { gameInfo.getCurrentPlayerCivilization().getCivUnits().any { it.health < 100 } }
displayTutorial(Tutorial.Workers) { gameInfo.getCurrentPlayerCivilization().getCivUnits().any { it.hasUnique(Constants.workerUnique) } } displayTutorial(Tutorial.Workers) {
gameInfo.getCurrentPlayerCivilization().getCivUnits().any {
(it.hasUnique(Constants.canBuildImprovements) || it.hasUnique(Constants.workerUnique))
&& it.type.isCivilian()
}
}
} }
private fun updateDiplomacyButton(civInfo: CivilizationInfo) { private fun updateDiplomacyButton(civInfo: CivilizationInfo) {

View File

@ -32,8 +32,8 @@ object UnitActions {
if (unit.isMoving()) actionList += UnitAction(UnitActionType.StopMovement) { unit.action = null } if (unit.isMoving()) actionList += UnitAction(UnitActionType.StopMovement) { unit.action = null }
val workingOnImprovement = unit.hasUnique("Can build improvements on tiles") // Constants.workerUnique deprecated since 3.15.5
&& unit.currentTile.hasImprovementInProgress() val workingOnImprovement = unit.currentTile.hasImprovementInProgress() && unit.canBuildImprovement(unit.currentTile.getTileImprovementInProgress()!!)
if (!unit.isFortified() && !unit.canFortify() && unit.currentMovement > 0 && !workingOnImprovement) { if (!unit.isFortified() && !unit.canFortify() && unit.currentMovement > 0 && !workingOnImprovement) {
addSleepActions(actionList, unit, unitTable) addSleepActions(actionList, unit, unitTable)
} }
@ -58,7 +58,9 @@ object UnitActions {
addSetupAction(unit, actionList) addSetupAction(unit, actionList)
addFoundCityAction(unit, actionList, tile) addFoundCityAction(unit, actionList, tile)
addWorkerActions(unit, actionList, tile, worldScreen, unitTable) addWorkerActions(unit, actionList, tile, worldScreen, unitTable)
addConstructRoadsAction(unit, tile, actionList) // Deprecated since 3.15.4
addConstructRoadsAction(unit, tile, actionList)
//
addCreateWaterImprovements(unit, actionList) addCreateWaterImprovements(unit, actionList)
addGreatPersonActions(unit, actionList, tile) addGreatPersonActions(unit, actionList, tile)
actionList += getImprovementConstructionActions(unit, tile) actionList += getImprovementConstructionActions(unit, tile)
@ -121,20 +123,22 @@ object UnitActions {
return null return null
} }
private fun addConstructRoadsAction(unit: MapUnit, tile: TileInfo, actionList: ArrayList<UnitAction>) { // This entire function is deprecated since 3.15.4, as the 'can construct roads' unique is deprecated
val improvement = RoadStatus.Road.improvement(unit.civInfo.gameInfo.ruleSet) ?: return private fun addConstructRoadsAction(unit: MapUnit, tile: TileInfo, actionList: ArrayList<UnitAction>) {
if (unit.hasUnique("Can construct roads") val improvement = RoadStatus.Road.improvement(unit.civInfo.gameInfo.ruleSet) ?: return
&& tile.roadStatus == RoadStatus.None if (unit.hasUnique("Can construct roads")
&& tile.improvementInProgress != "Road" && tile.roadStatus == RoadStatus.None
&& tile.isLand && tile.improvementInProgress != "Road"
&& (improvement.techRequired == null || unit.civInfo.tech.isResearched(improvement.techRequired!!))) && tile.isLand
actionList += UnitAction(UnitActionType.ConstructRoad, && (improvement.techRequired == null || unit.civInfo.tech.isResearched(improvement.techRequired!!)))
action = { actionList += UnitAction(UnitActionType.ConstructRoad,
tile.improvementInProgress = "Road" action = {
tile.turnsToImprovement = improvement.getTurnsToBuild(unit.civInfo) tile.improvementInProgress = "Road"
}.takeIf { unit.currentMovement > 0 }) tile.turnsToImprovement = improvement.getTurnsToBuild(unit.civInfo)
} }.takeIf { unit.currentMovement > 0 })
}
//
private fun addFoundCityAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) { private fun addFoundCityAction(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo) {
val getFoundCityAction = getFoundCityAction(unit, tile) val getFoundCityAction = getFoundCityAction(unit, tile)
@ -330,7 +334,8 @@ object UnitActions {
} }
private fun addWorkerActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo, worldScreen: WorldScreen, unitTable: UnitTable) { private fun addWorkerActions(unit: MapUnit, actionList: ArrayList<UnitAction>, tile: TileInfo, worldScreen: WorldScreen, unitTable: UnitTable) {
if (!unit.hasUnique("Can build improvements on tiles")) return // Constants.workerUnique deprecated since 3.15.5
if (!unit.hasUnique(Constants.canBuildImprovements) && !unit.hasUnique(Constants.workerUnique)) return
// Allow automate/unautomate when embarked, but not building improvements - see #1963 // Allow automate/unautomate when embarked, but not building improvements - see #1963
if (Constants.unitActionAutomation == unit.action) { if (Constants.unitActionAutomation == unit.action) {
@ -347,12 +352,12 @@ object UnitActions {
val canConstruct = unit.currentMovement > 0 val canConstruct = unit.currentMovement > 0
&& !tile.isCityCenter() && !tile.isCityCenter()
&& unit.civInfo.gameInfo.ruleSet.tileImprovements.values.any { tile.canBuildImprovement(it, unit.civInfo) } && unit.civInfo.gameInfo.ruleSet.tileImprovements.values.any { tile.canBuildImprovement(it, unit.civInfo) && unit.canBuildImprovement(it) }
actionList += UnitAction(UnitActionType.ConstructImprovement, actionList += UnitAction(UnitActionType.ConstructImprovement,
isCurrentAction = unit.currentTile.hasImprovementInProgress(), isCurrentAction = unit.currentTile.hasImprovementInProgress(),
action = { action = {
worldScreen.game.setScreen(ImprovementPickerScreen(tile) { unitTable.selectUnit() }) worldScreen.game.setScreen(ImprovementPickerScreen(tile, unit) { unitTable.selectUnit() })
}.takeIf { canConstruct }) }.takeIf { canConstruct })
} }
@ -361,22 +366,22 @@ object UnitActions {
if (unit.currentMovement > 0) for (unique in unit.getUniques()) when (unique.placeholderText) { if (unit.currentMovement > 0) for (unique in unit.getUniques()) when (unique.placeholderText) {
"Can hurry technology research" -> { "Can hurry technology research" -> {
actionList += UnitAction(UnitActionType.HurryResearch, actionList += UnitAction(UnitActionType.HurryResearch,
uncivSound = UncivSound.Chimes, uncivSound = UncivSound.Chimes,
action = { action = {
unit.civInfo.tech.addScience(unit.civInfo.tech.getScienceFromGreatScientist()) unit.civInfo.tech.addScience(unit.civInfo.tech.getScienceFromGreatScientist())
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy() unit.destroy()
}.takeIf { unit.civInfo.tech.currentTechnologyName() != null }) }.takeIf { unit.civInfo.tech.currentTechnologyName() != null })
} }
"Can start an []-turn golden age" -> { "Can start an []-turn golden age" -> {
val turnsToGoldenAge = unique.params[0].toInt() val turnsToGoldenAge = unique.params[0].toInt()
actionList += UnitAction(UnitActionType.StartGoldenAge, actionList += UnitAction(UnitActionType.StartGoldenAge,
uncivSound = UncivSound.Chimes, uncivSound = UncivSound.Chimes,
action = { action = {
unit.civInfo.goldenAges.enterGoldenAge(turnsToGoldenAge) unit.civInfo.goldenAges.enterGoldenAge(turnsToGoldenAge)
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy() unit.destroy()
}.takeIf { unit.currentTile.getOwner() != null && unit.currentTile.getOwner() == unit.civInfo }) }.takeIf { unit.currentTile.getOwner() != null && unit.currentTile.getOwner() == unit.civInfo })
} }
"Can speed up construction of a wonder" -> { "Can speed up construction of a wonder" -> {
val canHurryWonder = if (!tile.isCityCenter()) false val canHurryWonder = if (!tile.isCityCenter()) false
@ -386,39 +391,38 @@ object UnitActions {
else currentConstruction.isWonder || currentConstruction.isNationalWonder else currentConstruction.isWonder || currentConstruction.isNationalWonder
} }
actionList += UnitAction(UnitActionType.HurryWonder, actionList += UnitAction(UnitActionType.HurryWonder,
uncivSound = UncivSound.Chimes, uncivSound = UncivSound.Chimes,
action = { action = {
tile.getCity()!!.cityConstructions.apply { tile.getCity()!!.cityConstructions.apply {
addProductionPoints(300 + 30 * tile.getCity()!!.population.population) //http://civilization.wikia.com/wiki/Great_engineer_(Civ5) addProductionPoints(300 + 30 * tile.getCity()!!.population.population) //http://civilization.wikia.com/wiki/Great_engineer_(Civ5)
constructIfEnough() constructIfEnough()
} }
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy() unit.destroy()
}.takeIf { canHurryWonder }) }.takeIf { canHurryWonder })
} }
"Can undertake a trade mission with City-State, giving a large sum of gold and [] Influence" -> { "Can undertake a trade mission with City-State, giving a large sum of gold and [] Influence" -> {
val canConductTradeMission = tile.owningCity?.civInfo?.isCityState() == true val canConductTradeMission = tile.owningCity?.civInfo?.isCityState() == true
&& tile.owningCity?.civInfo?.isAtWarWith(unit.civInfo) == false && tile.owningCity?.civInfo?.isAtWarWith(unit.civInfo) == false
val influenceEarned = unique.params[0].toInt() val influenceEarned = unique.params[0].toInt()
actionList += UnitAction(UnitActionType.ConductTradeMission, actionList += UnitAction(UnitActionType.ConductTradeMission,
uncivSound = UncivSound.Chimes, uncivSound = UncivSound.Chimes,
action = { action = {
// http://civilization.wikia.com/wiki/Great_Merchant_(Civ5) // http://civilization.wikia.com/wiki/Great_Merchant_(Civ5)
var goldEarned = ((350 + 50 * unit.civInfo.getEraNumber()) * unit.civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt() var goldEarned = ((350 + 50 * unit.civInfo.getEraNumber()) * unit.civInfo.gameInfo.gameParameters.gameSpeed.modifier).toInt()
if (unit.civInfo.hasUnique("Double gold from Great Merchant trade missions")) if (unit.civInfo.hasUnique("Double gold from Great Merchant trade missions"))
goldEarned *= 2 goldEarned *= 2
unit.civInfo.addGold(goldEarned) unit.civInfo.addGold(goldEarned)
tile.owningCity!!.civInfo.getDiplomacyManager(unit.civInfo).influence += influenceEarned tile.owningCity!!.civInfo.getDiplomacyManager(unit.civInfo).influence += influenceEarned
unit.civInfo.addNotification("Your trade mission to [${tile.owningCity!!.civInfo}] has earned you [${goldEarned}] gold and [$influenceEarned] influence!", unit.civInfo.addNotification("Your trade mission to [${tile.owningCity!!.civInfo}] has earned you [${goldEarned}] gold and [$influenceEarned] influence!",
tile.owningCity!!.civInfo.civName, NotificationIcon.Gold, NotificationIcon.Culture) tile.owningCity!!.civInfo.civName, NotificationIcon.Gold, NotificationIcon.Culture)
addGoldPerGreatPersonUsage(unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
unit.destroy() unit.destroy()
}.takeIf { canConductTradeMission }) }.takeIf { canConductTradeMission })
} }
} }
} }
fun getImprovementConstructionActions(unit: MapUnit, tile: TileInfo): ArrayList<UnitAction> { fun getImprovementConstructionActions(unit: MapUnit, tile: TileInfo): ArrayList<UnitAction> {
val finalActions = ArrayList<UnitAction>() val finalActions = ArrayList<UnitAction>()
for (unique in unit.getMatchingUniques("Can construct []")) { for (unique in unit.getMatchingUniques("Can construct []")) {
@ -426,28 +430,31 @@ object UnitActions {
val improvement = tile.ruleset.tileImprovements[improvementName] val improvement = tile.ruleset.tileImprovements[improvementName]
if (improvement == null) continue if (improvement == null) continue
finalActions += UnitAction(UnitActionType.Create, finalActions += UnitAction(UnitActionType.Create,
title = "Create [$improvementName]", title = "Create [$improvementName]",
uncivSound = UncivSound.Chimes, uncivSound = UncivSound.Chimes,
action = { action = {
val unitTile = unit.getTile() val unitTile = unit.getTile()
for (terrainFeature in tile.terrainFeatures.filter { unitTile.ruleset.tileImprovements.containsKey("Remove $it") }) for (terrainFeature in tile.terrainFeatures.filter { unitTile.ruleset.tileImprovements.containsKey("Remove $it") })
unitTile.terrainFeatures.remove(terrainFeature)// remove forest/jungle/marsh unitTile.terrainFeatures.remove(terrainFeature)// remove forest/jungle/marsh
unitTile.improvement = improvementName unitTile.improvement = improvementName
unitTile.improvementInProgress = null unitTile.improvementInProgress = null
unitTile.turnsToImprovement = 0 unitTile.turnsToImprovement = 0
if (improvementName == Constants.citadel) if (improvementName == Constants.citadel)
takeOverTilesAround(unit) takeOverTilesAround(unit)
val city = unitTile.getCity() val city = unitTile.getCity()
if (city != null) { if (city != null) {
city.cityStats.update() city.cityStats.update()
city.civInfo.updateDetailedCivResources() city.civInfo.updateDetailedCivResources()
} }
addGoldPerGreatPersonUsage(unit.civInfo) // Why is this here? How do we now the unit is actually a great person?
unit.destroy() // What if in some mod some unit can construct a certain type of improvement using the "Can construct []" unique?
}.takeIf { // That unit does not need to be a great person at all, and yet it would trigger mausoleum of halicarnassus (?) here.
unit.currentMovement > 0f && tile.canBuildImprovement(improvement, unit.civInfo) addGoldPerGreatPersonUsage(unit.civInfo)
&& !tile.isImpassible() // Not 100% sure that this check is necessary... unit.destroy()
}) }.takeIf {
unit.currentMovement > 0f && tile.canBuildImprovement(improvement, unit.civInfo)
&& !tile.isImpassible() // Not 100% sure that this check is necessary...
})
} }
return finalActions return finalActions
} }