From 545b4afc5a684ec78322a6a2bc41936440bc86d9 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Sat, 26 Nov 2022 21:39:26 +0200 Subject: [PATCH] Generalized "Stats per policies" unique --- .../jsons/Civ V - Gods & Kings/Buildings.json | 2 +- .../jsons/Civ V - Vanilla/Buildings.json | 2 +- .../unciv/logic/civilization/CivInfoStats.kt | 88 ++++++++----------- .../unciv/models/ruleset/unique/UniqueType.kt | 4 + docs/Modders/uniques.md | 28 ++---- .../com/unciv/uniques/GlobalUniquesTests.kt | 13 +++ 6 files changed, 63 insertions(+), 74 deletions(-) diff --git a/android/assets/jsons/Civ V - Gods & Kings/Buildings.json b/android/assets/jsons/Civ V - Gods & Kings/Buildings.json index 47190969ab..7f6728f5d0 100644 --- a/android/assets/jsons/Civ V - Gods & Kings/Buildings.json +++ b/android/assets/jsons/Civ V - Gods & Kings/Buildings.json @@ -959,7 +959,7 @@ "happiness": 5, "greatPersonPoints": {"Great Merchant": 2}, "isWonder": true, - "uniques": ["Provides 1 happiness per 2 additional social policies adopted"], + "uniques": ["[+1 Happiness] per [2] social policies adopted"], "requiredTech": "Radio", "quote": "'We live only to discover beauty, all else is a form of waiting' - Kahlil Gibran" }, diff --git a/android/assets/jsons/Civ V - Vanilla/Buildings.json b/android/assets/jsons/Civ V - Vanilla/Buildings.json index 59f1608523..8adfbe7d30 100644 --- a/android/assets/jsons/Civ V - Vanilla/Buildings.json +++ b/android/assets/jsons/Civ V - Vanilla/Buildings.json @@ -816,7 +816,7 @@ "happiness": 5, "greatPersonPoints": {"Great Merchant": 2}, "isWonder": true, - "uniques": ["Provides 1 happiness per 2 additional social policies adopted"], + "uniques": ["[+1 Happiness] per [2] social policies adopted"], "requiredTech": "Radio", "quote": "'We live only to discover beauty, all else is a form of waiting' - Kahlil Gibran" }, diff --git a/core/src/com/unciv/logic/civilization/CivInfoStats.kt b/core/src/com/unciv/logic/civilization/CivInfoStats.kt index beb5def397..161e96e9fd 100644 --- a/core/src/com/unciv/logic/civilization/CivInfoStats.kt +++ b/core/src/com/unciv/logic/civilization/CivInfoStats.kt @@ -3,7 +3,6 @@ package com.unciv.logic.civilization import com.unciv.Constants import com.unciv.logic.civilization.diplomacy.RelationshipLevel import com.unciv.logic.map.RoadStatus -import com.unciv.models.ruleset.BeliefType import com.unciv.models.ruleset.Policy import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.unique.StateForConditionals @@ -140,10 +139,6 @@ class CivInfoStats(val civInfo: CivilizationInfo) { statMap.add(entry.key, entry.value) } - for (unique in civInfo.getMatchingUniques(UniqueType.Stats)) - if (unique.sourceObjectType != UniqueTarget.Building && unique.sourceObjectType != UniqueTarget.Wonder) - statMap.add(unique.sourceObjectType!!.name, unique.stats) - //City-States bonuses for (otherCiv in civInfo.getKnownCivs()) { if (!otherCiv.isCityState()) continue @@ -164,21 +159,6 @@ class CivInfoStats(val civInfo: CivilizationInfo) { statMap["Transportation upkeep"] = Stats(gold = -getTransportationUpkeep().toFloat()) statMap["Unit upkeep"] = Stats(gold = -getUnitMaintenance().toFloat()) - if (civInfo.religionManager.religion != null) { - for (unique in civInfo.religionManager.religion!!.getFounderUniques()) { - if (unique.isOfType(UniqueType.StatsFromGlobalCitiesFollowingReligion)) { - statMap.add( - "Religion", - unique.stats * civInfo.religionManager.numberOfCitiesFollowingThisReligion() - ) - } - if (unique.isOfType(UniqueType.StatsFromGlobalFollowers)) - statMap.add( - "Religion", - unique.stats * civInfo.religionManager.numberOfFollowersFollowingThisReligion(unique.params[2]).toFloat() / unique.params[1].toFloat() - ) - } - } if (civInfo.getHappiness() > 0) { val excessHappinessConversion = Stats() @@ -201,12 +181,22 @@ class CivInfoStats(val civInfo: CivilizationInfo) { if (goldDifferenceFromTrade != 0) statMap["Trade"] = Stats(gold = goldDifferenceFromTrade.toFloat()) + for ((key, value) in getGlobalStatsFromUniques()) + statMap.add(key,value) + return statMap } fun getHappinessBreakdown(): HashMap { val statMap = HashMap() + + fun HashMap.add(key:String, value: Float){ + if (!containsKey(key)) put(key, value) + else put(key, value+get(key)!!) + } + fun HashMap.add(key:String, value: Int) = add(key, value.toFloat()) + statMap["Base happiness"] = civInfo.getDifficulty().baseHappiness.toFloat() var happinessPerUniqueLuxury = 4f + civInfo.getDifficulty().extraHappinessPerLuxury @@ -252,18 +242,12 @@ class CivInfoStats(val civInfo: CivilizationInfo) { // There appears to be a concurrency problem? In concurrent thread in ConstructionsTable.getConstructionButtonDTOs // Literally no idea how, since happinessList is ONLY replaced, NEVER altered. // Oh well, toList() should solve the problem, wherever it may come from. - for ((key, value) in city.cityStats.happinessList.toList()) { - if (statMap.containsKey(key)) - statMap[key] = statMap[key]!! + value - else statMap[key] = value - } + for ((key, value) in city.cityStats.happinessList.toList()) + statMap.add(key, value) } if (civInfo.hasUnique(UniqueType.HappinessPer2Policies)) { - if (!statMap.containsKey("Policies")) statMap["Policies"] = 0f - statMap["Policies"] = statMap["Policies"]!! + - civInfo.policies.getAdoptedPolicies() - .count { !Policy.isBranchCompleteByName(it) } / 2 + statMap.add("Policies", civInfo.policies.getAdoptedPolicies().count { !Policy.isBranchCompleteByName(it) } / 2) } var happinessPerNaturalWonder = 1f @@ -272,32 +256,38 @@ class CivInfoStats(val civInfo: CivilizationInfo) { statMap["Natural Wonders"] = happinessPerNaturalWonder * civInfo.naturalWonders.size + for ((key, value) in getGlobalStatsFromUniques()) + statMap.add(key,value.happiness) + + return statMap + } + + fun getGlobalStatsFromUniques():StatMap{ + val statMap = StatMap() if (civInfo.religionManager.religion != null) { - var religionHappiness = 0f - for (unique in civInfo.religionManager.religion!!.getBeliefs(BeliefType.Founder) - .flatMap { it.uniqueObjects } - ) { - if (unique.type == UniqueType.StatsFromGlobalCitiesFollowingReligion) { - val followingCities = - civInfo.religionManager.numberOfCitiesFollowingThisReligion() - religionHappiness += unique.stats.happiness * followingCities - } - if (unique.type == UniqueType.StatsFromGlobalFollowers) { - val followers = - civInfo.religionManager.numberOfFollowersFollowingThisReligion(unique.params[2]) - religionHappiness += - unique.stats.happiness * (followers / unique.params[1].toInt()) + for (unique in civInfo.religionManager.religion!!.getFounderUniques()) { + if (unique.isOfType(UniqueType.StatsFromGlobalCitiesFollowingReligion)) { + statMap.add( + "Religion", + unique.stats * civInfo.religionManager.numberOfCitiesFollowingThisReligion() + ) } + if (unique.isOfType(UniqueType.StatsFromGlobalFollowers)) + statMap.add( + "Religion", + unique.stats * civInfo.religionManager.numberOfFollowersFollowingThisReligion(unique.params[2]).toFloat() / unique.params[1].toFloat() + ) } - if (religionHappiness > 0) statMap["Religion"] = religionHappiness + } + + for (unique in civInfo.getMatchingUniques(UniqueType.StatsPerPolicies)) { + val amount = civInfo.policies.getAdoptedPolicies().count { !Policy.isBranchCompleteByName(it) } / unique.params[1].toInt() + statMap.add("Policies", unique.stats.times(amount)) } for (unique in civInfo.getMatchingUniques(UniqueType.Stats)) - if (unique.sourceObjectType != UniqueTarget.Building && unique.sourceObjectType != UniqueTarget.Wonder && unique.stats.happiness != 0f){ - val sourceObjectType = unique.sourceObjectType!!.name - if (!statMap.containsKey(sourceObjectType)) statMap[sourceObjectType] = unique.stats.happiness - else statMap[sourceObjectType] = statMap[sourceObjectType]!! + unique.stats.happiness - } + if (unique.sourceObjectType != UniqueTarget.Building && unique.sourceObjectType != UniqueTarget.Wonder) + statMap.add(unique.sourceObjectType!!.name, unique.stats) return statMap } diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 0e7a80f167..e8d166808b 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -80,6 +80,8 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: StatsFromSpecialist("[stats] from every specialist [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), StatsPerPopulation("[stats] per [amount] population [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), + StatsPerPolicies("[stats] per [amount] social policies adopted", UniqueTarget.Global), + StatsFromCitiesOnSpecificTiles("[stats] in cities on [terrainFilter] tiles", UniqueTarget.Global, UniqueTarget.FollowerBelief), StatsFromBuildings("[stats] from all [buildingFilter] buildings", UniqueTarget.Global, UniqueTarget.FollowerBelief), @@ -157,8 +159,10 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags: StatsWhenAdoptingReligion("[stats] when a city adopts this religion for the first time", UniqueTarget.Global), StatsSpendingGreatPeople("[stats] whenever a Great Person is expended", UniqueTarget.Global), + UnhappinessFromPopulationTypePercentageChange("[relativeAmount]% Unhappiness from [populationFilter] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), FoodConsumptionBySpecialists("[relativeAmount]% Food consumption by specialists [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief), + @Deprecated("as of 4.3.4", ReplaceWith("[+1 Happiness] per [2] social policies adopted")) HappinessPer2Policies("Provides 1 happiness per 2 additional social policies adopted", UniqueTarget.Global), ExcessHappinessToGlobalStat("[relativeAmount]% of excess happiness converted to [stat]", UniqueTarget.Global), diff --git a/docs/Modders/uniques.md b/docs/Modders/uniques.md index c4a608ab5c..73be258568 100644 --- a/docs/Modders/uniques.md +++ b/docs/Modders/uniques.md @@ -93,6 +93,11 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Global, FollowerBelief +??? example "[stats] per [amount] social policies adopted" + Example: "[+1 Gold, +2 Production] per [3] social policies adopted" + + Applicable to: Global + ??? example "[stats] in cities on [terrainFilter] tiles" Example: "[+1 Gold, +2 Production] in cities on [Fresh Water] tiles" @@ -302,9 +307,6 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Global, FollowerBelief -??? example "Provides 1 happiness per 2 additional social policies adopted" - Applicable to: Global - ??? example "[relativeAmount]% of excess happiness converted to [stat]" Example: "[+20]% of excess happiness converted to [Culture]" @@ -1623,21 +1625,6 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Ruins ## CityState uniques -??? example "Provides [stats] per turn" - Example: "Provides [+1 Gold, +2 Production] per turn" - - Applicable to: CityState - -??? example "Provides [stats] [cityFilter] per turn" - Example: "Provides [+1 Gold, +2 Production] [in all cities] per turn" - - Applicable to: CityState - -??? example "Provides [amount] Happiness" - Example: "Provides [3] Happiness" - - Applicable to: CityState - ??? example "Provides military units every ≈[amount] turns" Example: "Provides military units every ≈[3] turns" @@ -1723,11 +1710,6 @@ Simple unique parameters are explained by mouseover. Complex parameters are expl Applicable to: Conditional -??? example "<upon discovering [tech]>" - Example: "<upon discovering [Agriculture]>" - - Applicable to: Conditional - ??? example "<after adopting [policy]>" Example: "<after adopting [Oligarchy]>" diff --git a/tests/src/com/unciv/uniques/GlobalUniquesTests.kt b/tests/src/com/unciv/uniques/GlobalUniquesTests.kt index 252fedc8f4..997abbc204 100644 --- a/tests/src/com/unciv/uniques/GlobalUniquesTests.kt +++ b/tests/src/com/unciv/uniques/GlobalUniquesTests.kt @@ -207,6 +207,19 @@ class GlobalUniquesTests { Assert.assertTrue(city2.cityStats.finalStatList["Trade routes"]!!.science == 30f) } + @Test + fun statsFromPolicies() { + game.makeHexagonalMap(3) + val civInfo = game.addCiv("[+30 Science] per [2] social policies adopted") + val policiesToAdopt = listOf("Tradition", "Aristocracy", "Legalism") + civInfo.policies.freePolicies = 3 + for (policyName in policiesToAdopt){ + val policy = game.ruleset.policies[policyName]!! + civInfo.policies.adopt(policy, ) + } + Assert.assertTrue(civInfo.stats().getStatMapForNextTurn()["Policies"]!!.science == 30f) + } + @Test fun statsFromGlobalCitiesFollowingReligion() { val civ1 = game.addCiv()