mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-22 02:42:16 -04:00
chore: purity - big batch
This commit is contained in:
parent
c1f0b97e2d
commit
f6b71fc0af
@ -37,7 +37,7 @@ plugins {
|
||||
// This is *with* gradle 8.2 downloaded according the project specs, no idea what that's about
|
||||
kotlin("multiplatform") version "1.9.24"
|
||||
kotlin("plugin.serialization") version "1.9.24"
|
||||
id("io.github.yairm210.purity-plugin") version "0.0.25" apply(false)
|
||||
id("io.github.yairm210.purity-plugin") version "0.0.27" apply(false)
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@ -51,7 +51,8 @@ allprojects {
|
||||
"com.unciv.logic.civilization.diplomacy.RelationshipLevel.compareTo",
|
||||
"kotlin.math.max",
|
||||
"kotlin.math.min",
|
||||
"kotlin.math.abs"
|
||||
"kotlin.math.abs",
|
||||
"kotlin.internal.ir.noWhenBranchMatchedException",
|
||||
)
|
||||
wellKnownReadonlyFunctions = setOf(
|
||||
// Looks like the Collection.contains is not considered overridden :thunk:
|
||||
|
@ -239,18 +239,17 @@ class GameInfo : IsPartOfGameInfoSerialization, HasGameInfoSerializationVersion
|
||||
fun getCivilizationsAsPreviews() = civilizations.map { it.asPreview() }.toMutableList()
|
||||
/** Get barbarian civ
|
||||
* @throws NoSuchElementException in no-barbarians games! */
|
||||
fun getBarbarianCivilization() = getCivilization(Constants.barbarians)
|
||||
@Readonly @Suppress("purity") // This should be autorecognized!!
|
||||
@Readonly fun getBarbarianCivilization() = getCivilization(Constants.barbarians)
|
||||
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] */
|
||||
@Readonly @Suppress("purity") // This should be autorecognized!!
|
||||
@Readonly @Suppress("purity") // should be autorecognized
|
||||
fun getGlobalUniques() = combinedGlobalUniques
|
||||
|
||||
/** @return Sequence of all cities in game, both major civilizations and city states */
|
||||
fun getCities() = civilizations.asSequence().flatMap { it.cities }
|
||||
fun getAliveCityStates() = civilizations.filter { it.isAlive() && it.isCityState }
|
||||
fun getAliveMajorCivs() = civilizations.filter { it.isAlive() && it.isMajorCiv() }
|
||||
@Readonly fun getCities() = civilizations.asSequence().flatMap { it.cities }
|
||||
@Readonly fun getAliveCityStates() = civilizations.filter { it.isAlive() && it.isCityState }
|
||||
@Readonly fun getAliveMajorCivs() = civilizations.filter { it.isAlive() && it.isMajorCiv() }
|
||||
|
||||
/** Gets civilizations in their commonly used order - City-states last,
|
||||
* otherwise alphabetically by culture and translation. [civToSortFirst] can be used to force
|
||||
|
@ -10,6 +10,8 @@ import com.unciv.models.ruleset.unique.GameContext
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.ruleset.unit.UnitType
|
||||
import com.unciv.ui.components.extensions.toPercent
|
||||
import yairm210.purity.annotations.Pure
|
||||
import yairm210.purity.annotations.Readonly
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@ -19,10 +21,10 @@ class CityCombatant(val city: City) : ICombatant {
|
||||
}
|
||||
|
||||
override fun getHealth(): Int = city.health
|
||||
override fun getCivInfo(): Civilization = city.civ
|
||||
@Readonly override fun getCivInfo(): Civilization = city.civ
|
||||
override fun getTile(): Tile = city.getCenterTile()
|
||||
override fun getName(): String = city.name
|
||||
override fun isDefeated(): Boolean = city.health == 1
|
||||
@Readonly override fun isDefeated(): Boolean = city.health == 1
|
||||
override fun isInvisible(to: Civilization): Boolean = false
|
||||
override fun canAttack(): Boolean = city.canBombard()
|
||||
override fun matchesFilter(filter: String, multiFilter: Boolean) =
|
||||
@ -37,12 +39,13 @@ class CityCombatant(val city: City) : ICombatant {
|
||||
|
||||
override fun getUnitType(): UnitType = UnitType.City
|
||||
override fun getAttackingStrength(): Int = (getCityStrength(CombatAction.Attack) * 0.75).roundToInt()
|
||||
override fun getDefendingStrength(attackedByRanged: Boolean): Int {
|
||||
@Readonly override fun getDefendingStrength(attackedByRanged: Boolean): Int {
|
||||
if (isDefeated()) return 1
|
||||
return getCityStrength()
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@Readonly
|
||||
fun getCityStrength(combatAction: CombatAction = CombatAction.Defend): Int { // Civ fanatics forum, from a modder who went through the original code
|
||||
val modConstants = getCivInfo().gameInfo.ruleset.modOptions.constants
|
||||
var strength = modConstants.cityStrengthBase
|
||||
|
@ -165,22 +165,21 @@ class City : IsPartOfGameInfoSerialization, INamed {
|
||||
return toReturn
|
||||
}
|
||||
|
||||
fun canBombard() = !attackedThisTurn && !isInResistance()
|
||||
@Readonly fun canBombard() = !attackedThisTurn && !isInResistance()
|
||||
@Readonly @Suppress("purity") // should be autorecognized
|
||||
fun getCenterTile(): Tile = centerTile
|
||||
fun getCenterTileOrNull(): Tile? = if (::centerTile.isInitialized) centerTile else null
|
||||
fun getTiles(): Sequence<Tile> = tiles.asSequence().map { tileMap[it] }
|
||||
fun getWorkableTiles() = tilesInRange.asSequence().filter { it.getOwner() == civ }
|
||||
@Readonly
|
||||
fun isWorked(tile: Tile) = workedTiles.contains(tile.position)
|
||||
@Readonly fun getCenterTileOrNull(): Tile? = if (::centerTile.isInitialized) centerTile else null
|
||||
@Readonly fun getTiles(): Sequence<Tile> = tiles.asSequence().map { tileMap[it] }
|
||||
@Readonly fun getWorkableTiles() = tilesInRange.asSequence().filter { it.getOwner() == civ }
|
||||
@Readonly fun isWorked(tile: Tile) = workedTiles.contains(tile.position)
|
||||
|
||||
@Readonly
|
||||
fun isCapital(): Boolean = cityConstructions.builtBuildingUniqueMap.hasUnique(UniqueType.IndicatesCapital, state)
|
||||
@Readonly
|
||||
fun isCoastal(): Boolean = centerTile.isCoastalTile()
|
||||
|
||||
fun getBombardRange(): Int = civ.gameInfo.ruleset.modOptions.constants.baseCityBombardRange
|
||||
fun getWorkRange(): Int = civ.gameInfo.ruleset.modOptions.constants.cityWorkRange
|
||||
fun getExpandRange(): Int = civ.gameInfo.ruleset.modOptions.constants.cityExpandRange
|
||||
@Readonly fun isCapital(): Boolean = cityConstructions.builtBuildingUniqueMap.hasUnique(UniqueType.IndicatesCapital, state)
|
||||
@Readonly fun isCoastal(): Boolean = centerTile.isCoastalTile()
|
||||
|
||||
@Readonly fun getBombardRange(): Int = civ.gameInfo.ruleset.modOptions.constants.baseCityBombardRange
|
||||
@Readonly fun getWorkRange(): Int = civ.gameInfo.ruleset.modOptions.constants.cityWorkRange
|
||||
@Readonly fun getExpandRange(): Int = civ.gameInfo.ruleset.modOptions.constants.cityExpandRange
|
||||
|
||||
fun isConnectedToCapital(connectionTypePredicate: (Set<String>) -> Boolean = { true }): Boolean {
|
||||
val mediumTypes = civ.cache.citiesConnectedToCapitalToMediums[this] ?: return false
|
||||
@ -193,11 +192,11 @@ class City : IsPartOfGameInfoSerialization, INamed {
|
||||
it.civ == this.civ && it.canGarrison()
|
||||
}
|
||||
|
||||
fun hasFlag(flag: CityFlags) = flagsCountdown.containsKey(flag.name)
|
||||
fun getFlag(flag: CityFlags) = flagsCountdown[flag.name]!!
|
||||
@Readonly fun hasFlag(flag: CityFlags) = flagsCountdown.containsKey(flag.name)
|
||||
@Readonly fun getFlag(flag: CityFlags) = flagsCountdown[flag.name]!!
|
||||
|
||||
fun isWeLoveTheKingDayActive() = hasFlag(CityFlags.WeLoveTheKing)
|
||||
fun isInResistance() = hasFlag(CityFlags.Resistance)
|
||||
@Readonly fun isInResistance() = hasFlag(CityFlags.Resistance)
|
||||
fun isBlockaded(): Boolean {
|
||||
// Coastal cities are blocked if every adjacent water tile is blocked
|
||||
if (!isCoastal()) return false
|
||||
@ -281,7 +280,7 @@ class City : IsPartOfGameInfoSerialization, INamed {
|
||||
internal fun getMaxHealth() =
|
||||
200 + cityConstructions.getBuiltBuildings().sumOf { it.cityHealth }
|
||||
|
||||
fun getStrength() = cityConstructions.getBuiltBuildings().sumOf { it.cityStrength }.toFloat()
|
||||
@Readonly fun getStrength() = cityConstructions.getBuiltBuildings().sumOf { it.cityStrength }.toFloat()
|
||||
|
||||
// This should probably be configurable
|
||||
@Transient
|
||||
|
@ -37,6 +37,7 @@ import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import com.unciv.ui.screens.pickerscreens.PromotionTree
|
||||
import com.unciv.utils.withItem
|
||||
import com.unciv.utils.withoutItem
|
||||
import yairm210.purity.annotations.Readonly
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
@ -253,7 +254,7 @@ class CityConstructions : IsPartOfGameInfoSerialization {
|
||||
throw NotBuildingOrUnitException("$constructionName is not a building or a unit!")
|
||||
}
|
||||
|
||||
fun getBuiltBuildings(): Sequence<Building> = builtBuildingObjects.asSequence()
|
||||
@Readonly fun getBuiltBuildings(): Sequence<Building> = builtBuildingObjects.asSequence()
|
||||
|
||||
fun containsBuildingOrEquivalent(buildingNameOrUnique: String): Boolean =
|
||||
isBuilt(buildingNameOrUnique) || getBuiltBuildings().any { it.replaces == buildingNameOrUnique || it.hasUnique(buildingNameOrUnique, city.state) }
|
||||
|
@ -425,6 +425,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
stats.statsForNextTurn = newStats
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun getHappiness() = stats.happiness
|
||||
|
||||
/** Note that for stockpiled resources, this gives by how much it grows per turn, not current amount */
|
||||
@ -694,7 +695,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Readonly
|
||||
fun getStatForRanking(category: RankingType): Int {
|
||||
return if (isDefeated()) 0
|
||||
else when (category) {
|
||||
@ -711,12 +712,14 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
}
|
||||
|
||||
@Readonly @Suppress("purity") // caches
|
||||
private fun getMilitaryMight(): Int {
|
||||
if (cachedMilitaryMight < 0)
|
||||
cachedMilitaryMight = calculateMilitaryMight()
|
||||
return cachedMilitaryMight
|
||||
}
|
||||
|
||||
@Readonly
|
||||
private fun calculateMilitaryMight(): Int {
|
||||
var sum = 1 // minimum value, so we never end up with 0
|
||||
for (unit in units.getCivUnits()) {
|
||||
@ -740,6 +743,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
fun isLongCountDisplay() = hasLongCountDisplayUnique && isLongCountActive()
|
||||
|
||||
@Readonly
|
||||
fun calculateScoreBreakdown(): HashMap<String,Double> {
|
||||
val scoreBreakdown = hashMapOf<String,Double>()
|
||||
// 1276 is the number of tiles in a medium sized map. The original uses 4160 for this,
|
||||
@ -762,6 +766,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
return scoreBreakdown
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun calculateTotalScore() = calculateScoreBreakdown().values.sum()
|
||||
|
||||
//endregion
|
||||
@ -824,7 +829,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
fun getTurnsBetweenDiplomaticVotes() = (15 * gameInfo.speed.modifier).toInt() // Dunno the exact calculation, hidden in Lua files
|
||||
fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
|
||||
|
||||
fun getRecentBullyingCountdown() = flagsCountdown[CivFlags.RecentlyBullied.name]
|
||||
@Readonly fun getRecentBullyingCountdown() = flagsCountdown[CivFlags.RecentlyBullied.name]
|
||||
fun getTurnsTillCallForBarbHelp() = flagsCountdown[CivFlags.TurnsTillCallForBarbHelp.name]
|
||||
|
||||
fun mayVoteForDiplomaticVictory() =
|
||||
@ -1046,7 +1051,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
|
||||
fun getAllyCiv(): Civilization? = if (allyCivName == null) null
|
||||
else gameInfo.getCivilization(allyCivName!!)
|
||||
@Readonly @Suppress("purity") // should be autorecognized!
|
||||
@Readonly @Suppress("purity") // should be autorecognized
|
||||
fun getAllyCivName() = allyCivName
|
||||
fun setAllyCiv(newAllyName: String?) { allyCivName = newAllyName }
|
||||
|
||||
|
@ -27,6 +27,7 @@ import com.unciv.models.ruleset.unit.BaseUnit
|
||||
import com.unciv.models.stats.Stat
|
||||
import com.unciv.ui.screens.victoryscreen.RankingType
|
||||
import com.unciv.utils.randomWeighted
|
||||
import yairm210.purity.annotations.LocalState
|
||||
import yairm210.purity.annotations.Readonly
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
@ -214,6 +215,7 @@ class CityStateFunctions(val civInfo: Civilization) {
|
||||
civInfo.questManager.receivedGoldGift(donorCiv)
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun getProtectorCivs() : List<Civilization> {
|
||||
if(civInfo.isMajorCiv()) return emptyList()
|
||||
return civInfo.diplomacy.values
|
||||
@ -414,8 +416,9 @@ class CityStateFunctions(val civInfo: Civilization) {
|
||||
return getTributeModifiers(demandingCiv, demandingWorker).values.sum()
|
||||
}
|
||||
|
||||
@Readonly @Suppress("purity") // Local state update
|
||||
@Readonly
|
||||
fun getTributeModifiers(demandingCiv: Civilization, demandingWorker: Boolean = false, requireWholeList: Boolean = false): HashMap<String, Int> {
|
||||
@LocalState
|
||||
val modifiers = LinkedHashMap<String, Int>() // Linked to preserve order when presenting the modifiers table
|
||||
// Can't bully major civs or unsettled CS's
|
||||
if (!civInfo.isCityState) {
|
||||
|
@ -198,10 +198,9 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
|
||||
//region pure functions
|
||||
@Readonly
|
||||
fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName)
|
||||
@Readonly
|
||||
fun otherCivDiplomacy() = otherCiv().getDiplomacyManager(civInfo)!!
|
||||
|
||||
@Readonly fun otherCiv() = civInfo.gameInfo.getCivilization(otherCivName)
|
||||
@Readonly fun otherCivDiplomacy() = otherCiv().getDiplomacyManager(civInfo)!!
|
||||
|
||||
fun turnsToPeaceTreaty(): Int {
|
||||
for (trade in trades)
|
||||
|
@ -96,7 +96,7 @@ class TechManager : IsPartOfGameInfoSerialization {
|
||||
return toReturn
|
||||
}
|
||||
|
||||
fun getNumberOfTechsResearched(): Int = techsResearched.size
|
||||
@Readonly fun getNumberOfTechsResearched(): Int = techsResearched.size
|
||||
|
||||
fun getOverflowScience(): Int = overflowScience
|
||||
|
||||
|
@ -127,10 +127,10 @@ class UnitManager(val civInfo: Civilization) {
|
||||
}
|
||||
return unit
|
||||
}
|
||||
@Readonly
|
||||
fun getCivUnitsSize(): Int = unitList.size
|
||||
fun getCivUnits(): Sequence<MapUnit> = unitList.asSequence()
|
||||
fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.isGreatPerson() }
|
||||
|
||||
@Readonly fun getCivUnitsSize(): Int = unitList.size
|
||||
@Readonly fun getCivUnits(): Sequence<MapUnit> = unitList.asSequence()
|
||||
@Readonly fun getCivGreatPeople(): Sequence<MapUnit> = getCivUnits().filter { mapUnit -> mapUnit.isGreatPerson() }
|
||||
|
||||
// Similar to getCivUnits(), but the returned list is rotated so that the
|
||||
// 'nextPotentiallyDueAt' unit is first here.
|
||||
|
@ -4,6 +4,7 @@ import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||
import com.unciv.logic.map.HexMath.getNumberOfTilesInHexagon
|
||||
import com.unciv.logic.map.mapgenerator.MapResourceSetting
|
||||
import com.unciv.models.metadata.BaseRuleset
|
||||
import yairm210.purity.annotations.Readonly
|
||||
|
||||
|
||||
object MapShape {
|
||||
@ -172,6 +173,7 @@ class MapParameters : IsPartOfGameInfoSerialization {
|
||||
yield(", {Water level}=" + waterThreshold.niceToString(2))
|
||||
}.joinToString("")
|
||||
|
||||
@Readonly
|
||||
fun numberOfTiles() =
|
||||
if (shape == MapShape.hexagonal || shape == MapShape.flatEarth) {
|
||||
1 + 3 * mapSize.radius * (mapSize.radius - 1)
|
||||
|
@ -497,7 +497,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
||||
// Only military land units can truly "garrison"
|
||||
fun canGarrison() = isMilitary() && baseUnit.isLandUnit
|
||||
|
||||
fun isGreatPerson() = baseUnit.isGreatPerson
|
||||
@Readonly fun isGreatPerson() = baseUnit.isGreatPerson
|
||||
fun isGreatPersonOfType(type: String) = baseUnit.isGreatPersonOfType(type)
|
||||
|
||||
fun canIntercept(attackedTile: Tile): Boolean {
|
||||
@ -621,6 +621,7 @@ class MapUnit : IsPartOfGameInfoSerialization {
|
||||
return civ.gameInfo.religions[religion]!!.getReligionDisplayName()
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun getForceEvaluation(): Int {
|
||||
val promotionBonus = (promotions.numberOfPromotions + 1).toFloat().pow(0.3f)
|
||||
var power = (baseUnit.getForceEvaluation() * promotionBonus).toInt()
|
||||
|
@ -261,7 +261,6 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable {
|
||||
return null
|
||||
}
|
||||
|
||||
@Readonly @Suppress("purity") // should be autorecognized as readonly
|
||||
fun getCity(): City? = owningCity
|
||||
|
||||
@Readonly internal fun getNaturalWonder(): Terrain =
|
||||
@ -282,7 +281,6 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable {
|
||||
return exploredBy.contains(player.civName)
|
||||
}
|
||||
|
||||
@Readonly @Suppress("purity") // should be autorecognized as readonly
|
||||
fun isCityCenter(): Boolean = isCityCenterInternal
|
||||
@Readonly fun isNaturalWonder(): Boolean = naturalWonder != null
|
||||
@Readonly fun isImpassible() = lastTerrain.impassable
|
||||
@ -583,7 +581,7 @@ class Tile : IsPartOfGameInfoSerialization, Json.Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
@Readonly @Suppress("purity") // should be auto-recognized!
|
||||
@Readonly @Suppress("purity") // should be autorecognized
|
||||
fun isCoastalTile() = _isCoastalTile
|
||||
|
||||
@Readonly
|
||||
@ -743,7 +741,6 @@ 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 */
|
||||
|
@ -8,6 +8,7 @@ import com.unciv.models.ruleset.unique.UniqueType
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.objectdescriptions.uniquesToCivilopediaTextLines
|
||||
import com.unciv.ui.screens.civilopediascreen.FormattedLine
|
||||
import yairm210.purity.annotations.Pure
|
||||
|
||||
open class Policy : RulesetObject() {
|
||||
lateinit var branch: PolicyBranch // not in json - added in gameBasics
|
||||
@ -32,6 +33,7 @@ open class Policy : RulesetObject() {
|
||||
/** Some tests to count policies by completion or not use only the String collection without instantiating them.
|
||||
* To keep the hardcoding in one place, this is public and should be used instead of duplicating it.
|
||||
*/
|
||||
@Pure
|
||||
fun isBranchCompleteByName(name: String) = name.endsWith(branchCompleteSuffix)
|
||||
}
|
||||
|
||||
|
@ -48,12 +48,12 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
|
||||
fun hasFlag(flag: UniqueFlag) = type != null && type.flags.contains(flag)
|
||||
fun isHiddenToUsers() = hasFlag(UniqueFlag.HiddenToUsers) || hasModifier(UniqueType.ModifierHiddenFromUsers)
|
||||
|
||||
|
||||
@Readonly fun getModifiers(type: UniqueType) = modifiersMap[type] ?: emptyList()
|
||||
@Readonly fun hasModifier(type: UniqueType) = modifiersMap.containsKey(type)
|
||||
@Readonly fun isModifiedByGameSpeed() = hasModifier(UniqueType.ModifiedByGameSpeed)
|
||||
@Readonly fun isModifiedByGameProgress() = hasModifier(UniqueType.ModifiedByGameProgress)
|
||||
@Readonly
|
||||
fun getModifiers(type: UniqueType) = modifiersMap[type] ?: emptyList()
|
||||
@Readonly
|
||||
fun hasModifier(type: UniqueType) = modifiersMap.containsKey(type)
|
||||
fun isModifiedByGameSpeed() = hasModifier(UniqueType.ModifiedByGameSpeed)
|
||||
fun isModifiedByGameProgress() = hasModifier(UniqueType.ModifiedByGameProgress)
|
||||
fun getGameProgressModifier(civ: Civilization): Float {
|
||||
//According to: https://www.reddit.com/r/civ/comments/gvx44v/comment/fsrifc2/
|
||||
var modifier = 1f
|
||||
@ -68,6 +68,7 @@ class Unique(val text: String, val sourceObjectType: UniqueTarget? = null, val s
|
||||
//Mod creators likely expect this to stack multiplicatively, otherwise they'd use a single modifier
|
||||
return modifier
|
||||
}
|
||||
@Readonly
|
||||
fun hasTriggerConditional(): Boolean {
|
||||
if (modifiers.none()) return false
|
||||
return modifiers.any { conditional ->
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.unciv.models.ruleset.unique
|
||||
|
||||
import yairm210.purity.annotations.Readonly
|
||||
|
||||
/**
|
||||
* Expresses which RulesetObject types a UniqueType is applicable to.
|
||||
*
|
||||
@ -78,6 +80,7 @@ enum class UniqueTarget(
|
||||
/** Checks whether a specific UniqueTarget `this` as e.g. given by [IHasUniques.getUniqueTarget] works with [uniqueTarget] as e.g. declared in UniqueType */
|
||||
// Building.canAcceptUniqueTarget(Global) == true
|
||||
// Global.canAcceptUniqueTarget(Building) == false
|
||||
@Readonly
|
||||
fun canAcceptUniqueTarget(uniqueTarget: UniqueTarget): Boolean {
|
||||
if (this == uniqueTarget) return true
|
||||
if (inheritsFrom != null) return inheritsFrom.canAcceptUniqueTarget(uniqueTarget)
|
||||
|
@ -136,30 +136,35 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
return unit
|
||||
}
|
||||
|
||||
@Readonly
|
||||
override fun hasUnique(uniqueType: UniqueType, state: GameContext?): Boolean {
|
||||
val gameContext = state ?: GameContext.EmptyState
|
||||
return if (::ruleset.isInitialized) rulesetUniqueMap.hasUnique(uniqueType, gameContext)
|
||||
else super<RulesetObject>.hasUnique(uniqueType, gameContext)
|
||||
}
|
||||
|
||||
@Readonly
|
||||
override fun hasUnique(uniqueTag: String, state: GameContext?): Boolean {
|
||||
val gameContext = state ?: GameContext.EmptyState
|
||||
return if (::ruleset.isInitialized) rulesetUniqueMap.hasUnique(uniqueTag, gameContext)
|
||||
else super<RulesetObject>.hasUnique(uniqueTag, gameContext)
|
||||
}
|
||||
|
||||
@Readonly
|
||||
override fun hasTagUnique(tagUnique: String): Boolean {
|
||||
return if (::ruleset.isInitialized) rulesetUniqueMap.hasTagUnique(tagUnique)
|
||||
else super<RulesetObject>.hasTagUnique(tagUnique)
|
||||
}
|
||||
|
||||
/** Allows unique functions (getMatchingUniques, hasUnique) to "see" uniques from the UnitType */
|
||||
@Readonly
|
||||
override fun getMatchingUniques(uniqueType: UniqueType, state: GameContext): Sequence<Unique> {
|
||||
return if (::ruleset.isInitialized) rulesetUniqueMap.getMatchingUniques(uniqueType, state)
|
||||
else super<RulesetObject>.getMatchingUniques(uniqueType, state)
|
||||
}
|
||||
|
||||
/** Allows unique functions (getMatchingUniques, hasUnique) to "see" uniques from the UnitType */
|
||||
@Readonly
|
||||
override fun getMatchingUniques(uniqueTag: String, state: GameContext): Sequence<Unique> {
|
||||
return if (::ruleset.isInitialized) rulesetUniqueMap.getMatchingUniques(uniqueTag, state)
|
||||
else super<RulesetObject>.getMatchingUniques(uniqueTag, state)
|
||||
@ -459,7 +464,7 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
fun isGreatPersonOfType(type: String) = getMatchingUniques(UniqueType.GreatPerson).any { it.params[0] == type }
|
||||
|
||||
/** Has a MapUnit implementation that does not ignore conditionals, which should be usually used */
|
||||
private fun isNuclearWeapon() = hasUnique(UniqueType.NuclearWeapon, GameContext.IgnoreConditionals)
|
||||
@Readonly private fun isNuclearWeapon() = hasUnique(UniqueType.NuclearWeapon, GameContext.IgnoreConditionals)
|
||||
|
||||
val movesLikeAirUnits by lazy { type.getMovementType() == UnitMovementType.Air }
|
||||
|
||||
@ -486,11 +491,13 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction {
|
||||
&& getMatchingUniques(UniqueType.Strength, GameContext.IgnoreConditionals)
|
||||
.any { it.params[0].toInt() > 0 && it.hasModifier(UniqueType.ConditionalVsCity) }
|
||||
|
||||
@Readonly
|
||||
fun getForceEvaluation(): Int {
|
||||
if (cachedForceEvaluation < 0) evaluateForce()
|
||||
return cachedForceEvaluation
|
||||
}
|
||||
|
||||
@Readonly @Suppress("purity") // reads from cache
|
||||
private fun evaluateForce() {
|
||||
if (strength == 0 && rangedStrength == 0) {
|
||||
cachedForceEvaluation = 0
|
||||
|
@ -14,6 +14,7 @@ import com.unciv.utils.Log
|
||||
import com.unciv.utils.debug
|
||||
import java.util.Locale
|
||||
import org.jetbrains.annotations.VisibleForTesting
|
||||
import yairm210.purity.annotations.LocalState
|
||||
import yairm210.purity.annotations.Pure
|
||||
import yairm210.purity.annotations.Readonly
|
||||
|
||||
@ -474,12 +475,13 @@ private fun String.translateIndividualWord(language: String, hideIcons: Boolean,
|
||||
* For example, a string like 'The city of [New [York]]' will return ['New [York]'],
|
||||
* allowing us to have nested translations!
|
||||
*/
|
||||
@Readonly @Suppress("purity") // Local state update
|
||||
@Readonly
|
||||
fun String.getPlaceholderParameters(): List<String> {
|
||||
if (!this.contains('[')) return emptyList()
|
||||
|
||||
val stringToParse = this.removeConditionals()
|
||||
|
||||
@LocalState
|
||||
val parameters = ArrayList<String>()
|
||||
var depthOfBraces = 0
|
||||
var startOfCurrentParameter = -1
|
||||
|
@ -3,6 +3,7 @@ package com.unciv.ui.components.extensions
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.unciv.models.translations.tr
|
||||
import com.unciv.ui.components.fonts.Fonts
|
||||
import yairm210.purity.annotations.Pure
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.Duration
|
||||
import java.time.temporal.ChronoUnit
|
||||
@ -11,13 +12,13 @@ import java.util.Locale
|
||||
import java.util.SortedMap
|
||||
|
||||
/** Translate a percentage number - e.g. 25 - to the multiplication value - e.g. 1.25f */
|
||||
fun String.toPercent() = toFloat().toPercent()
|
||||
@Pure fun String.toPercent() = toFloat().toPercent()
|
||||
|
||||
/** Translate a percentage number - e.g. 25 - to the multiplication value - e.g. 1.25f */
|
||||
fun Int.toPercent() = toFloat().toPercent()
|
||||
@Pure fun Int.toPercent() = toFloat().toPercent()
|
||||
|
||||
/** Translate a percentage number - e.g. 25 - to the multiplication value - e.g. 1.25f */
|
||||
fun Float.toPercent() = 1 + this/100
|
||||
@Pure fun Float.toPercent() = 1 + this/100
|
||||
|
||||
/** Convert a [resource name][this] into "Consumes [amount] $resource" string (untranslated) */
|
||||
fun String.getConsumesAmountString(amount: Int, isStockpiled: Boolean): String {
|
||||
|
Loading…
x
Reference in New Issue
Block a user