diff --git a/build.gradle.kts b/build.gradle.kts index 17b3fc9373..7b8ef4abba 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -47,9 +47,17 @@ allprojects { apply(plugin = "io.github.yairm210.purity-plugin") configure{ - wellKnownPureFunctions = setOf() + wellKnownPureFunctions = setOf( + "kotlin.let", + "kotlin.run", + "kotlin.also", + "kotlin.apply", + "kotlin.takeIf", + "kotlin.takeUnless", + ) wellKnownReadonlyFunctions = setOf( "kotlin.collections.any", + "kotlin.collections.get", "kotlin.collections.all", "kotlin.ranges.coerceAtLeast", // Looks like the Collection.contains is not considered overridden :thunk: diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index 31b4ff658a..4943c4534a 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -56,6 +56,7 @@ import com.unciv.models.translations.tr import com.unciv.ui.components.extensions.toPercent import com.unciv.ui.screens.victoryscreen.RankingType import org.jetbrains.annotations.VisibleForTesting +import yairm210.purity.annotations.Readonly import kotlin.math.max import kotlin.math.min import kotlin.math.roundToInt @@ -323,7 +324,9 @@ 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] fun getProximity(civInfo: Civilization) = getProximity(civInfo.civName) diff --git a/core/src/com/unciv/logic/map/tile/Tile.kt b/core/src/com/unciv/logic/map/tile/Tile.kt index d8643716cb..8cc558504f 100644 --- a/core/src/com/unciv/logic/map/tile/Tile.kt +++ b/core/src/com/unciv/logic/map/tile/Tile.kt @@ -283,12 +283,18 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { 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? { if (canPillageTileImprovement()) return ruleset.tileImprovements[improvement]!! @@ -297,6 +303,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return null } // same as above, but slightly quicker + @Readonly fun getImprovementToPillageName(): String? { if (canPillageTileImprovement()) return improvement @@ -304,6 +311,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return roadStatus.name return null } + @Readonly fun getImprovementToRepair(): TileImprovement? { if (improvement != null && improvementIsPillaged) return ruleset.tileImprovements[improvement]!! @@ -311,24 +319,30 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return ruleset.tileImprovements[roadStatus.name]!! return null } + @Readonly fun canPillageTile(): Boolean { return canPillageTileImprovement() || canPillageRoad() } + @Readonly fun canPillageTileImprovement(): Boolean { return improvement != null && !improvementIsPillaged && !ruleset.tileImprovements[improvement]!!.hasUnique(UniqueType.Unpillagable) && !ruleset.tileImprovements[improvement]!!.hasUnique(UniqueType.Irremovable) } + @Readonly fun canPillageRoad(): Boolean { return roadStatus != RoadStatus.None && !roadIsPillaged && !ruleset.tileImprovements[roadStatus.name]!!.hasUnique(UniqueType.Unpillagable) && !ruleset.tileImprovements[roadStatus.name]!!.hasUnique(UniqueType.Irremovable) } + @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 getUnpillagedRoadImprovement(): TileImprovement? { return if (getUnpillagedRoad() == RoadStatus.None) null else ruleset.tileImprovements[getUnpillagedRoad().name] @@ -491,7 +505,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return MultiFilter.multiFilter(filter, { matchesSingleFilter(it, civInfo) }) } - @Readonly @Suppress("purity") + @Readonly private fun matchesSingleFilter(filter: String, civInfo: Civilization? = null): Boolean { if (matchesSingleTerrainFilter(filter, civInfo)) return true if ((improvement == null || improvementIsPillaged) && filter == "unimproved") return true @@ -507,8 +521,8 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable { return if (multiFilter) MultiFilter.multiFilter(filter, { matchesSingleTerrainFilter(it, observingCiv) }) else matchesSingleTerrainFilter(filter, observingCiv) } - + @Readonly @Suppress("purity") private fun matchesSingleTerrainFilter(filter: String, observingCiv: Civilization?): Boolean { // Constant strings get their own 'when' for performance - // see https://yairm210.medium.com/kotlin-when-string-optimization-e15c6eea2734 diff --git a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt index 7db08ee7ea..df6044e642 100644 --- a/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt +++ b/core/src/com/unciv/models/ruleset/tile/TileImprovement.kt @@ -14,6 +14,7 @@ import com.unciv.models.ruleset.unit.BaseUnit import com.unciv.ui.components.extensions.toPercent import com.unciv.ui.objectdescriptions.ImprovementDescriptions import com.unciv.ui.screens.civilopediascreen.FormattedLine +import yairm210.purity.annotations.Readonly import kotlin.math.roundToInt class TileImprovement : RulesetStatsObject() { @@ -50,8 +51,11 @@ class TileImprovement : RulesetStatsObject() { fun getDescription(ruleset: Ruleset): String = ImprovementDescriptions.getDescription(this, ruleset) fun getShortDecription() = ImprovementDescriptions.getShortDescription(this) + @Readonly fun isGreatImprovement() = hasUnique(UniqueType.GreatImprovement) + @Readonly fun isRoad() = RoadStatus.entries.any { it != RoadStatus.None && it.name == this.name } + @Readonly fun isAncientRuinsEquivalent() = hasUnique(UniqueType.IsAncientRuinsEquivalent) fun canBeBuiltOn(terrain: String): Boolean { @@ -77,6 +81,7 @@ class TileImprovement : RulesetStatsObject() { /** Implements [UniqueParameterType.ImprovementFilter][com.unciv.models.ruleset.unique.UniqueParameterType.ImprovementFilter] */ + @Readonly fun matchesFilter(filter: String, tileState: GameContext? = null, multiFilter: Boolean = true): Boolean { return if (multiFilter) MultiFilter.multiFilter(filter, { matchesSingleFilter(it) || @@ -88,6 +93,7 @@ class TileImprovement : RulesetStatsObject() { tileState == null && hasTagUnique(filter) } + @Readonly private fun matchesSingleFilter(filter: String): Boolean { return when (filter) { "all", "All" -> true diff --git a/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt b/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt index 819350e74d..de9ab563c6 100644 --- a/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt +++ b/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt @@ -8,6 +8,7 @@ import com.unciv.models.ruleset.tech.TechColumn import com.unciv.models.ruleset.tech.Technology import com.unciv.models.stats.INamed import com.unciv.ui.components.extensions.toPercent +import yairm210.purity.annotations.Readonly /** * Common interface for all 'ruleset objects' that have Uniques, like BaseUnit, Nation, etc. @@ -48,12 +49,15 @@ interface IHasUniques : INamed { fun getMatchingUniques(uniqueTag: String, state: GameContext = GameContext.EmptyState) = uniqueMap.getMatchingUniques(uniqueTag, state) + @Readonly fun hasUnique(uniqueType: UniqueType, state: GameContext? = null) = uniqueMap.hasMatchingUnique(uniqueType, state ?: GameContext.EmptyState) + @Readonly fun hasUnique(uniqueTag: String, state: GameContext? = null) = uniqueMap.hasMatchingUnique(uniqueTag, state ?: GameContext.EmptyState) + @Readonly fun hasTagUnique(tagUnique: String) = uniqueMap.hasTagUnique(tagUnique)