From 72e0460bcb359675a0ec938f62644790d9547730 Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Fri, 30 May 2025 15:41:17 +0200 Subject: [PATCH] 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 --- core/src/com/unciv/models/ruleset/Policy.kt | 1 - .../unciv/models/ruleset/unique/Countables.kt | 12 +++++++ .../ruleset/unique/UniqueParameterType.kt | 5 ++- docs/Modders/Unique-parameters.md | 2 ++ docs/Modders/uniques.md | 2 +- tests/src/com/unciv/testing/TestGame.kt | 30 +++++++++++++++++ tests/src/com/unciv/uniques/CountableTests.kt | 33 +++++++++++++++++++ 7 files changed, 80 insertions(+), 5 deletions(-) diff --git a/core/src/com/unciv/models/ruleset/Policy.kt b/core/src/com/unciv/models/ruleset/Policy.kt index bc970802d1..c1695a13cb 100644 --- a/core/src/com/unciv/models/ruleset/Policy.kt +++ b/core/src/com/unciv/models/ruleset/Policy.kt @@ -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 } diff --git a/core/src/com/unciv/models/ruleset/unique/Countables.kt b/core/src/com/unciv/models/ruleset/unique/Countables.kt index 612d0fc141..8822357efb 100644 --- a/core/src/com/unciv/models/ruleset/unique/Countables.kt +++ b/core/src/com/unciv/models/ruleset/unique/Countables.kt @@ -120,6 +120,18 @@ enum class Countables( override fun getKnownValuesForAutocomplete(ruleset: Ruleset) = setOf() }, + 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 = + UniqueParameterType.PolicyFilter.getKnownValuesForAutocomplete(ruleset) + }, + RemainingCivs("Remaining [civFilter] Civilizations") { override fun eval(parameterText: String, stateForConditionals: StateForConditionals): Int? { val filter = parameterText.getPlaceholderParameters()[0] diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt index 5d9c7a1f34..6df85d290c 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt @@ -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 diff --git a/docs/Modders/Unique-parameters.md b/docs/Modders/Unique-parameters.md index 1f6c76c9b6..4bd21644ea 100644 --- a/docs/Modders/Unique-parameters.md +++ b/docs/Modders/Unique-parameters.md @@ -363,6 +363,8 @@ Allowed values: - Example: `Only available ` - `[buildingFilter] Buildings` - Example: `Only available ` +- `Adopted [policyFilter] Policies` + - Example: `Only available ` - `Remaining [civFilter] Civilizations` - Example: `Only available ` - `Owned [tileFilter] Tiles` diff --git a/docs/Modders/uniques.md b/docs/Modders/uniques.md index 8214ad1999..9d90855d52 100644 --- a/docs/Modders/uniques.md +++ b/docs/Modders/uniques.md @@ -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). diff --git a/tests/src/com/unciv/testing/TestGame.kt b/tests/src/com/unciv/testing/TestGame.kt index 71dc339bb4..24d5cae64c 100644 --- a/tests/src/com/unciv/testing/TestGame.kt +++ b/tests/src/com/unciv/testing/TestGame.kt @@ -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) = diff --git a/tests/src/com/unciv/uniques/CountableTests.kt b/tests/src/com/unciv/uniques/CountableTests.kt index 8bb3decf18..16b95ed78a 100644 --- a/tests/src/com/unciv/uniques/CountableTests.kt +++ b/tests/src/com/unciv/uniques/CountableTests.kt @@ -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` with the second being the expected number of parameters to fail UniqueParameterType validation */ @@ -205,6 +236,8 @@ class CountableTests { "[+1 Food] " to 1, "[+1 Food] " to 0, "[+1 Food] " to 2, + "[+1 Food] " to 0, + "[+1 Food] " to 2, "[+1 Food] " to 0, "[+1 Food] " to 2, "[+1 Food] " to 0,