mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-12 16:59:11 +07:00
Modding: Added "non-[filter]" filtering for unit filters
Cleaned up unit filter validations to match actual filters
This commit is contained in:
17
core/src/com/unciv/logic/MultiFilter.kt
Normal file
17
core/src/com/unciv/logic/MultiFilter.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package com.unciv.logic
|
||||
|
||||
object MultiFilter {
|
||||
fun multiFilter(input: String, filterFunction: (String)->Boolean,
|
||||
/** Unique validity doesn't check for actual matching */ forUniqueValidityTests:Boolean=false): Boolean {
|
||||
if (input.contains("} {"))
|
||||
return input.removePrefix("{").removeSuffix("}").split("} {")
|
||||
.all{ multiFilter(it, filterFunction) }
|
||||
if (input.startsWith("non-[") && input.endsWith("]")) {
|
||||
val internalResult = multiFilter(input.removePrefix("non-[").removeSuffix("]"), filterFunction)
|
||||
return if (forUniqueValidityTests) internalResult else !internalResult
|
||||
}
|
||||
return filterFunction(input)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -3,6 +3,7 @@ package com.unciv.logic.map.mapunit
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||
import com.unciv.logic.MultiFilter
|
||||
import com.unciv.logic.automation.unit.UnitAutomation
|
||||
import com.unciv.logic.battle.BattleUnitCapture
|
||||
import com.unciv.logic.battle.MapUnitCombatant
|
||||
@ -24,7 +25,6 @@ import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.ruleset.unit.UnitType
|
||||
import com.unciv.models.stats.Stats
|
||||
import com.unciv.ui.components.UnitMovementMemoryType
|
||||
import com.unciv.ui.components.extensions.filterAndLogic
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.ulp
|
||||
@ -888,9 +888,11 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
||||
|
||||
/** Implements [UniqueParameterType.MapUnitFilter][com.unciv.models.ruleset.unique.UniqueParameterType.MapUnitFilter] */
|
||||
fun matchesFilter(filter: String): Boolean {
|
||||
return filter.filterAndLogic { matchesFilter(it) } // multiple types at once - AND logic. Looks like:"{Military} {Land}"
|
||||
?: when (filter) {
|
||||
return MultiFilter.multiFilter(filter, ::matchesSingleFilter)
|
||||
}
|
||||
|
||||
private fun matchesSingleFilter(filter:String): Boolean {
|
||||
return when (filter) {
|
||||
Constants.wounded, "wounded units" -> health < 100
|
||||
Constants.barbarians, "Barbarian" -> civ.isBarbarian()
|
||||
"City-State" -> civ.isCityState()
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.unciv.models.ruleset.unique
|
||||
|
||||
import com.unciv.Constants
|
||||
import com.unciv.logic.MultiFilter
|
||||
import com.unciv.models.metadata.BaseRuleset
|
||||
import com.unciv.models.ruleset.BeliefType
|
||||
import com.unciv.models.ruleset.Ruleset
|
||||
@ -9,7 +10,6 @@ import com.unciv.models.ruleset.tile.ResourceType
|
||||
import com.unciv.models.ruleset.unique.UniqueParameterType.Companion.guessTypeForTranslationWriter
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.models.translations.TranslationFileWriter
|
||||
import com.unciv.ui.components.extensions.filterCompositeLogic
|
||||
|
||||
// 'region' names beginning with an underscore are used here for a prettier "Structure window" - they go in front ot the rest.
|
||||
|
||||
@ -39,6 +39,8 @@ enum class UniqueParameterType(
|
||||
) {
|
||||
//endregion
|
||||
|
||||
|
||||
|
||||
Number("amount", "3", "This indicates a whole number, possibly with a + or - sign, such as `2`, `+13`, or `-3`") {
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueParameterErrorSeverity? {
|
||||
@ -72,61 +74,76 @@ enum class UniqueParameterType(
|
||||
CombatantFilter("combatantFilter", "City", "This indicates a combatant, which can either be a unit or a city (when bombarding). Must either be `City` or a `mapUnitFilter`") {
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueParameterErrorSeverity? {
|
||||
if (parameterText == "City") return null // City also recognizes "All" but that's covered by UnitTypeFilter too
|
||||
if (parameterText == "City") return null // City also recognizes "All" but that's covered by BaseUnitFilter too
|
||||
return MapUnitFilter.getErrorSeverity(parameterText, ruleset)
|
||||
}
|
||||
},
|
||||
|
||||
/** Implemented by [MapUnit.matchesFilter][com.unciv.logic.map.mapunit.MapUnit.matchesFilter] */
|
||||
MapUnitFilter("mapUnitFilter", Constants.wounded, null, "Map Unit Filters") {
|
||||
private val knownValues = setOf(Constants.wounded, Constants.barbarians, "City-State", Constants.embarked, "Non-City")
|
||||
private val knownValues = setOf(Constants.wounded, Constants.barbarians, "Barbarian",
|
||||
"City-State", Constants.embarked, "Non-City")
|
||||
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueParameterErrorSeverity? {
|
||||
if (parameterText.startsWith('{')) // "{filter} {filter}" for and logic
|
||||
return parameterText.filterCompositeLogic({ getErrorSeverity(it, ruleset) }) { a, b -> maxOf(a, b) }
|
||||
if (parameterText in knownValues) return null
|
||||
if (ruleset.unitPromotions.values.any { it.hasUnique(parameterText) })
|
||||
return null
|
||||
return BaseUnitFilter.getErrorSeverity(parameterText, ruleset)
|
||||
val isKnown = MultiFilter.multiFilter(parameterText, {isKnownValue(it, ruleset)}, true)
|
||||
if (isKnown) return null
|
||||
return UniqueType.UniqueParameterErrorSeverity.PossibleFilteringUnique
|
||||
}
|
||||
|
||||
override fun isKnownValue(parameterText:String, ruleset: Ruleset): Boolean {
|
||||
if (parameterText in knownValues) return true
|
||||
if (ruleset.unitPromotions.values.any { it.hasUnique(parameterText) }) return true
|
||||
if (BaseUnitFilter.isKnownValue(parameterText, ruleset)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getTranslationWriterStringsForOutput() = knownValues
|
||||
},
|
||||
|
||||
/** Implemented by [BaseUnit.matchesFilter][com.unciv.models.ruleset.unit.BaseUnit.matchesFilter] */
|
||||
BaseUnitFilter("baseUnitFilter", "Melee") {
|
||||
private val knownValues = setOf(
|
||||
"All", "Melee", "Ranged", "Civilian", "Military", "non-air",
|
||||
"Nuclear Weapon", "Great Person", "Religious",
|
||||
"relevant", // used for UniqueType.UnitStartingPromotions
|
||||
)
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueParameterErrorSeverity? {
|
||||
if (parameterText.startsWith('{')) // "{filter} {filter}" for and logic
|
||||
return parameterText.filterCompositeLogic({ getErrorSeverity(it, ruleset) }) { a, b -> maxOf(a, b) }
|
||||
if (UnitName.getErrorSeverity(parameterText, ruleset) == null) return null
|
||||
if (ruleset.units.values.any { it.uniques.contains(parameterText) }) return null
|
||||
return UnitTypeFilter.getErrorSeverity(parameterText, ruleset)
|
||||
val isKnown = MultiFilter.multiFilter(parameterText, {isKnownValue(it, ruleset)}, true)
|
||||
if (isKnown) return null
|
||||
return UniqueType.UniqueParameterErrorSeverity.PossibleFilteringUnique
|
||||
}
|
||||
override fun isKnownValue(parameterText:String, ruleset: Ruleset): Boolean {
|
||||
if (parameterText in knownValues) return true
|
||||
if (UnitName.getErrorSeverity(parameterText, ruleset) == null) return true
|
||||
if (ruleset.units.values.any { it.uniques.contains(parameterText) }) return true
|
||||
if (UnitTypeFilter.isKnownValue(parameterText, ruleset)) return true
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
/** Implemented by [UnitType.matchesFilter][com.unciv.models.ruleset.unit.UnitType.matchesFilter] */
|
||||
//todo there is a large discrepancy between this parameter type and the actual filter, most of these are actually implemented by BaseUnitFilter
|
||||
UnitTypeFilter("unitType", "Water", null, "Unit Type Filters") {
|
||||
// As you can see there is a difference between these and what's in unitTypeStrings (for translation) -
|
||||
// the goal is to unify, but for now this is the "real" list
|
||||
// Note: this can't handle combinations of parameters (e.g. [{Military} {Water}])
|
||||
private val knownValues = setOf(
|
||||
"All", "Melee", "Ranged", "Civilian", "Military", "Land", "Water", "Air",
|
||||
"non-air", "Nuclear Weapon", "Great Person", "Religious", "Barbarian",
|
||||
"relevant", "City",
|
||||
// These are up for debate
|
||||
// "land units", "water units", "air units", "military units", "submarine units",
|
||||
"Land", "Water", "Air",
|
||||
)
|
||||
|
||||
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset):
|
||||
UniqueType.UniqueParameterErrorSeverity? {
|
||||
if (parameterText in knownValues) return null
|
||||
if (ruleset.unitTypes.containsKey(parameterText)) return null
|
||||
if (ruleset.eras.containsKey(parameterText)) return null
|
||||
if (ruleset.unitTypes.values.any { it.uniques.contains(parameterText) }) return null
|
||||
val isKnown = MultiFilter.multiFilter(parameterText, {isKnownValue(it, ruleset)}, true)
|
||||
if (isKnown) return null
|
||||
return UniqueType.UniqueParameterErrorSeverity.PossibleFilteringUnique
|
||||
}
|
||||
|
||||
override fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean {
|
||||
if (parameterText in knownValues) return true
|
||||
if (ruleset.unitTypes.containsKey(parameterText)) return true
|
||||
if (ruleset.eras.containsKey(parameterText)) return true
|
||||
if (ruleset.unitTypes.values.any { it.uniques.contains(parameterText) }) return true
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isTranslationWriterGuess(parameterText: String, ruleset: Ruleset) =
|
||||
parameterText in ruleset.unitTypes.keys || parameterText in getTranslationWriterStringsForOutput()
|
||||
|
||||
@ -560,6 +577,8 @@ enum class UniqueParameterType(
|
||||
/** Validate a [Unique] parameter */
|
||||
abstract fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity?
|
||||
|
||||
open fun isKnownValue(parameterText: String, ruleset: Ruleset): Boolean = false
|
||||
|
||||
/** Pick this type when [TranslationFileWriter] tries to guess for an untyped [Unique] */
|
||||
open fun isTranslationWriterGuess(parameterText: String, ruleset: Ruleset): Boolean =
|
||||
getErrorSeverity(parameterText, ruleset) == null
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.unciv.models.ruleset.unit
|
||||
|
||||
import com.unciv.logic.MultiFilter
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.city.CityConstructions
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
@ -15,7 +16,6 @@ import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueTarget
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.ui.components.extensions.filterAndLogic
|
||||
import com.unciv.ui.components.extensions.getNeedMoreAmountString
|
||||
import com.unciv.ui.components.extensions.toPercent
|
||||
import com.unciv.ui.objectdescriptions.BaseUnitDescriptions
|
||||
@ -264,9 +264,11 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
|
||||
/** Implements [UniqueParameterType.BaseUnitFilter][com.unciv.models.ruleset.unique.UniqueParameterType.BaseUnitFilter] */
|
||||
fun matchesFilter(filter: String): Boolean {
|
||||
return filter.filterAndLogic { matchesFilter(it) } // multiple types at once - AND logic. Looks like:"{Military} {Land}"
|
||||
?: when (filter) {
|
||||
return MultiFilter.multiFilter(filter, ::matchesSingleFilter)
|
||||
}
|
||||
|
||||
fun matchesSingleFilter(filter: String): Boolean {
|
||||
return when (filter) {
|
||||
unitType -> true
|
||||
name -> true
|
||||
replaces -> true
|
||||
@ -287,9 +289,9 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
|
||||
else -> {
|
||||
if (type.matchesFilter(filter)) return true
|
||||
if (requiredTech != null && ruleset.technologies[requiredTech]?.matchesFilter(filter)==true) return true
|
||||
if (requiredTech != null && ruleset.technologies[requiredTech]?.matchesFilter(filter) == true) return true
|
||||
if (
|
||||
// Uniques using these kinds of filters should be deprecated and replaced with adjective-only parameters
|
||||
// Uniques using these kinds of filters should be deprecated and replaced with adjective-only parameters
|
||||
filter.endsWith(" units")
|
||||
// "military units" --> "Military", using invariant locale
|
||||
&& matchesFilter(filter.removeSuffix(" units").lowercase().replaceFirstChar { it.uppercaseChar() })
|
||||
|
Reference in New Issue
Block a user