From 3d683c767ae44f5cd50f13c1b6b696e38fa1e3c1 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Sat, 6 Feb 2021 21:12:44 +0200 Subject: [PATCH] Added "Consumes [amount] [resourceName]" unique for buildings --- .../jsons/translations/template.properties | 1 + core/src/com/unciv/logic/city/CityInfo.kt | 17 ++++++++-- .../src/com/unciv/logic/city/IConstruction.kt | 32 ++++++------------ core/src/com/unciv/models/ruleset/Building.kt | 33 ++++++++++++------- core/src/com/unciv/models/ruleset/Ruleset.kt | 9 ++--- .../unciv/models/ruleset/tile/TileResource.kt | 29 ++++++++-------- .../com/unciv/models/ruleset/unit/BaseUnit.kt | 14 ++++++-- .../unciv/ui/cityscreen/ConstructionsTable.kt | 12 ++++--- 8 files changed, 84 insertions(+), 63 deletions(-) diff --git a/android/assets/jsons/translations/template.properties b/android/assets/jsons/translations/template.properties index 56eb2549f6..5b70a31bf8 100644 --- a/android/assets/jsons/translations/template.properties +++ b/android/assets/jsons/translations/template.properties @@ -38,6 +38,7 @@ Wonder is being built elsewhere = Requires a [buildingName] in all cities = Requires a [buildingName] in this city = Consumes 1 [resource] = +Consumes [amount] [resource] = Required tech: [requiredTech] = Requires [PolicyOrNationalWonder] = Cannot be purchased = diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 9d21110ab7..64e72219ae 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -34,14 +34,19 @@ import kotlin.math.roundToInt class CityInfo { @Transient lateinit var civInfo: CivilizationInfo + @Transient lateinit private var centerTileInfo: TileInfo // cached for better performance + @Transient val range = 2 + @Transient lateinit var tileMap: TileMap + @Transient lateinit var tilesInRange: HashSet + @Transient var hasJustBeenConquered = false // this is so that military units can enter the city, even before we decide what to do with it @@ -197,9 +202,15 @@ class CityInfo { cityResources.add(resource, unique.params[0].toInt() * civInfo.getResourceModifier(resource), "Tiles") } } - for (building in cityConstructions.getBuiltBuildings().filter { it.requiredResource != null }) { - val resource = getRuleset().tileResources[building.requiredResource]!! - cityResources.add(resource, -1, "Buildings") + for (building in cityConstructions.getBuiltBuildings()) { + for ((resourceName, amount) in building.getResourceRequirements()) { + val resource = getRuleset().tileResources[resourceName]!! + cityResources.add(resource, -amount, "Buildings") + } + for (unique in building.uniqueObjects.filter { it.placeholderText == "Consumes [] []" }) { + val resource = getRuleset().tileResources[unique.params[1]] + if (resource != null) cityResources.add(resource, -unique.params[0].toInt(), "Buildings") + } } for (unique in cityConstructions.builtBuildingUniqueMap.getUniques("Provides [] []")) { // E.G "Provides [1] [Iron]" val resource = getRuleset().tileResources[unique.params[1]] diff --git a/core/src/com/unciv/logic/city/IConstruction.kt b/core/src/com/unciv/logic/city/IConstruction.kt index 1355097f36..71fe1c1108 100644 --- a/core/src/com/unciv/logic/city/IConstruction.kt +++ b/core/src/com/unciv/logic/city/IConstruction.kt @@ -1,9 +1,7 @@ package com.unciv.logic.city -import com.unciv.Constants import com.unciv.logic.civilization.CivilizationInfo import com.unciv.models.stats.INamed -import com.unciv.models.translations.tr import com.unciv.ui.utils.Fonts import kotlin.math.roundToInt @@ -13,16 +11,14 @@ interface IConstruction : INamed { fun isBuildable(cityConstructions: CityConstructions): Boolean fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean fun postBuildEvent(construction: CityConstructions, wasBought: Boolean = false): Boolean // Yes I'm hilarious. - fun getResource(): String? + fun getResourceRequirements(): HashMap fun canBePurchased(): Boolean } open class PerpetualConstruction(override var name: String, val description: String) : IConstruction { - override fun shouldBeDisplayed(cityConstructions: CityConstructions): Boolean { - return isBuildable(cityConstructions) - } + override fun shouldBeDisplayed(cityConstructions: CityConstructions) = isBuildable(cityConstructions) open fun getProductionTooltip(cityInfo: CityInfo) : String = "\r\n${(cityInfo.cityStats.currentCityStats.production / CONVERSION_RATE).roundToInt()}/${Fonts.turn}" open fun getConversionRate(cityInfo: CityInfo) : Int @@ -54,26 +50,18 @@ open class PerpetualConstruction(override var name: String, val description: Str = mapOf(science.name to science, gold.name to gold, idle.name to idle) } - override fun canBePurchased(): Boolean { - return false - } + override fun canBePurchased() = false - override fun getProductionCost(civInfo: CivilizationInfo): Int { - throw Exception("Impossible!") - } + override fun getProductionCost(civInfo: CivilizationInfo) = throw Exception("Impossible!") - override fun getGoldCost(civInfo: CivilizationInfo): Int { - throw Exception("Impossible!") - } + override fun getGoldCost(civInfo: CivilizationInfo) = throw Exception("Impossible!") - override fun isBuildable(cityConstructions: CityConstructions): Boolean { - throw Exception("Impossible!") - } + override fun isBuildable(cityConstructions: CityConstructions): Boolean = + throw Exception("Impossible!") - override fun postBuildEvent(construction: CityConstructions, wasBought: Boolean): Boolean { - throw Exception("Impossible!") - } + override fun postBuildEvent(construction: CityConstructions, wasBought: Boolean) = + throw Exception("Impossible!") - override fun getResource(): String? =null + override fun getResourceRequirements(): HashMap = hashMapOf() } \ No newline at end of file diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index 4b1f80cf4d..046b92bc49 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -1,6 +1,5 @@ package com.unciv.models.ruleset -import com.unciv.Constants import com.unciv.logic.city.CityConstructions import com.unciv.logic.city.IConstruction import com.unciv.logic.civilization.CivilizationInfo @@ -12,6 +11,7 @@ import com.unciv.models.stats.Stats import com.unciv.models.translations.tr import java.util.* import kotlin.collections.ArrayList +import kotlin.collections.HashMap import kotlin.math.pow @@ -47,7 +47,7 @@ class Building : NamedStats(), IConstruction { var requiredBuildingInAllCities: String? = null /** A strategic resource that will be consumed by this building */ - var requiredResource: String? = null + private var requiredResource: String? = null /** City can only be built if one of these resources is nearby - it must be improved! */ private var requiredNearbyImprovedResources: List? = null @@ -105,8 +105,10 @@ class Building : NamedStats(), IConstruction { stringBuilder.appendln("Requires [$requiredBuilding] to be built in the city".tr()) if (!forBuildingPickerScreen && requiredBuildingInAllCities != null) stringBuilder.appendln("Requires [$requiredBuildingInAllCities] to be built in all cities".tr()) - if (requiredResource != null) - stringBuilder.appendln("Consumes 1 [$requiredResource]".tr()) + for ((resource, amount) in getResourceRequirements()) { + if (amount == 1) stringBuilder.appendln("Consumes 1 [$resource]".tr()) // For now, to keep the existing translations + else stringBuilder.appendln("Consumes [$amount] [$resource]".tr()) + } if (providesFreeBuilding != null) stringBuilder.appendln("Provides a free [$providesFreeBuilding] in the city".tr()) if (uniques.isNotEmpty()) { @@ -359,8 +361,11 @@ class Building : NamedStats(), IConstruction { if (cannotBeBuiltWith != null && construction.isBuilt(cannotBeBuiltWith!!)) return "Cannot be built with $cannotBeBuiltWith" - if (requiredResource != null && !civInfo.hasResource(requiredResource!!) && !civInfo.gameInfo.gameParameters.godMode) - return "Consumes 1 [$requiredResource]" + for ((resource, amount) in getResourceRequirements()) + if (civInfo.getCivResourcesByName()[resource]!! < amount) { + if (amount == 1) return "Consumes 1 [$resource]" // Again, to preserve existing translations + else return "Consumes [$amount] [$resource]" + } if (requiredNearbyImprovedResources != null) { val containsResourceWithImprovement = construction.cityInfo.getWorkableTiles() @@ -381,9 +386,8 @@ class Building : NamedStats(), IConstruction { return "" } - override fun isBuildable(cityConstructions: CityConstructions): Boolean { - return getRejectionReason(cityConstructions) == "" - } + override fun isBuildable(cityConstructions: CityConstructions): Boolean = + getRejectionReason(cityConstructions) == "" override fun postBuildEvent(cityConstructions: CityConstructions, wasBought: Boolean): Boolean { val civInfo = cityConstructions.cityInfo.civInfo @@ -433,8 +437,6 @@ class Building : NamedStats(), IConstruction { return true } - override fun getResource(): String? = requiredResource - fun isStatRelated(stat: Stat): Boolean { if (get(stat) > 0) return true if (getStatPercentageBonuses(null).get(stat) > 0) return true @@ -455,4 +457,13 @@ class Building : NamedStats(), IConstruction { } fun isSellable() = !isWonder && !isNationalWonder && !uniques.contains("Unsellable") + + override fun getResourceRequirements(): HashMap { + val resourceRequirements = HashMap() + if (requiredResource != null) resourceRequirements[requiredResource!!] = 1 + for (unique in uniqueObjects) + if (unique.placeholderText == "Consumes [] []") + resourceRequirements[unique.params[1]] = unique.params[0].toInt() + return resourceRequirements + } } \ No newline at end of file diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index a5a385bd57..2502863982 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -267,8 +267,9 @@ class Ruleset { for (building in buildings.values) { if (building.requiredTech != null && !technologies.containsKey(building.requiredTech!!)) lines += "${building.name} requires tech ${building.requiredTech} which does not exist!" - if (building.requiredResource != null && !tileResources.containsKey(building.requiredResource!!)) - lines += "${building.name} requires resource ${building.requiredResource} which does not exist!" + for(resource in building.getResourceRequirements().keys) + if (!tileResources.containsKey(resource)) + lines += "${building.name} requires resource $resource which does not exist!" if (building.replaces != null && !buildings.containsKey(building.replaces!!)) lines += "${building.name} replaces ${building.replaces} which does not exist!" if (building.requiredBuilding != null && !buildings.containsKey(building.requiredBuilding!!)) @@ -315,7 +316,7 @@ class Ruleset { * save all of the loaded rulesets somewhere for later use * */ object RulesetCache :HashMap() { - fun loadRulesets(consoleMode:Boolean=false, printOutput: Boolean=false) { + fun loadRulesets(consoleMode: Boolean = false, printOutput: Boolean = false) { clear() for (ruleset in BaseRuleset.values()) { val fileName = "jsons/${ruleset.fullName}" @@ -373,7 +374,7 @@ object RulesetCache :HashMap() { class Specialist: NamedStats() { var color = ArrayList() val colorObject by lazy { colorFromRGB(color) } - var greatPersonPoints= Stats() + var greatPersonPoints = Stats() companion object { internal fun specialistNameByStat(stat: Stat) = when (stat) { diff --git a/core/src/com/unciv/models/ruleset/tile/TileResource.kt b/core/src/com/unciv/models/ruleset/tile/TileResource.kt index 4ac1c53b62..b69830f94e 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileResource.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileResource.kt @@ -32,35 +32,34 @@ class TileResource : NamedStats() { stringBuilder.appendln("Improved by [$improvement]".tr()) stringBuilder.appendln("{Bonus stats for improvement}: ".tr() + "$improvementStats".tr()) - val buildingsThatConsumeThis = ruleset.buildings.values.filter { it.requiredResource==name } - if(buildingsThatConsumeThis.isNotEmpty()) + val buildingsThatConsumeThis = ruleset.buildings.values.filter { it.getResourceRequirements().containsKey(name) } + if (buildingsThatConsumeThis.isNotEmpty()) stringBuilder.appendln("{Buildings that consume this resource}: ".tr() + buildingsThatConsumeThis.joinToString { it.name.tr() }) - val unitsThatConsumeThis = ruleset.units.values.filter { it.requiredResource==name } - if(unitsThatConsumeThis.isNotEmpty()) + val unitsThatConsumeThis = ruleset.units.values.filter { it.requiredResource == name } + if (unitsThatConsumeThis.isNotEmpty()) stringBuilder.appendln("{Units that consume this resource}: ".tr() + unitsThatConsumeThis.joinToString { it.name.tr() }) - if(unique!=null) stringBuilder.appendln(unique!!.tr()) + if (unique != null) stringBuilder.appendln(unique!!.tr()) return stringBuilder.toString() } } data class ResourceSupply(val resource:TileResource,var amount:Int, val origin:String) -class ResourceSupplyList:ArrayList(){ - fun add(resource: TileResource, amount: Int, origin: String){ - val existingResourceSupply=firstOrNull{it.resource==resource && it.origin==origin} - if(existingResourceSupply!=null) { +class ResourceSupplyList:ArrayList() { + fun add(resource: TileResource, amount: Int, origin: String) { + val existingResourceSupply = firstOrNull { it.resource == resource && it.origin == origin } + if (existingResourceSupply != null) { existingResourceSupply.amount += amount - if(existingResourceSupply.amount==0) remove(existingResourceSupply) - } - else add(ResourceSupply(resource,amount,origin)) + if (existingResourceSupply.amount == 0) remove(existingResourceSupply) + } else add(ResourceSupply(resource, amount, origin)) } - fun add(resourceSupplyList: ResourceSupplyList){ - for(resourceSupply in resourceSupplyList) - add(resourceSupply.resource,resourceSupply.amount,resourceSupply.origin) + fun add(resourceSupplyList: ResourceSupplyList) { + for (resourceSupply in resourceSupplyList) + add(resourceSupply.resource, resourceSupply.amount, resourceSupply.origin) } } \ No newline at end of file diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index 0013db90b7..724b31824d 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -151,9 +151,9 @@ class BaseUnit : INamed, IConstruction { && uniques.contains("Nuclear weapon")) return "Disabled by setting" 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 + return unique.text for (unique in uniqueObjects.filter { it.placeholderText == "Requires []" }) { val filter = unique.params[0] @@ -208,7 +208,6 @@ class BaseUnit : INamed, IConstruction { return true } - override fun getResource(): String? = requiredResource fun getDirectUpgradeUnit(civInfo: CivilizationInfo): BaseUnit { return civInfo.getEquivalentUnit(upgradesTo!!) @@ -234,4 +233,13 @@ class BaseUnit : INamed, IConstruction { } fun isGreatPerson() = uniqueObjects.any { it.placeholderText == "Great Person - []" } + + override fun getResourceRequirements(): HashMap { + val resourceRequirements = HashMap() + if (requiredResource != null) resourceRequirements[requiredResource!!] = 1 + for (unique in uniqueObjects) + if (unique.placeholderText == "Consumes [] []") + resourceRequirements[unique.params[1]] = unique.params[0].toInt() + return resourceRequirements + } } diff --git a/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt b/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt index ca5730ce00..4b58036b26 100644 --- a/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt +++ b/core/src/com/unciv/ui/cityscreen/ConstructionsTable.kt @@ -33,7 +33,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre private val buttons = Table() private val pad = 10f - var improvementBuildingToConstruct:Building?=null + var improvementBuildingToConstruct: Building? = null init { @@ -136,8 +136,10 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre for (building in city.getRuleset().buildings.values.filter { it.shouldBeDisplayed(cityConstructions) }) { val turnsToBuilding = cityConstructions.turnsToConstruction(building.name) var buttonText = building.name.tr() + turnOrTurns(turnsToBuilding) - if (building.requiredResource != null) - buttonText += "\n" + "Consumes 1 [${building.requiredResource}]".tr() + for ((resource, amount) in building.getResourceRequirements()) { + if (amount == 1) buttonText += "\n" + "Consumes 1 [$resource]".tr() + else buttonText += "\n" + "Consumes [$amount] [$resource]".tr() + } constructionButtonDTOList.add(ConstructionButtonDTO(building, buttonText, @@ -223,7 +225,7 @@ class ConstructionsTable(val cityScreen: CityScreen) : Table(CameraStageBaseScre if (name in PerpetualConstruction.perpetualConstructionsMap) "\n∞" else turnOrTurns(turnsToComplete) - val constructionResource = cityConstructions.getConstruction(name).getResource() + val constructionResource = cityConstructions.getConstruction(name).getResourceRequirements() if (constructionResource != null) text += "\n" + "Consumes 1 [$constructionResource]".tr() @@ -347,7 +349,7 @@ 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" }){ + if (construction is Building && construction.uniqueObjects.any { it.placeholderText == "Creates a [] improvement on a specific tile" }) { cityScreen.selectedTile improvementBuildingToConstruct = construction return