All constructions are now in the queue - no more separate "current construction" and "construction queue" - #2428

That went pretty simply, compared to the magnitude of the change
This commit is contained in:
Yair Morgenstern 2020-04-16 13:01:45 +03:00
parent 3ba20dca52
commit 7d3a10b6ab
12 changed files with 102 additions and 98 deletions

View File

@ -233,10 +233,10 @@ class GameInfo {
// Renames as of version 3.1.8, because of translation conflicts with the property "Range" and the difficulty "Immortal" // Renames as of version 3.1.8, because of translation conflicts with the property "Range" and the difficulty "Immortal"
// Needs to be BEFORE tileMap.setTransients, because the units' setTransients is called from there // Needs to be BEFORE tileMap.setTransients, because the units' setTransients is called from there
for(tile in tileMap.values) for (tile in tileMap.values)
for(unit in tile.getUnits()){ for (unit in tile.getUnits()) {
if(unit.name=="Immortal") unit.name="Persian Immortal" if (unit.name == "Immortal") unit.name = "Persian Immortal"
if(unit.promotions.promotions.contains("Range")){ if (unit.promotions.promotions.contains("Range")) {
unit.promotions.promotions.remove("Range") unit.promotions.promotions.remove("Range")
unit.promotions.promotions.add("Extended Range") unit.promotions.promotions.add("Extended Range")
} }
@ -244,8 +244,8 @@ class GameInfo {
tileMap.setTransients(ruleSet) tileMap.setTransients(ruleSet)
if(currentPlayer=="") currentPlayer=civilizations.first { it.isPlayerCivilization() }.civName if (currentPlayer == "") currentPlayer = civilizations.first { it.isPlayerCivilization() }.civName
currentPlayerCiv=getCivilization(currentPlayer) currentPlayerCiv = getCivilization(currentPlayer)
// this is separated into 2 loops because when we activate updateVisibleTiles in civ.setTransients, // this is separated into 2 loops because when we activate updateVisibleTiles in civ.setTransients,
// we try to find new civs, and we check if civ is barbarian, which we can't know unless the gameInfo is already set. // we try to find new civs, and we check if civ is barbarian, which we can't know unless the gameInfo is already set.
@ -259,7 +259,7 @@ class GameInfo {
// which can fail if there's an "unregistered" building anywhere // which can fail if there's an "unregistered" building anywhere
for (civInfo in civilizations) { for (civInfo in civilizations) {
// As of 3.3.7, Facism -> Fascism // As of 3.3.7, Facism -> Fascism
if(civInfo.policies.adoptedPolicies.contains("Facism")){ if (civInfo.policies.adoptedPolicies.contains("Facism")) {
civInfo.policies.adoptedPolicies.remove("Facism") civInfo.policies.adoptedPolicies.remove("Facism")
civInfo.policies.adoptedPolicies.add("Fascism") civInfo.policies.adoptedPolicies.add("Fascism")
} }
@ -267,7 +267,7 @@ class GameInfo {
// This doesn't HAVE to go here, but why not. // This doesn't HAVE to go here, but why not.
// As of version 3.1.3, trade offers of "Declare war on X" and "Introduction to X" were changed to X, // As of version 3.1.3, trade offers of "Declare war on X" and "Introduction to X" were changed to X,
// with the extra text being added only on UI display (solved a couple of problems). // with the extra text being added only on UI display (solved a couple of problems).
for(trade in civInfo.tradeRequests.map { it.trade }) { for (trade in civInfo.tradeRequests.map { it.trade }) {
for (offer in trade.theirOffers.union(trade.ourOffers)) { for (offer in trade.theirOffers.union(trade.ourOffers)) {
offer.name = offer.name.removePrefix("Declare war on ") offer.name = offer.name.removePrefix("Declare war on ")
offer.name = offer.name.removePrefix("Introduction to ") offer.name = offer.name.removePrefix("Introduction to ")
@ -276,15 +276,15 @@ class GameInfo {
// As of 3.4.9 cities are referenced by id, not by name // As of 3.4.9 cities are referenced by id, not by name
// So try to update every tradeRequest (if there are no conflicting names) // So try to update every tradeRequest (if there are no conflicting names)
for(tradeRequest in civInfo.tradeRequests) { for (tradeRequest in civInfo.tradeRequests) {
val trade = tradeRequest.trade val trade = tradeRequest.trade
val toRemove = ArrayList<TradeOffer>() val toRemove = ArrayList<TradeOffer>()
for (offer in trade.ourOffers) { for (offer in trade.ourOffers) {
if (offer.type == TradeType.City) { if (offer.type == TradeType.City) {
val countNames = civInfo.cities.count { it.name == offer.name} val countNames = civInfo.cities.count { it.name == offer.name }
if (countNames == 1) if (countNames == 1)
offer.name = civInfo.cities.first { it.name == offer.name}.id offer.name = civInfo.cities.first { it.name == offer.name }.id
// There are conflicting names: we can't guess what city was being offered // There are conflicting names: we can't guess what city was being offered
else if (countNames > 1) else if (countNames > 1)
toRemove.add(offer) toRemove.add(offer)
@ -297,10 +297,10 @@ class GameInfo {
val themCivInfo = getCivilization(tradeRequest.requestingCiv) val themCivInfo = getCivilization(tradeRequest.requestingCiv)
for (offer in trade.theirOffers) { for (offer in trade.theirOffers) {
if (offer.type == TradeType.City) { if (offer.type == TradeType.City) {
val countNames = themCivInfo.cities.count { it.name == offer.name} val countNames = themCivInfo.cities.count { it.name == offer.name }
if (countNames == 1) if (countNames == 1)
offer.name = themCivInfo.cities.first { it.name == offer.name}.id offer.name = themCivInfo.cities.first { it.name == offer.name }.id
// There are conflicting names: we can't guess what city was being offered // There are conflicting names: we can't guess what city was being offered
else if (countNames > 1) else if (countNames > 1)
toRemove.add(offer) toRemove.add(offer)
@ -329,8 +329,8 @@ class GameInfo {
for (civInfo in civilizations) civInfo.setTransients() for (civInfo in civilizations) civInfo.setTransients()
for (civInfo in civilizations) civInfo.updateSightAndResources() for (civInfo in civilizations) civInfo.updateSightAndResources()
for (civInfo in civilizations){ for (civInfo in civilizations) {
for(unit in civInfo.getCivUnits()) for (unit in civInfo.getCivUnits())
unit.updateVisibleTiles() // this needs to be done after all the units are assigned to their civs and all other transients are set unit.updateVisibleTiles() // this needs to be done after all the units are assigned to their civs and all other transients are set
// Since this depends on the cities of ALL civilizations, // Since this depends on the cities of ALL civilizations,
@ -339,9 +339,19 @@ class GameInfo {
civInfo.initialSetCitiesConnectedToCapitalTransients() civInfo.initialSetCitiesConnectedToCapitalTransients()
// We need to determine the GLOBAL happiness state in order to determine the city stats // We need to determine the GLOBAL happiness state in order to determine the city stats
for(cityInfo in civInfo.cities) cityInfo.cityStats.updateCityHappiness() for (cityInfo in civInfo.cities) cityInfo.cityStats.updateCityHappiness()
for (cityInfo in civInfo.cities) cityInfo.cityStats.update() for (cityInfo in civInfo.cities) {
if(cityInfo.cityConstructions.currentConstruction!="") { // move it to the top of the queue
val constructionQueue = cityInfo.cityConstructions.constructionQueue
val itemsInQueue = constructionQueue.toList()
constructionQueue.clear()
constructionQueue.add(cityInfo.cityConstructions.currentConstruction)
constructionQueue.addAll(itemsInQueue)
cityInfo.cityConstructions.currentConstruction = ""
}
cityInfo.cityStats.update()
}
} }
} }
@ -350,8 +360,7 @@ class GameInfo {
cityConstructions.builtBuildings.remove(oldBuildingName) cityConstructions.builtBuildings.remove(oldBuildingName)
cityConstructions.builtBuildings.add(newBuildingName) cityConstructions.builtBuildings.add(newBuildingName)
} }
if (cityConstructions.currentConstruction == oldBuildingName) cityConstructions.constructionQueue.replaceAll { if(it==oldBuildingName) newBuildingName else it }
cityConstructions.currentConstruction = newBuildingName
if (cityConstructions.inProgressConstructions.containsKey(oldBuildingName)) { if (cityConstructions.inProgressConstructions.containsKey(oldBuildingName)) {
cityConstructions.inProgressConstructions[newBuildingName] = cityConstructions.inProgressConstructions[oldBuildingName]!! cityConstructions.inProgressConstructions[newBuildingName] = cityConstructions.inProgressConstructions[oldBuildingName]!!
cityConstructions.inProgressConstructions.remove(oldBuildingName) cityConstructions.inProgressConstructions.remove(oldBuildingName)

View File

@ -55,13 +55,13 @@ object Automation {
fun trainMilitaryUnit(city: CityInfo) { fun trainMilitaryUnit(city: CityInfo) {
val name = chooseMilitaryUnit(city) val name = chooseMilitaryUnit(city)
city.cityConstructions.currentConstruction = name city.cityConstructions.currentConstructionFromQueue = name
} }
fun chooseMilitaryUnit(city: CityInfo) : String { fun chooseMilitaryUnit(city: CityInfo) : String {
var militaryUnits = city.cityConstructions.getConstructableUnits().filter { !it.unitType.isCivilian() } var militaryUnits = city.cityConstructions.getConstructableUnits().filter { !it.unitType.isCivilian() }
if (militaryUnits.map { it.name }.contains(city.cityConstructions.currentConstruction)) if (militaryUnits.map { it.name }.contains(city.cityConstructions.currentConstructionFromQueue))
return city.cityConstructions.currentConstruction return city.cityConstructions.currentConstructionFromQueue
val findWaterConnectedCitiesAndEnemies = BFS(city.getCenterTile()){it.isWater || it.isCityCenter()} val findWaterConnectedCitiesAndEnemies = BFS(city.getCenterTile()){it.isWater || it.isCityCenter()}
findWaterConnectedCitiesAndEnemies.stepToEnd() findWaterConnectedCitiesAndEnemies.stepToEnd()

View File

@ -89,7 +89,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
else theChosenOne = relativeCostEffectiveness.minBy { it.remainingWork }!!.choice else theChosenOne = relativeCostEffectiveness.minBy { it.remainingWork }!!.choice
civInfo.addNotification("Work has started on [$theChosenOne]", Color.BROWN, CityAction(cityInfo.location)) civInfo.addNotification("Work has started on [$theChosenOne]", Color.BROWN, CityAction(cityInfo.location))
cityConstructions.currentConstruction = theChosenOne cityConstructions.currentConstructionFromQueue = theChosenOne
} }
private fun addMilitaryUnitChoice() { private fun addMilitaryUnitChoice() {

View File

@ -10,7 +10,6 @@ import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.map.MapUnit import com.unciv.logic.map.MapUnit
import com.unciv.logic.trade.* import com.unciv.logic.trade.*
import com.unciv.models.metadata.GameSpeed
import com.unciv.models.ruleset.VictoryType import com.unciv.models.ruleset.VictoryType
import com.unciv.models.ruleset.tech.Technology import com.unciv.models.ruleset.tech.Technology
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
@ -459,11 +458,11 @@ object NextTurnAutomation{
if (civInfo.cities.any() if (civInfo.cities.any()
&& civInfo.getHappiness() > civInfo.cities.size + 5 && civInfo.getHappiness() > civInfo.cities.size + 5
&& civInfo.getCivUnits().none { it.name == Constants.settler } && civInfo.getCivUnits().none { it.name == Constants.settler }
&& civInfo.cities.none { it.cityConstructions.currentConstruction == Constants.settler }) { && civInfo.cities.none { it.cityConstructions.currentConstructionFromQueue == Constants.settler }) {
val bestCity = civInfo.cities.maxBy { it.cityStats.currentCityStats.production }!! val bestCity = civInfo.cities.maxBy { it.cityStats.currentCityStats.production }!!
if (bestCity.cityConstructions.builtBuildings.size > 1) // 2 buildings or more, otherwise focus on self first if (bestCity.cityConstructions.builtBuildings.size > 1) // 2 buildings or more, otherwise focus on self first
bestCity.cityConstructions.currentConstruction = Constants.settler bestCity.cityConstructions.currentConstructionFromQueue = Constants.settler
} }
} }

View File

@ -19,8 +19,8 @@ import kotlin.math.roundToInt
* City constructions manager. * City constructions manager.
* *
* @property cityInfo the city it refers to * @property cityInfo the city it refers to
* @property currentConstruction the name of the construction is currently worked, default = "Monument" * @property currentConstructionFromQueue the name of the construction is currently worked, default = "Monument"
* @property currentConstructionIsUserSet a flag indicating if the [currentConstruction] has been set by the user or by the AI * @property currentConstructionIsUserSet a flag indicating if the [currentConstructionFromQueue] has been set by the user or by the AI
* @property constructionQueue a list of constructions names enqueued * @property constructionQueue a list of constructions names enqueued
*/ */
class CityConstructions { class CityConstructions {
@ -29,7 +29,13 @@ class CityConstructions {
var builtBuildings = HashSet<String>() var builtBuildings = HashSet<String>()
val inProgressConstructions = HashMap<String, Int>() val inProgressConstructions = HashMap<String, Int>()
var currentConstruction: String = "Monument" @Deprecated("As of 3.7.5, all constructions are in the queue")
var currentConstruction=""
var currentConstructionFromQueue: String
get() {
if(constructionQueue.isEmpty()) return "" else return constructionQueue.first()
}
set(value) { if(constructionQueue.isEmpty()) constructionQueue.add(value) else constructionQueue[0]=value }
var currentConstructionIsUserSet = false var currentConstructionIsUserSet = false
var constructionQueue = mutableListOf<String>() var constructionQueue = mutableListOf<String>()
val queueMaxSize = 10 val queueMaxSize = 10
@ -39,7 +45,6 @@ class CityConstructions {
val toReturn = CityConstructions() val toReturn = CityConstructions()
toReturn.builtBuildings.addAll(builtBuildings) toReturn.builtBuildings.addAll(builtBuildings)
toReturn.inProgressConstructions.putAll(inProgressConstructions) toReturn.inProgressConstructions.putAll(inProgressConstructions)
toReturn.currentConstruction=currentConstruction
toReturn.currentConstructionIsUserSet=currentConstructionIsUserSet toReturn.currentConstructionIsUserSet=currentConstructionIsUserSet
toReturn.constructionQueue.addAll(constructionQueue) toReturn.constructionQueue.addAll(constructionQueue)
return toReturn return toReturn
@ -89,13 +94,13 @@ class CityConstructions {
} }
fun getCityProductionTextForCityButton(): String { fun getCityProductionTextForCityButton(): String {
val currentConstructionSnapshot = currentConstruction // See below val currentConstructionSnapshot = currentConstructionFromQueue // See below
var result = currentConstructionSnapshot.tr() var result = currentConstructionSnapshot.tr()
if (currentConstructionSnapshot != "") { if (currentConstructionSnapshot != "") {
val construction = PerpetualConstruction.perpetualConstructionsMap[currentConstructionSnapshot] val construction = PerpetualConstruction.perpetualConstructionsMap[currentConstructionSnapshot]
if (construction == null) { if (construction == null) {
val turnsLeft = turnsToConstruction(currentConstructionSnapshot) val turnsLeft = turnsToConstruction(currentConstructionSnapshot)
result += ("\r\n" + "Cost".tr() + " " + getConstruction(currentConstruction).getProductionCost(cityInfo.civInfo).toString()).tr() result += ("\r\n" + "Cost".tr() + " " + getConstruction(currentConstructionFromQueue).getProductionCost(cityInfo.civInfo).toString()).tr()
result += ConstructionInfoTable.turnOrTurns(turnsLeft) result += ConstructionInfoTable.turnOrTurns(turnsLeft)
} else { } else {
result += construction.getProductionTooltip(cityInfo) result += construction.getProductionTooltip(cityInfo)
@ -107,7 +112,7 @@ class CityConstructions {
fun getProductionForTileInfo(): String { fun getProductionForTileInfo(): String {
/* this is because there were rare errors tht I assume were caused because /* this is because there were rare errors tht I assume were caused because
currentContruction changed on another thread */ currentContruction changed on another thread */
val currentConstructionSnapshot = currentConstruction val currentConstructionSnapshot = currentConstructionFromQueue
var result = currentConstructionSnapshot.tr() var result = currentConstructionSnapshot.tr()
if (currentConstructionSnapshot!="" if (currentConstructionSnapshot!=""
&& !PerpetualConstruction.perpetualConstructionsMap.containsKey(currentConstructionSnapshot)) { && !PerpetualConstruction.perpetualConstructionsMap.containsKey(currentConstructionSnapshot)) {
@ -117,16 +122,14 @@ class CityConstructions {
return result return result
} }
fun getCurrentConstruction(): IConstruction = getConstruction(currentConstruction) fun getCurrentConstruction(): IConstruction = getConstruction(currentConstructionFromQueue)
fun getIConstructionQueue(): List<IConstruction> = constructionQueue.map{ getConstruction(it) }
fun isBuilt(buildingName: String): Boolean = builtBuildings.contains(buildingName) fun isBuilt(buildingName: String): Boolean = builtBuildings.contains(buildingName)
fun isBeingConstructed(constructionName: String): Boolean = currentConstruction == constructionName fun isBeingConstructed(constructionName: String): Boolean = currentConstructionFromQueue == constructionName
fun isEnqueued(constructionName: String): Boolean = constructionQueue.contains(constructionName) fun isEnqueued(constructionName: String): Boolean = constructionQueue.contains(constructionName)
fun isBeingConstructedOrEnqueued(constructionName: String): Boolean = isBeingConstructed(constructionName) || isEnqueued(constructionName) fun isBeingConstructedOrEnqueued(constructionName: String): Boolean = isBeingConstructed(constructionName) || isEnqueued(constructionName)
fun isQueueFull(): Boolean = constructionQueue.size == queueMaxSize fun isQueueFull(): Boolean = constructionQueue.size == queueMaxSize
fun isQueueEmpty(): Boolean = constructionQueue.isEmpty()
fun isBuildingWonder(): Boolean { fun isBuildingWonder(): Boolean {
val currentConstruction = getCurrentConstruction() val currentConstruction = getCurrentConstruction()
@ -135,13 +138,8 @@ class CityConstructions {
/** If the city is constructing multiple units of the same type, subsequent units will require the full cost */ /** If the city is constructing multiple units of the same type, subsequent units will require the full cost */
fun isFirstConstructionOfItsKind(constructionQueueIndex: Int, name: String): Boolean { fun isFirstConstructionOfItsKind(constructionQueueIndex: Int, name: String): Boolean {
// idx = 1 is the currentConstruction, so it is the first
if (constructionQueueIndex == -1)
return true
// if the construction name is the same as the current construction, it isn't the first // if the construction name is the same as the current construction, it isn't the first
return !isBeingConstructed(name) && return constructionQueueIndex == constructionQueue.indexOfFirst { it == name }
constructionQueueIndex == constructionQueue.indexOfFirst{it == name}
} }
@ -150,10 +148,10 @@ class CityConstructions {
when { when {
gameBasics.buildings.containsKey(constructionName) -> return gameBasics.buildings[constructionName]!! gameBasics.buildings.containsKey(constructionName) -> return gameBasics.buildings[constructionName]!!
gameBasics.units.containsKey(constructionName) -> return gameBasics.units[constructionName]!! gameBasics.units.containsKey(constructionName) -> return gameBasics.units[constructionName]!!
constructionName=="" -> return getConstruction("Nothing") constructionName == "" -> return getConstruction("Nothing")
else -> { else -> {
val special = PerpetualConstruction.perpetualConstructionsMap[constructionName] val special = PerpetualConstruction.perpetualConstructionsMap[constructionName]
if(special!=null) return special if (special != null) return special
} }
} }
@ -185,19 +183,23 @@ class CityConstructions {
if(workLeft < 0) // we've done more work than actually necessary - possible if circumstances cause buildings to be cheaper later if(workLeft < 0) // we've done more work than actually necessary - possible if circumstances cause buildings to be cheaper later
return 1 // we'll finish this next turn return 1 // we'll finish this next turn
val currConstruction = currentConstruction
val cityStatsForConstruction: Stats val cityStatsForConstruction: Stats
if (currentConstruction == constructionName) cityStatsForConstruction = cityInfo.cityStats.currentCityStats if (currentConstructionFromQueue == constructionName) cityStatsForConstruction = cityInfo.cityStats.currentCityStats
else { else {
// The ol' Switcharoo - what would our stats be if that was our current construction? // The ol' Switcharoo - what would our stats be if that was our current construction?
// Since this is only ever used for UI purposes, I feel fine with having it be a bit inefficient // Since this is only ever used for UI purposes, I feel fine with having it be a bit inefficient
// and recalculating the entire city stats // and recalculating the entire city stats
currentConstruction = constructionName // We don't want to change our current construction queue - what if we have an empty queue, too many changes to check for -
// So we ust clone it and see what would happen f that was our construction
val cityConstructionsClone = clone()
cityConstructionsClone.currentConstructionFromQueue = constructionName
cityConstructionsClone.cityInfo = cityInfo
cityConstructionsClone.setTransients()
cityInfo.cityConstructions = cityConstructionsClone
cityInfo.cityStats.update() cityInfo.cityStats.update()
cityStatsForConstruction = cityInfo.cityStats.currentCityStats cityStatsForConstruction = cityInfo.cityStats.currentCityStats
// revert! // revert!
currentConstruction = currConstruction cityInfo.cityConstructions = this
cityInfo.cityStats.update() cityInfo.cityStats.update()
} }
@ -215,19 +217,19 @@ class CityConstructions {
} }
fun addProductionPoints(productionToAdd: Int) { fun addProductionPoints(productionToAdd: Int) {
if (!inProgressConstructions.containsKey(currentConstruction)) inProgressConstructions[currentConstruction] = 0 if (!inProgressConstructions.containsKey(currentConstructionFromQueue)) inProgressConstructions[currentConstructionFromQueue] = 0
inProgressConstructions[currentConstruction] = inProgressConstructions[currentConstruction]!! + productionToAdd inProgressConstructions[currentConstructionFromQueue] = inProgressConstructions[currentConstructionFromQueue]!! + productionToAdd
} }
fun constructIfEnough(){ fun constructIfEnough(){
stopUnbuildableConstruction() stopUnbuildableConstruction()
val construction = getConstruction(currentConstruction) val construction = getConstruction(currentConstructionFromQueue)
if(construction is PerpetualConstruction) chooseNextConstruction() // check every turn if we could be doing something better, because this doesn't end by itself if(construction is PerpetualConstruction) chooseNextConstruction() // check every turn if we could be doing something better, because this doesn't end by itself
else { else {
val productionCost = construction.getProductionCost(cityInfo.civInfo) val productionCost = construction.getProductionCost(cityInfo.civInfo)
if (inProgressConstructions.containsKey(currentConstruction) if (inProgressConstructions.containsKey(currentConstructionFromQueue)
&& inProgressConstructions[currentConstruction]!! >= productionCost) { && inProgressConstructions[currentConstructionFromQueue]!! >= productionCost) {
constructionComplete(construction) constructionComplete(construction)
cancelCurrentConstruction() cancelCurrentConstruction()
} }
@ -238,22 +240,22 @@ class CityConstructions {
stopUnbuildableConstruction() stopUnbuildableConstruction()
validateConstructionQueue() validateConstructionQueue()
if(getConstruction(currentConstruction) !is PerpetualConstruction) if(getConstruction(currentConstructionFromQueue) !is PerpetualConstruction)
addProductionPoints(cityStats.production.roundToInt()) addProductionPoints(cityStats.production.roundToInt())
} }
private fun stopUnbuildableConstruction() { private fun stopUnbuildableConstruction() {
// Let's try to remove the building from the city, and see if we can still build it (we need to remove because of wonders etc.) // Let's try to remove the building from the city, and see if we can still build it (we need to remove because of wonders etc.)
val construction = getConstruction(currentConstruction) val construction = getConstruction(currentConstructionFromQueue)
val saveCurrentConstruction = currentConstruction val saveCurrentConstruction = currentConstructionFromQueue
currentConstruction = "" currentConstructionFromQueue = ""
if (!construction.isBuildable(this)) { if (!construction.isBuildable(this)) {
// We can't build this building anymore! (Wonder has been built / resource is gone / etc.) // We can't build this building anymore! (Wonder has been built / resource is gone / etc.)
cityInfo.civInfo.addNotification("[${cityInfo.name}] cannot continue work on [$saveCurrentConstruction]", cityInfo.location, Color.BROWN) cityInfo.civInfo.addNotification("[${cityInfo.name}] cannot continue work on [$saveCurrentConstruction]", cityInfo.location, Color.BROWN)
cancelCurrentConstruction() cancelCurrentConstruction()
} else } else
currentConstruction = saveCurrentConstruction currentConstructionFromQueue = saveCurrentConstruction
} }
private fun validateConstructionQueue() { private fun validateConstructionQueue() {
@ -266,7 +268,7 @@ class CityConstructions {
} }
// remove obsolete stuff from in progress constructions - happens often and leaves clutter in memory and save files // remove obsolete stuff from in progress constructions - happens often and leaves clutter in memory and save files
// should have NO visible consequences - any accumulated points that may be reused later should stay (nukes when manhattan project city lost, nat wonder when conquered an empty city...) // should have NO visible consequences - any accumulated points that may be reused later should stay (nukes when manhattan project city lost, nat wonder when conquered an empty city...)
val inProgressSnapshot = inProgressConstructions.keys.filter { it != currentConstruction } val inProgressSnapshot = inProgressConstructions.keys.filter { it != currentConstructionFromQueue }
for (constructionName in inProgressSnapshot) { for (constructionName in inProgressSnapshot) {
val rejectionReason:String = val rejectionReason:String =
when (val construction = getConstruction(constructionName)) { when (val construction = getConstruction(constructionName)) {
@ -317,7 +319,7 @@ class CityConstructions {
return false // nothing built - no pay return false // nothing built - no pay
cityInfo.civInfo.gold -= getConstruction(constructionName).getGoldCost(cityInfo.civInfo) cityInfo.civInfo.gold -= getConstruction(constructionName).getGoldCost(cityInfo.civInfo)
if (currentConstruction == constructionName) if (currentConstructionFromQueue == constructionName)
cancelCurrentConstruction() cancelCurrentConstruction()
return true return true
@ -351,7 +353,7 @@ class CityConstructions {
private fun cancelCurrentConstruction() { private fun cancelCurrentConstruction() {
currentConstructionIsUserSet = false currentConstructionIsUserSet = false
currentConstruction = "" constructionQueue.removeAt(0)
chooseNextConstruction() chooseNextConstruction()
} }
@ -359,12 +361,10 @@ class CityConstructions {
if(currentConstructionIsUserSet) return if(currentConstructionIsUserSet) return
if (constructionQueue.isNotEmpty()) { if (constructionQueue.isNotEmpty()) {
currentConstructionIsUserSet = true currentConstructionIsUserSet = true
currentConstruction = constructionQueue.removeAt(0)
stopUnbuildableConstruction() stopUnbuildableConstruction()
if (currentConstruction != "") return if (currentConstructionFromQueue != "") return
} }
ConstructionAutomation(this).chooseNextConstruction() ConstructionAutomation(this).chooseNextConstruction()
@ -372,8 +372,8 @@ class CityConstructions {
fun addToQueue(constructionName: String) { fun addToQueue(constructionName: String) {
if (isQueueFull()) return if (isQueueFull()) return
if (isQueueEmpty() && currentConstruction == "" || currentConstruction == "Nothing") { if (currentConstructionFromQueue == "" || currentConstructionFromQueue == "Nothing") {
currentConstruction = constructionName currentConstructionFromQueue = constructionName
currentConstructionIsUserSet = true currentConstructionIsUserSet = true
} else } else
constructionQueue.add(constructionName) constructionQueue.add(constructionName)
@ -395,14 +395,8 @@ class CityConstructions {
} }
fun raisePriority(constructionQueueIndex: Int) { fun raisePriority(constructionQueueIndex: Int) {
// change current construction constructionQueue.swap(constructionQueueIndex - 1, constructionQueueIndex)
if(constructionQueueIndex == 0) {
// Add current construction to queue after the first element
constructionQueue.add(1, currentConstruction)
cancelCurrentConstruction()
}
else
constructionQueue.swap(constructionQueueIndex-1, constructionQueueIndex)
} }
// Lowering == Highering next element in queue // Lowering == Highering next element in queue

View File

@ -88,7 +88,7 @@ class CityInfo {
if (civInfo.cities.size == 1) { if (civInfo.cities.size == 1) {
cityConstructions.addBuilding("Palace") cityConstructions.addBuilding("Palace")
cityConstructions.currentConstruction = Constants.worker // Default for first city only! cityConstructions.currentConstructionFromQueue = Constants.worker // Default for first city only!
} }
if (civInfo.policies.isAdopted("Legalism")) if (civInfo.policies.isAdopted("Legalism"))
@ -213,7 +213,7 @@ class CityInfo {
} }
fun isGrowing(): Boolean { fun isGrowing(): Boolean {
return foodForNextTurn() > 0 && cityConstructions.currentConstruction != Constants.settler return foodForNextTurn() > 0 && cityConstructions.currentConstructionFromQueue != Constants.settler
} }
fun isStarving(): Boolean = foodForNextTurn() < 0 fun isStarving(): Boolean = foodForNextTurn() < 0

View File

@ -60,7 +60,7 @@ class CityStats {
private fun getStatsFromProduction(production: Float): Stats { private fun getStatsFromProduction(production: Float): Stats {
val stats = Stats() val stats = Stats()
when (cityInfo.cityConstructions.currentConstruction) { when (cityInfo.cityConstructions.currentConstructionFromQueue) {
"Gold" -> stats.gold += production / 4 "Gold" -> stats.gold += production / 4
"Science" -> { "Science" -> {
var scienceProduced = production / 4 var scienceProduced = production / 4
@ -476,7 +476,7 @@ class CityStats {
newFinalStatList["Maintenance"] = Stats().apply { gold -= buildingsMaintenance.toInt() } newFinalStatList["Maintenance"] = Stats().apply { gold -= buildingsMaintenance.toInt() }
if (cityInfo.cityConstructions.currentConstruction == Constants.settler && totalFood > 0) { if (cityInfo.cityConstructions.currentConstructionFromQueue == Constants.settler && totalFood > 0) {
newFinalStatList["Excess food to production"] = newFinalStatList["Excess food to production"] =
Stats().apply { production=totalFood; food=-totalFood } Stats().apply { production=totalFood; food=-totalFood }
} }

View File

@ -256,11 +256,12 @@ class TechManager {
} }
} }
val obsoleteUnits = getRuleset().units.values.filter { it.obsoleteTech == techName } val obsoleteUnits = getRuleset().units.values.filter { it.obsoleteTech == techName }.map { it.name }
for (city in civInfo.cities) for (city in civInfo.cities)
if (city.cityConstructions.getCurrentConstruction() in obsoleteUnits) { for(constructionName in city.cityConstructions.constructionQueue.toList()){ // copy, since we're changing the queue
val currentConstructionUnit = city.cityConstructions.getCurrentConstruction() as BaseUnit if(constructionName !in obsoleteUnits) continue
city.cityConstructions.currentConstruction = currentConstructionUnit.upgradesTo!! val constructionUnit = city.cityConstructions.getCurrentConstruction() as BaseUnit
city.cityConstructions.constructionQueue.replaceAll { if(it==constructionName) constructionUnit.upgradesTo!! else it }
} }
if(techName=="Writing" && civInfo.nation.unique == UniqueAbility.INGENUITY if(techName=="Writing" && civInfo.nation.unique == UniqueAbility.INGENUITY

View File

@ -68,7 +68,7 @@ class CityStatsTable(val cityScreen: CityScreen): Table() {
when { when {
cityInfo.isGrowing() -> "[${cityInfo.getNumTurnsToNewPopulation()}] turns to new population".tr() cityInfo.isGrowing() -> "[${cityInfo.getNumTurnsToNewPopulation()}] turns to new population".tr()
cityInfo.isStarving() -> "[${cityInfo.getNumTurnsToStarvation()}] turns to lose population".tr() cityInfo.isStarving() -> "[${cityInfo.getNumTurnsToStarvation()}] turns to lose population".tr()
cityInfo.cityConstructions.currentConstruction == Constants.settler -> "Food converts to production".tr() cityInfo.cityConstructions.currentConstructionFromQueue == Constants.settler -> "Food converts to production".tr()
else -> "Stopped population growth".tr() else -> "Stopped population growth".tr()
} }
turnsToPopString += " (" + cityInfo.population.foodStored + "/" + cityInfo.population.getFoodToNextPopulation() + ")" turnsToPopString += " (" + cityInfo.population.foodStored + "/" + cityInfo.population.getFoodToNextPopulation() + ")"

View File

@ -92,7 +92,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
private fun updateConstructionQueue() { private fun updateConstructionQueue() {
val city = cityScreen.city val city = cityScreen.city
val cityConstructions = city.cityConstructions val cityConstructions = city.cityConstructions
val currentConstruction = cityConstructions.currentConstruction val currentConstruction = cityConstructions.currentConstructionFromQueue
val queue = cityConstructions.constructionQueue val queue = cityConstructions.constructionQueue
constructionsQueueTable.defaults().pad(0f) constructionsQueueTable.defaults().pad(0f)
@ -100,7 +100,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
constructionsQueueTable.addSeparator() constructionsQueueTable.addSeparator()
if (currentConstruction != "") if (currentConstruction != "")
constructionsQueueTable.add(getQueueEntry(-1, currentConstruction, queue.isEmpty(), isSelectedCurrentConstruction())) constructionsQueueTable.add(getQueueEntry(0, currentConstruction))
.expandX().fillX().row() .expandX().fillX().row()
else else
constructionsQueueTable.add("Pick a construction".toLabel()).pad(2f).row() constructionsQueueTable.add("Pick a construction".toLabel()).pad(2f).row()
@ -111,7 +111,8 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
if (queue.isNotEmpty()) { if (queue.isNotEmpty()) {
queue.forEachIndexed { i, constructionName -> queue.forEachIndexed { i, constructionName ->
constructionsQueueTable.add(getQueueEntry(i, constructionName, i == queue.size - 1, i == selectedQueueEntry)) if (i != 0) // This is already displayed as "Current construction"
constructionsQueueTable.add(getQueueEntry(i, constructionName))
.expandX().fillX().row() .expandX().fillX().row()
if (i != queue.size - 1) if (i != queue.size - 1)
constructionsQueueTable.addSeparator() constructionsQueueTable.addSeparator()
@ -171,7 +172,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
availableConstructionsTable.addCategory("Other", specialConstructions) availableConstructionsTable.addCategory("Other", specialConstructions)
} }
private fun getQueueEntry(constructionQueueIndex: Int, name: String, isLast: Boolean, isSelected: Boolean): Table { private fun getQueueEntry(constructionQueueIndex: Int, name: String): Table {
val city = cityScreen.city val city = cityScreen.city
val cityConstructions = city.cityConstructions val cityConstructions = city.cityConstructions
@ -179,7 +180,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
table.align(Align.left).pad(5f) table.align(Align.left).pad(5f)
table.background = ImageGetter.getBackground(Color.BLACK) table.background = ImageGetter.getBackground(Color.BLACK)
if (isSelected) if (constructionQueueIndex == selectedQueueEntry)
table.background = ImageGetter.getBackground(Color.GREEN.cpy().lerp(Color.BLACK, 0.5f)) table.background = ImageGetter.getBackground(Color.GREEN.cpy().lerp(Color.BLACK, 0.5f))
val isFirstConstructionOfItsKind = cityConstructions.isFirstConstructionOfItsKind(constructionQueueIndex, name) val isFirstConstructionOfItsKind = cityConstructions.isFirstConstructionOfItsKind(constructionQueueIndex, name)
@ -197,14 +198,15 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
table.add(ImageGetter.getConstructionImage(name).surroundWithCircle(40f)).padRight(10f) table.add(ImageGetter.getConstructionImage(name).surroundWithCircle(40f)).padRight(10f)
table.add(text.toLabel()).expandX().fillX().left() table.add(text.toLabel()).expandX().fillX().left()
if (constructionQueueIndex >= 0) table.add(getRaisePriorityButton(constructionQueueIndex, name, city)).right() if (constructionQueueIndex > 0) table.add(getRaisePriorityButton(constructionQueueIndex, name, city)).right()
else table.add().right() else table.add().right()
if (!isLast) table.add(getLowerPriorityButton(constructionQueueIndex, name, city)).right() if (constructionQueueIndex != cityConstructions.constructionQueue.lastIndex)
table.add(getLowerPriorityButton(constructionQueueIndex, name, city)).right()
else table.add().right() else table.add().right()
table.touchable = Touchable.enabled table.touchable = Touchable.enabled
table.onClick { table.onClick {
cityScreen.selectedConstruction = cityScreen.city.cityConstructions.getConstruction(name) cityScreen.selectedConstruction = cityConstructions.getConstruction(name)
cityScreen.selectedTile = null cityScreen.selectedTile = null
selectedQueueEntry = constructionQueueIndex selectedQueueEntry = constructionQueueIndex
cityScreen.update() cityScreen.update()
@ -256,7 +258,6 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
} }
private fun isSelectedQueueEntry(): Boolean = selectedQueueEntry > -2 private fun isSelectedQueueEntry(): Boolean = selectedQueueEntry > -2
private fun isSelectedCurrentConstruction(): Boolean = selectedQueueEntry == -1
private fun getQueueButton(construction: IConstruction?): TextButton { private fun getQueueButton(construction: IConstruction?): TextButton {
val city = cityScreen.city val city = cityScreen.city
@ -303,7 +304,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre
val cityConstructions = city.cityConstructions val cityConstructions = city.cityConstructions
// We can't trust the isSelectedQueueEntry because that fails when we have the same unit as both the current construction and in the queue, // We can't trust the isSelectedQueueEntry because that fails when we have the same unit as both the current construction and in the queue,
// and then we purchase the unit from the queue - see #2157 // and then we purchase the unit from the queue - see #2157
val constructionIsCurrentConstruction = construction.name==cityConstructions.currentConstruction val constructionIsCurrentConstruction = construction.name==cityConstructions.currentConstructionFromQueue
if (!cityConstructions.purchaseConstruction(construction.name)) { if (!cityConstructions.purchaseConstruction(construction.name)) {
Popup(cityScreen).apply { Popup(cityScreen).apply {

View File

@ -320,7 +320,7 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup, skin
val circle = ImageGetter.getCircle() val circle = ImageGetter.getCircle()
circle.setSize(25f,25f) circle.setSize(25f,25f)
val image = ImageGetter.getConstructionImage(cityConstructions.currentConstruction) val image = ImageGetter.getConstructionImage(cityConstructions.currentConstructionFromQueue)
image.setSize(18f,18f) image.setSize(18f,18f)
image.centerY(group) image.centerY(group)
image.x = group.width-image.width image.x = group.width-image.width

View File

@ -527,10 +527,10 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
} }
} }
viewingCiv.cities.any { it.cityConstructions.currentConstruction == "" } -> viewingCiv.cities.any { it.cityConstructions.currentConstructionFromQueue == "" } ->
NextTurnAction("Pick construction", Color.CORAL) { NextTurnAction("Pick construction", Color.CORAL) {
val cityWithNoProductionSet = viewingCiv.cities val cityWithNoProductionSet = viewingCiv.cities
.firstOrNull { it.cityConstructions.currentConstruction == "" } .firstOrNull { it.cityConstructions.currentConstructionFromQueue == "" }
if (cityWithNoProductionSet != null) game.setScreen(CityScreen(cityWithNoProductionSet)) if (cityWithNoProductionSet != null) game.setScreen(CityScreen(cityWithNoProductionSet))
} }