mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-28 06:16:37 -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()
|
civInfo.initialSetCitiesConnectedToCapitalTransients()
|
||||||
|
|
||||||
// We need to determine the GLOBAL happiness state in order to determine the city stats
|
// 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) {
|
for (cityInfo in civInfo.cities) {
|
||||||
/** We remove constructions from the queue that aren't defined in the ruleset.
|
/** 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!
|
SO, we create an entirely new CityStats and iterate there - problem solve!
|
||||||
*/
|
*/
|
||||||
val cityStats = CityStats(cityInfo)
|
val cityStats = CityStats(cityInfo)
|
||||||
|
cityStats.statsFromTiles = cityInfo.cityStats.statsFromTiles // take as-is
|
||||||
val construction = cityInfo.cityConstructions.getConstruction(constructionName)
|
val construction = cityInfo.cityConstructions.getConstruction(constructionName)
|
||||||
cityStats.update(construction)
|
cityStats.update(construction, false)
|
||||||
cityStatsForConstruction = cityStats.currentCityStats
|
cityStatsForConstruction = cityStats.currentCityStats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ class CityStats(val cityInfo: CityInfo) {
|
|||||||
|
|
||||||
var happinessList = LinkedHashMap<String, Float>()
|
var happinessList = LinkedHashMap<String, Float>()
|
||||||
|
|
||||||
|
var statsFromTiles = Stats()
|
||||||
|
|
||||||
var foodEaten = 0f
|
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
|
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
|
//endregion
|
||||||
//region Pure Functions
|
//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 {
|
private fun getStatsFromTradeRoute(): Stats {
|
||||||
val stats = Stats()
|
val stats = Stats()
|
||||||
@ -389,9 +381,24 @@ class CityStats(val cityInfo: CityInfo) {
|
|||||||
//endregion
|
//endregion
|
||||||
//region State-Changing Methods
|
//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
|
// 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!
|
// in order to determine how much food is produced in a city!
|
||||||
fun updateCityHappiness() {
|
fun updateCityHappiness(statsFromBuildings: Stats) {
|
||||||
val civInfo = cityInfo.civInfo
|
val civInfo = cityInfo.civInfo
|
||||||
val newHappinessList = LinkedHashMap<String, Float>()
|
val newHappinessList = LinkedHashMap<String, Float>()
|
||||||
var unhappinessModifier = civInfo.getDifficulty().unhappinessModifier
|
var unhappinessModifier = civInfo.getDifficulty().unhappinessModifier
|
||||||
@ -439,8 +446,7 @@ class CityStats(val cityInfo: CityInfo) {
|
|||||||
val happinessFromSpecialists = getStatsFromSpecialists(cityInfo.population.getNewSpecialists()).happiness.toInt().toFloat()
|
val happinessFromSpecialists = getStatsFromSpecialists(cityInfo.population.getNewSpecialists()).happiness.toInt().toFloat()
|
||||||
if (happinessFromSpecialists > 0) newHappinessList["Specialists"] = happinessFromSpecialists
|
if (happinessFromSpecialists > 0) newHappinessList["Specialists"] = happinessFromSpecialists
|
||||||
|
|
||||||
val happinessFromBuildings = cityInfo.cityConstructions.getStats().happiness.toInt().toFloat()
|
newHappinessList["Buildings"] = statsFromBuildings.happiness.toInt().toFloat()
|
||||||
newHappinessList["Buildings"] = happinessFromBuildings
|
|
||||||
|
|
||||||
newHappinessList["National ability"] = getStatsFromUniques(cityInfo.civInfo.nation.uniqueObjects.asSequence()).happiness
|
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["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
|
// we don't want to modify the existing happiness list because that leads
|
||||||
// to concurrency problems if we iterate on it while changing
|
// to concurrency problems if we iterate on it while changing
|
||||||
happinessList = newHappinessList
|
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 newBaseStatList = LinkedHashMap<String, Stats>() // we don't edit the existing baseStatList directly, in order to avoid concurrency exceptions
|
||||||
val civInfo = cityInfo.civInfo
|
val civInfo = cityInfo.civInfo
|
||||||
|
|
||||||
@ -463,10 +469,10 @@ class CityStats(val cityInfo: CityInfo) {
|
|||||||
science = cityInfo.population.population.toFloat(),
|
science = cityInfo.population.population.toFloat(),
|
||||||
production = cityInfo.population.getFreePopulation().toFloat()
|
production = cityInfo.population.getFreePopulation().toFloat()
|
||||||
)
|
)
|
||||||
newBaseStatList["Tile yields"] = getStatsFromTiles()
|
newBaseStatList["Tile yields"] = statsFromTiles
|
||||||
newBaseStatList["Specialists"] = getStatsFromSpecialists(cityInfo.population.getNewSpecialists())
|
newBaseStatList["Specialists"] = getStatsFromSpecialists(cityInfo.population.getNewSpecialists())
|
||||||
newBaseStatList["Trade routes"] = getStatsFromTradeRoute()
|
newBaseStatList["Trade routes"] = getStatsFromTradeRoute()
|
||||||
newBaseStatList["Buildings"] = cityInfo.cityConstructions.getStats()
|
newBaseStatList["Buildings"] = statsFromBuildings
|
||||||
newBaseStatList["Policies"] = getStatsFromUniques(civInfo.policies.policyUniques.getAllUniques())
|
newBaseStatList["Policies"] = getStatsFromUniques(civInfo.policies.policyUniques.getAllUniques())
|
||||||
newBaseStatList["National ability"] = getStatsFromNationUnique()
|
newBaseStatList["National ability"] = getStatsFromNationUnique()
|
||||||
newBaseStatList["Wonders"] = getStatsFromUniques(civInfo.getCivWideBuildingUniques(cityInfo))
|
newBaseStatList["Wonders"] = getStatsFromUniques(civInfo.getCivWideBuildingUniques(cityInfo))
|
||||||
@ -503,7 +509,11 @@ class CityStats(val cityInfo: CityInfo) {
|
|||||||
statPercentBonusList = newStatPercentBonusList
|
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
|
// We calculate this here for concurrency reasons
|
||||||
// If something needs this, we pass this through as a parameter
|
// If something needs this, we pass this through as a parameter
|
||||||
val localBuildingUniques = cityInfo.cityConstructions.builtBuildingUniqueMap.getAllUniques()
|
val localBuildingUniques = cityInfo.cityConstructions.builtBuildingUniqueMap.getAllUniques()
|
||||||
@ -516,8 +526,10 @@ class CityStats(val cityInfo: CityInfo) {
|
|||||||
val citySpecificUniques = cityInfo.getAllLocalUniques()
|
val citySpecificUniques = cityInfo.getAllLocalUniques()
|
||||||
|
|
||||||
// We need to compute Tile yields before happiness
|
// 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)
|
updateStatPercentBonusList(currentConstruction, localBuildingUniques)
|
||||||
|
|
||||||
updateFinalStatList(currentConstruction, citySpecificUniques) // again, we don't edit the existing currentCityStats directly, in order to avoid concurrency exceptions
|
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()) {
|
if (isCityCenter()) {
|
||||||
val city = getCity()!!
|
val city = getCity()!!
|
||||||
var cityString = city.name.tr()
|
var cityString = city.name.tr()
|
||||||
if (isViewableToPlayer) cityString += " (" + city.health + ")"
|
if (isViewableToPlayer) cityString += " (${city.health})"
|
||||||
lineList += FormattedLine(cityString)
|
lineList += FormattedLine(cityString)
|
||||||
if (UncivGame.Current.viewEntireMapForDebug || city.civInfo == viewingCiv)
|
if (UncivGame.Current.viewEntireMapForDebug || city.civInfo == viewingCiv)
|
||||||
lineList += city.cityConstructions.getProductionMarkup(ruleset)
|
lineList += city.cityConstructions.getProductionMarkup(ruleset)
|
||||||
|
@ -161,6 +161,8 @@ class CityConstructionsTable(private val cityScreen: CityScreen) {
|
|||||||
|
|
||||||
val constructionsSequence = city.getRuleset().units.values.asSequence() +
|
val constructionsSequence = city.getRuleset().units.values.asSequence() +
|
||||||
city.getRuleset().buildings.values.asSequence()
|
city.getRuleset().buildings.values.asSequence()
|
||||||
|
|
||||||
|
city.cityStats.updateTileStats() // only once
|
||||||
for (entry in constructionsSequence.filter { it.shouldBeDisplayed(cityConstructions) }) {
|
for (entry in constructionsSequence.filter { it.shouldBeDisplayed(cityConstructions) }) {
|
||||||
val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name)
|
val useStoredProduction = entry is Building || !cityConstructions.isBeingConstructedOrEnqueued(entry.name)
|
||||||
var buttonText = entry.name.tr() + cityConstructions.getTurnsToConstructionString(entry.name, useStoredProduction)
|
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(),
|
YesNoPopup("Are you sure you want to sell this [${building.name}]?".tr(),
|
||||||
{
|
{
|
||||||
cityScreen.city.sellBuilding(building.name)
|
cityScreen.city.sellBuilding(building.name)
|
||||||
cityScreen.city.cityStats.update()
|
|
||||||
cityScreen.update()
|
cityScreen.update()
|
||||||
}, cityScreen,
|
}, cityScreen,
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user