diff --git a/build.gradle.kts b/build.gradle.kts index 44a1d0e9f8..bba4baadd6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,6 +59,8 @@ allprojects { "com.badlogic.gdx.math.Vector2.len", "com.badlogic.gdx.math.Vector2.cpy", "java.util.AbstractCollection.contains", + "java.util.AbstractCollection.isEmpty", + "java.util.AbstractCollection.iterator", "java.util.AbstractList.get", ) wellKnownPureClasses = setOf( diff --git a/core/src/com/unciv/logic/city/City.kt b/core/src/com/unciv/logic/city/City.kt index 4695d60fcd..1956f11757 100644 --- a/core/src/com/unciv/logic/city/City.kt +++ b/core/src/com/unciv/logic/city/City.kt @@ -28,6 +28,7 @@ import com.unciv.models.stats.GameResource import com.unciv.models.stats.INamed import com.unciv.models.stats.Stat import com.unciv.models.stats.SubStat +import yairm210.purity.annotations.LocalState import yairm210.purity.annotations.Readonly import java.util.UUID import kotlin.math.roundToInt @@ -105,7 +106,7 @@ class City : IsPartOfGameInfoSerialization, INamed { } private var cityAIFocus: String = CityFocus.NoFocus.name - fun getCityFocus() = CityFocus.entries.firstOrNull { it.name == cityAIFocus } ?: CityFocus.NoFocus + @Readonly fun getCityFocus() = CityFocus.entries.firstOrNull { it.name == cityAIFocus } ?: CityFocus.NoFocus fun setCityFocus(cityFocus: CityFocus){ cityAIFocus = cityFocus.name } @@ -130,7 +131,7 @@ class City : IsPartOfGameInfoSerialization, INamed { enum class ConnectedToCapitalStatus { Unknown, `false`, `true` } var connectedToCapitalStatus = ConnectedToCapitalStatus.Unknown - fun hasDiplomaticMarriage(): Boolean = foundingCiv == "" + @Readonly fun hasDiplomaticMarriage(): Boolean = foundingCiv == "" //region pure functions fun clone(): City { @@ -181,12 +182,14 @@ class City : IsPartOfGameInfoSerialization, INamed { @Readonly fun getWorkRange(): Int = civ.gameInfo.ruleset.modOptions.constants.cityWorkRange @Readonly fun getExpandRange(): Int = civ.gameInfo.ruleset.modOptions.constants.cityExpandRange + @Readonly @Suppress("purity") // Activates predicate fun isConnectedToCapital(connectionTypePredicate: (Set) -> Boolean = { true }): Boolean { val mediumTypes = civ.cache.citiesConnectedToCapitalToMediums[this] ?: return false return connectionTypePredicate(mediumTypes) } - fun isGarrisoned() = getGarrison() != null + @Readonly fun isGarrisoned() = getGarrison() != null + @Readonly fun getGarrison(): MapUnit? = getCenterTile().militaryUnit?.takeIf { it.civ == this.civ && it.canGarrison() @@ -195,7 +198,7 @@ class City : IsPartOfGameInfoSerialization, INamed { @Readonly fun hasFlag(flag: CityFlags) = flagsCountdown.containsKey(flag.name) @Readonly fun getFlag(flag: CityFlags) = flagsCountdown[flag.name]!! - fun isWeLoveTheKingDayActive() = hasFlag(CityFlags.WeLoveTheKing) + @Readonly fun isWeLoveTheKingDayActive() = hasFlag(CityFlags.WeLoveTheKing) @Readonly fun isInResistance() = hasFlag(CityFlags.Resistance) fun isBlockaded(): Boolean { // Coastal cities are blocked if every adjacent water tile is blocked @@ -205,22 +208,22 @@ class City : IsPartOfGameInfoSerialization, INamed { } } - fun getRuleset() = civ.gameInfo.ruleset + @Readonly fun getRuleset() = civ.gameInfo.ruleset fun getResourcesGeneratedByCity(civResourceModifiers: HashMap) = CityResources.getResourcesGeneratedByCity(this, civResourceModifiers) fun getAvailableResourceAmount(resourceName: String) = CityResources.getAvailableResourceAmount(this, resourceName) - fun isGrowing() = foodForNextTurn() > 0 - fun isStarving() = foodForNextTurn() < 0 - - fun foodForNextTurn() = cityStats.currentCityStats.food.roundToInt() + @Readonly fun isGrowing() = foodForNextTurn() > 0 + @Readonly fun isStarving() = foodForNextTurn() < 0 + @Readonly fun foodForNextTurn() = cityStats.currentCityStats.food.roundToInt() + @Readonly fun containsBuildingUnique(uniqueType: UniqueType, state: GameContext = this.state) = cityConstructions.builtBuildingUniqueMap.getMatchingUniques(uniqueType, state).any() - fun getGreatPersonPercentageBonus() = GreatPersonPointsBreakdown.getGreatPersonPercentageBonus(this) - fun getGreatPersonPoints() = GreatPersonPointsBreakdown(this).sum() + @Readonly fun getGreatPersonPercentageBonus() = GreatPersonPointsBreakdown.getGreatPersonPercentageBonus(this) + @Readonly fun getGreatPersonPoints() = GreatPersonPointsBreakdown(this).sum() fun gainStockpiledResource(resource: TileResource, amount: Int) { if (resource.isCityWide) resourceStockpiles.add(resource.name, amount) @@ -247,7 +250,8 @@ class City : IsPartOfGameInfoSerialization, INamed { else -> civ.addGameResource(stat, amount) } } - + + @Readonly fun getStatReserve(stat: Stat): Int { return when (stat) { Stat.Production -> cityConstructions.getWorkDone(cityConstructions.getCurrentConstruction().name) @@ -269,6 +273,7 @@ class City : IsPartOfGameInfoSerialization, INamed { } } + @Readonly fun hasStatToBuy(stat: Stat, price: Int): Boolean { return when { civ.gameInfo.gameParameters.godMode -> true @@ -277,8 +282,7 @@ class City : IsPartOfGameInfoSerialization, INamed { } } - internal fun getMaxHealth() = - 200 + cityConstructions.getBuiltBuildings().sumOf { it.cityHealth } + @Readonly internal fun getMaxHealth() = 200 + cityConstructions.getBuiltBuildings().sumOf { it.cityHealth } @Readonly fun getStrength() = cityConstructions.getBuiltBuildings().sumOf { it.cityStrength }.toFloat() @@ -290,9 +294,10 @@ class City : IsPartOfGameInfoSerialization, INamed { override fun toString() = name // for debug - fun isHolyCity(): Boolean = religion.religionThisIsTheHolyCityOf != null && !religion.isBlockedHolyCity - fun isHolyCityOf(religionName: String?) = isHolyCity() && religion.religionThisIsTheHolyCityOf == religionName + @Readonly fun isHolyCity(): Boolean = religion.religionThisIsTheHolyCityOf != null && !religion.isBlockedHolyCity + @Readonly fun isHolyCityOf(religionName: String?) = isHolyCity() && religion.religionThisIsTheHolyCityOf == religionName + @Readonly fun canBeDestroyed(justCaptured: Boolean = false): Boolean { if (civ.gameInfo.gameParameters.noCityRazing) return false @@ -440,6 +445,7 @@ class City : IsPartOfGameInfoSerialization, INamed { getCenterTile().setRoadStatus(requiredRoad, civ) } + @Readonly fun getGoldForSellingBuilding(buildingName: String) = getRuleset().buildings[buildingName]!!.cost / 10 @@ -467,12 +473,14 @@ class City : IsPartOfGameInfoSerialization, INamed { } /** Implements [UniqueParameterType.CityFilter][com.unciv.models.ruleset.unique.UniqueParameterType.CityFilter] */ + @Readonly fun matchesFilter(filter: String, viewingCiv: Civilization? = civ, multiFilter: Boolean = true): Boolean { return if (multiFilter) MultiFilter.multiFilter(filter, { matchesSingleFilter(it, viewingCiv) }) else matchesSingleFilter(filter, viewingCiv) } + @Readonly private fun matchesSingleFilter(filter: String, viewingCiv: Civilization? = civ): Boolean { return when (filter) { "in this city" -> true // Filtered by the way uniques are found @@ -521,6 +529,7 @@ class City : IsPartOfGameInfoSerialization, INamed { // Sadly, due to the large disparity between use cases, there needed to be lots of functions. // Finds matching uniques provided from both local and non-local sources. + @Readonly fun getMatchingUniques( uniqueType: UniqueType, gameContext: GameContext = state, @@ -538,6 +547,7 @@ class City : IsPartOfGameInfoSerialization, INamed { } // Uniques special to this city + @Readonly fun getLocalMatchingUniques(uniqueType: UniqueType, gameContext: GameContext = state): Sequence { val uniques = cityConstructions.builtBuildingUniqueMap.getUniques(uniqueType).filter { it.isLocalEffect } + religion.getUniques(uniqueType) diff --git a/core/src/com/unciv/logic/city/CityConstructions.kt b/core/src/com/unciv/logic/city/CityConstructions.kt index b1a880da1f..b50ad31023 100644 --- a/core/src/com/unciv/logic/city/CityConstructions.kt +++ b/core/src/com/unciv/logic/city/CityConstructions.kt @@ -200,7 +200,7 @@ class CityConstructions : IsPartOfGameInfoSerialization { else FormattedLine(label, link="$category/$currentConstructionSnapshot") } - fun getCurrentConstruction(): IConstruction = getConstruction(currentConstructionFromQueue) + @Readonly fun getCurrentConstruction(): IConstruction = getConstruction(currentConstructionFromQueue) fun isBuilt(buildingName: String): Boolean = builtBuildings.contains(buildingName) @@ -238,6 +238,7 @@ class CityConstructions : IsPartOfGameInfoSerialization { } + @Readonly internal fun getConstruction(constructionName: String): IConstruction { val gameBasics = city.getRuleset() when { @@ -259,6 +260,7 @@ class CityConstructions : IsPartOfGameInfoSerialization { fun containsBuildingOrEquivalent(buildingNameOrUnique: String): Boolean = isBuilt(buildingNameOrUnique) || getBuiltBuildings().any { it.replaces == buildingNameOrUnique || it.hasUnique(buildingNameOrUnique, city.state) } + @Readonly fun getWorkDone(constructionName: String): Int { return if (inProgressConstructions.containsKey(constructionName)) inProgressConstructions[constructionName]!! else 0 diff --git a/core/src/com/unciv/logic/city/CityResources.kt b/core/src/com/unciv/logic/city/CityResources.kt index 23daf21b43..0bfa6f5b1e 100644 --- a/core/src/com/unciv/logic/city/CityResources.kt +++ b/core/src/com/unciv/logic/city/CityResources.kt @@ -5,6 +5,7 @@ import com.unciv.models.ruleset.tile.ResourceSupplyList import com.unciv.models.ruleset.tile.ResourceType import com.unciv.models.ruleset.unique.GameContext import com.unciv.models.ruleset.unique.UniqueType +import yairm210.purity.annotations.Readonly object CityResources { diff --git a/core/src/com/unciv/logic/city/CityStats.kt b/core/src/com/unciv/logic/city/CityStats.kt index dd602213ad..94713a55ff 100644 --- a/core/src/com/unciv/logic/city/CityStats.kt +++ b/core/src/com/unciv/logic/city/CityStats.kt @@ -15,6 +15,7 @@ import com.unciv.models.stats.StatMap import com.unciv.models.stats.Stats import com.unciv.ui.components.extensions.toPercent import com.unciv.utils.DebugUtils +import yairm210.purity.annotations.Readonly import kotlin.math.min @@ -172,6 +173,7 @@ class CityStats(val city: City) { return growthSources } + @Readonly fun hasExtraAnnexUnhappiness(): Boolean { if (city.civ.civName == city.foundingCiv || city.isPuppet) return false return !city.containsBuildingUnique(UniqueType.RemoveAnnexUnhappiness) diff --git a/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt b/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt index 070ba04b4d..5868d717a9 100644 --- a/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt +++ b/core/src/com/unciv/logic/city/GreatPersonPointsBreakdown.kt @@ -5,6 +5,8 @@ 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.LocalState +import yairm210.purity.annotations.Readonly /** Manages calculating Great Person Points per City for nextTurn. See public constructor(city) below for details. */ class GreatPersonPointsBreakdown private constructor(private val ruleset: Ruleset) { @@ -38,8 +40,9 @@ class GreatPersonPointsBreakdown private constructor(private val ruleset: Rulese const val fixedPointFactor = 1000 - private fun getUniqueSourceName(unique: Unique) = unique.sourceObjectName ?: "Bonus" + @Readonly private fun getUniqueSourceName(unique: Unique) = unique.sourceObjectName ?: "Bonus" + @Readonly private fun guessPediaLink(unique: Unique): String? { if (unique.sourceObjectName == null) return null return unique.sourceObjectType!!.name + "/" + unique.sourceObjectName @@ -50,6 +53,7 @@ class GreatPersonPointsBreakdown private constructor(private val ruleset: Rulese * This is used internally from the public constructor to include them in the brakdown, * and exposed to autoAssignPopulation via [getGreatPersonPercentageBonus] */ + @Readonly private fun getPercentagesApplyingToAllGP(city: City) = sequence { // Now add boni for GreatPersonPointPercentage for (unique in city.getMatchingUniques(UniqueType.GreatPersonPointPercentage)) { @@ -73,6 +77,7 @@ class GreatPersonPointsBreakdown private constructor(private val ruleset: Rulese * * For use by [City.getGreatPersonPercentageBonus] (which in turn is only used by autoAssignPopulation) */ + @Readonly fun getGreatPersonPercentageBonus(city: City) = getPercentagesApplyingToAllGP(city).sumOf { it.bonus } } @@ -126,13 +131,16 @@ class GreatPersonPointsBreakdown private constructor(private val ruleset: Rulese } /** Aggregate over sources, applying percentage boni using fixed-point math to avoid rounding surprises */ + @Readonly fun sum(): Counter { // Accumulate base points as fake "fixed-point" + @LocalState val result = Counter() for (entry in basePoints) result.add(entry.counter * fixedPointFactor) // Accumulate percentage bonuses additively not multiplicatively + @LocalState val bonuses = Counter() for (entry in percentBonuses) { bonuses.add(entry.counter) diff --git a/core/src/com/unciv/logic/city/managers/CityReligionManager.kt b/core/src/com/unciv/logic/city/managers/CityReligionManager.kt index f684ad3ca9..f059e48e65 100644 --- a/core/src/com/unciv/logic/city/managers/CityReligionManager.kt +++ b/core/src/com/unciv/logic/city/managers/CityReligionManager.kt @@ -10,6 +10,7 @@ import com.unciv.models.Religion import com.unciv.models.ruleset.unique.Unique import com.unciv.models.ruleset.unique.UniqueType import com.unciv.ui.components.extensions.toPercent +import yairm210.purity.annotations.Readonly class CityReligionManager : IsPartOfGameInfoSerialization { @Transient @@ -57,6 +58,7 @@ class CityReligionManager : IsPartOfGameInfoSerialization { getAffectedBySurroundingCities() } + @Readonly fun getUniques(uniqueType: UniqueType): Sequence { val majorityReligion = getMajorityReligion() ?: return emptySequence() return majorityReligion.followerBeliefUniqueMap.getUniques(uniqueType) @@ -223,6 +225,7 @@ class CityReligionManager : IsPartOfGameInfoSerialization { updateNumberOfFollowers() } + @Readonly fun getMajorityReligionName(): String? { if (followers.isEmpty()) return null val religionWithMaxPressure = followers.maxByOrNull { it.value }!!.key @@ -233,6 +236,7 @@ class CityReligionManager : IsPartOfGameInfoSerialization { } } + @Readonly fun getMajorityReligion(): Religion? { val majorityReligionName = getMajorityReligionName() ?: return null return city.civ.gameInfo.religions[majorityReligionName] diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index 61a91c7f30..577e928813 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -909,6 +909,7 @@ class Civilization : IsPartOfGameInfoSerialization { resourceStockpiles.add(resource.name, amount) } + @Readonly fun getStatReserve(stat: Stat): Int { return when (stat) { Stat.Culture -> policies.storedCulture diff --git a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt index 3d0f5977b1..02a39169a9 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/DiplomacyManager.kt @@ -538,12 +538,12 @@ class DiplomacyManager() : IsPartOfGameInfoSerialization { } } - fun hasFlag(flag: DiplomacyFlags) = flagsCountdown.containsKey(flag.name) + @Readonly fun hasFlag(flag: DiplomacyFlags) = flagsCountdown.containsKey(flag.name) fun setFlag(flag: DiplomacyFlags, amount: Int) { flagsCountdown[flag.name] = amount } - fun getFlag(flag: DiplomacyFlags) = flagsCountdown[flag.name]!! + @Readonly fun getFlag(flag: DiplomacyFlags) = flagsCountdown[flag.name]!! fun removeFlag(flag: DiplomacyFlags) { flagsCountdown.remove(flag.name) } diff --git a/core/src/com/unciv/logic/civilization/managers/TechManager.kt b/core/src/com/unciv/logic/civilization/managers/TechManager.kt index a59993ec3d..b4b0e2ce6e 100644 --- a/core/src/com/unciv/logic/civilization/managers/TechManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/TechManager.kt @@ -108,8 +108,7 @@ class TechManager : IsPartOfGameInfoSerialization { return 1 + numberOfCivsResearchedThisTech / numberOfCivsRemaining.toFloat() * 0.3f } - @Readonly - private fun getRuleset() = civInfo.gameInfo.ruleset + @Readonly private fun getRuleset() = civInfo.gameInfo.ruleset fun costOfTech(techName: String): Int { var techCost = getRuleset().technologies[techName]!!.cost.toFloat() @@ -126,16 +125,18 @@ class TechManager : IsPartOfGameInfoSerialization { return techCost.toInt() } + @Readonly fun currentTechnology(): Technology? { val currentTechnologyName = currentTechnologyName() ?: return null return getRuleset().technologies[currentTechnologyName] } + @Readonly fun currentTechnologyName(): String? { return if (techsToResearch.isEmpty()) null else techsToResearch[0] } - fun researchOfTech(techName: String?) = techsInProgress[techName] ?: 0 + @Readonly fun researchOfTech(techName: String?) = techsInProgress[techName] ?: 0 // Was once duplicated as fun scienceSpentOnTech(tech: String): Int fun remainingScienceToTech(techName: String): Int { diff --git a/core/src/com/unciv/logic/map/mapunit/MapUnit.kt b/core/src/com/unciv/logic/map/mapunit/MapUnit.kt index 59d95d166b..31b75ebd2b 100644 --- a/core/src/com/unciv/logic/map/mapunit/MapUnit.kt +++ b/core/src/com/unciv/logic/map/mapunit/MapUnit.kt @@ -238,14 +238,15 @@ class MapUnit : IsPartOfGameInfoSerialization { it.getCenterTile().aerialDistanceTo(currentTile) } - fun isMilitary() = baseUnit.isMilitary - fun isCivilian() = baseUnit.isCivilian() + @Readonly fun isMilitary() = baseUnit.isMilitary + @Readonly fun isCivilian() = baseUnit.isCivilian() - fun isActionUntilHealed() = action?.endsWith("until healed") == true + @Readonly fun isActionUntilHealed() = action?.endsWith("until healed") == true - fun isFortified() = action?.startsWith(UnitActionType.Fortify.value) == true - fun isGuarding() = action?.equals(UnitActionType.Guard.value) == true - fun isFortifyingUntilHealed() = isFortified() && isActionUntilHealed() + @Readonly fun isFortified() = action?.startsWith(UnitActionType.Fortify.value) == true + @Readonly fun isGuarding() = action?.equals(UnitActionType.Guard.value) == true + @Readonly fun isFortifyingUntilHealed() = isFortified() && isActionUntilHealed() + @Readonly fun getFortificationTurns(): Int { if (!(isFortified() || isGuarding())) return 0 return turnsFortified @@ -495,7 +496,7 @@ class MapUnit : IsPartOfGameInfoSerialization { } // Only military land units can truly "garrison" - fun canGarrison() = isMilitary() && baseUnit.isLandUnit + @Readonly fun canGarrison() = isMilitary() && baseUnit.isLandUnit @Readonly fun isGreatPerson() = baseUnit.isGreatPerson fun isGreatPersonOfType(type: String) = baseUnit.isGreatPersonOfType(type) diff --git a/core/src/com/unciv/models/Counter.kt b/core/src/com/unciv/models/Counter.kt index 3aaadea4b2..8efd43d167 100644 --- a/core/src/com/unciv/models/Counter.kt +++ b/core/src/com/unciv/models/Counter.kt @@ -3,6 +3,7 @@ package com.unciv.models import com.badlogic.gdx.utils.Json import com.badlogic.gdx.utils.JsonValue import com.unciv.logic.IsPartOfGameInfoSerialization +import yairm210.purity.annotations.Readonly /** * Implements a specialized Map storing on-zero Integers. @@ -47,6 +48,8 @@ open class Counter( } operator fun minusAssign(other: Counter) = remove(other) + @Readonly + /** Creates a new instance (does not modify) */ operator fun times(amount: Int): Counter { val newCounter = Counter() for (key in keys) newCounter[key] = this[key] * amount diff --git a/core/src/com/unciv/models/Religion.kt b/core/src/com/unciv/models/Religion.kt index 40c5cdc9ae..68a7fa4c0b 100644 --- a/core/src/com/unciv/models/Religion.kt +++ b/core/src/com/unciv/models/Religion.kt @@ -10,6 +10,7 @@ import com.unciv.models.ruleset.unique.GameContext import com.unciv.models.ruleset.unique.UniqueMap import com.unciv.models.ruleset.unique.UniqueType import com.unciv.models.stats.INamed +import yairm210.purity.annotations.Readonly /** Data object for Religions */ class Religion() : INamed, IsPartOfGameInfoSerialization { @@ -77,11 +78,13 @@ class Religion() : INamed, IsPartOfGameInfoSerialization { if (displayName != null) displayName!! else name + @Readonly private fun mapToExistingBeliefs(beliefs: Set): Sequence { val rulesetBeliefs = gameInfo.ruleset.beliefs return beliefs.asSequence().mapNotNull { rulesetBeliefs[it] } } + @Readonly fun getBeliefs(beliefType: BeliefType): Sequence { if (beliefType == BeliefType.Any) return mapToExistingBeliefs((founderBeliefs + followerBeliefs).toHashSet()) @@ -104,20 +107,20 @@ class Religion() : INamed, IsPartOfGameInfoSerialization { mapToExistingBeliefs(founderBeliefs).filter { it.type == BeliefType.Enhancer } } - fun hasBelief(belief: String) = followerBeliefs.contains(belief) || founderBeliefs.contains(belief) + @Readonly fun hasBelief(belief: String) = followerBeliefs.contains(belief) || founderBeliefs.contains(belief) - fun isPantheon() = getBeliefs(BeliefType.Pantheon).any() && !isMajorReligion() + @Readonly fun isPantheon() = getBeliefs(BeliefType.Pantheon).any() && !isMajorReligion() + @Readonly fun isMajorReligion() = getBeliefs(BeliefType.Founder).any() + @Readonly fun isEnhancedReligion() = getBeliefs(BeliefType.Enhancer).any() - fun isMajorReligion() = getBeliefs(BeliefType.Founder).any() - - fun isEnhancedReligion() = getBeliefs(BeliefType.Enhancer).any() - - fun getFounder() = gameInfo.getCivilization(foundingCivName) + @Readonly fun getFounder() = gameInfo.getCivilization(foundingCivName) + @Readonly fun matchesFilter(filter: String, state: GameContext = GameContext.IgnoreConditionals, civ: Civilization? = null): Boolean { return MultiFilter.multiFilter(filter, { matchesSingleFilter(it, state, civ) }) } - + + @Readonly private fun matchesSingleFilter(filter: String, state: GameContext = GameContext.IgnoreConditionals, civ: Civilization? = null): Boolean { val foundingCiv = getFounder() when (filter) { diff --git a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt index c0744e8cfb..2cdae8bd51 100644 --- a/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt +++ b/core/src/com/unciv/models/ruleset/unit/BaseUnit.kt @@ -478,10 +478,10 @@ class BaseUnit : RulesetObject(), INonPerpetualConstruction { } - fun isRanged() = rangedStrength > 0 - fun isMelee() = !isRanged() && strength > 0 + @Readonly fun isRanged() = rangedStrength > 0 + @Readonly fun isMelee() = !isRanged() && strength > 0 val isMilitary by lazy { isRanged() || isMelee() } - fun isCivilian() = !isMilitary + @Readonly fun isCivilian() = !isMilitary val isLandUnit by lazy { type.isLandUnit() } val isWaterUnit by lazy { type.isWaterUnit() }