chore(purity): Civilization

This commit is contained in:
yairm210 2025-08-06 23:23:32 +03:00
parent 0c4b9cbb85
commit cca650ab8a
7 changed files with 38 additions and 30 deletions

View File

@ -233,10 +233,10 @@ class Civilization : IsPartOfGameInfoSerialization {
var attackingUnit: String? = null
lateinit var source: Vector2
lateinit var target: Vector2
fun clone() = HistoricalAttackMemory(attackingUnit, Vector2(source), Vector2(target))
@Readonly fun clone() = HistoricalAttackMemory(attackingUnit, Vector2(source), Vector2(target))
}
/** Deep clone an ArrayList of [HistoricalAttackMemory]s. */
private fun ArrayList<HistoricalAttackMemory>.copy() = ArrayList(this.map { it.clone() })
@Readonly private fun ArrayList<HistoricalAttackMemory>.copy() = ArrayList(this.map { it.clone() })
/**
* List of attacks that this civilization has performed since the start of its most recent turn. Does not include attacks already tracked in [MapUnit.attacksSinceTurnStart] of living units. Used in movement arrow overlay.
* @see [MapUnit.attacksSinceTurnStart]
@ -325,9 +325,9 @@ class Civilization : IsPartOfGameInfoSerialization {
@Readonly fun getDiplomacyManager(civInfo: Civilization): DiplomacyManager? = getDiplomacyManager(civInfo.civName)
@Readonly fun getDiplomacyManager(civName: String): DiplomacyManager? = diplomacy[civName]
fun getProximity(civInfo: Civilization) = getProximity(civInfo.civName)
@Readonly fun getProximity(civInfo: Civilization) = getProximity(civInfo.civName)
@Suppress("MemberVisibilityCanBePrivate") // same visibility for overloads
fun getProximity(civName: String) = proximity[civName] ?: Proximity.None
@Readonly fun getProximity(civName: String) = proximity[civName] ?: Proximity.None
/** Returns only undefeated civs, aka the ones we care about
*
@ -432,6 +432,7 @@ class Civilization : IsPartOfGameInfoSerialization {
/** Preserves some origins for resources so we can separate them for trades
* Stockpiled uniques cannot be traded currently
*/
@Readonly
fun getPerTurnResourcesWithOriginsForTrade(): ResourceSupplyList {
val newResourceSupplyList = ResourceSupplyList(keepZeroAmounts = true)
@ -448,7 +449,8 @@ class Civilization : IsPartOfGameInfoSerialization {
}
return newResourceSupplyList
}
@Readonly
fun getStockpiledResourcesForTrade(): ResourceSupplyList {
val newResourceSupplyList = ResourceSupplyList(keepZeroAmounts = false)
@ -549,6 +551,7 @@ class Civilization : IsPartOfGameInfoSerialization {
yieldAll(gameInfo.getGlobalUniques().getMatchingUniques(uniqueType, gameContext))
}
@Readonly
fun getTriggeredUniques(
trigger: UniqueType,
gameContext: GameContext = state,
@ -737,15 +740,16 @@ class Civilization : IsPartOfGameInfoSerialization {
return sum
}
fun isMinorCivAggressor() = numMinorCivsAttacked >= 2
fun isMinorCivWarmonger() = numMinorCivsAttacked >= 4
@Readonly fun isMinorCivAggressor() = numMinorCivsAttacked >= 2
@Readonly fun isMinorCivWarmonger() = numMinorCivsAttacked >= 4
@Readonly
fun isLongCountActive(): Boolean {
val unique = getMatchingUniques(UniqueType.MayanGainGreatPerson).firstOrNull()
?: return false
return tech.isResearched(unique.params[1])
}
fun isLongCountDisplay() = hasLongCountDisplayUnique && isLongCountActive()
@Readonly fun isLongCountDisplay() = hasLongCountDisplayUnique && isLongCountActive()
@Readonly
fun calculateScoreBreakdown(): HashMap<String,Double> {
@ -770,8 +774,7 @@ class Civilization : IsPartOfGameInfoSerialization {
return scoreBreakdown
}
@Readonly
fun calculateTotalScore() = calculateScoreBreakdown().values.sum()
@Readonly fun calculateTotalScore() = calculateScoreBreakdown().values.sum()
//endregion
@ -828,14 +831,15 @@ class Civilization : IsPartOfGameInfoSerialization {
fun addFlag(flag: String, count: Int) = flagsCountdown.set(flag, count)
fun removeFlag(flag: String) = flagsCountdown.remove(flag)
fun hasFlag(flag: String) = flagsCountdown.contains(flag)
@Readonly fun hasFlag(flag: String) = flagsCountdown.contains(flag)
fun getTurnsBetweenDiplomaticVotes() = (15 * gameInfo.speed.modifier).toInt() // Dunno the exact calculation, hidden in Lua files
fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
@Readonly fun getTurnsBetweenDiplomaticVotes() = (15 * gameInfo.speed.modifier).toInt() // Dunno the exact calculation, hidden in Lua files
@Readonly fun getTurnsTillNextDiplomaticVote() = flagsCountdown[CivFlags.TurnsTillNextDiplomaticVote.name]
@Readonly fun getRecentBullyingCountdown() = flagsCountdown[CivFlags.RecentlyBullied.name]
fun getTurnsTillCallForBarbHelp() = flagsCountdown[CivFlags.TurnsTillCallForBarbHelp.name]
@Readonly fun getTurnsTillCallForBarbHelp() = flagsCountdown[CivFlags.TurnsTillCallForBarbHelp.name]
@Readonly
fun mayVoteForDiplomaticVictory() =
// Does not need checks for Barbarians or dead civs because the callers already ensure that
// (NextTurnAutomation.tryVoteForDiplomaticVictory and NextTurnAction.WorldCongressVote)
@ -849,6 +853,7 @@ class Civilization : IsPartOfGameInfoSerialization {
gameInfo.diplomaticVictoryVotesCast[civName] = chosenCivName
}
@Readonly
fun shouldShowDiplomaticVotingResults() =
flagsCountdown[CivFlags.ShowDiplomaticVotingResults.name] == 0
&& gameInfo.civilizations.any { it.isMajorCiv() && !it.isDefeated() && it != this }
@ -1049,7 +1054,8 @@ class Civilization : IsPartOfGameInfoSerialization {
* Can only be `true` if [GameParameters.randomNumberOfPlayers] is `true`, but in that case
* we try to see if the player _could_ be certain with a modicum of cleverness...
*/
fun hideCivCount(): Boolean {
@Readonly
fun shouldHideCivCount(): Boolean {
if (!gameInfo.gameParameters.randomNumberOfPlayers) return false
val knownCivs = 1 + getKnownCivs().count { it.isMajorCiv() }
if (knownCivs >= gameInfo.gameParameters.maxNumberOfPlayers) return false
@ -1065,6 +1071,7 @@ class Civilization : IsPartOfGameInfoSerialization {
if (isAI() || isSpectator()) return null
return lastSeenImprovement[position]
}
fun setLastSeenImprovement(position: Vector2, improvement: String?) {
if (isAI() || isSpectator()) return
if (improvement == null)
@ -1081,7 +1088,7 @@ class CivilizationInfoPreview() {
var civName = ""
var playerType = PlayerType.AI
var playerId = ""
fun isPlayerCivilization() = playerType == PlayerType.Human
@Readonly fun isPlayerCivilization() = playerType == PlayerType.Human
/**
* Converts a CivilizationInfo object (can be uninitialized) into a CivilizationInfoPreview object.

View File

@ -247,7 +247,7 @@ class ReligionManager : IsPartOfGameInfoSerialization {
val multiplier = ruleset.modOptions.constants.religionLimitMultiplier
val base = ruleset.modOptions.constants.religionLimitBase
val civCount = gameInfo.civilizations.count { it.isMajorCiv() }
val hideCivCount = civInfo.hideCivCount()
val hideCivCount = civInfo.shouldHideCivCount()
if (hideCivCount) {
val knownCivs = 1 + civInfo.getKnownCivs().count { it.isMajorCiv() }
val estimatedCivCount = (

View File

@ -68,7 +68,7 @@ class Victory : INamed {
class Milestone(val uniqueDescription: String, private val parentVictory: Victory) {
val type: MilestoneType? = MilestoneType.values().firstOrNull { uniqueDescription.getPlaceholderText() == it.text.getPlaceholderText() }
val type: MilestoneType? = MilestoneType.entries.firstOrNull { uniqueDescription.getPlaceholderText() == it.text.getPlaceholderText() }
val params = uniqueDescription.getPlaceholderParameters()
private fun getIncompleteSpaceshipParts(civInfo: Civilization): Counter<String> {
@ -147,7 +147,7 @@ class Milestone(val uniqueDescription: String, private val parentVictory: Victor
val amountDone =
if (completed) amountToDo
else originalMajorCapitalsOwned(civInfo)
if (civInfo.hideCivCount())
if (civInfo.shouldHideCivCount())
"{$uniqueDescription} (${amountDone.tr()}/?)"
else
"{$uniqueDescription} (${amountDone.tr()}/${amountToDo.tr()})"
@ -157,7 +157,7 @@ class Milestone(val uniqueDescription: String, private val parentVictory: Victor
val amountDone =
if (completed) amountToDo
else amountToDo - (civInfo.gameInfo.getAliveMajorCivs().count { it != civInfo })
if (civInfo.hideCivCount())
if (civInfo.shouldHideCivCount())
"{$uniqueDescription} (${amountDone.tr()}/?)"
else
"{$uniqueDescription} (${amountDone.tr()}/${amountToDo.tr()})"
@ -223,7 +223,7 @@ class Milestone(val uniqueDescription: String, private val parentVictory: Victor
}
MilestoneType.DestroyAllPlayers -> {
val hideCivCount = civInfo.hideCivCount()
val hideCivCount = civInfo.shouldHideCivCount()
for (civ in civInfo.gameInfo.civilizations) {
if (civ == civInfo || !civ.isMajorCiv()) continue
if (hideCivCount && !civInfo.knows(civ)) continue
@ -236,7 +236,7 @@ class Milestone(val uniqueDescription: String, private val parentVictory: Victor
}
MilestoneType.CaptureAllCapitals -> {
val hideCivCount = civInfo.hideCivCount()
val hideCivCount = civInfo.shouldHideCivCount()
val majorCivs = civInfo.gameInfo.civilizations.filter { it.isMajorCiv() }
val originalCapitals = civInfo.gameInfo.getCities().filter { it.isOriginalCapital }
.associateBy { it.foundingCiv }
@ -268,7 +268,7 @@ class Milestone(val uniqueDescription: String, private val parentVictory: Victor
}
MilestoneType.WorldReligion -> {
val hideCivCount = civInfo.hideCivCount()
val hideCivCount = civInfo.shouldHideCivCount()
val majorCivs = civInfo.gameInfo.civilizations.filter { it.isMajorCiv() && it.isAlive() }
val civReligion = civInfo.religionManager.religion
for (civ in majorCivs) {

View File

@ -3,6 +3,7 @@ package com.unciv.models.ruleset.tile
import com.unciv.Constants
import com.unciv.models.ruleset.Ruleset
import com.unciv.models.ruleset.IConstruction // Kdoc only
import yairm210.purity.annotations.Readonly
/** Container helps aggregating supply and demand of [resources][ResourceSupply.resource], categorized by [origin][ResourceSupply.origin].
*
@ -19,12 +20,12 @@ class ResourceSupplyList(
* [ResourceSupplyList.add] will update the value in existing instances, and should remain the only place.
*/
data class ResourceSupply(val resource: TileResource, val origin: String, var amount: Int) {
fun isCityStateOrTradeOrigin() = (origin == Constants.cityStates || origin == "Trade") && amount > 0
@Readonly fun isCityStateOrTradeOrigin() = (origin == Constants.cityStates || origin == "Trade") && amount > 0
override fun toString() = "$amount ${resource.name} from $origin"
}
/** Fetch a [ResourceSupply] entry or `null` if no match found */
fun get(resource: TileResource, origin: String) =
@Readonly fun get(resource: TileResource, origin: String) =
firstOrNull { it.resource.name == resource.name && it.origin == origin }
/** Get the total amount for a resource by [resourceName] */

View File

@ -287,7 +287,7 @@ class GlobalPoliticsOverviewTable(
add(civTableScroll.addBorder(2f, Color.WHITE)).pad(10f)
}
val hideCivsCount = viewingPlayer.hideCivCount() ||
val hideCivsCount = viewingPlayer.shouldHideCivCount() ||
persistableData.includeCityStates && viewingPlayer.hideCityStateCount()
relevantCivsCount = if (hideCivsCount) "?"
else gameInfo.civilizations.count {
@ -334,7 +334,7 @@ class GlobalPoliticsOverviewTable(
civTableScroll.setScrollingDisabled(portraitMode, portraitMode)
}
/** Same as [Civilization.hideCivCount] but for City-States instead of Major Civs */
/** Same as [Civilization.shouldHideCivCount] but for City-States instead of Major Civs */
private fun Civilization.hideCityStateCount(): Boolean {
if (!gameInfo.gameParameters.randomNumberOfCityStates) return false
val knownCivs = 1 + getKnownCivs().count { it.isCityState }

View File

@ -70,7 +70,7 @@ class ReligionOverviewTab(
val minWidth = max(religionButtonLabel.prefWidth, overviewScreen.stage.width / 3)
val manager = viewingPlayer.religionManager
val headerText =
if (viewingPlayer.hideCivCount()) "Religions to be founded: [?]"
if (viewingPlayer.shouldHideCivCount()) "Religions to be founded: [?]"
else "Religions to be founded: [${manager.remainingFoundableReligions()}]"
val religionCountExpander = ExpanderTab(
headerText, fontSize = 18, headerPad = 5f,

View File

@ -228,14 +228,14 @@ class VictoryScreenIllustrations(
civ.victoryManager.currentsSpaceshipParts.sumValues()
}
MilestoneType.DestroyAllPlayers -> {
total += if (selectedCiv.hideCivCount()) game.gameParameters.maxNumberOfPlayers
total += if (selectedCiv.shouldHideCivCount()) game.gameParameters.maxNumberOfPlayers
else game.civilizations.count { it.isMajorCiv() }
game.civilizations.count {
it != civ && it.isMajorCiv() && civ.knows(it) && it.isDefeated()
}
}
MilestoneType.CaptureAllCapitals -> {
total += if (selectedCiv.hideCivCount()) game.gameParameters.maxNumberOfPlayers
total += if (selectedCiv.shouldHideCivCount()) game.gameParameters.maxNumberOfPlayers
else game.getCities().count { it.isOriginalCapital }
civ.cities.count { it.isOriginalCapital }
}