From 03f7af436086ea32b1afaab6005eb56c9b6a9a0d Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Wed, 9 Feb 2022 12:18:14 +0200 Subject: [PATCH] Typified Obsolete unique, made luxury resources obsoletable (#6129) * Typified Obsolete unique, made luxury resources obsoletable Things that were luxuries in the past become uninteresting as time goes on * Unified obsolescence handling for buildings, improvements and resources * Obsolete icons have their x's in the right place * All images &c are done - we have the skeleton of the mod in place! * Revert "All images &c are done - we have the skeleton of the mod in place!" This reverts commit 61553c17 --- .../unciv/logic/civilization/CivInfoStats.kt | 7 ++++-- core/src/com/unciv/logic/map/TileInfo.kt | 9 ++++---- core/src/com/unciv/models/ruleset/Building.kt | 2 +- .../unciv/models/ruleset/tech/Technology.kt | 20 ++++++++++------ .../unciv/models/ruleset/unique/UniqueType.kt | 1 + .../com/unciv/ui/pickerscreens/TechButton.kt | 23 ++++++++++++++----- core/src/com/unciv/ui/utils/ImageGetter.kt | 4 ++-- docs/uniques.md | 5 ++++ 8 files changed, 48 insertions(+), 23 deletions(-) diff --git a/core/src/com/unciv/logic/civilization/CivInfoStats.kt b/core/src/com/unciv/logic/civilization/CivInfoStats.kt index 5ca921da03..72dd58f839 100644 --- a/core/src/com/unciv/logic/civilization/CivInfoStats.kt +++ b/core/src/com/unciv/logic/civilization/CivInfoStats.kt @@ -240,9 +240,12 @@ class CivInfoStats(val civInfo: CivilizationInfo) { val ownedLuxuries = civInfo.getCivResources().map { it.resource } .filter { it.resourceType == ResourceType.Luxury } - statMap["Luxury resources"] = civInfo.getCivResources() + val relevantLuxuries = civInfo.getCivResources().asSequence() .map { it.resource } - .count { it.resourceType === ResourceType.Luxury } * happinessPerUniqueLuxury + .count { it.resourceType == ResourceType.Luxury + && it.getMatchingUniques(UniqueType.ObsoleteWith) + .none { unique -> civInfo.tech.isResearched(unique.params[0]) } } + statMap["Luxury resources"] = relevantLuxuries * happinessPerUniqueLuxury val happinessBonusForCityStateProvidedLuxuries = ( diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index 1f0e2899bd..0aca54a134 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -454,15 +454,14 @@ open class TileInfo { && neighbors.any { it.getOwner() == civInfo } && civInfo.cities.isNotEmpty() ) ) -> false - improvement.uniqueObjects.any { - it.placeholderText == "Obsolete with []" && civInfo.tech.isResearched(it.params[0]) + improvement.getMatchingUniques(UniqueType.ObsoleteWith).any { + civInfo.tech.isResearched(it.params[0]) } -> return false improvement.getMatchingUniques(UniqueType.CannotBuildOnTile, StateForConditionals(civInfo=civInfo)).any { matchesTerrainFilter(it.params[0], civInfo) } -> false - improvement.uniqueObjects.any { - it.isOfType(UniqueType.ConsumesResources) - && civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt() + improvement.getMatchingUniques(UniqueType.ConsumesResources).any { + civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt() } -> false // Calling this function does double the check for 'cannot be build on tile', but this is unavoidable. // Only in this function do we have the civInfo of the civ, so only here we can check whether diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index 2dd6580c69..b7280e1169 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -515,7 +515,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction { if (!cityConstructions.cityInfo.matchesFilter(unique.params[0])) rejectionReasons.add(RejectionReason.CanOnlyBeBuiltInSpecificCities.apply { errorMessage = unique.text }) - "Obsolete with []" -> + UniqueType.ObsoleteWith.placeholderText -> if (civInfo.tech.isResearched(unique.params[0])) rejectionReasons.add(RejectionReason.Obsoleted.apply { errorMessage = unique.text }) diff --git a/core/src/com/unciv/models/ruleset/tech/Technology.kt b/core/src/com/unciv/models/ruleset/tech/Technology.kt index 7ba7e987a3..d3373ac730 100644 --- a/core/src/com/unciv/models/ruleset/tech/Technology.kt +++ b/core/src/com/unciv/models/ruleset/tech/Technology.kt @@ -5,6 +5,7 @@ import com.unciv.logic.civilization.CivilizationInfo import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetObject +import com.unciv.models.ruleset.RulesetStatsObject import com.unciv.models.ruleset.unique.UniqueFlag import com.unciv.models.ruleset.unique.UniqueTarget import com.unciv.models.ruleset.unique.UniqueType @@ -74,8 +75,8 @@ class Technology: RulesetObject() { lineList += " * " + wonder.name.tr() + " (" + wonder.getShortDescription() + ")" } - for (building in getObsoletedBuildings(viewingCiv)) - lineList += "[${building.name}] obsoleted" + for (obj in getObsoletedObjects(viewingCiv)) + lineList += "[${obj.name}] obsoleted" for (resource in ruleset.tileResources.values.asSequence().filter { it.revealedBy == name } .map { it.name }) @@ -105,8 +106,13 @@ class Technology: RulesetObject() { * nuclear weapons and religion settings, and without those expressly hidden from Civilopedia. */ // Used for Civilopedia, Alert and Picker, so if any of these decide to ignore the "Will not be displayed in Civilopedia" unique this needs refactoring - fun getObsoletedBuildings(civInfo: CivilizationInfo) = getFilteredBuildings(civInfo) - { it.uniqueObjects.any { unique -> unique.placeholderText == "Obsolete with []" && unique.params[0] == name } } + + fun getObsoletedObjects(civInfo: CivilizationInfo): Sequence = + (getFilteredBuildings(civInfo){true} + + civInfo.gameInfo.ruleSet.tileResources.values.asSequence() + + civInfo.gameInfo.ruleSet.tileImprovements.values.filter { + it.uniqueTo==null || it.uniqueTo == civInfo.civName + }).filter { it.getMatchingUniques(UniqueType.ObsoleteWith).any { it.params[0] == name } } // Helper: common filtering for both getEnabledBuildings and getObsoletedBuildings, difference via predicate parameter private fun getFilteredBuildings(civInfo: CivilizationInfo, predicate: (Building)->Boolean): Sequence { @@ -241,10 +247,10 @@ class Technology: RulesetObject() { lineList += FormattedLine(building.name.tr() + " (" + building.getShortDescription() + ")", link = building.makeLink()) } - val obsoletedBuildings = getObsoletedBuildings(viewingCiv) - if (obsoletedBuildings.any()) { + val obsoletedObjects = getObsoletedObjects(viewingCiv) + if (obsoletedObjects.any()) { lineList += FormattedLine() - obsoletedBuildings.forEach { + obsoletedObjects.forEach { lineList += FormattedLine("[${it.name}] obsoleted", link = it.makeLink()) } } diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index e01c49fac0..dc47adb650 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -330,6 +330,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: MustNotBeNextTo("Must not be next to [terrainFilter]", UniqueTarget.Building), Unsellable("Unsellable", UniqueTarget.Building), + ObsoleteWith("Obsolete with [tech]", UniqueTarget.Building, UniqueTarget.Resource, UniqueTarget.Improvement), RemoveAnnexUnhappiness("Remove extra unhappiness from annexed cities", UniqueTarget.Building), diff --git a/core/src/com/unciv/ui/pickerscreens/TechButton.kt b/core/src/com/unciv/ui/pickerscreens/TechButton.kt index d8c87b6896..d8f23a1d5e 100644 --- a/core/src/com/unciv/ui/pickerscreens/TechButton.kt +++ b/core/src/com/unciv/ui/pickerscreens/TechButton.kt @@ -2,10 +2,12 @@ package com.unciv.ui.pickerscreens import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.Touchable -import com.badlogic.gdx.scenes.scene2d.ui.Button import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.Align import com.unciv.logic.civilization.TechManager +import com.unciv.models.ruleset.Building +import com.unciv.models.ruleset.tile.TileImprovement +import com.unciv.models.ruleset.tile.TileResource import com.unciv.ui.utils.* class TechButton(techName:String, private val techManager: TechManager, isWorldScreen: Boolean = true) : Table(BaseScreen.skin) { @@ -59,12 +61,21 @@ class TechButton(techName:String, private val techManager: TechManager, isWorldS for (building in tech.getEnabledBuildings(techManager.civInfo)) techEnabledIcons.add(ImageGetter.getConstructionImage(building.name).surroundWithCircle(techIconSize)) - for (building in tech.getObsoletedBuildings(techManager.civInfo)) - techEnabledIcons.add(ImageGetter.getConstructionImage(building.name).surroundWithCircle(techIconSize).apply { + for (obj in tech.getObsoletedObjects(techManager.civInfo)) { + val obsoletedIcon = when { + obj is Building -> ImageGetter.getConstructionImage(obj.name) + .surroundWithCircle(techIconSize) + obj is TileResource -> ImageGetter.getResourceImage(obj.name, techIconSize) + obj is TileImprovement -> ImageGetter.getImprovementIcon(obj.name, techIconSize) + else -> continue + }.also { val closeImage = ImageGetter.getRedCross(techIconSize / 2, 1f) - closeImage.center(this) - addActor(closeImage) - }) + closeImage.center(it) + it.addActor(closeImage) + } + techEnabledIcons.add(obsoletedIcon) + } + for (improvement in ruleset.tileImprovements.values.asSequence() .filter { diff --git a/core/src/com/unciv/ui/utils/ImageGetter.kt b/core/src/com/unciv/ui/utils/ImageGetter.kt index eafea4e421..cadc5b90b9 100644 --- a/core/src/com/unciv/ui/utils/ImageGetter.kt +++ b/core/src/com/unciv/ui/utils/ImageGetter.kt @@ -260,7 +260,7 @@ object ImageGetter { } - fun getImprovementIcon(improvementName: String, size: Float = 20f): Actor { + fun getImprovementIcon(improvementName: String, size: Float = 20f): Group { if (improvementName.startsWith(Constants.remove) || improvementName == Constants.cancelImprovementOrder) return Table().apply { add(getImage("OtherIcons/Stop")).size(size) } @@ -333,7 +333,7 @@ object ImageGetter { return redCross } - fun getResourceImage(resourceName: String, size: Float): Actor { + fun getResourceImage(resourceName: String, size: Float): IconCircleGroup { val iconGroup = getImage("ResourceIcons/$resourceName").surroundWithCircle(size) val resource = ruleset.tileResources[resourceName] ?: return iconGroup // This is the result of a bad modding setup, just give em an empty circle. Their problem. diff --git a/docs/uniques.md b/docs/uniques.md index 2fcea593da..731084d70c 100644 --- a/docs/uniques.md +++ b/docs/uniques.md @@ -769,6 +769,11 @@ Applicable to: Building #### Unsellable Applicable to: Building +#### Obsolete with [tech] +Example: "Obsolete with [Agriculture]" + +Applicable to: Building, Improvement, Resource + #### Remove extra unhappiness from annexed cities Applicable to: Building