mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-08 23:08:35 +07:00
Rework of the "Creates improvement on a specific tile" Unique (#6687)
* Make Citadel tile takeover a unique * CreatesOneImprovement unique overhaul step 1 * CreatesOneImprovement unique overhaul - increase highlights alpha * Fix missing translatables, again * CreatesOneImprovement unique overhaul - review suggestions Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
@ -198,8 +198,12 @@
|
||||
},
|
||||
{
|
||||
"name": "Citadel",
|
||||
"uniques": ["Great Improvement", "Gives a defensive bonus of [100]%", "Adjacent enemy units ending their turn take [30] damage", "Can be built just outside your borders"],
|
||||
"civilopediaText": [{"text":"Constructing it will take over the tiles around it and assign them to your closest city"}]
|
||||
"uniques": [
|
||||
"Great Improvement",
|
||||
"Gives a defensive bonus of [100]%",
|
||||
"Adjacent enemy units ending their turn take [30] damage",
|
||||
"Can be built just outside your borders",
|
||||
"Constructing it will take over the tiles around it and assign them to your closest city"]
|
||||
},
|
||||
|
||||
//Civilization unique improvements
|
||||
|
@ -198,8 +198,12 @@
|
||||
},
|
||||
{
|
||||
"name": "Citadel",
|
||||
"uniques": ["Great Improvement", "Gives a defensive bonus of [100]%", "Adjacent enemy units ending their turn take [30] damage", "Can be built just outside your borders"],
|
||||
"civilopediaText": [{"text":"Constructing it will take over the tiles around it and assign them to your closest city"}]
|
||||
"uniques": [
|
||||
"Great Improvement",
|
||||
"Gives a defensive bonus of [100]%",
|
||||
"Adjacent enemy units ending their turn take [30] damage",
|
||||
"Can be built just outside your borders",
|
||||
"Constructing it will take over the tiles around it and assign them to your closest city"]
|
||||
},
|
||||
|
||||
//Civilization unique improvements
|
||||
|
@ -953,6 +953,7 @@ Lock =
|
||||
Unlock =
|
||||
Move to city =
|
||||
Please enter a new name for your city =
|
||||
Please select a tile for this building's [improvement] =
|
||||
|
||||
# Ask for text or numbers popup UI
|
||||
|
||||
|
@ -44,7 +44,6 @@ object Constants {
|
||||
const val unknownNationName = "???"
|
||||
|
||||
const val fort = "Fort"
|
||||
const val citadel = "Citadel"
|
||||
|
||||
const val futureTech = "Future Tech"
|
||||
// Easter egg name. Is to avoid conflicts when players name their own religions.
|
||||
|
@ -12,6 +12,7 @@ import com.unciv.models.ruleset.MilestoneType
|
||||
import com.unciv.models.ruleset.Victory
|
||||
import com.unciv.models.ruleset.Victory.Focus
|
||||
import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
@ -169,9 +170,35 @@ object Automation {
|
||||
return true
|
||||
}
|
||||
|
||||
/** Checks both feasibility of Buildings with a CreatesOneImprovement unique
|
||||
* and resource scarcity making a construction undesirable.
|
||||
*/
|
||||
fun allowAutomatedConstruction(
|
||||
civInfo: CivilizationInfo,
|
||||
cityInfo: CityInfo,
|
||||
construction: INonPerpetualConstruction
|
||||
): Boolean {
|
||||
return allowCreateImprovementBuildings(civInfo, cityInfo, construction)
|
||||
&& allowSpendingResource(civInfo, construction)
|
||||
}
|
||||
|
||||
/** Checks both feasibility of Buildings with a [UniqueType.CreatesOneImprovement] unique (appropriate tile available).
|
||||
* Constructions without pass uncontested. */
|
||||
fun allowCreateImprovementBuildings(
|
||||
civInfo: CivilizationInfo,
|
||||
cityInfo: CityInfo,
|
||||
construction: INonPerpetualConstruction
|
||||
): Boolean {
|
||||
if (construction !is Building) return true
|
||||
if (!construction.hasCreateOneImprovementUnique()) return true // redundant but faster???
|
||||
val improvement = construction.getImprovementToCreate(cityInfo.getRuleset()) ?: return true
|
||||
return cityInfo.getTiles().any {
|
||||
it.canBuildImprovement(improvement, civInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/** Determines whether the AI should be willing to spend strategic resources to build
|
||||
* [construction] in [city], assumes that we are actually able to do so. */
|
||||
* [construction] for [civInfo], assumes that we are actually able to do so. */
|
||||
fun allowSpendingResource(civInfo: CivilizationInfo, construction: INonPerpetualConstruction): Boolean {
|
||||
// City states do whatever they want
|
||||
if (civInfo.isCityState())
|
||||
@ -255,6 +282,15 @@ object Automation {
|
||||
}
|
||||
}
|
||||
|
||||
/** Support [UniqueType.CreatesOneImprovement] unique - find best tile for placement automation */
|
||||
fun getTileForConstructionImprovement(cityInfo: CityInfo, improvement: TileImprovement): TileInfo? {
|
||||
return cityInfo.getTiles().filter {
|
||||
it.canBuildImprovement(improvement, cityInfo.civInfo)
|
||||
}.maxByOrNull {
|
||||
rankTileForCityWork(it, cityInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// Ranks a tile for any purpose except the expansion algorithm of cities
|
||||
internal fun rankTile(tile: TileInfo?, civInfo: CivilizationInfo): Float {
|
||||
if (tile == null) return 0f
|
||||
|
@ -55,17 +55,15 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
data class ConstructionChoice(val choice: String, var choiceModifier: Float, val remainingWork: Int)
|
||||
|
||||
private fun addChoice(choices: ArrayList<ConstructionChoice>, choice: String, choiceModifier: Float){
|
||||
choices.add(ConstructionChoice(choice,choiceModifier,cityConstructions.getRemainingWork(choice)))
|
||||
private fun addChoice(choices: ArrayList<ConstructionChoice>, choice: String, choiceModifier: Float) {
|
||||
choices.add(ConstructionChoice(choice, choiceModifier, cityConstructions.getRemainingWork(choice)))
|
||||
}
|
||||
|
||||
|
||||
fun chooseNextConstruction() {
|
||||
if (!UncivGame.Current.settings.autoAssignCityProduction
|
||||
&& civInfo.playerType == PlayerType.Human && !cityInfo.isPuppet
|
||||
) {
|
||||
&& civInfo.playerType == PlayerType.Human && !cityInfo.isPuppet)
|
||||
return
|
||||
}
|
||||
if (cityConstructions.getCurrentConstruction() !is PerpetualConstruction) return // don't want to be stuck on these forever
|
||||
|
||||
addFoodBuildingChoice()
|
||||
@ -119,9 +117,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
.filter { it.getStatBuyCost(cityInfo, stat = Stat.Faith)!! <= civInfo.religionManager.storedFaith }
|
||||
.firstOrNull() ?: return
|
||||
|
||||
|
||||
cityConstructions.purchaseConstruction(chosenItem.name, -1, false, stat=Stat.Faith)
|
||||
|
||||
}
|
||||
|
||||
private fun addMilitaryUnitChoice() {
|
||||
@ -152,7 +148,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
val buildableWorkboatUnits = buildableUnits
|
||||
.filter {
|
||||
it.hasUnique(UniqueType.CreateWaterImprovements)
|
||||
&& Automation.allowSpendingResource(civInfo, it)
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}
|
||||
val alreadyHasWorkBoat = buildableWorkboatUnits.any()
|
||||
&& !cityInfo.getTiles().any {
|
||||
@ -182,7 +178,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
val workerEquivalents = buildableUnits
|
||||
.filter {
|
||||
it.hasUnique(UniqueType.BuildImprovements)
|
||||
&& Automation.allowSpendingResource(civInfo, it)
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}
|
||||
if (workerEquivalents.none()) return // for mods with no worker units
|
||||
|
||||
@ -196,7 +192,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
private fun addCultureBuildingChoice() {
|
||||
val cultureBuilding = buildableNotWonders
|
||||
.filter { it.isStatRelated(Stat.Culture)
|
||||
&& Automation.allowSpendingResource(civInfo, it) }.minByOrNull { it.cost }
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }.minByOrNull { it.cost }
|
||||
if (cultureBuilding != null) {
|
||||
var modifier = 0.5f
|
||||
if (cityInfo.cityStats.currentCityStats.culture == 0f) // It won't grow if we don't help it
|
||||
@ -216,7 +212,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
private fun addOtherBuildingChoice() {
|
||||
val otherBuilding = buildableNotWonders
|
||||
.filter { Automation.allowSpendingResource(civInfo, it) }.minByOrNull { it.cost }
|
||||
.filter { Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }.minByOrNull { it.cost }
|
||||
if (otherBuilding != null) {
|
||||
val modifier = 0.6f
|
||||
addChoice(relativeCostEffectiveness, otherBuilding.name, modifier)
|
||||
@ -256,7 +252,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
if (!buildableWonders.any()) return
|
||||
|
||||
val highestPriorityWonder = buildableWonders
|
||||
.filter { Automation.allowSpendingResource(civInfo, it) }
|
||||
.filter { Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.maxByOrNull { getWonderPriority(it) }!!
|
||||
val citiesBuildingWonders = civInfo.cities
|
||||
.count { it.cityConstructions.isBuildingWonder() }
|
||||
@ -269,7 +265,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
private fun addUnitTrainingBuildingChoice() {
|
||||
val unitTrainingBuilding = buildableNotWonders.asSequence()
|
||||
.filter { it.hasUnique(UniqueType.UnitStartingExperience)
|
||||
&& Automation.allowSpendingResource(civInfo, it)}.minByOrNull { it.cost }
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }.minByOrNull { it.cost }
|
||||
if (unitTrainingBuilding != null && (!civInfo.wantsToFocusOn(Focus.Culture) || isAtWar)) {
|
||||
var modifier = if (cityIsOverAverageProduction) 0.5f else 0.1f // You shouldn't be cranking out units anytime soon
|
||||
if (isAtWar) modifier *= 2
|
||||
@ -282,7 +278,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
private fun addDefenceBuildingChoice() {
|
||||
val defensiveBuilding = buildableNotWonders.asSequence()
|
||||
.filter { it.cityStrength > 0
|
||||
&& Automation.allowSpendingResource(civInfo, it)}.minByOrNull { it.cost }
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it)}.minByOrNull { it.cost }
|
||||
if (defensiveBuilding != null && (isAtWar || !civInfo.wantsToFocusOn(Victory.Focus.Culture))) {
|
||||
var modifier = 0.2f
|
||||
if (isAtWar) modifier = 0.5f
|
||||
@ -300,7 +296,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
val happinessBuilding = buildableNotWonders.asSequence()
|
||||
.filter { (it.isStatRelated(Stat.Happiness)
|
||||
|| it.uniques.contains("Remove extra unhappiness from annexed cities"))
|
||||
&& Automation.allowSpendingResource(civInfo, it)}
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.minByOrNull { it.cost }
|
||||
if (happinessBuilding != null) {
|
||||
var modifier = 1f
|
||||
@ -315,7 +311,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
if (allTechsAreResearched) return
|
||||
val scienceBuilding = buildableNotWonders.asSequence()
|
||||
.filter { it.isStatRelated(Stat.Science)
|
||||
&& Automation.allowSpendingResource(civInfo, it)}
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.minByOrNull { it.cost }
|
||||
if (scienceBuilding != null) {
|
||||
var modifier = 1.1f
|
||||
@ -327,7 +323,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
private fun addGoldBuildingChoice() {
|
||||
val goldBuilding = buildableNotWonders.asSequence().filter { it.isStatRelated(Stat.Gold)
|
||||
&& Automation.allowSpendingResource(civInfo, it)}
|
||||
&& Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.minByOrNull { it.cost }
|
||||
if (goldBuilding != null) {
|
||||
val modifier = if (civInfo.statsForNextTurn.gold < 0) 3f else 1.2f
|
||||
@ -337,7 +333,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
|
||||
private fun addProductionBuildingChoice() {
|
||||
val productionBuilding = buildableNotWonders.asSequence()
|
||||
.filter { it.isStatRelated(Stat.Production) && Automation.allowSpendingResource(civInfo, it) }
|
||||
.filter { it.isStatRelated(Stat.Production) && Automation.allowAutomatedConstruction(civInfo, cityInfo, it) }
|
||||
.minByOrNull { it.cost }
|
||||
if (productionBuilding != null) {
|
||||
addChoice(relativeCostEffectiveness, productionBuilding.name, 1.5f)
|
||||
@ -350,7 +346,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){
|
||||
.filter {
|
||||
(it.isStatRelated(Stat.Food)
|
||||
|| it.hasUnique(UniqueType.CarryOverFood, conditionalState)
|
||||
) && Automation.allowSpendingResource(civInfo, it)
|
||||
) && Automation.allowAutomatedConstruction(civInfo, cityInfo, it)
|
||||
}.minByOrNull { it.cost }
|
||||
if (foodBuilding != null) {
|
||||
var modifier = 1f
|
||||
|
@ -736,9 +736,7 @@ object Battle {
|
||||
if (tile.getTileImprovement()!!.hasUnique(UniqueType.Unpillagable)) {
|
||||
tile.improvement = null
|
||||
} else {
|
||||
tile.turnsToImprovement = 2
|
||||
tile.improvementInProgress = tile.improvement
|
||||
tile.improvement = null
|
||||
tile.setPillaged()
|
||||
}
|
||||
}
|
||||
tile.roadStatus = RoadStatus.None
|
||||
|
@ -1,9 +1,13 @@
|
||||
package com.unciv.logic.city
|
||||
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.automation.Automation
|
||||
import com.unciv.logic.automation.ConstructionAutomation
|
||||
import com.unciv.logic.civilization.AlertType
|
||||
import com.unciv.logic.civilization.NotificationIcon
|
||||
import com.unciv.logic.civilization.PopupAlert
|
||||
import com.unciv.logic.map.MapUnit // for Kdoc only
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
import com.unciv.models.ruleset.unique.LocalUniqueCache
|
||||
@ -19,11 +23,13 @@ import com.unciv.ui.civilopedia.FormattedLine
|
||||
import com.unciv.ui.utils.Fonts
|
||||
import com.unciv.ui.utils.withItem
|
||||
import com.unciv.ui.utils.withoutItem
|
||||
import com.unciv.ui.worldscreen.unit.UnitActions // for Kdoc only
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
/**
|
||||
* City constructions manager.
|
||||
*
|
||||
@ -33,6 +39,7 @@ import kotlin.math.roundToInt
|
||||
* @property constructionQueue a list of constructions names enqueued
|
||||
*/
|
||||
class CityConstructions {
|
||||
//region Non-Serialized Properties
|
||||
@Transient
|
||||
lateinit var cityInfo: CityInfo
|
||||
|
||||
@ -42,8 +49,7 @@ class CityConstructions {
|
||||
@Transient
|
||||
val builtBuildingUniqueMap = UniqueMap()
|
||||
|
||||
var builtBuildings = HashSet<String>()
|
||||
val inProgressConstructions = HashMap<String, Int>()
|
||||
// No backing field, not serialized
|
||||
var currentConstructionFromQueue: String
|
||||
get() {
|
||||
return if (constructionQueue.isEmpty()) ""
|
||||
@ -52,15 +58,23 @@ class CityConstructions {
|
||||
set(value) {
|
||||
if (constructionQueue.isEmpty()) constructionQueue.add(value) else constructionQueue[0] = value
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region Serialized Fields
|
||||
|
||||
var builtBuildings = HashSet<String>()
|
||||
val inProgressConstructions = HashMap<String, Int>()
|
||||
var currentConstructionIsUserSet = false
|
||||
var constructionQueue = mutableListOf<String>()
|
||||
var productionOverflow = 0
|
||||
val queueMaxSize = 10
|
||||
private val queueMaxSize = 10
|
||||
|
||||
// Maps cities to the buildings they received
|
||||
val freeBuildingsProvidedFromThisCity: HashMap<String, HashSet<String>> = hashMapOf()
|
||||
|
||||
//endregion
|
||||
//region pure functions
|
||||
|
||||
fun clone(): CityConstructions {
|
||||
val toReturn = CityConstructions()
|
||||
toReturn.builtBuildings.addAll(builtBuildings)
|
||||
@ -111,49 +125,14 @@ class CityConstructions {
|
||||
fun getCityProductionTextForCityButton(): String {
|
||||
val currentConstructionSnapshot = currentConstructionFromQueue // See below
|
||||
var result = currentConstructionSnapshot.tr()
|
||||
if (currentConstructionSnapshot != "") {
|
||||
if (currentConstructionSnapshot.isNotEmpty()) {
|
||||
val construction = PerpetualConstruction.perpetualConstructionsMap[currentConstructionSnapshot]
|
||||
if (construction == null) result += getTurnsToConstructionString(currentConstructionSnapshot)
|
||||
else result += construction.getProductionTooltip(cityInfo)
|
||||
result += construction?.getProductionTooltip(cityInfo)
|
||||
?: getTurnsToConstructionString(currentConstructionSnapshot)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun addFreeBuildings() {
|
||||
// "Gain a free [buildingName] [cityFilter]"
|
||||
val freeBuildingUniques = cityInfo.getLocalMatchingUniques(UniqueType.GainFreeBuildings, StateForConditionals(cityInfo.civInfo, cityInfo))
|
||||
|
||||
for (unique in freeBuildingUniques) {
|
||||
val freeBuildingName = cityInfo.civInfo.getEquivalentBuilding(unique.params[0]).name
|
||||
val citiesThatApply = when (unique.params[1]) {
|
||||
"in this city" -> listOf(cityInfo)
|
||||
"in other cities" -> cityInfo.civInfo.cities.filter { it !== cityInfo }
|
||||
else -> cityInfo.civInfo.cities.filter { it.matchesFilter(unique.params[1]) }
|
||||
}
|
||||
|
||||
for (city in citiesThatApply) {
|
||||
if (city.cityConstructions.containsBuildingOrEquivalent(freeBuildingName)) continue
|
||||
city.cityConstructions.addBuilding(freeBuildingName)
|
||||
if (city.id !in freeBuildingsProvidedFromThisCity)
|
||||
freeBuildingsProvidedFromThisCity[city.id] = hashSetOf()
|
||||
|
||||
freeBuildingsProvidedFromThisCity[city.id]!!.add(freeBuildingName)
|
||||
}
|
||||
}
|
||||
|
||||
// Civ-level uniques - for these only add free buildings from each city to itself to avoid weirdness on city conquest
|
||||
for (unique in cityInfo.civInfo.getMatchingUniques(UniqueType.GainFreeBuildings, stateForConditionals = StateForConditionals(cityInfo.civInfo, cityInfo))) {
|
||||
val freeBuildingName = cityInfo.civInfo.getEquivalentBuilding(unique.params[0]).name
|
||||
if (cityInfo.matchesFilter(unique.params[1])) {
|
||||
if (cityInfo.id !in freeBuildingsProvidedFromThisCity)
|
||||
freeBuildingsProvidedFromThisCity[cityInfo.id] = hashSetOf()
|
||||
freeBuildingsProvidedFromThisCity[cityInfo.id]!!.add(freeBuildingName)
|
||||
if (!isBuilt(freeBuildingName))
|
||||
addBuilding(freeBuildingName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @constructionName needs to be a non-perpetual construction, else an empty string is returned */
|
||||
internal fun getTurnsToConstructionString(constructionName: String, useStoredProduction:Boolean = true): String {
|
||||
val construction = getConstruction(constructionName)
|
||||
@ -284,9 +263,17 @@ class CityConstructions {
|
||||
|
||||
return ceil((workLeft-productionOverflow) / production.toDouble()).toInt()
|
||||
}
|
||||
//endregion
|
||||
|
||||
fun hasBuildableStatBuildings(stat: Stat): Boolean {
|
||||
return getBasicStatBuildings(stat)
|
||||
.map { cityInfo.civInfo.getEquivalentBuilding(it.name) }
|
||||
.filter { it.isBuildable(this) || isBeingConstructedOrEnqueued(it.name) }
|
||||
.any()
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region state changing functions
|
||||
|
||||
fun setTransients() {
|
||||
builtBuildingObjects = ArrayList(builtBuildings.map {
|
||||
cityInfo.getRuleset().buildings[it]
|
||||
@ -469,6 +456,41 @@ class CityConstructions {
|
||||
builtBuildingUniqueMap.addUnique(unique)
|
||||
}
|
||||
|
||||
fun addFreeBuildings() {
|
||||
// "Gain a free [buildingName] [cityFilter]"
|
||||
val freeBuildingUniques = cityInfo.getLocalMatchingUniques(UniqueType.GainFreeBuildings, StateForConditionals(cityInfo.civInfo, cityInfo))
|
||||
|
||||
for (unique in freeBuildingUniques) {
|
||||
val freeBuildingName = cityInfo.civInfo.getEquivalentBuilding(unique.params[0]).name
|
||||
val citiesThatApply = when (unique.params[1]) {
|
||||
"in this city" -> listOf(cityInfo)
|
||||
"in other cities" -> cityInfo.civInfo.cities.filter { it !== cityInfo }
|
||||
else -> cityInfo.civInfo.cities.filter { it.matchesFilter(unique.params[1]) }
|
||||
}
|
||||
|
||||
for (city in citiesThatApply) {
|
||||
if (city.cityConstructions.containsBuildingOrEquivalent(freeBuildingName)) continue
|
||||
city.cityConstructions.addBuilding(freeBuildingName)
|
||||
if (city.id !in freeBuildingsProvidedFromThisCity)
|
||||
freeBuildingsProvidedFromThisCity[city.id] = hashSetOf()
|
||||
|
||||
freeBuildingsProvidedFromThisCity[city.id]!!.add(freeBuildingName)
|
||||
}
|
||||
}
|
||||
|
||||
// Civ-level uniques - for these only add free buildings from each city to itself to avoid weirdness on city conquest
|
||||
for (unique in cityInfo.civInfo.getMatchingUniques(UniqueType.GainFreeBuildings, stateForConditionals = StateForConditionals(cityInfo.civInfo, cityInfo))) {
|
||||
val freeBuildingName = cityInfo.civInfo.getEquivalentBuilding(unique.params[0]).name
|
||||
if (cityInfo.matchesFilter(unique.params[1])) {
|
||||
if (cityInfo.id !in freeBuildingsProvidedFromThisCity)
|
||||
freeBuildingsProvidedFromThisCity[cityInfo.id] = hashSetOf()
|
||||
freeBuildingsProvidedFromThisCity[cityInfo.id]!!.add(freeBuildingName)
|
||||
if (!isBuilt(freeBuildingName))
|
||||
addBuilding(freeBuildingName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purchase a construction for gold
|
||||
* called from NextTurnAutomation and the City UI
|
||||
@ -480,22 +502,36 @@ class CityConstructions {
|
||||
* @param automatic Flag whether automation should try to choose what next to build (not coming from UI)
|
||||
* Note: settings.autoAssignCityProduction is handled later
|
||||
* @param stat Stat object of the stat with which was paid for the construction
|
||||
* @return Success (false e.g. unit cannot be placed
|
||||
* @param tile Supports [UniqueType.CreatesOneImprovement] the tile to place the improvement from that unique on.
|
||||
* Ignored when the [constructionName] does not have that unique. If null and the building has the unique, a tile is chosen automatically.
|
||||
* @return Success (false e.g. unit cannot be placed)
|
||||
*/
|
||||
fun purchaseConstruction(
|
||||
constructionName: String,
|
||||
queuePosition: Int,
|
||||
automatic: Boolean,
|
||||
stat: Stat = Stat.Gold
|
||||
stat: Stat = Stat.Gold,
|
||||
tile: TileInfo? = null
|
||||
): Boolean {
|
||||
if (!(getConstruction(constructionName) as INonPerpetualConstruction).postBuildEvent(this, stat))
|
||||
val construction = getConstruction(constructionName) as? INonPerpetualConstruction ?: return false
|
||||
|
||||
// Support UniqueType.CreatesOneImprovement: it is active when getImprovementToCreate returns an improvement
|
||||
val improvementToPlace = (construction as? Building)?.getImprovementToCreate(cityInfo.getRuleset())
|
||||
if (improvementToPlace != null) {
|
||||
// If active without a predetermined tile to place the improvement on, automate a tile
|
||||
val finalTile = tile
|
||||
?: Automation.getTileForConstructionImprovement(cityInfo, improvementToPlace)
|
||||
?: return false // This was never reached in testing
|
||||
finalTile.markForCreatesOneImprovement(improvementToPlace.name)
|
||||
// postBuildEvent does the rest by calling cityConstructions.applyCreateOneImprovement
|
||||
}
|
||||
|
||||
if (!construction.postBuildEvent(this, stat))
|
||||
return false // nothing built - no pay
|
||||
|
||||
if (!cityInfo.civInfo.gameInfo.gameParameters.godMode) {
|
||||
val construction = getConstruction(constructionName)
|
||||
if (construction is PerpetualConstruction) return false
|
||||
val constructionCost = (construction as INonPerpetualConstruction).getStatBuyCost(cityInfo, stat)
|
||||
if (constructionCost == null) return false // We should never end up here anyway, so things have already gone _way_ wrong
|
||||
val constructionCost = construction.getStatBuyCost(cityInfo, stat)
|
||||
?: return false // We should never end up here anyway, so things have already gone _way_ wrong
|
||||
cityInfo.addStat(stat, -1 * constructionCost)
|
||||
|
||||
val conditionalState = StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo)
|
||||
@ -523,21 +559,12 @@ class CityConstructions {
|
||||
return true
|
||||
}
|
||||
|
||||
fun hasBuildableStatBuildings(stat: Stat): Boolean {
|
||||
return getBasicStatBuildings(stat)
|
||||
.map { cityInfo.civInfo.getEquivalentBuilding(it.name) }
|
||||
.filter { it.isBuildable(this) || isBeingConstructedOrEnqueued(it.name) }
|
||||
.any()
|
||||
}
|
||||
|
||||
fun addCheapestBuildableStatBuilding(stat: Stat): String? {
|
||||
val cheapestBuildableStatBuilding = getBasicStatBuildings(stat)
|
||||
.map { cityInfo.civInfo.getEquivalentBuilding(it.name) }
|
||||
.filter { it.isBuildable(this) || isBeingConstructedOrEnqueued(it.name) }
|
||||
.minByOrNull { it.cost }?.name
|
||||
|
||||
if (cheapestBuildableStatBuilding == null)
|
||||
return null
|
||||
?: return null
|
||||
|
||||
constructionComplete(getConstruction(cheapestBuildableStatBuilding) as INonPerpetualConstruction)
|
||||
|
||||
@ -555,6 +582,13 @@ class CityConstructions {
|
||||
}
|
||||
|
||||
ConstructionAutomation(this).chooseNextConstruction()
|
||||
|
||||
/** Support for [UniqueType.CreatesOneImprovement] - if an Improvement-creating Building was auto-queued, auto-choose a tile: */
|
||||
val building = getCurrentConstruction() as? Building ?: return
|
||||
val improvement = building.getImprovementToCreate(cityInfo.getRuleset()) ?: return
|
||||
if (getTileForImprovement(improvement.name) != null) return
|
||||
val newTile = Automation.getTileForConstructionImprovement(cityInfo, improvement) ?: return
|
||||
newTile.markForCreatesOneImprovement(improvement.name)
|
||||
}
|
||||
|
||||
fun addToQueue(constructionName: String) {
|
||||
@ -578,13 +612,13 @@ 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) {
|
||||
val constructionName = constructionQueue.removeAt(constructionQueueIndex)
|
||||
|
||||
// UniqueType.CreatesOneImprovement support
|
||||
val construction = getConstruction(constructionName)
|
||||
if (construction is Building) {
|
||||
val improvement = construction.getImprovement(cityInfo.getRuleset())
|
||||
val improvement = construction.getImprovementToCreate(cityInfo.getRuleset())
|
||||
if (improvement != null) {
|
||||
val tileWithImprovement = cityInfo.getTiles().firstOrNull { it.improvementInProgress == improvement.name }
|
||||
tileWithImprovement?.improvementInProgress = null
|
||||
tileWithImprovement?.turnsToImprovement = 0
|
||||
getTileForImprovement(improvement.name)?.stopWorkingOnImprovement()
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,10 +638,61 @@ class CityConstructions {
|
||||
raisePriority(constructionQueueIndex + 1)
|
||||
}
|
||||
|
||||
//endregion
|
||||
private fun MutableList<String>.swap(idx1: Int, idx2: Int) {
|
||||
val tmp = this[idx1]
|
||||
this[idx1] = this[idx2]
|
||||
this[idx2] = tmp
|
||||
}
|
||||
|
||||
/** Support for [UniqueType.CreatesOneImprovement]:
|
||||
*
|
||||
* If [building] is an improvement-creating one, find a marked tile matching the improvement to be created
|
||||
* (skip if none found), then un-mark the tile and place the improvement unless [removeOnly] is set.
|
||||
*/
|
||||
fun applyCreateOneImprovement(building: Building, removeOnly: Boolean = false) {
|
||||
val improvement = building.getImprovementToCreate(cityInfo.getRuleset())
|
||||
?: return
|
||||
val tileForImprovement = getTileForImprovement(improvement.name) ?: return
|
||||
tileForImprovement.stopWorkingOnImprovement() // clears mark
|
||||
if (removeOnly) return
|
||||
/**todo unify with [UnitActions.getImprovementConstructionActions] and [MapUnit.workOnImprovement] - this won't allow e.g. a building to place a road */
|
||||
tileForImprovement.improvement = improvement.name
|
||||
cityInfo.civInfo.lastSeenImprovement[tileForImprovement.position] = improvement.name
|
||||
cityInfo.cityStats.update()
|
||||
cityInfo.civInfo.updateDetailedCivResources()
|
||||
// If bought the worldscreen will not have been marked to update, and the new improvement won't show until later...
|
||||
if (UncivGame.isCurrentInitialized())
|
||||
UncivGame.Current.worldScreen.shouldUpdate = true
|
||||
}
|
||||
|
||||
/** Support for [UniqueType.CreatesOneImprovement]:
|
||||
*
|
||||
* To be called after circumstances forced clearing a marker from a tile (pillaging, nuking).
|
||||
* Should remove one matching building from the queue without looking for a marked tile.
|
||||
*/
|
||||
fun removeCreateOneImprovementConstruction(improvement: String) {
|
||||
val ruleset = cityInfo.getRuleset()
|
||||
val indexToRemove = constructionQueue.withIndex().mapNotNull {
|
||||
val construction = getConstruction(it.value)
|
||||
val buildingImprovement = (construction as? Building)?.getImprovementToCreate(ruleset)?.name
|
||||
it.index.takeIf { buildingImprovement == improvement }
|
||||
}.firstOrNull() ?: return
|
||||
|
||||
constructionQueue.removeAt(indexToRemove)
|
||||
|
||||
if (constructionQueue.isEmpty()) {
|
||||
constructionQueue.add("Nothing")
|
||||
currentConstructionIsUserSet = false
|
||||
} else currentConstructionIsUserSet = true
|
||||
}
|
||||
|
||||
/** Support for [UniqueType.CreatesOneImprovement]:
|
||||
*
|
||||
* Find the selected tile for a specific improvement being constructed via a building, if any.
|
||||
*/
|
||||
fun getTileForImprovement(improvementName: String) = cityInfo.getTiles()
|
||||
.firstOrNull {
|
||||
it.isMarkedForCreatesOneImprovement(improvementName)
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
|
@ -129,6 +129,8 @@ class CityExpansionManager {
|
||||
city.lockedTiles.remove(tileInfo.position)
|
||||
}
|
||||
|
||||
tileInfo.removeCreatesOneImprovementMarker()
|
||||
|
||||
tileInfo.setOwningCity(null)
|
||||
|
||||
cityInfo.civInfo.updateDetailedCivResources()
|
||||
|
@ -696,7 +696,9 @@ class CityInfo {
|
||||
|
||||
// The relinquish ownership MUST come before removing the city,
|
||||
// because it updates the city stats which assumes there is a capital, so if you remove the capital it crashes
|
||||
getTiles().forEach { expansion.relinquishOwnership(it) }
|
||||
for (tile in getTiles()) {
|
||||
expansion.relinquishOwnership(tile)
|
||||
}
|
||||
civInfo.cities = civInfo.cities.toMutableList().apply { remove(this@CityInfo) }
|
||||
getCenterTile().improvement = "City ruins"
|
||||
|
||||
|
@ -449,9 +449,11 @@ class MapUnit {
|
||||
|
||||
fun isIdle(): Boolean {
|
||||
if (currentMovement == 0f) return false
|
||||
if (getTile().improvementInProgress != null
|
||||
&& canBuildImprovement(getTile().getTileImprovementInProgress()!!))
|
||||
return false
|
||||
val tile = getTile()
|
||||
if (tile.improvementInProgress != null &&
|
||||
canBuildImprovement(tile.getTileImprovementInProgress()!!) &&
|
||||
!tile.isMarkedForCreatesOneImprovement()
|
||||
) return false
|
||||
return !(isFortified() || isExploring() || isSleeping() || isAutomated() || isMoving())
|
||||
}
|
||||
|
||||
@ -625,6 +627,7 @@ class MapUnit {
|
||||
|
||||
private fun workOnImprovement() {
|
||||
val tile = getTile()
|
||||
if (tile.isMarkedForCreatesOneImprovement()) return
|
||||
tile.turnsToImprovement -= 1
|
||||
if (tile.turnsToImprovement != 0) return
|
||||
|
||||
|
@ -120,6 +120,8 @@ open class TileInfo {
|
||||
return toReturn
|
||||
}
|
||||
|
||||
//region pure functions
|
||||
|
||||
fun containsGreatImprovement(): Boolean {
|
||||
return getTileImprovement()?.isGreatImprovement() == true
|
||||
}
|
||||
@ -128,7 +130,6 @@ open class TileInfo {
|
||||
if (improvementInProgress == null) return false
|
||||
return ruleset.tileImprovements[improvementInProgress!!]!!.isGreatImprovement()
|
||||
}
|
||||
//region pure functions
|
||||
|
||||
/** Returns military, civilian and air units in tile */
|
||||
fun getUnits() = sequence {
|
||||
@ -592,7 +593,7 @@ open class TileInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fun hasImprovementInProgress() = improvementInProgress != null
|
||||
fun hasImprovementInProgress() = improvementInProgress != null && turnsToImprovement > 0
|
||||
|
||||
@delegate:Transient
|
||||
private val _isCoastalTile: Boolean by lazy { neighbors.any { it.baseTerrain == Constants.coast } }
|
||||
@ -714,6 +715,7 @@ open class TileInfo {
|
||||
return true
|
||||
}
|
||||
|
||||
/** Get info on a selected tile, used on WorldScreen (right side above minimap), CityScreen or MapEditorViewTab. */
|
||||
fun toMarkup(viewingCiv: CivilizationInfo?): ArrayList<FormattedLine> {
|
||||
val lineList = ArrayList<FormattedLine>()
|
||||
val isViewableToPlayer = viewingCiv == null || UncivGame.Current.viewEntireMapForDebug
|
||||
@ -727,6 +729,7 @@ open class TileInfo {
|
||||
if (UncivGame.Current.viewEntireMapForDebug || city.civInfo == viewingCiv)
|
||||
lineList += city.cityConstructions.getProductionMarkup(ruleset)
|
||||
}
|
||||
|
||||
lineList += FormattedLine(baseTerrain, link="Terrain/$baseTerrain")
|
||||
for (terrainFeature in terrainFeatures)
|
||||
lineList += FormattedLine(terrainFeature, link="Terrain/$terrainFeature")
|
||||
@ -753,11 +756,14 @@ open class TileInfo {
|
||||
val shownImprovement = getShownImprovement(viewingCiv)
|
||||
if (shownImprovement != null)
|
||||
lineList += FormattedLine(shownImprovement, link="Improvement/$shownImprovement")
|
||||
|
||||
if (improvementInProgress != null && isViewableToPlayer) {
|
||||
// Negative turnsToImprovement is used for UniqueType.CreatesOneImprovement
|
||||
val line = "{$improvementInProgress}" +
|
||||
if (turnsToImprovement > 0) " - $turnsToImprovement${Fonts.turn}" else " ({Under construction})"
|
||||
lineList += FormattedLine(line, link="Improvement/$improvementInProgress")
|
||||
}
|
||||
|
||||
if (civilianUnit != null && isViewableToPlayer)
|
||||
lineList += FormattedLine(civilianUnit!!.name.tr() + " - " + civilianUnit!!.civInfo.civName.tr(),
|
||||
link="Unit/${civilianUnit!!.name}")
|
||||
@ -767,6 +773,7 @@ open class TileInfo {
|
||||
" - " + militaryUnit!!.civInfo.civName.tr()
|
||||
lineList += FormattedLine(milUnitString, link="Unit/${militaryUnit!!.name}")
|
||||
}
|
||||
|
||||
val defenceBonus = getDefensiveBonus()
|
||||
if (defenceBonus != 0f) {
|
||||
var defencePercentString = (defenceBonus * 100).toInt().toString() + "%"
|
||||
@ -815,9 +822,16 @@ open class TileInfo {
|
||||
|
||||
fun getContinent() = continent
|
||||
|
||||
//endregion
|
||||
/** Checks if this tile is marked as target tile for a building with a [UniqueType.CreatesOneImprovement] unique */
|
||||
fun isMarkedForCreatesOneImprovement() =
|
||||
turnsToImprovement < 0 && improvementInProgress != null
|
||||
/** Checks if this tile is marked as target tile for a building with a [UniqueType.CreatesOneImprovement] unique creating a specific [improvement] */
|
||||
fun isMarkedForCreatesOneImprovement(improvement: String) =
|
||||
turnsToImprovement < 0 && improvementInProgress == improvement
|
||||
|
||||
//endregion
|
||||
//region state-changing functions
|
||||
|
||||
fun setTransients() {
|
||||
setTerrainTransients()
|
||||
setUnitTransients(true)
|
||||
@ -914,11 +928,39 @@ open class TileInfo {
|
||||
else improvement.getTurnsToBuild(civInfo, unit)
|
||||
}
|
||||
|
||||
/** Clears [improvementInProgress] and [turnsToImprovement] */
|
||||
fun stopWorkingOnImprovement() {
|
||||
improvementInProgress = null
|
||||
turnsToImprovement = 0
|
||||
}
|
||||
|
||||
/** Sets tile improvement to pillaged (without prior checks for validity)
|
||||
* and ensures that matching [UniqueType.CreatesOneImprovement] queued buildings are removed. */
|
||||
fun setPillaged() {
|
||||
// http://well-of-souls.com/civ/civ5_improvements.html says that naval improvements are destroyed upon pillage
|
||||
// and I can't find any other sources so I'll go with that
|
||||
if (isLand) {
|
||||
// Setting turnsToImprovement might interfere with UniqueType.CreatesOneImprovement
|
||||
removeCreatesOneImprovementMarker()
|
||||
improvementInProgress = improvement
|
||||
turnsToImprovement = 2
|
||||
}
|
||||
improvement = null
|
||||
}
|
||||
|
||||
/** Marks tile as target tile for a building with a [UniqueType.CreatesOneImprovement] unique */
|
||||
fun markForCreatesOneImprovement(improvement: String) {
|
||||
improvementInProgress = improvement
|
||||
turnsToImprovement = -1
|
||||
}
|
||||
/** Un-Marks a tile as target tile for a building with a [UniqueType.CreatesOneImprovement] unique,
|
||||
* and ensures that matching queued buildings are removed. */
|
||||
fun removeCreatesOneImprovementMarker() {
|
||||
if (!isMarkedForCreatesOneImprovement()) return
|
||||
owningCity?.cityConstructions?.removeCreateOneImprovementConstruction(improvementInProgress!!)
|
||||
stopWorkingOnImprovement()
|
||||
}
|
||||
|
||||
fun normalizeToRuleset(ruleset: Ruleset) {
|
||||
if (naturalWonder != null && !ruleset.terrains.containsKey(naturalWonder))
|
||||
naturalWonder = null
|
||||
|
@ -683,15 +683,8 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
/** Support for [UniqueType.CreatesOneImprovement] */
|
||||
cityConstructions.applyCreateOneImprovement(this)
|
||||
|
||||
// "Provides a free [buildingName] [cityFilter]"
|
||||
cityConstructions.addFreeBuildings()
|
||||
@ -741,10 +734,20 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
|
||||
return false
|
||||
}
|
||||
|
||||
fun getImprovement(ruleset: Ruleset): TileImprovement? {
|
||||
val improvementUnique = getMatchingUniques("Creates a [] improvement on a specific tile")
|
||||
private val _hasCreatesOneImprovementUnique by lazy {
|
||||
hasUnique(UniqueType.CreatesOneImprovement)
|
||||
}
|
||||
fun hasCreateOneImprovementUnique() = _hasCreatesOneImprovementUnique
|
||||
|
||||
private var _getImprovementToCreate: TileImprovement? = null
|
||||
fun getImprovementToCreate(ruleset: Ruleset): TileImprovement? {
|
||||
if (!hasCreateOneImprovementUnique()) return null
|
||||
if (_getImprovementToCreate == null) {
|
||||
val improvementUnique = getMatchingUniques(UniqueType.CreatesOneImprovement)
|
||||
.firstOrNull() ?: return null
|
||||
return ruleset.tileImprovements[improvementUnique.params[0]]
|
||||
_getImprovementToCreate = ruleset.tileImprovements[improvementUnique.params[0]]
|
||||
}
|
||||
return _getImprovementToCreate
|
||||
}
|
||||
|
||||
fun isSellable() = !isAnyWonder() && !hasUnique(UniqueType.Unsellable)
|
||||
|
@ -657,9 +657,6 @@ class Ruleset {
|
||||
if (building.requiredBuildingInAllCities != null)
|
||||
lines.add("${building.name} contains 'requiredBuildingInAllCities' - please convert to a \"" +
|
||||
UniqueType.RequiresBuildingInAllCities.text.fillPlaceholders(building.requiredBuildingInAllCities!!)+"\" unique", RulesetErrorSeverity.Warning)
|
||||
for (unique in building.getMatchingUniques("Creates a [] improvement on a specific tile"))
|
||||
if (!tileImprovements.containsKey(unique.params[0]))
|
||||
lines += "${building.name} creates a ${unique.params[0]} improvement which does not exist!"
|
||||
checkUniques(building, lines, rulesetSpecific, forOptionsPopup)
|
||||
}
|
||||
|
||||
|
@ -364,10 +364,12 @@ enum class UniqueParameterType(
|
||||
}
|
||||
},
|
||||
|
||||
/** [UniqueType.ConstructImprovementConsumingUnit] */
|
||||
/** For [UniqueType.ConstructImprovementConsumingUnit], [UniqueType.CreatesOneImprovement] */
|
||||
ImprovementName("improvementName", "Trading Post", "The name of any improvement"){
|
||||
override fun getErrorSeverity(parameterText: String,ruleset: Ruleset):
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueComplianceErrorSeverity? {
|
||||
if (parameterText == Constants.cancelImprovementOrder)
|
||||
return UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant
|
||||
if (ruleset.tileImprovements.containsKey(parameterText)) return null
|
||||
return UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
|
||||
}
|
||||
|
@ -390,10 +390,10 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
||||
NotDestroyedWhenCityCaptured("Never destroyed when the city is captured", UniqueTarget.Building),
|
||||
DoublesGoldFromCapturingCity("Doubles Gold given to enemy if city is captured", UniqueTarget.Building),
|
||||
|
||||
|
||||
RemoveAnnexUnhappiness("Remove extra unhappiness from annexed cities", UniqueTarget.Building),
|
||||
ConnectTradeRoutes("Connects trade routes over water", UniqueTarget.Building),
|
||||
|
||||
CreatesOneImprovement("Creates a [improvementName] improvement on a specific tile", UniqueTarget.Building),
|
||||
//endregion
|
||||
|
||||
|
||||
@ -614,6 +614,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
|
||||
DefensiveBonus("Gives a defensive bonus of [relativeAmount]%", UniqueTarget.Improvement),
|
||||
ImprovementMaintenance("Costs [amount] gold per turn when in your territory", UniqueTarget.Improvement), // Unused
|
||||
DamagesAdjacentEnemyUnits("Adjacent enemy units ending their turn take [amount] damage", UniqueTarget.Improvement),
|
||||
TakeOverTilesAroundWhenBuilt("Constructing it will take over the tiles around it and assign them to your closest city", UniqueTarget.Improvement),
|
||||
|
||||
GreatImprovement("Great Improvement", UniqueTarget.Improvement),
|
||||
IsAncientRuinsEquivalent("Provides a random bonus when entered", UniqueTarget.Improvement),
|
||||
|
@ -9,8 +9,10 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.city.*
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.UncivSound
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.tr
|
||||
@ -37,7 +39,6 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
/* -1 = Nothing, >= 0 queue entry (0 = current construction) */
|
||||
private var selectedQueueEntry = -1 // None
|
||||
private var preferredBuyStat = Stat.Gold // Used for keyboard buy
|
||||
var improvementBuildingToConstruct: Building? = null
|
||||
|
||||
private val upperTable = Table(BaseScreen.skin)
|
||||
private val showCityInfoTableButton = "Show stats drilldown".toTextButton()
|
||||
@ -91,7 +92,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
/** Forces layout calculation and returns the lower Table's (available constructions) width
|
||||
* - or - the upper Table's width, whichever is greater (in case the former only contains "Loading...")
|
||||
*/
|
||||
fun getLowerWidth() = max(lowerTable.packIfNeeded().width, getUpperWidth()) //
|
||||
fun getLowerWidth() = max(lowerTable.packIfNeeded().width, getUpperWidth())
|
||||
|
||||
fun addActorsToStage() {
|
||||
cityScreen.stage.addActor(upperTable)
|
||||
@ -296,8 +297,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
|
||||
table.touchable = Touchable.enabled
|
||||
table.onClick {
|
||||
cityScreen.selectedConstruction = cityConstructions.getConstruction(constructionName)
|
||||
cityScreen.selectedTile = null
|
||||
cityScreen.selectConstruction(constructionName)
|
||||
selectedQueueEntry = constructionQueueIndex
|
||||
cityScreen.update()
|
||||
}
|
||||
@ -326,7 +326,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
pickConstructionButton.background = ImageGetter.getBackground(Color.BLACK)
|
||||
pickConstructionButton.touchable = Touchable.enabled
|
||||
|
||||
if (!isSelectedQueueEntry() && cityScreen.selectedConstruction != null && cityScreen.selectedConstruction == construction) {
|
||||
if (!isSelectedQueueEntry() && cityScreen.selectedConstruction == construction) {
|
||||
pickConstructionButton.background = ImageGetter.getBackground(Color.GREEN.darken(0.5f))
|
||||
}
|
||||
|
||||
@ -336,7 +336,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
|
||||
if (!cannotAddConstructionToQueue(construction, cityScreen.city, cityScreen.city.cityConstructions)) {
|
||||
val addToQueueButton = ImageGetter.getImage("OtherIcons/New").apply { color = Color.BLACK }.surroundWithCircle(40f)
|
||||
addToQueueButton.onClick(getConstructionSound(construction)) {
|
||||
addToQueueButton.onClick(UncivSound.Silent) {
|
||||
addConstructionToQueue(construction, cityScreen.city.cityConstructions)
|
||||
}
|
||||
pickConstructionButton.add(addToQueueButton)
|
||||
@ -350,8 +350,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
.colspan(pickConstructionButton.columns).fillX().left().padTop(2f)
|
||||
}
|
||||
pickConstructionButton.onClick {
|
||||
cityScreen.selectedConstruction = construction
|
||||
cityScreen.selectedTile = null
|
||||
cityScreen.selectConstruction(construction)
|
||||
selectedQueueEntry = -1
|
||||
cityScreen.update()
|
||||
}
|
||||
@ -381,7 +380,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
else {
|
||||
button.onClick {
|
||||
cityConstructions.removeFromQueue(selectedQueueEntry, false)
|
||||
cityScreen.selectedConstruction = null
|
||||
cityScreen.clearSelection()
|
||||
selectedQueueEntry = -1
|
||||
cityScreen.update()
|
||||
}
|
||||
@ -392,7 +391,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
|| cannotAddConstructionToQueue(construction, city, cityConstructions)) {
|
||||
button.disable()
|
||||
} else {
|
||||
button.onClick(getConstructionSound(construction)) {
|
||||
button.onClick(UncivSound.Silent) {
|
||||
addConstructionToQueue(construction, cityConstructions)
|
||||
}
|
||||
}
|
||||
@ -404,17 +403,21 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
|
||||
private fun addConstructionToQueue(construction: IConstruction, cityConstructions: CityConstructions) {
|
||||
// Some evil person decided to double tap real fast - #4977
|
||||
if (cannotAddConstructionToQueue(construction, cityScreen.city, cityScreen.city.cityConstructions))
|
||||
if (cannotAddConstructionToQueue(construction, cityScreen.city, cityConstructions))
|
||||
return
|
||||
if (construction is Building && construction.uniqueObjects.any { it.placeholderText == "Creates a [] improvement on a specific tile" }) {
|
||||
cityScreen.selectedTile
|
||||
improvementBuildingToConstruct = construction
|
||||
|
||||
// UniqueType.CreatesOneImprovement support - don't add yet, postpone until target tile for the improvement is selected
|
||||
if (construction is Building && construction.hasCreateOneImprovementUnique()) {
|
||||
cityScreen.startPickTileForCreatesOneImprovement(construction, Stat.Gold, false)
|
||||
return
|
||||
}
|
||||
cityScreen.stopPickTileForCreatesOneImprovement()
|
||||
|
||||
Sounds.play(getConstructionSound(construction))
|
||||
|
||||
cityConstructions.addToQueue(construction.name)
|
||||
if (!construction.shouldBeDisplayed(cityConstructions)) // For buildings - unlike units which can be queued multiple times
|
||||
cityScreen.selectedConstruction = null
|
||||
cityScreen.clearSelection()
|
||||
cityScreen.update()
|
||||
cityScreen.game.settings.addCompletedTutorialTask("Pick construction")
|
||||
}
|
||||
@ -454,7 +457,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
|
||||
button.onClick {
|
||||
button.disable()
|
||||
askToBuyConstruction(construction, stat)
|
||||
buyButtonOnClick(construction, stat)
|
||||
}
|
||||
button.isEnabled = isConstructionPurchaseAllowed(construction, stat, constructionBuyCost)
|
||||
button.addTooltip('B') // The key binding is done in CityScreen constructor
|
||||
@ -466,12 +469,28 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
return button
|
||||
}
|
||||
|
||||
private fun buyButtonOnClick(construction: INonPerpetualConstruction, stat: Stat = preferredBuyStat) {
|
||||
if (construction !is Building || !construction.hasCreateOneImprovementUnique())
|
||||
return askToBuyConstruction(construction, stat)
|
||||
if (selectedQueueEntry < 0)
|
||||
return cityScreen.startPickTileForCreatesOneImprovement(construction, stat, true)
|
||||
// Buying a UniqueType.CreatesOneImprovement building from queue must pass down
|
||||
// the already selected tile, otherwise a new one is chosen from Automation code.
|
||||
val improvement = construction.getImprovementToCreate(cityScreen.city.getRuleset())!!
|
||||
val tileForImprovement = cityScreen.city.cityConstructions.getTileForImprovement(improvement.name)
|
||||
askToBuyConstruction(construction, stat, tileForImprovement)
|
||||
}
|
||||
|
||||
/** Ask whether user wants to buy [construction] for [stat].
|
||||
*
|
||||
* Used from onClick and keyboard dispatch, thus only minimal parameters are passed,
|
||||
* and it needs to do all checks and the sound as appropriate.
|
||||
*/
|
||||
fun askToBuyConstruction(construction: INonPerpetualConstruction, stat: Stat = preferredBuyStat) {
|
||||
fun askToBuyConstruction(
|
||||
construction: INonPerpetualConstruction,
|
||||
stat: Stat = preferredBuyStat,
|
||||
tile: TileInfo? = null
|
||||
) {
|
||||
if (!isConstructionPurchaseShown(construction, stat)) return
|
||||
val city = cityScreen.city
|
||||
val constructionBuyCost = construction.getStatBuyCost(city, stat)!!
|
||||
@ -483,7 +502,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
"Would you like to purchase [${construction.name}] for [$constructionBuyCost] [${stat.character}]?".tr()
|
||||
YesNoPopup(
|
||||
purchasePrompt,
|
||||
action = { purchaseConstruction(construction, stat) },
|
||||
action = { purchaseConstruction(construction, stat, tile) },
|
||||
screen = cityScreen,
|
||||
restoreDefault = { cityScreen.update() }
|
||||
).open()
|
||||
@ -510,11 +529,17 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
}
|
||||
}
|
||||
|
||||
// called only by askToBuyConstruction's Yes answer
|
||||
private fun purchaseConstruction(construction: INonPerpetualConstruction, stat: Stat = Stat.Gold) {
|
||||
/** Called only by askToBuyConstruction's Yes answer - not to be confused with [CityConstructions.purchaseConstruction]
|
||||
* @param tile supports [UniqueType.CreatesOneImprovement]
|
||||
*/
|
||||
private fun purchaseConstruction(
|
||||
construction: INonPerpetualConstruction,
|
||||
stat: Stat = Stat.Gold,
|
||||
tile: TileInfo? = null
|
||||
) {
|
||||
Sounds.play(stat.purchaseSound)
|
||||
val city = cityScreen.city
|
||||
if (!city.cityConstructions.purchaseConstruction(construction.name, selectedQueueEntry, false, stat)) {
|
||||
if (!city.cityConstructions.purchaseConstruction(construction.name, selectedQueueEntry, false, stat, tile)) {
|
||||
Popup(cityScreen).apply {
|
||||
add("No space available to place [${construction.name}] near [${city.name}]".tr()).row()
|
||||
addCloseButton()
|
||||
@ -524,12 +549,15 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
}
|
||||
if (isSelectedQueueEntry() || cityScreen.selectedConstruction?.isBuildable(city.cityConstructions) != true) {
|
||||
selectedQueueEntry = -1
|
||||
cityScreen.selectedConstruction = null
|
||||
cityScreen.clearSelection()
|
||||
|
||||
// Allow buying next queued or auto-assigned construction right away
|
||||
city.cityConstructions.chooseNextConstruction()
|
||||
if (city.cityConstructions.currentConstructionFromQueue.isNotEmpty())
|
||||
cityScreen.selectedConstruction = city.cityConstructions.getCurrentConstruction().takeIf { it is INonPerpetualConstruction }
|
||||
if (city.cityConstructions.currentConstructionFromQueue.isNotEmpty()) {
|
||||
val newConstruction = city.cityConstructions.getCurrentConstruction()
|
||||
if (newConstruction is INonPerpetualConstruction)
|
||||
cityScreen.selectConstruction(newConstruction)
|
||||
}
|
||||
}
|
||||
cityScreen.update()
|
||||
}
|
||||
@ -542,8 +570,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
tab.onClick {
|
||||
tab.touchable = Touchable.disabled
|
||||
city.cityConstructions.raisePriority(constructionQueueIndex)
|
||||
cityScreen.selectedConstruction = cityScreen.city.cityConstructions.getConstruction(name)
|
||||
cityScreen.selectedTile = null
|
||||
cityScreen.selectConstruction(name)
|
||||
selectedQueueEntry = constructionQueueIndex - 1
|
||||
cityScreen.update()
|
||||
}
|
||||
@ -559,8 +586,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
tab.onClick {
|
||||
tab.touchable = Touchable.disabled
|
||||
city.cityConstructions.lowerPriority(constructionQueueIndex)
|
||||
cityScreen.selectedConstruction = cityScreen.city.cityConstructions.getConstruction(name)
|
||||
cityScreen.selectedTile = null
|
||||
cityScreen.selectConstruction(name)
|
||||
selectedQueueEntry = constructionQueueIndex + 1
|
||||
cityScreen.update()
|
||||
}
|
||||
@ -576,7 +602,7 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
tab.onClick {
|
||||
tab.touchable = Touchable.disabled
|
||||
city.cityConstructions.removeFromQueue(constructionQueueIndex, false)
|
||||
cityScreen.selectedConstruction = null
|
||||
cityScreen.clearSelection()
|
||||
cityScreen.update()
|
||||
}
|
||||
}
|
||||
|
@ -6,20 +6,27 @@ import com.badlogic.gdx.scenes.scene2d.ui.Image
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table
|
||||
import com.badlogic.gdx.utils.Align
|
||||
import com.unciv.UncivGame
|
||||
import com.unciv.logic.automation.Automation
|
||||
import com.unciv.logic.city.CityInfo
|
||||
import com.unciv.logic.city.IConstruction
|
||||
import com.unciv.logic.city.INonPerpetualConstruction
|
||||
import com.unciv.logic.map.TileInfo
|
||||
import com.unciv.models.ruleset.Building
|
||||
import com.unciv.models.ruleset.tile.TileImprovement
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.ui.images.ImageGetter
|
||||
import com.unciv.ui.map.TileGroupMap
|
||||
import com.unciv.ui.popup.ToastPopup
|
||||
import com.unciv.ui.tilegroups.TileGroup
|
||||
import com.unciv.ui.tilegroups.TileSetStrings
|
||||
import com.unciv.ui.utils.*
|
||||
import java.util.*
|
||||
|
||||
class CityScreen(
|
||||
internal val city: CityInfo,
|
||||
var selectedConstruction: IConstruction? = null,
|
||||
var selectedTile: TileInfo? = null
|
||||
initSelectedConstruction: IConstruction? = null,
|
||||
initSelectedTile: TileInfo? = null
|
||||
): BaseScreen() {
|
||||
companion object {
|
||||
/** Distance from stage edges to floating widgets */
|
||||
@ -73,6 +80,27 @@ class CityScreen(
|
||||
/** The ScrollPane for the background map view of the city surroundings */
|
||||
private val mapScrollPane = ZoomableScrollPane()
|
||||
|
||||
/** Support for [UniqueType.CreatesOneImprovement] - need user to pick a tile */
|
||||
class PickTileForImprovementData (
|
||||
val building: Building,
|
||||
val improvement: TileImprovement,
|
||||
val isBuying: Boolean,
|
||||
val buyStat: Stat
|
||||
)
|
||||
|
||||
// The following fields control what the user selects
|
||||
var selectedConstruction: IConstruction? = initSelectedConstruction
|
||||
private set
|
||||
var selectedTile: TileInfo? = initSelectedTile
|
||||
private set
|
||||
/** If set, we are waiting for the user to pick a tile for [UniqueType.CreatesOneImprovement] */
|
||||
var pickTileData: PickTileForImprovementData? = null
|
||||
/** A [Building] with [UniqueType.CreatesOneImprovement] has been selected _in the queue_: show the tile it will place the improvement on */
|
||||
private var selectedQueueEntryTargetTile: TileInfo? = null
|
||||
/** Cached city.expansion.chooseNewTileToOwn() */
|
||||
// val should be OK as buying tiles is what changes this, and that would re-create the whole CityScreen
|
||||
private val nextTileToOwn = city.expansion.chooseNewTileToOwn()
|
||||
|
||||
init {
|
||||
onBackButtonClicked { game.setWorldScreen() }
|
||||
UncivGame.Current.settings.addCompletedTutorialTask("Enter city screen")
|
||||
@ -160,21 +188,38 @@ class CityScreen(
|
||||
}
|
||||
|
||||
private fun updateTileGroups() {
|
||||
val nextTile = city.expansion.chooseNewTileToOwn()
|
||||
fun isExistingImprovementValuable(tileInfo: TileInfo, improvementToPlace: TileImprovement): Boolean {
|
||||
if (tileInfo.improvement == null) return false
|
||||
val civInfo = city.civInfo
|
||||
val existingStats = tileInfo.getImprovementStats(tileInfo.getTileImprovement()!!, civInfo, city)
|
||||
val replacingStats = tileInfo.getImprovementStats(improvementToPlace, civInfo, city)
|
||||
return Automation.rankStatsValue(existingStats, civInfo) > Automation.rankStatsValue(replacingStats, civInfo)
|
||||
}
|
||||
fun getPickImprovementColor(tileInfo: TileInfo): Pair<Color, Float> {
|
||||
val improvementToPlace = pickTileData!!.improvement
|
||||
return when {
|
||||
tileInfo.isMarkedForCreatesOneImprovement() -> Color.BROWN to 0.7f
|
||||
!tileInfo.canBuildImprovement(improvementToPlace, city.civInfo) -> Color.RED to 0.4f
|
||||
isExistingImprovementValuable(tileInfo, improvementToPlace) -> Color.ORANGE to 0.5f
|
||||
tileInfo.improvement != null -> Color.YELLOW to 0.6f
|
||||
tileInfo.turnsToImprovement > 0 -> Color.YELLOW to 0.6f
|
||||
else -> Color.GREEN to 0.5f
|
||||
}
|
||||
}
|
||||
for (tileGroup in tileGroups) {
|
||||
tileGroup.update()
|
||||
tileGroup.hideHighlight()
|
||||
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.showHighlight(Color.GREEN)
|
||||
else tileGroup.showHighlight(Color.RED)
|
||||
}
|
||||
if (tileGroup.tileInfo == nextTile) {
|
||||
when {
|
||||
tileGroup.tileInfo == nextTileToOwn -> {
|
||||
tileGroup.showHighlight(Color.PURPLE)
|
||||
tileGroup.setColor(0f, 0f, 0f, 0.7f)
|
||||
}
|
||||
/** Support for [UniqueType.CreatesOneImprovement] */
|
||||
tileGroup.tileInfo == selectedQueueEntryTargetTile ->
|
||||
tileGroup.showHighlight(Color.BROWN, 0.7f)
|
||||
pickTileData != null && city.tiles.contains(tileGroup.tileInfo.position) ->
|
||||
getPickImprovementColor(tileGroup.tileInfo).run { tileGroup.showHighlight(first, second) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,50 +280,20 @@ class CityScreen(
|
||||
|
||||
val tileSetStrings = TileSetStrings()
|
||||
val cityTileGroups = cityInfo.getCenterTile().getTilesInDistance(5)
|
||||
.filter { city.civInfo.exploredTiles.contains(it.position) }
|
||||
.filter { cityInfo.civInfo.exploredTiles.contains(it.position) }
|
||||
.map { CityTileGroup(cityInfo, it, tileSetStrings) }
|
||||
|
||||
for (tileGroup in cityTileGroups) {
|
||||
val tileInfo = tileGroup.tileInfo
|
||||
|
||||
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()
|
||||
tileGroupOnClick(tileGroup, cityInfo)
|
||||
}
|
||||
return@onClick
|
||||
}
|
||||
|
||||
selectedTile = tileInfo
|
||||
selectedConstruction = null
|
||||
if (tileGroup.isWorkable && canChangeState) {
|
||||
if (!tileInfo.providesYield() && city.population.getFreePopulation() > 0) {
|
||||
city.workedTiles.add(tileInfo.position)
|
||||
game.settings.addCompletedTutorialTask("Reassign worked tiles")
|
||||
} else if (tileInfo.isWorked() && !tileInfo.isLocked())
|
||||
city.workedTiles.remove(tileInfo.position)
|
||||
city.cityStats.update()
|
||||
}
|
||||
update()
|
||||
}
|
||||
|
||||
tileGroups.add(tileGroup)
|
||||
}
|
||||
|
||||
val tilesToUnwrap = ArrayList<CityTileGroup>()
|
||||
for (tileGroup in tileGroups) {
|
||||
val xDifference = city.getCenterTile().position.x - tileGroup.tileInfo.position.x
|
||||
val yDifference = city.getCenterTile().position.y - tileGroup.tileInfo.position.y
|
||||
val xDifference = cityInfo.getCenterTile().position.x - tileGroup.tileInfo.position.x
|
||||
val yDifference = cityInfo.getCenterTile().position.y - tileGroup.tileInfo.position.y
|
||||
//if difference is bigger than 5 the tileGroup we are looking for is on the other side of the map
|
||||
if (xDifference > 5 || xDifference < -5 || yDifference > 5 || yDifference < -5) {
|
||||
//so we want to unwrap its position
|
||||
@ -299,6 +314,78 @@ class CityScreen(
|
||||
mapScrollPane.updateVisualScroll()
|
||||
}
|
||||
|
||||
private fun tileGroupOnClick(tileGroup: CityTileGroup, cityInfo: CityInfo) {
|
||||
if (cityInfo.isPuppet) return
|
||||
val tileInfo = tileGroup.tileInfo
|
||||
|
||||
/** [UniqueType.CreatesOneImprovement] support - select tile for improvement */
|
||||
if (pickTileData != null) {
|
||||
val pickTileData = this.pickTileData!!
|
||||
this.pickTileData = null
|
||||
val improvement = pickTileData.improvement
|
||||
if (tileInfo.canBuildImprovement(improvement, cityInfo.civInfo)) {
|
||||
if (pickTileData.isBuying) {
|
||||
constructionsTable.askToBuyConstruction(pickTileData.building, pickTileData.buyStat, tileInfo)
|
||||
} else {
|
||||
// This way to store where the improvement a CreatesOneImprovement Building will create goes
|
||||
// might get a bit fragile if several buildings constructing the same improvement type
|
||||
// were to be allowed in the queue - or a little nontransparent to the user why they
|
||||
// won't reorder - maybe one day redesign to have the target tiles attached to queue entries.
|
||||
tileInfo.markForCreatesOneImprovement(improvement.name)
|
||||
cityInfo.cityConstructions.addToQueue(pickTileData.building.name)
|
||||
}
|
||||
}
|
||||
update()
|
||||
return
|
||||
}
|
||||
|
||||
selectTile(tileInfo)
|
||||
if (tileGroup.isWorkable && canChangeState) {
|
||||
if (!tileInfo.providesYield() && cityInfo.population.getFreePopulation() > 0) {
|
||||
cityInfo.workedTiles.add(tileInfo.position)
|
||||
game.settings.addCompletedTutorialTask("Reassign worked tiles")
|
||||
} else if (tileInfo.isWorked() && !tileInfo.isLocked())
|
||||
cityInfo.workedTiles.remove(tileInfo.position)
|
||||
cityInfo.cityStats.update()
|
||||
}
|
||||
update()
|
||||
}
|
||||
|
||||
fun selectConstruction(name: String) {
|
||||
selectConstruction(city.cityConstructions.getConstruction(name))
|
||||
}
|
||||
fun selectConstruction(newConstruction: IConstruction) {
|
||||
selectedConstruction = newConstruction
|
||||
if (newConstruction is Building && newConstruction.hasCreateOneImprovementUnique()) {
|
||||
val improvement = newConstruction.getImprovementToCreate(city.getRuleset())
|
||||
selectedQueueEntryTargetTile = if (improvement == null) null
|
||||
else city.cityConstructions.getTileForImprovement(improvement.name)
|
||||
} else {
|
||||
selectedQueueEntryTargetTile = null
|
||||
pickTileData = null
|
||||
}
|
||||
selectedTile = null
|
||||
}
|
||||
private fun selectTile(newTile: TileInfo?) {
|
||||
selectedConstruction = null
|
||||
selectedQueueEntryTargetTile = null
|
||||
pickTileData = null
|
||||
selectedTile = newTile
|
||||
}
|
||||
fun clearSelection() = selectTile(null)
|
||||
|
||||
fun startPickTileForCreatesOneImprovement(construction: Building, stat: Stat, isBuying: Boolean) {
|
||||
val improvement = construction.getImprovementToCreate(city.getRuleset()) ?: return
|
||||
pickTileData = PickTileForImprovementData(construction, improvement, isBuying, stat)
|
||||
updateTileGroups()
|
||||
ToastPopup("Please select a tile for this building's [${improvement.name}]", this)
|
||||
}
|
||||
fun stopPickTileForCreatesOneImprovement() {
|
||||
if (pickTileData == null) return
|
||||
pickTileData = null
|
||||
updateTileGroups()
|
||||
}
|
||||
|
||||
fun exit() {
|
||||
game.setWorldScreen()
|
||||
game.worldScreen.mapHolder.setCenterPosition(city.location)
|
||||
|
@ -111,7 +111,7 @@ class CityScreenTileTable(private val cityScreen: CityScreen): Table() {
|
||||
Sounds.play(UncivSound.Coin)
|
||||
city.expansion.buyTile(selectedTile)
|
||||
// preselect the next tile on city screen rebuild so bulk buying can go faster
|
||||
UncivGame.Current.setScreen(CityScreen(city, selectedTile = city.expansion.chooseNewTileToOwn()))
|
||||
UncivGame.Current.setScreen(CityScreen(city, initSelectedTile = city.expansion.chooseNewTileToOwn()))
|
||||
},
|
||||
screen = cityScreen,
|
||||
restoreDefault = { cityScreen.update() }
|
||||
|
@ -26,12 +26,14 @@ class ImprovementPickerScreen(
|
||||
private val gameInfo = tileInfo.tileMap.gameInfo
|
||||
private val ruleSet = gameInfo.ruleSet
|
||||
private val currentPlayerCiv = gameInfo.getCurrentPlayerCivilization()
|
||||
// Support for UniqueType.CreatesOneImprovement
|
||||
private val tileMarkedForCreatesOneImprovement = tileInfo.isMarkedForCreatesOneImprovement()
|
||||
|
||||
private fun getRequiredTechColumn(improvement: TileImprovement) =
|
||||
ruleSet.technologies[improvement.techRequired]?.column?.columnNumber ?: -1
|
||||
|
||||
fun accept(improvement: TileImprovement?) {
|
||||
if (improvement == null) return
|
||||
if (improvement == null || tileMarkedForCreatesOneImprovement) return
|
||||
if (improvement.name == Constants.cancelImprovementOrder) {
|
||||
tileInfo.stopWorkingOnImprovement()
|
||||
// no onAccept() - Worker can stay selected
|
||||
@ -108,8 +110,9 @@ class ImprovementPickerScreen(
|
||||
|
||||
val pickNow = when {
|
||||
suggestRemoval -> "${Constants.remove}[${tileInfo.getLastTerrain().name}] first".toLabel()
|
||||
tileInfo.improvementInProgress != improvement.name -> "Pick now!".toLabel().onClick { accept(improvement) }
|
||||
else -> "Current construction".toLabel()
|
||||
tileInfo.improvementInProgress == improvement.name -> "Current construction".toLabel()
|
||||
tileMarkedForCreatesOneImprovement -> null
|
||||
else -> "Pick now!".toLabel().onClick { accept(improvement) }
|
||||
}
|
||||
|
||||
val statIcons = getStatIconsTable(provideResource, removeImprovement)
|
||||
@ -126,7 +129,6 @@ class ImprovementPickerScreen(
|
||||
val statsTable = getStatsTable(stats)
|
||||
statIcons.add(statsTable).padLeft(13f)
|
||||
|
||||
|
||||
regularImprovements.add(statIcons).align(Align.right)
|
||||
|
||||
val improvementButton = getPickerOptionButton(image, labelText)
|
||||
@ -137,16 +139,14 @@ class ImprovementPickerScreen(
|
||||
}
|
||||
|
||||
if (improvement.name == tileInfo.improvementInProgress) improvementButton.color = Color.GREEN
|
||||
if (suggestRemoval){
|
||||
if (suggestRemoval || tileMarkedForCreatesOneImprovement) {
|
||||
improvementButton.disable()
|
||||
}
|
||||
regularImprovements.add(improvementButton)
|
||||
|
||||
if (shortcutKey != null) {
|
||||
} else if (shortcutKey != null) {
|
||||
keyPressDispatcher[shortcutKey] = { accept(improvement) }
|
||||
improvementButton.addTooltip(shortcutKey)
|
||||
}
|
||||
|
||||
regularImprovements.add(improvementButton)
|
||||
regularImprovements.add(pickNow).padLeft(10f).fillY()
|
||||
regularImprovements.row()
|
||||
}
|
||||
|
@ -280,13 +280,7 @@ object UnitActions {
|
||||
|
||||
return UnitAction(UnitActionType.Pillage,
|
||||
action = {
|
||||
// http://well-of-souls.com/civ/civ5_improvements.html says that naval improvements are destroyed upon pillage
|
||||
// and I can't find any other sources so I'll go with that
|
||||
if (tile.isLand) {
|
||||
tile.improvementInProgress = tile.improvement
|
||||
tile.turnsToImprovement = 2
|
||||
}
|
||||
tile.improvement = null
|
||||
tile.setPillaged()
|
||||
unit.civInfo.lastSeenImprovement.remove(tile.position)
|
||||
if (tile.resource != null) tile.getOwner()?.updateDetailedCivResources() // this might take away a resource
|
||||
|
||||
@ -408,7 +402,6 @@ object UnitActions {
|
||||
&& unit.canBuildImprovement(it)
|
||||
}
|
||||
|
||||
|
||||
actionList += UnitAction(UnitActionType.ConstructImprovement,
|
||||
isCurrentAction = unit.currentTile.hasImprovementInProgress(),
|
||||
action = {
|
||||
@ -652,11 +645,10 @@ object UnitActions {
|
||||
val improvement = tile.ruleset.tileImprovements[improvementName]
|
||||
?: continue
|
||||
|
||||
var resourcesAvailable = true
|
||||
if (improvement.uniqueObjects.any {
|
||||
it.isOfType(UniqueType.ConsumesResources) && civResources[unique.params[1]] ?: 0 < unique.params[0].toInt()
|
||||
})
|
||||
resourcesAvailable = false
|
||||
val resourcesAvailable = improvement.uniqueObjects.none {
|
||||
it.isOfType(UniqueType.ConsumesResources) &&
|
||||
civResources[unique.params[1]] ?: 0 < unique.params[0].toInt()
|
||||
}
|
||||
|
||||
finalActions += UnitAction(UnitActionType.Create,
|
||||
title = "Create [$improvementName]",
|
||||
@ -670,10 +662,10 @@ object UnitActions {
|
||||
it in improvement.terrainsCanBeBuiltOn
|
||||
}
|
||||
)
|
||||
unitTile.removeCreatesOneImprovementMarker()
|
||||
unitTile.improvement = improvementName
|
||||
unitTile.improvementInProgress = null
|
||||
unitTile.turnsToImprovement = 0
|
||||
if (improvementName == Constants.citadel)
|
||||
unitTile.stopWorkingOnImprovement()
|
||||
if (improvement.hasUnique(UniqueType.TakeOverTilesAroundWhenBuilt))
|
||||
takeOverTilesAround(unit)
|
||||
val city = unitTile.getCity()
|
||||
if (city != null) {
|
||||
@ -687,6 +679,9 @@ object UnitActions {
|
||||
resourcesAvailable
|
||||
&& unit.currentMovement > 0f
|
||||
&& tile.canBuildImprovement(improvement, unit.civInfo)
|
||||
// Next test is to prevent interfering with UniqueType.CreatesOneImprovement -
|
||||
// not pretty, but users *can* remove the building from the city queue an thus clear this:
|
||||
&& !tile.isMarkedForCreatesOneImprovement()
|
||||
&& !tile.isImpassible() // Not 100% sure that this check is necessary...
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user