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
This commit is contained in:
Yair Morgenstern 2022-02-12 19:03:11 +02:00 committed by GitHub
parent 55d92b9735
commit 4fb4722e3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 37 additions and 19 deletions

View File

@ -407,7 +407,8 @@
}, },
{ {
"name": "Autocracy Complete", "name": "Autocracy Complete",
"uniques": ["+[25]% attack strength to all [Military] units for [50] turns", "uniques": [
"[+25]% Strength <when attacking> <for [Military] units> <for [50] turns>",
"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]) <starting from the [Industrial era]>", "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]) <starting from the [Industrial era]>",
"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]) <starting from the [Industrial era]>" "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]) <starting from the [Industrial era]>"
] ]

View File

@ -400,7 +400,7 @@
}, },
{ {
"name": "Autocracy Complete", "name": "Autocracy Complete",
"uniques": ["+[25]% attack strength to all [Military] units for [30] turns"] "uniques": ["[+25]% Strength <when attacking> <for [Military] units> <for [30] turns>"]
} }
] ]
}, },

View File

@ -462,7 +462,8 @@ class CityConstructions {
builtBuildingUniqueMap.clear() builtBuildingUniqueMap.clear()
for (building in getBuiltBuildings()) for (building in getBuiltBuildings())
for (unique in building.uniqueObjects) for (unique in building.uniqueObjects)
builtBuildingUniqueMap.addUnique(unique) if (unique.conditionals.none { it.type == UniqueType.ConditionalTimedUnique })
builtBuildingUniqueMap.addUnique(unique)
} }
/** /**

View File

@ -21,7 +21,6 @@ import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.tile.TileResource import com.unciv.models.ruleset.tile.TileResource
import com.unciv.models.ruleset.unique.StateForConditionals import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.TemporaryUnique 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.unique.UniqueType
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
@ -150,6 +149,7 @@ class CivilizationInfo {
/** Arraylist instead of HashMap as the same unique might appear multiple times /** 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 * 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<TemporaryUnique>() val temporaryUniques = ArrayList<TemporaryUnique>()
@ -864,9 +864,10 @@ class CivilizationInfo {
// Update turn counter for temporary uniques // Update turn counter for temporary uniques
for (unique in temporaryUniques) { 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()) goldenAges.endTurn(getHappiness())
getCivUnits().forEach { it.endTurn() } // This is the most expensive part of endTurn getCivUnits().forEach { it.endTurn() } // This is the most expensive part of endTurn

View File

@ -54,8 +54,9 @@ class PolicyManager {
fun addPolicyToTransients(policy: Policy) { fun addPolicyToTransients(policy: Policy) {
for (unique in policy.uniqueObjects) { for (unique in policy.uniqueObjects) {
// Should be replaced with a conditional of the same form later // Should be deprecated together with TimedAttackStrength so I'm putting this here so the compiler will complain if we don't
if (!unique.text.contains(turnCountRegex)) val rememberToDeprecate = UniqueType.TimedAttackStrength
if (!unique.text.contains(turnCountRegex) && unique.conditionals.none { it.type == UniqueType.ConditionalTimedUnique })
policyUniques.addUnique(unique) policyUniques.addUnique(unique)
} }
} }

View File

@ -365,7 +365,8 @@ class TechManager {
fun addTechToTransients(tech: Technology) { fun addTechToTransients(tech: Technology) {
for (unique in tech.uniqueObjects) for (unique in tech.uniqueObjects)
techUniques.addUnique(unique) if (unique.conditionals.none { it.type == UniqueType.ConditionalTimedUnique })
techUniques.addUnique(unique)
} }
fun setTransients() { fun setTransients() {

View File

@ -85,6 +85,7 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
} }
return when (condition.type) { return when (condition.type) {
UniqueType.ConditionalTimedUnique -> true
UniqueType.ConditionalWar -> state.civInfo?.isAtWar() == true UniqueType.ConditionalWar -> state.civInfo?.isAtWar() == true
UniqueType.ConditionalNotWar -> state.civInfo?.isAtWar() == false UniqueType.ConditionalNotWar -> state.civInfo?.isAtWar() == false
UniqueType.ConditionalWithResource -> state.civInfo?.hasResource(condition.params[0]) == true UniqueType.ConditionalWithResource -> state.civInfo?.hasResource(condition.params[0]) == true
@ -224,7 +225,6 @@ class UniqueMapTyped: EnumMap<UniqueType, ArrayList<Unique>>(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() { class TemporaryUnique() {
constructor(uniqueObject: Unique, turns: Int) : this() { constructor(uniqueObject: Unique, turns: Int) : this() {

View File

@ -31,6 +31,12 @@ object UniqueTriggerActivation {
if (!unique.conditionalsApply(StateForConditionals(civInfo, cityInfo))) return false 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 @Suppress("NON_EXHAUSTIVE_WHEN") // Yes we're not treating all types here
when (unique.type) { when (unique.type) {
OneTimeFreeUnit -> { OneTimeFreeUnit -> {

View File

@ -548,6 +548,8 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
ConditionalPolicy("after adopting [policy]", UniqueTarget.Conditional), ConditionalPolicy("after adopting [policy]", UniqueTarget.Conditional),
ConditionalNoPolicy("before adopting [policy]", UniqueTarget.Conditional), ConditionalNoPolicy("before adopting [policy]", UniqueTarget.Conditional),
ConditionalTimedUnique("for [amount] turns", UniqueTarget.Conditional),
/////// city conditionals /////// city conditionals
ConditionalSpecialistCount("if this city has at least [amount] specialists", UniqueTarget.Conditional), ConditionalSpecialistCount("if this city has at least [amount] specialists", UniqueTarget.Conditional),
ConditionalFollowerCount("in cities where this religion has at least [amount] followers", 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 UnitsGainPromotion("[mapUnitFilter] units gain the [promotion] promotion", UniqueTarget.Global), // Not used in Vanilla
// todo: remove forced sign // todo: remove forced sign
StrategicResourcesIncrease("Quantity of strategic resources produced by the empire +[amount]%", UniqueTarget.Global), // used in Policy StrategicResourcesIncrease("Quantity of strategic resources produced by the empire +[amount]%", UniqueTarget.Global), // used in Policy
// todo: remove forced sign
// todo: convert to "[amount]% Strength <when attacking> <for [baseUnitFilter] units> <for [amount] turns>" @Deprecated("as of 3.19.8", ReplaceWith("[+amount]% Strength <when attacking> <for [mapUnitFilter] units> <for [amount2] turns>"))
TimedAttackStrength("+[amount]% attack strength to all [mapUnitFilter] units for [amount] turns", UniqueTarget.Global), // used in Policy 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 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 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()) { for (placeholder in text.getPlaceholderParameters()) {
val matchingParameterTypes = placeholder val matchingParameterTypes = placeholder
.split('/') .split('/')
.map { UniqueParameterType.safeValueOf(it) } .map { UniqueParameterType.safeValueOf(it.replace(numberRegex, "")) }
parameterTypeMap.add(matchingParameterTypes) parameterTypeMap.add(matchingParameterTypes)
} }
targetTypes.addAll(targets) targetTypes.addAll(targets)
@ -854,5 +856,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
fun getDeprecationAnnotation(): Deprecated? = declaringClass.getField(name) fun getDeprecationAnnotation(): Deprecated? = declaringClass.getField(name)
.getAnnotation(Deprecated::class.java) .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.

View File

@ -624,11 +624,6 @@ Example: "Quantity of strategic resources produced by the empire +[20]%"
Applicable to: Global 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 #### 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" Example: "Provides the cheapest [Culture] building in your first [20] cities for free"
@ -1351,6 +1346,11 @@ Example: "<before adopting [Oligarchy]>"
Applicable to: Conditional Applicable to: Conditional
#### <for [amount turns>
Example: "<for [amount turns>"
Applicable to: Conditional
#### <if this city has at least [amount] specialists> #### <if this city has at least [amount] specialists>
Example: "<if this city has at least [20] specialists>" Example: "<if this city has at least [20] specialists>"
@ -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 <in [tileFilter] tiles>" - "[amount] HP when healing in [tileFilter] tiles" - Deprecated as of 3.19.4, replace with "[amount] HP when healing <in [tileFilter] tiles>"
- "Melee units pay no movement cost to pillage" - Deprecated as of 3.18.17, replace with "No movement cost to pillage <for [Melee] units>" - "Melee units pay no movement cost to pillage" - Deprecated as of 3.18.17, replace with "No movement cost to pillage <for [Melee] units>"
- "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" - "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 <when attacking> <for [mapUnitFilter] units> <for [amount2] turns>"
- "[stats] per turn from cities before [tech/policy]" - Deprecated as of 3.18.14, replace with "[stats] [in all cities] <before discovering [tech]> OR [stats] [in all cities] <before adopting [policy]>" - "[stats] per turn from cities before [tech/policy]" - Deprecated as of 3.18.14, replace with "[stats] [in all cities] <before discovering [tech]> OR [stats] [in all cities] <before adopting [policy]>"
- "[mapUnitFilter] units gain [amount]% more Experience from combat" - Deprecated as of 3.18.12, replace with "[amount]% XP gained from combat <for [mapUnitFilter] units>" - "[mapUnitFilter] units gain [amount]% more Experience from combat" - Deprecated as of 3.18.12, replace with "[amount]% XP gained from combat <for [mapUnitFilter] units>"
- "[amount]% maintenance costs for [mapUnitFilter] units" - Deprecated as of 3.18.14, replace with "[amount]% maintenance costs <for [mapUnitFilter] units>" - "[amount]% maintenance costs for [mapUnitFilter] units" - Deprecated as of 3.18.14, replace with "[amount]% maintenance costs <for [mapUnitFilter] units>"