Unified and enumified production percentage bonus uniques (#5521)

This commit is contained in:
Xander Lenstra 2021-10-20 15:57:55 +02:00 committed by GitHub
parent ae8c72adbc
commit 13859e78c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 135 additions and 68 deletions

View File

@ -75,15 +75,14 @@
{
"name": "Monument to the Gods",
"type": "Pantheon",
"uniques": ["+[15]% Production when constructing [Wonders]"]
"uniques": ["[+15]% Production when constructing [All] wonders [in cities following this religion]"]
// ToDo: Should only be ancient/classical era wonders, but implementing that is another can of worms
// For that we really should need an era.matchesFilter() AND support for multiple values in
// construction.matchesFilter(), so we could write something like:
//"uniques": ["[+15]% Production when constructing [{Ancient Era} {Wonders}]",
// "[+15]% Production when constructing [{Classical Era} {Wonders}]"]
// For that we really should need an era.matchesFilter(), so we could write something like:
//"uniques": ["[+15]% Production when constructing [Ancient era] wonders [in cities following this religion]",
// "[+15]% Production when constructing [Classical era] wonders [in cities following this religion]"]
// For now this feels like overkill, but I'll leave this here for the future
// Alternatively, we could approximate this with "[+15]% Production when constructing [Wonders] <during the [Ancient era]>"
// Alternatively, we could approximate this with "[+15]% Production when constructing [All] wonders [in all cities] <during the [Ancient era]>"
},
{
"name": "One with Nature",

View File

@ -67,7 +67,7 @@
"culture": 1,
"isWonder": true,
"greatPersonPoints": {"Great Engineer": 1},
"uniques": ["+[10]% [Food] [in all cities]", "+[15]% Production when constructing [Ranged] units [in all cities]"],
"uniques": ["[+10]% [Food] [in all cities]", "[+15]% Production when constructing [Ranged] units [in all cities]"],
"requiredTech": "Archery",
"quote": "'It is not so much for its beauty that the forest makes a claim upon men's hearts, as for that subtle something, that quality of air, that emanation from old trees, that so wonderfully changes and renews a weary spirit.' - Robert Louis Stevenson"
},
@ -239,7 +239,7 @@
"maintenance": 1,
"requiredNearbyImprovedResources": ["Horses","Sheep","Cattle"],
"hurryCostModifier": 25,
"uniques": ["+[15]% Production when constructing [Mounted] units [in this city]",
"uniques": ["[+15]% Production when constructing [Mounted] units [in this city]",
"[+1 Production] from [Cattle] tiles [in this city]",
"[+1 Production] from [Horses] tiles [in this city]",
"[+1 Production] from [Sheep] tiles [in this city]"],
@ -528,9 +528,9 @@
"requiredNearbyImprovedResources": ["Iron"],
"requiredTech": "Metal Casting",
// If spaceship parts are changed into units, the spaceship part unique should be changed to
// "+[15]% Production when constructing [Spaceship part] units [in this city]"
"uniques": ["+[15]% Production when constructing [Spaceship part] [in this city]",
"+[15]% Production when constructing [Land] units [in this city]",
// "[+15]% Production when constructing [Spaceship part] units [in this city]"
"uniques": ["[+15]% Production when constructing [Spaceship part] buildings [in this city]",
"[+15]% Production when constructing [Land] units [in this city]",
"[+1 Production] from [Iron] tiles [in this city]"]
},
// Column 6
@ -684,7 +684,7 @@
"culture": 1,
"isWonder": true,
"greatPersonPoints": {"Great Artist": 2},
"uniques": ["+[25]% [Culture] [in all cities]"],
"uniques": ["[+25]% [Culture] [in all cities]"],
"requiredTech": "Acoustics",
"quote": "'I live and love in God's peculiar light.' - Michelangelo Buonarroti"
},
@ -765,7 +765,7 @@
"maintenance": 3,
"requiredBuilding": "Harbor",
"uniques": ["[+1 Production, +1 Gold] from [Water resource] tiles [in this city]",
"Must be next to [Coast]", "+[15]% Production when constructing [Water] units [in this city]"],
"Must be next to [Coast]", "[+15]% Production when constructing [Water] units [in this city]"],
"requiredTech": "Navigation"
},
{
@ -802,7 +802,7 @@
"specialistSlots": {"Engineer": 1},
"hurryCostModifier": 25,
"maintenance": 2,
"uniques": ["Must not be on [Hill]", "+[10]% Production when constructing [Buildings] [in this city]"],
"uniques": ["Must not be on [Hill]", "[+10]% Production when constructing [All] buildings [in this city]"],
"requiredTech": "Economics"
},
{
@ -1107,9 +1107,9 @@
"isWonder": true,
"greatPersonPoints": {"Great Scientist": 1},
// If spaceship parts are changed into units, the spaceship part unique should be changed to
// "+[25]% Production when constructing [Spaceship part] units [in this city]"
// "[+25]% Production when constructing [Spaceship part] units [in this city]"
"uniques": ["[2] free [Great Scientist] units appear",
"+[25]% Production when constructing [Spaceship part] [in this city]",
"[+25]% Production when constructing [Spaceship part] buildings [in this city]",
"Gain a free [Spaceship Factory] [in this city]"],
"requiredTech": "Satellites",
"quote": "'The wonder is, not that the field of stars is so vast, but that man has measured it.' - Anatole France"
@ -1127,8 +1127,8 @@
"cost": 360,
"requiredBuilding": "Factory",
// If spaceship parts are changed into units, this unique should be changed to
// "+[50]% Production when constructing [Spaceship part] units [in this city]"
"uniques": ["+[50]% Production when constructing [Spaceship part] [in this city]"],
// "[+50]% Production when constructing [Spaceship part] units [in this city]"
"uniques": ["[+50]% Production when constructing [Spaceship part] buildings [in this city]"],
"requiredTech": "Robotics"
},
// Column 16

View File

@ -117,7 +117,7 @@
"innerColor": [98,10,210],
"favoredReligion": "Islam",
"uniqueName": "Monument Builders",
"uniques": ["+[20]% Production when constructing [Wonders]"],
"uniques": ["[+20]% Production when constructing [All] wonders [in all cities]"],
"cities": ["Thebes","Memphis","Heliopolis","Elephantine","Alexandria","Pi-Ramesses","Giza","Byblos","Akhetaten",
"Hieraconpolis","Abydos","Asyut","Avaris","Lisht","Buto","Edfu","Pithom","Busiris","Kahun","Athribis",
"Mendes","Elashmunein","Tanis","Bubastis","Oryx","Sebennytus","Akhmin","Karnak","Luxor","El Kab","Armant",

View File

@ -6,7 +6,7 @@
"policies": [
{
"name": "Aristocracy",
"uniques": ["+[15]% Production when constructing [Wonders]", "[+1 Happiness] per [10] population [in all cities]"],
"uniques": ["[+15]% Production when constructing [All] wonders [in all cities]", "[+1 Happiness] per [10] population [in all cities]"],
"row": 1,
"column": 1
},
@ -49,7 +49,7 @@
"policies": [
{
"name": "Republic",
"uniques": ["[+1 Production] [in all cities]", "+[5]% Production when constructing [Buildings]"],
"uniques": ["[+1 Production] [in all cities]", "[+5]% Production when constructing [All] buildings [in all cities]"],
"row": 1,
"column": 1
},
@ -61,7 +61,7 @@
},
{
"name": "Collective Rule",
"uniques": ["+[50]% Production when constructing [Settler] units [in capital]", "Free [Settler] appears"],
"uniques": ["[+50]% Production when constructing [Settler] units [in capital]", "Free [Settler] appears"],
"requires": ["Republic"],
"row": 2,
"column": 1
@ -94,7 +94,7 @@
"policies": [
{
"name": "Warrior Code",
"uniques":["+[15]% Production when constructing [Melee] units [in all cities]", "Free [Great General] appears"],
"uniques":["[+15]% Production when constructing [Melee] units [in all cities]", "Free [Great General] appears"],
"row": 1,
"column": 2
},
@ -135,7 +135,7 @@
},{
"name": "Piety",
"era": "Classical era",
"uniques": ["+[100]% Production when constructing a [Shrine]", "+[100]% Production when constructing a [Temple]",
"uniques": ["[+100]% Production when constructing [Shrine] buildings [in all cities]", "[+100]% Production when constructing [Temple] buildings [in all cities]",
"Incompatible with [Rationalism]"],
"policies": [
{
@ -159,7 +159,7 @@
},
{
"name": "Reformation",
"uniques": ["+[33]% [Culture] [in all cities with a world wonder]", "Empire enters golden age"],
"uniques": ["[+33]% [Culture] [in all cities with a world wonder]", "Empire enters golden age"],
"requires": ["Organized Religion"],
"row": 2,
"column": 3
@ -225,7 +225,7 @@
},
{
"name": "Commerce",
"uniques": ["+[25]% [Gold] [in capital]"],
"uniques": ["[+25]% [Gold] [in capital]"],
"era": "Medieval era",
"policies": [
{
@ -391,7 +391,7 @@
},
{
"name": "Police State",
"uniques": ["[+3 Happiness] from every [Courthouse]", "+[100]% Production when constructing [Courthouse]"],
"uniques": ["[+3 Happiness] from every [Courthouse]", "[+100]% Production when constructing [Courthouse] buildings [in all cities]"],
// There are also some uniques regarding espoinage, which as of this writing is not yet implemented
"requires": ["Militarism"],
"row": 2,
@ -399,7 +399,7 @@
},
{
"name": "Total War",
"uniques": ["+[25]% Production when constructing [Military] units [in all cities]", "New [Military] units start with [15] Experience [in all cities]"],
"uniques": ["[+25]% Production when constructing [Military] units [in all cities]", "New [Military] units start with [15] Experience [in all cities]"],
"requires": ["Police State","Fascism"],
"row": 3,
"column": 4
@ -426,7 +426,7 @@
},
{
"name": "Planned Economy",
"uniques": ["+[25]% [Science] from every [Factory]", "+[100]% Production when constructing a [Factory]"],
"uniques": ["+[25]% [Science] from every [Factory]", "[+100]% Production when constructing [Factory] buildings [in all cities]"],
"row": 1,
"column": 3
},

View File

@ -240,7 +240,7 @@
"gold": 2,
"improvement": "Quarry",
"improvementStats": {"production": 1},
"uniques": ["+[15]% Production when constructing [Wonders]"]
"uniques": ["[+15]% Production when constructing [All] wonders [in all cities]"]
},
{
"name": "Whales",

View File

@ -7,6 +7,7 @@ import com.unciv.logic.map.RoadStatus
import com.unciv.models.Counter
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.ModOptionsConstants
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.Unique
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.ruleset.unit.BaseUnit
@ -266,14 +267,7 @@ class CityStats(val cityInfo: CityInfo) {
val uniques = uniqueSequence.toList().asSequence()
// Since this is sometimes run from a different thread (getConstructionButtonDTOs),
// this helps mitigate concurrency problems.
if (currentConstruction is Building && !currentConstruction.isAnyWonder())
for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing [] buildings" }) {
val stat = Stat.valueOf(unique.params[1])
if (currentConstruction.isStatRelated(stat))
stats.production += unique.params[0].toInt()
}
for (unique in uniques.filter { it.isOfType(UniqueType.StatPercentBonus) }) {
if (!unique.conditionalsApply(cityInfo.civInfo, cityInfo)) continue
stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
@ -285,33 +279,65 @@ class CityStats(val cityInfo: CityInfo) {
stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
//
// Params: "+[amount]% [Stat] [cityFilter]", pretty crazy amirite
// For instance "+[50]% [Production] [in all cities]
for (unique in uniques.filter { it.placeholderText == "+[]% [] []"})
// Deprecated since 3.17.10
// Params: "+[amount]% [Stat] [cityFilter]", pretty crazy amirite
// For instance "+[50]% [Production] [in all cities]
for (unique in uniques.filter { it.isOfType(UniqueType.StatPercentBonusCitiesDeprecated) })
if (cityInfo.matchesFilter(unique.params[2]))
stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
//
for (unique in uniques.filter { it.isOfType(UniqueType.StatPercentBonusCities) }) {
if (!unique.conditionalsApply(StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo))) continue
if (cityInfo.matchesFilter(unique.params[2]))
stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing []" }) {
if (constructionMatchesFilter(currentConstruction, unique.params[1]))
stats.production += unique.params[0].toInt()
}
// Used for specific buildings (e.g. +100% Production when constructing a Factory)
for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing a []" }) {
if (constructionMatchesFilter(currentConstruction, unique.params[1]))
stats.production += unique.params[0].toInt()
}
// "+[amount]% Production when constructing [constructionFilter] [cityFilter]"
for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing [] []" }) {
val uniquesToCheck =
if (currentConstruction is Building && !currentConstruction.isAnyWonder()) {
uniques.filter { it.isOfType(UniqueType.PercentProductionWonders) }
} else if (currentConstruction is Building && currentConstruction.isAnyWonder()) {
uniques.filter { it.isOfType(UniqueType.PercentProductionBuildings) }
} else if (currentConstruction is BaseUnit) {
uniques.filter { it.isOfType(UniqueType.PercentProductionUnits) }
} else { // Science/Gold production
sequenceOf()
}
for (unique in uniquesToCheck) {
if (!unique.conditionalsApply(StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo))) continue
if (constructionMatchesFilter(currentConstruction, unique.params[1]) && cityInfo.matchesFilter(unique.params[2]))
stats.production += unique.params[0].toInt()
}
// "+[amount]% Production when constructing [unitFilter] units [cityFilter]"
for (unique in uniques.filter { it.placeholderText == "+[]% Production when constructing [] units []" }) {
if (constructionMatchesFilter(currentConstruction, unique.params[1]) && cityInfo.matchesFilter(unique.params[2]))
stats.production += unique.params[0].toInt()
}
// Deprecated since 3.17.10
if (currentConstruction is Building && !currentConstruction.isAnyWonder())
for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionStatBuildings) }) {
val stat = Stat.valueOf(unique.params[1])
if (currentConstruction.isStatRelated(stat))
stats.production += unique.params[0].toInt()
}
for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionConstructions) }) {
if (constructionMatchesFilter(currentConstruction, unique.params[1]))
stats.production += unique.params[0].toInt()
}
// Used for specific buildings (e.g. +100% Production when constructing a Factory)
for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionBuildingName) }) {
if (constructionMatchesFilter(currentConstruction, unique.params[1]))
stats.production += unique.params[0].toInt()
}
// "+[amount]% Production when constructing [constructionFilter] [cityFilter]"
for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionConstructionsCities) }) {
if (constructionMatchesFilter(currentConstruction, unique.params[1]) && cityInfo.matchesFilter(unique.params[2]))
stats.production += unique.params[0].toInt()
}
// "+[amount]% Production when constructing [unitFilter] units [cityFilter]"
for (unique in uniques.filter { it.isOfType(UniqueType.PercentProductionUnitsDeprecated) }) {
if (constructionMatchesFilter(currentConstruction, unique.params[1]) && cityInfo.matchesFilter(unique.params[2]))
stats.production += unique.params[0].toInt()
}
//
// Deprecated since 3.17.1
if (cityInfo.civInfo.getHappiness() >= 0) {

View File

@ -1,6 +1,7 @@
package com.unciv.models.ruleset.unique
import com.unciv.models.ruleset.BeliefType
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.tile.TerrainType
@ -91,6 +92,29 @@ enum class UniqueParameterType(val parameterName:String) {
return UniqueType.UniqueComplianceErrorSeverity.RulesetSpecific
}
},
BuildingFilter("buildingFilter") {
override fun getErrorSeverity(
parameterText: String,
ruleset: Ruleset
): UniqueType.UniqueComplianceErrorSeverity? {
if (parameterText == "All") return null
if (Stat.values().any { it.name == parameterText }) return null
if (BuildingName.getErrorSeverity(parameterText, ruleset) == null) return null
return UniqueType.UniqueComplianceErrorSeverity.WarningOnly
}
},
// Only used in values deprecated in 3.17.10
ConstructionFilter("constructionFilter") {
override fun getErrorSeverity(
parameterText: String,
ruleset: Ruleset
): UniqueType.UniqueComplianceErrorSeverity? {
if (BuildingFilter.getErrorSeverity(parameterText, ruleset) == null) return null
if (BaseUnitFilter.getErrorSeverity(parameterText, ruleset) == null) return null
return UniqueType.UniqueComplianceErrorSeverity.WarningOnly
}
},
//
TerrainFilter("terrainFilter") {
private val knownValues = setOf("All",
"Coastal", "River", "Open terrain", "Rough terrain", "Water resource",

View File

@ -67,8 +67,35 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
@Deprecated("As of 3.16.16", ReplaceWith("[stats] <if this city has at least [amount] specialists>"), DeprecationLevel.ERROR)
StatBonusForNumberOfSpecialists("[stats] if this city has at least [amount] specialists", UniqueTarget.Global),
StatsFromSpecialist("[stats] from every specialist [cityFilter]", UniqueTarget.Global),
@Deprecated("As of 3.16.16", ReplaceWith("[stats] from every specialist [in all cities]"), DeprecationLevel.WARNING)
StatsFromSpecialistDeprecated("[stats] from every specialist", UniqueTarget.Global),
StatsPerPopulation("[stats] per [amount] population [cityFilter]", UniqueTarget.Global),
StatsSpendingGreatPeople("[stats] whenever a Great Person is expended", UniqueTarget.Global),
StatPercentBonus("[amount]% [stat]", UniqueTarget.Global),
BonusStatsFromCityStates("[amount]% [stat] from City-States", UniqueTarget.Global),
StatPercentBonusCities("[amount]% [stat] [cityFilter]", UniqueTarget.Global),
@Deprecated("As of 3.17.10", ReplaceWith("[+amount]% [stat] [cityFilter]"), DeprecationLevel.WARNING)
StatPercentBonusCitiesDeprecated("+[amount]% [stat] [cityFilter]", UniqueTarget.Global),
PercentProductionWonders("[amount]% Production when constructing [buildingFilter] wonders [cityFilter]", UniqueTarget.Global, UniqueTarget.Resource, UniqueTarget.FollowerBelief),
PercentProductionBuildings("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]", UniqueTarget.Global),
PercentProductionUnits("[amount]% Production when constructing [baseUnitFilter] units [cityFilter]", UniqueTarget.Global),
@Deprecated("As of 3.17.10", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.WARNING)
PercentProductionStatBuildings("+[amount]% Production when constructing [stat] buildings", UniqueTarget.Global),
@Deprecated("As of 3.17.10", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.WARNING)
PercentProductionConstructions("+[amount]% Production when constructing [constructionFilter]", UniqueTarget.Global),
@Deprecated("As of 3.17.10", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.WARNING)
PercentProductionBuildingName("+[amount]% Production when constructing a [buildingName]", UniqueTarget.Global),
@Deprecated("As of 3.17.10", ReplaceWith("[amount]% Production when constructing [buildingFilter] buildings [cityFilter]"), DeprecationLevel.WARNING)
PercentProductionConstructionsCities("+[amount]% Production when constructing [constructionFilter] [cityFilter]", UniqueTarget.Global),
@Deprecated("As of 3.17.10", ReplaceWith("[+amount]% Production when constructing [baseUnitFilter] units [cityFilter]"), DeprecationLevel.WARNING)
PercentProductionUnitsDeprecated("+[amount]% Production when constructing [baseUnitFilter] units [cityFilter]", UniqueTarget.Global),
RemoveAnnexUnhappiness("Remove extra unhappiness from annexed cities", UniqueTarget.Building),
UnhappinessFromPopulationPercentageChange("[amount]% unhappiness from population [cityFilter]", UniqueTarget.Global),
@ -79,15 +106,6 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
UnhappinessFromPopulationPercentageChangeOld2("Unhappiness from population decreased by [amount]% [cityFilter]", UniqueTarget.Global),
StatsFromSpecialist("[stats] from every specialist [cityFilter]", UniqueTarget.Global),
@Deprecated("As of 3.16.16", ReplaceWith("[stats] from every specialist [in all cities]"), DeprecationLevel.WARNING)
StatsFromSpecialistDeprecated("[stats] from every specialist", UniqueTarget.Global),
StatsPerPopulation("[stats] per [amount] population [cityFilter]", UniqueTarget.Global),
StatsSpendingGreatPeople("[stats] whenever a Great Person is expended", UniqueTarget.Global),
/////// City-State related uniques
// I don't like the fact that currently "city state bonuses" are separate from the "global bonuses",