mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-27 13:55:54 -04:00
Further Espionage Implementation (#11077)
* Added getSpiesInCity method in EspionageManager * Expanded stealing technology code * Spies can now die and revive * Added basic rigging elections * Spies rigging elections can now be caught * Added spy rank * Players can now move spies to city-states * Spies at a higher rank steal tech quicker * EspionageAutomation now sends spies to city-states and to do counter intelligence * Fixed some EspionageAutomation logic * Fixed EspionageAutomation error * Spy.location is now stored as a vector2 * Accounted for the only able to be one spy of a civ in each city * Spies level up when successfully stealing tech. * Increased tech steal rate by spy rank * Increased influence gained by rigging elections * Added a spy level cap * Spies no longer get stuck on counter-intelligence action * Spy automation no longer tries to rig elections in city states that it is at war with * canMoveTo now checks if the city tile is visible * Added espionage translations * Changed automateSpySteal/Rig/Counter intelligence return types * Simplifies automateSpies in EspionageAutomation * Added blank lines before titles * Improved spy being found and killed message phrasing
This commit is contained in:
parent
ed7fd447c2
commit
ccea2c88d3
@ -1729,14 +1729,28 @@ Move =
|
|||||||
|
|
||||||
After an unknown civilization entered the [eraName], we have recruited [spyName] as a spy! =
|
After an unknown civilization entered the [eraName], we have recruited [spyName] as a spy! =
|
||||||
We have recruited [spyName] as a spy! =
|
We have recruited [spyName] as a spy! =
|
||||||
A spy from [civilization] stole the Technology [techName] from [cityName]! =
|
Your spy [spyName] has leveled up! =
|
||||||
An unidentified spy stole the Technology [techName] from [cityName]! =
|
Your spy [spyName] cannot steal any more techs from [civName] as we've already researched all the technology they know! =
|
||||||
Your spy [name] stole the Technology [techName] from [cityName]! =
|
|
||||||
Your spy [name] cannot steal any more techs from [civilization] as we've already researched all the technology they know! =
|
|
||||||
|
|
||||||
|
# Stealing Technology defending civ
|
||||||
|
An unidentified spy stole the Technology [techName] from [cityName]! =
|
||||||
|
A spy from [civName] stole the Technology [techName] from [cityName]! =
|
||||||
|
A spy from [civName] was found and killed trying to steal Technology in [cityName]! =
|
||||||
|
A spy from [civName] was found and killed by [spyName] trying to steal Technology in [cityName]! =
|
||||||
|
|
||||||
|
# Stealing Technology offending civ
|
||||||
|
Your spy [spyName] stole the Technology [techName] from [cityName]! =
|
||||||
|
Your spy [spyName] was killed trying to steal Technology in [cityName]! =
|
||||||
|
|
||||||
|
# Rigging elections
|
||||||
|
A spy from [civName] tried to rig elections and was found and killed in [cityName] by [spyName]! =
|
||||||
|
Your spy [spyName] was killed trying to rig the election in [cityName]! =
|
||||||
|
Your spy successfully rigged the election in [cityName]! =
|
||||||
|
|
||||||
|
# Spy fleeing city
|
||||||
After the city of [cityName] was destroyed, your spy [spyName] has fled back to our hideout. =
|
After the city of [cityName] was destroyed, your spy [spyName] has fled back to our hideout. =
|
||||||
After the city of [cityName] was conquered, your spy [spyName] has fled back to our hideout. =
|
After the city of [cityName] was conquered, your spy [spyName] has fled back to our hideout. =
|
||||||
Due to the chaos ensuing in [cityName], your spy [spyname] has fled back to our hideout. =
|
Due to the chaos ensuing in [cityName], your spy [spyName] has fled back to our hideout. =
|
||||||
|
|
||||||
# Promotions
|
# Promotions
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ object NextTurnAutomation {
|
|||||||
}
|
}
|
||||||
if (civInfo.gameInfo.isEspionageEnabled()) {
|
if (civInfo.gameInfo.isEspionageEnabled()) {
|
||||||
// Do after cities are conquered
|
// Do after cities are conquered
|
||||||
EspionageAutomation.automateSpies(civInfo)
|
EspionageAutomation(civInfo).automateSpies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,33 +1,80 @@
|
|||||||
package com.unciv.logic.automation.unit
|
package com.unciv.logic.automation.unit
|
||||||
|
|
||||||
|
import com.unciv.logic.city.City
|
||||||
import com.unciv.logic.civilization.Civilization
|
import com.unciv.logic.civilization.Civilization
|
||||||
|
import com.unciv.models.Spy
|
||||||
import com.unciv.models.SpyAction
|
import com.unciv.models.SpyAction
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
object EspionageAutomation {
|
class EspionageAutomation(val civInfo: Civilization) {
|
||||||
|
private val civsToStealFrom: List<Civilization> by lazy {
|
||||||
fun automateSpies(civInfo: Civilization) {
|
|
||||||
val civsToStealFrom: List<Civilization> by lazy {
|
|
||||||
civInfo.getKnownCivs().filter {otherCiv -> otherCiv.isMajorCiv() && otherCiv.cities.any { it.getCenterTile().isVisible(civInfo) }
|
civInfo.getKnownCivs().filter {otherCiv -> otherCiv.isMajorCiv() && otherCiv.cities.any { it.getCenterTile().isVisible(civInfo) }
|
||||||
&& civInfo.espionageManager.getTechsToSteal(otherCiv).isNotEmpty() }.toList()
|
&& civInfo.espionageManager.getTechsToSteal(otherCiv).isNotEmpty() }.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val getCivsToStealFromSorted: List<Civilization> =
|
private val getCivsToStealFromSorted: List<Civilization> =
|
||||||
civsToStealFrom.sortedBy { otherCiv -> civInfo.espionageManager.spyList
|
civsToStealFrom.sortedBy { otherCiv -> civInfo.espionageManager.spyList
|
||||||
.count { it.isDoingWork() && it.getLocation()?.civ == otherCiv }
|
.count { it.isDoingWork() && it.getLocation()?.civ == otherCiv }
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
for (spy in civInfo.espionageManager.spyList) {
|
private val cityStatesToRig: List<Civilization> by lazy {
|
||||||
if (spy.isDoingWork()) continue
|
civInfo.getKnownCivs().filter { otherCiv -> otherCiv.isMinorCiv() && otherCiv.knows(civInfo) && !civInfo.isAtWarWith(otherCiv) }.toList()
|
||||||
if (civsToStealFrom.isNotEmpty()) {
|
}
|
||||||
|
|
||||||
|
fun automateSpies() {
|
||||||
|
val spies = civInfo.espionageManager.spyList
|
||||||
|
val spiesToMove = spies.filter { it.isAlive() && !it.isDoingWork() }
|
||||||
|
for (spy in spiesToMove) {
|
||||||
|
val randomSeed = spies.size + spies.indexOf(spy) + civInfo.gameInfo.turns
|
||||||
|
val randomAction = Random(randomSeed).nextInt(10)
|
||||||
|
|
||||||
|
// Try each operation based on the random value and the success rate
|
||||||
|
// If an operation was not successfull try the next one
|
||||||
|
if (randomAction <= 7 && automateSpyStealTech(spy)) {
|
||||||
|
continue
|
||||||
|
} else if (randomAction <= 9 && automateSpyRigElection(spy)) {
|
||||||
|
continue
|
||||||
|
} else if (automateSpyCounterInteligence(spy)) {
|
||||||
|
continue
|
||||||
|
} else if (spy.isDoingWork()) {
|
||||||
|
continue // We might have been doing counter intelligence and wanted to look for something better
|
||||||
|
} else {
|
||||||
|
// Retry all of the operations one more time
|
||||||
|
if (automateSpyStealTech(spy)) continue
|
||||||
|
if (automateSpyRigElection(spy)) continue
|
||||||
|
if(automateSpyCounterInteligence(spy)) continue
|
||||||
|
}
|
||||||
|
// There is nothing for our spy to do, put it in a random city
|
||||||
|
val randomCity = civInfo.gameInfo.getCities().filter { spy.canMoveTo(it) }.toList().randomOrNull()
|
||||||
|
spy.moveTo(randomCity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the spy to a city that we can steal a tech from
|
||||||
|
*/
|
||||||
|
fun automateSpyStealTech(spy: Spy): Boolean {
|
||||||
|
if (civsToStealFrom.isEmpty()) return false
|
||||||
// We want to move the spy to the city with the highest science generation
|
// We want to move the spy to the city with the highest science generation
|
||||||
// Players can't usually figure this out so lets do highest population instead
|
// Players can't usually figure this out so lets do highest population instead
|
||||||
spy.moveTo(getCivsToStealFromSorted.first().cities.filter { it.getCenterTile().isVisible(civInfo) }.maxByOrNull { it.population.population })
|
spy.moveTo(getCivsToStealFromSorted.first().cities.filter { spy.canMoveTo(it) }.maxByOrNull { it.population.population })
|
||||||
continue
|
return spy.action == SpyAction.StealingTech
|
||||||
}
|
|
||||||
if (spy.action == SpyAction.None) {
|
|
||||||
spy.moveTo(civInfo.getKnownCivs().filter { otherCiv -> otherCiv.isMajorCiv() && otherCiv.cities.any { it.getCenterTile().isVisible(civInfo) }}
|
|
||||||
.toList().randomOrNull()?.cities?.filter { it.getCenterTile().isVisible(civInfo) }?.randomOrNull())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the spy to a random city-state
|
||||||
|
*/
|
||||||
|
private fun automateSpyRigElection(spy: Spy): Boolean {
|
||||||
|
val potentialCities = cityStatesToRig.flatMap { it.cities }.filter { !it.isBeingRazed && spy.canMoveTo(it) }
|
||||||
|
spy.moveTo(potentialCities.randomOrNull())
|
||||||
|
return spy.action == SpyAction.RiggingElections
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the spy to a random city of ours
|
||||||
|
*/
|
||||||
|
private fun automateSpyCounterInteligence(spy: Spy): Boolean {
|
||||||
|
spy.moveTo(civInfo.cities.filter { spy.canMoveTo(it) }.randomOrNull())
|
||||||
|
return spy.action == SpyAction.CounterIntelligence
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,13 +26,11 @@ class CityEspionageManager : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun hasSpyOf(civInfo: Civilization): Boolean {
|
fun hasSpyOf(civInfo: Civilization): Boolean {
|
||||||
return civInfo.espionageManager.spyList.any { it.location == city.id }
|
return civInfo.espionageManager.spyList.any { it.getLocation() == city }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAllStationedSpies(): List<Spy> {
|
private fun getAllStationedSpies(): List<Spy> {
|
||||||
return city.civ.gameInfo.civilizations.flatMap { civ ->
|
return city.civ.gameInfo.civilizations.flatMap { it.espionageManager.getSpiesInCity(city) }
|
||||||
civ.espionageManager.spyList.filter { it.location == city.id }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeAllPresentSpies(reason: SpyFleeReason) {
|
fun removeAllPresentSpies(reason: SpyFleeReason) {
|
||||||
@ -44,7 +42,7 @@ class CityEspionageManager : IsPartOfGameInfoSerialization {
|
|||||||
else -> "Due to the chaos ensuing in [${city.name}], your spy [${spy.name}] has fled back to our hideout."
|
else -> "Due to the chaos ensuing in [${city.name}], your spy [${spy.name}] has fled back to our hideout."
|
||||||
}
|
}
|
||||||
owningCiv.addNotification(notificationString, city.location, NotificationCategory.Espionage, NotificationIcon.Spy)
|
owningCiv.addNotification(notificationString, city.location, NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
spy.location = null
|
spy.moveTo(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.unciv.logic.civilization.managers
|
package com.unciv.logic.civilization.managers
|
||||||
|
|
||||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||||
|
import com.unciv.logic.city.City
|
||||||
import com.unciv.logic.civilization.Civilization
|
import com.unciv.logic.civilization.Civilization
|
||||||
import com.unciv.logic.map.tile.Tile
|
import com.unciv.logic.map.tile.Tile
|
||||||
import com.unciv.models.Spy
|
import com.unciv.models.Spy
|
||||||
@ -33,7 +34,7 @@ class EspionageManager : IsPartOfGameInfoSerialization {
|
|||||||
spy.endTurn()
|
spy.endTurn()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getSpyName(): String {
|
fun getSpyName(): String {
|
||||||
val usedSpyNames = spyList.map { it.name }.toHashSet()
|
val usedSpyNames = spyList.map { it.name }.toHashSet()
|
||||||
val validSpyNames = civInfo.nation.spyNames.filter { it !in usedSpyNames }
|
val validSpyNames = civInfo.nation.spyNames.filter { it !in usedSpyNames }
|
||||||
if (validSpyNames.isEmpty()) { return "Spy ${spyList.size+1}" } // +1 as non-programmers count from 1
|
if (validSpyNames.isEmpty()) { return "Spy ${spyList.size+1}" } // +1 as non-programmers count from 1
|
||||||
@ -64,4 +65,10 @@ class EspionageManager : IsPartOfGameInfoSerialization {
|
|||||||
}
|
}
|
||||||
return techsToSteal
|
return techsToSteal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSpiesInCity(city: City): MutableList<Spy> {
|
||||||
|
return spyList.filter { it.getLocation() == city }.toMutableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSpyAssignedToCity(city: City): Spy? = spyList.firstOrNull {it.getLocation() == city}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.unciv.models
|
package com.unciv.models
|
||||||
|
|
||||||
|
import com.badlogic.gdx.math.Vector2
|
||||||
import com.unciv.Constants
|
import com.unciv.Constants
|
||||||
import com.unciv.logic.IsPartOfGameInfoSerialization
|
import com.unciv.logic.IsPartOfGameInfoSerialization
|
||||||
import com.unciv.logic.city.City
|
import com.unciv.logic.city.City
|
||||||
@ -17,16 +18,18 @@ enum class SpyAction(val displayString: String) {
|
|||||||
Surveillance("Observing City"),
|
Surveillance("Observing City"),
|
||||||
StealingTech("Stealing Tech"),
|
StealingTech("Stealing Tech"),
|
||||||
RiggingElections("Rigging Elections"),
|
RiggingElections("Rigging Elections"),
|
||||||
CounterIntelligence("Conducting Counter-intelligence")
|
CounterIntelligence("Conducting Counter-intelligence"),
|
||||||
|
Dead("Dead")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Spy() : IsPartOfGameInfoSerialization {
|
class Spy() : IsPartOfGameInfoSerialization {
|
||||||
// `location == null` means that the spy is in its hideout
|
// `location == null` means that the spy is in its hideout
|
||||||
var location: String? = null
|
private var location: Vector2? = null
|
||||||
lateinit var name: String
|
lateinit var name: String
|
||||||
var action = SpyAction.None
|
var action = SpyAction.None
|
||||||
private set
|
private set
|
||||||
|
var rank: Int = 1
|
||||||
var turnsRemainingForAction = 0
|
var turnsRemainingForAction = 0
|
||||||
private set
|
private set
|
||||||
private var progressTowardsStealingTech = 0
|
private var progressTowardsStealingTech = 0
|
||||||
@ -72,8 +75,10 @@ class Spy() : IsPartOfGameInfoSerialization {
|
|||||||
val location = getLocation()!! // This should never throw an exception, as going to the hideout sets your action to None.
|
val location = getLocation()!! // This should never throw an exception, as going to the hideout sets your action to None.
|
||||||
if (location.civ.isCityState()) {
|
if (location.civ.isCityState()) {
|
||||||
action = SpyAction.RiggingElections
|
action = SpyAction.RiggingElections
|
||||||
|
turnsRemainingForAction = 10
|
||||||
} else if (location.civ == civInfo) {
|
} else if (location.civ == civInfo) {
|
||||||
action = SpyAction.CounterIntelligence
|
action = SpyAction.CounterIntelligence
|
||||||
|
turnsRemainingForAction = 10
|
||||||
} else {
|
} else {
|
||||||
startStealingTech()
|
startStealingTech()
|
||||||
}
|
}
|
||||||
@ -95,12 +100,36 @@ class Spy() : IsPartOfGameInfoSerialization {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val techStealCost = stealableTechs.maxOfOrNull { civInfo.gameInfo.ruleset.technologies[it]!!.cost }!!
|
val techStealCost = stealableTechs.maxOfOrNull { civInfo.gameInfo.ruleset.technologies[it]!!.cost }!!
|
||||||
val progressThisTurn = getLocation()!!.cityStats.currentCityStats.science
|
// 33% spy bonus for each level
|
||||||
|
val progressThisTurn = getLocation()!!.cityStats.currentCityStats.science * (rank + 2f) / 3f
|
||||||
progressTowardsStealingTech += progressThisTurn.toInt()
|
progressTowardsStealingTech += progressThisTurn.toInt()
|
||||||
if (progressTowardsStealingTech > techStealCost) {
|
if (progressTowardsStealingTech > techStealCost) {
|
||||||
stealTech()
|
stealTech()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SpyAction.RiggingElections -> {
|
||||||
|
--turnsRemainingForAction
|
||||||
|
if (turnsRemainingForAction > 0) return
|
||||||
|
|
||||||
|
rigElection()
|
||||||
|
}
|
||||||
|
SpyAction.Dead -> {
|
||||||
|
--turnsRemainingForAction
|
||||||
|
if (turnsRemainingForAction > 0) return
|
||||||
|
|
||||||
|
val oldSpyName = name
|
||||||
|
name = espionageManager.getSpyName()
|
||||||
|
action = SpyAction.None
|
||||||
|
civInfo.addNotification("We have recruited a new spy name [$name] after [$oldSpyName] was killed.",
|
||||||
|
NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
|
}
|
||||||
|
SpyAction.CounterIntelligence -> {
|
||||||
|
// Counter inteligence spies don't do anything here
|
||||||
|
// However the AI will want to keep track of how long a spy has been doing counter intelligence for
|
||||||
|
// Once turnRemainingForAction is <= 0 the spy won't be considered to be doing work any more
|
||||||
|
--turnsRemainingForAction
|
||||||
|
return
|
||||||
|
}
|
||||||
else -> return // Not implemented yet, so don't do anything
|
else -> return // Not implemented yet, so don't do anything
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,47 +150,138 @@ class Spy() : IsPartOfGameInfoSerialization {
|
|||||||
if (stolenTech != null) {
|
if (stolenTech != null) {
|
||||||
civInfo.tech.addTechnology(stolenTech)
|
civInfo.tech.addTechnology(stolenTech)
|
||||||
}
|
}
|
||||||
|
// Lower is better
|
||||||
|
var spyResult = Random(randomSeed.toInt()).nextInt(300)
|
||||||
|
// Add our spies experience
|
||||||
|
spyResult -= getSkillModifier()
|
||||||
|
// Subtract the experience of the counter inteligence spies
|
||||||
|
val defendingSpy = city.civ.espionageManager.getSpyAssignedToCity(city)
|
||||||
|
spyResult += defendingSpy?.getSkillModifier() ?: 0
|
||||||
|
//TODO: Add policies modifier here
|
||||||
|
|
||||||
val spyDetected = Random(randomSeed.toInt()).nextInt(3)
|
val detectionString = when {
|
||||||
val detectionString = when (spyDetected) {
|
spyResult < 0 -> null // Not detected
|
||||||
0 -> "A spy from [${civInfo.civName}] stole the Technology [$stolenTech] from [$city]!"
|
spyResult < 100 -> "An unidentified spy stole the Technology [$stolenTech] from [$city]!"
|
||||||
1 -> "An unidentified spy stole the Technology [$stolenTech] from [$city]!"
|
spyResult < 200 -> "A spy from [${civInfo.civName}] stole the Technology [$stolenTech] from [$city]!"
|
||||||
else -> null // Not detected
|
else -> { // The spy was killed in the attempt
|
||||||
|
if (defendingSpy == null) "A spy from [${civInfo.civName}] was found and killed trying to steal Technology in [$city]!"
|
||||||
|
else "A spy from [${civInfo.civName}] was found and killed by [${defendingSpy.name}] trying to steal Technology in [$city]!"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (detectionString != null)
|
if (detectionString != null)
|
||||||
otherCiv.addNotification(detectionString, city.location, NotificationCategory.Espionage, NotificationIcon.Spy)
|
otherCiv.addNotification(detectionString, city.location, NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
|
|
||||||
|
if (spyResult < 200) {
|
||||||
civInfo.addNotification("Your spy [$name] stole the Technology [$stolenTech] from [$city]!", city.location,
|
civInfo.addNotification("Your spy [$name] stole the Technology [$stolenTech] from [$city]!", city.location,
|
||||||
NotificationCategory.Espionage,
|
NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
NotificationIcon.Spy
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
startStealingTech()
|
startStealingTech()
|
||||||
|
levelUpSpy()
|
||||||
|
} else {
|
||||||
|
civInfo.addNotification("Your spy [$name] was killed trying to steal Technology in [$city]!", city.location,
|
||||||
|
NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
|
defendingSpy?.levelUpSpy()
|
||||||
|
killSpy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rigElection() {
|
||||||
|
val city = getLocation()!!
|
||||||
|
val cityStateCiv = city.civ
|
||||||
|
// TODO: Simple implementation, please implement this in the future. This is a guess.
|
||||||
|
turnsRemainingForAction = 10
|
||||||
|
|
||||||
|
if (cityStateCiv.getAllyCiv() != null && cityStateCiv.getAllyCiv() != civInfo.civName) {
|
||||||
|
val allyCiv = civInfo.gameInfo.getCivilization(cityStateCiv.getAllyCiv()!!)
|
||||||
|
val defendingSpy = allyCiv.espionageManager.getSpyAssignedToCity(getLocation()!!)
|
||||||
|
if (defendingSpy != null) {
|
||||||
|
val randomSeed = city.location.x * city.location.y + 123f * civInfo.gameInfo.turns
|
||||||
|
var spyResult = Random(randomSeed.toInt()).nextInt(120)
|
||||||
|
spyResult -= getSkillModifier()
|
||||||
|
spyResult += defendingSpy.getSkillModifier()
|
||||||
|
if (spyResult > 100) {
|
||||||
|
// The Spy was killed
|
||||||
|
allyCiv.addNotification("A spy from [${civInfo.civName}] tried to rig elections and was found and killed in [${city}] by [${defendingSpy.name}]!",
|
||||||
|
getLocation()!!.location, NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
|
civInfo.addNotification("Your spy [$name] was killed trying to rig the election in [$city]!", city.location,
|
||||||
|
NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
|
killSpy()
|
||||||
|
defendingSpy.levelUpSpy()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Starts at 10 influence and increases by 3 for each extra rank.
|
||||||
|
cityStateCiv.getDiplomacyManager(civInfo).addInfluence(7f + getSpyRank() * 3)
|
||||||
|
civInfo.addNotification("Your spy successfully rigged the election in [$city]!", city.location,
|
||||||
|
NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
|
}
|
||||||
|
|
||||||
fun moveTo(city: City?) {
|
fun moveTo(city: City?) {
|
||||||
location = city?.id
|
|
||||||
if (city == null) { // Moving to spy hideout
|
if (city == null) { // Moving to spy hideout
|
||||||
|
location = null
|
||||||
action = SpyAction.None
|
action = SpyAction.None
|
||||||
turnsRemainingForAction = 0
|
turnsRemainingForAction = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
location = city.location
|
||||||
action = SpyAction.Moving
|
action = SpyAction.Moving
|
||||||
turnsRemainingForAction = 1
|
turnsRemainingForAction = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun canMoveTo(city: City): Boolean {
|
||||||
|
if (getLocation() == city) return true
|
||||||
|
if (!city.getCenterTile().isVisible(civInfo)) return false
|
||||||
|
return espionageManager.getSpyAssignedToCity(city) == null
|
||||||
|
}
|
||||||
|
|
||||||
fun isSetUp() = action !in listOf(SpyAction.Moving, SpyAction.None, SpyAction.EstablishNetwork)
|
fun isSetUp() = action !in listOf(SpyAction.Moving, SpyAction.None, SpyAction.EstablishNetwork)
|
||||||
|
|
||||||
// Only returns true if the spy is doing a helpful and implemented action
|
// Only returns true if the spy is doing a helpful and implemented action
|
||||||
fun isDoingWork() = action == SpyAction.StealingTech || action == SpyAction.EstablishNetwork
|
fun isDoingWork(): Boolean {
|
||||||
|
if (action == SpyAction.StealingTech || action == SpyAction.EstablishNetwork || action == SpyAction.Moving) return true
|
||||||
|
if (action == SpyAction.RiggingElections && !civInfo.isAtWarWith(getLocation()!!.civ)) return true
|
||||||
|
if (action == SpyAction.CounterIntelligence && turnsRemainingForAction > 0) return true
|
||||||
|
else return false
|
||||||
|
}
|
||||||
|
|
||||||
fun getLocation(): City? {
|
fun getLocation(): City? {
|
||||||
return civInfo.gameInfo.getCities().firstOrNull { it.id == location }
|
if (location == null) return null
|
||||||
|
return civInfo.gameInfo.tileMap[location!!].getCity()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLocationName(): String {
|
fun getLocationName(): String {
|
||||||
return getLocation()?.name ?: Constants.spyHideout
|
return getLocation()?.name ?: Constants.spyHideout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSpyRank(): Int {
|
||||||
|
return rank
|
||||||
|
}
|
||||||
|
|
||||||
|
fun levelUpSpy() {
|
||||||
|
//TODO: Make the spy level cap dependent on some unique
|
||||||
|
if (rank >= 3) return
|
||||||
|
if (getLocation() != null) {
|
||||||
|
civInfo.addNotification("Your spy [$name] has leveled up!", getLocation()!!.location,
|
||||||
|
NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
|
} else {
|
||||||
|
civInfo.addNotification("Your spy [$name] has leveled up!",
|
||||||
|
NotificationCategory.Espionage, NotificationIcon.Spy)
|
||||||
|
}
|
||||||
|
rank++
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSkillModifier(): Int {
|
||||||
|
return getSpyRank() * 30
|
||||||
|
}
|
||||||
|
|
||||||
|
fun killSpy() {
|
||||||
|
// We don't actually remove this spy object, we set them as dead and let them revive
|
||||||
|
moveTo(null)
|
||||||
|
action = SpyAction.Dead
|
||||||
|
turnsRemainingForAction = 5
|
||||||
|
rank = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isAlive(): Boolean = action != SpyAction.Dead
|
||||||
}
|
}
|
||||||
|
@ -77,10 +77,8 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS
|
|||||||
spySelectionTable.add(spy.getLocationName().toLabel())
|
spySelectionTable.add(spy.getLocationName().toLabel())
|
||||||
val actionString =
|
val actionString =
|
||||||
when (spy.action) {
|
when (spy.action) {
|
||||||
SpyAction.None, SpyAction.StealingTech, SpyAction.Surveillance -> spy.action.displayString
|
SpyAction.None, SpyAction.StealingTech, SpyAction.Surveillance, SpyAction.CounterIntelligence -> spy.action.displayString
|
||||||
SpyAction.Moving, SpyAction.EstablishNetwork -> "[${spy.action.displayString}] ${spy.turnsRemainingForAction}${Fonts.turn}"
|
SpyAction.Moving, SpyAction.EstablishNetwork, SpyAction.Dead, SpyAction.RiggingElections -> "[${spy.action.displayString}] ${spy.turnsRemainingForAction}${Fonts.turn}"
|
||||||
SpyAction.RiggingElections -> TODO()
|
|
||||||
SpyAction.CounterIntelligence -> TODO()
|
|
||||||
}
|
}
|
||||||
spySelectionTable.add(actionString.toLabel())
|
spySelectionTable.add(actionString.toLabel())
|
||||||
|
|
||||||
@ -95,14 +93,10 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS
|
|||||||
selectedSpy = spy
|
selectedSpy = spy
|
||||||
selectedSpyButton!!.label.setText(Constants.cancel.tr())
|
selectedSpyButton!!.label.setText(Constants.cancel.tr())
|
||||||
for ((button, city) in moveSpyHereButtons) {
|
for ((button, city) in moveSpyHereButtons) {
|
||||||
// For now, only allow spies to be sent to cities of other major civs and their hideout
|
|
||||||
// Not own cities as counterintelligence isn't implemented
|
// Not own cities as counterintelligence isn't implemented
|
||||||
// Not city-state civs as rigging elections isn't implemented
|
// Not city-state civs as rigging elections isn't implemented
|
||||||
button.isVisible = city == null // hideout
|
button.isVisible = city == null // hideout
|
||||||
|| (city.civ.isMajorCiv()
|
|| (city.civ != civInfo && !city.espionage.hasSpyOf(civInfo))
|
||||||
&& city.civ != civInfo
|
|
||||||
&& !city.espionage.hasSpyOf(civInfo)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!worldScreen.canChangeState) {
|
if (!worldScreen.canChangeState) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user