mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-04 23:40:01 +07:00
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:
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
},
|
||||
|
@ -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) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 }
|
||||
)
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user