mirror of
https://github.com/yairm210/Unciv.git
synced 2025-07-28 21:58:54 +07:00
MultiFilter gets 'or' capability
This commit is contained in:
@ -1583,7 +1583,9 @@ Resource [resource] does not exist in ruleset! =
|
||||
Improvement [improvement] does not exist in ruleset! =
|
||||
Nation [nation] does not exist in ruleset! =
|
||||
Natural Wonder [naturalWonder] does not exist in ruleset! =
|
||||
|
||||
non-[filter] =
|
||||
[filter1] or [filter2] =
|
||||
|
||||
# Civilopedia difficulty levels
|
||||
Player settings =
|
||||
|
@ -6,12 +6,16 @@ object MultiFilter {
|
||||
private const val andSuffix = "}"
|
||||
private const val notPrefix = "non-["
|
||||
private const val notSuffix = "]"
|
||||
private const val orPrefix = "["
|
||||
private const val orSeparator = "] or ["
|
||||
private const val orSuffix = "]"
|
||||
|
||||
/**
|
||||
* Implements `and` and `not` logic on top of a [filterFunction].
|
||||
* Implements `and`, `or` and `not` logic on top of a [filterFunction].
|
||||
*
|
||||
* Syntax:
|
||||
* - `and`: `{filter1} {filter2}`... (can repeat as needed)
|
||||
* - `or`: `[filter1] or [filter2]`... (can repeat as needed)
|
||||
* - `not`: `non-[filter]`
|
||||
* @param input The complex filtering term
|
||||
* @param filterFunction The single filter implementation
|
||||
@ -25,6 +29,9 @@ object MultiFilter {
|
||||
if (input.hasSurrounding(andPrefix, andSuffix) && input.contains(andSeparator))
|
||||
return input.removeSurrounding(andPrefix, andSuffix).split(andSeparator)
|
||||
.all { multiFilter(it, filterFunction, forUniqueValidityTests) }
|
||||
if (input.hasSurrounding(orPrefix, orSuffix) && input.contains(orSeparator))
|
||||
return input.removeSurrounding(orPrefix, orSuffix).split(orSeparator)
|
||||
.any { multiFilter(it, filterFunction, forUniqueValidityTests) }
|
||||
if (input.hasSurrounding(notPrefix, notSuffix)) {
|
||||
//same as `return multiFilter() == forUniqueValidityTests`, but clearer
|
||||
val internalResult = multiFilter(input.removeSurrounding(notPrefix, notSuffix), filterFunction, forUniqueValidityTests)
|
||||
@ -39,6 +46,11 @@ object MultiFilter {
|
||||
input.removeSurrounding(andPrefix, andSuffix)
|
||||
.splitToSequence(andSeparator)
|
||||
.flatMap { getAllSingleFilters(it) }
|
||||
input.hasSurrounding(orPrefix, orSuffix) && input.contains(orSeparator) ->
|
||||
// Resolve "OR" filters
|
||||
input.removeSurrounding(orPrefix, orSuffix)
|
||||
.splitToSequence(orSeparator)
|
||||
.flatMap { getAllSingleFilters(it) }
|
||||
input.hasSurrounding(notPrefix, notSuffix) ->
|
||||
// Simply remove "non" syntax
|
||||
getAllSingleFilters(input.removeSurrounding(notPrefix, notSuffix))
|
||||
|
@ -11,22 +11,29 @@ Note that all of these are case-sensitive!
|
||||
|
||||
## General Filter Rules
|
||||
|
||||
All filters except for `populationFilter` accept multiple values in the format: `{A} {B} {C}` etc, meaning "the object must match ALL of these filters"
|
||||
All filters except for `populationFilter` accept combining sub-filters with 'and', 'or' or 'not' logic.
|
||||
|
||||
'And' logic combines multiple values in the format: `{A} {B} {C}` etc, meaning "the object must match ALL of these filters".
|
||||
|
||||
> Example: `[{Military} {Water}] units`, `[{Wounded} {Armor}] units`, etc.
|
||||
|
||||
No space or other text is allowed between the `[` and the first `{`, nor between the last `}` and the ending `]`. The space in `} {`, however, is mandatory.
|
||||
|
||||
All filters accept `non-[filter]` as a possible value
|
||||
'Or' logic combines multiple values in the format: `[A] or [B] or [C]` etc, meaning "the object must match ANY of these filters".
|
||||
|
||||
> Example: `[[Water] or [Helicopter]] units`, `[[Puppeted] or [Razing]] cities`, etc.
|
||||
|
||||
'Not' logic uses the syntax `non-[filter]`.
|
||||
|
||||
> Example: `[non-[Wounded]] units`
|
||||
|
||||
These can be combined by nesting, with the exception that an "ALL" filter cannot contain another "ALL" filter, even with a NON-filter in between.
|
||||
These can be combined by nesting, with the exception that an "ALL" filter cannot contain another "ALL" filter, or an "ANY" filter cannot contain another "ANY" filter, even with other logic types in between.
|
||||
|
||||
> Example: `[{non-[Wounded]} {Armor}] units` means unit is type Armor and at full health.
|
||||
> Example: `[non-[{Wounded} {Armor}]] units` means unit is neither wounded nor an Armor one.
|
||||
> Example: `[[{Wounded} {Water}] or [{Embarked} {Scout}]] units`
|
||||
|
||||
`[{non-[{Wounded} {Armor}]} {Embarked}] units` WILL FAIL because the game will treat both "} {" at the same time and see `non-[{Wounded` and `Armor}]`, both invalid.
|
||||
However, `[{non-[{Wounded} {Armor}]} {Embarked}] units` WILL FAIL because the game will treat both "} {" at the same time and see `non-[{Wounded` and `Armor}]`, both invalid.
|
||||
|
||||
Display of complex filters in Civilopedia may become unreadable. If so, consider hiding that unique and provide a better wording using the `Comment []` unique separately.
|
||||
|
||||
|
@ -62,6 +62,20 @@ class MultiFilterTests {
|
||||
Assert.assertNull(UniqueParameterType.MapUnitFilter.getErrorSeverity("{Wounded} {Melee} {Land}", game.ruleset))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOrLogic() {
|
||||
Assert.assertTrue(MultiFilter.multiFilter("[A] or [B]", { it=="A"}))
|
||||
Assert.assertTrue(MultiFilter.multiFilter("[A] or [B]", { it=="B"}))
|
||||
Assert.assertFalse(MultiFilter.multiFilter("[A] or [B]", { it=="C"}))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAndNestedInOrLogic() {
|
||||
Assert.assertTrue(MultiFilter.multiFilter("[{A} {B}] or [{C} {D}]", { it=="A" || it == "B"}))
|
||||
Assert.assertTrue(MultiFilter.multiFilter("[{A} {B}] or [{C} {D}]", { it=="C" || it == "D"}))
|
||||
Assert.assertFalse(MultiFilter.multiFilter("[{A} {B}] or [{C} {D}]", { it=="A" || it == "C"}))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test a complete Unique with a complex multi-filter is parsed and validated correctly`() {
|
||||
val text = "Only available <if [Colosseum] is constructed in all [non-[{non-[Resisting]} {non-[Razing]} {non-[Coastal]}]] cities>"
|
||||
@ -109,4 +123,17 @@ class MultiFilterTests {
|
||||
city.isPuppet = true
|
||||
Assert.assertFalse(Conditionals.conditionalApplies(null, conditional, stateForConditionals))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test cityFilter nesting 'or' logic in 'and' logic`() {
|
||||
val condition = "in [{[Puppeted] or [Razing]} {[Coastal] or [non-[Garrisoned]]}] cities"
|
||||
val conditional = Unique(condition)
|
||||
Assert.assertFalse(Conditionals.conditionalApplies(null, conditional, stateForConditionals)) // only [non-[Garrisoned]] is true
|
||||
|
||||
city.isPuppet = true
|
||||
Assert.assertTrue(Conditionals.conditionalApplies(null, conditional, stateForConditionals)) // Puppeted fulfills left of AND, no garrison right
|
||||
|
||||
game.addUnit("Warrior", civ, game.getTile(Vector2.Zero)).fortify()
|
||||
Assert.assertFalse(Conditionals.conditionalApplies(null, conditional, stateForConditionals)) // Adding garrison will make right term false
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user