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
This commit is contained in:
Yair Morgenstern 2022-02-09 12:18:14 +02:00 committed by GitHub
parent 644c61a851
commit 03f7af4360
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 48 additions and 23 deletions

View File

@ -240,9 +240,12 @@ class CivInfoStats(val civInfo: CivilizationInfo) {
val ownedLuxuries = civInfo.getCivResources().map { it.resource } val ownedLuxuries = civInfo.getCivResources().map { it.resource }
.filter { it.resourceType == ResourceType.Luxury } .filter { it.resourceType == ResourceType.Luxury }
statMap["Luxury resources"] = civInfo.getCivResources() val relevantLuxuries = civInfo.getCivResources().asSequence()
.map { it.resource } .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 = val happinessBonusForCityStateProvidedLuxuries =
( (

View File

@ -454,15 +454,14 @@ open class TileInfo {
&& neighbors.any { it.getOwner() == civInfo } && civInfo.cities.isNotEmpty() && neighbors.any { it.getOwner() == civInfo } && civInfo.cities.isNotEmpty()
) )
) -> false ) -> false
improvement.uniqueObjects.any { improvement.getMatchingUniques(UniqueType.ObsoleteWith).any {
it.placeholderText == "Obsolete with []" && civInfo.tech.isResearched(it.params[0]) civInfo.tech.isResearched(it.params[0])
} -> return false } -> return false
improvement.getMatchingUniques(UniqueType.CannotBuildOnTile, StateForConditionals(civInfo=civInfo)).any { improvement.getMatchingUniques(UniqueType.CannotBuildOnTile, StateForConditionals(civInfo=civInfo)).any {
matchesTerrainFilter(it.params[0], civInfo) matchesTerrainFilter(it.params[0], civInfo)
} -> false } -> false
improvement.uniqueObjects.any { improvement.getMatchingUniques(UniqueType.ConsumesResources).any {
it.isOfType(UniqueType.ConsumesResources) civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
&& civInfo.getCivResourcesByName()[it.params[1]]!! < it.params[0].toInt()
} -> false } -> false
// Calling this function does double the check for 'cannot be build on tile', but this is unavoidable. // 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 // Only in this function do we have the civInfo of the civ, so only here we can check whether

View File

@ -515,7 +515,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
if (!cityConstructions.cityInfo.matchesFilter(unique.params[0])) if (!cityConstructions.cityInfo.matchesFilter(unique.params[0]))
rejectionReasons.add(RejectionReason.CanOnlyBeBuiltInSpecificCities.apply { errorMessage = unique.text }) rejectionReasons.add(RejectionReason.CanOnlyBeBuiltInSpecificCities.apply { errorMessage = unique.text })
"Obsolete with []" -> UniqueType.ObsoleteWith.placeholderText ->
if (civInfo.tech.isResearched(unique.params[0])) if (civInfo.tech.isResearched(unique.params[0]))
rejectionReasons.add(RejectionReason.Obsoleted.apply { errorMessage = unique.text }) rejectionReasons.add(RejectionReason.Obsoleted.apply { errorMessage = unique.text })

View File

@ -5,6 +5,7 @@ import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetObject 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.UniqueFlag
import com.unciv.models.ruleset.unique.UniqueTarget import com.unciv.models.ruleset.unique.UniqueTarget
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
@ -74,8 +75,8 @@ class Technology: RulesetObject() {
lineList += " * " + wonder.name.tr() + " (" + wonder.getShortDescription() + ")" lineList += " * " + wonder.name.tr() + " (" + wonder.getShortDescription() + ")"
} }
for (building in getObsoletedBuildings(viewingCiv)) for (obj in getObsoletedObjects(viewingCiv))
lineList += "[${building.name}] obsoleted" lineList += "[${obj.name}] obsoleted"
for (resource in ruleset.tileResources.values.asSequence().filter { it.revealedBy == name } for (resource in ruleset.tileResources.values.asSequence().filter { it.revealedBy == name }
.map { it.name }) .map { it.name })
@ -105,8 +106,13 @@ class Technology: RulesetObject() {
* nuclear weapons and religion settings, and without those expressly hidden from Civilopedia. * 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 // 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<RulesetStatsObject> =
(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 // Helper: common filtering for both getEnabledBuildings and getObsoletedBuildings, difference via predicate parameter
private fun getFilteredBuildings(civInfo: CivilizationInfo, predicate: (Building)->Boolean): Sequence<Building> { private fun getFilteredBuildings(civInfo: CivilizationInfo, predicate: (Building)->Boolean): Sequence<Building> {
@ -241,10 +247,10 @@ class Technology: RulesetObject() {
lineList += FormattedLine(building.name.tr() + " (" + building.getShortDescription() + ")", link = building.makeLink()) lineList += FormattedLine(building.name.tr() + " (" + building.getShortDescription() + ")", link = building.makeLink())
} }
val obsoletedBuildings = getObsoletedBuildings(viewingCiv) val obsoletedObjects = getObsoletedObjects(viewingCiv)
if (obsoletedBuildings.any()) { if (obsoletedObjects.any()) {
lineList += FormattedLine() lineList += FormattedLine()
obsoletedBuildings.forEach { obsoletedObjects.forEach {
lineList += FormattedLine("[${it.name}] obsoleted", link = it.makeLink()) lineList += FormattedLine("[${it.name}] obsoleted", link = it.makeLink())
} }
} }

View File

@ -330,6 +330,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
MustNotBeNextTo("Must not be next to [terrainFilter]", UniqueTarget.Building), MustNotBeNextTo("Must not be next to [terrainFilter]", UniqueTarget.Building),
Unsellable("Unsellable", 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), RemoveAnnexUnhappiness("Remove extra unhappiness from annexed cities", UniqueTarget.Building),

View File

@ -2,10 +2,12 @@ package com.unciv.ui.pickerscreens
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.Touchable 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.scenes.scene2d.ui.Table
import com.badlogic.gdx.utils.Align import com.badlogic.gdx.utils.Align
import com.unciv.logic.civilization.TechManager 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.* import com.unciv.ui.utils.*
class TechButton(techName:String, private val techManager: TechManager, isWorldScreen: Boolean = true) : Table(BaseScreen.skin) { 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)) for (building in tech.getEnabledBuildings(techManager.civInfo))
techEnabledIcons.add(ImageGetter.getConstructionImage(building.name).surroundWithCircle(techIconSize)) techEnabledIcons.add(ImageGetter.getConstructionImage(building.name).surroundWithCircle(techIconSize))
for (building in tech.getObsoletedBuildings(techManager.civInfo)) for (obj in tech.getObsoletedObjects(techManager.civInfo)) {
techEnabledIcons.add(ImageGetter.getConstructionImage(building.name).surroundWithCircle(techIconSize).apply { 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) val closeImage = ImageGetter.getRedCross(techIconSize / 2, 1f)
closeImage.center(this) closeImage.center(it)
addActor(closeImage) it.addActor(closeImage)
}) }
techEnabledIcons.add(obsoletedIcon)
}
for (improvement in ruleset.tileImprovements.values.asSequence() for (improvement in ruleset.tileImprovements.values.asSequence()
.filter { .filter {

View File

@ -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) if (improvementName.startsWith(Constants.remove) || improvementName == Constants.cancelImprovementOrder)
return Table().apply { add(getImage("OtherIcons/Stop")).size(size) } return Table().apply { add(getImage("OtherIcons/Stop")).size(size) }
@ -333,7 +333,7 @@ object ImageGetter {
return redCross return redCross
} }
fun getResourceImage(resourceName: String, size: Float): Actor { fun getResourceImage(resourceName: String, size: Float): IconCircleGroup {
val iconGroup = getImage("ResourceIcons/$resourceName").surroundWithCircle(size) val iconGroup = getImage("ResourceIcons/$resourceName").surroundWithCircle(size)
val resource = ruleset.tileResources[resourceName] val resource = ruleset.tileResources[resourceName]
?: return iconGroup // This is the result of a bad modding setup, just give em an empty circle. Their problem. ?: return iconGroup // This is the result of a bad modding setup, just give em an empty circle. Their problem.

View File

@ -769,6 +769,11 @@ Applicable to: Building
#### Unsellable #### Unsellable
Applicable to: Building Applicable to: Building
#### Obsolete with [tech]
Example: "Obsolete with [Agriculture]"
Applicable to: Building, Improvement, Resource
#### Remove extra unhappiness from annexed cities #### Remove extra unhappiness from annexed cities
Applicable to: Building Applicable to: Building