diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 7bae497e40..6c08e1ad63 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -240,6 +240,7 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion /** Get barbarian civ * @throws NoSuchElementException in no-barbarians games! */ fun getBarbarianCivilization() = getCivilization(Constants.barbarians) + @Readonly @Suppress("purity") // This should be autorecognized!! fun getDifficulty() = difficultyObject /** Access a cached `GlobalUniques` that combines the [ruleset]'s [globalUniques][Ruleset.globalUniques] * with the Uniques of the chosen [speed] and [difficulty][getDifficulty] */ diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index ead7889369..1d9449d688 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -324,10 +324,8 @@ class Civilization : IsPartOfGameInfoSerialization { if (!knows(civInfo)) diplomacyFunctions.makeCivilizationsMeet(civInfo) return getDiplomacyManager(civInfo.civName)!! } - @Readonly - fun getDiplomacyManager(civInfo: Civilization): DiplomacyManager? = getDiplomacyManager(civInfo.civName) - @Readonly - fun getDiplomacyManager(civName: String): DiplomacyManager? = diplomacy[civName] + @Readonly fun getDiplomacyManager(civInfo: Civilization): DiplomacyManager? = getDiplomacyManager(civInfo.civName) + @Readonly fun getDiplomacyManager(civName: String): DiplomacyManager? = diplomacy[civName] fun getProximity(civInfo: Civilization) = getProximity(civInfo.civName) @Suppress("MemberVisibilityCanBePrivate") // same visibility for overloads @@ -347,17 +345,15 @@ class Civilization : IsPartOfGameInfoSerialization { fun getKnownCivsWithSpectators() = diplomacy.values.asSequence().map { it.otherCiv() } .filter { !it.isDefeated() } - @Readonly - fun knows(otherCivName: String) = diplomacy.containsKey(otherCivName) - @Readonly - fun knows(otherCiv: Civilization) = knows(otherCiv.civName) + + @Readonly fun knows(otherCivName: String) = diplomacy.containsKey(otherCivName) + @Readonly fun knows(otherCiv: Civilization) = knows(otherCiv.civName) @Readonly fun getCapital(firstCityIfNoCapital: Boolean = true) = cities.firstOrNull { it.isCapital() } ?: if (firstCityIfNoCapital) cities.firstOrNull() else null - @Readonly - fun isHuman() = playerType == PlayerType.Human - @Readonly - fun isAI() = playerType == PlayerType.AI + + @Readonly fun isHuman() = playerType == PlayerType.Human + @Readonly fun isAI() = playerType == PlayerType.AI @Readonly fun isAIOrAutoPlaying(): Boolean { if (playerType == PlayerType.AI) return true @@ -365,14 +361,11 @@ class Civilization : IsPartOfGameInfoSerialization { val worldScreen = UncivGame.Current.worldScreen ?: return false return worldScreen.viewingCiv == this && worldScreen.autoPlay.isAutoPlaying() } - @Readonly - fun isOneCityChallenger() = playerType == PlayerType.Human && gameInfo.gameParameters.oneCityChallenge - @Readonly - fun isCurrentPlayer() = gameInfo.currentPlayerCiv == this - @Readonly - fun isMajorCiv() = nation.isMajorCiv - @Readonly - fun isMinorCiv() = nation.isCityState || nation.isBarbarian + + @Readonly fun isOneCityChallenger() = playerType == PlayerType.Human && gameInfo.gameParameters.oneCityChallenge + @Readonly fun isCurrentPlayer() = gameInfo.currentPlayerCiv == this + @Readonly fun isMajorCiv() = nation.isMajorCiv + @Readonly fun isMinorCiv() = nation.isCityState || nation.isBarbarian @delegate:Transient val isCityState by lazy { nation.isCityState } @@ -380,10 +373,9 @@ class Civilization : IsPartOfGameInfoSerialization { @delegate:Transient val isBarbarian by lazy { nation.isBarbarian } - @Readonly - fun isSpectator() = nation.isSpectator - @Readonly - fun isAlive(): Boolean = !isDefeated() + + @Readonly fun isSpectator() = nation.isSpectator + @Readonly fun isAlive(): Boolean = !isDefeated() @delegate:Transient val cityStateType: CityStateType by lazy { gameInfo.ruleset.cityStateTypes[nation.cityStateType!!]!! } @@ -407,13 +399,6 @@ class Civilization : IsPartOfGameInfoSerialization { } - @Suppress("MemberVisibilityCanBePrivate") - fun getPreferredVictoryTypeObjects(): List { - val preferredVictoryTypes = getPreferredVictoryTypes() - return if (preferredVictoryTypes.contains(Constants.neutralVictoryType)) emptyList() - else preferredVictoryTypes.map { gameInfo.ruleset.victories[it]!! } - } - @Readonly fun getPersonality(): Personality { return if (isAIOrAutoPlaying()) gameInfo.ruleset.personalities[nation.personality] ?: Personality.neutralPersonality @@ -1081,6 +1066,7 @@ class Civilization : IsPartOfGameInfoSerialization { fun asPreview() = CivilizationInfoPreview(this) + @Readonly fun getLastSeenImprovement(position: Vector2): String? { if (isAI() || isSpectator()) return null return lastSeenImprovement[position] diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt index 358250faed..9bab07e909 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt @@ -147,6 +147,7 @@ class DiplomacyFunctions(val civInfo: Civilization) { * Use [UnitMovement.canPassThrough] to check whether a specific unit can pass through * a specific tile. */ + @Readonly fun canPassThroughTiles(otherCiv: Civilization): Boolean { if (otherCiv == civInfo) return true if (otherCiv.isBarbarian) return true diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 09a7e3fa59..ddddaa6b61 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -18,10 +18,11 @@ import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.utils.addToMapOfSets import com.unciv.utils.contains +import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly -import java.lang.Integer.max import java.util.concurrent.ConcurrentHashMap import kotlin.math.abs +import kotlin.math.max /** An Unciv map with all properties as produced by the [map editor][com.unciv.ui.screens.mapeditorscreen.MapEditorScreen] * or [MapGenerator][com.unciv.logic.map.mapgenerator.MapGenerator]; or as part of a running [game][GameInfo]. @@ -400,8 +401,10 @@ class TileMap(initialCapacity: Int = 10) : IsPartOfGameInfoSerialization { data class ViewableTile(val tile: Tile, val maxHeightSeenToTile: Int, val isVisible: Boolean, val isAttackable: Boolean) /** @return List of tiles visible from location [position] for a unit with sight range [sightDistance] */ + @Readonly fun getViewableTiles(position: Vector2, sightDistance: Int, forAttack: Boolean = false): List { val aUnitHeight = get(position).unitHeight + @LocalState val viewableTiles = mutableListOf(ViewableTile( get(position), aUnitHeight, @@ -412,6 +415,7 @@ 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 870b55de54..f94397e038 100644 --- a/core/src/com/unciv/logic/map/mapunit/MapUnit.kt +++ b/core/src/com/unciv/logic/map/mapunit/MapUnit.kt @@ -26,6 +26,7 @@ import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.models.ruleset.unit.UnitType import com.unciv.models.translations.tr import com.unciv.ui.components.UnitMovementMemoryType +import yairm210.purity.annotations.Readonly import java.text.DecimalFormat import kotlin.math.pow import kotlin.math.ulp @@ -230,6 +231,7 @@ class MapUnit : IsPartOfGameInfoSerialization { fun getMovementString(): String = (DecimalFormat("0.#").format(currentMovement.toDouble()) + "/" + getMaxMovement()).tr() + @Readonly @Suppress("purity") // should be autorecognized fun getTile(): Tile = currentTile fun getClosestCity(): City? = civ.cities.minByOrNull { @@ -284,6 +286,7 @@ class MapUnit : IsPartOfGameInfoSerialization { fun getUniques(): Sequence = tempUniquesMap.getAllUniques() + @Readonly fun getMatchingUniques( uniqueType: UniqueType, gameContext: GameContext = cache.state, @@ -296,6 +299,7 @@ class MapUnit : IsPartOfGameInfoSerialization { yieldAll(civ.getMatchingUniques(uniqueType, gameContext)) } + @Readonly fun hasUnique( uniqueType: UniqueType, gameContext: GameContext = cache.state, @@ -405,12 +409,14 @@ class MapUnit : IsPartOfGameInfoSerialization { return getRange() * 2 } + @Readonly fun isEmbarked(): Boolean { if (!baseUnit.isLandUnit) return false if (cache.canMoveOnWater) return false return currentTile.isWater } + @Readonly fun isInvisible(to: Civilization): Boolean { if (hasUnique(UniqueType.Invisible) && !to.isSpectator()) return true diff --git a/core/src/com/unciv/logic/map/tile/Tile.kt b/core/src/com/unciv/logic/map/tile/Tile.kt index d7091fe683..0603f22ea1 100644 --- a/core/src/com/unciv/logic/map/tile/Tile.kt +++ b/core/src/com/unciv/logic/map/tile/Tile.kt @@ -34,6 +34,7 @@ import com.unciv.utils.DebugUtils import com.unciv.utils.Log import com.unciv.utils.withItem import com.unciv.utils.withoutItem +import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly import kotlin.collections.ArrayList import kotlin.collections.HashSet @@ -241,9 +242,10 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { //region pure functions - fun isHill() = baseTerrain == Constants.hill || terrainFeatures.contains(Constants.hill) + @Readonly fun isHill() = baseTerrain == Constants.hill || terrainFeatures.contains(Constants.hill) /** Returns military, civilian and air units in tile */ + @Readonly fun getUnits() = sequence { if (militaryUnit != null) yield(militaryUnit!!) if (civilianUnit != null) yield(civilianUnit!!) @@ -251,6 +253,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { } /** This is for performance reasons of canPassThrough() - faster than getUnits().firstOrNull() */ + @Readonly fun getFirstUnit(): MapUnit? { if (militaryUnit != null) return militaryUnit!! if (civilianUnit != null) return civilianUnit!! @@ -258,41 +261,39 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return null } - @Readonly + @Readonly @Suppress("purity") // should be autorecognized as readonly fun getCity(): City? = owningCity - internal fun getNaturalWonder(): Terrain = + @Readonly internal fun getNaturalWonder(): Terrain = if (naturalWonder == null) throw Exception("No natural wonder exists for this tile!") else ruleset.terrains[naturalWonder!!]!! + @Readonly fun isVisible(player: Civilization): Boolean { if (DebugUtils.VISIBLE_MAP) return true return player.viewableTiles.contains(this) } + @Readonly fun isExplored(player: Civilization): Boolean { if (DebugUtils.VISIBLE_MAP || player.civName == Constants.spectator) return true return exploredBy.contains(player.civName) } + @Readonly @Suppress("purity") // should be autorecognized as readonly fun isCityCenter(): Boolean = isCityCenterInternal - fun isNaturalWonder(): Boolean = naturalWonder != null - fun isImpassible() = lastTerrain.impassable + @Readonly fun isNaturalWonder(): Boolean = naturalWonder != null + @Readonly fun isImpassible() = lastTerrain.impassable - fun hasImprovementInProgress() = improvementQueue.isNotEmpty() - - @Readonly - fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!] - @Readonly - fun isPillaged(): Boolean = improvementIsPillaged || roadIsPillaged - @Readonly - fun getUnpillagedTileImprovement(): TileImprovement? = if (getUnpillagedImprovement() == null) null else ruleset.tileImprovements[improvement!!] - @Readonly - fun getTileImprovementInProgress(): TileImprovement? = improvementQueue.firstOrNull()?.let { ruleset.tileImprovements[it.improvement] } - @Readonly - fun containsGreatImprovement() = getTileImprovement()?.isGreatImprovement() == true + @Readonly fun hasImprovementInProgress() = improvementQueue.isNotEmpty() + + @Readonly fun getTileImprovement(): TileImprovement? = if (improvement == null) null else ruleset.tileImprovements[improvement!!] + @Readonly fun isPillaged(): Boolean = improvementIsPillaged || roadIsPillaged + @Readonly fun getUnpillagedTileImprovement(): TileImprovement? = if (getUnpillagedImprovement() == null) null else ruleset.tileImprovements[improvement!!] + @Readonly fun getTileImprovementInProgress(): TileImprovement? = improvementQueue.firstOrNull()?.let { ruleset.tileImprovements[it.improvement] } + @Readonly fun containsGreatImprovement() = getTileImprovement()?.isGreatImprovement() == true @Readonly fun getImprovementToPillage(): TileImprovement? { @@ -319,10 +320,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return ruleset.tileImprovements[roadStatus.name]!! return null } - @Readonly - fun canPillageTile(): Boolean { - return canPillageTileImprovement() || canPillageRoad() - } + @Readonly fun canPillageTile(): Boolean = canPillageTileImprovement() || canPillageRoad() @Readonly fun canPillageTileImprovement(): Boolean { return improvement != null && !improvementIsPillaged @@ -335,12 +333,10 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { && !ruleset.tileImprovements[roadStatus.name]!!.hasUnique(UniqueType.Unpillagable) && !ruleset.tileImprovements[roadStatus.name]!!.hasUnique(UniqueType.Irremovable) } - @Readonly - fun getUnpillagedImprovement(): String? = if (improvementIsPillaged) null else improvement + @Readonly fun getUnpillagedImprovement(): String? = if (improvementIsPillaged) null else improvement /** @return [RoadStatus] on this [Tile], pillaged road counts as [RoadStatus.None] */ - @Readonly - fun getUnpillagedRoad(): RoadStatus = if (roadIsPillaged) RoadStatus.None else roadStatus + @Readonly fun getUnpillagedRoad(): RoadStatus = if (roadIsPillaged) RoadStatus.None else roadStatus @Readonly fun getUnpillagedRoadImprovement(): TileImprovement? { @@ -354,12 +350,13 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { * @param viewingCiv `null` means civ-agnostic and thus always showing the actual improvement * @return The improvement name, or `null` if no improvement should be shown */ + @Readonly fun getShownImprovement(viewingCiv: Civilization?): String? = if (viewingCiv == null || viewingCiv.playerType == PlayerType.AI || viewingCiv.isSpectator()) improvement else viewingCiv.getLastSeenImprovement(position) /** Returns true if this tile has fallout or an equivalent terrain feature */ - fun hasFalloutEquivalent(): Boolean = terrainFeatures.any { ruleset.terrains[it]!!.hasUnique(UniqueType.NullifyYields)} + @Readonly fun hasFalloutEquivalent(): Boolean = terrainFeatures.any { ruleset.terrains[it]!!.hasUnique(UniqueType.NullifyYields)} fun getRow() = HexMath.getRow(position) @@ -367,9 +364,9 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { fun getBaseTerrain(): Terrain = baseTerrainObject - @Readonly - fun getOwner(): Civilization? = getCity()?.civ + @Readonly fun getOwner(): Civilization? = getCity()?.civ + @Readonly fun getRoadOwner(): Civilization? { return if (roadOwner != "") tileMap.gameInfo.getCivilization(roadOwner) @@ -394,19 +391,22 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return civInfo.isAtWarWith(tileOwner) } - fun isRoughTerrain() = allTerrains.any { it.isRough() } + @Readonly fun isRoughTerrain() = allTerrains.any { it.isRough() } @Transient internal var stateThisTile: GameContext = GameContext.EmptyState /** Checks whether any of the TERRAINS of this tile has a certain unique */ + @Readonly fun terrainHasUnique(uniqueType: UniqueType, state: GameContext = stateThisTile) = cachedTerrainData.uniques.hasMatchingUnique(uniqueType, state) /** Get all uniques of this type that any TERRAIN on this tile has */ + @Readonly fun getTerrainMatchingUniques(uniqueType: UniqueType, gameContext: GameContext = stateThisTile ): Sequence { return cachedTerrainData.uniques.getMatchingUniques(uniqueType, gameContext) } /** Get all uniques of this type that any part of this tile has: terrains, improvement, resource */ + @Readonly fun getMatchingUniques(uniqueType: UniqueType, gameContext: GameContext = stateThisTile): Sequence { var uniques = getTerrainMatchingUniques(uniqueType, gameContext) if (getUnpillagedImprovement() != null) { @@ -427,6 +427,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return civInfo.cities.firstOrNull { it != owningCity && it.isWorked(this) } } + @Readonly fun isBlockaded(): Boolean { val owner = getOwner() ?: return false val unit = militaryUnit @@ -463,11 +464,13 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { || terrainHasUnique(UniqueType.TileProvidesYieldWithoutPopulation) } + @Readonly fun isLocked(): Boolean { val workingCity = getWorkingCity() return workingCity != null && workingCity.lockedTiles.contains(position) } - + + @Readonly fun providesResources(civInfo: Civilization): Boolean { if (!hasViewableResource(civInfo)) return false if (isCityCenter()) { @@ -520,6 +523,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { } /** Implements [UniqueParameterType.TerrainFilter][com.unciv.models.ruleset.unique.UniqueParameterType.TerrainFilter] */ + @Readonly fun matchesTerrainFilter(filter: String, observingCiv: Civilization?, multiFilter: Boolean = true): Boolean { return if (multiFilter) MultiFilter.multiFilter(filter, { matchesSingleTerrainFilter(it, observingCiv) }) else matchesSingleTerrainFilter(filter, observingCiv) @@ -586,11 +590,13 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { fun hasViewableResource(civInfo: Civilization): Boolean = resource != null && civInfo.tech.isRevealed(tileResource) - fun getViewableTilesList(distance: Int): List = tileMap.getViewableTiles(position, distance) - fun getTilesInDistance(distance: Int): Sequence = tileMap.getTilesInDistance(position, distance) - fun getTilesInDistanceRange(range: IntRange): Sequence = tileMap.getTilesInDistanceRange(position, range) - fun getTilesAtDistance(distance: Int): Sequence = tileMap.getTilesAtDistance(position, distance) + @Readonly fun getViewableTilesList(distance: Int): List = tileMap.getViewableTiles(position, distance) + @Readonly fun getTilesInDistance(distance: Int): Sequence = tileMap.getTilesInDistance(position, distance) + @Readonly fun getTilesInDistanceRange(range: IntRange): Sequence = tileMap.getTilesInDistanceRange(position, range) + @Readonly fun getTilesAtDistance(distance: Int): Sequence = tileMap.getTilesAtDistance(position, distance) + + @Readonly fun getDefensiveBonus(includeImprovementBonus: Boolean = true, unit: MapUnit? = null): Float { var bonus = baseTerrainObject.defenceBonus if (terrainFeatureObjects.isNotEmpty()) { @@ -612,6 +618,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { * @param otherTile Destination tile * @return Shortest distance from this [Tile] to [otherTile] in count of tiles including impassable tiles but not including origin tile */ + @Readonly fun aerialDistanceTo(otherTile: Tile): Int { val xDelta = position.x - otherTile.position.x val yDelta = position.y - otherTile.position.y @@ -628,6 +635,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return min(distance, wrappedDistance).toInt() } + @Readonly fun canBeSettled(): Boolean { val modConstants = tileMap.gameInfo.ruleset.modOptions.constants return when { @@ -641,6 +649,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { } /** The two tiles have a river between them */ + @Readonly fun isConnectedByRiver(otherTile: Tile): Boolean { if (otherTile == this) throw Exception("Should not be called to compare to self!") @@ -672,6 +681,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { * @returns whether units of [civInfo] can pass through this tile, considering only civ-wide filters. * Use [UnitMovement.canPassThrough] to check whether a specific unit can pass through a tile. */ + @Readonly fun canCivPassThrough(civInfo: Civilization): Boolean { val tileOwner = getOwner() // comparing the CivInfo objects is cheaper than comparing strings @@ -680,7 +690,8 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { && !getCity()!!.hasJustBeenConquered) return false return civInfo.diplomacyFunctions.canPassThroughTiles(tileOwner) } - + + @Readonly fun hasEnemyInvisibleUnit(viewingCiv: Civilization): Boolean { val unitsInTile = getUnits() return when { @@ -691,28 +702,33 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { } } + @Readonly fun hasConnection(civInfo: Civilization) = getUnpillagedRoad() != RoadStatus.None || forestOrJungleAreRoads(civInfo) + @Readonly fun hasRoadConnection(civInfo: Civilization, mustBeUnpillaged: Boolean) = if (mustBeUnpillaged) (getUnpillagedRoad() == RoadStatus.Road) || forestOrJungleAreRoads(civInfo) else roadStatus == RoadStatus.Road || forestOrJungleAreRoads(civInfo) + @Readonly fun hasRailroadConnection(mustBeUnpillaged: Boolean) = if (mustBeUnpillaged) getUnpillagedRoad() == RoadStatus.Railroad else roadStatus == RoadStatus.Railroad - + @Readonly private fun forestOrJungleAreRoads(civInfo: Civilization) = civInfo.nation.forestsAndJunglesAreRoads && (terrainFeatures.contains(Constants.jungle) || terrainFeatures.contains(Constants.forest)) && isFriendlyTerritory(civInfo) - + + @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!") @@ -727,15 +743,17 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return out } + @Readonly @Suppress("purity") // should be auto-recognized as readonly fun getContinent() = continent /** Checks if this tile is marked as target tile for a building with a [UniqueType.CreatesOneImprovement] unique */ - fun isMarkedForCreatesOneImprovement() = + @Readonly fun isMarkedForCreatesOneImprovement() = turnsToImprovement < 0 && improvementInProgress != null /** Checks if this tile is marked as target tile for a building with a [UniqueType.CreatesOneImprovement] unique creating a specific [improvement] */ - fun isMarkedForCreatesOneImprovement(improvement: String) = + @Readonly fun isMarkedForCreatesOneImprovement(improvement: String) = turnsToImprovement < 0 && improvementInProgress == improvement + @Readonly private fun approximateMajorDepositDistribution(): Double { // We can't replicate the MapRegions resource distributor, so let's try to get // a close probability of major deposits per tile diff --git a/core/src/com/unciv/models/ruleset/tile/TileResource.kt b/core/src/com/unciv/models/ruleset/tile/TileResource.kt index 4578623863..d8d83b60fd 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileResource.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileResource.kt @@ -13,6 +13,7 @@ import com.unciv.models.stats.GameResource import com.unciv.models.stats.Stats import com.unciv.ui.objectdescriptions.uniquesToCivilopediaTextLines import com.unciv.ui.screens.civilopediascreen.FormattedLine +import yairm210.purity.annotations.Readonly class TileResource : RulesetStatsObject(), GameResource { @@ -52,6 +53,7 @@ class TileResource : RulesetStatsObject(), GameResource { * @see improvedBy * @see UniqueType.ImprovesResources */ + @Readonly @Suppress("purity") // requires some plumbing fun getImprovements(): Set { if (improvementsInitialized) return allImprovements val ruleset = this.ruleset