mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-24 20:31:51 -04:00
chore(purity): Espionage
This commit is contained in:
parent
666c2056f3
commit
5c09f1b743
@ -61,6 +61,7 @@ allprojects {
|
||||
|
||||
"java.util.BitSet.get", // moved
|
||||
"kotlin.collections.getValue", // moved
|
||||
"kotlin.collections.randomOrNull",
|
||||
)
|
||||
wellKnownPureClasses = setOf<String>(
|
||||
)
|
||||
|
@ -4,6 +4,7 @@ import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||
import com.unciv.logic.city.City
|
||||
import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.models.Spy
|
||||
import yairm210.purity.annotations.Readonly
|
||||
|
||||
enum class SpyFleeReason {
|
||||
CityDestroyed,
|
||||
@ -25,13 +26,11 @@ class CityEspionageManager : IsPartOfGameInfoSerialization {
|
||||
this.city = city
|
||||
}
|
||||
|
||||
fun hasSpyOf(civInfo: Civilization): Boolean {
|
||||
return civInfo.espionageManager.spyList.any { it.getCityOrNull() == city }
|
||||
}
|
||||
|
||||
@Readonly fun hasSpyOf(civInfo: Civilization): Boolean = civInfo.espionageManager.spyList.any { it.getCityOrNull() == city }
|
||||
|
||||
fun getAllStationedSpies(): List<Spy> {
|
||||
return city.civ.gameInfo.civilizations.flatMap { it.espionageManager.getSpiesInCity(city) }
|
||||
}
|
||||
@Readonly fun getAllStationedSpies(): List<Spy> =
|
||||
city.civ.gameInfo.civilizations.flatMap { it.espionageManager.getSpiesInCity(city) }
|
||||
|
||||
fun removeAllPresentSpies(reason: SpyFleeReason) {
|
||||
for (spy in getAllStationedSpies()) {
|
||||
|
@ -71,6 +71,7 @@ class CityPopulationManager : IsPartOfGameInfoSerialization {
|
||||
|
||||
|
||||
/** Take null to mean infinity. */
|
||||
@Readonly
|
||||
fun getNumTurnsToNewPopulation(): Int? {
|
||||
if (!city.isGrowing()) return null
|
||||
val roundedFoodPerTurn = city.foodForNextTurn().toFloat()
|
||||
|
@ -1039,7 +1039,7 @@ class Civilization : IsPartOfGameInfoSerialization {
|
||||
moveCapitalTo(newCapital, oldCapital)
|
||||
}
|
||||
|
||||
fun getAllyCiv(): Civilization? = if (allyCivName == null) null
|
||||
@Readonly fun getAllyCiv(): Civilization? = if (allyCivName == null) null
|
||||
else gameInfo.getCivilization(allyCivName!!)
|
||||
@Readonly fun getAllyCivName() = allyCivName
|
||||
fun setAllyCiv(newAllyName: String?) { allyCivName = newAllyName }
|
||||
|
@ -6,6 +6,7 @@ import com.unciv.logic.civilization.Civilization
|
||||
import com.unciv.logic.map.tile.Tile
|
||||
import com.unciv.models.Spy
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import yairm210.purity.annotations.Readonly
|
||||
|
||||
|
||||
class EspionageManager : IsPartOfGameInfoSerialization {
|
||||
@ -42,6 +43,7 @@ class EspionageManager : IsPartOfGameInfoSerialization {
|
||||
spy.endTurn()
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun getSpyName(): String {
|
||||
val usedSpyNames = spyList.map { it.name }.toHashSet()
|
||||
val validSpyNames = civInfo.nation.spyNames.filter { it !in usedSpyNames }
|
||||
@ -57,6 +59,7 @@ class EspionageManager : IsPartOfGameInfoSerialization {
|
||||
return newSpy
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun getTilesVisibleViaSpies(): Sequence<Tile> {
|
||||
return spyList.asSequence()
|
||||
.filter { it.isSetUp() }
|
||||
@ -64,6 +67,7 @@ class EspionageManager : IsPartOfGameInfoSerialization {
|
||||
.flatMap { it.getCenterTile().getTilesInDistance(1) }
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun getTechsToSteal(otherCiv: Civilization): Set<String> {
|
||||
val techsToSteal = mutableSetOf<String>()
|
||||
for (tech in otherCiv.tech.techsResearched) {
|
||||
@ -74,30 +78,26 @@ class EspionageManager : IsPartOfGameInfoSerialization {
|
||||
return techsToSteal
|
||||
}
|
||||
|
||||
fun getSpiesInCity(city: City): List<Spy> {
|
||||
return spyList.filterTo(mutableListOf()) { it.getCityOrNull() == city }
|
||||
}
|
||||
@Readonly fun getSpiesInCity(city: City): List<Spy> = spyList.filter { it.getCityOrNull() == city }
|
||||
|
||||
fun getStartingSpyRank(): Int = 1 + civInfo.getMatchingUniques(UniqueType.SpyStartingLevel).sumOf { it.params[0].toInt() }
|
||||
@Readonly fun getStartingSpyRank(): Int = 1 + civInfo.getMatchingUniques(UniqueType.SpyStartingLevel).sumOf { it.params[0].toInt() }
|
||||
|
||||
/**
|
||||
* Returns a list of all cities with our spies in them.
|
||||
* The list needs to be stable across calls on the same turn.
|
||||
*/
|
||||
fun getCitiesWithOurSpies(): List<City> = spyList.filter { it.isSetUp() }.mapNotNull { it.getCityOrNull() }
|
||||
@Readonly fun getCitiesWithOurSpies(): List<City> = spyList.filter { it.isSetUp() }.mapNotNull { it.getCityOrNull() }
|
||||
|
||||
fun getSpyAssignedToCity(city: City): Spy? = spyList.firstOrNull { it.getCityOrNull() == city }
|
||||
@Readonly fun getSpyAssignedToCity(city: City): Spy? = spyList.firstOrNull { it.getCityOrNull() == city }
|
||||
|
||||
/**
|
||||
* Determines whether the NextTurnAction MoveSpies should be shown or not
|
||||
* @return true if there are spies waiting to be moved
|
||||
*/
|
||||
fun shouldShowMoveSpies(): Boolean = !dismissedShouldMoveSpies && spyList.any { it.isIdle() }
|
||||
@Readonly fun shouldShowMoveSpies(): Boolean = !dismissedShouldMoveSpies && spyList.any { it.isIdle() }
|
||||
&& civInfo.gameInfo.getCities().any { civInfo.hasExplored(it.getCenterTile()) && getSpyAssignedToCity(it) == null }
|
||||
|
||||
fun getIdleSpies(): List<Spy> {
|
||||
return spyList.filterTo(mutableListOf()) { it.isIdle() }
|
||||
}
|
||||
@Readonly fun getIdleSpies(): List<Spy> = spyList.filter { it.isIdle() }
|
||||
|
||||
/**
|
||||
* Takes all spies away from their cities.
|
||||
|
@ -14,6 +14,7 @@ import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
|
||||
import com.unciv.logic.civilization.managers.EspionageManager
|
||||
import com.unciv.models.ruleset.unique.Unique
|
||||
import com.unciv.models.ruleset.unique.UniqueType
|
||||
import yairm210.purity.annotations.Readonly
|
||||
import kotlin.math.ceil
|
||||
import kotlin.random.Random
|
||||
|
||||
@ -166,6 +167,7 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
* A -1 means we have no techonologies to steal.
|
||||
* A -2 means we the city produces no science
|
||||
*/
|
||||
@Readonly @Suppress("purity") // something here is wrong, it's actually mutating?!
|
||||
private fun getTurnsRemainingToStealTech(): Int {
|
||||
val stealableTechs = espionageManager.getTechsToSteal(getCity().civ)
|
||||
if (stealableTechs.isEmpty()) return -1
|
||||
@ -314,6 +316,7 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
/**
|
||||
* Calculates the success chance of a coup in this city state.
|
||||
*/
|
||||
@Readonly
|
||||
fun getCoupChanceOfSuccess(includeunknownFactors: Boolean): Float {
|
||||
val cityState = getCity().civ
|
||||
var successPercentage = 50f
|
||||
@ -348,7 +351,8 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
this.city = city
|
||||
setAction(SpyAction.Moving, 1)
|
||||
}
|
||||
|
||||
|
||||
@Readonly
|
||||
private fun canDismissAgreementToNotSendSpies(city: City): Boolean {
|
||||
val otherCivDiplomacyManager = city.civ.getDiplomacyManager(civInfo) ?: return false
|
||||
val otherCiv = otherCivDiplomacyManager.civInfo
|
||||
@ -370,6 +374,7 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
return false
|
||||
}
|
||||
|
||||
@Readonly
|
||||
fun canMoveTo(city: City): Boolean {
|
||||
if (canDismissAgreementToNotSendSpies(city)) return false
|
||||
if (getCityOrNull() == city) return true
|
||||
@ -377,13 +382,14 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
return espionageManager.getSpyAssignedToCity(city) == null
|
||||
}
|
||||
|
||||
fun isSetUp() = action.isSetUp
|
||||
@Readonly fun isSetUp() = action.isSetUp
|
||||
|
||||
fun isIdle() = action == SpyAction.None
|
||||
@Readonly fun isIdle() = action == SpyAction.None
|
||||
|
||||
fun isDoingWork() = action.isDoingWork(this)
|
||||
@Readonly fun isDoingWork() = action.isDoingWork(this)
|
||||
|
||||
/** Returns the City this Spy is in, or `null` if it is in the hideout. */
|
||||
@Readonly @Suppress("purity") // this also appears to be NOT readonly. Something is fishy.
|
||||
fun getCityOrNull(): City? {
|
||||
if (location == null) return null
|
||||
if (city == null) city = civInfo.gameInfo.tileMap[location!!].getCity()
|
||||
@ -392,9 +398,9 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
|
||||
/** Non-null version of [getCityOrNull] for the frequent case it is known the spy cannot be in the hideout.
|
||||
* @throws NullPointerException if the spy is in the hideout */
|
||||
fun getCity(): City = getCityOrNull()!!
|
||||
@Readonly fun getCity(): City = getCityOrNull()!!
|
||||
|
||||
fun getLocationName() = getCityOrNull()?.name ?: Constants.spyHideout
|
||||
@Readonly fun getLocationName() = getCityOrNull()?.name ?: Constants.spyHideout
|
||||
|
||||
fun levelUpSpy(amount: Int = 1) {
|
||||
if (rank >= civInfo.gameInfo.ruleset.modOptions.constants.maxSpyRank) return
|
||||
@ -406,12 +412,13 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
}
|
||||
|
||||
/** Modifier of the skill bonus of the spy by percent */
|
||||
fun getSkillModifierPercent() = rank * civInfo.gameInfo.ruleset.modOptions.constants.spyRankSkillPercentBonus
|
||||
@Readonly fun getSkillModifierPercent() = rank * civInfo.gameInfo.ruleset.modOptions.constants.spyRankSkillPercentBonus
|
||||
|
||||
/**
|
||||
* Gets a friendly and enemy efficiency uniques for the spy at the location
|
||||
* @return a value centered around 1.0 for the work efficiency of the spy, won't be negative
|
||||
*/
|
||||
@Readonly
|
||||
fun getEfficiencyModifier(): Double {
|
||||
val friendlyUniques: Sequence<Unique>
|
||||
val enemyUniques: Sequence<Unique>
|
||||
@ -446,7 +453,7 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
rank = 1
|
||||
}
|
||||
|
||||
fun isAlive(): Boolean = action != SpyAction.Dead
|
||||
@Readonly fun isAlive(): Boolean = action != SpyAction.Dead
|
||||
|
||||
/** Shorthand for [Civilization.addNotification] specialized for espionage - action, category and icon are always the same */
|
||||
fun addNotification(text: String) =
|
||||
@ -454,5 +461,5 @@ class Spy private constructor() : IsPartOfGameInfoSerialization {
|
||||
|
||||
/** Anti-save-scum: Deterministic random from city and turn
|
||||
* @throws NullPointerException for spies in the hideout */
|
||||
private fun randomSeed() = (getCity().run { location.x * location.y } + 123f * civInfo.gameInfo.turns).toInt() + name.hashCode()
|
||||
@Readonly private fun randomSeed() = (getCity().run { location.x * location.y } + 123f * civInfo.gameInfo.turns).toInt() + name.hashCode()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user