From 0d68c5eac2d286f69107c420b7eabed16ddc6f56 Mon Sep 17 00:00:00 2001 From: yairm210 Date: Wed, 20 Aug 2025 14:13:45 +0300 Subject: [PATCH] chore(purity) --- build.gradle.kts | 1 + .../com/unciv/logic/battle/BattleDamage.kt | 3 ++- .../battle/GreatGeneralImplementation.kt | 1 + .../logic/city/GreatPersonPointsBreakdown.kt | 5 +++-- .../city/managers/CityConquestFunctions.kt | 2 ++ .../unciv/logic/city/managers/CityFounder.kt | 3 +++ .../city/managers/CityReligionManager.kt | 5 ----- .../logic/civilization/CivConstructions.kt | 2 ++ .../logic/civilization/ExploredRegion.kt | 21 +++++++++++-------- .../unciv/logic/civilization/PlayerType.kt | 1 - .../diplomacy/DiplomacyFunctions.kt | 1 + .../diplomacy/DiplomacyTurnManager.kt | 3 ++- .../civilization/managers/PolicyManager.kt | 2 +- .../civilization/managers/RuinsManager.kt | 3 +++ .../newgamescreen/PlayerPickerTable.kt | 4 +++- .../ui/screens/worldscreen/minimap/Minimap.kt | 4 ++++ 16 files changed, 40 insertions(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b200860f6c..07457b6f77 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,6 +50,7 @@ allprojects { configure{ wellKnownPureFunctions = setOf( "kotlin.with", // moved + "kotlin.sequences.generateSequence", ) wellKnownReadonlyFunctions = setOf( "com.badlogic.gdx.math.Vector2.len", diff --git a/core/src/com/unciv/logic/battle/BattleDamage.kt b/core/src/com/unciv/logic/battle/BattleDamage.kt index 37703da9df..1945cf1ffb 100644 --- a/core/src/com/unciv/logic/battle/BattleDamage.kt +++ b/core/src/com/unciv/logic/battle/BattleDamage.kt @@ -10,6 +10,7 @@ import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.translations.tr import com.unciv.ui.components.extensions.toPercent import yairm210.purity.annotations.LocalState +import yairm210.purity.annotations.Pure import yairm210.purity.annotations.Readonly import kotlin.collections.set import kotlin.math.max @@ -313,7 +314,7 @@ object BattleDamage { return (damageModifier(ratio, false, randomnessFactor) * getHealthDependantDamageRatio(attacker)).roundToInt() } - @Readonly + @Pure private fun damageModifier( attackerToDefenderRatio: Float, damageToAttacker: Boolean, diff --git a/core/src/com/unciv/logic/battle/GreatGeneralImplementation.kt b/core/src/com/unciv/logic/battle/GreatGeneralImplementation.kt index 090a871b1a..7e47e53898 100644 --- a/core/src/com/unciv/logic/battle/GreatGeneralImplementation.kt +++ b/core/src/com/unciv/logic/battle/GreatGeneralImplementation.kt @@ -66,6 +66,7 @@ object GreatGeneralImplementation { * * Used by [SpecificUnitAutomation.automateGreatGeneral]. */ + @Readonly fun getBestAffectedTroopsTile(general: MapUnit): Tile? { // Normally we have only one Unique here. But a mix is not forbidden, // (imagine several GreatGeneralAura uniques - +50% at radius 1, +25% at radius 2, +5% at radius 3 - possibly learnable from promotions via buildings or natural wonders?) diff --git a/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt b/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt index d4160559f7..56efa605bb 100644 --- a/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt +++ b/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt @@ -5,6 +5,7 @@ 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.Pure import yairm210.purity.annotations.Readonly /** Manages calculating Great Person Points per City for nextTurn. See public constructor(city) below for details. */ @@ -39,9 +40,9 @@ class GreatPersonPointsBreakdown private constructor(private val ruleset: Rulese const val fixedPointFactor = 1000 - @Readonly private fun getUniqueSourceName(unique: Unique) = unique.sourceObjectName ?: "Bonus" + @Pure private fun getUniqueSourceName(unique: Unique) = unique.sourceObjectName ?: "Bonus" - @Readonly + @Pure private fun guessPediaLink(unique: Unique): String? { if (unique.sourceObjectName == null) return null return unique.sourceObjectType!!.name + "/" + unique.sourceObjectName diff --git a/core/src/com/unciv/logic/city/managers/CityConquestFunctions.kt b/core/src/com/unciv/logic/city/managers/CityConquestFunctions.kt index 228dafb20d..eee58c36da 100644 --- a/core/src/com/unciv/logic/city/managers/CityConquestFunctions.kt +++ b/core/src/com/unciv/logic/city/managers/CityConquestFunctions.kt @@ -18,6 +18,7 @@ import com.unciv.logic.trade.TradeOfferType import com.unciv.models.ruleset.unique.GameContext import com.unciv.models.ruleset.unique.UniqueTriggerActivation import com.unciv.models.ruleset.unique.UniqueType +import yairm210.purity.annotations.Readonly import kotlin.math.max import kotlin.math.min import kotlin.math.roundToInt @@ -27,6 +28,7 @@ import kotlin.random.Random class CityConquestFunctions(val city: City) { private val tileBasedRandom = Random(city.getCenterTile().position.toString().hashCode()) + @Readonly private fun getGoldForCapturingCity(conqueringCiv: Civilization): Int { val baseGold = 20 + 10 * city.population.population + tileBasedRandom.nextInt(40) val turnModifier = max(0, min(50, city.civ.gameInfo.turns - city.turnAcquired)) / 50f diff --git a/core/src/com/unciv/logic/city/managers/CityFounder.kt b/core/src/com/unciv/logic/city/managers/CityFounder.kt index 252507aaab..8169f9ccd2 100644 --- a/core/src/com/unciv/logic/city/managers/CityFounder.kt +++ b/core/src/com/unciv/logic/city/managers/CityFounder.kt @@ -12,6 +12,7 @@ import com.unciv.models.ruleset.nation.Nation import com.unciv.models.ruleset.unique.GameContext import com.unciv.models.ruleset.unique.UniqueTriggerActivation import com.unciv.models.ruleset.unique.UniqueType +import yairm210.purity.annotations.Readonly class CityFounder { fun foundCity(civInfo: Civilization, cityLocation: Vector2, unit: MapUnit? = null): City { @@ -116,6 +117,7 @@ class CityFounder { * @param aliveCivs Every civilization currently alive. * @return A new city name in [String]. Null if failed to generate a name. */ + @Readonly private fun generateNewCityName( foundingCiv: Civilization, aliveCivs: Set @@ -162,6 +164,7 @@ class CityFounder { * @param usedCityNames Every city name that have already been taken. * @return A new city named in [String]. Null if failed to generate a name. */ + @Readonly private fun borrowCityName( foundingCiv: Civilization, aliveCivs: Set, diff --git a/core/src/com/unciv/logic/city/managers/CityReligionManager.kt b/core/src/com/unciv/logic/city/managers/CityReligionManager.kt index c411c95c75..11bc61feec 100644 --- a/core/src/com/unciv/logic/city/managers/CityReligionManager.kt +++ b/core/src/com/unciv/logic/city/managers/CityReligionManager.kt @@ -196,11 +196,6 @@ class CityReligionManager : IsPartOfGameInfoSerialization { return followers[majorityReligion] } - fun getFollowersOfOurReligion(): Int { - val ourReligion = city.civ.religionManager.religion ?: return 0 - return followers[ourReligion.name] - } - @Readonly fun getFollowersOfOtherReligionsThan(religion: String): Int { return followers.filterNot { it.key == religion }.values.sum() diff --git a/core/src/com/unciv/logic/civilization/CivConstructions.kt b/core/src/com/unciv/logic/civilization/CivConstructions.kt index 7c283b0a9d..bed90ed864 100644 --- a/core/src/com/unciv/logic/civilization/CivConstructions.kt +++ b/core/src/com/unciv/logic/civilization/CivConstructions.kt @@ -91,10 +91,12 @@ class CivConstructions : IsPartOfGameInfoSerialization { getFreeBuildingNamesSequence(city.id).toSet() /** Tests whether the [city] has [building] for free, from nationwide sources or buildings in other cities */ + @Readonly fun hasFreeBuilding(city: City, building: Building) = hasFreeBuildingByName(city.id, building.name) /** Tests whether a city by [cityId] has a building named [buildingName] for free, from nationwide sources or buildings in other cities */ + @Readonly private fun hasFreeBuildingByName(cityId: String, buildingName: String) = getFreeBuildingNamesSequence(cityId).contains(buildingName) diff --git a/core/src/com/unciv/logic/civilization/ExploredRegion.kt b/core/src/com/unciv/logic/civilization/ExploredRegion.kt index 7bda372822..8570684aa0 100644 --- a/core/src/com/unciv/logic/civilization/ExploredRegion.kt +++ b/core/src/com/unciv/logic/civilization/ExploredRegion.kt @@ -9,6 +9,7 @@ import com.unciv.logic.map.HexMath.worldFromLatLong import com.unciv.logic.map.MapParameters import com.unciv.logic.map.MapShape import com.unciv.ui.components.tilegroups.TileGroupMap +import yairm210.purity.annotations.Readonly import kotlin.math.abs import kotlin.math.sqrt @@ -57,14 +58,14 @@ class ExploredRegion : IsPartOfGameInfoSerialization { private var bottomRight = Vector2() // Getters - fun shouldRecalculateCoords(): Boolean = shouldRecalculateCoords - fun shouldUpdateMinimap(): Boolean = shouldUpdateMinimap - fun getRectangle(): Rectangle = exploredRectangle - fun shouldRestrictX(): Boolean = shouldRestrictX - fun getLeftX(): Float = topLeftStage.x - fun getRightX(): Float = bottomRightStage.x - fun getTopY(): Float = topLeftStage.y - fun getBottomY(): Float = bottomRightStage.y + @Readonly fun shouldRecalculateCoords(): Boolean = shouldRecalculateCoords + @Readonly fun shouldUpdateMinimap(): Boolean = shouldUpdateMinimap + @Readonly fun getRectangle(): Rectangle = exploredRectangle + @Readonly fun shouldRestrictX(): Boolean = shouldRestrictX + @Readonly fun getLeftX(): Float = topLeftStage.x + @Readonly fun getRightX(): Float = bottomRightStage.x + @Readonly fun getTopY(): Float = topLeftStage.y + @Readonly fun getBottomY(): Float = bottomRightStage.y fun clone(): ExploredRegion { val toReturn = ExploredRegion() @@ -201,6 +202,7 @@ class ExploredRegion : IsPartOfGameInfoSerialization { exploredRectangle.height = getHeight() * yOffset } + @Readonly fun isPositionInRegion(postition: Vector2): Boolean { val long = getLongitude(postition) val lat = getLatitude(postition) @@ -210,6 +212,7 @@ class ExploredRegion : IsPartOfGameInfoSerialization { (((long >= topLeft.x && long >= bottomRight.x) || (long <= topLeft.x && long <= bottomRight.x)) && lat <= topLeft.y && lat >= bottomRight.y) } + @Readonly fun getWidth(): Int { val result: Float if (topLeft.x > bottomRight.x) result = topLeft.x - bottomRight.x @@ -217,7 +220,7 @@ class ExploredRegion : IsPartOfGameInfoSerialization { return result.toInt() + 1 } - fun getHeight(): Int = (topLeft.y - bottomRight.y).toInt() + 1 + @Readonly fun getHeight(): Int = (topLeft.y - bottomRight.y).toInt() + 1 fun getMinimapLeft(tileSize: Float): Float { shouldUpdateMinimap = false diff --git a/core/src/com/unciv/logic/civilization/PlayerType.kt b/core/src/com/unciv/logic/civilization/PlayerType.kt index b8c20a0aa6..cec72f3340 100644 --- a/core/src/com/unciv/logic/civilization/PlayerType.kt +++ b/core/src/com/unciv/logic/civilization/PlayerType.kt @@ -5,5 +5,4 @@ import com.unciv.logic.IsPartOfGameInfoSerialization enum class PlayerType : IsPartOfGameInfoSerialization { AI, Human; - fun toggle() = if (this == AI) Human else AI } diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt index eda1b73efd..8ee218003f 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyFunctions.kt @@ -163,6 +163,7 @@ class DiplomacyFunctions(val civInfo: Civilization) { theirDiploManager.removeModifier(DiplomaticModifiers.SharedEmbassies) } + @Readonly fun canSignDeclarationOfFriendshipWith(otherCiv: Civilization): Boolean { return otherCiv.isMajorCiv() && !otherCiv.isAtWarWith(civInfo) && !civInfo.getDiplomacyManager(otherCiv)!!.hasFlag(DiplomacyFlags.Denunciation) diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt index 13aa83662c..d4546b9e9f 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyTurnManager.kt @@ -9,6 +9,7 @@ import com.unciv.logic.trade.TradeOffer import com.unciv.logic.trade.TradeOfferType import com.unciv.models.ruleset.unique.UniqueType import com.unciv.ui.components.extensions.toPercent +import yairm210.purity.annotations.Readonly import kotlin.math.absoluteValue import kotlin.math.max import kotlin.math.min @@ -114,7 +115,7 @@ object DiplomacyTurnManager { } } - + @Readonly private fun DiplomacyManager.getCityStateInfluenceRecovery(): Float { if (getInfluence() >= getCityStateInfluenceRestingPoint()) return 0f diff --git a/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt b/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt index ef2169fd79..99440ae8d2 100644 --- a/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/PolicyManager.kt @@ -102,7 +102,7 @@ class PolicyManager : IsPartOfGameInfoSerialization { @Readonly private fun getRulesetPolicies() = civInfo.gameInfo.ruleset.policies @Suppress("MemberVisibilityCanBePrivate") - fun getPolicyByName(name: String): Policy = getRulesetPolicies()[name]!! + @Readonly fun getPolicyByName(name: String): Policy = getRulesetPolicies()[name]!! fun setTransients(civInfo: Civilization) { this.civInfo = civInfo diff --git a/core/src/com/unciv/logic/civilization/managers/RuinsManager.kt b/core/src/com/unciv/logic/civilization/managers/RuinsManager.kt index 46ab1a0212..ecf09360bc 100644 --- a/core/src/com/unciv/logic/civilization/managers/RuinsManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/RuinsManager.kt @@ -7,6 +7,7 @@ import com.unciv.models.ruleset.RuinReward import com.unciv.models.ruleset.unique.GameContext import com.unciv.models.ruleset.unique.UniqueTriggerActivation import com.unciv.models.ruleset.unique.UniqueType +import yairm210.purity.annotations.Readonly import kotlin.random.Random class RuinsManager( @@ -30,6 +31,7 @@ class RuinsManager( lastChosenRewards[1] = reward } + @Readonly private fun getShuffledPossibleRewards(triggeringUnit: MapUnit): Iterable { val candidates = validRewards.asSequence().filter { isPossibleReward(it, triggeringUnit) } @@ -45,6 +47,7 @@ class RuinsManager( return candidates } + @Readonly private fun isPossibleReward(ruinReward: RuinReward, unit: MapUnit): Boolean { if (ruinReward.name in lastChosenRewards) return false if (ruinReward.isUnavailableBySettings(civInfo.gameInfo)) return false diff --git a/core/src/com/unciv/ui/screens/newgamescreen/PlayerPickerTable.kt b/core/src/com/unciv/ui/screens/newgamescreen/PlayerPickerTable.kt index 130202dee8..21be42dbb3 100644 --- a/core/src/com/unciv/ui/screens/newgamescreen/PlayerPickerTable.kt +++ b/core/src/com/unciv/ui/screens/newgamescreen/PlayerPickerTable.kt @@ -9,6 +9,8 @@ import com.unciv.Constants import com.unciv.UncivGame import com.unciv.logic.IdChecker import com.unciv.logic.civilization.PlayerType +import com.unciv.logic.civilization.PlayerType.AI +import com.unciv.logic.civilization.PlayerType.Human import com.unciv.logic.multiplayer.FriendList import com.unciv.models.metadata.GameParameters import com.unciv.models.metadata.GameSetupInfo @@ -213,7 +215,7 @@ class PlayerPickerTable( updatePlayerTypeButtonEnabled() } playerTypeTextButton.onClick { - player.playerType = player.playerType.toggle() + player.playerType = if (player.playerType == AI) Human else AI update() } diff --git a/core/src/com/unciv/ui/screens/worldscreen/minimap/Minimap.kt b/core/src/com/unciv/ui/screens/worldscreen/minimap/Minimap.kt index 11b5114f00..e4e3a99508 100644 --- a/core/src/com/unciv/ui/screens/worldscreen/minimap/Minimap.kt +++ b/core/src/com/unciv/ui/screens/worldscreen/minimap/Minimap.kt @@ -13,6 +13,8 @@ import com.unciv.ui.components.NonTransformGroup import com.unciv.ui.images.ClippingImage import com.unciv.ui.images.ImageGetter import com.unciv.ui.screens.worldscreen.worldmap.WorldMapHolder +import yairm210.purity.annotations.Pure +import yairm210.purity.annotations.Readonly import kotlin.math.max import kotlin.math.min import kotlin.math.sqrt @@ -73,6 +75,7 @@ class Minimap(val mapHolder: WorldMapHolder, minimapSize: Int, private val civIn mapHolder.onViewportChangedListener = ::updateScrollPosition } + @Readonly private fun calcTileSize(minimapSize: Vector2): Float { val height: Float val width: Float @@ -201,6 +204,7 @@ class Minimap(val mapHolder: WorldMapHolder, minimapSize: Int, private val civIn worldHeight: Float, worldViewport: Rectangle ) { + @Pure operator fun Rectangle.times(other: Vector2) = Rectangle(x * other.x, y * other.y, width * other.x, height * other.y)