From 4fb4722e3ab53a491b37201c7bac923564de6ee6 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Sat, 12 Feb 2022 19:03:11 +0200 Subject: [PATCH] Arbitrary conditionals can become timed conditionals! (#6134) * Arbitrary conditionals can become timed conditionals! triggerCivwideUniques is activated in 3 situations: - Tech complete - Policy adopted - Building complete So for each of the unique containers for these I added a catch saying 'if this unique is a temporary unique then don't save it yourself This has been lying around in todos for a while and generalizes one of the nasty hardcoded messes that I hate about Civ V. Why would you introduce ONE timed effect in the whole game?! You can use them all the time like Humankind or never use them like Civ IV, but why have just one whyyy One more thing in this PR, that needed to be solved by the by, is allowing parameters to have a '2' on the end so we can autoreplace multiple parameters of the same type. Using a regex is slightly more inefficient, but since this is A. only used once per UniqueType.kt, and B. allows us to have as many 'amount's as we want which is important if we have a lot of conditionals, I feel this is justified. * Fixed comments from PR --- .../jsons/Civ V - Gods & Kings/Policies.json | 3 ++- android/assets/jsons/Civ V - Vanilla/Policies.json | 2 +- core/src/com/unciv/logic/city/CityConstructions.kt | 3 ++- .../unciv/logic/civilization/CivilizationInfo.kt | 7 ++++--- .../com/unciv/logic/civilization/PolicyManager.kt | 5 +++-- .../com/unciv/logic/civilization/TechManager.kt | 3 ++- core/src/com/unciv/models/ruleset/unique/Unique.kt | 2 +- .../ruleset/unique/UniqueTriggerActivation.kt | 6 ++++++ .../com/unciv/models/ruleset/unique/UniqueType.kt | 14 ++++++++++---- docs/uniques.md | 11 ++++++----- 10 files changed, 37 insertions(+), 19 deletions(-) diff --git a/android/assets/jsons/Civ V - Gods & Kings/Policies.json b/android/assets/jsons/Civ V - Gods & Kings/Policies.json index 8a261c1fdf..31b6edc79b 100644 --- a/android/assets/jsons/Civ V - Gods & Kings/Policies.json +++ b/android/assets/jsons/Civ V - Gods & Kings/Policies.json @@ -407,7 +407,8 @@ }, { "name": "Autocracy Complete", - "uniques": ["+[25]% attack strength to all [Military] units for [50] turns", + "uniques": [ + "[+25]% Strength ", "May buy [Great General] units for [1000] [Faith] [in all cities in which the majority religion is a major religion] at an increasing price ([500]) ", "May buy [Great Admiral] units for [1000] [Faith] [in all cities in which the majority religion is a major religion] at an increasing price ([500]) " ] diff --git a/android/assets/jsons/Civ V - Vanilla/Policies.json b/android/assets/jsons/Civ V - Vanilla/Policies.json index 7310d0fe0e..ee445ecc8a 100644 --- a/android/assets/jsons/Civ V - Vanilla/Policies.json +++ b/android/assets/jsons/Civ V - Vanilla/Policies.json @@ -400,7 +400,7 @@ }, { "name": "Autocracy Complete", - "uniques": ["+[25]% attack strength to all [Military] units for [30] turns"] + "uniques": ["[+25]% Strength "] } ] }, diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index f0e523f486..121d0fee08 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -462,7 +462,8 @@ class CityConstructions { builtBuildingUniqueMap.clear() for (building in getBuiltBuildings()) for (unique in building.uniqueObjects) - builtBuildingUniqueMap.addUnique(unique) + if (unique.conditionals.none { it.type == UniqueType.ConditionalTimedUnique }) + builtBuildingUniqueMap.addUnique(unique) } /** diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 2a69b030d4..d0c9a66cb1 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -21,7 +21,6 @@ import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.TileResource import com.unciv.models.ruleset.unique.StateForConditionals import com.unciv.models.ruleset.unique.TemporaryUnique -import com.unciv.models.ruleset.unique.Unique import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.stats.Stat @@ -150,6 +149,7 @@ class CivilizationInfo { /** Arraylist instead of HashMap as the same unique might appear multiple times * We don't use pairs, as these cannot be serialized due to having no no-arg constructor + * This can also contain NON-temporary uniques but I can't be bothered to do the deprecation dance with this one */ val temporaryUniques = ArrayList() @@ -864,9 +864,10 @@ class CivilizationInfo { // Update turn counter for temporary uniques for (unique in temporaryUniques) { - unique.turnsLeft -= 1 + if (unique.turnsLeft >= 0) + unique.turnsLeft -= 1 } - temporaryUniques.removeAll { it.turnsLeft <= 0 } + temporaryUniques.removeAll { it.turnsLeft == 0 } goldenAges.endTurn(getHappiness()) getCivUnits().forEach { it.endTurn() } // This is the most expensive part of endTurn diff --git a/core/src/com/unciv/logic/civilization/PolicyManager.kt b/core/src/com/unciv/logic/civilization/PolicyManager.kt index c245201a21..434ee5d5c7 100644 --- a/core/src/com/unciv/logic/civilization/PolicyManager.kt +++ b/core/src/com/unciv/logic/civilization/PolicyManager.kt @@ -54,8 +54,9 @@ class PolicyManager { fun addPolicyToTransients(policy: Policy) { for (unique in policy.uniqueObjects) { - // Should be replaced with a conditional of the same form later - if (!unique.text.contains(turnCountRegex)) + // Should be deprecated together with TimedAttackStrength so I'm putting this here so the compiler will complain if we don't + val rememberToDeprecate = UniqueType.TimedAttackStrength + if (!unique.text.contains(turnCountRegex) && unique.conditionals.none { it.type == UniqueType.ConditionalTimedUnique }) policyUniques.addUnique(unique) } } diff --git a/core/src/com/unciv/logic/civilization/TechManager.kt b/core/src/com/unciv/logic/civilization/TechManager.kt index 73ce340473..ab6e051d38 100644 --- a/core/src/com/unciv/logic/civilization/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/TechManager.kt @@ -365,7 +365,8 @@ class TechManager { fun addTechToTransients(tech: Technology) { for (unique in tech.uniqueObjects) - techUniques.addUnique(unique) + if (unique.conditionals.none { it.type == UniqueType.ConditionalTimedUnique }) + techUniques.addUnique(unique) } fun setTransients() { diff --git a/core/src/com/unciv/models/ruleset/unique/Unique.kt b/core/src/com/unciv/models/ruleset/unique/Unique.kt index cadcbdc1a5..1b41928493 100644 --- a/core/src/com/unciv/models/ruleset/unique/Unique.kt +++ b/core/src/com/unciv/models/ruleset/unique/Unique.kt @@ -85,6 +85,7 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s } return when (condition.type) { + UniqueType.ConditionalTimedUnique -> true UniqueType.ConditionalWar -> state.civInfo?.isAtWar() == true UniqueType.ConditionalNotWar -> state.civInfo?.isAtWar() == false UniqueType.ConditionalWithResource -> state.civInfo?.hasResource(condition.params[0]) == true @@ -224,7 +225,6 @@ class UniqueMapTyped: EnumMap>(UniqueType::class.j } -// Will probably be allowed to be used as a conditional when I get the motivation to work on that -xlenstra class TemporaryUnique() { constructor(uniqueObject: Unique, turns: Int) : this() { diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueTriggerActivation.kt b/core/src/com/unciv/models/ruleset/unique/UniqueTriggerActivation.kt index 269da92e8d..f2a97a8d68 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueTriggerActivation.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueTriggerActivation.kt @@ -31,6 +31,12 @@ object UniqueTriggerActivation { if (!unique.conditionalsApply(StateForConditionals(civInfo, cityInfo))) return false + val timingConditional = unique.conditionals.firstOrNull{it.type == ConditionalTimedUnique} + if (timingConditional!=null) { + civInfo.temporaryUniques.add(TemporaryUnique(unique, timingConditional.params[0].toInt())) + return true + } + @Suppress("NON_EXHAUSTIVE_WHEN") // Yes we're not treating all types here when (unique.type) { OneTimeFreeUnit -> { diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 8b1d3ab454..52b8998f0e 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -548,6 +548,8 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: ConditionalPolicy("after adopting [policy]", UniqueTarget.Conditional), ConditionalNoPolicy("before adopting [policy]", UniqueTarget.Conditional), + ConditionalTimedUnique("for [amount] turns", UniqueTarget.Conditional), + /////// city conditionals ConditionalSpecialistCount("if this city has at least [amount] specialists", UniqueTarget.Conditional), ConditionalFollowerCount("in cities where this religion has at least [amount] followers", UniqueTarget.Conditional), @@ -614,9 +616,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Global), // Not used in Vanilla // todo: remove forced sign StrategicResourcesIncrease("Quantity of strategic resources produced by the empire +[amount]%", UniqueTarget.Global), // used in Policy - // todo: remove forced sign - // todo: convert to "[amount]% Strength " - TimedAttackStrength("+[amount]% attack strength to all [mapUnitFilter] units for [amount] turns", UniqueTarget.Global), // used in Policy + + @Deprecated("as of 3.19.8", ReplaceWith("[+amount]% Strength ")) + TimedAttackStrength("+[amount]% attack strength to all [mapUnitFilter] units for [amount2] turns", UniqueTarget.Global), // used in Policy FreeStatBuildings("Provides the cheapest [stat] building in your first [amount] cities for free", UniqueTarget.Global), // used in Policy FreeSpecificBuildings("Provides a [buildingName] in your first [amount] cities for free", UniqueTarget.Global), // used in Policy @@ -811,7 +813,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: for (placeholder in text.getPlaceholderParameters()) { val matchingParameterTypes = placeholder .split('/') - .map { UniqueParameterType.safeValueOf(it) } + .map { UniqueParameterType.safeValueOf(it.replace(numberRegex, "")) } parameterTypeMap.add(matchingParameterTypes) } targetTypes.addAll(targets) @@ -854,5 +856,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: fun getDeprecationAnnotation(): Deprecated? = declaringClass.getField(name) .getAnnotation(Deprecated::class.java) + } +// I didn't put this is a companion object because APPARENTLY doing that means you can't use it in the init function. +val numberRegex = Regex("\\d") // I really doubt we'll get to double-digit numbers of parameters in a single unique. + diff --git a/docs/uniques.md b/docs/uniques.md index f3b366357e..a4906289cb 100644 --- a/docs/uniques.md +++ b/docs/uniques.md @@ -624,11 +624,6 @@ Example: "Quantity of strategic resources produced by the empire +[20]%" Applicable to: Global -#### +[amount]% attack strength to all [mapUnitFilter] units for [amount] turns -Example: "+[20]% attack strength to all [Wounded] units for [20] turns" - -Applicable to: Global - #### Provides the cheapest [stat] building in your first [amount] cities for free Example: "Provides the cheapest [Culture] building in your first [20] cities for free" @@ -1351,6 +1346,11 @@ Example: "" Applicable to: Conditional +#### +Example: "" + +Applicable to: Conditional + #### Example: "" @@ -1481,6 +1481,7 @@ Applicable to: Conditional - "[amount] HP when healing in [tileFilter] tiles" - Deprecated as of 3.19.4, replace with "[amount] HP when healing " - "Melee units pay no movement cost to pillage" - Deprecated as of 3.18.17, replace with "No movement cost to pillage " - "Heal adjacent units for an additional 15 HP per turn" - Deprecated as of 3.19.3, replace with "All adjacent units heal [+15] HP when healing" + - "+[amount]% attack strength to all [mapUnitFilter] units for [amount2] turns" - Deprecated as of 3.19.8, replace with "[+amount]% Strength " - "[stats] per turn from cities before [tech/policy]" - Deprecated as of 3.18.14, replace with "[stats] [in all cities] OR [stats] [in all cities] " - "[mapUnitFilter] units gain [amount]% more Experience from combat" - Deprecated as of 3.18.12, replace with "[amount]% XP gained from combat " - "[amount]% maintenance costs for [mapUnitFilter] units" - Deprecated as of 3.18.14, replace with "[amount]% maintenance costs "