From 4a72213cdedd3b626a745319fa3eaf98a33d2351 Mon Sep 17 00:00:00 2001 From: SeventhM <127357473+SeventhM@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:41:52 -0800 Subject: [PATCH] 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 --- .../logic/automation/unit/WorkerAutomation.kt | 2 +- core/src/com/unciv/logic/map/tile/Tile.kt | 10 +- .../unciv/logic/map/tile/TileStatFunctions.kt | 213 +++++++----------- docs/Modders/Unique-parameters.md | 2 +- 4 files changed, 89 insertions(+), 138 deletions(-) diff --git a/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt b/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt index 6c4f56b944..c6daa9d39b 100644 --- a/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt +++ b/core/src/com/unciv/logic/automation/unit/WorkerAutomation.kt @@ -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 diff --git a/core/src/com/unciv/logic/map/tile/Tile.kt b/core/src/com/unciv/logic/map/tile/Tile.kt index 1d98094e96..cc048147e5 100644 --- a/core/src/com/unciv/logic/map/tile/Tile.kt +++ b/core/src/com/unciv/logic/map/tile/Tile.kt @@ -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 { diff --git a/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt b/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt index 38565a6585..8f5180937b 100644 --- a/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt +++ b/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt @@ -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 { + 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 diff --git a/docs/Modders/Unique-parameters.md b/docs/Modders/Unique-parameters.md index db66ca0eac..e89d671d3a 100644 --- a/docs/Modders/Unique-parameters.md +++ b/docs/Modders/Unique-parameters.md @@ -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