Updated Tradition branch to G&K (#4106)

* Updated Tradition branch to G&K

* Small performance boost for calcualting maintenance; fix crash on next turn

* Fixed a bug where maintenance-free buildings would still cost maintenance

* Simplified some code

* I am unable to read

* Fixed a few broken uniques, including #4109

* Implemented requested changes
This commit is contained in:
Xander Lenstra
2021-06-13 07:14:31 +02:00
committed by GitHub
parent da991b5f66
commit 0d79326869
8 changed files with 98 additions and 38 deletions

View File

@ -52,7 +52,7 @@
"name": "Library",
"hurryCostModifier": 25,
"maintenance": 1,
"uniques": ["[+1 Science] Per [2] Population in this city"],
"uniques": ["[+1 Science] Per [2] Population [in this city]"],
"requiredTech": "Writing"
},
{
@ -72,7 +72,7 @@
"uniqueTo": "China",
"hurryCostModifier": 25,
"gold": 2,
"uniques": ["[+1 Science] Per [2] Population in this city"],
"uniques": ["[+1 Science] Per [2] Population [in this city]"],
"requiredTech": "Writing"
},
{
@ -752,7 +752,7 @@
"requiredBuilding": "University",
"maintenance": 3,
"hurryCostModifier": 0,
"uniques": ["[+1 Science] Per [2] Population in this city"],
"uniques": ["[+1 Science] Per [2] Population [in this city]"],
"requiredTech": "Scientific Theory"
},
{

View File

@ -6,19 +6,19 @@
"policies": [
{
"name": "Aristocracy",
"uniques": ["+[15]% Production when constructing [Wonders]", "[+1 Happiness] per [10] population in all cities"],
"uniques": ["+[15]% Production when constructing [Wonders]", "[+1 Happiness] per [10] population [in all cities]"],
"row": 1,
"column": 1
},
{
"name": "Legalism",
"uniques":["Immediately creates a cheapest available cultural building in each of your first 4 cities for free"],
"uniques":["Immediately creates the cheapest available cultural building in each of your first [4] cities for free"],
"row": 1,
"column": 3
},
{
"name": "Oligarchy",
"uniques": ["Units in cities cost no Maintenance", "+50% attacking strength for cities with garrisoned units"],
"uniques": ["Units in cities cost no Maintenance", "+[50]% attacking strength for cities with garrisoned units"],
"row": 1,
"column": 5
},
@ -38,7 +38,7 @@
},
{
"name": "Tradition Complete",
"uniques": ["+[15]% growth [in all cities]","[+2 Food] [in all cities]"]
"uniques": ["+[15]% growth [in all cities]","Immediately creates a [Aqueduct] in each of your first [4] cities for free"]
}
]
},

View File

@ -181,11 +181,12 @@ object BattleDamage {
)
modifiers["Statue of Zeus"] = 15
} else if (attacker is CityCombatant) {
if (attacker.getCivInfo()
.hasUnique("+50% attacking strength for cities with garrisoned units")
&& attacker.city.getCenterTile().militaryUnit != null
)
modifiers["Oligarchy"] = 50
if (attacker.city.getCenterTile().militaryUnit != null) {
val garrisonBonus = attacker.getCivInfo().getMatchingUniques("+[]% attacking strength for cities with garrisoned units")
.sumBy { it.params[0].toInt() }
if (garrisonBonus != 0)
modifiers["Garrisoned unit"] = garrisonBonus
}
}
return modifiers

View File

@ -81,8 +81,12 @@ class CityConstructions {
stats.add(building.getStats(cityInfo.civInfo))
for (unique in builtBuildingUniqueMap.getAllUniques()) when (unique.placeholderText) {
"[] Per [] Population in this city" -> stats.add(unique.stats.times(cityInfo.population.population / unique.params[1].toFloat()))
"[] per [] population []" -> if (cityInfo.matchesFilter(unique.params[2]))
stats.add(unique.stats.times(cityInfo.population.population / unique.params[1].toFloat()))
"[] once [] is discovered" -> if (cityInfo.civInfo.tech.isResearched(unique.params[1])) stats.add(unique.stats)
// Deprecated since 3.14.17, left for modding compatibility
"[] Per [] Population in this city" ->
stats.add(unique.stats.times(cityInfo.population.population / unique.params[1].toFloat()))
}
return stats
@ -92,13 +96,14 @@ class CityConstructions {
* @return Maintenance cost of all built buildings
*/
fun getMaintenanceCosts(): Int {
var maintenanceCost = getBuiltBuildings().sumBy { it.maintenance }
val policyManager = cityInfo.civInfo.policies
if (cityInfo.id in policyManager.legalismState) {
val buildingName = policyManager.legalismState[cityInfo.id]
maintenanceCost -= cityInfo.getRuleset().buildings[buildingName]!!.maintenance
var maintenanceCost = 0
// We cache this to increase performance
val freeBuildings = cityInfo.civInfo.policies.getListOfFreeBuildings(cityInfo.id)
for (building in getBuiltBuildings()) {
if (building.name !in freeBuildings) {
maintenanceCost += building.maintenance
}
}
return maintenanceCost
}

View File

@ -88,7 +88,7 @@ class CityInfo {
if (civInfo.cities.size == 1) cityConstructions.addBuilding(capitalCityIndicator())
civInfo.policies.tryAddLegalismBuildings()
civInfo.policies.tryToAddPolicyBuildings()
for (unique in civInfo.getMatchingUniques("Gain a free [] []")) {
val freeBuildingName = unique.params[0]

View File

@ -158,13 +158,9 @@ class CityStats {
fun getGrowthBonusFromPoliciesAndWonders(): Float {
var bonus = 0f
// This requires more... complex navigation of the local uniques to merge into "+[amount]% growth [cityFilter]"
for (unique in cityInfo.civInfo.getMatchingUniques("+[]% growth in all cities"))
bonus += unique.params[0].toFloat()
// "+[amount]% growth [cityFilter]"
for (unique in cityInfo.civInfo.getMatchingUniques("+[]% growth []"))
if (cityInfo.matchesFilter(unique.params[0]))
if (cityInfo.matchesFilter(unique.params[1]))
bonus += unique.params[0].toFloat()
return bonus / 100
}
@ -444,7 +440,7 @@ class CityStats {
First we see how much food we generate. Then we apply production bonuses to it.
Up till here, business as usual.
Then, we deduct food eaten (from the total produced).
Now we have the excess food, whih has its own things. If we're unhappy, cut it by 1/4.
Now we have the excess food, which has its own things. If we're unhappy, cut it by 1/4.
Some policies have bonuses for excess food only, not general food production.
*/

View File

@ -24,8 +24,14 @@ class PolicyManager {
var numberOfAdoptedPolicies = 0
var shouldOpenPolicyPicker = false
get() = field && canAdoptPolicy()
var legalismState = HashMap<String, String>()
private var cultureBuildingsAdded = HashMap<String, String>() // Maps cities to buildings
private var specificBuildingsAdded = HashMap<String, MutableSet<String>>() // Maps buildings to cities
var autocracyCompletedTurns = 0
@Deprecated("Deprecated since 3.14.17") // Replaced with cultureBuildingsAdded
var legalismState = HashMap<String, String>() // Maps cities to buildings
// We make it a reference copy of the original variable. This way, it can still works in older versions
fun clone(): PolicyManager {
val toReturn = PolicyManager()
@ -34,8 +40,13 @@ class PolicyManager {
toReturn.freePolicies = freePolicies
toReturn.shouldOpenPolicyPicker = shouldOpenPolicyPicker
toReturn.storedCulture = storedCulture
toReturn.legalismState.putAll(legalismState)
toReturn.cultureBuildingsAdded.putAll(cultureBuildingsAdded)
toReturn.specificBuildingsAdded.putAll(specificBuildingsAdded)
toReturn.autocracyCompletedTurns = autocracyCompletedTurns
// Deprecated since 3.14.17, left for backwards compatibility
toReturn.legalismState.putAll(cultureBuildingsAdded)
return toReturn
}
@ -44,6 +55,10 @@ class PolicyManager {
fun setTransients() {
for (policyName in adoptedPolicies)
addPolicyToTransients(getPolicyByName(policyName))
// Deprecated since 3.14.17, left for backwards compatibility
if (cultureBuildingsAdded.isEmpty() && legalismState.isNotEmpty()) {
cultureBuildingsAdded.putAll(legalismState)
}
}
fun addPolicyToTransients(policy: Policy) {
@ -52,7 +67,7 @@ class PolicyManager {
}
fun startTurn() {
tryAddLegalismBuildings()
tryToAddPolicyBuildings()
}
fun addCulture(culture: Int) {
@ -133,7 +148,7 @@ class PolicyManager {
for (unique in policy.uniqueObjects)
UniqueTriggerActivation.triggerCivwideUnique(unique, civInfo)
tryAddLegalismBuildings()
tryToAddPolicyBuildings()
// This ALSO has the side-effect of updating the CivInfo statForNextTurn so we don't need to call it explicitly
for (cityInfo in civInfo.cities)
@ -141,22 +156,63 @@ class PolicyManager {
if (!canAdoptPolicy()) shouldOpenPolicyPicker = false
}
fun tryToAddPolicyBuildings() {
tryAddCultureBuildings()
tryAddFreeBuildings()
}
fun tryAddLegalismBuildings() {
if (!civInfo.hasUnique("Immediately creates a cheapest available cultural building in each of your first 4 cities for free"))
return
if (legalismState.size >= 4) return
private fun tryAddCultureBuildings() {
val cultureBuildingUniques = civInfo.getMatchingUniques("Immediately creates the cheapest available cultural building in each of your first [] cities for free")
val citiesToReceiveCultureBuilding = cultureBuildingUniques.sumOf { it.params[0].toInt() }
if (!cultureBuildingUniques.any()) return
if (cultureBuildingsAdded.size >= citiesToReceiveCultureBuilding) return
val candidateCities = civInfo.cities
.sortedBy { it.turnAcquired }
.subList(0, min(4, civInfo.cities.size))
.subList(0, min(citiesToReceiveCultureBuilding, civInfo.cities.size))
.filter {
it.id !in legalismState
it.id !in cultureBuildingsAdded
&& it.cityConstructions.hasBuildableCultureBuilding()
}
for (city in candidateCities) {
val builtBuilding = city.cityConstructions.addCultureBuilding()
if (builtBuilding != null) legalismState[city.id] = builtBuilding!!
if (builtBuilding != null) cultureBuildingsAdded[city.id] = builtBuilding!!
}
}
private fun tryAddFreeBuildings() {
val matchingUniques = civInfo.getMatchingUniques("Immediately creates a [] in each of your first [] cities for free")
// If we have "create a free aqueduct in first 3 cities" and "create free aqueduct in first 4 cities", we do: "create free aqueduct in first 3+4=7 cities"
val sortedUniques = matchingUniques.groupBy {it.params[0]}
for (unique in sortedUniques) {
tryAddSpecificBuilding(unique.key, unique.value.sumBy {it.params[1].toInt()})
}
}
private fun tryAddSpecificBuilding(building: String, cityCount: Int) {
if (specificBuildingsAdded[building] == null) specificBuildingsAdded[building] = mutableSetOf()
val citiesAlreadyGivenBuilding = specificBuildingsAdded[building]
if (citiesAlreadyGivenBuilding!!.size >= cityCount) return
val candidateCities = civInfo.cities
.sortedBy { it.turnAcquired }
.subList(0, min(cityCount, civInfo.cities.size))
.filter {
it.id !in citiesAlreadyGivenBuilding && !it.cityConstructions.containsBuildingOrEquivalent(building)
}
for (city in candidateCities) {
city.cityConstructions.getConstruction(building).postBuildEvent(city.cityConstructions, false)
citiesAlreadyGivenBuilding.add(city.id)
}
}
fun getListOfFreeBuildings(cityId: String): MutableSet<String> {
val freeBuildings = cultureBuildingsAdded.filter { it.key == cityId }.values.toMutableSet()
for (building in specificBuildingsAdded.filter { it.value.contains(cityId) }) {
freeBuildings.add(building.key)
}
return freeBuildings
}
}

View File

@ -441,7 +441,9 @@ class Building : NamedStats(), IConstruction {
fun isStatRelated(stat: Stat): Boolean {
if (get(stat) > 0) return true
if (getStatPercentageBonuses(null).get(stat) > 0) return true
if (uniqueObjects.any { it.placeholderText == "[] Per [] Population in this city" && it.stats.get(stat) > 0 }) return true
if (uniqueObjects.any { it.placeholderText == "[] per [] population []" && it.stats.get(stat) > 0 }) return true
// Deprecated since 3.14.17, left for modding compatibility
if (uniqueObjects.any { it.placeholderText == "[] Per [] Population in this city"}) return true
return false
}