mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 14:24:43 -04:00
City construction speedup with caching stats from tiles (#5536)
* City construction speedup with caching stats from tiles * Reduced cityStats.update to only one cityConstructions.getStats() call, improving performance by another 30% approx
This commit is contained in:
parent
9a7ea263d6
commit
6ac3547b8e
@ -337,7 +337,9 @@ class GameInfo {
|
||||
civInfo.initialSetCitiesConnectedToCapitalTransients()
|
||||
|
||||
// We need to determine the GLOBAL happiness state in order to determine the city stats
|
||||
for (cityInfo in civInfo.cities) cityInfo.cityStats.updateCityHappiness()
|
||||
for (cityInfo in civInfo.cities) cityInfo.cityStats.updateCityHappiness(
|
||||
cityInfo.cityConstructions.getStats()
|
||||
)
|
||||
|
||||
for (cityInfo in civInfo.cities) {
|
||||
/** We remove constructions from the queue that aren't defined in the ruleset.
|
||||
|
@ -317,8 +317,9 @@ class CityConstructions {
|
||||
SO, we create an entirely new CityStats and iterate there - problem solve!
|
||||
*/
|
||||
val cityStats = CityStats(cityInfo)
|
||||
cityStats.statsFromTiles = cityInfo.cityStats.statsFromTiles // take as-is
|
||||
val construction = cityInfo.cityConstructions.getConstruction(constructionName)
|
||||
cityStats.update(construction)
|
||||
cityStats.update(construction, false)
|
||||
cityStatsForConstruction = cityStats.currentCityStats
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
|
||||
var happinessList = LinkedHashMap<String, Float>()
|
||||
|
||||
var statsFromTiles = Stats()
|
||||
|
||||
var foodEaten = 0f
|
||||
|
||||
var currentCityStats: Stats = Stats() // This is so we won't have to calculate this multiple times - takes a lot of time, especially on phones
|
||||
@ -41,16 +43,6 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
//endregion
|
||||
//region Pure Functions
|
||||
|
||||
private fun getStatsFromTiles(): Stats {
|
||||
val stats = Stats()
|
||||
for (cell in cityInfo.tilesInRange
|
||||
.filter { cityInfo.location == it.position || cityInfo.isWorked(it) ||
|
||||
it.owningCity == cityInfo && (it.getTileImprovement()?.hasUnique(UniqueType.TileProvidesYieldWithoutPopulation) == true ||
|
||||
it.hasUnique(UniqueType.TileProvidesYieldWithoutPopulation))
|
||||
})
|
||||
stats.add(cell.getTileStats(cityInfo, cityInfo.civInfo))
|
||||
return stats
|
||||
}
|
||||
|
||||
private fun getStatsFromTradeRoute(): Stats {
|
||||
val stats = Stats()
|
||||
@ -389,9 +381,24 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
//endregion
|
||||
//region State-Changing Methods
|
||||
|
||||
fun updateTileStats() {
|
||||
val stats = Stats()
|
||||
for (cell in cityInfo.tilesInRange
|
||||
.filter {
|
||||
cityInfo.location == it.position
|
||||
|| cityInfo.isWorked(it)
|
||||
|| it.owningCity == cityInfo && (it.getTileImprovement()
|
||||
?.hasUnique(UniqueType.TileProvidesYieldWithoutPopulation) == true
|
||||
|| it.hasUnique(UniqueType.TileProvidesYieldWithoutPopulation))
|
||||
})
|
||||
stats.add(cell.getTileStats(cityInfo, cityInfo.civInfo))
|
||||
statsFromTiles = stats
|
||||
}
|
||||
|
||||
|
||||
// needs to be a separate function because we need to know the global happiness state
|
||||
// in order to determine how much food is produced in a city!
|
||||
fun updateCityHappiness() {
|
||||
fun updateCityHappiness(statsFromBuildings: Stats) {
|
||||
val civInfo = cityInfo.civInfo
|
||||
val newHappinessList = LinkedHashMap<String, Float>()
|
||||
var unhappinessModifier = civInfo.getDifficulty().unhappinessModifier
|
||||
@ -439,8 +446,7 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
val happinessFromSpecialists = getStatsFromSpecialists(cityInfo.population.getNewSpecialists()).happiness.toInt().toFloat()
|
||||
if (happinessFromSpecialists > 0) newHappinessList["Specialists"] = happinessFromSpecialists
|
||||
|
||||
val happinessFromBuildings = cityInfo.cityConstructions.getStats().happiness.toInt().toFloat()
|
||||
newHappinessList["Buildings"] = happinessFromBuildings
|
||||
newHappinessList["Buildings"] = statsFromBuildings.happiness.toInt().toFloat()
|
||||
|
||||
newHappinessList["National ability"] = getStatsFromUniques(cityInfo.civInfo.nation.uniqueObjects.asSequence()).happiness
|
||||
|
||||
@ -448,14 +454,14 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
|
||||
newHappinessList["Religion"] = getStatsFromUniques(cityInfo.religion.getUniques()).happiness
|
||||
|
||||
newHappinessList["Tile yields"] = getStatsFromTiles().happiness
|
||||
newHappinessList["Tile yields"] = statsFromTiles.happiness
|
||||
|
||||
// we don't want to modify the existing happiness list because that leads
|
||||
// to concurrency problems if we iterate on it while changing
|
||||
happinessList = newHappinessList
|
||||
}
|
||||
|
||||
private fun updateBaseStatList() {
|
||||
private fun updateBaseStatList(statsFromBuildings: Stats) {
|
||||
val newBaseStatList = LinkedHashMap<String, Stats>() // we don't edit the existing baseStatList directly, in order to avoid concurrency exceptions
|
||||
val civInfo = cityInfo.civInfo
|
||||
|
||||
@ -463,10 +469,10 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
science = cityInfo.population.population.toFloat(),
|
||||
production = cityInfo.population.getFreePopulation().toFloat()
|
||||
)
|
||||
newBaseStatList["Tile yields"] = getStatsFromTiles()
|
||||
newBaseStatList["Tile yields"] = statsFromTiles
|
||||
newBaseStatList["Specialists"] = getStatsFromSpecialists(cityInfo.population.getNewSpecialists())
|
||||
newBaseStatList["Trade routes"] = getStatsFromTradeRoute()
|
||||
newBaseStatList["Buildings"] = cityInfo.cityConstructions.getStats()
|
||||
newBaseStatList["Buildings"] = statsFromBuildings
|
||||
newBaseStatList["Policies"] = getStatsFromUniques(civInfo.policies.policyUniques.getAllUniques())
|
||||
newBaseStatList["National ability"] = getStatsFromNationUnique()
|
||||
newBaseStatList["Wonders"] = getStatsFromUniques(civInfo.getCivWideBuildingUniques(cityInfo))
|
||||
@ -503,7 +509,11 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
statPercentBonusList = newStatPercentBonusList
|
||||
}
|
||||
|
||||
fun update(currentConstruction: IConstruction = cityInfo.cityConstructions.getCurrentConstruction()) {
|
||||
/** Does not update tile stats - instead, updating tile stats updates this */
|
||||
fun update(currentConstruction: IConstruction = cityInfo.cityConstructions.getCurrentConstruction(),
|
||||
updateTileStats:Boolean = true) {
|
||||
if (updateTileStats) updateTileStats()
|
||||
|
||||
// We calculate this here for concurrency reasons
|
||||
// If something needs this, we pass this through as a parameter
|
||||
val localBuildingUniques = cityInfo.cityConstructions.builtBuildingUniqueMap.getAllUniques()
|
||||
@ -516,8 +526,10 @@ class CityStats(val cityInfo: CityInfo) {
|
||||
val citySpecificUniques = cityInfo.getAllLocalUniques()
|
||||
|
||||
// We need to compute Tile yields before happiness
|
||||
updateBaseStatList()
|
||||
updateCityHappiness()
|
||||
|
||||
val statsFromBuildings = cityInfo.cityConstructions.getStats() // this is performance heavy, so calculate once
|
||||
updateBaseStatList(statsFromBuildings)
|
||||
updateCityHappiness(statsFromBuildings)
|
||||
updateStatPercentBonusList(currentConstruction, localBuildingUniques)
|
||||
|
||||
updateFinalStatList(currentConstruction, citySpecificUniques) // again, we don't edit the existing currentCityStats directly, in order to avoid concurrency exceptions
|
||||
|
@ -628,7 +628,7 @@ open class TileInfo {
|
||||
if (isCityCenter()) {
|
||||
val city = getCity()!!
|
||||
var cityString = city.name.tr()
|
||||
if (isViewableToPlayer) cityString += " (" + city.health + ")"
|
||||
if (isViewableToPlayer) cityString += " (${city.health})"
|
||||
lineList += FormattedLine(cityString)
|
||||
if (UncivGame.Current.viewEntireMapForDebug || city.civInfo == viewingCiv)
|
||||
lineList += city.cityConstructions.getProductionMarkup(ruleset)
|
||||
|
@ -161,6 +161,8 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
||||
|
||||
val constructionsSequence = city.getRuleset().units.values.asSequence() +
|
||||
city.getRuleset().buildings.values.asSequence()
|
||||
|
||||
city.cityStats.updateTileStats() // only once
|
||||
for (entry in constructionsSequence.filter { it.shouldBeDisplayed(cityConstructions) }) {
|
||||
val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name)
|
||||
var buttonText = entry.name.tr() + cityConstructions.getTurnsToConstructionString(entry.name, useStoredProduction)
|
||||
|
@ -81,7 +81,6 @@ class CityInfoTable(private val cityScreen: CityScreen) : Table(CameraStageBaseS
|
||||
YesNoPopup("Are you sure you want to sell this [${building.name}]?".tr(),
|
||||
{
|
||||
cityScreen.city.sellBuilding(building.name)
|
||||
cityScreen.city.cityStats.update()
|
||||
cityScreen.update()
|
||||
}, cityScreen,
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user