From 68894cdac577f56efbcb509d7fc1d4a30a0de79f Mon Sep 17 00:00:00 2001 From: yairm210 Date: Tue, 5 Aug 2025 08:12:21 +0300 Subject: [PATCH] chore(purity): Replace extra @LocalState with declaring that the classes are well known local state classes --- build.gradle.kts | 36 ++++++++++--------- core/src/com/unciv/logic/MultiFilter.kt | 3 +- .../com/unciv/logic/automation/Automation.kt | 9 ++++- .../com/unciv/logic/battle/BattleDamage.kt | 8 ++--- .../src/com/unciv/logic/city/CityResources.kt | 10 +++--- .../logic/city/GreatPersonPointsBreakdown.kt | 3 -- .../city/managers/CityReligionManager.kt | 3 +- .../unciv/logic/civilization/Civilization.kt | 2 -- .../diplomacy/CityStateFunctions.kt | 2 -- core/src/com/unciv/logic/map/HexMath.kt | 6 ---- core/src/com/unciv/logic/map/TileMap.kt | 2 -- .../com/unciv/logic/map/mapunit/MapUnit.kt | 2 +- core/src/com/unciv/logic/map/tile/Tile.kt | 1 - .../unciv/logic/map/tile/TileStatFunctions.kt | 26 ++++++++++---- core/src/com/unciv/models/Counter.kt | 1 - core/src/com/unciv/models/ruleset/Building.kt | 2 +- .../com/unciv/models/ruleset/IConstruction.kt | 3 +- .../unciv/models/ruleset/tile/TileResource.kt | 3 +- .../com/unciv/models/ruleset/unique/Unique.kt | 5 ++- .../ruleset/unique/UniqueParameterType.kt | 3 +- .../unciv/models/ruleset/unique/UniqueType.kt | 2 +- .../com/unciv/models/ruleset/unit/BaseUnit.kt | 3 +- .../ruleset/validation/UniqueValidator.kt | 2 +- core/src/com/unciv/models/stats/Stats.kt | 1 + .../unciv/models/translations/Translations.kt | 2 +- .../unit/actions/UnitActionModifiers.kt | 3 +- 26 files changed, 71 insertions(+), 72 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2a14e20dc3..722aaf16d6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -38,7 +38,7 @@ plugins { // This is *with* gradle 8.2 downloaded according the project specs, no idea what that's about kotlin("multiplatform") version "1.9.24" kotlin("plugin.serialization") version "1.9.24" - id("io.github.yairm210.purity-plugin") version "0.0.43" apply(false) + id("io.github.yairm210.purity-plugin") version "0.0.45" apply(false) } allprojects { @@ -49,28 +49,32 @@ allprojects { apply(plugin = "io.github.yairm210.purity-plugin") configure{ wellKnownPureFunctions = setOf( - "kotlin.assert", - "kotlin.lazy", - "kotlin.getValue", - "kotlin.error", + "kotlin.lazy", // moved + "kotlin.getValue", // moved + "kotlin.error", // moved ) wellKnownReadonlyFunctions = setOf( "com.badlogic.gdx.math.Vector2.len", "com.badlogic.gdx.math.Vector2.cpy", "com.badlogic.gdx.math.Vector2.hashCode", - "java.lang.reflect.Field.getAnnotation", // not sure if generic enough to be useful globally - "java.lang.Class.getField", - // Looks like the Collection.contains is not considered overridden :thunk: - "kotlin.Array.get", - "kotlin.collections.mutableSetOf", - "kotlin.collections.withIndex", // applicable to sequence as well - "kotlin.collections.intersect", - "kotlin.collections.maxOfOrNull", - "kotlin.collections.minOfOrNull", - "kotlin.reflect.KMutableProperty0.get", // also 1 and 2 ) - wellKnownPureClasses = setOf( + wellKnownPureClasses = setOf( + ) + wellKnownInternalStateClasses = setOf( + // Moved all + "kotlin.collections.MutableList", + "kotlin.collections.MutableSet", + "kotlin.collections.MutableMap", + "kotlin.collections.List", + "kotlin.collections.Set", + "kotlin.collections.Map", + "kotlin.collections.ArrayDequeue", + + "com.unciv.models.stats.Stats", + "com.unciv.models.Counter", + "com.unciv.models.ruleset.tile.ResourceSupplyList", + "com.badlogic.gdx.math.Vector2", ) warnOnPossibleAnnotations = true } diff --git a/core/src/com/unciv/logic/MultiFilter.kt b/core/src/com/unciv/logic/MultiFilter.kt index 86fcfc04cb..ca9d2c62b1 100644 --- a/core/src/com/unciv/logic/MultiFilter.kt +++ b/core/src/com/unciv/logic/MultiFilter.kt @@ -42,8 +42,7 @@ object MultiFilter { fun getAllSingleFilters(input: String): Sequence = when { input.hasSurrounding(andPrefix, andSuffix) && input.contains(andSeparator) -> { // Resolve "AND" filters - @LocalState - val filters = input.removeSurrounding(andPrefix, andSuffix) + @LocalState val filters = input.removeSurrounding(andPrefix, andSuffix) .splitToSequence(andSeparator) filters.flatMap { getAllSingleFilters(it) } } diff --git a/core/src/com/unciv/logic/automation/Automation.kt b/core/src/com/unciv/logic/automation/Automation.kt index 76f8e2e81f..d1d6a30289 100644 --- a/core/src/com/unciv/logic/automation/Automation.kt +++ b/core/src/com/unciv/logic/automation/Automation.kt @@ -183,11 +183,13 @@ object Automation { } @Suppress("MemberVisibilityCanBePrivate") + @Readonly fun providesUnneededCarryingSlots(baseUnit: BaseUnit, civInfo: Civilization): Boolean { // Simplified, will not work for crazy mods with more than one carrying filter for a unit val carryUnique = baseUnit.getMatchingUniques(UniqueType.CarryAirUnits).first() val carryFilter = carryUnique.params[1] + @Readonly fun getCarryAmount(mapUnit: MapUnit): Int { val mapUnitCarryUnique = mapUnit.getMatchingUniques(UniqueType.CarryAirUnits).firstOrNull() ?: return 0 @@ -338,6 +340,7 @@ object Automation { /** Determines whether the AI should be willing to spend strategic resources to build * [construction] for [civInfo], assumes that we are actually able to do so. */ + @Readonly fun allowSpendingResource(civInfo: Civilization, construction: INonPerpetualConstruction, cityInfo: City? = null): Boolean { // City states do whatever they want if (civInfo.isCityState) @@ -418,13 +421,14 @@ object Automation { else -> ThreatLevel.Medium } } - + @Readonly private fun improvementIsRemovable(city: City, tile: Tile): Boolean { val gameContext = GameContext(city.civ, city, tile = tile) return (tile.getTileImprovement()?.hasUnique(UniqueType.AutomatedUnitsWillNotReplace, gameContext) == false && tile.getTileImprovement()?.hasUnique(UniqueType.Irremovable, gameContext) == false) } /** Support [UniqueType.CreatesOneImprovement] unique - find best tile for placement automation */ + @Readonly fun getTileForConstructionImprovement(city: City, improvement: TileImprovement): Tile? { val localUniqueCache = LocalUniqueCache() val civ = city.civ @@ -441,6 +445,7 @@ object Automation { } // Ranks a tile for any purpose except the expansion algorithm of cities + @Readonly internal fun rankTile(tile: Tile?, civInfo: Civilization, localUniqueCache: LocalUniqueCache): Float { if (tile == null) return 0f @@ -460,6 +465,7 @@ object Automation { } // Ranks a tile for the expansion algorithm of cities + @Readonly internal fun rankTileForExpansion(tile: Tile, city: City, localUniqueCache: LocalUniqueCache): Int { // https://github.com/Gedemon/Civ5-DLL/blob/aa29e80751f541ae04858b6d2a2c7dcca454201e/CvGameCoreDLL_Expansion1/CvCity.cpp#L10301 @@ -513,6 +519,7 @@ object Automation { return score } + @Readonly fun rankStatsValue(stats: Stats, civInfo: Civilization): Float { var rank = 0.0f rank += stats.food * 1.2f //food get more value to keep city growing diff --git a/core/src/com/unciv/logic/battle/BattleDamage.kt b/core/src/com/unciv/logic/battle/BattleDamage.kt index 6bc146d5e9..8d2739801e 100644 --- a/core/src/com/unciv/logic/battle/BattleDamage.kt +++ b/core/src/com/unciv/logic/battle/BattleDamage.kt @@ -35,7 +35,7 @@ object BattleDamage { @Readonly private fun getGeneralModifiers(combatant: ICombatant, enemy: ICombatant, combatAction: CombatAction, tileToAttackFrom: Tile): Counter { - @LocalState val modifiers = Counter() + val modifiers = Counter() val conditionalState = getStateForConditionals(combatAction, combatant, enemy) val civInfo = combatant.getCivInfo() @@ -101,7 +101,7 @@ object BattleDamage { private fun getUnitUniqueModifiers(combatant: MapUnitCombatant, enemy: ICombatant, conditionalState: GameContext, tileToAttackFrom: Tile): Counter { val civInfo = combatant.getCivInfo() - @LocalState val modifiers = Counter() + val modifiers = Counter() for (unique in combatant.getMatchingUniques(UniqueType.Strength, conditionalState, true)) { modifiers.add(getModifierStringFromUnique(unique), unique.params[0].toInt()) @@ -179,7 +179,7 @@ object BattleDamage { @Readonly private fun getTerrainAttackModifiers(attacker: MapUnitCombatant, defender: ICombatant, tileToAttackFrom: Tile): Counter { - @LocalState val modifiers = Counter() + val modifiers = Counter() if (attacker.unit.isEmbarked() && defender.getTile().isLand && !attacker.unit.hasUnique(UniqueType.AttackAcrossCoast) ) @@ -219,7 +219,7 @@ object BattleDamage { fun getAirSweepAttackModifiers( attacker: ICombatant ): Counter { - @LocalState val modifiers = Counter() + val modifiers = Counter() if (attacker is MapUnitCombatant) { for (unique in attacker.unit.getMatchingUniques(UniqueType.StrengthWhenAirsweep)) { diff --git a/core/src/com/unciv/logic/city/CityResources.kt b/core/src/com/unciv/logic/city/CityResources.kt index 7831739f6f..7c186d4c7b 100644 --- a/core/src/com/unciv/logic/city/CityResources.kt +++ b/core/src/com/unciv/logic/city/CityResources.kt @@ -39,7 +39,7 @@ object CityResources { @Readonly private fun getResourcesGeneratedByCityNotIncludingBuildings(city: City, resourceModifers: Map): ResourceSupplyList { - @LocalState val cityResources = ResourceSupplyList() + val cityResources = ResourceSupplyList() cityResources.add(getResourcesFromTiles(city, resourceModifers)) cityResources.add(getResourceFromUniqueImprovedTiles(city, resourceModifers)) @@ -79,7 +79,7 @@ object CityResources { @Readonly private fun getResourcesFromTiles(city: City, resourceModifer: Map): ResourceSupplyList { - @LocalState val resourceSupplyList = ResourceSupplyList() + val resourceSupplyList = ResourceSupplyList() for (tileInfo in city.getTiles().filter { it.resource != null }) { val resource = tileInfo.tileResource val amount = getTileResourceAmount(city, tileInfo) * resourceModifer[resource.name]!! @@ -90,7 +90,7 @@ object CityResources { @Readonly private fun getResourceFromUniqueImprovedTiles(city: City, resourceModifer: Map): ResourceSupplyList { - @LocalState val resourceSupplyList = ResourceSupplyList() + val resourceSupplyList = ResourceSupplyList() for (tileInfo in city.getTiles().filter { it.getUnpillagedImprovement() != null }) { val gameContext = GameContext(city.civ, city, tile = tileInfo) val tileImprovement = tileInfo.getUnpillagedTileImprovement() @@ -114,7 +114,7 @@ object CityResources { @Readonly private fun getNegativeCityResourcesRequiredByBuildings(city: City): ResourceSupplyList { - @LocalState val resourceSupplyList = ResourceSupplyList() + val resourceSupplyList = ResourceSupplyList() val freeBuildings = city.civ.civConstructions.getFreeBuildingNames(city) for (building in city.cityConstructions.getBuiltBuildings()) { // Free buildings cost no resources @@ -126,7 +126,7 @@ object CityResources { @Readonly private fun getCityResourcesFromCiv(city: City, resourceModifers: HashMap): ResourceSupplyList { - @LocalState val resourceSupplyList = ResourceSupplyList() + val resourceSupplyList = ResourceSupplyList() // This includes the uniques from buildings, from this and all other cities for (unique in city.getMatchingUniques(UniqueType.ProvidesResources, city.state)) { // E.G "Provides [1] [Iron]" val resource = city.getRuleset().tileResources[unique.params[1]] diff --git a/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt b/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt index 5868d717a9..d4160559f7 100644 --- a/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt +++ b/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt @@ -5,7 +5,6 @@ import com.unciv.models.Counter import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.unique.Unique import com.unciv.models.ruleset.unique.UniqueType -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly /** Manages calculating Great Person Points per City for nextTurn. See public constructor(city) below for details. */ @@ -134,13 +133,11 @@ class GreatPersonPointsBreakdown private constructor(private val ruleset: Rulese @Readonly fun sum(): Counter { // Accumulate base points as fake "fixed-point" - @LocalState val result = Counter() for (entry in basePoints) result.add(entry.counter * fixedPointFactor) // Accumulate percentage bonuses additively not multiplicatively - @LocalState val bonuses = Counter() for (entry in percentBonuses) { bonuses.add(entry.counter) diff --git a/core/src/com/unciv/logic/city/managers/CityReligionManager.kt b/core/src/com/unciv/logic/city/managers/CityReligionManager.kt index e467bfb27d..c411c95c75 100644 --- a/core/src/com/unciv/logic/city/managers/CityReligionManager.kt +++ b/core/src/com/unciv/logic/city/managers/CityReligionManager.kt @@ -10,7 +10,6 @@ import com.unciv.models.Religion import com.unciv.models.ruleset.unique.Unique import com.unciv.models.ruleset.unique.UniqueType import com.unciv.ui.components.extensions.toPercent -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly class CityReligionManager : IsPartOfGameInfoSerialization { @@ -282,7 +281,7 @@ class CityReligionManager : IsPartOfGameInfoSerialization { /** Doesn't update the pressures, only returns what they are if the update were to happen right now */ @Readonly fun getPressuresFromSurroundingCities(): Counter { - @LocalState val addedPressure = Counter() + val addedPressure = Counter() if (city.isHolyCity()) { addedPressure[religionThisIsTheHolyCityOf!!] = 5 * pressureFromAdjacentCities } diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index 8f9954d864..23de2699ed 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -56,7 +56,6 @@ import com.unciv.ui.components.extensions.toPercent import com.unciv.ui.screens.victoryscreen.RankingType import org.jetbrains.annotations.VisibleForTesting import yairm210.purity.annotations.Cache -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly import kotlin.math.max import kotlin.math.min @@ -750,7 +749,6 @@ class Civilization : IsPartOfGameInfoSerialization { @Readonly fun calculateScoreBreakdown(): HashMap { - @LocalState val scoreBreakdown = hashMapOf() // 1276 is the number of tiles in a medium sized map. The original uses 4160 for this, // but they have bigger maps diff --git a/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt b/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt index ce0eaf127a..b37a1fb077 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt @@ -27,7 +27,6 @@ import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.stats.Stat import com.unciv.ui.screens.victoryscreen.RankingType import com.unciv.utils.randomWeighted -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly import kotlin.math.min import kotlin.math.pow @@ -418,7 +417,6 @@ class CityStateFunctions(val civInfo: Civilization) { @Readonly fun getTributeModifiers(demandingCiv: Civilization, demandingWorker: Boolean = false, requireWholeList: Boolean = false): HashMap { - @LocalState val modifiers = LinkedHashMap() // Linked to preserve order when presenting the modifiers table // Can't bully major civs or unsettled CS's if (!civInfo.isCityState) { diff --git a/core/src/com/unciv/logic/map/HexMath.kt b/core/src/com/unciv/logic/map/HexMath.kt index 291fd6ed77..70f6f5794d 100644 --- a/core/src/com/unciv/logic/map/HexMath.kt +++ b/core/src/com/unciv/logic/map/HexMath.kt @@ -128,11 +128,9 @@ object HexMath { @Readonly fun hex2WorldCoords(hexCoord: Vector2): Vector2 { // Distance between cells = 2* normal of triangle = 2* (sqrt(3)/2) = sqrt(3) - @LocalState val xVector = getVectorByClockHour(10) xVector.scl(sqrt(3.0).toFloat() * hexCoord.x) - @LocalState val yVector = getVectorByClockHour(2) yVector.scl(sqrt(3.0).toFloat() * hexCoord.y) @@ -143,10 +141,8 @@ object HexMath { @Readonly fun world2HexCoords(worldCoord: Vector2): Vector2 { // D: diagonal, A: antidiagonal versors - @LocalState val D = getVectorByClockHour(10) D.scl(sqrt(3.0).toFloat()) - @LocalState val A = getVectorByClockHour(2) A.scl(sqrt(3.0).toFloat()) val den = D.x * A.y - D.y * A.x @@ -219,7 +215,6 @@ object HexMath { @Readonly fun getVectorsAtDistance(origin: Vector2, distance: Int, maxDistance: Int, worldWrap: Boolean): List { - @LocalState val vectors = mutableListOf() if (distance == 0) { return listOf(origin.cpy()) @@ -259,7 +254,6 @@ object HexMath { @Readonly fun getVectorsInDistance(origin: Vector2, distance: Int, worldWrap: Boolean): List { - @LocalState val hexesToReturn = mutableListOf() for (i in 0..distance) { hexesToReturn += getVectorsAtDistance(origin, i, distance, worldWrap) diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 5a5b69400e..7f8d44df7e 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -405,7 +405,6 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization { @Readonly fun getViewableTiles(position: Vector2, sightDistance: Int, forAttack: Boolean = false): List { val aUnitHeight = get(position).unitHeight - @LocalState val viewableTiles = mutableListOf(ViewableTile( get(position), aUnitHeight, @@ -416,7 +415,6 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization { for (i in 1..sightDistance+1) { // in each layer, // This is so we don't use tiles in the same distance to "see over", // that is to say, the "viewableTiles.contains(it) check will return false for neighbors from the same distance - @LocalState val tilesToAddInDistanceI = ArrayList() for (cTile in getTilesAtDistance(position, i)) { // for each tile in that layer, diff --git a/core/src/com/unciv/logic/map/mapunit/MapUnit.kt b/core/src/com/unciv/logic/map/mapunit/MapUnit.kt index eccc5d1f60..637e7bf05c 100644 --- a/core/src/com/unciv/logic/map/mapunit/MapUnit.kt +++ b/core/src/com/unciv/logic/map/mapunit/MapUnit.kt @@ -329,7 +329,7 @@ class MapUnit : IsPartOfGameInfoSerialization { * StateForConditionals is assumed to regarding this mapUnit*/ @Readonly fun getResourceRequirementsPerTurn(): Counter { - @LocalState val resourceRequirements = Counter() + val resourceRequirements = Counter() if (baseUnit.requiredResource != null) resourceRequirements[baseUnit.requiredResource!!] = 1 for (unique in getMatchingUniques(UniqueType.ConsumesResources, cache.state)) resourceRequirements.add(unique.params[1], unique.params[0].toInt()) diff --git a/core/src/com/unciv/logic/map/tile/Tile.kt b/core/src/com/unciv/logic/map/tile/Tile.kt index d5cffa96f2..193cc0ed1a 100644 --- a/core/src/com/unciv/logic/map/tile/Tile.kt +++ b/core/src/com/unciv/logic/map/tile/Tile.kt @@ -738,7 +738,6 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { @Readonly fun getRulesetIncompatibility(ruleset: Ruleset): HashSet { - @LocalState val out = HashSet() if (!ruleset.terrains.containsKey(baseTerrain)) out.add("Base terrain [$baseTerrain] does not exist in ruleset!") diff --git a/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt b/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt index 43936e02d9..71dc58d5f5 100644 --- a/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt +++ b/core/src/com/unciv/logic/map/tile/TileStatFunctions.kt @@ -11,8 +11,11 @@ import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.stats.Stat import com.unciv.models.stats.Stats import com.unciv.ui.components.extensions.toPercent +import yairm210.purity.annotations.LocalState +import yairm210.purity.annotations.Readonly import java.util.EnumMap +@Readonly fun List>.toStats(): Stats { val stats = Stats() for ((_, statsToAdd) in this) @@ -28,6 +31,7 @@ class TileStatFunctions(val tile: Tile) { localUniqueCache: LocalUniqueCache = LocalUniqueCache(false) ): Stats = getTileStats(tile.getCity(), observingCiv, localUniqueCache) + @Readonly @Suppress("purity") // requires "for @LocalState X" fun getTileStats( city: City?, observingCiv: Civilization?, localUniqueCache: LocalUniqueCache = LocalUniqueCache(false) @@ -51,21 +55,22 @@ class TileStatFunctions(val tile: Tile) { return statsBreakdown.toStats() } + @Readonly fun getTileStatsBreakdown(city: City?, observingCiv: Civilization?, localUniqueCache: LocalUniqueCache = LocalUniqueCache(false) ): List> { val gameContext = GameContext(civInfo = observingCiv, city = city, tile = tile) - val listOfStats = getTerrainStatsBreakdown(gameContext) + @LocalState val listOfStats = getTerrainStatsBreakdown(gameContext) val otherYieldsIgnored = tile.allTerrains.any { it.hasUnique(UniqueType.NullifyYields, gameContext) } val improvement = if (otherYieldsIgnored) null // Treat it as if there is no improvement else tile.getUnpillagedTileImprovement() - val improvementStats = improvement?.cloneStats() ?: Stats.ZERO // If improvement==null, will never be added to + @LocalState val improvementStats = improvement?.cloneStats() ?: Stats.ZERO // If improvement==null, will never be added to val road = if (otherYieldsIgnored) null else tile.getUnpillagedRoadImprovement() - val roadStats = road?.cloneStats() ?: Stats.ZERO + @LocalState val roadStats = road?.cloneStats() ?: Stats.ZERO if (city != null) { val statsFromTilesUniques = @@ -135,6 +140,7 @@ class TileStatFunctions(val tile: Tile) { } /** Ensures each stat is >= [minimumStats].stat - modifies in place */ + @Readonly private fun missingFromMinimum(current: Stats, minimumStats: Stats): Stats { // Note: Not `for ((stat, value) in other)` - that would skip zero values val missingStats = Stats() @@ -148,6 +154,7 @@ class TileStatFunctions(val tile: Tile) { /** Gets stats of a single Terrain, unifying the Stats class a Terrain inherits and the Stats Unique * @return A Stats reference, must not be mutated */ + @Readonly private fun getSingleTerrainStats(terrain: Terrain, gameContext: GameContext): ArrayList> { val list = arrayListOf(terrain.name to (terrain as Stats)) @@ -158,8 +165,10 @@ class TileStatFunctions(val tile: Tile) { } /** Gets basic stats to start off [getTileStats] or [getTileStartYield], independently mutable result */ + @Readonly fun getTerrainStatsBreakdown(gameContext: GameContext = GameContext()): ArrayList> { - var list = ArrayList>() + // needs to be marked, because it's a var + @LocalState var list = ArrayList>() // allTerrains iterates over base, natural wonder, then features for (terrain in tile.allTerrains) { @@ -183,7 +192,8 @@ class TileStatFunctions(val tile: Tile) { } // Only gets the tile percentage bonus, not the improvement percentage bonus - @Suppress("MemberVisibilityCanBePrivate") + @Suppress("MemberVisibilityCanBePrivate", "purity") + @Readonly fun getTilePercentageStats(observingCiv: Civilization?, city: City?, uniqueCache: LocalUniqueCache): EnumMap { val terrainStats = Stats() val gameContext = GameContext(civInfo = observingCiv, city = city, tile = tile) @@ -274,6 +284,7 @@ class TileStatFunctions(val tile: Tile) { /** Returns the extra stats that we would get if we switched to this improvement * Can be negative if we're switching to a worse improvement */ + @Readonly fun getStatDiffForImprovement( improvement: TileImprovement, observingCiv: Civilization, @@ -285,16 +296,17 @@ class TileStatFunctions(val tile: Tile) { val currentStats = currentTileStats ?: getTileStats(city, observingCiv, cityUniqueCache) - val tileClone = tile.clone(addUnits = false) + @LocalState val tileClone = tile.clone(addUnits = false) tileClone.setTerrainTransients() tileClone.setImprovement(improvement.name) - val futureStats = tileClone.stats.getTileStats(city, observingCiv, cityUniqueCache) + @LocalState val futureStats = tileClone.stats.getTileStats(city, observingCiv, cityUniqueCache) return futureStats.minus(currentStats) } // Also multiplies the stats by the percentage bonus for improvements (but not for tiles) + @Readonly private fun getExtraImprovementStats( improvement: TileImprovement, observingCiv: Civilization, diff --git a/core/src/com/unciv/models/Counter.kt b/core/src/com/unciv/models/Counter.kt index 844caf786b..6eb3edad73 100644 --- a/core/src/com/unciv/models/Counter.kt +++ b/core/src/com/unciv/models/Counter.kt @@ -52,7 +52,6 @@ open class Counter( @Readonly /** Creates a new instance (does not modify) */ operator fun times(amount: Int): Counter { - @LocalState val newCounter = Counter() for (key in keys) newCounter[key] = this[key] * amount return newCounter diff --git a/core/src/com/unciv/models/ruleset/Building.kt b/core/src/com/unciv/models/ruleset/Building.kt index f50fc59de9..8ca3ada723 100644 --- a/core/src/com/unciv/models/ruleset/Building.kt +++ b/core/src/com/unciv/models/ruleset/Building.kt @@ -556,7 +556,7 @@ class Building : RulesetStatsObject(), INonPerpetualConstruction { state ?: GameContext.EmptyState) if (uniques.none() && requiredResource == null) return Counter.ZERO - @LocalState val resourceRequirements = Counter() + val resourceRequirements = Counter() if (requiredResource != null) resourceRequirements[requiredResource!!] = 1 for (unique in uniques) resourceRequirements.add(unique.params[1], unique.params[0].toInt()) diff --git a/core/src/com/unciv/models/ruleset/IConstruction.kt b/core/src/com/unciv/models/ruleset/IConstruction.kt index a566493046..787bc45814 100644 --- a/core/src/com/unciv/models/ruleset/IConstruction.kt +++ b/core/src/com/unciv/models/ruleset/IConstruction.kt @@ -13,7 +13,6 @@ import com.unciv.models.stats.Stat import com.unciv.models.stats.Stat.Companion.statsUsableToBuy import com.unciv.ui.components.extensions.toPercent import com.unciv.ui.components.fonts.Fonts -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Pure import yairm210.purity.annotations.Readonly import kotlin.math.pow @@ -133,7 +132,7 @@ interface INonPerpetualConstruction : IConstruction, INamed, IHasUniques { @Readonly override fun getStockpiledResourceRequirements(state: GameContext): Counter { - @LocalState val counter = Counter() + val counter = Counter() for (unique in getMatchingUniquesNotConflicting(UniqueType.CostsResources, state)){ var amount = unique.params[0].toInt() if (unique.isModifiedByGameSpeed()) amount = (amount * state.gameInfo!!.speed.modifier).toInt() diff --git a/core/src/com/unciv/models/ruleset/tile/TileResource.kt b/core/src/com/unciv/models/ruleset/tile/TileResource.kt index 224b81b512..ed2f56b5d7 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileResource.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileResource.kt @@ -14,7 +14,6 @@ import com.unciv.models.stats.Stats import com.unciv.ui.objectdescriptions.uniquesToCivilopediaTextLines import com.unciv.ui.screens.civilopediascreen.FormattedLine import yairm210.purity.annotations.Cache -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly class TileResource : RulesetStatsObject(), GameResource { @@ -61,7 +60,7 @@ class TileResource : RulesetStatsObject(), GameResource { val ruleset = this.ruleset ?: throw IllegalStateException("No ruleset on TileResource when initializing improvements") - @LocalState val allImprovementsLocal = mutableSetOf() + val allImprovementsLocal = mutableSetOf() if (improvement != null) allImprovementsLocal += improvement!! allImprovementsLocal.addAll(improvedBy) diff --git a/core/src/com/unciv/models/ruleset/unique/Unique.kt b/core/src/com/unciv/models/ruleset/unique/Unique.kt index 5c96075de0..0c9224c9cc 100644 --- a/core/src/com/unciv/models/ruleset/unique/Unique.kt +++ b/core/src/com/unciv/models/ruleset/unique/Unique.kt @@ -11,7 +11,6 @@ import com.unciv.models.translations.getModifiers import com.unciv.models.translations.getPlaceholderParameters import com.unciv.models.translations.getPlaceholderText import com.unciv.models.translations.removeConditionals -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly import kotlin.math.max @@ -181,12 +180,12 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s // note this is only done for the replacement, not the deprecated unique, thus parameters of // conditionals on the deprecated unique are ignored - @LocalState val finalPossibleUniques = ArrayList() + val finalPossibleUniques = ArrayList() for (possibleUnique in possibleUniques) { var resultingUnique = possibleUnique - @LocalState val timesParameterWasSeen = Counter() + val timesParameterWasSeen = Counter() for (parameter in possibleUnique.replace('<', ' ').getPlaceholderParameters()) { val parameterHasSign = parameter.startsWith('-') || parameter.startsWith('+') val parameterUnsigned = if (parameterHasSign) parameter.drop(1) else parameter diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt index 0c2732f03e..c39cd21083 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueParameterType.kt @@ -13,7 +13,6 @@ import com.unciv.models.ruleset.validation.Suppression import com.unciv.models.stats.Stat import com.unciv.models.stats.SubStat import com.unciv.models.translations.TranslationFileWriter -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Pure import yairm210.purity.annotations.Readonly @@ -714,7 +713,7 @@ enum class UniqueParameterType( } @Readonly private fun scanExistingValues(type: UniqueParameterType, ruleset: Ruleset): Set { - @LocalState val result = mutableSetOf() + val result = mutableSetOf() for (unique in ruleset.allUniques()) { val parameterMap = unique.type?.parameterTypeMap ?: continue for ((index, param) in unique.params.withIndex()) { diff --git a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt index 1132a97ed8..824c8d1377 100644 --- a/core/src/com/unciv/models/ruleset/unique/UniqueType.kt +++ b/core/src/com/unciv/models/ruleset/unique/UniqueType.kt @@ -1489,7 +1489,7 @@ enum class UniqueType( * For 95% of cases, auto-matching is fine. */ @Readonly open fun parameterTypeMapInitializer(): ArrayList> { - @LocalState val map = ArrayList>() + val map = ArrayList>() for (placeholder in text.getPlaceholderParameters()) { val matchingParameterTypes = placeholder .split('/') diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index 15999a2736..7a47f6bb53 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -26,7 +26,6 @@ import com.unciv.ui.objectdescriptions.BaseUnitDescriptions import com.unciv.ui.screens.civilopediascreen.FormattedLine import com.unciv.utils.yieldIfNotNull import yairm210.purity.annotations.Cache -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly import kotlin.math.pow @@ -478,7 +477,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction { /** Returns resource requirements from both uniques and requiredResource field */ override fun getResourceRequirementsPerTurn(state: GameContext?): Counter { - @LocalState val resourceRequirements = Counter() + val resourceRequirements = Counter() if (requiredResource != null) resourceRequirements[requiredResource!!] = 1 for (unique in getMatchingUniques(UniqueType.ConsumesResources, state ?: GameContext.EmptyState)) resourceRequirements.add(unique.params[1], unique.params[0].toInt()) diff --git a/core/src/com/unciv/models/ruleset/validation/UniqueValidator.kt b/core/src/com/unciv/models/ruleset/validation/UniqueValidator.kt index 86a044fc48..41e111ef32 100644 --- a/core/src/com/unciv/models/ruleset/validation/UniqueValidator.kt +++ b/core/src/com/unciv/models/ruleset/validation/UniqueValidator.kt @@ -349,7 +349,7 @@ class UniqueValidator(val ruleset: Ruleset) { unique: Unique, ): List { if (unique.type == null) return emptyList() - @LocalState val errorList = ArrayList() + val errorList = ArrayList() for ((index, param) in unique.params.withIndex()) { // Trying to catch the error at #11404 if (unique.type.parameterTypeMap.size != unique.params.size) { diff --git a/core/src/com/unciv/models/stats/Stats.kt b/core/src/com/unciv/models/stats/Stats.kt index 26df9fa92b..aeb52cf8d7 100644 --- a/core/src/com/unciv/models/stats/Stats.kt +++ b/core/src/com/unciv/models/stats/Stats.kt @@ -125,6 +125,7 @@ open class Stats( /** **Non-Mutating function** * @return a new [Stats] instance with the result of multiplying each value of this instance by [number] as a new instance */ + @Readonly operator fun times(number: Float) = Stats( production * number, food * number, diff --git a/core/src/com/unciv/models/translations/Translations.kt b/core/src/com/unciv/models/translations/Translations.kt index 42ba53bcab..6489b0a83b 100644 --- a/core/src/com/unciv/models/translations/Translations.kt +++ b/core/src/com/unciv/models/translations/Translations.kt @@ -483,7 +483,7 @@ fun String.getPlaceholderParameters(): List { val stringToParse = this.removeConditionals() - @LocalState val parameters = ArrayList() + val parameters = ArrayList() var depthOfBraces = 0 var startOfCurrentParameter = -1 stringToParse.indices.forEach { i -> diff --git a/core/src/com/unciv/ui/screens/worldscreen/unit/actions/UnitActionModifiers.kt b/core/src/com/unciv/ui/screens/worldscreen/unit/actions/UnitActionModifiers.kt index ec70599eb1..173a18305e 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/unit/actions/UnitActionModifiers.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/unit/actions/UnitActionModifiers.kt @@ -9,7 +9,6 @@ import com.unciv.models.translations.removeConditionals import com.unciv.models.translations.tr import com.unciv.ui.components.fonts.FontRulesetIcons import com.unciv.ui.components.fonts.Fonts -import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly import kotlin.math.ceil @@ -180,7 +179,7 @@ object UnitActionModifiers { if (maxUsages!=null) effects += "${usagesLeft(unit, actionUnique)}/$maxUsages" if (actionUnique.hasModifier(UniqueType.UnitActionStatsCost)) { - @LocalState val statCost = Stats() + val statCost = Stats() for (conditional in actionUnique.getModifiers(UniqueType.UnitActionStatsCost)) statCost.add(conditional.stats) effects += statCost.toStringOnlyIcons(false)