Improvement queue (#11677)

* Improvement queue: Framework

* Improvement queue: Minimal UI

* Remove debug code

* Fix merge error

* Address tuvus's input

* Implement tuvus's UI wishes

* Fix merge errors

* Fix more merge errors
This commit is contained in:
SomeTroglodyte
2024-06-14 16:39:46 +02:00
committed by GitHub
parent 728713dc3e
commit e74897469c
9 changed files with 193 additions and 106 deletions

View File

@ -1,6 +1,5 @@
package com.unciv.logic.map.mapunit
import com.unciv.UncivGame
import com.unciv.logic.civilization.LocationAction
import com.unciv.logic.civilization.MapUnitAction
import com.unciv.logic.civilization.NotificationCategory
@ -19,7 +18,12 @@ class UnitTurnManager(val unit: MapUnit) {
if (unit.currentMovement > 0
&& unit.getTile().improvementInProgress != null
&& unit.canBuildImprovement(unit.getTile().getTileImprovementInProgress()!!)
) workOnImprovement()
) {
val tile = unit.getTile()
if (tile.doWorkerTurn(unit))
tile.getCity()?.updateCitizens = true
}
if (!unit.hasUnitMovedThisTurn() && unit.isFortified() && unit.turnsFortified < 2) {
unit.turnsFortified++
}
@ -162,23 +166,4 @@ class UnitTurnManager(val unit: MapUnit) {
unit.addMovementMemory()
unit.attacksSinceTurnStart.clear()
}
private fun workOnImprovement() {
val tile = unit.getTile()
if (tile.isMarkedForCreatesOneImprovement()) return
tile.turnsToImprovement -= 1
if (tile.turnsToImprovement != 0) return
if (unit.civ.isCurrentPlayer())
UncivGame.Current.settings.addCompletedTutorialTask("Construct an improvement")
val improvementInProgress = tile.improvementInProgress ?: return
tile.setImprovement(improvementInProgress, unit.civ, unit)
tile.improvementInProgress = null
tile.getCity()?.updateCitizens = true
}
}

View File

@ -1,8 +1,11 @@
package com.unciv.logic.map.tile
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.utils.Json
import com.badlogic.gdx.utils.JsonValue
import com.unciv.Constants
import com.unciv.GUI
import com.unciv.UncivGame
import com.unciv.logic.IsPartOfGameInfoSerialization
import com.unciv.logic.MultiFilter
import com.unciv.logic.city.City
@ -14,6 +17,7 @@ import com.unciv.logic.map.TileMap
import com.unciv.logic.map.mapgenerator.MapGenerator
import com.unciv.logic.map.mapgenerator.MapResourceSetting
import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.logic.map.mapunit.UnitTurnManager
import com.unciv.logic.map.mapunit.movement.UnitMovement
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.tile.ResourceType
@ -27,13 +31,14 @@ import com.unciv.models.ruleset.unique.UniqueMap
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.ui.components.extensions.withItem
import com.unciv.ui.components.extensions.withoutItem
import com.unciv.ui.components.fonts.Fonts
import com.unciv.utils.DebugUtils
import com.unciv.utils.Log
import kotlin.math.abs
import kotlin.math.min
import kotlin.random.Random
class Tile : IsPartOfGameInfoSerialization {
class Tile : IsPartOfGameInfoSerialization, Json.Serializable {
//region Serialized fields
var militaryUnit: MapUnit? = null
var civilianUnit: MapUnit? = null
@ -56,13 +61,27 @@ class Tile : IsPartOfGameInfoSerialization {
var resourceAmount: Int = 0
var improvement: String? = null
var improvementInProgress: String? = null
var improvementIsPillaged = false
internal class ImprovementQueueEntry(
val improvement: String, turnsToImprovement: Int
) : IsPartOfGameInfoSerialization {
@Suppress("unused") // Gdx Json will find this constructor and use it
private constructor() : this("", 0)
var turnsToImprovement: Int = turnsToImprovement
private set
override fun toString() = "$improvement: $turnsToImprovement${Fonts.turn}"
/** @return `true` if it's still counting and not finished */
fun countDown(): Boolean {
turnsToImprovement = (turnsToImprovement - 1).coerceAtLeast(0)
return turnsToImprovement > 0
}
}
private val improvementQueue = ArrayList<ImprovementQueueEntry>(1)
var roadStatus = RoadStatus.None
var roadIsPillaged = false
private var roadOwner: String = "" // either who last built the road or last owner of tile
var turnsToImprovement: Int = 0
var hasBottomRightRiver = false
var hasBottomRiver = false
@ -168,6 +187,10 @@ class Tile : IsPartOfGameInfoSerialization {
private var isAdjacentToRiver = false
@Transient
private var isAdjacentToRiverKnown = false
val improvementInProgress get() = improvementQueue.firstOrNull()?.improvement
val turnsToImprovement get() = improvementQueue.firstOrNull()?.turnsToImprovement ?: 0
//endregion
fun clone(): Tile {
@ -191,12 +214,11 @@ class Tile : IsPartOfGameInfoSerialization {
toReturn.resource = resource
toReturn.resourceAmount = resourceAmount
toReturn.improvement = improvement
toReturn.improvementInProgress = improvementInProgress
toReturn.improvementQueue.addAll(improvementQueue)
toReturn.improvementIsPillaged = improvementIsPillaged
toReturn.roadStatus = roadStatus
toReturn.roadIsPillaged = roadIsPillaged
toReturn.roadOwner = roadOwner
toReturn.turnsToImprovement = turnsToImprovement
toReturn.hasBottomLeftRiver = hasBottomLeftRiver
toReturn.hasBottomRightRiver = hasBottomRightRiver
toReturn.hasBottomRiver = hasBottomRiver
@ -249,11 +271,12 @@ class Tile : IsPartOfGameInfoSerialization {
fun isNaturalWonder(): Boolean = naturalWonder != null
fun isImpassible() = lastTerrain.impassable
fun hasImprovementInProgress() = improvementQueue.isNotEmpty()
fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!]
fun isPillaged(): Boolean = improvementIsPillaged || roadIsPillaged
fun getUnpillagedTileImprovement(): TileImprovement? = if (getUnpillagedImprovement() == null) null else ruleset.tileImprovements[improvement!!]
fun getTileImprovementInProgress(): TileImprovement? = if (improvementInProgress == null) null else ruleset.tileImprovements[improvementInProgress!!]
fun getTileImprovementInProgress(): TileImprovement? = improvementQueue.firstOrNull()?.let { ruleset.tileImprovements[it.improvement] }
fun containsGreatImprovement() = getTileImprovement()?.isGreatImprovement() == true
fun getImprovementToPillage(): TileImprovement? {
@ -494,8 +517,6 @@ class Tile : IsPartOfGameInfoSerialization {
}
}
fun hasImprovementInProgress() = improvementInProgress != null && turnsToImprovement > 0
fun isCoastalTile() = _isCoastalTile
fun hasViewableResource(civInfo: Civilization): Boolean =
@ -822,13 +843,12 @@ class Tile : IsPartOfGameInfoSerialization {
}
}
/** Does not remove roads */
fun removeImprovement() =
improvementFunctions.changeImprovement(null)
improvementFunctions.setImprovement(null)
fun setImprovement(improvementStr: String, civToHandleCompletion: Civilization? = null, unit: MapUnit? = null) =
improvementFunctions.changeImprovement(improvementStr, civToHandleCompletion, unit)
improvementFunctions.setImprovement(improvementStr, civToHandleCompletion, unit)
// function handling when adding a road to the tile
fun addRoad(roadType: RoadStatus, creatingCivInfo: Civilization?) {
@ -852,15 +872,41 @@ class Tile : IsPartOfGameInfoSerialization {
}
fun startWorkingOnImprovement(improvement: TileImprovement, civInfo: Civilization, unit: MapUnit) {
improvementInProgress = improvement.name
turnsToImprovement = if (civInfo.gameInfo.gameParameters.godMode) 1
else improvement.getTurnsToBuild(civInfo, unit)
improvementQueue.clear()
queueImprovement(improvement, civInfo, unit)
}
/** Clears [improvementInProgress] and [turnsToImprovement] */
/** Clears [improvementQueue] */
fun stopWorkingOnImprovement() {
improvementInProgress = null
turnsToImprovement = 0
improvementQueue.clear()
}
/** Adds an entry to the [improvementQueue], by looking up the time it takes using [civInfo] and [unit] */
fun queueImprovement(improvement: TileImprovement, civInfo: Civilization, unit: MapUnit) {
val turns = if (civInfo.gameInfo.gameParameters.godMode) 1
else improvement.getTurnsToBuild(civInfo, unit)
queueImprovement(improvement.name, turns)
}
/** Adds an entry to the [improvementQueue] with explicit [turnsToImprovement] */
fun queueImprovement(improvementName: String, turnsToImprovement: Int) {
improvementQueue.add(ImprovementQueueEntry(improvementName, turnsToImprovement))
}
/** Called from [UnitTurnManager.endTurn] when a Worker "spends time" here
* @return `true` if any work got finished and upstream moght want to update things */
fun doWorkerTurn(worker: MapUnit): Boolean {
if (isMarkedForCreatesOneImprovement()) return false
if (improvementQueue.isEmpty()) return false
if (improvementQueue.first().countDown()) return false
val queueEntry = improvementQueue.removeAt(0)
if (worker.civ.isCurrentPlayer())
UncivGame.Current.settings.addCompletedTutorialTask("Construct an improvement")
setImprovement(queueEntry.improvement, worker.civ, worker)
return true
}
/** Sets tile improvement to pillaged (without prior checks for validity)
@ -878,8 +924,7 @@ class Tile : IsPartOfGameInfoSerialization {
// Setting turnsToImprovement might interfere with UniqueType.CreatesOneImprovement
improvementFunctions.removeCreatesOneImprovementMarker()
improvementInProgress = null // remove any in progress work as well
turnsToImprovement = 0
improvementQueue.clear() // remove any in progress work as well
// if no Repair action, destroy improvements instead
if (ruleset.tileImprovements[Constants.repair] == null) {
if (canPillageTileImprovement())
@ -902,8 +947,7 @@ class Tile : IsPartOfGameInfoSerialization {
}
fun setRepaired() {
improvementInProgress = null
turnsToImprovement = 0
improvementQueue.clear()
if (improvementIsPillaged)
improvementIsPillaged = false
else
@ -1016,5 +1060,23 @@ class Tile : IsPartOfGameInfoSerialization {
return lineList.joinToString()
}
override fun write(json: Json) {
json.writeFields(this)
// Compatibility code for the case an improvementQueue-using game is loaded by an older version: Write fake fields
json.writeValue("improvementInProgress", improvementInProgress, String::class.java)
json.writeValue("turnsToImprovement", turnsToImprovement, Int::class.java)
}
override fun read(json: Json, jsonData: JsonValue) {
json.readFields(this, jsonData)
// Compatibility code for the case an pre-improvementQueue game is loaded by this version: Read legacy fields
if (improvementQueue.isEmpty() && jsonData.get("improvementQueue") == null) {
val improvementInProgress = jsonData.getString("improvementInProgress", "")
val turnsToImprovement = jsonData.getInt("turnsToImprovement", 0)
if (improvementInProgress.isNotEmpty() && turnsToImprovement != 0)
improvementQueue.add(ImprovementQueueEntry(improvementInProgress, turnsToImprovement))
}
}
//endregion
}

View File

@ -195,7 +195,7 @@ class TileImprovementFunctions(val tile: Tile) {
}
fun changeImprovement(improvementName: String?,
fun setImprovement(improvementName: String?,
/** For road assignment and taking over tiles - DO NOT pass when simulating improvement effects! */
civToActivateBroaderEffects: Civilization? = null, unit: MapUnit? = null) {
val improvementObject = tile.ruleset.tileImprovements[improvementName]
@ -307,8 +307,7 @@ class TileImprovementFunctions(val tile: Tile) {
private fun tryProvideProductionToClosestCity(removedTerrainFeature: String, civ: Civilization) {
val closestCity = civ.cities.minByOrNull { it.getCenterTile().aerialDistanceTo(tile) }
@Suppress("FoldInitializerAndIfToElvis")
if (closestCity == null) return
?: return
val distance = closestCity.getCenterTile().aerialDistanceTo(tile)
var productionPointsToAdd = if (distance == 1) 20 else 20 - (distance - 2) * 5
if (tile.owningCity == null || tile.owningCity!!.civ != civ) productionPointsToAdd =
@ -372,8 +371,8 @@ class TileImprovementFunctions(val tile: Tile) {
/** Marks tile as target tile for a building with a [UniqueType.CreatesOneImprovement] unique */
fun markForCreatesOneImprovement(improvement: String) {
tile.improvementInProgress = improvement
tile.turnsToImprovement = -1
tile.stopWorkingOnImprovement()
tile.queueImprovement(improvement, -1)
}
/** Un-Marks a tile as target tile for a building with a [UniqueType.CreatesOneImprovement] unique,
@ -383,6 +382,4 @@ class TileImprovementFunctions(val tile: Tile) {
tile.owningCity?.cityConstructions?.removeCreateOneImprovementConstruction(tile.improvementInProgress!!)
tile.stopWorkingOnImprovement()
}
}

View File

@ -55,7 +55,6 @@ object TileNormalizer {
private fun Tile.clearImprovement() {
// This runs from mapgen, so don't go through the side-effect-triggering TileImprovementFunctions
improvement = null
improvementInProgress = null
turnsToImprovement = 0
stopWorkingOnImprovement()
}
}

View File

@ -48,6 +48,7 @@ private class RestorableTextButtonStyle(
val restoreStyle: ButtonStyle
) : TextButtonStyle(baseStyle)
//todo ButtonStyle *does* have a `disabled` Drawable, and Button ignores touches in disabled state anyway - all this is a wrong approach
/** Disable a [Button] by setting its [touchable][Button.touchable] and [style][Button.style] properties. */
fun Button.disable() {
touchable = Touchable.disabled

View File

@ -28,14 +28,14 @@ internal class ConsoleTileCommands: ConsoleCommandNode {
val selectedTile = console.getSelectedTile()
val improvement = params[0].find(console.gameInfo.ruleset.tileImprovements.values)
val civ = params.getOrNull(1)?.let { console.getCivByName(it) }
selectedTile.improvementFunctions.changeImprovement(improvement.name, civ)
selectedTile.improvementFunctions.setImprovement(improvement.name, civ)
selectedTile.getCity()?.reassignPopulation()
DevConsoleResponse.OK
},
"removeimprovement" to ConsoleAction("tile removeimprovement") { console, _ ->
val selectedTile = console.getSelectedTile()
selectedTile.improvementFunctions.changeImprovement(null)
selectedTile.improvementFunctions.setImprovement(null)
selectedTile.getCity()?.reassignPopulation()
DevConsoleResponse.OK
},

View File

@ -1,6 +1,7 @@
package com.unciv.ui.screens.pickerscreens
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Actor
import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align
import com.unciv.Constants
@ -12,6 +13,7 @@ import com.unciv.models.ruleset.unique.LocalUniqueCache
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.stats.Stats
import com.unciv.models.translations.tr
import com.unciv.ui.components.SmallButtonStyle
import com.unciv.ui.components.UncivTooltip.Companion.addTooltip
import com.unciv.ui.components.extensions.disable
import com.unciv.ui.components.extensions.toLabel
@ -39,22 +41,26 @@ class ImprovementPickerScreen(
private var selectedImprovement: TileImprovement? = null
private val gameInfo = tile.tileMap.gameInfo
private val ruleSet = gameInfo.ruleset
private val ruleset = gameInfo.ruleset
private val currentPlayerCiv = gameInfo.getCurrentPlayerCivilization()
// Support for UniqueType.CreatesOneImprovement
private val tileMarkedForCreatesOneImprovement = tile.isMarkedForCreatesOneImprovement()
private val tileWithoutLastTerrain: Tile
private fun getRequiredTechColumn(improvement: TileImprovement) =
ruleSet.technologies[improvement.techRequired]?.column?.columnNumber ?: -1
ruleset.technologies[improvement.techRequired]?.column?.columnNumber ?: -1
fun accept(improvement: TileImprovement?) {
fun accept(improvement: TileImprovement?, secondImprovement: TileImprovement? = null) {
if (improvement == null || tileMarkedForCreatesOneImprovement) return
if (improvement.name == Constants.cancelImprovementOrder) {
tile.stopWorkingOnImprovement()
// no onAccept() - Worker can stay selected
} else {
if (improvement.name != tile.improvementInProgress)
if (improvement.name != tile.improvementInProgress) {
tile.startWorkingOnImprovement(improvement, currentPlayerCiv, unit)
if (secondImprovement != null)
tile.queueImprovement(secondImprovement, currentPlayerCiv, unit)
}
unit.action = null // this is to "wake up" the worker if it's sleeping
onAccept()
}
@ -74,28 +80,20 @@ class ImprovementPickerScreen(
// clone tileInfo without "top" feature if it could be removed
// Keep this copy around for speed
val tileWithoutLastTerrain: Tile = tile.clone()
tileWithoutLastTerrain = tile.clone()
tileWithoutLastTerrain.setTerrainTransients()
if (Constants.remove + tileWithoutLastTerrain.lastTerrain.name in ruleSet.tileImprovements) {
if (Constants.remove + tileWithoutLastTerrain.lastTerrain.name in ruleset.tileImprovements) {
tileWithoutLastTerrain.removeTerrainFeature(tileWithoutLastTerrain.lastTerrain.name)
}
val cityUniqueCache = LocalUniqueCache()
for (improvement in ruleSet.tileImprovements.values) {
var suggestRemoval = false
for (improvement in ruleset.tileImprovements.values) {
// canBuildImprovement() would allow e.g. great improvements thus we need to exclude them - except cancel
if (improvement.turnsToBuild == -1 && improvement.name != Constants.cancelImprovementOrder) continue
if (improvement.name == tile.improvement) continue // also checked by canImprovementBeBuiltHere, but after more expensive tests
if (!unit.canBuildImprovement(improvement)) continue
var unbuildableBecause = tile.improvementFunctions.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
if (!canReport(unbuildableBecause)) {
// Try after pretending to have removed the top terrain layer.
unbuildableBecause = tileWithoutLastTerrain.improvementFunctions.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
if (!canReport(unbuildableBecause)) continue
else suggestRemoval = true
}
val problemReport = getProblemReport(improvement) ?: continue
val image = ImageGetter.getImprovementPortrait(improvement.name, 30f)
@ -103,7 +101,7 @@ class ImprovementPickerScreen(
var shortcutKey = improvement.shortcutKey
if (shortcutKey != null) {
val techLevel = getRequiredTechColumn(improvement)
val isSuperseded = ruleSet.tileImprovements.values.asSequence()
val isSuperseded = ruleset.tileImprovements.values.asSequence()
// *other* improvements with same shortcutKey
.filter { it.shortcutKey == improvement.shortcutKey && it != improvement }
// civ can build it (checks tech researched)
@ -127,29 +125,6 @@ class ImprovementPickerScreen(
&& improvement.name != Constants.cancelImprovementOrder)
if (tile.improvement != null && removeImprovement) labelText += "\n" + "Replaces [${tile.improvement}]".tr()
val proposedSolutions = mutableListOf<String>()
if (suggestRemoval)
proposedSolutions.add("${Constants.remove}[${tile.lastTerrain.name}] first")
if (ImprovementBuildingProblem.MissingTech in unbuildableBecause)
proposedSolutions.add("Research [${improvement.techRequired}] first")
if (ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause)
proposedSolutions.add("Have this tile close to your borders")
if (ImprovementBuildingProblem.OutsideBorders in unbuildableBecause)
proposedSolutions.add("Have this tile inside your empire")
if (ImprovementBuildingProblem.MissingResources in unbuildableBecause) {
proposedSolutions.addAll(improvement.getMatchingUniques(UniqueType.ConsumesResources).filter {
currentPlayerCiv.getResourceAmount(it.params[1]) < it.params[0].toInt()
}.map { "Acquire more [$it]" })
}
val explanationText = when {
proposedSolutions.any() -> proposedSolutions.joinToString("}\n{", "{", "}").toLabel()
tile.improvementInProgress == improvement.name -> "Current construction".toLabel()
tileMarkedForCreatesOneImprovement -> null
else -> "Pick now!".toLabel().onClick { accept(improvement) }
}
val statIcons = getStatIconsTable(provideResource, removeImprovement)
// get benefits of the new improvement
@ -182,13 +157,20 @@ class ImprovementPickerScreen(
improvementButton.onActivation(type = ActivationTypes.Tap, noEquivalence = true) {
selectedImprovement = improvement
pick(improvement.name.tr())
descriptionLabel.setText(improvement.getDescription(ruleSet))
descriptionLabel.setText(improvement.getDescription(ruleset))
}
improvementButton.onDoubleClick { accept(improvement) }
if (improvement.name == tile.improvementInProgress) improvementButton.color = Color.GREEN
if (proposedSolutions.isNotEmpty() || tileMarkedForCreatesOneImprovement) {
when {
improvement.name == tile.improvementInProgress ->
improvementButton.color = Color.GREEN
problemReport.isQueueable() ->
// TODO should be a skin ButtonStyle, this mixes with the style override from disable() below - which is a very wrong approach anyway
improvementButton.setColor(0.625f, 1f, 0.625f, 1f) // #a0ffa0 - brightened GREEN
}
if (!problemReport.isEmpty() || tileMarkedForCreatesOneImprovement) {
improvementButton.disable()
} else if (shortcutKey != null) {
// Shortcut keys trigger what onDoubleClick does, not equivalent to single Click:
@ -197,7 +179,7 @@ class ImprovementPickerScreen(
}
regularImprovements.add(improvementButton)
regularImprovements.add(explanationText).padLeft(10f).fillY()
regularImprovements.add(getExplanationActor(improvement, problemReport)).padLeft(10f)
regularImprovements.row()
}
@ -251,4 +233,66 @@ class ImprovementPickerScreen(
}
return statsTable
}
private class ProblemReport {
var suggestRemoval = false
var removalImprovement: TileImprovement? = null
val proposedSolutions = mutableListOf<String>()
fun isEmpty() = proposedSolutions.isEmpty()
fun isQueueable() = removalImprovement != null && proposedSolutions.size == 1
fun toLabel() = proposedSolutions.joinToString("}\n{", "{", "}").toLabel()
}
private fun getProblemReport(improvement: TileImprovement): ProblemReport? {
val report = ProblemReport()
var unbuildableBecause = tile.improvementFunctions.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
if (!canReport(unbuildableBecause)) {
// Try after pretending to have removed the top terrain layer.
unbuildableBecause = tileWithoutLastTerrain.improvementFunctions.getImprovementBuildingProblems(improvement, currentPlayerCiv).toSet()
if (!canReport(unbuildableBecause)) return null
report.suggestRemoval = true
}
with(report) {
if (suggestRemoval) {
val removalName = Constants.remove + tile.lastTerrain.name
removalImprovement = ruleset.tileImprovements[removalName]
if (removalImprovement != null)
proposedSolutions.add("${Constants.remove}[${tile.lastTerrain.name}] first")
}
if (ImprovementBuildingProblem.MissingTech in unbuildableBecause)
proposedSolutions.add("Research [${improvement.techRequired}] first")
if (ImprovementBuildingProblem.NotJustOutsideBorders in unbuildableBecause)
proposedSolutions.add("Have this tile close to your borders")
if (ImprovementBuildingProblem.OutsideBorders in unbuildableBecause)
proposedSolutions.add("Have this tile inside your empire")
if (ImprovementBuildingProblem.MissingResources in unbuildableBecause) {
proposedSolutions.addAll(improvement.getMatchingUniques(UniqueType.ConsumesResources).filter {
currentPlayerCiv.getResourceAmount(it.params[1]) < it.params[0].toInt()
}.map { "Acquire more [$it]" })
}
}
return report
}
private fun getExplanationActor(improvement: TileImprovement, report: ProblemReport): Actor? {
fun getPickNowButton(action: ()->Unit) = "Pick now!".toTextButton(SmallButtonStyle()).onClick(action)
// Formerly: "Pick now!".toLabel().onClick(action), with later fillY() to make it easier to hit (#3989)
if (report.isEmpty()) return when {
tile.improvementInProgress == improvement.name -> "Current construction".toLabel()
tileMarkedForCreatesOneImprovement -> null
else -> getPickNowButton { accept(improvement) }
}
val label = report.toLabel()
if (!report.isQueueable()) return label
return Table().apply {
defaults().center()
add(label).padBottom(5f).row()
add(getPickNowButton { accept(report.removalImprovement, improvement) })
}
}
}

View File

@ -456,8 +456,7 @@ object UnitActionsFromUniques {
return UnitAction(UnitActionType.Repair, 90f,
title = "${UnitActionType.Repair} [${unit.currentTile.getImprovementToRepair()!!.name}] - [${turnsToBuild}${Fonts.turn}]",
action = {
tile.turnsToImprovement = getRepairTurns(unit)
tile.improvementInProgress = Constants.repair
tile.queueImprovement(Constants.repair, turnsToBuild)
}.takeIf { couldConstruct }
)
}

View File

@ -177,7 +177,7 @@ class TileImprovementConstructionTests {
@Test
fun buildingRoadBuildsARoad() {
val tile = tileMap[1,1]
tile.improvementFunctions.changeImprovement("Road")
tile.improvementFunctions.setImprovement("Road")
assert(tile.roadStatus == RoadStatus.Road)
}
@ -185,7 +185,7 @@ class TileImprovementConstructionTests {
fun removingRoadRemovesRoad() {
val tile = tileMap[1,1]
tile.roadStatus = RoadStatus.Road
tile.improvementFunctions.changeImprovement("Remove Road")
tile.improvementFunctions.setImprovement("Remove Road")
assert(tile.roadStatus == RoadStatus.None)
}
@ -193,9 +193,9 @@ class TileImprovementConstructionTests {
fun removingForestRemovesForestAndLumbermill() {
val tile = tileMap[1,1]
tile.addTerrainFeature("Forest")
tile.improvementFunctions.changeImprovement("Lumber mill")
tile.improvementFunctions.setImprovement("Lumber mill")
assert(tile.getTileImprovement()!!.name == "Lumber mill")
tile.improvementFunctions.changeImprovement("Remove Forest")
tile.improvementFunctions.setImprovement("Remove Forest")
assert(tile.terrainFeatures.isEmpty())
assert(tile.improvement == null) // Lumber mill can ONLY be on Forest, and is therefore removed
}
@ -206,9 +206,9 @@ class TileImprovementConstructionTests {
tile.addTerrainFeature("Forest")
tile.resource = "Deer"
tile.baseTerrain = "Plains"
tile.improvementFunctions.changeImprovement("Camp")
tile.improvementFunctions.setImprovement("Camp")
assert(tile.getTileImprovement()!!.name == "Camp")
tile.improvementFunctions.changeImprovement("Remove Forest")
tile.improvementFunctions.setImprovement("Remove Forest")
assert(tile.terrainFeatures.isEmpty())
assert(tile.improvement == "Camp") // Camp can be both on Forest AND on Plains, so not removed
}
@ -253,7 +253,7 @@ class TileImprovementConstructionTests {
tile.addTerrainFeature("Forest")
val lumberMill = testGame.ruleset.tileImprovements["Lumber mill"]!!
tile.improvementFunctions.changeImprovement(lumberMill.name)
tile.improvementFunctions.setImprovement(lumberMill.name)
assert(tile.getTileImprovement() == lumberMill)
// 1f 1p from forest, 2p from lumber mill since all techs are researched