diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index f8791c1b12..f4348f48d5 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -39,7 +39,7 @@ Requires a [buildingName] in all cities = Requires a [buildingName] in this city = Consumes 1 [resource] = Required tech: [requiredTech] = -Requires [PolicyOrNationalWonder] = +Requires [PolicyOrNationalWonder] = Cannot be purchased = Current construction = @@ -504,6 +504,8 @@ Start Golden Age = Yes = No = Acquire = +Under construction = + Science = Happiness = Production = @@ -517,6 +519,7 @@ Golden Age = [year] BC = [year] AD = Civilopedia = + Start new game = Save game = Load game = diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index 166a1e3d25..7e98fe9319 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -413,7 +413,17 @@ class CityConstructions { /** If this was done automatically, we should automatically try to choose a new construction and treat it as such */ fun removeFromQueue(constructionQueueIndex: Int, automatic: Boolean) { - constructionQueue.removeAt(constructionQueueIndex) + val constructionName = constructionQueue.removeAt(constructionQueueIndex) + val construction = getConstruction(constructionName) + if (construction is Building) { + val improvement = construction.getImprovement(cityInfo.getRuleset()) + if (improvement != null) { + val tileWithImprovement = cityInfo.getTiles().firstOrNull { it.improvementInProgress == improvement.name } + tileWithImprovement?.improvementInProgress = null + tileWithImprovement?.turnsToImprovement = 0 + } + } + if (constructionQueue.isEmpty()) { if (automatic) chooseNextConstruction() else constructionQueue.add("Nothing") // To prevent Construction Automation diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 616bff828e..e6b8090e2b 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -85,8 +85,10 @@ class CivilizationInfo { private var allyCivName = "" var naturalWonders = ArrayList() - //** for trades here, ourOffers is the current civ's offers, and theirOffers is what the requesting civ offers */ + /** for trades here, ourOffers is the current civ's offers, and theirOffers is what the requesting civ offers */ val tradeRequests = ArrayList() + /** See DiplomacyManager.flagsCountdown to why not eEnum */ + private var flagsCountdown = HashMap() // if we only use lists, and change the list each time the cities are changed, // we won't get concurrent modification exceptions. @@ -128,6 +130,7 @@ class CivilizationInfo { toReturn.tradeRequests.addAll(tradeRequests) toReturn.naturalWonders.addAll(naturalWonders) toReturn.cityStatePersonality = cityStatePersonality + toReturn.flagsCountdown.putAll(flagsCountdown) return toReturn } diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 1ffb48e13e..1ad6bc779f 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -442,10 +442,14 @@ open class TileInfo { if (terrainFeature != null) lineList += terrainFeature!!.tr() if (resource != null && (viewingCiv == null || hasViewableResource(viewingCiv))) lineList += resource!!.tr() if (naturalWonder != null) lineList += naturalWonder!!.tr() - if (roadStatus !== RoadStatus.None && !isCityCenter()) lineList += roadStatus.toString().tr() + if (roadStatus !== RoadStatus.None && !isCityCenter()) lineList += roadStatus.name.tr() if (improvement != null) lineList += improvement!!.tr() - if (improvementInProgress != null && isViewableToPlayer) - lineList += "{$improvementInProgress} - $turnsToImprovement${Fonts.turn}".tr() + if (improvementInProgress != null && isViewableToPlayer) { + var line = "{$improvementInProgress}" + if (turnsToImprovement > 0) line += " - $turnsToImprovement${Fonts.turn}" + else line += " ({Under construction})" + lineList += line.tr() + } if (civilianUnit != null && isViewableToPlayer) lineList += civilianUnit!!.name.tr() + " - " + civilianUnit!!.civInfo.civName.tr() if (militaryUnit != null && isViewableToPlayer) { diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index cb9a32e801..da28296e9a 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -5,6 +5,7 @@ import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.IConstruction import com.unciv.logic.civilization.CivilizationInfo import com.unciv.models.Counter +import com.unciv.models.ruleset.tile.TileImprovement import com.unciv.models.stats.NamedStats import com.unciv.models.stats.Stat import com.unciv.models.stats.Stats @@ -108,7 +109,7 @@ class Building : NamedStats(), IConstruction { stringBuilder.appendln("Consumes 1 [$requiredResource]".tr()) if (providesFreeBuilding != null) stringBuilder.appendln("Provides a free [$providesFreeBuilding] in the city".tr()) - if (uniques.isNotEmpty()){ + if (uniques.isNotEmpty()) { if (replacementTextForUniques != "") stringBuilder.appendln(replacementTextForUniques) else stringBuilder.appendln(uniques.asSequence().map { it.tr() }.joinToString("\n")) } @@ -298,7 +299,7 @@ class Building : NamedStats(), IConstruction { if (requiredTech != null && !civInfo.tech.isResearched(requiredTech!!)) return "$requiredTech not researched" for (unique in uniqueObjects.filter { it.placeholderText == "Unlocked with []" }) - if(civInfo.tech.researchedTechnologies.none { it.era() == unique.params[0] || it.name == unique.params[0] } + if (civInfo.tech.researchedTechnologies.none { it.era() == unique.params[0] || it.name == unique.params[0] } && !civInfo.policies.isAdopted(unique.params[0])) return unique.text @@ -404,6 +405,18 @@ class Building : NamedStats(), IConstruction { } cityConstructions.addBuilding(name) + + val improvement = getImprovement(civInfo.gameInfo.ruleSet) + if (improvement != null) { + val tileWithImprovement = cityConstructions.cityInfo.getTiles().firstOrNull { it.improvementInProgress == improvement.name } + if (tileWithImprovement != null) { + tileWithImprovement.turnsToImprovement = 0 + tileWithImprovement.improvementInProgress = null + tileWithImprovement.improvement = improvement.name + } + } + + if (providesFreeBuilding != null && !cityConstructions.containsBuildingOrEquivalent(providesFreeBuilding!!)) { var buildingToAdd = providesFreeBuilding!! @@ -444,4 +457,13 @@ class Building : NamedStats(), IConstruction { if (replaces == null) return this else return ruleset.buildings[replaces!!]!! } + + fun getImprovement(ruleset: Ruleset): TileImprovement? { + val improvementUnique = uniqueObjects + .firstOrNull { it.placeholderText == "Creates a [] improvement on a specific tile" } + if (improvementUnique == null) return null + return ruleset.tileImprovements[improvementUnique.params[0]]!! + } + + fun isSellable() = !isWonder && !isNationalWonder } \ No newline at end of file diff --git a/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt b/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt index 7f74dff9a3..c85e67b6d2 100644 --- a/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt +++ b/core/src/com/unciv/ui/cityscreen/CityInfoTable.kt @@ -96,7 +96,7 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(CameraStageBaseS cityScreen.city.civInfo, cityScreen.city.civInfo.gameInfo.ruleSet) wonderDetailsTable.add(detailsString.toLabel().apply { wrap = true }) .width(cityScreen.stage.width / 4 - 2 * pad).row() // when you set wrap, then you need to manually set the size of the label - if (!building.isWonder && !building.isNationalWonder) { + if (building.isSellable()) { val sellAmount = cityScreen.city.getGoldForSellingBuilding(building.name) val sellBuildingButton = "Sell for [$sellAmount] gold".toTextButton() wonderDetailsTable.add(sellBuildingButton).pad(5f).row() diff --git a/core/src/com/unciv/ui/cityscreen/CityScreen.kt b/core/src/com/unciv/ui/cityscreen/CityScreen.kt index 21cb5f04f4..d5b428b8f1 100644 --- a/core/src/com/unciv/ui/cityscreen/CityScreen.kt +++ b/core/src/com/unciv/ui/cityscreen/CityScreen.kt @@ -11,6 +11,8 @@ import com.unciv.logic.HexMath import com.unciv.logic.city.CityInfo import com.unciv.logic.city.IConstruction import com.unciv.logic.map.TileInfo +import com.unciv.models.ruleset.Building +import com.unciv.models.ruleset.tile.TileImprovement import com.unciv.ui.map.TileGroupMap import com.unciv.ui.tilegroups.TileSetStrings import com.unciv.ui.utils.* @@ -101,7 +103,7 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { exitCityButton.y = 10f cityPickerTable.update() cityPickerTable.centerX(stage) - cityPickerTable.setY(exitCityButton.top+10f, Align.bottom) + cityPickerTable.setY(exitCityButton.top + 10f, Align.bottom) tileTable.update(selectedTile) tileTable.setPosition(stage.width - 5f, 5f, Align.bottomRight) @@ -110,7 +112,7 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { selectedConstructionTable.setPosition(stage.width - 5f, 5f, Align.bottomRight) cityStatsTable.update() - cityStatsTable.setPosition( stage.width - 5f, stage.height - 5f, Align.topRight) + cityStatsTable.setPosition(stage.width - 5f, stage.height - 5f, Align.topRight) updateAnnexAndRazeCityButton() updateTileGroups() @@ -120,9 +122,17 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { val nextTile = city.expansion.chooseNewTileToOwn() for (tileGroup in tileGroups) { tileGroup.update() - if(tileGroup.tileInfo == nextTile){ + tileGroup.hideCircle() + if (city.tiles.contains(tileGroup.tileInfo.position) + && constructionsTable.improvementBuildingToConstruct != null) { + val improvement = constructionsTable.improvementBuildingToConstruct!!.getImprovement(city.getRuleset())!! + if (tileGroup.tileInfo.canBuildImprovement(improvement, city.civInfo)) + tileGroup.showCircle(Color.GREEN) + else tileGroup.showCircle(Color.RED) + } + if (tileGroup.tileInfo == nextTile) { tileGroup.showCircle(Color.PURPLE) - tileGroup.setColor(0f,0f,0f,0.7f) + tileGroup.setColor(0f, 0f, 0f, 0.7f) } } } @@ -130,7 +140,7 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { private fun updateAnnexAndRazeCityButton() { razeCityButtonHolder.clear() - if(city.isPuppet) { + if (city.isPuppet) { val annexCityButton = "Annex city".toTextButton() annexCityButton.labelCell.pad(10f) annexCityButton.onClick { @@ -139,19 +149,19 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { } if (!canChangeState) annexCityButton.disable() razeCityButtonHolder.add(annexCityButton).colspan(cityPickerTable.columns) - } else if(!city.isBeingRazed) { + } else if (!city.isBeingRazed) { val razeCityButton = "Raze city".toTextButton() razeCityButton.labelCell.pad(10f) - razeCityButton.onClick { city.isBeingRazed=true; update() } - if(!canChangeState || city.isOriginalCapital) + razeCityButton.onClick { city.isBeingRazed = true; update() } + if (!canChangeState || city.isOriginalCapital) razeCityButton.disable() razeCityButtonHolder.add(razeCityButton).colspan(cityPickerTable.columns) } else { val stopRazingCityButton = "Stop razing city".toTextButton() stopRazingCityButton.labelCell.pad(10f) - stopRazingCityButton.onClick { city.isBeingRazed=false; update() } - if(!canChangeState) stopRazingCityButton.disable() + stopRazingCityButton.onClick { city.isBeingRazed = false; update() } + if (!canChangeState) stopRazingCityButton.disable() razeCityButtonHolder.add(stopRazingCityButton).colspan(cityPickerTable.columns) } razeCityButtonHolder.pack() @@ -175,6 +185,21 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { tileGroup.onClick { if (city.isPuppet) return@onClick + if (constructionsTable.improvementBuildingToConstruct != null) { + val improvement = constructionsTable.improvementBuildingToConstruct!!.getImprovement(city.getRuleset())!! + if (tileInfo.canBuildImprovement(improvement, cityInfo.civInfo)) { + tileInfo.improvementInProgress = improvement.name + tileInfo.turnsToImprovement = -1 + constructionsTable.improvementBuildingToConstruct = null + cityInfo.cityConstructions.addToQueue(improvement.name) + update() + } else { + constructionsTable.improvementBuildingToConstruct = null + update() + } + return@onClick + } + selectedTile = tileInfo selectedConstruction = null if (tileGroup.isWorkable && canChangeState) { @@ -196,16 +221,16 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { stage.height / 2 + positionalVector.y * 0.8f * groupSize.toFloat()) } - val tileMapGroup = TileGroupMap(tileGroups,stage.width/2) + val tileMapGroup = TileGroupMap(tileGroups, stage.width / 2) val scrollPane = ScrollPane(tileMapGroup) - scrollPane.setSize(stage.width,stage.height) + scrollPane.setSize(stage.width, stage.height) scrollPane.setOrigin(stage.width / 2, stage.height / 2) scrollPane.center(stage) stage.addActor(scrollPane) scrollPane.layout() // center scrolling - scrollPane.scrollPercentX=0.5f - scrollPane.scrollPercentY=0.5f + scrollPane.scrollPercentX = 0.5f + scrollPane.scrollPercentY = 0.5f scrollPane.updateVisualScroll() } @@ -233,7 +258,7 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { private fun getKeyboardListener(): InputListener = object : InputListener() { override fun keyDown(event: InputEvent?, keyCode: Int): Boolean { if (event == null) return super.keyDown(event, keyCode) - when(event.keyCode) { + when (event.keyCode) { Input.Keys.LEFT -> page(-1) Input.Keys.RIGHT -> page(1) else -> return super.keyDown(event, keyCode) @@ -241,4 +266,4 @@ class CityScreen(internal val city: CityInfo): CameraStageBaseScreen() { return true } } -} +} \ No newline at end of file diff --git a/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt b/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt index ec79ef5fe7..e7510bda46 100644 --- a/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt +++ b/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt @@ -31,9 +31,11 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre private val constructionsQueueTable = Table() private val availableConstructionsTable = Table() private val buttons = Table() - private val pad = 10f + var improvementBuildingToConstruct:Building?=null + + init { showCityInfoTableButton = "Show stats drilldown".toTextButton() showCityInfoTableButton.onClick { @@ -347,8 +349,15 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre } fun addConstructionToQueue(construction: IConstruction, cityConstructions: CityConstructions) { + if (construction is Building && construction.uniqueObjects.any { it.placeholderText=="Creates a [] improvement on a specific tile" }){ + cityScreen.selectedTile + improvementBuildingToConstruct = construction + return + } + cityConstructions.addToQueue(construction.name) - if (!construction.shouldBeDisplayed(cityConstructions)) cityScreen.selectedConstruction = null + if (!construction.shouldBeDisplayed(cityConstructions)) // For buildings - unlike units which can be queued multiple times + cityScreen.selectedConstruction = null cityScreen.update() cityScreen.game.settings.addCompletedTutorialTask("Pick construction") }