mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-06 00:09:23 +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
|
package com.unciv.logic.map.mapunit
|
||||||
|
|
||||||
import com.unciv.UncivGame
|
|
||||||
import com.unciv.logic.civilization.LocationAction
|
import com.unciv.logic.civilization.LocationAction
|
||||||
import com.unciv.logic.civilization.MapUnitAction
|
import com.unciv.logic.civilization.MapUnitAction
|
||||||
import com.unciv.logic.civilization.NotificationCategory
|
import com.unciv.logic.civilization.NotificationCategory
|
||||||
@ -19,7 +18,12 @@ class UnitTurnManager(val unit: MapUnit) {
|
|||||||
if (unit.currentMovement > 0
|
if (unit.currentMovement > 0
|
||||||
&& unit.getTile().improvementInProgress != null
|
&& unit.getTile().improvementInProgress != null
|
||||||
&& unit.canBuildImprovement(unit.getTile().getTileImprovementInProgress()!!)
|
&& 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) {
|
if (!unit.hasUnitMovedThisTurn() && unit.isFortified() && unit.turnsFortified < 2) {
|
||||||
unit.turnsFortified++
|
unit.turnsFortified++
|
||||||
}
|
}
|
||||||
@ -162,23 +166,4 @@ class UnitTurnManager(val unit: MapUnit) {
|
|||||||
unit.addMovementMemory()
|
unit.addMovementMemory()
|
||||||
unit.attacksSinceTurnStart.clear()
|
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
|
package com.unciv.logic.map.tile
|
||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2
|
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.Constants
|
||||||
import com.unciv.GUI
|
import com.unciv.GUI
|
||||||
|
import com.unciv.UncivGame
|
||||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||||
import com.unciv.logic.MultiFilter
|
import com.unciv.logic.MultiFilter
|
||||||
import com.unciv.logic.city.City
|
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.MapGenerator
|
||||||
import com.unciv.logic.map.mapgenerator.MapResourceSetting
|
import com.unciv.logic.map.mapgenerator.MapResourceSetting
|
||||||
import com.unciv.logic.map.mapunit.MapUnit
|
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.logic.map.mapunit.movement.UnitMovement
|
||||||
import com.unciv.models.ruleset.Ruleset
|
import com.unciv.models.ruleset.Ruleset
|
||||||
import com.unciv.models.ruleset.tile.ResourceType
|
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.models.ruleset.unique.UniqueType
|
||||||
import com.unciv.ui.components.extensions.withItem
|
import com.unciv.ui.components.extensions.withItem
|
||||||
import com.unciv.ui.components.extensions.withoutItem
|
import com.unciv.ui.components.extensions.withoutItem
|
||||||
|
import com.unciv.ui.components.fonts.Fonts
|
||||||
import com.unciv.utils.DebugUtils
|
import com.unciv.utils.DebugUtils
|
||||||
import com.unciv.utils.Log
|
import com.unciv.utils.Log
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
class Tile : IsPartOfGameInfoSerialization {
|
class Tile : IsPartOfGameInfoSerialization, Json.Serializable {
|
||||||
//region Serialized fields
|
//region Serialized fields
|
||||||
var militaryUnit: MapUnit? = null
|
var militaryUnit: MapUnit? = null
|
||||||
var civilianUnit: MapUnit? = null
|
var civilianUnit: MapUnit? = null
|
||||||
@ -56,13 +61,27 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
var resourceAmount: Int = 0
|
var resourceAmount: Int = 0
|
||||||
|
|
||||||
var improvement: String? = null
|
var improvement: String? = null
|
||||||
var improvementInProgress: String? = null
|
|
||||||
var improvementIsPillaged = false
|
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 roadStatus = RoadStatus.None
|
||||||
var roadIsPillaged = false
|
var roadIsPillaged = false
|
||||||
private var roadOwner: String = "" // either who last built the road or last owner of tile
|
private var roadOwner: String = "" // either who last built the road or last owner of tile
|
||||||
var turnsToImprovement: Int = 0
|
|
||||||
|
|
||||||
var hasBottomRightRiver = false
|
var hasBottomRightRiver = false
|
||||||
var hasBottomRiver = false
|
var hasBottomRiver = false
|
||||||
@ -168,6 +187,10 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
private var isAdjacentToRiver = false
|
private var isAdjacentToRiver = false
|
||||||
@Transient
|
@Transient
|
||||||
private var isAdjacentToRiverKnown = false
|
private var isAdjacentToRiverKnown = false
|
||||||
|
|
||||||
|
val improvementInProgress get() = improvementQueue.firstOrNull()?.improvement
|
||||||
|
val turnsToImprovement get() = improvementQueue.firstOrNull()?.turnsToImprovement ?: 0
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
fun clone(): Tile {
|
fun clone(): Tile {
|
||||||
@ -191,12 +214,11 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
toReturn.resource = resource
|
toReturn.resource = resource
|
||||||
toReturn.resourceAmount = resourceAmount
|
toReturn.resourceAmount = resourceAmount
|
||||||
toReturn.improvement = improvement
|
toReturn.improvement = improvement
|
||||||
toReturn.improvementInProgress = improvementInProgress
|
toReturn.improvementQueue.addAll(improvementQueue)
|
||||||
toReturn.improvementIsPillaged = improvementIsPillaged
|
toReturn.improvementIsPillaged = improvementIsPillaged
|
||||||
toReturn.roadStatus = roadStatus
|
toReturn.roadStatus = roadStatus
|
||||||
toReturn.roadIsPillaged = roadIsPillaged
|
toReturn.roadIsPillaged = roadIsPillaged
|
||||||
toReturn.roadOwner = roadOwner
|
toReturn.roadOwner = roadOwner
|
||||||
toReturn.turnsToImprovement = turnsToImprovement
|
|
||||||
toReturn.hasBottomLeftRiver = hasBottomLeftRiver
|
toReturn.hasBottomLeftRiver = hasBottomLeftRiver
|
||||||
toReturn.hasBottomRightRiver = hasBottomRightRiver
|
toReturn.hasBottomRightRiver = hasBottomRightRiver
|
||||||
toReturn.hasBottomRiver = hasBottomRiver
|
toReturn.hasBottomRiver = hasBottomRiver
|
||||||
@ -249,11 +271,12 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
fun isNaturalWonder(): Boolean = naturalWonder != null
|
fun isNaturalWonder(): Boolean = naturalWonder != null
|
||||||
fun isImpassible() = lastTerrain.impassable
|
fun isImpassible() = lastTerrain.impassable
|
||||||
|
|
||||||
|
fun hasImprovementInProgress() = improvementQueue.isNotEmpty()
|
||||||
|
|
||||||
fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!]
|
fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!]
|
||||||
fun isPillaged(): Boolean = improvementIsPillaged || roadIsPillaged
|
fun isPillaged(): Boolean = improvementIsPillaged || roadIsPillaged
|
||||||
fun getUnpillagedTileImprovement(): TileImprovement? = if (getUnpillagedImprovement() == null) null else ruleset.tileImprovements[improvement!!]
|
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 containsGreatImprovement() = getTileImprovement()?.isGreatImprovement() == true
|
||||||
|
|
||||||
fun getImprovementToPillage(): TileImprovement? {
|
fun getImprovementToPillage(): TileImprovement? {
|
||||||
@ -494,8 +517,6 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasImprovementInProgress() = improvementInProgress != null && turnsToImprovement > 0
|
|
||||||
|
|
||||||
fun isCoastalTile() = _isCoastalTile
|
fun isCoastalTile() = _isCoastalTile
|
||||||
|
|
||||||
fun hasViewableResource(civInfo: Civilization): Boolean =
|
fun hasViewableResource(civInfo: Civilization): Boolean =
|
||||||
@ -822,13 +843,12 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Does not remove roads */
|
/** Does not remove roads */
|
||||||
fun removeImprovement() =
|
fun removeImprovement() =
|
||||||
improvementFunctions.changeImprovement(null)
|
improvementFunctions.setImprovement(null)
|
||||||
|
|
||||||
fun setImprovement(improvementStr: String, civToHandleCompletion: Civilization? = null, unit: MapUnit? = 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
|
// function handling when adding a road to the tile
|
||||||
fun addRoad(roadType: RoadStatus, creatingCivInfo: Civilization?) {
|
fun addRoad(roadType: RoadStatus, creatingCivInfo: Civilization?) {
|
||||||
@ -852,15 +872,41 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun startWorkingOnImprovement(improvement: TileImprovement, civInfo: Civilization, unit: MapUnit) {
|
fun startWorkingOnImprovement(improvement: TileImprovement, civInfo: Civilization, unit: MapUnit) {
|
||||||
improvementInProgress = improvement.name
|
improvementQueue.clear()
|
||||||
turnsToImprovement = if (civInfo.gameInfo.gameParameters.godMode) 1
|
queueImprovement(improvement, civInfo, unit)
|
||||||
else improvement.getTurnsToBuild(civInfo, unit)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears [improvementInProgress] and [turnsToImprovement] */
|
/** Clears [improvementQueue] */
|
||||||
fun stopWorkingOnImprovement() {
|
fun stopWorkingOnImprovement() {
|
||||||
improvementInProgress = null
|
improvementQueue.clear()
|
||||||
turnsToImprovement = 0
|
}
|
||||||
|
|
||||||
|
/** 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)
|
/** Sets tile improvement to pillaged (without prior checks for validity)
|
||||||
@ -878,8 +924,7 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
|
|
||||||
// Setting turnsToImprovement might interfere with UniqueType.CreatesOneImprovement
|
// Setting turnsToImprovement might interfere with UniqueType.CreatesOneImprovement
|
||||||
improvementFunctions.removeCreatesOneImprovementMarker()
|
improvementFunctions.removeCreatesOneImprovementMarker()
|
||||||
improvementInProgress = null // remove any in progress work as well
|
improvementQueue.clear() // remove any in progress work as well
|
||||||
turnsToImprovement = 0
|
|
||||||
// if no Repair action, destroy improvements instead
|
// if no Repair action, destroy improvements instead
|
||||||
if (ruleset.tileImprovements[Constants.repair] == null) {
|
if (ruleset.tileImprovements[Constants.repair] == null) {
|
||||||
if (canPillageTileImprovement())
|
if (canPillageTileImprovement())
|
||||||
@ -902,8 +947,7 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setRepaired() {
|
fun setRepaired() {
|
||||||
improvementInProgress = null
|
improvementQueue.clear()
|
||||||
turnsToImprovement = 0
|
|
||||||
if (improvementIsPillaged)
|
if (improvementIsPillaged)
|
||||||
improvementIsPillaged = false
|
improvementIsPillaged = false
|
||||||
else
|
else
|
||||||
@ -1016,5 +1060,23 @@ class Tile : IsPartOfGameInfoSerialization {
|
|||||||
return lineList.joinToString()
|
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
|
//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! */
|
/** For road assignment and taking over tiles - DO NOT pass when simulating improvement effects! */
|
||||||
civToActivateBroaderEffects: Civilization? = null, unit: MapUnit? = null) {
|
civToActivateBroaderEffects: Civilization? = null, unit: MapUnit? = null) {
|
||||||
val improvementObject = tile.ruleset.tileImprovements[improvementName]
|
val improvementObject = tile.ruleset.tileImprovements[improvementName]
|
||||||
@ -307,8 +307,7 @@ class TileImprovementFunctions(val tile: Tile) {
|
|||||||
|
|
||||||
private fun tryProvideProductionToClosestCity(removedTerrainFeature: String, civ: Civilization) {
|
private fun tryProvideProductionToClosestCity(removedTerrainFeature: String, civ: Civilization) {
|
||||||
val closestCity = civ.cities.minByOrNull { it.getCenterTile().aerialDistanceTo(tile) }
|
val closestCity = civ.cities.minByOrNull { it.getCenterTile().aerialDistanceTo(tile) }
|
||||||
@Suppress("FoldInitializerAndIfToElvis")
|
?: return
|
||||||
if (closestCity == null) return
|
|
||||||
val distance = closestCity.getCenterTile().aerialDistanceTo(tile)
|
val distance = closestCity.getCenterTile().aerialDistanceTo(tile)
|
||||||
var productionPointsToAdd = if (distance == 1) 20 else 20 - (distance - 2) * 5
|
var productionPointsToAdd = if (distance == 1) 20 else 20 - (distance - 2) * 5
|
||||||
if (tile.owningCity == null || tile.owningCity!!.civ != civ) productionPointsToAdd =
|
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 */
|
/** Marks tile as target tile for a building with a [UniqueType.CreatesOneImprovement] unique */
|
||||||
fun markForCreatesOneImprovement(improvement: String) {
|
fun markForCreatesOneImprovement(improvement: String) {
|
||||||
tile.improvementInProgress = improvement
|
tile.stopWorkingOnImprovement()
|
||||||
tile.turnsToImprovement = -1
|
tile.queueImprovement(improvement, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Un-Marks a tile as target tile for a building with a [UniqueType.CreatesOneImprovement] unique,
|
/** 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.owningCity?.cityConstructions?.removeCreateOneImprovementConstruction(tile.improvementInProgress!!)
|
||||||
tile.stopWorkingOnImprovement()
|
tile.stopWorkingOnImprovement()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ object TileNormalizer {
|
|||||||
private fun Tile.clearImprovement() {
|
private fun Tile.clearImprovement() {
|
||||||
// This runs from mapgen, so don't go through the side-effect-triggering TileImprovementFunctions
|
// This runs from mapgen, so don't go through the side-effect-triggering TileImprovementFunctions
|
||||||
improvement = null
|
improvement = null
|
||||||
improvementInProgress = null
|
stopWorkingOnImprovement()
|
||||||
turnsToImprovement = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ private class RestorableTextButtonStyle(
|
|||||||
val restoreStyle: ButtonStyle
|
val restoreStyle: ButtonStyle
|
||||||
) : TextButtonStyle(baseStyle)
|
) : 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. */
|
/** Disable a [Button] by setting its [touchable][Button.touchable] and [style][Button.style] properties. */
|
||||||
fun Button.disable() {
|
fun Button.disable() {
|
||||||
touchable = Touchable.disabled
|
touchable = Touchable.disabled
|
||||||
|
@ -28,14 +28,14 @@ internal class ConsoleTileCommands: ConsoleCommandNode {
|
|||||||
val selectedTile = console.getSelectedTile()
|
val selectedTile = console.getSelectedTile()
|
||||||
val improvement = params[0].find(console.gameInfo.ruleset.tileImprovements.values)
|
val improvement = params[0].find(console.gameInfo.ruleset.tileImprovements.values)
|
||||||
val civ = params.getOrNull(1)?.let { console.getCivByName(it) }
|
val civ = params.getOrNull(1)?.let { console.getCivByName(it) }
|
||||||
selectedTile.improvementFunctions.changeImprovement(improvement.name, civ)
|
selectedTile.improvementFunctions.setImprovement(improvement.name, civ)
|
||||||
selectedTile.getCity()?.reassignPopulation()
|
selectedTile.getCity()?.reassignPopulation()
|
||||||
DevConsoleResponse.OK
|
DevConsoleResponse.OK
|
||||||
},
|
},
|
||||||
|
|
||||||
"removeimprovement" to ConsoleAction("tile removeimprovement") { console, _ ->
|
"removeimprovement" to ConsoleAction("tile removeimprovement") { console, _ ->
|
||||||
val selectedTile = console.getSelectedTile()
|
val selectedTile = console.getSelectedTile()
|
||||||
selectedTile.improvementFunctions.changeImprovement(null)
|
selectedTile.improvementFunctions.setImprovement(null)
|
||||||
selectedTile.getCity()?.reassignPopulation()
|
selectedTile.getCity()?.reassignPopulation()
|
||||||
DevConsoleResponse.OK
|
DevConsoleResponse.OK
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.unciv.ui.screens.pickerscreens
|
package com.unciv.ui.screens.pickerscreens
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color
|
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.scenes.scene2d.ui.Table
|
||||||
import com.badlogic.gdx.utils.Align
|
import com.badlogic.gdx.utils.Align
|
||||||
import com.unciv.Constants
|
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.ruleset.unique.UniqueType
|
||||||
import com.unciv.models.stats.Stats
|
import com.unciv.models.stats.Stats
|
||||||
import com.unciv.models.translations.tr
|
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.UncivTooltip.Companion.addTooltip
|
||||||
import com.unciv.ui.components.extensions.disable
|
import com.unciv.ui.components.extensions.disable
|
||||||
import com.unciv.ui.components.extensions.toLabel
|
import com.unciv.ui.components.extensions.toLabel
|
||||||
@ -39,22 +41,26 @@ class ImprovementPickerScreen(
|
|||||||
|
|
||||||
private var selectedImprovement: TileImprovement? = null
|
private var selectedImprovement: TileImprovement? = null
|
||||||
private val gameInfo = tile.tileMap.gameInfo
|
private val gameInfo = tile.tileMap.gameInfo
|
||||||
private val ruleSet = gameInfo.ruleset
|
private val ruleset = gameInfo.ruleset
|
||||||
private val currentPlayerCiv = gameInfo.getCurrentPlayerCivilization()
|
private val currentPlayerCiv = gameInfo.getCurrentPlayerCivilization()
|
||||||
// Support for UniqueType.CreatesOneImprovement
|
// Support for UniqueType.CreatesOneImprovement
|
||||||
private val tileMarkedForCreatesOneImprovement = tile.isMarkedForCreatesOneImprovement()
|
private val tileMarkedForCreatesOneImprovement = tile.isMarkedForCreatesOneImprovement()
|
||||||
|
private val tileWithoutLastTerrain: Tile
|
||||||
|
|
||||||
private fun getRequiredTechColumn(improvement: TileImprovement) =
|
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 == null || tileMarkedForCreatesOneImprovement) return
|
||||||
if (improvement.name == Constants.cancelImprovementOrder) {
|
if (improvement.name == Constants.cancelImprovementOrder) {
|
||||||
tile.stopWorkingOnImprovement()
|
tile.stopWorkingOnImprovement()
|
||||||
// no onAccept() - Worker can stay selected
|
// no onAccept() - Worker can stay selected
|
||||||
} else {
|
} else {
|
||||||
if (improvement.name != tile.improvementInProgress)
|
if (improvement.name != tile.improvementInProgress) {
|
||||||
tile.startWorkingOnImprovement(improvement, currentPlayerCiv, unit)
|
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
|
unit.action = null // this is to "wake up" the worker if it's sleeping
|
||||||
onAccept()
|
onAccept()
|
||||||
}
|
}
|
||||||
@ -74,28 +80,20 @@ class ImprovementPickerScreen(
|
|||||||
|
|
||||||
// clone tileInfo without "top" feature if it could be removed
|
// clone tileInfo without "top" feature if it could be removed
|
||||||
// Keep this copy around for speed
|
// Keep this copy around for speed
|
||||||
val tileWithoutLastTerrain: Tile = tile.clone()
|
tileWithoutLastTerrain = tile.clone()
|
||||||
tileWithoutLastTerrain.setTerrainTransients()
|
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)
|
tileWithoutLastTerrain.removeTerrainFeature(tileWithoutLastTerrain.lastTerrain.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cityUniqueCache = LocalUniqueCache()
|
val cityUniqueCache = LocalUniqueCache()
|
||||||
|
|
||||||
for (improvement in ruleSet.tileImprovements.values) {
|
for (improvement in ruleset.tileImprovements.values) {
|
||||||
var suggestRemoval = false
|
|
||||||
// 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 == -1 && improvement.name != Constants.cancelImprovementOrder) continue
|
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 (improvement.name == tile.improvement) continue // also checked by canImprovementBeBuiltHere, but after more expensive tests
|
||||||
if (!unit.canBuildImprovement(improvement)) continue
|
if (!unit.canBuildImprovement(improvement)) continue
|
||||||
|
val problemReport = getProblemReport(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 image = ImageGetter.getImprovementPortrait(improvement.name, 30f)
|
val image = ImageGetter.getImprovementPortrait(improvement.name, 30f)
|
||||||
|
|
||||||
@ -103,7 +101,7 @@ class ImprovementPickerScreen(
|
|||||||
var shortcutKey = improvement.shortcutKey
|
var shortcutKey = improvement.shortcutKey
|
||||||
if (shortcutKey != null) {
|
if (shortcutKey != null) {
|
||||||
val techLevel = getRequiredTechColumn(improvement)
|
val techLevel = getRequiredTechColumn(improvement)
|
||||||
val isSuperseded = ruleSet.tileImprovements.values.asSequence()
|
val isSuperseded = ruleset.tileImprovements.values.asSequence()
|
||||||
// *other* improvements with same shortcutKey
|
// *other* improvements with same shortcutKey
|
||||||
.filter { it.shortcutKey == improvement.shortcutKey && it != improvement }
|
.filter { it.shortcutKey == improvement.shortcutKey && it != improvement }
|
||||||
// civ can build it (checks tech researched)
|
// civ can build it (checks tech researched)
|
||||||
@ -127,29 +125,6 @@ class ImprovementPickerScreen(
|
|||||||
&& improvement.name != Constants.cancelImprovementOrder)
|
&& improvement.name != Constants.cancelImprovementOrder)
|
||||||
if (tile.improvement != null && removeImprovement) labelText += "\n" + "Replaces [${tile.improvement}]".tr()
|
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)
|
val statIcons = getStatIconsTable(provideResource, removeImprovement)
|
||||||
|
|
||||||
// get benefits of the new improvement
|
// get benefits of the new improvement
|
||||||
@ -182,13 +157,20 @@ class ImprovementPickerScreen(
|
|||||||
improvementButton.onActivation(type = ActivationTypes.Tap, noEquivalence = true) {
|
improvementButton.onActivation(type = ActivationTypes.Tap, noEquivalence = true) {
|
||||||
selectedImprovement = improvement
|
selectedImprovement = improvement
|
||||||
pick(improvement.name.tr())
|
pick(improvement.name.tr())
|
||||||
descriptionLabel.setText(improvement.getDescription(ruleSet))
|
descriptionLabel.setText(improvement.getDescription(ruleset))
|
||||||
}
|
}
|
||||||
|
|
||||||
improvementButton.onDoubleClick { accept(improvement) }
|
improvementButton.onDoubleClick { accept(improvement) }
|
||||||
|
|
||||||
if (improvement.name == tile.improvementInProgress) improvementButton.color = Color.GREEN
|
when {
|
||||||
if (proposedSolutions.isNotEmpty() || tileMarkedForCreatesOneImprovement) {
|
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()
|
improvementButton.disable()
|
||||||
} else if (shortcutKey != null) {
|
} else if (shortcutKey != null) {
|
||||||
// Shortcut keys trigger what onDoubleClick does, not equivalent to single Click:
|
// Shortcut keys trigger what onDoubleClick does, not equivalent to single Click:
|
||||||
@ -197,7 +179,7 @@ class ImprovementPickerScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
regularImprovements.add(improvementButton)
|
regularImprovements.add(improvementButton)
|
||||||
regularImprovements.add(explanationText).padLeft(10f).fillY()
|
regularImprovements.add(getExplanationActor(improvement, problemReport)).padLeft(10f)
|
||||||
regularImprovements.row()
|
regularImprovements.row()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,4 +233,66 @@ class ImprovementPickerScreen(
|
|||||||
}
|
}
|
||||||
return statsTable
|
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,
|
return UnitAction(UnitActionType.Repair, 90f,
|
||||||
title = "${UnitActionType.Repair} [${unit.currentTile.getImprovementToRepair()!!.name}] - [${turnsToBuild}${Fonts.turn}]",
|
title = "${UnitActionType.Repair} [${unit.currentTile.getImprovementToRepair()!!.name}] - [${turnsToBuild}${Fonts.turn}]",
|
||||||
action = {
|
action = {
|
||||||
tile.turnsToImprovement = getRepairTurns(unit)
|
tile.queueImprovement(Constants.repair, turnsToBuild)
|
||||||
tile.improvementInProgress = Constants.repair
|
|
||||||
}.takeIf { couldConstruct }
|
}.takeIf { couldConstruct }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ class TileImprovementConstructionTests {
|
|||||||
@Test
|
@Test
|
||||||
fun buildingRoadBuildsARoad() {
|
fun buildingRoadBuildsARoad() {
|
||||||
val tile = tileMap[1,1]
|
val tile = tileMap[1,1]
|
||||||
tile.improvementFunctions.changeImprovement("Road")
|
tile.improvementFunctions.setImprovement("Road")
|
||||||
assert(tile.roadStatus == RoadStatus.Road)
|
assert(tile.roadStatus == RoadStatus.Road)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ class TileImprovementConstructionTests {
|
|||||||
fun removingRoadRemovesRoad() {
|
fun removingRoadRemovesRoad() {
|
||||||
val tile = tileMap[1,1]
|
val tile = tileMap[1,1]
|
||||||
tile.roadStatus = RoadStatus.Road
|
tile.roadStatus = RoadStatus.Road
|
||||||
tile.improvementFunctions.changeImprovement("Remove Road")
|
tile.improvementFunctions.setImprovement("Remove Road")
|
||||||
assert(tile.roadStatus == RoadStatus.None)
|
assert(tile.roadStatus == RoadStatus.None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,9 +193,9 @@ class TileImprovementConstructionTests {
|
|||||||
fun removingForestRemovesForestAndLumbermill() {
|
fun removingForestRemovesForestAndLumbermill() {
|
||||||
val tile = tileMap[1,1]
|
val tile = tileMap[1,1]
|
||||||
tile.addTerrainFeature("Forest")
|
tile.addTerrainFeature("Forest")
|
||||||
tile.improvementFunctions.changeImprovement("Lumber mill")
|
tile.improvementFunctions.setImprovement("Lumber mill")
|
||||||
assert(tile.getTileImprovement()!!.name == "Lumber mill")
|
assert(tile.getTileImprovement()!!.name == "Lumber mill")
|
||||||
tile.improvementFunctions.changeImprovement("Remove Forest")
|
tile.improvementFunctions.setImprovement("Remove Forest")
|
||||||
assert(tile.terrainFeatures.isEmpty())
|
assert(tile.terrainFeatures.isEmpty())
|
||||||
assert(tile.improvement == null) // Lumber mill can ONLY be on Forest, and is therefore removed
|
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.addTerrainFeature("Forest")
|
||||||
tile.resource = "Deer"
|
tile.resource = "Deer"
|
||||||
tile.baseTerrain = "Plains"
|
tile.baseTerrain = "Plains"
|
||||||
tile.improvementFunctions.changeImprovement("Camp")
|
tile.improvementFunctions.setImprovement("Camp")
|
||||||
assert(tile.getTileImprovement()!!.name == "Camp")
|
assert(tile.getTileImprovement()!!.name == "Camp")
|
||||||
tile.improvementFunctions.changeImprovement("Remove Forest")
|
tile.improvementFunctions.setImprovement("Remove Forest")
|
||||||
assert(tile.terrainFeatures.isEmpty())
|
assert(tile.terrainFeatures.isEmpty())
|
||||||
assert(tile.improvement == "Camp") // Camp can be both on Forest AND on Plains, so not removed
|
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")
|
tile.addTerrainFeature("Forest")
|
||||||
|
|
||||||
val lumberMill = testGame.ruleset.tileImprovements["Lumber mill"]!!
|
val lumberMill = testGame.ruleset.tileImprovements["Lumber mill"]!!
|
||||||
tile.improvementFunctions.changeImprovement(lumberMill.name)
|
tile.improvementFunctions.setImprovement(lumberMill.name)
|
||||||
assert(tile.getTileImprovement() == lumberMill)
|
assert(tile.getTileImprovement() == lumberMill)
|
||||||
|
|
||||||
// 1f 1p from forest, 2p from lumber mill since all techs are researched
|
// 1f 1p from forest, 2p from lumber mill since all techs are researched
|
||||||
|
Reference in New Issue
Block a user