New countable for adopted policies supports policyFilter (#13191)

* New countable: Countables.FilteredPolicies with test

* Result of doc autoupdate

* Result of doc autoupdate

* Some clarifying comments

* Remove policy filter with bad wording
This commit is contained in:
SomeTroglodyte 2025-05-30 15:41:17 +02:00 committed by GitHub
parent 299c043e99
commit 72e0460bcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 80 additions and 5 deletions

View File

@ -49,7 +49,6 @@ open class Policy : RulesetObject() {
return when(filter) {
in Constants.all -> true
name -> true
"[all] branch" -> branch == this
"[${branch.name}] branch" -> true
else -> false
}

View File

@ -120,6 +120,18 @@ enum class Countables(
override fun getKnownValuesForAutocomplete(ruleset: Ruleset) = setOf<String>()
},
FilteredPolicies("Adopted [policyFilter] Policies") {
override fun eval(parameterText: String, stateForConditionals: StateForConditionals): Int? {
val filter = parameterText.getPlaceholderParameters()[0]
val policyManager = stateForConditionals.civInfo?.policies ?: return null
return policyManager.getAdoptedPoliciesMatching(filter, stateForConditionals).size
}
override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): UniqueType.UniqueParameterErrorSeverity? =
UniqueParameterType.PolicyFilter.getTranslatedErrorSeverity(parameterText, ruleset)
override fun getKnownValuesForAutocomplete(ruleset: Ruleset): Set<String> =
UniqueParameterType.PolicyFilter.getKnownValuesForAutocomplete(ruleset)
},
RemainingCivs("Remaining [civFilter] Civilizations") {
override fun eval(parameterText: String, stateForConditionals: StateForConditionals): Int? {
val filter = parameterText.getPlaceholderParameters()[0]

View File

@ -561,10 +561,9 @@ enum class UniqueParameterType(
PolicyFilter("policyFilter", "Oligarchy",
"The name of any policy, a filtering Unique, any branch (matching only the branch itself)," +
" a branch name with \" Completed\" appended (matches if the branch is completed)," +
" a policy branch as `[branchName] branch` (matching all policies in that branch)," +
" or `[all] branch` which matches all branch starter policies."
" or a policy branch as `[branchName] branch` (matching all policies in that branch)."
) {
override val staticKnownValues = Constants.all + "[all] branch"
override val staticKnownValues = Constants.all
override fun isKnownValue(parameterText: String, ruleset: Ruleset) = when {
parameterText in staticKnownValues -> true
parameterText in ruleset.policies -> true

View File

@ -363,6 +363,8 @@ Allowed values:
- Example: `Only available <when number of [[Wounded] Units] is more than [0]>`
- `[buildingFilter] Buildings`
- Example: `Only available <when number of [[Culture] Buildings] is more than [0]>`
- `Adopted [policyFilter] Policies`
- Example: `Only available <when number of [Adopted [Oligarchy] Policies] is more than [0]>`
- `Remaining [civFilter] Civilizations`
- Example: `Only available <when number of [Remaining [City-States] Civilizations] is more than [0]>`
- `Owned [tileFilter] Tiles`

View File

@ -3559,7 +3559,7 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl
*[modFilter]: A Mod name, case-sensitive _or_ a simple wildcard filter beginning and ending in an Asterisk, case-insensitive.
*[nonNegativeAmount]: This indicates a non-negative whole number, larger than or equal to zero, a '+' sign is optional.
*[policy]: The name of any policy.
*[policyFilter]: The name of any policy, a filtering Unique, any branch (matching only the branch itself), a branch name with " Completed" appended (matches if the branch is completed), a policy branch as `[branchName] branch` (matching all policies in that branch), or `[all] branch` which matches all branch starter policies.
*[policyFilter]: The name of any policy, a filtering Unique, any branch (matching only the branch itself), a branch name with " Completed" appended (matches if the branch is completed), or a policy branch as `[branchName] branch` (matching all policies in that branch).
*[positiveAmount]: This indicates a positive whole number, larger than zero, a '+' sign is optional.
*[promotion]: The name of any promotion.
*[relativeAmount]: This indicates a number, usually with a + or - sign, such as `+25` (this kind of parameter is often followed by '%' which is nevertheless not part of the value).

View File

@ -20,6 +20,7 @@ import com.unciv.models.ruleset.BeliefType
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.IRulesetObject
import com.unciv.models.ruleset.Policy
import com.unciv.models.ruleset.PolicyBranch
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.RulesetCache
import com.unciv.models.ruleset.Specialist
@ -263,8 +264,37 @@ class TestGame(vararg addGlobalUniques: String) {
createdBuilding.isWonder = true
return createdBuilding
}
/**
* Creates a new Policy.
* - This is internally inconsistent as it has no branch, and thus only supports simple tests.
* - Do not run policyFilter on a TestGame with such a Policy (or you'll get a lateinit not initialized exception).
* - Use [createPolicyBranch] to create a wrapping branch and a consistent state that supports testing filters.
*/
fun createPolicy(vararg uniques: String) =
createRulesetObject(ruleset.policies, *uniques) { Policy() }
/**
* Creates a new PolicyBranch, optionally including [policy] as member Policy,
* and including its own "... complete" Policy.
* @see createPolicy
*/
fun createPolicyBranch(vararg uniques: String, policy: Policy? = null): PolicyBranch {
val branch = createRulesetObject(ruleset.policyBranches, *uniques) { PolicyBranch() }
branch.branch = branch
ruleset.policies[branch.name] = branch
if (policy != null) {
policy.branch = branch
branch.policies.add(policy)
}
val complete = Policy()
complete.name = branch.name + Policy.branchCompleteSuffix
complete.branch = branch
branch.policies.add(complete)
ruleset.policies[complete.name] = complete
return branch
}
fun createTileImprovement(vararg uniques: String) =
createRulesetObject(ruleset.tileImprovements, *uniques) { TileImprovement() }
fun createUnitType(vararg uniques: String) =

View File

@ -188,6 +188,37 @@ class CountableTests {
assertEquals(10.5f, civCulture, 0.005f)
}
@Test
fun testPoliciesCountables() {
setupModdedGame()
civ.policies.run {
for (name in listOf(
"Tradition", "Aristocracy", "Legalism", "Oligarchy", "Landed Elite", "Monarchy",
"Liberty", "Citizenship", "Honor", "Piety"
)) {
freePolicies++
val policy = getPolicyByName(name)
adopt(policy)
}
// Don't use a fake Policy without a branch, the policyFilter would stumble over a lateinit.
val taggedPolicyBranch = game.createPolicyBranch("Some marker")
freePolicies++
adopt(taggedPolicyBranch) // Will be completed as it has no member policies
}
val tests = listOf(
"Completed Policy branches" to 2, // Tradition and taggedPolicyBranch
"Adopted [Tradition Complete] Policies" to 1,
"Adopted [[Tradition] branch] Policies" to 7, // Branch start and completion plus 5 members
"Adopted [Liberty Complete] Policies" to 0,
"Adopted [[Liberty] branch] Policies" to 2, // Liberty has only 1 member adopted
"Adopted [Some marker] Policies" to 1,
)
for ((test, expected) in tests) {
val actual = Countables.getCountableAmount(test, StateForConditionals(civ))
assertEquals("Testing `$test` countable:", expected, actual)
}
}
@Test
fun testRulesetValidation() {
/** These are `Pair<String, Int>` with the second being the expected number of parameters to fail UniqueParameterType validation */
@ -205,6 +236,8 @@ class CountableTests {
"[+1 Food] <when number of [[Barbarian] Units] is between [[Japanese] Units] and [[Embarked] Units]>" to 1,
"[+1 Food] <when number of [[Science] Buildings] is between [[Wonder] Buildings] and [[All] Buildings]>" to 0,
"[+1 Food] <when number of [[42] Buildings] is between [[Universe] Buildings] and [[Library] Buildings]>" to 2,
"[+1 Food] <when number of [Adopted [Tradition] Policies] is between [Adopted [[Tradition] branch] Policies] and [Adopted [all] Policies]>" to 0,
"[+1 Food] <when number of [Adopted [[Legalism] branch] Policies] is between [Adopted [Folklore] Policies] and [Completed Policy branches]>" to 2,
"[+1 Food] <when number of [Remaining [Human player] Civilizations] is between [Remaining [City-State] Civilizations] and [Remaining [Major] Civilizations]>" to 0,
"[+1 Food] <when number of [Remaining [city-state] Civilizations] is between [Remaining [la la la] Civilizations] and [Remaining [all] Civilizations]>" to 2,
"[+1 Food] <when number of [Owned [Land] Tiles] is between [Owned [Desert] Tiles] and [Owned [All] Tiles]>" to 0,