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