Generalize production-to-stat conversion uniques (#7342)

* Generalize production to stat conversion enabling uniques

* Remove unnecessary code

* Rename class

* Update sound
This commit is contained in:
OptimizedForDensity
2022-07-04 09:38:24 -04:00
committed by GitHub
parent 1649b236bb
commit 9477b319bc
9 changed files with 57 additions and 38 deletions

View File

@ -197,7 +197,7 @@
"name": "Guilds",
"row": 7,
"prerequisites": ["Currency"],
"uniques": ["Enables conversion of city production to gold"],
"uniques": ["Enables conversion of city production to [Gold]"],
"quote": "'The merchants and the traders have come; their profits are pre-ordained...' - Sri Guru Granth Sahib"
},
{
@ -226,7 +226,7 @@
"name": "Education",
"row": 3,
"prerequisites": ["Theology","Civil Service"],
"uniques": ["Enables conversion of city production to science","Enables Research agreements"],
"uniques": ["Enables conversion of city production to [Science]","Enables Research agreements"],
"quote": "'Education is the best provision for old age.' - Aristotle"
},
{

View File

@ -170,7 +170,7 @@
"name": "Currency",
"row": 7,
"prerequisites": ["Mathematics"],
"uniques": ["Enables conversion of city production to gold"],
"uniques": ["Enables conversion of city production to [Gold]"],
"quote": "'Better is bread with a happy heart than wealth with vexation.' - Amenemope"
},
{
@ -206,7 +206,7 @@
"name": "Education",
"row": 3,
"prerequisites": ["Theology","Civil Service"],
"uniques": ["Enables conversion of city production to science"],
"uniques": ["Enables conversion of city production to [Science]"],
"quote": "'Education is the best provision for old age.' - Aristotle"
},
{

View File

@ -1023,6 +1023,8 @@ Current points =
Points per turn =
Convert production to gold at a rate of 4 to 1 =
Convert production to science at a rate of [rate] to 1 =
Convert production to [stat] at a rate of [rate] to 1 =
Production to [stat] conversion in cities changed by [relativeAmount]% =
The city will not produce anything. =
Worked by [cityName] =
Lock =

View File

@ -107,17 +107,22 @@ class CityStats(val cityInfo: CityInfo) {
private fun getStatsFromProduction(production: Float): Stats {
val stats = Stats()
when (cityInfo.cityConstructions.currentConstructionFromQueue) {
"Gold" -> stats.gold += production / 4
"Science" -> stats.science += production * getScienceConversionRate()
if (cityInfo.cityConstructions.currentConstructionFromQueue in Stat.statsWithCivWideField.map { it.name }) {
val stat = Stat.valueOf(cityInfo.cityConstructions.currentConstructionFromQueue)
stats[stat] = production * getStatConversionRate(stat)
}
return stats
}
fun getScienceConversionRate(): Float {
fun getStatConversionRate(stat: Stat): Float {
var conversionRate = 1 / 4f
if (cityInfo.civInfo.hasUnique(UniqueType.ProductionToScienceConversionBonus))
val conversionUnique = cityInfo.civInfo.getMatchingUniques(UniqueType.ProductionToCivWideStatConversionBonus).firstOrNull { it.params[0] == stat.name }
if (conversionUnique != null) {
conversionRate *= conversionUnique.params[1].toPercent()
} else if (stat == Stat.Science && cityInfo.civInfo.hasUnique(UniqueType.ProductionToScienceConversionBonus)) {
// backwards compatibility
conversionRate *= 1.33f
}
return conversionRate
}

View File

@ -212,35 +212,19 @@ data class RejectionReasonInstance(val rejectionReason:RejectionReason,
open class PerpetualConstruction(override var name: String, val description: String) : IConstruction {
override fun shouldBeDisplayed(cityConstructions: CityConstructions) = isBuildable(cityConstructions)
open fun getProductionTooltip(cityInfo: CityInfo) : String
= "\r\n${(cityInfo.cityStats.currentCityStats.production / CONVERSION_RATE).roundToInt()}/${Fonts.turn}"
open fun getConversionRate(cityInfo: CityInfo) : Int
= CONVERSION_RATE
open fun getProductionTooltip(cityInfo: CityInfo) : String = ""
companion object {
const val CONVERSION_RATE: Int = 4
val science = object : PerpetualConstruction("Science", "Convert production to science at a rate of [rate] to 1") {
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
return cityConstructions.cityInfo.civInfo.hasUnique(UniqueType.EnablesScienceProduction)
}
override fun getProductionTooltip(cityInfo: CityInfo): String {
return "\r\n${(cityInfo.cityStats.currentCityStats.production / getConversionRate(cityInfo)).roundToInt()}/${Fonts.turn}"
}
override fun getConversionRate(cityInfo: CityInfo) = (1/cityInfo.cityStats.getScienceConversionRate()).roundToInt()
}
val gold = object : PerpetualConstruction("Gold", "Convert production to gold at a rate of $CONVERSION_RATE to 1") {
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
return cityConstructions.cityInfo.civInfo.hasUnique(UniqueType.EnablesGoldProduction)
}
}
val science = PerpetualStatConversion(Stat.Science)
val gold = PerpetualStatConversion(Stat.Gold)
val culture = PerpetualStatConversion(Stat.Culture)
val faith = PerpetualStatConversion(Stat.Faith)
val idle = object : PerpetualConstruction("Nothing", "The city will not produce anything.") {
override fun isBuildable(cityConstructions: CityConstructions): Boolean = true
override fun getProductionTooltip(cityInfo: CityInfo): String = ""
}
val perpetualConstructionsMap: Map<String, PerpetualConstruction>
= mapOf(science.name to science, gold.name to gold, idle.name to idle)
= mapOf(science.name to science, gold.name to gold, culture.name to culture, faith.name to faith, idle.name to idle)
}
override fun isBuildable(cityConstructions: CityConstructions): Boolean =
@ -251,3 +235,24 @@ open class PerpetualConstruction(override var name: String, val description: Str
override fun requiresResource(resource: String) = false
}
open class PerpetualStatConversion(val stat: Stat) :
PerpetualConstruction(stat.name, "Convert production to [${stat.name}] at a rate of [rate] to 1") {
override fun getProductionTooltip(cityInfo: CityInfo) : String
= "\r\n${(cityInfo.cityStats.currentCityStats.production / getConversionRate(cityInfo)).roundToInt()}/${Fonts.turn}"
fun getConversionRate(cityInfo: CityInfo) : Int = (1/cityInfo.cityStats.getStatConversionRate(stat)).roundToInt()
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
val hasProductionUnique = cityConstructions.cityInfo.civInfo.getMatchingUniques(UniqueType.EnablesCivWideStatProduction).any { it.params[0] == stat.name }
return when (stat) {
Stat.Science -> hasProductionUnique
|| cityConstructions.cityInfo.civInfo.hasUnique(UniqueType.EnablesScienceProduction) // backwards compatibility
Stat.Gold -> hasProductionUnique
|| cityConstructions.cityInfo.civInfo.hasUnique(UniqueType.EnablesGoldProduction) // backwards compatibility
Stat.Culture -> hasProductionUnique
Stat.Faith -> cityConstructions.cityInfo.civInfo.gameInfo.isReligionEnabled() && hasProductionUnique
else -> false
}
}
}

View File

@ -162,7 +162,7 @@ enum class UniqueParameterType(
},
/** [UniqueType.DamageUnitsPlunder] and others near that one */
PlunderableStatName("plunderableStat", "Gold", "All the following stats can be plundered: `Gold`, `Science`, `Culture`, `Faith`") {
CivWideStatName("civWideStat", "Gold", "All the following stats have civ-wide fields: `Gold`, `Science`, `Culture`, `Faith`") {
private val knownValues = Stat.statsWithCivWideField.map { it.name }.toSet()
override fun getErrorSeverity(
parameterText: String,

View File

@ -193,9 +193,11 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
BuyBuildingsByProductionCost("May buy [buildingFilter] buildings with [stat] for [amount] times their normal Production cost", UniqueTarget.FollowerBelief, UniqueTarget.Global),
// ToDo: Unify
@Deprecated("As of 4.1.14", ReplaceWith("Enables conversion of city production to [Gold]"))
EnablesGoldProduction("Enables conversion of city production to gold", UniqueTarget.Global),
@Deprecated("s of 4.1.14", ReplaceWith("Enables conversion of city production to [Science]"))
EnablesScienceProduction("Enables conversion of city production to science", UniqueTarget.Global),
EnablesCivWideStatProduction("Enables conversion of city production to [civWideStat]", UniqueTarget.Global),
BuyItemsDiscount("[stat] cost of purchasing items in cities [relativeAmount]%", UniqueTarget.Global, UniqueTarget.FollowerBelief),
BuyBuildingsDiscount("[stat] cost of purchasing [buildingFilter] buildings [relativeAmount]%", UniqueTarget.Global, UniqueTarget.FollowerBelief),
@ -227,7 +229,9 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
EnablesConstructionOfSpaceshipParts("Enables construction of Spaceship parts", UniqueTarget.Global),
EnemyLandUnitsSpendExtraMovement("Enemy land units must spend 1 extra movement point when inside your territory (obsolete upon Dynamite)", UniqueTarget.Global),
@Deprecated("s of 4.1.14", ReplaceWith("Production to [Science] conversion in cities changed by [33]%"))
ProductionToScienceConversionBonus("Production to science conversion in cities increased by 33%", UniqueTarget.Global),
ProductionToCivWideStatConversionBonus("Production to [civWideStat] conversion in cities changed by [relativeAmount]%", UniqueTarget.Global),
// Misc national uniques
NotifiedOfBarbarianEncampments("Notified of new Barbarian encampments", UniqueTarget.Global),
@ -490,10 +494,10 @@ enum class UniqueType(val text: String, vararg targets: UniqueTarget, val flags:
UnitUpgradeCost("[relativeAmount]% Gold cost of upgrading", UniqueTarget.Unit, UniqueTarget.Global),
GreatPersonEarnedFaster("[greatPerson] is earned [relativeAmount]% faster", UniqueTarget.Unit, UniqueTarget.Global),
DamageUnitsPlunder("Earn [amount]% of the damage done to [combatantFilter] units as [plunderableStat]", UniqueTarget.Unit, UniqueTarget.Global),
CaptureCityPlunder("Upon capturing a city, receive [amount] times its [stat] production as [plunderableStat] immediately", UniqueTarget.Unit, UniqueTarget.Global),
KillUnitPlunder("Earn [amount]% of killed [mapUnitFilter] unit's [costOrStrength] as [plunderableStat]", UniqueTarget.Unit, UniqueTarget.Global),
KillUnitPlunderNearCity("Earn [amount]% of [mapUnitFilter] unit's [costOrStrength] as [plunderableStat] when killed within 4 tiles of a city following this religion", UniqueTarget.FollowerBelief),
DamageUnitsPlunder("Earn [amount]% of the damage done to [combatantFilter] units as [civWideStat]", UniqueTarget.Unit, UniqueTarget.Global),
CaptureCityPlunder("Upon capturing a city, receive [amount] times its [stat] production as [civWideStat] immediately", UniqueTarget.Unit, UniqueTarget.Global),
KillUnitPlunder("Earn [amount]% of killed [mapUnitFilter] unit's [costOrStrength] as [civWideStat]", UniqueTarget.Unit, UniqueTarget.Global),
KillUnitPlunderNearCity("Earn [amount]% of [mapUnitFilter] unit's [costOrStrength] as [civWideStat] when killed within 4 tiles of a city following this religion", UniqueTarget.FollowerBelief),
KillUnitCapture("May capture killed [mapUnitFilter] units", UniqueTarget.Unit),
FlatXPGain("[amount] XP gained from combat", UniqueTarget.Unit, UniqueTarget.Global),

View File

@ -444,6 +444,8 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
is BaseUnit -> UncivSound.Promote
PerpetualConstruction.gold -> UncivSound.Coin
PerpetualConstruction.science -> UncivSound.Paper
PerpetualConstruction.culture -> UncivSound.Policy
PerpetualConstruction.faith -> UncivSound.Choir
else -> UncivSound.Click
}
}

View File

@ -7,6 +7,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table
import com.unciv.UncivGame
import com.unciv.logic.city.IConstruction
import com.unciv.logic.city.PerpetualConstruction
import com.unciv.logic.city.PerpetualStatConversion
import com.unciv.models.ruleset.Building
import com.unciv.models.ruleset.IRulesetObject
import com.unciv.models.ruleset.unit.BaseUnit
@ -63,7 +64,7 @@ class ConstructionInfoTable(val cityScreen: CityScreen): Table() {
val description = when (construction) {
is BaseUnit -> construction.getDescription(city)
is Building -> construction.getDescription(city, true)
is PerpetualConstruction -> construction.description.replace("[rate]", "[${construction.getConversionRate(city)}]").tr()
is PerpetualStatConversion -> construction.description.replace("[rate]", "[${construction.getConversionRate(city)}]").tr()
else -> "" // Should never happen
}