Reworked buying buildings & units with stats a bit (#5618)

* Reworked buying buildings & units with stats a bit

* Reviews
This commit is contained in:
Xander Lenstra
2021-10-31 16:59:13 +01:00
committed by GitHub
parent 045b52c935
commit 53a0c9b248
5 changed files with 153 additions and 76 deletions

View File

@ -128,7 +128,7 @@
{ {
"name": "Cathedrals", "name": "Cathedrals",
"type": "Follower", "type": "Follower",
"uniques": ["May buy [Cathedral] buildings for [200] [Faith] [in cities following this religion]"] "uniques": ["May buy [Cathedral] buildings with [Faith] [in cities following this religion]"]
}, },
{ {
"name": "Choral Music", "name": "Choral Music",
@ -163,17 +163,17 @@
{ {
"name": "Monasteries", "name": "Monasteries",
"type": "Follower", "type": "Follower",
"uniques": ["May buy [Monastery] buildings for [200] [Faith] [in cities following this religion]"] "uniques": ["May buy [Monastery] buildings with [Faith] [in cities following this religion]"]
}, },
{ {
"name": "Mosques", "name": "Mosques",
"type": "Follower", "type": "Follower",
"uniques": ["May buy [Mosque] buildings for [200] [Faith] [in cities following this religion]"] "uniques": ["May buy [Mosque] buildings with [Faith] [in cities following this religion]"]
}, },
{ {
"name": "Pagodas", "name": "Pagodas",
"type": "Follower", "type": "Follower",
"uniques": ["May buy [Pagoda] buildings for [200] [Faith] [in cities following this religion]"] "uniques": ["May buy [Pagoda] buildings with [Faith] [in cities following this religion]"]
}, },
{ {
"name": "Peace Gardens", "name": "Peace Gardens",

View File

@ -2,6 +2,7 @@ package com.unciv.logic.city
import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.ruleset.IHasUniques import com.unciv.models.ruleset.IHasUniques
import com.unciv.models.ruleset.unique.StateForConditionals
import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.stats.INamed import com.unciv.models.stats.INamed
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
@ -34,7 +35,7 @@ interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
.any { it.params[0] == stat.name && (cityInfo != null && cityInfo.matchesFilter(it.params[1])) } .any { it.params[0] == stat.name && (cityInfo != null && cityInfo.matchesFilter(it.params[1])) }
) return true ) return true
// Can be purchased for [amount] [Stat] [cityFilter] // Can be purchased for [amount] [Stat] [cityFilter]
if (getMatchingUniques("Can be purchased for [] [] []") if (getMatchingUniques(UniqueType.CanBePurchasedForAmountStat)
.any { it.params[1] == stat.name && (cityInfo != null && cityInfo.matchesFilter(it.params[2])) } .any { it.params[1] == stat.name && (cityInfo != null && cityInfo.matchesFilter(it.params[2])) }
) return true ) return true
return false return false
@ -58,18 +59,24 @@ interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques {
fun getBaseBuyCost(cityInfo: CityInfo, stat: Stat): Int? { fun getBaseBuyCost(cityInfo: CityInfo, stat: Stat): Int? {
if (stat == Stat.Gold) return getBaseGoldCost(cityInfo.civInfo).toInt() if (stat == Stat.Gold) return getBaseGoldCost(cityInfo.civInfo).toInt()
val conditionalState = StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo)
// Can be purchased for [amount] [Stat] [cityFilter] // Can be purchased for [amount] [Stat] [cityFilter]
val lowestCostUnique = getMatchingUniques("Can be purchased for [] [] []") val lowestCostUnique = getMatchingUniques(UniqueType.CanBePurchasedForAmountStat, conditionalState)
.filter { it.params[1] == stat.name && cityInfo.matchesFilter(it.params[2]) } .filter { it.params[1] == stat.name && cityInfo.matchesFilter(it.params[2]) }
.minByOrNull { it.params[0].toInt() } .minByOrNull { it.params[0].toInt() }
if (lowestCostUnique != null) return lowestCostUnique.params[0].toInt() if (lowestCostUnique != null) return lowestCostUnique.params[0].toInt()
// Can be purchased with [Stat] [cityFilter] // Can be purchased with [Stat] [cityFilter]
if (getMatchingUniques(UniqueType.CanBePurchasedWithStat) if (getMatchingUniques(UniqueType.CanBePurchasedWithStat, conditionalState)
.any { it.params[0] == stat.name && cityInfo.matchesFilter(it.params[1]) } .any { it.params[0] == stat.name && cityInfo.matchesFilter(it.params[1]) }
) return cityInfo.civInfo.getEra().baseUnitBuyCost ) return cityInfo.civInfo.getEra().baseUnitBuyCost
return null return null
} }
fun getCostForConstructionsIncreasingInPrice(baseCost: Int, increaseCost: Int, previouslyBought: Int): Int {
return (baseCost + increaseCost / 2f * ( previouslyBought * previouslyBought + previouslyBought )).toInt()
}
} }

View File

@ -6,10 +6,7 @@ import com.unciv.logic.civilization.CivilizationInfo
import com.unciv.models.Counter import com.unciv.models.Counter
import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.tile.ResourceType
import com.unciv.models.ruleset.tile.TileImprovement import com.unciv.models.ruleset.tile.TileImprovement
import com.unciv.models.ruleset.unique.Unique import com.unciv.models.ruleset.unique.*
import com.unciv.models.ruleset.unique.UniqueTarget
import com.unciv.models.ruleset.unique.UniqueTriggerActivation
import com.unciv.models.ruleset.unique.UniqueType
import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat
import com.unciv.models.stats.Stats import com.unciv.models.stats.Stats
import com.unciv.models.translations.fillPlaceholders import com.unciv.models.translations.fillPlaceholders
@ -343,27 +340,76 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction {
override fun canBePurchasedWithStat(cityInfo: CityInfo?, stat: Stat): Boolean { override fun canBePurchasedWithStat(cityInfo: CityInfo?, stat: Stat): Boolean {
if (stat == Stat.Gold && isAnyWonder()) return false if (stat == Stat.Gold && isAnyWonder()) return false
// May buy [buildingFilter] buildings for [amount] [Stat] [cityFilter] if (cityInfo == null) return super.canBePurchasedWithStat(cityInfo, stat)
if (cityInfo != null && cityInfo.getMatchingUniques("May buy [] buildings for [] [] []")
.any { it.params[2] == stat.name && matchesFilter(it.params[0]) && cityInfo.matchesFilter(it.params[3]) } val conditionalState = StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo)
) return true return (
return super.canBePurchasedWithStat(cityInfo, stat) cityInfo.getMatchingUniques(UniqueType.BuyBuildingsIncreasingCost, conditionalState)
.any {
it.params[2] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3])
}
|| cityInfo.getMatchingUniques(UniqueType.BuyBuildingsByProductionCost, conditionalState)
.any { it.params[1] == stat.name && matchesFilter(it.params[0]) }
|| cityInfo.getMatchingUniques(UniqueType.BuyBuildingsWithStat, conditionalState)
.any {
it.params[1] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[2])
}
|| cityInfo.getMatchingUniques(UniqueType.BuyBuildingsForAmountStat, conditionalState)
.any {
it.params[2] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3])
}
|| return super.canBePurchasedWithStat(cityInfo, stat)
)
} }
override fun getBaseBuyCost(cityInfo: CityInfo, stat: Stat): Int? { override fun getBaseBuyCost(cityInfo: CityInfo, stat: Stat): Int? {
if (stat == Stat.Gold) return getBaseGoldCost(cityInfo.civInfo).toInt() if (stat == Stat.Gold) return getBaseGoldCost(cityInfo.civInfo).toInt()
val conditionalState = StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo)
return ( return sequence {
sequenceOf(super.getBaseBuyCost(cityInfo, stat)).filterNotNull() val baseCost = super.getBaseBuyCost(cityInfo, stat)
// May buy [buildingFilter] buildings for [amount] [Stat] [cityFilter] if (baseCost != null)
+ cityInfo.getMatchingUniques("May buy [] buildings for [] [] []") yield(baseCost)
yieldAll(cityInfo.getMatchingUniques(UniqueType.BuyBuildingsIncreasingCost, conditionalState)
.filter { .filter {
it.params[2] == stat.name && matchesFilter(it.params[0]) && cityInfo.matchesFilter( it.params[2] == stat.name
it.params[3] && matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3])
}.map {
getCostForConstructionsIncreasingInPrice(
it.params[1].toInt(),
it.params[4].toInt(),
cityInfo.civInfo.civConstructions.boughtItemsWithIncreasingPrice[name] ?: 0
) )
} }
.map { it.params[1].toInt() } )
).minOrNull() yieldAll(cityInfo.getMatchingUniques(UniqueType.BuyBuildingsByProductionCost, conditionalState)
.filter { it.params[1] == stat.name && matchesFilter(it.params[0]) }
.map { getProductionCost(cityInfo.civInfo) * it.params[2].toInt() }
)
if (cityInfo.getMatchingUniques(UniqueType.BuyBuildingsWithStat, conditionalState)
.any {
it.params[1] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[2])
}
) {
yield(cityInfo.civInfo.getEra().baseUnitBuyCost)
}
yieldAll(cityInfo.getMatchingUniques(UniqueType.BuyBuildingsForAmountStat, conditionalState)
.filter {
it.params[2] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3])
}.map { it.params[1].toInt() }
)
}.minOrNull()
} }
override fun getStatBuyCost(cityInfo: CityInfo, stat: Stat): Int? { override fun getStatBuyCost(cityInfo: CityInfo, stat: Stat): Int? {

View File

@ -150,11 +150,19 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
FreeExtraBeliefs("May choose [amount] additional [beliefType] beliefs when [foundingOrEnhancing] a religion", UniqueTarget.Global), FreeExtraBeliefs("May choose [amount] additional [beliefType] beliefs when [foundingOrEnhancing] a religion", UniqueTarget.Global),
FreeExtraAnyBeliefs("May choose [amount] additional belief(s) of any type when [foundingOrEnhancing] a religion", UniqueTarget.Global), FreeExtraAnyBeliefs("May choose [amount] additional belief(s) of any type when [foundingOrEnhancing] a religion", UniqueTarget.Global),
// There is potential to merge these
BuyUnitsIncreasingCost("May buy [baseUnitFilter] units for [amount] [stat] [cityFilter] at an increasing price ([amount])", UniqueTarget.Global), BuyUnitsIncreasingCost("May buy [baseUnitFilter] units for [amount] [stat] [cityFilter] at an increasing price ([amount])", UniqueTarget.Global),
BuyBuildingsIncreasingCost("May buy [buildingFilter] buildings for [amount] [stat] [cityFilter] at an increasing price ([amount])", UniqueTarget.Global), BuyBuildingsIncreasingCost("May buy [buildingFilter] buildings for [amount] [stat] [cityFilter] at an increasing price ([amount])", UniqueTarget.Global),
BuyUnitsForAmountStat("May buy [baseUnitFilter] units for [amount] [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyBuildingsForAmountStat("May buy [buildingFilter] buildings for [amount] [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyUnitsWithStat("May buy [baseUnitFilter] units with [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyBuildingsWithStat("May buy [buildingFilter] buildings with [stat] [cityFilter]", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyUnitsByProductionCost("May buy [baseUnitFilter] units with [stat] for [amount] times their normal Production cost", UniqueTarget.FollowerBelief, UniqueTarget.Global),
BuyBuildingsByProductionCost("May buy [buildingFilter] buildings with [stat] for [amount] times their normal Production cost", UniqueTarget.FollowerBelief, UniqueTarget.Global),
@Deprecated("As of 3.17.9", ReplaceWith ("May buy [baseUnitFilter] units for [amount] [stat] [cityFilter] at an increasing price ([amount]) <starting from the [era]>")) @Deprecated("As of 3.17.9", ReplaceWith ("May buy [baseUnitFilter] units for [amount] [stat] [cityFilter] at an increasing price ([amount]) <starting from the [era]>"))
BuyUnitsIncreasingCostEra("May buy [baseUnitFilter] units for [amount] [stat] [cityFilter] starting from the [era] at an increasing price ([amount])", UniqueTarget.Global), BuyUnitsIncreasingCostEra("May buy [baseUnitFilter] units for [amount] [stat] [cityFilter] starting from the [era] at an increasing price ([amount])", UniqueTarget.Global),
BuyUnitsByProductionCost("May buy [baseUnitFilter] units with [stat] for [amount] times their normal Production cost", UniqueTarget.FollowerBelief, UniqueTarget.Global),
MayanGainGreatPerson("Receive a free Great Person at the end of every [comment] (every 394 years), after researching [tech]. Each bonus person can only be chosen once.", UniqueTarget.Nation), MayanGainGreatPerson("Receive a free Great Person at the end of every [comment] (every 394 years), after researching [tech]. Each bonus person can only be chosen once.", UniqueTarget.Nation),
MayanCalendarDisplay("Once The Long Count activates, the year on the world screen displays as the traditional Mayan Long Count.", UniqueTarget.Nation), MayanCalendarDisplay("Once The Long Count activates, the year on the world screen displays as the traditional Mayan Long Count.", UniqueTarget.Nation),
@ -165,6 +173,7 @@ enum class UniqueType(val text:String, vararg targets: UniqueTarget) {
Unbuildable("Unbuildable", UniqueTarget.Building, UniqueTarget.Unit), Unbuildable("Unbuildable", UniqueTarget.Building, UniqueTarget.Unit),
CannotBePurchased("Cannot be purchased", UniqueTarget.Building, UniqueTarget.Unit), CannotBePurchased("Cannot be purchased", UniqueTarget.Building, UniqueTarget.Unit),
CanBePurchasedWithStat("Can be purchased with [stat] [cityFilter]", UniqueTarget.Building, UniqueTarget.Unit), CanBePurchasedWithStat("Can be purchased with [stat] [cityFilter]", UniqueTarget.Building, UniqueTarget.Unit),
CanBePurchasedForAmountStat("Can be purchased for [amount] [stat] [cityFilter]", UniqueTarget.Building, UniqueTarget.Unit),
///////////////////////////////////////// BUILDING UNIQUES ///////////////////////////////////////// ///////////////////////////////////////// BUILDING UNIQUES /////////////////////////////////////////

View File

@ -250,53 +250,56 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
} }
override fun canBePurchasedWithStat(cityInfo: CityInfo?, stat: Stat): Boolean { override fun canBePurchasedWithStat(cityInfo: CityInfo?, stat: Stat): Boolean {
// May buy [unitFilter] units for [amount] [Stat] [cityFilter] starting from the [eraName] at an increasing price ([amount]) if (cityInfo == null) return super.canBePurchasedWithStat(cityInfo, stat)
if (cityInfo != null && cityInfo.getMatchingUniques("May buy [] units for [] [] [] starting from the [] at an increasing price ([])") val conditionalState = StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo)
return (
cityInfo.getMatchingUniques(UniqueType.BuyUnitsIncreasingCostEra, conditionalState)
.any { .any {
matchesFilter(it.params[0]) it.params[2] == stat.name
&& cityInfo.matchesFilter(it.params[3])
&& cityInfo.civInfo.getEraNumber() >= ruleset.eras[it.params[4]]!!.eraNumber && cityInfo.civInfo.getEraNumber() >= ruleset.eras[it.params[4]]!!.eraNumber
&& it.params[2] == stat.name && matchesFilter(it.params[0])
}
) return true
// May buy [unitFilter] units for [amount] [Stat] [cityFilter] at an increasing price ([amount])
if (cityInfo != null && cityInfo.getMatchingUniques("May buy [] units for [] [] [] at an increasing price ([])")
.any {
matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3]) && cityInfo.matchesFilter(it.params[3])
&& it.params[2] == stat.name
} }
) return true || cityInfo.getMatchingUniques(UniqueType.BuyUnitsIncreasingCost, conditionalState)
.any {
if (cityInfo != null && cityInfo.getMatchingUniques( it.params[2] == stat.name
UniqueType.BuyUnitsByProductionCost, && matchesFilter(it.params[0])
stateForConditionals = StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo) && cityInfo.matchesFilter(it.params[3])
).any {
matchesFilter(it.params[0])
&& it.params[1] == stat.name
} }
) return true || cityInfo.getMatchingUniques(UniqueType.BuyUnitsByProductionCost, conditionalState)
.any { it.params[1] == stat.name && matchesFilter(it.params[0]) }
return super.canBePurchasedWithStat(cityInfo, stat) || cityInfo.getMatchingUniques(UniqueType.BuyUnitsWithStat, conditionalState)
.any {
it.params[1] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[2])
} }
|| cityInfo.getMatchingUniques(UniqueType.BuyUnitsForAmountStat, conditionalState)
private fun getCostForConstructionsIncreasingInPrice(baseCost: Int, increaseCost: Int, previouslyBought: Int): Int { .any {
return (baseCost + increaseCost / 2f * ( previouslyBought * previouslyBought + previouslyBought )).toInt() it.params[2] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3])
}
|| return super.canBePurchasedWithStat(cityInfo, stat)
)
} }
override fun getBaseBuyCost(cityInfo: CityInfo, stat: Stat): Int? { override fun getBaseBuyCost(cityInfo: CityInfo, stat: Stat): Int? {
if (stat == Stat.Gold) return getBaseGoldCost(cityInfo.civInfo).toInt() if (stat == Stat.Gold) return getBaseGoldCost(cityInfo.civInfo).toInt()
val conditionalState = StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo) val conditionalState = StateForConditionals(civInfo = cityInfo.civInfo, cityInfo = cityInfo)
return (
sequenceOf(super.getBaseBuyCost(cityInfo, stat)).filterNotNull() return sequence {
val baseCost = super.getBaseBuyCost(cityInfo, stat)
if (baseCost != null)
yield(baseCost)
// Deprecated since 3.17.9 // Deprecated since 3.17.9
+ (cityInfo.getMatchingUniques(UniqueType.BuyUnitsIncreasingCostEra, conditionalState) yieldAll(cityInfo.getMatchingUniques(UniqueType.BuyUnitsIncreasingCostEra, conditionalState)
.filter { .filter {
matchesFilter(it.params[0]) it.params[2] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3]) && cityInfo.matchesFilter(it.params[3])
&& cityInfo.civInfo.getEraNumber() >= ruleset.eras[it.params[4]]!!.eraNumber && cityInfo.civInfo.getEraNumber() >= ruleset.eras[it.params[4]]!!.eraNumber
&& it.params[2] == stat.name
}.map { }.map {
getCostForConstructionsIncreasingInPrice( getCostForConstructionsIncreasingInPrice(
it.params[1].toInt(), it.params[1].toInt(),
@ -306,11 +309,11 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
} }
) )
// //
+ (cityInfo.getMatchingUniques(UniqueType.BuyUnitsIncreasingCost, conditionalState) yieldAll(cityInfo.getMatchingUniques(UniqueType.BuyUnitsIncreasingCost, conditionalState)
.filter { .filter {
matchesFilter(it.params[0]) it.params[2] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3]) && cityInfo.matchesFilter(it.params[3])
&& it.params[2] == stat.name
}.map { }.map {
getCostForConstructionsIncreasingInPrice( getCostForConstructionsIncreasingInPrice(
it.params[1].toInt(), it.params[1].toInt(),
@ -319,15 +322,27 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
) )
} }
) )
+ (cityInfo.getMatchingUniques(UniqueType.BuyUnitsByProductionCost, conditionalState) yieldAll(cityInfo.getMatchingUniques(UniqueType.BuyUnitsByProductionCost, conditionalState)
.filter { .filter { it.params[1] == stat.name && matchesFilter(it.params[0]) }
.map { getProductionCost(cityInfo.civInfo) * it.params[2].toInt() }
)
if (cityInfo.getMatchingUniques(UniqueType.BuyUnitsWithStat, conditionalState)
.any {
it.params[1] == stat.name it.params[1] == stat.name
&& matchesFilter(it.params[0]) && matchesFilter(it.params[0])
}.map { && cityInfo.matchesFilter(it.params[2])
getProductionCost(cityInfo.civInfo) * it.params[2].toInt()
} }
) {
yield(cityInfo.civInfo.getEra().baseUnitBuyCost)
}
yieldAll(cityInfo.getMatchingUniques(UniqueType.BuyUnitsForAmountStat, conditionalState)
.filter {
it.params[2] == stat.name
&& matchesFilter(it.params[0])
&& cityInfo.matchesFilter(it.params[3])
}.map { it.params[1].toInt() }
) )
).minOrNull() }.minOrNull()
} }
override fun getStatBuyCost(cityInfo: CityInfo, stat: Stat): Int? { override fun getStatBuyCost(cityInfo: CityInfo, stat: Stat): Int? {