From d809f3a1327947d8a66614cbbb116c54065f988a Mon Sep 17 00:00:00 2001 From: Xander Lenstra <71121390+xlenstra@users.noreply.github.com> Date: Mon, 10 Jan 2022 22:55:22 +0100 Subject: [PATCH] Made all the other constants determining the strength of cities moddable (#5940) --- .../jsons/Civ V - Gods & Kings/Buildings.json | 2 +- .../jsons/Civ V - Vanilla/Buildings.json | 2 +- .../com/unciv/logic/battle/CityCombatant.kt | 27 +++++++++++-------- core/src/com/unciv/logic/city/CityInfo.kt | 2 +- core/src/com/unciv/models/ModConstants.kt | 12 +++++++-- .../unciv/models/ruleset/unique/UniqueType.kt | 15 +++++++---- .../src/com/unciv/ui/tilegroups/CityButton.kt | 2 +- .../unciv/ui/worldscreen/unit/UnitTable.kt | 2 +- docs/uniques.md | 16 +++++++---- 9 files changed, 52 insertions(+), 28 deletions(-) diff --git a/android/assets/jsons/Civ V - Gods & Kings/Buildings.json b/android/assets/jsons/Civ V - Gods & Kings/Buildings.json index 322eef2bb4..84219692a6 100644 --- a/android/assets/jsons/Civ V - Gods & Kings/Buildings.json +++ b/android/assets/jsons/Civ V - Gods & Kings/Buildings.json @@ -831,7 +831,7 @@ "culture": 3, "cityStrength": 12, "isWonder": true, - "uniques": ["Defensive buildings in all cities are 25% more effective"], + "uniques": ["[+25]% City Strength from defensive buildings"], "requiredTech": "Metallurgy", "quote": "'The Law is a fortress on a hill that armies cannot take or floods wash away.' - The Prophet Muhammed" }, diff --git a/android/assets/jsons/Civ V - Vanilla/Buildings.json b/android/assets/jsons/Civ V - Vanilla/Buildings.json index 079d0a01c7..4a01cfa343 100644 --- a/android/assets/jsons/Civ V - Vanilla/Buildings.json +++ b/android/assets/jsons/Civ V - Vanilla/Buildings.json @@ -694,7 +694,7 @@ "culture": 3, "cityStrength": 12, "isWonder": true, - "uniques": ["Defensive buildings in all cities are 25% more effective"], + "uniques": ["[+25]% City Strength from defensive buildings"], "requiredTech": "Acoustics", "quote": "'The Law is a fortress on a hill that armies cannot take or floods wash away.' - The Prophet Muhammed" }, diff --git a/core/src/com/unciv/logic/battle/CityCombatant.kt b/core/src/com/unciv/logic/battle/CityCombatant.kt index e663070855..28b1bdd0c3 100644 --- a/core/src/com/unciv/logic/battle/CityCombatant.kt +++ b/core/src/com/unciv/logic/battle/CityCombatant.kt @@ -4,6 +4,7 @@ import com.unciv.logic.city.CityInfo import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.map.TileInfo import com.unciv.models.UncivSound +import com.unciv.models.ruleset.unique.StateForConditionals import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unit.UnitType import kotlin.math.pow @@ -30,37 +31,41 @@ class CityCombatant(val city: CityInfo) : ICombatant { } override fun getUnitType(): UnitType = UnitType.City - override fun getAttackingStrength(): Int = (getCityStrength() * 0.75).roundToInt() + override fun getAttackingStrength(): Int = (getCityStrength(CombatAction.Attack) * 0.75).roundToInt() override fun getDefendingStrength(): Int { if (isDefeated()) return 1 return getCityStrength() } - fun getCityStrength(): Int { // Civ fanatics forum, from a modder who went through the original code + fun getCityStrength(combatAction: CombatAction = CombatAction.Defend): Int { // Civ fanatics forum, from a modder who went through the original code val modConstants = getCivInfo().gameInfo.ruleSet.modOptions.constants - var strength = 8f - strength += (city.population.population / 5) * 2 // Each 5 pop gives 2 defence + var strength = modConstants.cityStrengthBase + strength += (city.population.population * modConstants.cityStrengthPerPop) // Each 5 pop gives 2 defence val cityTile = city.getCenterTile() for (unique in cityTile.getAllTerrains().flatMap { it.getMatchingUniques(UniqueType.GrantsCityStrength) }) strength += unique.params[0].toInt() // as tech progresses so does city strength val techCount = getCivInfo().gameInfo.ruleSet.technologies.count() val techsPercentKnown: Float = if (techCount > 0) city.civInfo.tech.techsResearched.count().toFloat() / techCount else 0.5f // for mods with no tech - strength += (techsPercentKnown * modConstants.cityStrengthFromTechsMultiplier).pow(modConstants.cityStrengthFromTechsExponent).toFloat() - + strength += (techsPercentKnown * modConstants.cityStrengthFromTechsMultiplier).pow(modConstants.cityStrengthFromTechsExponent) * modConstants.cityStrengthFromTechsFullMultiplier // The way all of this adds up... // All ancient techs - 0.5 extra, Classical - 2.7, Medieval - 8, Renaissance - 17.5, // Industrial - 32.4, Modern - 51, Atomic - 72.5, All - 118.3 - // 100% of the way through the game provides an extra 50.00 - + // Garrisoned unit gives up to 20% of strength to city, health-dependant if (cityTile.militaryUnit != null) - strength += cityTile.militaryUnit!!.baseUnit().strength * (cityTile.militaryUnit!!.health / 100f) * 0.2f + strength += cityTile.militaryUnit!!.baseUnit().strength * (cityTile.militaryUnit!!.health / 100f) * modConstants.cityStrengthFromGarrison var buildingsStrength = city.cityConstructions.getBuiltBuildings().sumOf { it.cityStrength }.toFloat() - if (getCivInfo().hasUnique("Defensive buildings in all cities are 25% more effective")) - buildingsStrength *= 1.25f + val stateForConditionals = StateForConditionals(getCivInfo(), city, ourCombatant = this, combatAction = combatAction) + + // Deprecated since 3.18.17 + if (getCivInfo().hasUnique(UniqueType.DefensiveBuilding25, stateForConditionals)) + buildingsStrength *= 1.25f + // + for (unique in getCivInfo().getMatchingUniques(UniqueType.BetterDefensiveBuildings, stateForConditionals)) + buildingsStrength *= unique.params[0].toInt() strength += buildingsStrength return strength.roundToInt() diff --git a/core/src/com/unciv/logic/city/CityInfo.kt b/core/src/com/unciv/logic/city/CityInfo.kt index 86a46b30f7..57d411cacf 100644 --- a/core/src/com/unciv/logic/city/CityInfo.kt +++ b/core/src/com/unciv/logic/city/CityInfo.kt @@ -872,7 +872,7 @@ class CityInfo { fun getForceEvaluation(): Int { // Same as for units, so higher values count more - return CityCombatant(this).getCityStrength().toFloat().pow(1.5f).toInt() + return CityCombatant(this).getDefendingStrength().toFloat().pow(1.5f).toInt() } diff --git a/core/src/com/unciv/models/ModConstants.kt b/core/src/com/unciv/models/ModConstants.kt index c0cff5d8c3..69bfec1d33 100644 --- a/core/src/com/unciv/models/ModConstants.kt +++ b/core/src/com/unciv/models/ModConstants.kt @@ -5,9 +5,17 @@ class ModConstants { var maxXPfromBarbarians = 30 // Formula for city Strength: - // Strength = baseStrength * (%techs * multiplier) ^ exponent - // If no techs exist in this ruleset, %techs = 0.5 + // Strength = baseStrength + strengthPerPop + strengthFromTiles + + // ((%techs * multiplier) ^ exponent) * fullMultiplier + + // (garrisonBonus * garrisonUnitStrength * garrisonUnitHealth/100) + + // defensiveBuildingStrength + // where %techs is the percentage of techs in the tech tree that are complete + // If no techs exist in this ruleset, %techs = 0.5 (=50%) + val cityStrengthBase = 8.0 + val cityStrengthPerPop = 0.4 val cityStrengthFromTechsMultiplier = 5.5 val cityStrengthFromTechsExponent = 2.8 + val cityStrengthFromTechsFullMultiplier = 1.0 + val cityStrengthFromGarrison = 0.2 } \ No newline at end of file diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 5be5261bbe..6f1bf4994e 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -178,6 +178,10 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget, val flags: 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), + //endregion Global uniques @@ -189,6 +193,10 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget, val flags: CanBePurchasedForAmountStat("Can be purchased for [amount] [stat] [cityFilter]", UniqueTarget.Building, UniqueTarget.Unit), MaxNumberBuildable("Limited to [amount] per Civilization", UniqueTarget.Building, UniqueTarget.Unit), HiddenBeforeAmountPolicies("Hidden until [amount] social policy branches have been completed", UniqueTarget.Building, UniqueTarget.Unit), + NotDisplayedWithout("Not displayed as an available construction without [buildingName/tech/resource/policy]", UniqueTarget.Building, UniqueTarget.Unit), + //UniqueType added in 3.18.4 + @Deprecated("As of 3.16.11", ReplaceWith("Not displayed as an available construction without [buildingName]"), DeprecationLevel.WARNING) + NotDisplayedUnlessOtherBuildingBuilt("Not displayed as an available construction unless [buildingName] is built", UniqueTarget.Building), //endregion @@ -201,10 +209,7 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget, val flags: RequiresAnotherBuilding("Requires a [buildingName] in this city", UniqueTarget.Building), RequiresBuildingInAllCities("Requires a [buildingName] in all cities", UniqueTarget.Building), - NotDisplayedWithout("Not displayed as an available construction without [buildingName/tech/resource/policy]", UniqueTarget.Building, UniqueTarget.Unit), - //UniqueType added in 3.18.4 - @Deprecated("As of 3.16.11", ReplaceWith("Not displayed as an available construction without [buildingName]"), DeprecationLevel.WARNING) - NotDisplayedUnlessOtherBuildingBuilt("Not displayed as an available construction unless [buildingName] is built", UniqueTarget.Building), + MustBeOn("Must be on [terrainFilter]", UniqueTarget.Building), MustNotBeOn("Must not be on [terrainFilter]", UniqueTarget.Building), @@ -498,7 +503,7 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget, val flags: HiddenAfterGreatProphet("Hidden after generating a Great Prophet", UniqueTarget.Ruins), AvailableAfterCertainTurns("Only available after [amount] turns", UniqueTarget.Ruins), HiddenWithoutVictoryType("Hidden when [victoryType] Victory is disabled", UniqueTarget.Building, UniqueTarget.Unit), - + // region DEPRECATED AND REMOVED diff --git a/core/src/com/unciv/ui/tilegroups/CityButton.kt b/core/src/com/unciv/ui/tilegroups/CityButton.kt index a54dfa6b0b..d0434b985b 100644 --- a/core/src/com/unciv/ui/tilegroups/CityButton.kt +++ b/core/src/com/unciv/ui/tilegroups/CityButton.kt @@ -231,7 +231,7 @@ class CityButton(val city: CityInfo, private val tileGroup: WorldTileGroup): Tab label.toBack() // this is so the label is rendered right before the population group, // so we save the font texture and avoid another texture switch - val cityStrength = CityCombatant(city).getCityStrength() + val cityStrength = CityCombatant(city).getDefendingStrength() val cityStrengthLabel = "${Fonts.strength}$cityStrength".toLabel(city.civInfo.nation.getInnerColor(), 10) if (!forPopup) { diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt index 0e5e1ff769..15462f809a 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt @@ -197,7 +197,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ unitDescriptionTable.clear() unitDescriptionTable.defaults().pad(2f).padRight(5f) unitDescriptionTable.add("Strength".tr()) - unitDescriptionTable.add(CityCombatant(city).getCityStrength().toString()).row() + unitDescriptionTable.add(CityCombatant(city).getDefendingStrength().toString()).row() unitDescriptionTable.add("Bombard strength".tr()) unitDescriptionTable.add(CityCombatant(city).getAttackingStrength().toString()).row() diff --git a/docs/uniques.md b/docs/uniques.md index c7c7aa0225..70e0c8bd10 100644 --- a/docs/uniques.md +++ b/docs/uniques.md @@ -223,6 +223,11 @@ Example: "Cannot build [Melee] units" Applicable to: Global +#### [amount]% City Strength from defensive buildings +Example: "[20]% City Strength from defensive buildings" + +Applicable to: Global + #### [amount]% Strength Example: "[20]% Strength" @@ -417,6 +422,11 @@ Example: "Hidden until [20] social policy branches have been completed" Applicable to: Building, Unit +#### Not displayed as an available construction without [buildingName/tech/resource/policy] +Example: "Not displayed as an available construction without [buildingName/tech/resource/policy]" + +Applicable to: Building, Unit + #### Cost increases by [amount] per owned city Example: "Cost increases by [20] per owned city" @@ -437,11 +447,6 @@ Example: "Requires a [Library] in all cities" Applicable to: Building -#### Not displayed as an available construction without [buildingName/tech/resource/policy] -Example: "Not displayed as an available construction without [buildingName/tech/resource/policy]" - -Applicable to: Building, Unit - #### Must be on [terrainFilter] Example: "Must be on [Grassland]" @@ -1087,6 +1092,7 @@ Applicable to: Conditional - "-[amount]% food consumption by specialists [cityFilter]" - Deprecated As of 3.18.2, replace with "[-amount]% food consumption by specialists [cityFilter]" - "50% of excess happiness added to culture towards policies" - Deprecated As of 3.18.2, replace with "[50]% of excess happiness converted to [Culture]" - "May buy [baseUnitFilter] units for [amount] [stat] [cityFilter] starting from the [era] at an increasing price ([amount])" - Deprecated As of 3.17.9, replace with "May buy [baseUnitFilter] units for [amount] [stat] [cityFilter] at an increasing price ([amount]) " + - "Defensive buildings in all cities are 25% more effective" - Deprecated As of 3.18.17, replace with "[+25]% City Strength from defensive buildings" - "[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 " - "Immediately creates the cheapest available cultural building in each of your first [amount] cities for free" - Deprecated As of 3.16.15 - removed 3.18.4, replace with "Provides the cheapest [stat] building in your first [amount] cities for free"