chore(purity)

This commit is contained in:
yairm210 2025-08-20 14:13:45 +03:00
parent feaa016053
commit 0d68c5eac2
16 changed files with 40 additions and 21 deletions

View File

@ -50,6 +50,7 @@ allprojects {
configure<yairm210.purity.PurityConfiguration>{
wellKnownPureFunctions = setOf(
"kotlin.with", // moved
"kotlin.sequences.generateSequence",
)
wellKnownReadonlyFunctions = setOf(
"com.badlogic.gdx.math.Vector2.len",

View File

@ -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,

View File

@ -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?)

View File

@ -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

View File

@ -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

View File

@ -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<Civilization>
@ -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<Civilization>,

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -5,5 +5,4 @@ import com.unciv.logic.IsPartOfGameInfoSerialization
enum class PlayerType : IsPartOfGameInfoSerialization {
AI,
Human;
fun toggle() = if (this == AI) Human else AI
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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<RuinReward> {
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

View File

@ -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()
}

View File

@ -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)