Performance improvements for parametrized uniques

This commit is contained in:
Yair Morgenstern
2020-08-05 21:31:45 +03:00
parent 69ed4e3b53
commit a39303625a
14 changed files with 110 additions and 82 deletions

View File

@ -6,6 +6,7 @@ import com.unciv.logic.automation.ConstructionAutomation
import com.unciv.logic.civilization.AlertType import com.unciv.logic.civilization.AlertType
import com.unciv.logic.civilization.PopupAlert import com.unciv.logic.civilization.PopupAlert
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.UniqueMap
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
@ -13,6 +14,7 @@ import com.unciv.ui.cityscreen.ConstructionInfoTable
import com.unciv.ui.utils.withItem import com.unciv.ui.utils.withItem
import com.unciv.ui.utils.withoutItem import com.unciv.ui.utils.withoutItem
import java.util.* import java.util.*
import kotlin.collections.ArrayList
import kotlin.math.roundToInt import kotlin.math.roundToInt
/** /**
@ -26,6 +28,7 @@ import kotlin.math.roundToInt
class CityConstructions { class CityConstructions {
@Transient lateinit var cityInfo: CityInfo @Transient lateinit var cityInfo: CityInfo
@Transient private var builtBuildingObjects = ArrayList<Building>() @Transient private var builtBuildingObjects = ArrayList<Building>()
@Transient val builtBuildingUniqueMap = UniqueMap()
var builtBuildings = HashSet<String>() var builtBuildings = HashSet<String>()
val inProgressConstructions = HashMap<String, Int>() val inProgressConstructions = HashMap<String, Int>()
@ -214,6 +217,7 @@ class CityConstructions {
fun setTransients(){ fun setTransients(){
builtBuildingObjects = ArrayList(builtBuildings.map { cityInfo.getRuleset().buildings[it] builtBuildingObjects = ArrayList(builtBuildings.map { cityInfo.getRuleset().buildings[it]
?: throw java.lang.Exception("Building $it is not found!")}) ?: throw java.lang.Exception("Building $it is not found!")})
updateUniques()
} }
fun addProductionPoints(productionToAdd: Int) { fun addProductionPoints(productionToAdd: Int) {
@ -301,12 +305,21 @@ class CityConstructions {
val buildingObject = cityInfo.getRuleset().buildings[buildingName]!! val buildingObject = cityInfo.getRuleset().buildings[buildingName]!!
builtBuildingObjects = builtBuildingObjects.withItem(buildingObject) builtBuildingObjects = builtBuildingObjects.withItem(buildingObject)
builtBuildings.add(buildingName) builtBuildings.add(buildingName)
updateUniques()
} }
fun removeBuilding(buildingName:String){ fun removeBuilding(buildingName:String){
val buildingObject = cityInfo.getRuleset().buildings[buildingName]!! val buildingObject = cityInfo.getRuleset().buildings[buildingName]!!
builtBuildingObjects = builtBuildingObjects.withoutItem(buildingObject) builtBuildingObjects = builtBuildingObjects.withoutItem(buildingObject)
builtBuildings.remove(buildingName) builtBuildings.remove(buildingName)
updateUniques()
}
fun updateUniques(){
builtBuildingUniqueMap.clear()
for(building in getBuiltBuildings())
for(unique in building.uniqueObjects)
builtBuildingUniqueMap.addUnique(unique)
} }
/** /**

View File

@ -195,19 +195,19 @@ class CityInfo {
if (resource.improvement == tileInfo.improvement || tileInfo.isCityCenter() if (resource.improvement == tileInfo.improvement || tileInfo.isCityCenter()
// Per https://gaming.stackexchange.com/questions/53155/do-manufactories-and-customs-houses-sacrifice-the-strategic-or-luxury-resources // Per https://gaming.stackexchange.com/questions/53155/do-manufactories-and-customs-houses-sacrifice-the-strategic-or-luxury-resources
|| (resource.resourceType==ResourceType.Strategic && tileInfo.containsGreatImprovement())){ || (resource.resourceType==ResourceType.Strategic && tileInfo.containsGreatImprovement())) {
var amountToAdd = 1 var amountToAdd = 1
if(resource.resourceType == ResourceType.Strategic) { if (resource.resourceType == ResourceType.Strategic) {
amountToAdd = 2 amountToAdd = 2
if (civInfo.hasUnique("Quantity of strategic resources produced by the empire increased by 100%")) if (civInfo.hasUnique("Quantity of strategic resources produced by the empire increased by 100%"))
amountToAdd *= 2 amountToAdd *= 2
} }
for(unique in civInfo.getMatchingUniques("Double quantity of [] produced")) for (unique in civInfo.getMatchingUniques2("Double quantity of [] produced"))
if(unique.getPlaceholderParameters()[0]==resource.name) if (unique.params[0] == resource.name)
amountToAdd*=2 amountToAdd *= 2
if(resource.resourceType == ResourceType.Luxury if (resource.resourceType == ResourceType.Luxury
&& containsBuildingUnique("Provides 1 extra copy of each improved luxury resource near this City")) && containsBuildingUnique("Provides 1 extra copy of each improved luxury resource near this City"))
amountToAdd*=2 amountToAdd *= 2
return amountToAdd return amountToAdd
} }
@ -260,20 +260,19 @@ class CityInfo {
stats["Buildings"] = buildingStats stats["Buildings"] = buildingStats
for (entry in stats) { for (entry in stats) {
for (unique in civInfo.getMatchingUniques("[] is earned []% faster")) { for (unique in civInfo.getMatchingUniques2("[] is earned []% faster")) {
val params = unique.getPlaceholderParameters() val unit = civInfo.gameInfo.ruleSet.units[unique.params[0]]
val unit = civInfo.gameInfo.ruleSet.units[params[0]]
if (unit == null) continue if (unit == null) continue
val greatUnitUnique = unit.uniques.firstOrNull { it.equalsPlaceholderText("Great Person - []") } val greatUnitUnique = unit.uniques.firstOrNull { it.equalsPlaceholderText("Great Person - []") }
if (greatUnitUnique == null) continue if (greatUnitUnique == null) continue
val statName = greatUnitUnique.getPlaceholderParameters()[0] val statName = greatUnitUnique.getPlaceholderParameters()[0]
val stat = Stat.values().firstOrNull { it.name == statName } val stat = Stat.values().firstOrNull { it.name == statName }
// this is not very efficient, and if it causes problems we can try and think of a way of improving it // this is not very efficient, and if it causes problems we can try and think of a way of improving it
if (stat != null) entry.value.add(stat, entry.value.get(stat) * params[1].toFloat()/100) if (stat != null) entry.value.add(stat, entry.value.get(stat) * unique.params[1].toFloat()/100)
} }
for (unique in civInfo.getMatchingUniques("+[]% great person generation in all cities")) for (unique in civInfo.getMatchingUniques2("+[]% great person generation in all cities"))
stats[entry.key] = stats[entry.key]!!.times(1 + (unique.getPlaceholderParameters()[0].toFloat() / 100)) stats[entry.key] = stats[entry.key]!!.times(1 + (unique.params[0].toFloat() / 100))
} }
return stats return stats

View File

@ -51,8 +51,8 @@ class CityStats {
if (!cityInfo.isCapital() && cityInfo.isConnectedToCapital()) { if (!cityInfo.isCapital() && cityInfo.isConnectedToCapital()) {
val civInfo = cityInfo.civInfo 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) 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 civInfo.getMatchingUniques("[] from each Trade Route")) for (unique in civInfo.getMatchingUniques2("[] from each Trade Route"))
stats.add(Stats.parse(unique.getPlaceholderParameters()[0])) stats.add(Stats.parse(unique.params[0]))
if (civInfo.hasUnique("Gold from all trade routes +25%")) stats.gold *= 1.25f // Machu Pichu speciality if (civInfo.hasUnique("Gold from all trade routes +25%")) stats.gold *= 1.25f // Machu Pichu speciality
} }
return stats return stats
@ -247,8 +247,8 @@ class CityStats {
if (stat == Stat.Culture || stat == Stat.Science) stats.add(stat, 3f) if (stat == Stat.Culture || stat == Stat.Science) stats.add(stat, 3f)
else stats.add(stat, 2f) // science and gold specialists else stats.add(stat, 2f) // science and gold specialists
for(unique in cityInfo.civInfo.getMatchingUniques("[] from every specialist")) for(unique in cityInfo.civInfo.getMatchingUniques2("[] from every specialist"))
stats.add(Stats.parse(unique.getPlaceholderParameters()[0])) stats.add(Stats.parse(unique.params[0]))
if (cityInfo.civInfo.hasUnique("+1 Production from specialists")) if (cityInfo.civInfo.hasUnique("+1 Production from specialists"))
stats.production += 1 stats.production += 1
if(cityInfo.civInfo.nation.unique == UniqueAbility.SCHOLARS_OF_THE_JADE_HALL) if(cityInfo.civInfo.nation.unique == UniqueAbility.SCHOLARS_OF_THE_JADE_HALL)

View File

@ -118,7 +118,7 @@ class CivInfoStats(val civInfo: CivilizationInfo){
// TODO - happinessPerUnique should be difficulty-dependent, 5 on Settler and Chieftian and 4 on other difficulties (should be parameter, not in code) // TODO - happinessPerUnique should be difficulty-dependent, 5 on Settler and Chieftian and 4 on other difficulties (should be parameter, not in code)
var happinessPerUniqueLuxury = 4f + civInfo.getDifficulty().extraHappinessPerLuxury var happinessPerUniqueLuxury = 4f + civInfo.getDifficulty().extraHappinessPerLuxury
for(unique in civInfo.getMatchingUniques("+1 happiness from each luxury resource")) for(unique in civInfo.getMatchingUniques2("+1 happiness from each luxury resource"))
happinessPerUniqueLuxury += 1 happinessPerUniqueLuxury += 1
statMap["Luxury resources"]= civInfo.getCivResources().map { it.resource } statMap["Luxury resources"]= civInfo.getCivResources().map { it.resource }
.count { it.resourceType === ResourceType.Luxury } * happinessPerUniqueLuxury .count { it.resourceType === ResourceType.Luxury } * happinessPerUniqueLuxury

View File

@ -15,10 +15,7 @@ import com.unciv.logic.map.MapUnit
import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileInfo
import com.unciv.logic.trade.TradeEvaluation import com.unciv.logic.trade.TradeEvaluation
import com.unciv.logic.trade.TradeRequest import com.unciv.logic.trade.TradeRequest
import com.unciv.models.ruleset.Building import com.unciv.models.ruleset.*
import com.unciv.models.ruleset.Difficulty
import com.unciv.models.ruleset.Nation
import com.unciv.models.ruleset.VictoryType
import com.unciv.models.ruleset.tile.ResourceSupplyList import com.unciv.models.ruleset.tile.ResourceSupplyList
import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.BaseUnit
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
@ -180,14 +177,13 @@ class CivilizationInfo {
fun getBuildingUniques() = cities.asSequence().flatMap { it.getBuildingUniques() } fun getBuildingUniques() = cities.asSequence().flatMap { it.getBuildingUniques() }
private fun getCivUniques() = nation.uniques.asSequence() + policies.policyEffects.asSequence() + getBuildingUniques() fun hasUnique(unique:String) = getMatchingUniques2(unique).any()
fun hasUnique(unique:String) = getCivUniques().contains(unique) fun getMatchingUniques2(uniqueTemplate: String): Sequence<Unique> {
return nation.uniqueObjects.asSequence().filter { it.placeholderText == uniqueTemplate } +
fun getMatchingUniques(uniqueTemplate: String) = cities.asSequence().flatMap { it.cityConstructions.builtBuildingUniqueMap.getUniques(uniqueTemplate).asSequence() } +
if (uniqueTemplate.contains('[')) policies.policyUniques.getUniques(uniqueTemplate)
getCivUniques().filter { it.equalsPlaceholderText(uniqueTemplate) } }
else getCivUniques().filter { it==uniqueTemplate }
//region Units //region Units
fun getCivUnits(): Sequence<MapUnit> = units.asSequence() fun getCivUnits(): Sequence<MapUnit> = units.asSequence()

View File

@ -27,7 +27,7 @@ class GoldenAgeManager{
fun enterGoldenAge() { fun enterGoldenAge() {
var turnsToGoldenAge = 10.0 var turnsToGoldenAge = 10.0
for(unique in civInfo.getMatchingUniques("Golden Age length increases +50%")) for(unique in civInfo.getMatchingUniques2("Golden Age length increases +50%"))
turnsToGoldenAge *= 1.5 turnsToGoldenAge *= 1.5
if(civInfo.nation.unique == UniqueAbility.ACHAEMENID_LEGACY ) if(civInfo.nation.unique == UniqueAbility.ACHAEMENID_LEGACY )
turnsToGoldenAge*=1.5 turnsToGoldenAge*=1.5

View File

@ -1,7 +1,10 @@
package com.unciv.logic.civilization package com.unciv.logic.civilization
import com.sun.xml.internal.ws.policy.PolicyMap
import com.unciv.Constants import com.unciv.Constants
import com.unciv.models.ruleset.Policy import com.unciv.models.ruleset.Policy
import com.unciv.models.ruleset.Unique
import com.unciv.models.ruleset.UniqueMap
import com.unciv.models.ruleset.VictoryType import com.unciv.models.ruleset.VictoryType
import com.unciv.models.translations.equalsPlaceholderText import com.unciv.models.translations.equalsPlaceholderText
import com.unciv.models.translations.getPlaceholderParameters import com.unciv.models.translations.getPlaceholderParameters
@ -18,6 +21,7 @@ class PolicyManager {
// Needs to be separate from the actual adopted policies, so that // Needs to be separate from the actual adopted policies, so that
// in different game versions, policies can have different effects // in different game versions, policies can have different effects
@Transient internal val policyEffects = HashSet<String>() @Transient internal val policyEffects = HashSet<String>()
@Transient internal val policyUniques = UniqueMap()
var freePolicies = 0 var freePolicies = 0
var storedCulture = 0 var storedCulture = 0
@ -42,9 +46,15 @@ class PolicyManager {
fun getPolicyByName(name:String): Policy = getAllPolicies().first { it.name==name } fun getPolicyByName(name:String): Policy = getAllPolicies().first { it.name==name }
fun setTransients(){ fun setTransients() {
for(policy in adoptedPolicies) for (policyName in adoptedPolicies)
policyEffects.addAll(getPolicyByName(policy).uniques) addPolicyToTransients(getPolicyByName(policyName))
}
fun addPolicyToTransients(policy: Policy){
policyEffects.addAll(policy.uniques)
for(unique in policy.uniqueObjects)
policyUniques.addUnique(unique)
} }
private fun getAllPolicies() = civInfo.gameInfo.ruleSet.policyBranches.values.asSequence() private fun getAllPolicies() = civInfo.gameInfo.ruleSet.policyBranches.values.asSequence()
@ -71,7 +81,7 @@ class PolicyManager {
if (civInfo.hasUnique("Each city founded increases culture cost of policies 33% less than normal")) if (civInfo.hasUnique("Each city founded increases culture cost of policies 33% less than normal"))
cityModifier *= (2 / 3f) cityModifier *= (2 / 3f)
for(unique in civInfo.getMatchingUniques("Culture cost of adopting new Policies reduced by 10%")) for(unique in civInfo.getMatchingUniques2("Culture cost of adopting new Policies reduced by 10%"))
policyCultureCost *= 0.9 policyCultureCost *= 0.9
if (civInfo.isPlayerCivilization()) if (civInfo.isPlayerCivilization())
policyCultureCost *= civInfo.getDifficulty().policyCostModifier policyCultureCost *= civInfo.getDifficulty().policyCostModifier
@ -117,7 +127,7 @@ class PolicyManager {
} }
adoptedPolicies.add(policy.name) adoptedPolicies.add(policy.name)
policyEffects.addAll(policy.uniques) addPolicyToTransients(policy)
if (!branchCompletion) { if (!branchCompletion) {
val branch = policy.branch val branch = policy.branch

View File

@ -170,7 +170,7 @@ class TechManager {
private fun scienceFromResearchAgreements(): Int { private fun scienceFromResearchAgreements(): Int {
// https://forums.civfanatics.com/resources/research-agreements-bnw.25568/ // https://forums.civfanatics.com/resources/research-agreements-bnw.25568/
var researchAgreementModifier = 0.5f var researchAgreementModifier = 0.5f
for(unique in civInfo.getMatchingUniques("Science gained from research agreements +50%")) for(unique in civInfo.getMatchingUniques2("Science gained from research agreements +50%"))
researchAgreementModifier += 0.25f researchAgreementModifier += 0.25f
return (scienceFromResearchAgreements / 3 * researchAgreementModifier).toInt() return (scienceFromResearchAgreements / 3 * researchAgreementModifier).toInt()
} }
@ -272,10 +272,9 @@ class TechManager {
} }
} }
for(unique in civInfo.getMatchingUniques("Receive free [] when you discover []")){ for(unique in civInfo.getMatchingUniques2("Receive free [] when you discover []")) {
val params = unique.getPlaceholderParameters() if (unique.params[1] != techName) continue
if(params[1]!=techName) continue civInfo.addUnit(unique.params[0])
civInfo.addUnit(params[0])
} }
} }

View File

@ -263,7 +263,7 @@ class MapUnit {
fun getCostOfUpgrade(): Int { fun getCostOfUpgrade(): Int {
val unitToUpgradeTo = getUnitToUpgradeTo() val unitToUpgradeTo = getUnitToUpgradeTo()
var goldCostOfUpgrade = (unitToUpgradeTo.cost - baseUnit().cost) * 2 + 10 var goldCostOfUpgrade = (unitToUpgradeTo.cost - baseUnit().cost) * 2 + 10
for(unique in civInfo.getMatchingUniques("Gold cost of upgrading military units reduced by 33%")) for(unique in civInfo.getMatchingUniques2("Gold cost of upgrading military units reduced by 33%"))
goldCostOfUpgrade = (goldCostOfUpgrade * 0.66f).toInt() goldCostOfUpgrade = (goldCostOfUpgrade * 0.66f).toInt()
if(goldCostOfUpgrade<0) return 0 // For instance, Landsknecht costs less than Spearman, so upgrading would cost negative gold if(goldCostOfUpgrade<0) return 0 // For instance, Landsknecht costs less than Spearman, so upgrading would cost negative gold
return goldCostOfUpgrade return goldCostOfUpgrade

View File

@ -172,7 +172,6 @@ open class TileInfo {
fun getTileStats(city: CityInfo?, observingCiv: CivilizationInfo): Stats { fun getTileStats(city: CityInfo?, observingCiv: CivilizationInfo): Stats {
var stats = getBaseTerrain().clone() var stats = getBaseTerrain().clone()
if (terrainFeature != null) { if (terrainFeature != null) {
val terrainFeatureBase = getTerrainFeature() val terrainFeatureBase = getTerrainFeature()
if (terrainFeatureBase!!.overrideStats) if (terrainFeatureBase!!.overrideStats)
@ -181,14 +180,11 @@ open class TileInfo {
stats.add(terrainFeatureBase) stats.add(terrainFeatureBase)
} }
if(city!=null) for(unique in city.getBuildingUniques()) { if(city!=null) for(unique in city.cityConstructions.builtBuildingUniqueMap.getUniques("[] from [] tiles")) {
if (unique.equalsPlaceholderText("[] from [] tiles")) { val tileType = unique.params[1]
val placeholderParams = unique.getPlaceholderParameters() if (baseTerrain == tileType || terrainFeature == tileType || resource == tileType || improvement == tileType
val tileType = placeholderParams[1] || (tileType == "Strategic resource" && hasViewableResource(observingCiv) && getTileResource().resourceType == ResourceType.Strategic))
if (baseTerrain == tileType || terrainFeature == tileType || resource == tileType || improvement == tileType stats.add(Stats.parse(unique.params[0]))
|| (tileType == "Strategic resource" && hasViewableResource(observingCiv) && getTileResource().resourceType == ResourceType.Strategic))
stats.add(Stats.parse(placeholderParams[0]))
}
} }
if (naturalWonder != null) { if (naturalWonder != null) {
@ -249,10 +245,9 @@ open class TileInfo {
stats.add(improvement.improvingTechStats!!) // eg Chemistry for mines stats.add(improvement.improvingTechStats!!) // eg Chemistry for mines
if(city!=null) if(city!=null)
for(unique in city.civInfo.getMatchingUniques("[] from every []")) { for(unique in city.civInfo.getMatchingUniques2("[] from every []")) {
val placeholderParams = unique.getPlaceholderParameters() if (improvement.name == unique.params[1])
if (unique == placeholderParams[1]) stats.add(Stats.parse(unique.params[0]))
stats.add(Stats.parse(placeholderParams[0]))
} }
if (containsGreatImprovement() if (containsGreatImprovement()

View File

@ -9,10 +9,29 @@ import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.models.translations.equalsPlaceholderText import com.unciv.models.translations.equalsPlaceholderText
import com.unciv.models.translations.getPlaceholderParameters import com.unciv.models.translations.getPlaceholderParameters
import com.unciv.models.translations.getPlaceholderText
import com.unciv.models.translations.tr import com.unciv.models.translations.tr
import kotlin.math.pow import kotlin.math.pow
class Building : NamedStats(), IConstruction{ class Unique(val text:String){
val placeholderText = text.getPlaceholderText()
val params = text.getPlaceholderParameters()
}
class UniqueMap:HashMap<String, ArrayList<Unique>>() {
fun addUnique(unique: Unique) {
if (!containsKey(unique.placeholderText)) this[unique.placeholderText] = ArrayList()
this[unique.placeholderText]!!.add(unique)
}
fun getUniques(placeholderText: String): List<Unique> {
val result = this.get(placeholderText)
if (result == null) return listOf()
else return result
}
}
class Building : NamedStats(), IConstruction {
var requiredTech: String? = null var requiredTech: String? = null
@ -40,7 +59,7 @@ class Building : NamedStats(), IConstruction{
var quote:String="" var quote:String=""
private var providesFreeBuilding: String? = null private var providesFreeBuilding: String? = null
var uniques = ArrayList<String>() var uniques = ArrayList<String>()
val uniqueObjects:List<Unique> by lazy { uniques.map { Unique(it) } }
/** /**
* The bonus stats that a resource gets when this building is built * The bonus stats that a resource gets when this building is built
@ -129,32 +148,29 @@ class Building : NamedStats(), IConstruction{
return stringBuilder.toString().trim() return stringBuilder.toString().trim()
} }
private val cultureBuildings = hashSetOf("Monument", "Temple", "Monastery")
fun getStats(civInfo: CivilizationInfo?): Stats { fun getStats(civInfo: CivilizationInfo?): Stats {
val stats = this.clone() val stats = this.clone()
if(civInfo != null) { if(civInfo != null) {
val adoptedPolicies = civInfo.policies.adoptedPolicies val adoptedPolicies = civInfo.policies.adoptedPolicies
val baseBuildingName = getBaseBuilding(civInfo.gameInfo.ruleSet).name val baseBuildingName = getBaseBuilding(civInfo.gameInfo.ruleSet).name
for(unique in civInfo.getMatchingUniques("[] from every []")) { for(unique in civInfo.getMatchingUniques2("[] from every []")) {
val placeholderParams = unique.getPlaceholderParameters() if (unique.params[1] != baseBuildingName) continue
if (placeholderParams[1] != baseBuildingName) continue stats.add(Stats.parse(unique.params[0]))
stats.add(Stats.parse(placeholderParams[0]))
} }
// todo policy
if (adoptedPolicies.contains("Humanism") && hashSetOf("University", "Observatory", "Public School").contains(baseBuildingName )) if (adoptedPolicies.contains("Humanism") && hashSetOf("University", "Observatory", "Public School").contains(baseBuildingName ))
stats.happiness += 1f stats.happiness += 1f
if(!isWonder) if(!isWonder)
for(unique in civInfo.getMatchingUniques("[] from all [] buildings")){ for(unique in civInfo.getMatchingUniques2("[] from all [] buildings")){
val placeholderParams = unique.getPlaceholderParameters() if(isStatRelated(Stat.valueOf(unique.params[1])))
if(isStatRelated(Stat.valueOf(placeholderParams[1]))) stats.add(Stats.parse(unique.params[0]))
stats.add(Stats.parse(placeholderParams[0]))
} }
else else
for(unique in civInfo.getMatchingUniques("[] from every Wonder")) for(unique in civInfo.getMatchingUniques2("[] from every Wonder"))
stats.add(Stats.parse(unique.getPlaceholderParameters()[0])) stats.add(Stats.parse(unique.params[0]))
if (adoptedPolicies.contains("Police State") && baseBuildingName == "Courthouse") if (adoptedPolicies.contains("Police State") && baseBuildingName == "Courthouse")
stats.happiness += 3 stats.happiness += 3
@ -169,10 +185,9 @@ class Building : NamedStats(), IConstruction{
val baseBuildingName = getBaseBuilding(civInfo.gameInfo.ruleSet).name val baseBuildingName = getBaseBuilding(civInfo.gameInfo.ruleSet).name
for (unique in civInfo.getMatchingUniques("+[]% [] from every []")) { for (unique in civInfo.getMatchingUniques2("+[]% [] from every []")) {
val placeholderParams = unique.getPlaceholderParameters() if (unique.params[2] == baseBuildingName)
if (placeholderParams[2] == baseBuildingName) stats.add(Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
stats.add(Stat.valueOf(placeholderParams[1]), placeholderParams[0].toFloat())
} }
if (uniques.contains("+5% Production for every Trade Route with a City-State in the empire")) if (uniques.contains("+5% Production for every Trade Route with a City-State in the empire"))
@ -208,13 +223,12 @@ class Building : NamedStats(), IConstruction{
// https://forums.civfanatics.com/threads/rush-buying-formula.393892/ // https://forums.civfanatics.com/threads/rush-buying-formula.393892/
var cost = (30 * getProductionCost(civInfo)).toDouble().pow(0.75) * (1 + hurryCostModifier / 100) var cost = (30 * getProductionCost(civInfo)).toDouble().pow(0.75) * (1 + hurryCostModifier / 100)
for (unique in civInfo.getMatchingUniques("Cost of purchasing items in cities reduced by []%")) for (unique in civInfo.getMatchingUniques2("Cost of purchasing items in cities reduced by []%"))
cost *= 1 - (unique.getPlaceholderParameters()[0].toFloat() / 100) cost *= 1 - (unique.params[0].toFloat() / 100)
for (unique in civInfo.getMatchingUniques("Cost of purchasing [] buildings reduced by []%")) { for (unique in civInfo.getMatchingUniques2("Cost of purchasing [] buildings reduced by []%")) {
val placeholderParams = unique.getPlaceholderParameters() if (isStatRelated(Stat.valueOf(unique.params[0])))
if (isStatRelated(Stat.valueOf(placeholderParams[0]))) cost *= 1 - (unique.params[1].toFloat() / 100)
cost *= 1 - (placeholderParams[1].toFloat() / 100)
} }
return (cost / 10).toInt() * 10 return (cost / 10).toInt() * 10

View File

@ -37,6 +37,7 @@ class Nation : INamed {
lateinit var outerColor: List<Int> lateinit var outerColor: List<Int>
var unique: UniqueAbility? = null var unique: UniqueAbility? = null
val uniques = HashSet<String>() val uniques = HashSet<String>()
val uniqueObjects:List<Unique> by lazy { uniques.map { Unique(it) } }
var innerColor: List<Int>? = null var innerColor: List<Int>? = null
var startBias = ArrayList<String>() var startBias = ArrayList<String>()

View File

@ -8,6 +8,7 @@ open class Policy : INamed {
override lateinit var name: String override lateinit var name: String
lateinit var effect: String lateinit var effect: String
var uniques: ArrayList<String> = ArrayList() var uniques: ArrayList<String> = ArrayList()
val uniqueObjects:List<Unique> by lazy { uniques.map { Unique(it) } }
var row: Int = 0 var row: Int = 0
var column: Int = 0 var column: Int = 0
var requires: ArrayList<String>? = null var requires: ArrayList<String>? = null

View File

@ -104,8 +104,8 @@ class BaseUnit : INamed, IConstruction {
override fun getGoldCost(civInfo: CivilizationInfo): Int { override fun getGoldCost(civInfo: CivilizationInfo): Int {
var cost = getBaseGoldCost() var cost = getBaseGoldCost()
if (civInfo.hasUnique("Gold cost of purchasing units -33%")) cost *= 0.66f if (civInfo.hasUnique("Gold cost of purchasing units -33%")) cost *= 0.66f
for(unique in civInfo.getMatchingUniques("Cost of purchasing items in cities reduced by []%")) for(unique in civInfo.getMatchingUniques2("Cost of purchasing items in cities reduced by []%"))
cost *= 1-(unique.getPlaceholderParameters()[0].toFloat()) cost *= 1-(unique.params[0].toFloat())
return (cost / 10).toInt() * 10 // rounded down o nearest ten return (cost / 10).toInt() * 10 // rounded down o nearest ten
} }
@ -159,8 +159,8 @@ class BaseUnit : INamed, IConstruction {
if (this.unitType.isCivilian()) return true // tiny optimization makes save files a few bytes smaller if (this.unitType.isCivilian()) return true // tiny optimization makes save files a few bytes smaller
var XP = construction.getBuiltBuildings().sumBy { it.xpForNewUnits } var XP = construction.getBuiltBuildings().sumBy { it.xpForNewUnits }
for (unique in construction.cityInfo.civInfo.getMatchingUniques("New military units start with [] Experience")) for (unique in construction.cityInfo.civInfo.getMatchingUniques2("New military units start with [] Experience"))
XP += unique.getPlaceholderParameters()[0].toInt() XP += unique.params[0].toInt()
unit.promotions.XP = XP unit.promotions.XP = XP
if (unit.type in listOf(UnitType.Melee,UnitType.Mounted,UnitType.Armor) if (unit.type in listOf(UnitType.Melee,UnitType.Mounted,UnitType.Armor)