From 12302deef6da7e91afca8c2c400b134084ae8570 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Fri, 24 Sep 2021 15:02:41 +0300 Subject: [PATCH] Precursor to mod target check - (#5303) * Precursor to mod target check - Define acceptable targets for each unique holder * Warn for bad unique targets, and divide between different types of beliefs * Moved the belief type logic into the class, rather than being external * Added comment that getUniqueTarget is currently unneeded, but planned for future use --- core/src/com/unciv/models/ruleset/Belief.kt | 13 +++-- core/src/com/unciv/models/ruleset/Building.kt | 1 + core/src/com/unciv/models/ruleset/Era.kt | 4 +- .../com/unciv/models/ruleset/IHasUniques.kt | 6 +++ core/src/com/unciv/models/ruleset/Nation.kt | 4 +- core/src/com/unciv/models/ruleset/Policy.kt | 4 +- .../com/unciv/models/ruleset/RuinReward.kt | 4 +- core/src/com/unciv/models/ruleset/Ruleset.kt | 17 +++++-- .../unciv/models/ruleset/tech/Technology.kt | 4 +- .../com/unciv/models/ruleset/tile/Terrain.kt | 4 +- .../models/ruleset/tile/TileImprovement.kt | 4 +- .../unciv/models/ruleset/tile/TileResource.kt | 4 +- .../unciv/models/ruleset/unique/UniqueType.kt | 47 +++++++++++++------ .../com/unciv/models/ruleset/unit/BaseUnit.kt | 6 ++- .../unciv/models/ruleset/unit/Promotion.kt | 5 +- .../com/unciv/models/ruleset/unit/UnitType.kt | 4 +- 16 files changed, 99 insertions(+), 32 deletions(-) diff --git a/core/src/com/unciv/models/ruleset/Belief.kt b/core/src/com/unciv/models/ruleset/Belief.kt index 56d8494959..1a4eb476d5 100644 --- a/core/src/com/unciv/models/ruleset/Belief.kt +++ b/core/src/com/unciv/models/ruleset/Belief.kt @@ -7,13 +7,18 @@ import com.unciv.models.stats.INamed import com.unciv.models.translations.tr import com.unciv.ui.civilopedia.FormattedLine import com.unciv.ui.civilopedia.ICivilopediaText -import java.util.ArrayList +import kotlin.collections.ArrayList class Belief : INamed, ICivilopediaText, IHasUniques { override var name: String = "" var type: BeliefType = BeliefType.None override var uniques = ArrayList() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Belief, name) } } + + override fun getUniqueTarget() = + if (type == BeliefType.Founder || type == BeliefType.Enhancer) UniqueTarget.FounderBelief + else UniqueTarget.FollowerBelief + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } override var civilopediaText = listOf() @@ -26,12 +31,12 @@ class Belief : INamed, ICivilopediaText, IHasUniques { val textList = ArrayList() if (type != BeliefType.None) textList += FormattedLine("{Type}: $type", color = type.color ) - uniqueObjects.forEach { + uniqueObjects.forEach { textList += FormattedLine(it) } return textList } - + companion object { // private but potentially reusable, therefore not folded into getCivilopediaTextMatching private fun getBeliefsMatching(name: String, ruleset: Ruleset): Sequence { diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index ff28db4fd6..04e6faad77 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -67,6 +67,7 @@ class Building : NamedStats(), INonPerpetualConstruction, ICivilopediaText { var uniqueTo: String? = null var quote: String = "" override var uniques = ArrayList() + override fun getUniqueTarget() = UniqueTarget.Building override val uniqueObjects: List by lazy { uniques.map { Unique(it, if (isAnyWonder()) UniqueTarget.Wonder else UniqueTarget.Building, name) diff --git a/core/src/com/unciv/models/ruleset/Era.kt b/core/src/com/unciv/models/ruleset/Era.kt index 84ea77bddd..0b224e5221 100644 --- a/core/src/com/unciv/models/ruleset/Era.kt +++ b/core/src/com/unciv/models/ruleset/Era.kt @@ -33,7 +33,9 @@ class Era : INamed, IHasUniques { var iconRGB: List? = null override var uniques: ArrayList = arrayListOf() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Era, name) } } + override fun getUniqueTarget() = UniqueTarget.Era + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } private fun initBonuses(bonusMap: Map>): Map> { val objectMap = HashMap>() diff --git a/core/src/com/unciv/models/ruleset/IHasUniques.kt b/core/src/com/unciv/models/ruleset/IHasUniques.kt index a537c1bc02..31518b13d5 100644 --- a/core/src/com/unciv/models/ruleset/IHasUniques.kt +++ b/core/src/com/unciv/models/ruleset/IHasUniques.kt @@ -1,6 +1,7 @@ package com.unciv.models.ruleset import com.unciv.models.ruleset.unique.Unique +import com.unciv.models.ruleset.unique.UniqueTarget import com.unciv.models.ruleset.unique.UniqueType /** @@ -8,7 +9,12 @@ import com.unciv.models.ruleset.unique.UniqueType */ interface IHasUniques { var uniques: ArrayList // Can not be a hashset as that would remove doubles + // I bet there's a way of initializing these without having to override it everywhere... val uniqueObjects: List + /** Technically not currently needed, since the unique target can be retrieved from every unique in the uniqueObjects, + * But making this a function is relevant for future "unify Unciv object" plans ;) + * */ + fun getUniqueTarget(): UniqueTarget fun getMatchingUniques(uniqueTemplate: String) = uniqueObjects.asSequence().filter { it.placeholderText == uniqueTemplate } fun getMatchingUniques(uniqueType: UniqueType) = uniqueObjects.asSequence().filter { it.isOfType(uniqueType) } diff --git a/core/src/com/unciv/models/ruleset/Nation.kt b/core/src/com/unciv/models/ruleset/Nation.kt index 84213d6f49..3f5702de6b 100644 --- a/core/src/com/unciv/models/ruleset/Nation.kt +++ b/core/src/com/unciv/models/ruleset/Nation.kt @@ -44,7 +44,9 @@ class Nation : INamed, ICivilopediaText, IHasUniques { lateinit var outerColor: List var uniqueName = "" override var uniques = ArrayList() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Nation, name) } } + override fun getUniqueTarget() = UniqueTarget.Nation + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } var uniqueText = "" var innerColor: List? = null var startBias = ArrayList() diff --git a/core/src/com/unciv/models/ruleset/Policy.kt b/core/src/com/unciv/models/ruleset/Policy.kt index c25a125876..cd5c2bebcc 100644 --- a/core/src/com/unciv/models/ruleset/Policy.kt +++ b/core/src/com/unciv/models/ruleset/Policy.kt @@ -12,7 +12,9 @@ open class Policy : INamed, IHasUniques, ICivilopediaText { override lateinit var name: String override var uniques: ArrayList = ArrayList() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Policy, name) } } + override fun getUniqueTarget() = UniqueTarget.Policy + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } var row: Int = 0 var column: Int = 0 var requires: ArrayList? = null diff --git a/core/src/com/unciv/models/ruleset/RuinReward.kt b/core/src/com/unciv/models/ruleset/RuinReward.kt index d8a03a5289..58afad0d6c 100644 --- a/core/src/com/unciv/models/ruleset/RuinReward.kt +++ b/core/src/com/unciv/models/ruleset/RuinReward.kt @@ -10,9 +10,11 @@ class RuinReward : INamed, ICivilopediaText, IHasUniques { override lateinit var name: String // Displayed in Civilopedia! val notification: String = "" override var uniques = ArrayList() + + override fun getUniqueTarget() = UniqueTarget.Ruins @delegate:Transient // Defense in depth against mad modders override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Ruins, name) } } - + val excludedDifficulties: List = listOf() val weight: Int = 1 val color: String = "" // For Civilopedia diff --git a/core/src/com/unciv/models/ruleset/Ruleset.kt b/core/src/com/unciv/models/ruleset/Ruleset.kt index 21ecc7301e..c32c6af086 100644 --- a/core/src/com/unciv/models/ruleset/Ruleset.kt +++ b/core/src/com/unciv/models/ruleset/Ruleset.kt @@ -51,6 +51,8 @@ class ModOptions : IHasUniques { override var uniques = ArrayList() // If this is delegated with "by lazy", the mod download process crashes and burns override var uniqueObjects: List = listOf() + override fun getUniqueTarget() = UniqueTarget.ModOptions + } class Ruleset { @@ -296,14 +298,23 @@ class Ruleset { val deprecationAnnotation = unique.type.declaringClass.getField(unique.type.name) .getAnnotation(Deprecated::class.java) if (deprecationAnnotation != null) { - val deprecationText = "$name's unique \"${unique.text}\" is deprecated ${deprecationAnnotation.message}," + - " replace with \"${deprecationAnnotation.replaceWith.expression}\"" - val severity = if(deprecationAnnotation.level == DeprecationLevel.WARNING) + val deprecationText = + "$name's unique \"${unique.text}\" is deprecated ${deprecationAnnotation.message}," + + " replace with \"${deprecationAnnotation.replaceWith.expression}\"" + val severity = if (deprecationAnnotation.level == DeprecationLevel.WARNING) RulesetErrorSeverity.WarningOptionsOnly // Not user-visible else RulesetErrorSeverity.Warning // User visible lines.add(deprecationText, severity) } + + val acceptableUniqueType = uniqueContainer.getUniqueTarget() + if (unique.type.targetTypes.none { acceptableUniqueType.canAcceptUniqueTarget(it) }) + lines.add( + "$name's unique \"${unique.text}\" cannot be put on this type of object!", + RulesetErrorSeverity.Warning + ) + } } diff --git a/core/src/com/unciv/models/ruleset/tech/Technology.kt b/core/src/com/unciv/models/ruleset/tech/Technology.kt index d6d2ff0c0d..53b83bac0d 100644 --- a/core/src/com/unciv/models/ruleset/tech/Technology.kt +++ b/core/src/com/unciv/models/ruleset/tech/Technology.kt @@ -23,7 +23,9 @@ class Technology: INamed, ICivilopediaText, IHasUniques { var cost: Int = 0 var prerequisites = HashSet() override var uniques = ArrayList() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Tech, name) } } + override fun getUniqueTarget() = UniqueTarget.Tech + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } var column: TechColumn? = null // The column that this tech is in the tech tree var row: Int = 0 diff --git a/core/src/com/unciv/models/ruleset/tile/Terrain.kt b/core/src/com/unciv/models/ruleset/tile/Terrain.kt index a0b325cd71..7111939f66 100644 --- a/core/src/com/unciv/models/ruleset/tile/Terrain.kt +++ b/core/src/com/unciv/models/ruleset/tile/Terrain.kt @@ -29,7 +29,9 @@ class Terrain : NamedStats(), ICivilopediaText, IHasUniques { /** Uniques (Properties such as Temp/humidity, Fresh water, elevation, rough, defense, Natural Wonder specials) */ override var uniques = ArrayList() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Terrain, name) } } + override fun getUniqueTarget() = UniqueTarget.Terrain + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } /** Natural Wonder weight: probability to be picked */ var weight = 10 diff --git a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt index f7ad2800c8..9bd45ff2d3 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt @@ -23,7 +23,9 @@ class TileImprovement : NamedStats(), ICivilopediaText, IHasUniques { var techRequired: String? = null var uniqueTo:String? = null override var uniques = ArrayList() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Improvement, name) } } + override fun getUniqueTarget() = UniqueTarget.Improvement + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } val shortcutKey: Char? = null val turnsToBuild: Int = 0 // This is the base cost. diff --git a/core/src/com/unciv/models/ruleset/tile/TileResource.kt b/core/src/com/unciv/models/ruleset/tile/TileResource.kt index 23c4dc82f7..a3288e89a9 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileResource.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileResource.kt @@ -20,7 +20,9 @@ class TileResource : NamedStats(), ICivilopediaText, IHasUniques { @Deprecated("As of 3.16.16 - replaced by uniques") var unique: String? = null override var uniques: ArrayList = arrayListOf() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Resource, name) } } + override fun getUniqueTarget() = UniqueTarget.Resource + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } override var civilopediaText = listOf() diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index a8a94eeaf8..5068526a2d 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -3,27 +3,38 @@ package com.unciv.models.ruleset.unique import com.unciv.models.ruleset.Ruleset import com.unciv.models.translations.getPlaceholderParameters import com.unciv.models.translations.getPlaceholderText +import kotlin.collections.ArrayList +import kotlin.collections.HashSet -/** Buildings, units, nations, policies, religions, techs etc. - * Basically anything caught by CivInfo.getMatchingUniques. */ -enum class UniqueTarget { +/** inheritsFrom means that all such uniques are acceptable as well. + * For example, all Global uniques are acceptable for Nations, Eras, etc. */ +enum class UniqueTarget(val inheritsFrom:UniqueTarget?=null) { + + /** Buildings, units, nations, policies, religions, techs etc. + * Basically anything caught by CivInfo.getMatchingUniques. */ Global, // Civilization-specific - Nation, - Era, - Tech, - Policy, - Belief, + Nation(Global), + Era(Global), + Tech(Global), + Policy(Global), + FounderBelief(Global), + /** These apply only to cities where the religion is the majority religion */ + FollowerBelief, // City-specific - Building, - Wonder, + /** This is used as the base when checking buildings */ + Building(Global), + Wonder(Building), // Unit-specific + // These are a bit of a lie. There's no "Promotion only" or "UnitType only" uniques, + // they're all just Unit uniques in different places. + // So there should be no uniqueType that has a Promotion or UnitType target. Unit, - UnitType, - Promotion, + UnitType(Unit), + Promotion(Unit), // Tile-specific Terrain, @@ -35,9 +46,16 @@ enum class UniqueTarget { CityState, ModOptions, Conditional, + ; + + fun canAcceptUniqueTarget(uniqueTarget: UniqueTarget): Boolean { + if (this == uniqueTarget) return true + if (inheritsFrom != null) return inheritsFrom.canAcceptUniqueTarget(uniqueTarget) + return false + } } -enum class UniqueType(val text:String, vararg target: UniqueTarget) { +enum class UniqueType(val text:String, vararg targets: UniqueTarget) { Stats("[stats]", UniqueTarget.Global), StatsPerCity("[stats] [cityFilter]", UniqueTarget.Global), @@ -91,7 +109,7 @@ enum class UniqueType(val text:String, vararg target: UniqueTarget) { /** For uniques that have "special" parameters that can accept multiple types, we can override them manually * For 95% of cases, auto-matching is fine. */ val parameterTypeMap = ArrayList>() - val replacedBy: UniqueType? = null + val targetTypes = HashSet() init { for (placeholder in text.getPlaceholderParameters()) { @@ -100,6 +118,7 @@ enum class UniqueType(val text:String, vararg target: UniqueTarget) { ?: UniqueParameterType.Unknown parameterTypeMap.add(listOf(matchingParameterType)) } + targetTypes.addAll(targets) } val placeholderText = text.getPlaceholderText() diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index f097e1f9a7..ecbfd9ba20 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -40,8 +40,12 @@ class BaseUnit : INamed, INonPerpetualConstruction, ICivilopediaText { fun getType() = ruleset.unitTypes[unitType]!! var requiredTech: String? = null private var requiredResource: String? = null + override var uniques = ArrayList() // Can not be a hashset as that would remove doubles - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Unit, name) } } + override fun getUniqueTarget() = UniqueTarget.Unit + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } + private var replacementTextForUniques = "" var promotions = HashSet() var obsoleteTech: String? = null diff --git a/core/src/com/unciv/models/ruleset/unit/Promotion.kt b/core/src/com/unciv/models/ruleset/unit/Promotion.kt index efc790e37c..39547ac3dd 100644 --- a/core/src/com/unciv/models/ruleset/unit/Promotion.kt +++ b/core/src/com/unciv/models/ruleset/unit/Promotion.kt @@ -23,7 +23,10 @@ class Promotion : INamed, ICivilopediaText, IHasUniques { if (effect.isNotEmpty()) yield(effect) yieldAll(uniques) } - override val uniqueObjects: List by lazy { uniquesWithEffect().map { Unique(it, UniqueTarget.Promotion, name) }.toList() } + + override fun getUniqueTarget() = UniqueTarget.Promotion + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } override var civilopediaText = listOf() diff --git a/core/src/com/unciv/models/ruleset/unit/UnitType.kt b/core/src/com/unciv/models/ruleset/unit/UnitType.kt index f45559aefe..0e2fb5b491 100644 --- a/core/src/com/unciv/models/ruleset/unit/UnitType.kt +++ b/core/src/com/unciv/models/ruleset/unit/UnitType.kt @@ -24,7 +24,9 @@ class UnitType() : INamed, IHasUniques { private val unitMovementType: UnitMovementType? by lazy { if (movementType == null) null else UnitMovementType.valueOf(movementType!!) } override var uniques: ArrayList = ArrayList() - override val uniqueObjects: List by lazy { uniques.map { Unique(it, UniqueTarget.Unit, name) } } + override fun getUniqueTarget() = UniqueTarget.UnitType + override val uniqueObjects: List by lazy { uniques.map { Unique(it, + getUniqueTarget(), name) } } constructor(name: String, domain: String? = null) : this() { this.name = name