Fix problems with stats from tiles and improvements (#10980)

* Fix problems with stats from tiles and improvements

* Hold on, we can get the ruleset from the tile. And I was about to complain about that

* Update docs

* Funky null fix

* if else chain doesn't make sense for percent changes on second thought

* Incorporate suggestions

* Use getUnpillagedImprovement functions in matchesFilter

* We don't actually need the whole improvement here, just the name

* Move Golden Age to after all other stats

* Simplify percent stat adds
This commit is contained in:
SeventhM 2024-01-24 13:41:52 -08:00 committed by GitHub
parent 8d7e1a2a42
commit 4a72213cde
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 138 deletions

View File

@ -576,7 +576,7 @@ class WorkerAutomation(
var repairBonusPriority = tile.getImprovementToRepair()!!.getTurnsToBuild(unit.civ,unit) - UnitActionsFromUniques.getRepairTurns(unit)
if (tile.improvementInProgress == Constants.repair) repairBonusPriority += UnitActionsFromUniques.getRepairTurns(unit) - tile.turnsToImprovement
val repairPriority = repairBonusPriority + Automation.rankStatsValue(TileStatFunctions(tile).getImprovementStats(tile.getTileImprovement()!!,unit.civ, tile.owningCity), unit.civ)
val repairPriority = repairBonusPriority + Automation.rankStatsValue(TileStatFunctions(tile).getStatDiffForImprovement(tile.getTileImprovement()!!, unit.civ, tile.owningCity), unit.civ)
if (repairPriority > rank.improvementPriority!!) {
rank.improvementPriority = repairPriority
rank.bestImprovement = null

View File

@ -475,11 +475,17 @@ open class Tile : IsPartOfGameInfoSerialization {
}
/** Implements [UniqueParameterType.TileFilter][com.unciv.models.ruleset.unique.UniqueParameterType.TileFilter] */
fun matchesFilter(filter: String, civInfo: Civilization? = null): Boolean {
fun matchesFilter(filter: String, civInfo: Civilization? = null, ignoreImprovement: Boolean = false): Boolean {
return MultiFilter.multiFilter(filter, { matchesSingleFilter(it, civInfo, ignoreImprovement) })
}
fun matchesSingleFilter(filter: String, civInfo: Civilization? = null, ignoreImprovement: Boolean = false): Boolean {
if (matchesTerrainFilter(filter, civInfo)) return true
if ((improvement == null || improvementIsPillaged) && filter == "unimproved") return true
if (improvement != null && !improvementIsPillaged && filter == "improved") return true
return improvement != null && !improvementIsPillaged && ruleset.tileImprovements[improvement]!!.matchesFilter(filter)
if (ignoreImprovement) return false
if (getUnpillagedTileImprovement()?.matchesFilter(filter) == true) return true
return getUnpillagedRoadImprovement()?.matchesFilter(filter) == true
}
fun matchesTerrainFilter(filter: String, observingCiv: Civilization? = null): Boolean {

View File

@ -27,16 +27,27 @@ class TileStatFunctions(val tile: Tile) {
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
): Stats = getTileStats(tile.getCity(), observingCiv, localUniqueCache)
fun getTileStats(city: City?, observingCiv: Civilization?,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
fun getTileStats(
city: City?, observingCiv: Civilization?,
localUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
): Stats {
val stats = getTileStatsBreakdown(city, observingCiv, localUniqueCache).toStats()
val statsBreakdown = getTileStatsBreakdown(city, observingCiv, localUniqueCache)
for ((stat, value) in getTilePercentageStats(observingCiv, city, localUniqueCache)) {
stats[stat] *= value.toPercent()
val improvement = tile.getUnpillagedImprovement()
val road = tile.getUnpillagedRoad()
val percentageStats = getTilePercentageStats(observingCiv, city, localUniqueCache)
for (stats in statsBreakdown) {
val tileType = when(stats.first) {
improvement -> "Improvement"
road.name -> "Road"
else -> "Terrain"
}
for ((stat, value) in percentageStats[tileType]!!)
stats.second[stat] *= value.toPercent()
}
return stats
return statsBreakdown.toStats()
}
fun getTileStatsBreakdown(city: City?, observingCiv: Civilization?,
@ -45,6 +56,12 @@ class TileStatFunctions(val tile: Tile) {
val stateForConditionals = StateForConditionals(civInfo = observingCiv, city = city, tile = tile)
val listOfStats = getTerrainStatsBreakdown(stateForConditionals)
val improvement = tile.getUnpillagedTileImprovement()
val improvementStats = improvement?.cloneStats() ?: Stats()
val road = tile.getUnpillagedRoadImprovement()
val roadStats = road?.cloneStats() ?: Stats()
if (city != null) {
val statsFromTilesUniques =
localUniqueCache.forCityGetMatchingUniques(city, UniqueType.StatsFromTiles, stateForConditionals)
@ -53,20 +70,18 @@ class TileStatFunctions(val tile: Tile) {
val statsFromObjectsUniques = localUniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatsFromObject, stateForConditionals)
for (unique in statsFromTilesUniques + statsFromObjectsUniques) {
val tileType = unique.params[1]
if (!tile.matchesTerrainFilter(tileType, observingCiv)) continue
listOfStats.add("{${unique.sourceObjectName}} ({${unique.text}})" to unique.stats)
}
val statsFromTilesWithoutUniques = localUniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatsFromTilesWithout, stateForConditionals)
.filter { city.matchesFilter(it.params[3]) && !tile.matchesFilter(it.params[2]) }
for (unique in localUniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatsFromTilesWithout, stateForConditionals)) {
if (
tile.matchesTerrainFilter(unique.params[1]) &&
!tile.matchesTerrainFilter(unique.params[2]) &&
city.matchesFilter(unique.params[3])
)
for (unique in statsFromTilesUniques + statsFromObjectsUniques + statsFromTilesWithoutUniques) {
val tileType = unique.params[1]
if (tile.matchesFilter(tileType, observingCiv, true))
listOfStats.add("{${unique.sourceObjectName}} ({${unique.text}})" to unique.stats)
else if (improvement != null && improvement.matchesFilter(tileType))
improvementStats.add(unique.stats)
else if (road != null && road.matchesFilter(tileType))
roadStats.add(unique.stats)
}
}
@ -86,12 +101,11 @@ class TileStatFunctions(val tile: Tile) {
// resource base
if (tile.hasViewableResource(observingCiv)) listOfStats.add(tile.tileResource.name to tile.tileResource)
val improvement = tile.getUnpillagedTileImprovement()
if (improvement != null)
listOfStats.add(improvement.name to getImprovementStats(improvement, observingCiv, city, localUniqueCache))
improvementStats.add(getExtraImprovementStats(improvement, observingCiv, city))
if (listOfStats.toStats().gold != 0f && observingCiv.goldenAges.isGoldenAge())
listOfStats.add("Golden Age" to Stats(gold = 1f))
if (road != null)
roadStats.add(getExtraImprovementStats(road, observingCiv, city))
if (improvement != null) {
val ensureMinUnique = improvement
@ -101,9 +115,16 @@ class TileStatFunctions(val tile: Tile) {
}
}
if (road != null) listOfStats.add(road.name to roadStats)
if (improvement != null) listOfStats.add(improvement.name to improvementStats)
val statsFromMinimum = missingFromMinimum(listOfStats.toStats(), minimumStats)
listOfStats.add("Minimum" to statsFromMinimum)
if (observingCiv != null &&
listOfStats.toStats().gold != 0f && observingCiv.goldenAges.isGoldenAge())
listOfStats.add("Golden Age" to Stats(gold = 1f))
return listOfStats.filter { !it.second.isEmpty() }
}
@ -150,51 +171,59 @@ class TileStatFunctions(val tile: Tile) {
// Only gets the tile percentage bonus, not the improvement percentage bonus
@Suppress("MemberVisibilityCanBePrivate")
fun getTilePercentageStats(observingCiv: Civilization?, city: City?, uniqueCache: LocalUniqueCache): Stats {
val stats = Stats()
fun getTilePercentageStats(observingCiv: Civilization?, city: City?, uniqueCache: LocalUniqueCache): HashMap<String, Stats> {
val terrainStats = Stats()
val stateForConditionals = StateForConditionals(civInfo = observingCiv, city = city, tile = tile)
val improvement = tile.getUnpillagedTileImprovement()
val improvementStats = Stats()
val road = tile.getUnpillagedRoadImprovement()
val roadStats = Stats()
fun addStats(filter: String, stat: Stat, amount: Float) {
if (tile.matchesFilter(filter, observingCiv, true))
terrainStats.add(stat, amount)
else if (improvement != null && improvement.matchesFilter(filter))
improvementStats.add(stat, amount)
else if (road != null && road.matchesFilter(filter))
roadStats.add(stat, amount)
}
if (city != null) {
val cachedStatPercentFromObjectCityUniques = uniqueCache.forCityGetMatchingUniques(
city, UniqueType.StatPercentFromObject, stateForConditionals)
for (unique in cachedStatPercentFromObjectCityUniques) {
val tileFilter = unique.params[2]
if (tile.matchesTerrainFilter(tileFilter, observingCiv))
stats[Stat.valueOf(unique.params[1])] += unique.params[0].toFloat()
addStats(unique.params[2], Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
}
val cachedAllStatPercentFromObjectCityUniques = uniqueCache.forCityGetMatchingUniques(
city, UniqueType.AllStatsPercentFromObject, stateForConditionals)
for (unique in cachedAllStatPercentFromObjectCityUniques) {
val tileFilter = unique.params[1]
if (!tile.matchesTerrainFilter(tileFilter, observingCiv)) continue
val statPercentage = unique.params[0].toFloat()
for (stat in Stat.values())
stats[stat] += statPercentage
addStats(unique.params[1], stat, unique.params[0].toFloat())
}
} else if (observingCiv != null) {
val cachedStatPercentFromObjectCivUniques = uniqueCache.forCivGetMatchingUniques(
observingCiv, UniqueType.StatPercentFromObject, stateForConditionals)
for (unique in cachedStatPercentFromObjectCivUniques) {
val tileFilter = unique.params[2]
if (tile.matchesTerrainFilter(tileFilter, observingCiv))
stats[Stat.valueOf(unique.params[1])] += unique.params[0].toFloat()
addStats(unique.params[2], Stat.valueOf(unique.params[1]), unique.params[0].toFloat())
}
val cachedAllStatPercentFromObjectCivUniques = uniqueCache.forCivGetMatchingUniques(
observingCiv, UniqueType.AllStatsPercentFromObject, stateForConditionals)
for (unique in cachedAllStatPercentFromObjectCivUniques) {
val tileFilter = unique.params[1]
if (!tile.matchesTerrainFilter(tileFilter, observingCiv)) continue
val statPercentage = unique.params[0].toFloat()
for (stat in Stat.values())
stats[stat] += statPercentage
addStats(unique.params[1], stat, unique.params[0].toFloat())
}
}
return stats
return hashMapOf(
Pair("Terrain", terrainStats),
Pair("Improvement",improvementStats),
Pair("Road", roadStats),
)
}
fun getTileStartScore(cityCenterMinStats: Stats): Float {
@ -249,13 +278,13 @@ class TileStatFunctions(val tile: Tile) {
}
// Also multiplies the stats by the percentage bonus for improvements (but not for tiles)
fun getImprovementStats(
private fun getExtraImprovementStats(
improvement: TileImprovement,
observingCiv: Civilization,
city: City?,
cityUniqueCache: LocalUniqueCache = LocalUniqueCache(false)
city: City?
): Stats {
val stats = improvement.cloneStats()
val stats = Stats()
if (tile.hasViewableResource(observingCiv) && tile.tileResource.isImprovedBy(improvement.name)
&& tile.tileResource.improvementStats != null
)
@ -275,96 +304,12 @@ class TileStatFunctions(val tile: Tile) {
stats.add(unique.stats.times(numberOfBonuses.toFloat()))
}
if (city != null) stats.add(getImprovementStatsForCity(improvement, city, conditionalState, cityUniqueCache))
for ((stat, value) in getImprovementPercentageStats(improvement, observingCiv, city, cityUniqueCache)) {
stats[stat] *= value.toPercent()
}
return stats
}
private fun getImprovementStatsForCity(
improvement: TileImprovement,
city: City,
conditionalState: StateForConditionals,
uniqueCache: LocalUniqueCache
): Stats {
val stats = Stats()
fun statsFromTiles() {
for (unique in improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile, conditionalState)) {
if (tile.matchesFilter(unique.params[1])
|| unique.params[1] == Constants.freshWater && tile.isAdjacentTo(Constants.freshWater)
|| unique.params[1] == "non-fresh water" && !tile.isAdjacentTo(Constants.freshWater)
)
stats.add(unique.stats)
}
}
statsFromTiles()
fun statsFromObject() {
val uniques = uniqueCache.forCityGetMatchingUniques(city,
UniqueType.StatsFromObject, conditionalState) +
uniqueCache.forCityGetMatchingUniques(city,
UniqueType.StatsFromTiles, conditionalState).filter { city.matchesFilter(it.params[2]) }
for (unique in uniques) {
if (improvement.matchesFilter(unique.params[1])) {
stats.add(unique.stats)
}
}
}
statsFromObject()
return stats
}
@Suppress("MemberVisibilityCanBePrivate")
fun getImprovementPercentageStats(
improvement: TileImprovement,
observingCiv: Civilization,
city: City?,
cityUniqueCache: LocalUniqueCache
): Stats {
val stats = Stats()
val conditionalState = StateForConditionals(civInfo = observingCiv, city = city, tile = tile)
if (city != null) {
val allStatPercentUniques = cityUniqueCache.forCityGetMatchingUniques(
city,
UniqueType.AllStatsPercentFromObject,
conditionalState
)
for (unique in allStatPercentUniques) {
if (!improvement.matchesFilter(unique.params[1])) continue
for (stat in Stat.values()) {
stats[stat] += unique.params[0].toFloat()
}
}
val statPercentUniques = cityUniqueCache.forCityGetMatchingUniques(
city,
UniqueType.StatPercentFromObject,
conditionalState
)
for (unique in statPercentUniques) {
if (!improvement.matchesFilter(unique.params[2])) continue
val stat = Stat.valueOf(unique.params[1])
stats[stat] += unique.params[0].toFloat()
}
} else {
for (unique in observingCiv.getMatchingUniques(UniqueType.AllStatsPercentFromObject, conditionalState)) {
if (!improvement.matchesFilter(unique.params[1])) continue
for (stat in Stat.values()) {
stats[stat] += unique.params[0].toFloat()
}
}
for (unique in observingCiv.getMatchingUniques(UniqueType.StatPercentFromObject, conditionalState)) {
if (!improvement.matchesFilter(unique.params[2])) continue
val stat = Stat.valueOf(unique.params[1])
stats[stat] += unique.params[0].toFloat()
}
for (unique in improvement.getMatchingUniques(UniqueType.ImprovementStatsOnTile, conditionalState)) {
if (tile.matchesFilter(unique.params[1])
|| unique.params[1] == Constants.freshWater && tile.isAdjacentTo(Constants.freshWater)
|| unique.params[1] == "non-fresh water" && !tile.isAdjacentTo(Constants.freshWater)
)
stats.add(unique.stats)
}
return stats

View File

@ -134,7 +134,7 @@ For filtering a specific improvement.
Allowed values are:
- improvement name (Note that "Road" and "Railroad" _do_ work as improvementFilters, but not as tileFilters at the moment.)
- improvement name
- `All`
- `Great Improvements`, `Great`
- `All Road` - for Roads & Railroads