mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-24 03:53:12 -04:00
chore(purity): CityConstructions
This commit is contained in:
parent
99fa2cd964
commit
5e0d23fb1c
@ -66,6 +66,7 @@ allprojects {
|
|||||||
"kotlin.collections.subtract",
|
"kotlin.collections.subtract",
|
||||||
"kotlin.collections.union",
|
"kotlin.collections.union",
|
||||||
"kotlin.collections.intersect",
|
"kotlin.collections.intersect",
|
||||||
|
"kotlin.collections.List.indexOf",
|
||||||
|
|
||||||
)
|
)
|
||||||
wellKnownPureClasses = setOf<String>(
|
wellKnownPureClasses = setOf<String>(
|
||||||
|
@ -15,6 +15,7 @@ import com.unciv.models.ruleset.unique.UniqueType
|
|||||||
import com.unciv.models.stats.Stat
|
import com.unciv.models.stats.Stat
|
||||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActions
|
||||||
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsFromUniques
|
import com.unciv.ui.screens.worldscreen.unit.actions.UnitActionsFromUniques
|
||||||
|
import yairm210.purity.annotations.Readonly
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
object SpecificUnitAutomation {
|
object SpecificUnitAutomation {
|
||||||
@ -347,6 +348,7 @@ object SpecificUnitAutomation {
|
|||||||
return tileBeforeMoving != unit.currentTile
|
return tileBeforeMoving != unit.currentTile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Readonly
|
||||||
private fun getWonderThatWouldBenefitFromBeingSpedUp(city: City): Building? {
|
private fun getWonderThatWouldBenefitFromBeingSpedUp(city: City): Building? {
|
||||||
return city.cityConstructions.getBuildableBuildings().filter { building ->
|
return city.cityConstructions.getBuildableBuildings().filter { building ->
|
||||||
building.isWonder && !building.hasUnique(UniqueType.CannotBeHurried)
|
building.isWonder && !building.hasUnique(UniqueType.CannotBeHurried)
|
||||||
|
@ -37,6 +37,7 @@ import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
|||||||
import com.unciv.ui.screens.pickerscreens.PromotionTree
|
import com.unciv.ui.screens.pickerscreens.PromotionTree
|
||||||
import com.unciv.utils.withItem
|
import com.unciv.utils.withItem
|
||||||
import com.unciv.utils.withoutItem
|
import com.unciv.utils.withoutItem
|
||||||
|
import yairm210.purity.annotations.LocalState
|
||||||
import yairm210.purity.annotations.Readonly
|
import yairm210.purity.annotations.Readonly
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -101,17 +102,20 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Why is one of these called 'buildable' and the other 'constructable'?
|
// Why is one of these called 'buildable' and the other 'constructable'?
|
||||||
|
@Readonly
|
||||||
internal fun getBuildableBuildings(): Sequence<Building> = city.getRuleset().buildings.values
|
internal fun getBuildableBuildings(): Sequence<Building> = city.getRuleset().buildings.values
|
||||||
.asSequence().filter { it.isBuildable(this) }
|
.asSequence().filter { it.isBuildable(this) }
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun getConstructableUnits() = city.getRuleset().units.values
|
fun getConstructableUnits() = city.getRuleset().units.values
|
||||||
.asSequence().filter { it.isBuildable(this) }
|
.asSequence().filter { it.isBuildable(this) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return [Stats] provided by all built buildings in city plus the bonus from Library
|
* @return [Stats] provided by all built buildings in city
|
||||||
*/
|
*/
|
||||||
|
@Readonly
|
||||||
fun getStats(localUniqueCache: LocalUniqueCache): StatTreeNode {
|
fun getStats(localUniqueCache: LocalUniqueCache): StatTreeNode {
|
||||||
val stats = StatTreeNode()
|
@LocalState val stats = StatTreeNode()
|
||||||
for (building in getBuiltBuildings())
|
for (building in getBuiltBuildings())
|
||||||
stats.addStats(building.getStats(city, localUniqueCache), building.name)
|
stats.addStats(building.getStats(city, localUniqueCache), building.name)
|
||||||
return stats
|
return stats
|
||||||
@ -120,6 +124,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
/**
|
/**
|
||||||
* @return Maintenance cost of all built buildings
|
* @return Maintenance cost of all built buildings
|
||||||
*/
|
*/
|
||||||
|
@Readonly
|
||||||
fun getMaintenanceCosts(): Float {
|
fun getMaintenanceCosts(): Float {
|
||||||
var maintenanceCost = 0f
|
var maintenanceCost = 0f
|
||||||
val freeBuildings = city.civ.civConstructions.getFreeBuildingNames(city)
|
val freeBuildings = city.civ.civConstructions.getFreeBuildingNames(city)
|
||||||
@ -144,6 +149,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
return maintenanceCost
|
return maintenanceCost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun getCityProductionTextForCityButton(): String {
|
fun getCityProductionTextForCityButton(): String {
|
||||||
val currentConstructionSnapshot = currentConstructionFromQueue // See below
|
val currentConstructionSnapshot = currentConstructionFromQueue // See below
|
||||||
var result = currentConstructionSnapshot.tr(true)
|
var result = currentConstructionSnapshot.tr(true)
|
||||||
@ -156,10 +162,12 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @param constructionName needs to be a non-perpetual construction, else an empty string is returned */
|
/** @param constructionName needs to be a non-perpetual construction, else an empty string is returned */
|
||||||
|
@Readonly
|
||||||
internal fun getTurnsToConstructionString(constructionName: String, useStoredProduction: Boolean = true) =
|
internal fun getTurnsToConstructionString(constructionName: String, useStoredProduction: Boolean = true) =
|
||||||
getTurnsToConstructionString(getConstruction(constructionName), useStoredProduction)
|
getTurnsToConstructionString(getConstruction(constructionName), useStoredProduction)
|
||||||
|
|
||||||
/** @param construction needs to be a non-perpetual construction, else an empty string is returned */
|
/** @param construction needs to be a non-perpetual construction, else an empty string is returned */
|
||||||
|
@Readonly
|
||||||
internal fun getTurnsToConstructionString(construction: IConstruction, useStoredProduction: Boolean = true): String {
|
internal fun getTurnsToConstructionString(construction: IConstruction, useStoredProduction: Boolean = true): String {
|
||||||
if (construction !is INonPerpetualConstruction) return "" // shouldn't happen
|
if (construction !is INonPerpetualConstruction) return "" // shouldn't happen
|
||||||
val cost = construction.getProductionCost(city.civ, city)
|
val cost = construction.getProductionCost(city.civ, city)
|
||||||
@ -179,6 +187,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
return lines.joinToString("\n", "\n")
|
return lines.joinToString("\n", "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun getProductionMarkup(ruleset: Ruleset): FormattedLine {
|
fun getProductionMarkup(ruleset: Ruleset): FormattedLine {
|
||||||
val currentConstructionSnapshot = currentConstructionFromQueue
|
val currentConstructionSnapshot = currentConstructionFromQueue
|
||||||
if (currentConstructionSnapshot.isEmpty()) return FormattedLine()
|
if (currentConstructionSnapshot.isEmpty()) return FormattedLine()
|
||||||
@ -219,19 +228,22 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
/** @return `true` if [constructionName] is anywhere in the construction queue - [isBeingConstructed] **or** [isEnqueuedForLater] */
|
/** @return `true` if [constructionName] is anywhere in the construction queue - [isBeingConstructed] **or** [isEnqueuedForLater] */
|
||||||
@Readonly fun isBeingConstructedOrEnqueued(constructionName: String) = constructionQueue.contains(constructionName)
|
@Readonly fun isBeingConstructedOrEnqueued(constructionName: String) = constructionQueue.contains(constructionName)
|
||||||
|
|
||||||
fun isQueueFull(): Boolean = constructionQueue.size >= queueMaxSize
|
@Readonly fun isQueueFull(): Boolean = constructionQueue.size >= queueMaxSize
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun isBuildingWonder(): Boolean {
|
fun isBuildingWonder(): Boolean {
|
||||||
val currentConstruction = getCurrentConstruction()
|
val currentConstruction = getCurrentConstruction()
|
||||||
return currentConstruction is Building && currentConstruction.isWonder
|
return currentConstruction is Building && currentConstruction.isWonder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun canBeHurried(): Boolean {
|
fun canBeHurried(): Boolean {
|
||||||
val currentConstruction = getCurrentConstruction()
|
val currentConstruction = getCurrentConstruction()
|
||||||
return currentConstruction is INonPerpetualConstruction && !currentConstruction.hasUnique(UniqueType.CannotBeHurried)
|
return currentConstruction is INonPerpetualConstruction && !currentConstruction.hasUnique(UniqueType.CannotBeHurried)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If the city is constructing multiple units of the same type, subsequent units will require the full cost */
|
/** If the city is constructing multiple units of the same type, subsequent units will require the full cost */
|
||||||
|
@Readonly
|
||||||
fun isFirstConstructionOfItsKind(constructionQueueIndex: Int, name: String): Boolean {
|
fun isFirstConstructionOfItsKind(constructionQueueIndex: Int, name: String): Boolean {
|
||||||
// Simply compare index of first found [name] with given index
|
// Simply compare index of first found [name] with given index
|
||||||
return constructionQueueIndex == constructionQueue.indexOf(name)
|
return constructionQueueIndex == constructionQueue.indexOf(name)
|
||||||
@ -276,6 +288,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun turnsToConstruction(constructionName: String, useStoredProduction: Boolean = true): Int {
|
fun turnsToConstruction(constructionName: String, useStoredProduction: Boolean = true): Int {
|
||||||
val workLeft = getRemainingWork(constructionName, useStoredProduction)
|
val workLeft = getRemainingWork(constructionName, useStoredProduction)
|
||||||
if (workLeft <= 0) // This most often happens when a production is more than finished in a multiplayer game while its not your turn
|
if (workLeft <= 0) // This most often happens when a production is more than finished in a multiplayer game while its not your turn
|
||||||
@ -287,6 +300,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
return ceil((workLeft-productionOverflow) / productionForConstruction(constructionName).toDouble()).toInt()
|
return ceil((workLeft-productionOverflow) / productionForConstruction(constructionName).toDouble()).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun productionForConstruction(constructionName: String): Int {
|
fun productionForConstruction(constructionName: String): Int {
|
||||||
val cityStatsForConstruction: Stats
|
val cityStatsForConstruction: Stats
|
||||||
if (currentConstructionFromQueue == constructionName) cityStatsForConstruction = city.cityStats.currentCityStats
|
if (currentConstructionFromQueue == constructionName) cityStatsForConstruction = city.cityStats.currentCityStats
|
||||||
@ -302,7 +316,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
we get all sorts of fun concurrency problems when accessing various parts of the cityStats.
|
we get all sorts of fun concurrency problems when accessing various parts of the cityStats.
|
||||||
SO, we create an entirely new CityStats and iterate there - problem solve!
|
SO, we create an entirely new CityStats and iterate there - problem solve!
|
||||||
*/
|
*/
|
||||||
val cityStats = CityStats(city)
|
@LocalState val cityStats = CityStats(city)
|
||||||
cityStats.statsFromTiles = city.cityStats.statsFromTiles // take as-is
|
cityStats.statsFromTiles = city.cityStats.statsFromTiles // take as-is
|
||||||
val construction = city.cityConstructions.getConstruction(constructionName)
|
val construction = city.cityConstructions.getConstruction(constructionName)
|
||||||
cityStats.update(construction, false, false)
|
cityStats.update(construction, false, false)
|
||||||
@ -312,6 +326,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
|||||||
return cityStatsForConstruction.production.roundToInt()
|
return cityStatsForConstruction.production.roundToInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun cheapestStatBuilding(stat: Stat): Building? {
|
fun cheapestStatBuilding(stat: Stat): Building? {
|
||||||
return city.getRuleset().buildings.values.asSequence()
|
return city.getRuleset().buildings.values.asSequence()
|
||||||
.filter { !it.isAnyWonder() && it.isStatRelated(stat, city) &&
|
.filter { !it.isAnyWonder() && it.isStatRelated(stat, city) &&
|
||||||
|
@ -120,6 +120,7 @@ class CityStats(val city: City) {
|
|||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Readonly
|
||||||
fun getStatConversionRate(stat: Stat): Float {
|
fun getStatConversionRate(stat: Stat): Float {
|
||||||
var conversionRate = 1 / 4f
|
var conversionRate = 1 / 4f
|
||||||
val conversionUnique = city.civ.getMatchingUniques(UniqueType.ProductionToCivWideStatConversionBonus).firstOrNull { it.params[0] == stat.name }
|
val conversionUnique = city.civ.getMatchingUniques(UniqueType.ProductionToCivWideStatConversionBonus).firstOrNull { it.params[0] == stat.name }
|
||||||
|
@ -293,7 +293,7 @@ open class PerpetualConstruction(override var name: String, val description: Str
|
|||||||
IConstruction {
|
IConstruction {
|
||||||
|
|
||||||
override fun shouldBeDisplayed(cityConstructions: CityConstructions) = isBuildable(cityConstructions)
|
override fun shouldBeDisplayed(cityConstructions: CityConstructions) = isBuildable(cityConstructions)
|
||||||
open fun getProductionTooltip(city: City, withIcon: Boolean = false) : String = ""
|
@Readonly open fun getProductionTooltip(city: City, withIcon: Boolean = false) : String = ""
|
||||||
override fun getStockpiledResourceRequirements(state: GameContext) = Counter.ZERO
|
override fun getStockpiledResourceRequirements(state: GameContext) = Counter.ZERO
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -325,7 +325,7 @@ open class PerpetualStatConversion(val stat: Stat) :
|
|||||||
|
|
||||||
override fun getProductionTooltip(city: City, withIcon: Boolean) : String
|
override fun getProductionTooltip(city: City, withIcon: Boolean) : String
|
||||||
= "\r\n${(city.cityStats.currentCityStats.production / getConversionRate(city)).roundToInt()}${if (withIcon) stat.character else ""}/${Fonts.turn}"
|
= "\r\n${(city.cityStats.currentCityStats.production / getConversionRate(city)).roundToInt()}${if (withIcon) stat.character else ""}/${Fonts.turn}"
|
||||||
fun getConversionRate(city: City) : Int = (1/city.cityStats.getStatConversionRate(stat)).roundToInt()
|
@Readonly fun getConversionRate(city: City) : Int = (1/city.cityStats.getStatConversionRate(stat)).roundToInt()
|
||||||
|
|
||||||
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
|
override fun isBuildable(cityConstructions: CityConstructions): Boolean {
|
||||||
val city = cityConstructions.city
|
val city = cityConstructions.city
|
||||||
|
Loading…
x
Reference in New Issue
Block a user