diff --git a/android/assets/jsons/Civ V - Gods & Kings/Beliefs.json b/android/assets/jsons/Civ V - Gods & Kings/Beliefs.json index 90e50ecbec..516a4be4b3 100644 --- a/android/assets/jsons/Civ V - Gods & Kings/Beliefs.json +++ b/android/assets/jsons/Civ V - Gods & Kings/Beliefs.json @@ -123,7 +123,7 @@ { "name": "Asceticism", "type": "Follower", - "uniques": ["[+1 Happiness] from every [Shrine] in cities where this religion has at least [3] followers"] + "uniques": ["[+1 Happiness] from every [Shrine] "] }, { "name": "Cathedrals", @@ -133,7 +133,7 @@ { "name": "Choral Music", "type": "Follower", - "uniques": ["[+2 Culture] from every [Temple] in cities where this religion has at least [5] followers"] + "uniques": ["[+2 Culture] from every [Temple] "] }, { "name": "Divine inspiration", @@ -158,7 +158,7 @@ { "name": "Liturgical Drama", "type": "Follower", - "uniques": ["[+1 Faith] from every [Amphitheatre] in cities where this religion has at least [3] followers"] + "uniques": ["[+1 Faith] from every [Amphitheatre] "] }, { "name": "Monasteries", @@ -188,7 +188,7 @@ { "name": "Religious Center", "type": "Follower", - "uniques": ["[+2 Happiness] from every [Temple] in cities where this religion has at least [5] followers"] + "uniques": ["[+2 Happiness] from every [Temple] "] }, { "name": "Religious Community", @@ -285,12 +285,12 @@ { "name": "Religious Texts", "type": "Enhancer", - "uniques": ["[+34]% Natural religion spread [in all cities]", "[+34]% Natural religion spread [in all cities] with [Printing Press]"] + "uniques": ["[+34]% Natural religion spread [in all cities]", "[+34]% Natural religion spread [in all cities] "] }, { "name": "Religious Unity", "type": "Enhancer", - "uniques": ["[+100]% Natural religion spread to [in City-State cities]"] + "uniques": ["[+100]% Natural religion spread [in City-State cities]"] }, { "name": "Reliquary", diff --git a/android/assets/jsons/Civ V - Gods & Kings/Nations.json b/android/assets/jsons/Civ V - Gods & Kings/Nations.json index ed53d2280e..7ea68005bc 100644 --- a/android/assets/jsons/Civ V - Gods & Kings/Nations.json +++ b/android/assets/jsons/Civ V - Gods & Kings/Nations.json @@ -222,7 +222,7 @@ "innerColor": [238,201,9], "favoredReligion": "Christianity", "uniqueName": "The Glory of Rome", - "uniques": ["+25% Production towards any buildings that already exist in the Capital"], + "uniques": ["[+25]% Production towards any buildings that already exist in the Capital"], "cities": ["Rome","Antium","Cumae","Neapolis","Ravenna","Arretium","Mediolanum","Arpinum","Circei","Setia", "Satricum","Ardea","Ostia","Velitrae","Viroconium","Tarentum","Brundisium","Caesaraugusta","Caesarea","Palmyra", "Signia","Aquileia","Clusium","Sutrium","Cremona","Placentia","Hispalis","Artaxata","Aurelianorum","Nicopolis", diff --git a/android/assets/jsons/Civ V - Vanilla/Nations.json b/android/assets/jsons/Civ V - Vanilla/Nations.json index d70bf57fd2..33ca1d8a90 100644 --- a/android/assets/jsons/Civ V - Vanilla/Nations.json +++ b/android/assets/jsons/Civ V - Vanilla/Nations.json @@ -214,7 +214,7 @@ "outerColor": [ 53,0,87], "innerColor": [238,201,9], "uniqueName": "The Glory of Rome", - "uniques": ["+25% Production towards any buildings that already exist in the Capital"], + "uniques": ["[+25]% Production towards any buildings that already exist in the Capital"], "cities": ["Rome","Antium","Cumae","Neapolis","Ravenna","Arretium","Mediolanum","Arpinum","Circei","Setia", "Satricum","Ardea","Ostia","Velitrae","Viroconium","Tarentum","Brundisium","Caesaraugusta","Caesarea","Palmyra", "Signia","Aquileia","Clusium","Sutrium","Cremona","Placentia","Hispalis","Artaxata","Aurelianorum","Nicopolis", diff --git a/core/src/com/unciv/logic/automation/ConstructionAutomation.kt b/core/src/com/unciv/logic/automation/ConstructionAutomation.kt index cf1e6b7026..fd392857b3 100644 --- a/core/src/com/unciv/logic/automation/ConstructionAutomation.kt +++ b/core/src/com/unciv/logic/automation/ConstructionAutomation.kt @@ -67,7 +67,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){ addCultureBuildingChoice() addSpaceshipPartChoice() addOtherBuildingChoice() - addReligousUnit() + addReligiousUnit() if (!cityInfo.isPuppet) { addWondersChoice() @@ -342,9 +342,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions){ } } - private fun addReligousUnit(){ - - + private fun addReligiousUnit() { // these 4 if conditions are used to determine if an AI should buy units to spread religion, or spend faith to buy things like new military units or new buildings. // currently this AI can only buy inquisitors and missionaries with faith // this system will have to be reengineered to support buying other stuff with faith diff --git a/core/src/com/unciv/logic/battle/Battle.kt b/core/src/com/unciv/logic/battle/Battle.kt index 67400260d2..acd50e9b3d 100644 --- a/core/src/com/unciv/logic/battle/Battle.kt +++ b/core/src/com/unciv/logic/battle/Battle.kt @@ -734,11 +734,11 @@ object Battle { else -> 1f } // Deprecated since 3.16.11 - for (unique in targetedCity.getLocalMatchingUniques("Population loss from nuclear attacks -[]%")) { + for (unique in targetedCity.getLocalMatchingUniques(UniqueType.PopulationLossFromNukesDeprecated)) { populationLoss *= 1 - unique.params[0].toFloat() / 100f } // - for (unique in targetedCity.getMatchingUniques("Population loss from nuclear attacks []% []")) { + for (unique in targetedCity.getMatchingUniques(UniqueType.PopulationLossFromNukes)) { if (!targetedCity.matchesFilter(unique.params[1])) continue populationLoss *= unique.params[0].toPercent() } diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 9d9fe060c0..193d5e7dc6 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -435,7 +435,9 @@ class CityInfo { } var allGppPercentageBonus = 0 - for (unique in getMatchingUniques("[]% great person generation []")) { + for (unique in getMatchingUniques(UniqueType.GreatPersonPointPercentage) + + getMatchingUniques(UniqueType.GreatPersonPointPercentageDeprecated) + ) { if (!matchesFilter(unique.params[1])) continue allGppPercentageBonus += unique.params[0].toInt() } @@ -484,6 +486,33 @@ class CityInfo { 200 + cityConstructions.getBuiltBuildings().sumOf { it.cityHealth } override fun toString() = name // for debug + + fun isHolyCity(): Boolean = religion.religionThisIsTheHolyCityOf != null + + fun canBeDestroyed(justCaptured: Boolean = false): Boolean { + return !isOriginalCapital && !isHolyCity() && (!isCapital() || justCaptured) + } + + fun getForceEvaluation(): Int { + // Same as for units, so higher values count more + return CityCombatant(this).getDefendingStrength().toFloat().pow(1.5f).toInt() + } + + fun getNeighbouringCivs(): Set { + val tilesList: HashSet = getTiles().toHashSet() + val cityPositionList: ArrayList = arrayListOf() + + for (tiles in tilesList) + for (tile in tiles.neighbors) + if (!tilesList.contains(tile)) + cityPositionList.add(tile) + + return cityPositionList + .asSequence() + .mapNotNull { it.getOwner()?.civName } + .toSet() + } + //endregion //region state-changing functions @@ -799,19 +828,6 @@ class CityInfo { // Looking at all the use cases, the following functions were written to handle all findMatchingUniques() problems. // Sadly, due to the large disparity between use cases, there needed to be lots of functions. - - // Finds matching uniques provided from both local and non-local sources. - fun getMatchingUniques( - placeholderText: String, - // We might have this cached to avoid concurrency problems. If we don't, just get it directly - localUniques: Sequence = getLocalMatchingUniques(placeholderText), - ): Sequence { - // The localUniques might not be filtered when passed as a parameter, so we filter it anyway - // The time loss shouldn't be that large I don't think - return civInfo.getMatchingUniques(placeholderText, this) + - localUniques.filter { it.placeholderText == placeholderText } - } - // Finds matching uniques provided from both local and non-local sources. fun getMatchingUniques( uniqueType: UniqueType, @@ -822,13 +838,6 @@ class CityInfo { localUniques.filter { it.isOfType(uniqueType) && it.conditionalsApply(stateForConditionals) } } - // Matching uniques provided by sources in the city itself - fun getLocalMatchingUniques(placeholderText: String): Sequence { - return cityConstructions.builtBuildingUniqueMap.getUniques(placeholderText) - .filter { !it.isAntiLocalEffect } + - religion.getUniques().filter { it.placeholderText == placeholderText } - } - fun getLocalMatchingUniques(uniqueType: UniqueType, stateForConditionals: StateForConditionals? = null): Sequence { return ( cityConstructions.builtBuildingUniqueMap.getUniques(uniqueType) @@ -859,33 +868,6 @@ class CityInfo { .filter { !it.isLocalEffect } // Note that we don't query religion here, as those only have local effects } - - fun isHolyCity(): Boolean = religion.religionThisIsTheHolyCityOf != null - - fun canBeDestroyed(justCaptured: Boolean = false): Boolean { - return !isOriginalCapital && !isHolyCity() && (!isCapital() || justCaptured) - } - - fun getForceEvaluation(): Int { - // Same as for units, so higher values count more - return CityCombatant(this).getDefendingStrength().toFloat().pow(1.5f).toInt() - } - - - fun getNeighbouringCivs(): Set { - val tilesList: HashSet = getTiles().toHashSet() - val cityPositionList: ArrayList = arrayListOf() - - for (tiles in tilesList) - for (tile in tiles.neighbors) - if (!tilesList.contains(tile)) - cityPositionList.add(tile) - - return cityPositionList - .asSequence() - .mapNotNull { it.getOwner()?.civName } - .toSet() - } - + //endregion } \ No newline at end of file diff --git a/core/src/com/unciv/logic/city/CityReligion.kt b/core/src/com/unciv/logic/city/CityReligion.kt index 76bc78477d..d6dee2f2d8 100644 --- a/core/src/com/unciv/logic/city/CityReligion.kt +++ b/core/src/com/unciv/logic/city/CityReligion.kt @@ -6,6 +6,8 @@ import com.unciv.models.Counter import com.unciv.models.Religion import com.unciv.models.metadata.GameSpeed import com.unciv.models.ruleset.unique.Unique +import com.unciv.models.ruleset.unique.UniqueType +import com.unciv.ui.utils.toPercent class CityInfoReligionManager { @Transient @@ -113,7 +115,7 @@ class CityInfoReligionManager { if (newMajorityReligion in religionsAtSomePointAdopted) return - val religionOwningCiv = cityInfo.civInfo.gameInfo.getCivilization(newMajorityReligionObject.foundingCivName) + val religionOwningCiv = newMajorityReligionObject.getFounder() for (unique in newMajorityReligionObject.getFounderUniques()) { val statsGranted = when (unique.placeholderText) { "[] when a city adopts this religion for the first time (modified by game speed)" -> @@ -254,13 +256,15 @@ class CityInfoReligionManager { private fun getSpreadRange(): Int { var spreadRange = 10 - for (unique in cityInfo.getMatchingUniques("Religion naturally spreads to cities [] tiles away")) - spreadRange += unique.params[0].toInt() - if (getMajorityReligion() != null) - for (unique in getMajorityReligion()!!.getFounderUniques() - .filter { it.placeholderText == "Religion naturally spreads to cities [] tiles away"} - ) spreadRange += unique.params[0].toInt() + for (unique in cityInfo.getLocalMatchingUniques(UniqueType.ReligionSpreadDistance)) { + spreadRange += unique.params[0].toInt() + } + + if (getMajorityReligion() != null) { + for (unique in getMajorityReligion()!!.getFounder().getMatchingUniques(UniqueType.ReligionSpreadDistance)) + spreadRange += unique.params[0].toInt() + } return spreadRange } @@ -295,17 +299,27 @@ class CityInfoReligionManager { private fun pressureAmountToAdjacentCities(pressuredCity: CityInfo): Int { var pressure = pressureFromAdjacentCities.toFloat() - - for (unique in cityInfo.getMatchingUniques("[]% Natural religion spread []")) { + + // Follower beliefs of this religion + for (unique in cityInfo.getLocalMatchingUniques(UniqueType.NaturalReligionSpreadStrength)) { if (pressuredCity.matchesFilter(unique.params[1])) - pressure *= 1f + unique.params[0].toFloat() / 100f + pressure *= unique.params[0].toPercent() } - - for (unique in cityInfo.getMatchingUniques("[]% Natural religion spread [] with []")) - if (pressuredCity.matchesFilter(unique.params[1]) - && cityInfo.civInfo.hasTechOrPolicy(unique.params[2]) - ) pressure *= 1f + unique.params[0].toFloat() / 100f - + + // Founder beliefs of this religion + if (getMajorityReligion() != null) { + for (unique in getMajorityReligion()!!.getFounder().getMatchingUniques(UniqueType.NaturalReligionSpreadStrength)) + if (pressuredCity.matchesFilter(unique.params[1])) + pressure *= unique.params[0].toPercent() + } + + // Deprecated since 3.19.3 + for (unique in cityInfo.getLocalMatchingUniques(UniqueType.NaturalReligionSpreadStrengthWith)) + if (pressuredCity.matchesFilter(unique.params[1]) + && cityInfo.civInfo.hasTechOrPolicy(unique.params[2]) + ) pressure *= unique.params[0].toPercent() + // + return pressure.toInt() } } \ No newline at end of file diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index 6d5baee5c8..efa1e60db0 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -88,7 +88,7 @@ class CityStats(val cityInfo: CityInfo) { if (!cityInfo.isCapital() && cityInfo.isConnectedToCapital()) { val civInfo = cityInfo.civInfo stats.gold = civInfo.getCapital().population.population * 0.15f + cityInfo.population.population * 1.1f - 1 // Calculated by http://civilization.wikia.com/wiki/Trade_route_(Civ5) - for (unique in cityInfo.getMatchingUniques("[] from each Trade Route")) + for (unique in cityInfo.getMatchingUniques(UniqueType.StatsFromTradeRoute)) stats.add(unique.stats) if (civInfo.hasUnique("Gold from all trade routes +25%")) stats.gold *= 1.25f // Machu Picchu speciality } @@ -314,13 +314,17 @@ class CityStats(val cityInfo: CityInfo) { unique.params[0].toFloat() * cityInfo.religion.getFollowersOfMajorityReligion(), unique.params[2].toFloat() )) - + if (currentConstruction is Building && cityInfo.civInfo.cities.isNotEmpty() && cityInfo.civInfo.getCapital().cityConstructions.builtBuildings.contains(currentConstruction.name) ) { - for (unique in cityInfo.getMatchingUniques("+25% Production towards any buildings that already exist in the Capital")) - addUniqueStats(unique, Stat.Production, 25f) + // Deprecated since 3.19.3 + for (unique in cityInfo.getMatchingUniques(UniqueType.PercentProductionBuildingsInCapitalDeprecated)) + addUniqueStats(unique, Stat.Production, 25f) + // + for (unique in cityInfo.getMatchingUniques(UniqueType.PercentProductionBuildingsInCapital)) + addUniqueStats(unique, Stat.Production, unique.params[0].toFloat()) } return sourceToStats @@ -361,7 +365,7 @@ class CityStats(val cityInfo: CityInfo) { // e.g. "-[50]% maintenance costs for buildings [in this city]" // Deprecated since 3.18.17 - for (unique in cityInfo.getMatchingUniques(UniqueType.DecrasedBuildingMaintenanceDeprecated)) { + for (unique in cityInfo.getMatchingUniques(UniqueType.DecreasedBuildingMaintenanceDeprecated)) { buildingsMaintenance *= (1f - unique.params[0].toFloat() / 100) } // diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 1ee03e1929..20cad7f496 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -739,10 +739,10 @@ class MapUnit { } val healingCity = tileInfo.getTilesInDistance(1).firstOrNull { - it.isCityCenter() && it.getCity()!!.getMatchingUniques("[] Units adjacent to this city heal [] HP per turn when healing").any() + it.isCityCenter() && it.getCity()!!.getMatchingUniques(UniqueType.CityHealingUnits).any() }?.getCity() if (healingCity != null) { - for (unique in healingCity.getMatchingUniques("[] Units adjacent to this city heal [] HP per turn when healing")) { + for (unique in healingCity.getMatchingUniques(UniqueType.CityHealingUnits)) { if (!matchesFilter(unique.params[0])) continue healing += unique.params[1].toInt() } @@ -1160,7 +1160,7 @@ class MapUnit { val baseAmount = getBaseMaxActionUses(action) val additional = if (buildCity == null) 0 - else buildCity.getMatchingUniques("[] units built [] can [] [] extra times") + else buildCity.getMatchingUniques(UniqueType.UnitStartingActions) .filter { matchesFilter(it.params[0]) && buildCity.matchesFilter(it.params[1]) && it.params[2] == action } .sumOf { it.params[3].toInt() } diff --git a/core/src/com/unciv/models/Religion.kt b/core/src/com/unciv/models/Religion.kt index 4e63a63f40..f0447b31e7 100644 --- a/core/src/com/unciv/models/Religion.kt +++ b/core/src/com/unciv/models/Religion.kt @@ -91,4 +91,6 @@ class Religion() : INamed { fun isMajorReligion() = getBeliefs(BeliefType.Founder).any() fun isEnhancedReligion() = getBeliefs(BeliefType.Enhancer).any() + + fun getFounder() = gameInfo.civilizations.first { it.civName == foundingCivName } } diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index 0dc36f131e..522a2c2635 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -173,9 +173,11 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction { stats.add(unique.stats) } - for (unique in city.getMatchingUniques("[] from every [] in cities where this religion has at least [] followers")) - if (unique.params[2].toInt() <= city.religion.getFollowersOfMajorityReligion() && matchesFilter(unique.params[1])) - stats.add(unique.stats) + // Deprecated since 3.19.3 + for (unique in city.getMatchingUniques(UniqueType.StatsForBuildingsWithFollowers)) + if (unique.params[2].toInt() <= city.religion.getFollowersOfMajorityReligion() && matchesFilter(unique.params[1])) + stats.add(unique.stats) + // @Suppress("RemoveRedundantQualifierName") // make it clearer Building inherits Stats for (unique in getMatchingUniques(UniqueType.StatsWithResource)) diff --git a/core/src/com/unciv/models/ruleset/unique/Unique.kt b/core/src/com/unciv/models/ruleset/unique/Unique.kt index 75b4473d8d..548497dc1d 100644 --- a/core/src/com/unciv/models/ruleset/unique/Unique.kt +++ b/core/src/com/unciv/models/ruleset/unique/Unique.kt @@ -83,6 +83,8 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s UniqueType.ConditionalSpecialistCount -> state.cityInfo != null && state.cityInfo.population.getNumberOfSpecialists() >= condition.params[0].toInt() + UniqueType.ConditionalFollowerCount -> + state.cityInfo != null && state.cityInfo.religion.getFollowersOfMajorityReligion() >= condition.params[0].toInt() UniqueType.ConditionalWhenGarrisoned -> state.cityInfo != null && state.cityInfo.getCenterTile().militaryUnit != null && state.cityInfo.getCenterTile().militaryUnit!!.canGarrison() diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt index ea814d967d..1fc30492bb 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt @@ -1,5 +1,6 @@ package com.unciv.models.ruleset.unique +import com.unciv.Constants import com.unciv.models.ruleset.BeliefType import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.VictoryType @@ -302,6 +303,16 @@ enum class UniqueParameterType(val parameterName:String) { else UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant } }, + Action("action") { + private val knownValues = setOf(Constants.spreadReligionAbilityCount, Constants.removeHeresyAbilityCount) + override fun getErrorSeverity( + parameterText: String, + ruleset: Ruleset + ): UniqueType.UniqueComplianceErrorSeverity? { + return if (parameterText in knownValues) null + else UniqueType.UniqueComplianceErrorSeverity.RulesetInvariant + } + }, /** Behaves like [Unknown], but states explicitly the parameter is OK and its contents are ignored */ Comment("comment") { override fun getErrorSeverity(parameterText: String, ruleset: Ruleset): diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 8df1d429b7..e912715b3f 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -70,9 +70,10 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: StatsPerCity("[stats] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), StatsFromSpecialist("[stats] from every specialist [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), - StatsPerPopulation("[stats] per [amount] population [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), + // ToDo: Reword to `[stats] ` for consistency with other conditionals StatsFromXPopulation("[stats] in cities with [amount] or more population", UniqueTarget.Global, UniqueTarget.FollowerBelief), + StatsFromCitiesOnSpecificTiles("[stats] in cities on [terrainFilter] tiles", UniqueTarget.Global, UniqueTarget.FollowerBelief), @Deprecated("As of 3.18.14", ReplaceWith("[stats] [in all cities] OR [stats] [in all cities] ")) StatsFromCitiesBefore("[stats] per turn from cities before [tech/policy]", UniqueTarget.Global, UniqueTarget.FollowerBelief), @@ -84,7 +85,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: StatsFromTilesWithout("[stats] from [tileFilter] tiles without [tileFilter] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), // This is a doozy StatsFromObject("[stats] from every [tileFilter/specialist/buildingFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), - + @Deprecated("As of 3.19.3", ReplaceWith("[stats] from every [buildingFilter] ")) + StatsForBuildingsWithFollowers("[stats] from every [buildingFilter] in cities where this religion has at least [amount] followers", UniqueTarget.Global, UniqueTarget.FollowerBelief), + StatsFromTradeRoute("[stats] from each Trade Route", UniqueTarget.Global, UniqueTarget.FollowerBelief), // Stat percentage boosts StatPercentBonus("[amount]% [stat]", UniqueTarget.Global, UniqueTarget.FollowerBelief), @@ -97,12 +100,16 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: AllStatsSignedPercentFromObject("+[amount]% yield from every [tileFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), StatPercentFromReligionFollowers("[amount]% [stat] from every follower, up to [amount]%", UniqueTarget.FollowerBelief), BonusStatsFromCityStates("[amount]% [stat] from City-States", UniqueTarget.Global), + NullifiesStat("Nullifies [stat] [cityFilter]", UniqueTarget.Global), NullifiesGrowth("Nullifies Growth [cityFilter]", UniqueTarget.Global), PercentProductionWonders("[amount]% Production when constructing [buildingFilter] wonders [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), PercentProductionBuildings("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), PercentProductionUnits("[amount]% Production when constructing [baseUnitFilter] units [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), + PercentProductionBuildingsInCapital("[amount]% Production towards any buildings that already exist in the Capital", UniqueTarget.Global, UniqueTarget.FollowerBelief), + @Deprecated("As of 3.19.3", ReplaceWith("[amount]% Production towards any buildings that already exist in the Capital")) + PercentProductionBuildingsInCapitalDeprecated("+25% Production towards any buildings that already exist in the Capital", UniqueTarget.Global, UniqueTarget.FollowerBelief), //endregion Stat providing uniques @@ -143,6 +150,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: /////// region Other global uniques FreeUnits("[amount] units cost no maintenance", UniqueTarget.Global), + CannotBuildUnits("Cannot build [baseUnitFilter] units", UniqueTarget.Global), ConsumesResources("Consumes [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Building, UniqueTarget.Unit), ProvidesResources("Provides [amount] [resource]", UniqueTarget.Improvement, UniqueTarget.Building), @@ -155,6 +163,8 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: CarryOverFoodAlsoDeprecated("[amount]% of food is carried over [cityFilter] after population increases", UniqueTarget.Global, UniqueTarget.FollowerBelief), GainFreeBuildings("Gain a free [buildingName] [cityFilter]", UniqueTarget.Global), + GreatPersonPointPercentage("[amount]% Great Person generation [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), + GreatPersonPointPercentageDeprecated("[amount]% great person generation [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), FreeExtraBeliefs("May choose [amount] additional [beliefType] beliefs when [foundingOrEnhancing] a religion", UniqueTarget.Global), FreeExtraAnyBeliefs("May choose [amount] additional belief(s) of any type when [foundingOrEnhancing] a religion", UniqueTarget.Global), @@ -209,7 +219,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: BuildingMaintenance("[amount]% maintenance cost for buildings [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), @Deprecated("As of 3.18.17", ReplaceWith("[-amount]% maintenace cost for buildings [cityFilter]")) - DecrasedBuildingMaintenanceDeprecated("-[amount]% maintenance cost for buildings [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), + DecreasedBuildingMaintenanceDeprecated("-[amount]% maintenance cost for buildings [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), // This should probably support conditionals, e.g. MayanGainGreatPerson("Receive a free Great Person at the end of every [comment] (every 394 years), after researching [tech]. Each bonus person can only be chosen once.", UniqueTarget.Global), @@ -233,14 +243,14 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: TriggersVictory("Triggers victory", UniqueTarget.Global), TriggersCulturalVictory("Triggers a Cultural Victory upon completion", UniqueTarget.Global), - CannotBuildUnits("Cannot build [baseUnitFilter] units", UniqueTarget.Global), - BetterDefensiveBuildings("[amount]% City Strength from defensive buildings", UniqueTarget.Global), @Deprecated("As of 3.18.17", ReplaceWith("[+25]% City Strength from defensive buildings")) DefensiveBuilding25("Defensive buildings in all cities are 25% more effective", UniqueTarget.Global), TileImprovementTime("[amount]% tile improvement construction time", UniqueTarget.Global), PercentGoldFromTradeMissions("[amount]% Gold from Great Merchant trade missions", UniqueTarget.Global), + // Todo: Lowercase the 'U' of 'Units' in this unique + CityHealingUnits("[mapUnitFilter] Units adjacent to this city heal [amount] HP per turn when healing", UniqueTarget.Global, UniqueTarget.FollowerBelief), @Deprecated("As of 3.18.17", ReplaceWith("[amount]% Strength ")) StrengthFromAdjacentUnits("[amount]% Strength for [mapUnitFilter] units which have another [mapUnitFilter] unit in an adjacent tile", UniqueTarget.Unit, UniqueTarget.Global), @@ -262,10 +272,21 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: StrengthForGarrisonedCitiesAttacking("+[amount]% attacking strength for cities with garrisoned units", UniqueTarget.Global), UnitStartingExperience("New [baseUnitFilter] units start with [amount] Experience [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), + UnitStartingPromotions("All newly-trained [baseUnitFilter] units [cityFilter] receive the [promotion] promotion", UniqueTarget.Global, UniqueTarget.FollowerBelief), + UnitStartingActions("[baseUnitFilter] units built [cityFilter] can [action] [amount] extra times", UniqueTarget.Global, UniqueTarget.FollowerBelief), // ToDo: make per unit and use unit filters? LandUnitEmbarkation("Enables embarkation for land units", UniqueTarget.Global), EmbarkedUnitsMayEnterOcean("Enables embarked units to enter ocean tiles", UniqueTarget.Global), + PopulationLossFromNukes("Population loss from nuclear attacks [amount]% [cityFilter]", UniqueTarget.Global), + @Deprecated("As of 3.19.2", ReplaceWith("Population loss from nuclear attacks [-amount]% [in this city]")) + PopulationLossFromNukesDeprecated("Population loss from nuclear attacks -[amount]%", UniqueTarget.Global), + + NaturalReligionSpreadStrength("[amount]% Natural religion spread [cityFilter]", UniqueTarget.FollowerBelief, UniqueTarget.Global), + @Deprecated("As of 3.19.3", ReplaceWith("[amount]% Natural religion spread [cityFilter] OR [amount]% natural religion spread [cityFilter] ")) + NaturalReligionSpreadStrengthWith("[amount]% Natural religion spread [cityFilter] with [tech/policy]", UniqueTarget.Global, UniqueTarget.FollowerBelief), + ReligionSpreadDistance("Religion naturally spreads to cities [amount] tiles away", UniqueTarget.Global, UniqueTarget.FollowerBelief), + IncompatibleWith("Incompatible with [policy/tech/promotion]", UniqueTarget.Policy, UniqueTarget.Tech, UniqueTarget.Promotion), StartingTech("Starting tech", UniqueTarget.Tech), StartsWithTech("Starts with [tech]", UniqueTarget.Nation), @@ -538,6 +559,7 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: /////// 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), ConditionalWhenGarrisoned("with a garrison", UniqueTarget.Conditional), /////// unit conditionals diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index 5f14819e95..6b607d8372 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -482,7 +482,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction { } unit.promotions.XP = XP - for (unique in cityConstructions.cityInfo.getMatchingUniques("All newly-trained [] units [] receive the [] promotion") + for (unique in cityConstructions.cityInfo.getMatchingUniques(UniqueType.UnitStartingPromotions) .filter { cityConstructions.cityInfo.matchesFilter(it.params[1]) }) { val filter = unique.params[0] val promotion = unique.params.last() diff --git a/core/src/com/unciv/ui/overviewscreen/ReligionOverviewTable.kt b/core/src/com/unciv/ui/overviewscreen/ReligionOverviewTable.kt index db746c0ba2..88e968ff40 100644 --- a/core/src/com/unciv/ui/overviewscreen/ReligionOverviewTable.kt +++ b/core/src/com/unciv/ui/overviewscreen/ReligionOverviewTable.kt @@ -74,7 +74,7 @@ class ReligionOverviewTable( val button: Button if (religion.isPantheon()) { val image = if (viewingPlayer.knows(religion.foundingCivName) || viewingPlayer.civName == religion.foundingCivName) - ImageGetter.getNationIndicator(gameInfo.getCivilization(religion.foundingCivName).nation, 60f) + ImageGetter.getNationIndicator(religion.getFounder().nation, 60f) else ImageGetter.getRandomNationIndicator(60f) button = Button(image, BaseScreen.skin) @@ -126,7 +126,7 @@ class ReligionOverviewTable( } } statsTable.add("Cities following this religion:".toLabel()).left() - statsTable.add(gameInfo.getCivilization(religion.foundingCivName).religionManager.numberOfCitiesFollowingThisReligion().toString()).right().pad(5f).row() + statsTable.add(religion.getFounder().religionManager.numberOfCitiesFollowingThisReligion().toString()).right().pad(5f).row() val minWidth = max(statsTable.minWidth, beliefsTable.minWidth) + 5 diff --git a/docs/unique parameters.md b/docs/unique parameters.md index 9608d8a547..3502c9c7b1 100644 --- a/docs/unique parameters.md +++ b/docs/unique parameters.md @@ -6,6 +6,12 @@ These are split into two categories: Note that all of these are case-sensitive! +## action +An action that a unit can preform. Currently, there are only two actions part of this: +- `Spread Religion` +- `Remove Foreign religions from your own cities` + + ## amount This indicates a whole number, possibly with a + or - sign, such as `2`, `+13`, or `-3`. diff --git a/docs/uniques.md b/docs/uniques.md index ce3e53b13f..f8216a294f 100644 --- a/docs/uniques.md +++ b/docs/uniques.md @@ -71,6 +71,11 @@ Example: "[+1 Gold, +2 Production] from every [tileFilter/specialist/buildingFil Applicable to: Global, FollowerBelief +#### [stats] from each Trade Route +Example: "[+1 Gold, +2 Production] from each Trade Route" + +Applicable to: Global, FollowerBelief + #### [amount]% [stat] Example: "[20]% [Culture]" @@ -121,6 +126,11 @@ Example: "[20]% Production when constructing [Melee] units [in all cities]" Applicable to: Global, FollowerBelief +#### [amount]% Production towards any buildings that already exist in the Capital +Example: "[20]% Production towards any buildings that already exist in the Capital" + +Applicable to: Global, FollowerBelief + #### Military Units gifted from City-States start with [amount] XP Example: "Military Units gifted from City-States start with [20] XP" @@ -177,6 +187,11 @@ Example: "[20] units cost no maintenance" Applicable to: Global +#### Cannot build [baseUnitFilter] units +Example: "Cannot build [Melee] units" + +Applicable to: Global + #### [amount]% growth [cityFilter] Example: "[20]% growth [in all cities]" @@ -192,6 +207,16 @@ Example: "Gain a free [Library] [in all cities]" Applicable to: Global +#### [amount]% Great Person generation [cityFilter] +Example: "[20]% Great Person generation [in all cities]" + +Applicable to: Global, FollowerBelief + +#### [amount]% great person generation [cityFilter] +Example: "[20]% great person generation [in all cities]" + +Applicable to: Global, FollowerBelief + #### May choose [amount] additional [beliefType] beliefs when [foundingOrEnhancing] a religion Example: "May choose [20] additional [Follower] beliefs when [founding] a religion" @@ -354,11 +379,6 @@ Applicable to: Global #### Triggers a Cultural Victory upon completion Applicable to: Global -#### Cannot build [baseUnitFilter] units -Example: "Cannot build [Melee] units" - -Applicable to: Global - #### [amount]% City Strength from defensive buildings Example: "[20]% City Strength from defensive buildings" @@ -374,6 +394,11 @@ Example: "[20]% Gold from Great Merchant trade missions" Applicable to: Global +#### [mapUnitFilter] Units adjacent to this city heal [amount] HP per turn when healing +Example: "[Wounded] Units adjacent to this city heal [20] HP per turn when healing" + +Applicable to: Global, FollowerBelief + #### [amount]% Golden Age length Example: "[20]% Golden Age length" @@ -389,12 +414,37 @@ Example: "New [Melee] units start with [20] Experience [in all cities]" Applicable to: Global, FollowerBelief +#### All newly-trained [baseUnitFilter] units [cityFilter] receive the [promotion] promotion +Example: "All newly-trained [Melee] units [in all cities] receive the [Shock I] promotion" + +Applicable to: Global, FollowerBelief + +#### [baseUnitFilter] units built [cityFilter] can [action] [amount] extra times +Example: "[Melee] units built [in all cities] can [action] [20] extra times" + +Applicable to: Global, FollowerBelief + #### Enables embarkation for land units Applicable to: Global #### Enables embarked units to enter ocean tiles Applicable to: Global +#### Population loss from nuclear attacks [amount]% [cityFilter] +Example: "Population loss from nuclear attacks [20]% [in all cities]" + +Applicable to: Global + +#### [amount]% Natural religion spread [cityFilter] +Example: "[20]% Natural religion spread [in all cities]" + +Applicable to: Global, FollowerBelief + +#### Religion naturally spreads to cities [amount] tiles away +Example: "Religion naturally spreads to cities [20] tiles away" + +Applicable to: Global, FollowerBelief + #### Can be continually researched Applicable to: Global @@ -1275,6 +1325,11 @@ Example: "" Applicable to: Conditional +#### +Example: "" + +Applicable to: Conditional + #### Applicable to: Conditional @@ -1364,8 +1419,10 @@ Applicable to: Conditional ## Deprecated uniques - "[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] " - "[stats] from every Wonder" - Deprecated As of 3.19.1, replace with "[stats] from every [Wonder]" + - "[stats] from every [buildingFilter] in cities where this religion has at least [amount] followers" - Deprecated As of 3.19.3, replace with "[stats] from every [buildingFilter] " - "+[amount]% [stat] from every [tileFilter/specialist/buildingName]" - Deprecated As of 3.18.17, replace with "[amount]% [stat] from every [tileFilter/specialist/buildingName]" - "+[amount]% yield from every [tileFilter]" - Deprecated As of 3.18.17, replace with "[+amount]% Yield from every [tileFilter]" + - "+25% Production towards any buildings that already exist in the Capital" - Deprecated As of 3.19.3, replace with "[amount]% Production towards any buildings that already exist in the Capital" - "City-State Influence degrades [amount]% slower" - Deprecated As of 3.18.17, replace with "[-amount]% City-State Influence degradation" - "Quantity of Resources gifted by City-States increased by [amount]%" - Deprecated As of 3.18.17, replace with "[+amount]% resources gifted by City-States" - "Happiness from Luxury Resources gifted by City-States increased by [amount]%" - Deprecated As of 3.18.17, replace with "[+amount]% Happiness from luxury resources gifted by City-States" @@ -1390,6 +1447,8 @@ Applicable to: Conditional - "+[amount]% Defensive Strength for cities" - Deprecated As of 3.18.17, replace with "[+amount]% Strength for cities " - "[amount]% Attacking Strength for cities" - Deprecated As of 3.18.17, replace with "[amount]% Strength for cities " - "+[amount]% attacking strength for cities with garrisoned units" - Deprecated As of 3.19.1, replace with "[amount]% Strength for cities " + - "Population loss from nuclear attacks -[amount]%" - Deprecated As of 3.19.2, replace with "Population loss from nuclear attacks [-amount]% [in this city]" + - "[amount]% Natural religion spread [cityFilter] with [tech/policy]" - Deprecated As of 3.19.3, replace with "[amount]% Natural religion spread [cityFilter] OR [amount]% natural religion spread [cityFilter] " - "Melee units pay no movement cost to pillage" - Deprecated As of 3.18.17, replace with "No movement cost to pillage " - "[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 "