diff --git a/build.gradle.kts b/build.gradle.kts index a629909d39..6992659e6e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -49,6 +49,7 @@ allprojects { apply(plugin = "io.github.yairm210.purity-plugin") configure{ wellKnownPureFunctions = setOf( + "kotlin.with", ) wellKnownReadonlyFunctions = setOf( "com.badlogic.gdx.math.Vector2.len", diff --git a/core/src/com/unciv/logic/automation/civilization/ReligionAutomation.kt b/core/src/com/unciv/logic/automation/civilization/ReligionAutomation.kt index 38017f4014..7d11d1a55f 100644 --- a/core/src/com/unciv/logic/automation/civilization/ReligionAutomation.kt +++ b/core/src/com/unciv/logic/automation/civilization/ReligionAutomation.kt @@ -11,6 +11,7 @@ import com.unciv.models.ruleset.BeliefType import com.unciv.models.ruleset.unique.GameContext import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.stats.Stat +import yairm210.purity.annotations.Readonly import kotlin.math.min import kotlin.math.pow import kotlin.random.Random @@ -205,7 +206,7 @@ object ReligionAutomation { // endregion // region rate beliefs - + @Readonly fun rateBelief(civInfo: Civilization, belief: Belief): Float { var score = 0f // Roughly equivalent to the sum of stats gained across all cities @@ -234,6 +235,7 @@ object ReligionAutomation { return score } + @Readonly private fun beliefBonusForTile(belief: Belief, tile: Tile, city: City): Float { var bonusYield = 0f for (unique in belief.uniqueObjects) { @@ -253,6 +255,7 @@ object ReligionAutomation { return bonusYield } + @Readonly private fun beliefBonusForCity(civInfo: Civilization, belief: Belief, city: City): Float { var score = 0f val ruleSet = civInfo.gameInfo.ruleset @@ -305,6 +308,7 @@ object ReligionAutomation { return score } + @Readonly private fun beliefBonusForPlayer(civInfo: Civilization, belief: Belief): Float { var score = 0f val numberOfFoundedReligions = civInfo.gameInfo.civilizations.count { @@ -411,7 +415,7 @@ object ReligionAutomation { // line 4426 through 4870. // This is way too much work for now, so I'll just choose a random pantheon instead. // Should probably be changed later, but it works for now. - val chosenPantheon = chooseBeliefOfType(civInfo, BeliefType.Pantheon) + val chosenPantheon = pickBeliefOfType(civInfo, BeliefType.Pantheon) ?: return // panic! civInfo.religionManager.chooseBeliefs( listOf(chosenPantheon), @@ -461,14 +465,15 @@ object ReligionAutomation { if (belief == BeliefType.None) continue repeat(beliefsToChoose[belief]) { chosenBeliefs.add( - chooseBeliefOfType(civInfo, belief, chosenBeliefs) ?: return@repeat + pickBeliefOfType(civInfo, belief, chosenBeliefs) ?: return@repeat ) } } return chosenBeliefs } - private fun chooseBeliefOfType(civInfo: Civilization, beliefType: BeliefType, additionalBeliefsToExclude: HashSet = hashSetOf()): Belief? { + @Readonly + private fun pickBeliefOfType(civInfo: Civilization, beliefType: BeliefType, additionalBeliefsToExclude: HashSet = hashSetOf()): Belief? { return civInfo.gameInfo.ruleset.beliefs.values .filter { (it.type == beliefType || beliefType == BeliefType.Any) diff --git a/core/src/com/unciv/logic/map/mapunit/UnitUpgradeManager.kt b/core/src/com/unciv/logic/map/mapunit/UnitUpgradeManager.kt index 9eec8249a6..a7f4e51fd9 100644 --- a/core/src/com/unciv/logic/map/mapunit/UnitUpgradeManager.kt +++ b/core/src/com/unciv/logic/map/mapunit/UnitUpgradeManager.kt @@ -45,6 +45,7 @@ class UnitUpgradeManager(val unit: MapUnit) { */ // Only one use from getUpgradeAction at the moment, so AI-specific rules omitted //todo Does the AI never buy upgrades??? + @Readonly fun getCostOfUpgrade(unitToUpgradeTo: BaseUnit): Int { // Source rounds to int every step, we don't //TODO From the source, this should apply _Production_ modifiers (Temple of Artemis? GameSpeed! StartEra!), at the moment it doesn't diff --git a/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt b/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt index 8167020c7e..c0abc4fe01 100644 --- a/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt +++ b/core/src/com/unciv/models/ruleset/unique/IHasUniques.kt @@ -82,17 +82,21 @@ interface IHasUniques : INamed { @Readonly fun requiredTechs(): Sequence = legacyRequiredTechs() + techsRequiredByUniques() + @Readonly fun requiredTechnologies(ruleset: Ruleset): Sequence = requiredTechs().map { ruleset.technologies[it] } + @Readonly fun era(ruleset: Ruleset): Era? = requiredTechnologies(ruleset).map { it?.era() }.map { ruleset.eras[it] }.maxByOrNull { it?.eraNumber ?: 0 } // This will return null only if requiredTechnologies() is empty or all required techs have no eraNumber + @Readonly fun techColumn(ruleset: Ruleset): TechColumn? = requiredTechnologies(ruleset).map { it?.column }.filterNotNull().maxByOrNull { it.columnNumber } // This will return null only if *all* required techs have null TechColumn. + @Readonly fun availableInEra(ruleset: Ruleset, requestedEra: String): Boolean { val eraAvailable: Era = era(ruleset) ?: return true // No technologies are required, so available in the starting era. @@ -103,6 +107,7 @@ interface IHasUniques : INamed { return eraAvailable.eraNumber <= ruleset.eras[requestedEra]!!.eraNumber } + @Readonly fun getWeightForAiDecision(gameContext: GameContext): Float { var weight = 1f for (unique in getMatchingUniques(UniqueType.AiChoiceWeight, gameContext)) @@ -160,6 +165,7 @@ interface IHasUniques : INamed { * In that case only this parameter can be `null`. So if you know it - provide! * @param ruleset Required if [gameInfo] is null, otherwise optional (but with both null this will simply return `false`). */ + @Readonly fun isHiddenFromCivilopedia( gameInfo: GameInfo?, ruleset: Ruleset? = null @@ -187,13 +193,14 @@ interface IHasUniques : INamed { return false } /** Overload of [isHiddenFromCivilopedia] for use in actually game-agnostic parts of Civilopedia */ - fun isHiddenFromCivilopedia(ruleset: Ruleset) = isHiddenFromCivilopedia(UncivGame.getGameInfoOrNull(), ruleset) + @Readonly fun isHiddenFromCivilopedia(ruleset: Ruleset) = isHiddenFromCivilopedia(UncivGame.getGameInfoOrNull(), ruleset) /** Common for Religion/Espionage: Hidden check when no game is loaded * @param hasFeature Best guess from the Ruleset whether the feature is available * @param enabler The modifier testing feature is on: `ConditionalReligionEnabled` or `ConditionalEspionageEnabled` * @param disabler The modifier testing feature is off: `ConditionalReligionDisabled` or `ConditionalEspionageDisabled` */ + @Readonly private fun shouldBeHiddenIfNoGameLoaded(hasFeature: Boolean, enabler: UniqueType, disabler: UniqueType): Boolean { for (unique in getMatchingUniques(UniqueType.OnlyAvailable, GameContext.IgnoreConditionals)) { if (unique.hasModifier(enabler)) return !hasFeature