Espionage automation (#10974)

* Civilizations now send their spies out

* Idle spies move to a city even if there is no tech to steal

* Fixed moving spies

* Game doesn't crash when the city the spy was at is taken over

* Fixed crash when no other city is viewable

* Spies no longer go to city states again

* Added a new line for the test

* Spies are now removed from a city when it is captured
This commit is contained in:
Oskar Niesen 2024-01-25 15:27:11 -06:00 committed by GitHub
parent 780c4744cc
commit 5cbc04b63a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 58 additions and 3 deletions

View File

@ -2,6 +2,7 @@ package com.unciv.logic.automation.civilization
import com.unciv.logic.automation.Automation
import com.unciv.logic.automation.ThreatLevel
import com.unciv.logic.automation.unit.EspionageAutomation
import com.unciv.logic.automation.unit.UnitAutomation
import com.unciv.logic.city.City
import com.unciv.logic.civilization.AlertType
@ -13,6 +14,7 @@ import com.unciv.logic.civilization.diplomacy.DiplomacyFlags
import com.unciv.logic.civilization.diplomacy.DiplomaticModifiers
import com.unciv.logic.civilization.diplomacy.DiplomaticStatus
import com.unciv.logic.civilization.diplomacy.RelationshipLevel
import com.unciv.logic.civilization.managers.EspionageManager
import com.unciv.logic.map.mapunit.MapUnit
import com.unciv.models.ruleset.MilestoneType
import com.unciv.models.ruleset.ModOptionsConstants
@ -68,9 +70,15 @@ object NextTurnAutomation {
}
automateUnits(civInfo) // this is the most expensive part
if (civInfo.isMajorCiv() && civInfo.gameInfo.isReligionEnabled()) {
// Can only be done now, as the prophet first has to decide to found/enhance a religion
ReligionAutomation.chooseReligiousBeliefs(civInfo)
if (civInfo.isMajorCiv()) {
if (civInfo.gameInfo.isReligionEnabled()) {
// Can only be done now, as the prophet first has to decide to found/enhance a religion
ReligionAutomation.chooseReligiousBeliefs(civInfo)
}
if (civInfo.gameInfo.isEspionageEnabled()) {
// Do after cities are conquered
EspionageAutomation.automateSpies(civInfo)
}
}
automateCities(civInfo) // second most expensive

View File

@ -0,0 +1,33 @@
package com.unciv.logic.automation.unit
import com.unciv.logic.civilization.Civilization
import com.unciv.models.SpyAction
object EspionageAutomation {
fun automateSpies(civInfo: Civilization) {
val civsToStealFrom: List<Civilization> by lazy {
civInfo.getKnownCivs().filter {otherCiv -> otherCiv.isMajorCiv() && otherCiv.cities.any { it.getCenterTile().isVisible(civInfo) }
&& civInfo.espionageManager.getTechsToSteal(otherCiv).isNotEmpty() }.toList()
}
val getCivsToStealFromSorted: List<Civilization> =
civsToStealFrom.sortedBy { otherCiv -> civInfo.espionageManager.spyList
.count { it.isDoingWork() && it.getLocation()?.civ == otherCiv }
}.toList()
for (spy in civInfo.espionageManager.spyList) {
if (spy.isDoingWork()) continue
if (civsToStealFrom.isNotEmpty()) {
// 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
spy.moveTo(getCivsToStealFromSorted.first().cities.filter { it.getCenterTile().isVisible(civInfo) }.maxByOrNull { it.population.population })
continue
}
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())
}
}
}
}

View File

@ -527,6 +527,17 @@ object Battle {
for (airUnit in airUnits.toList()) airUnit.destroy()
}
// Move all spies in the city
if (attackerCiv.gameInfo.isEspionageEnabled()) {
for (civ in attackerCiv.gameInfo.civilizations.filter { it.isMajorCiv() }) {
for (spy in civ.espionageManager.spyList) {
if (spy.getLocation() == city) {
spy.moveTo(null)
}
}
}
}
val stateForConditionals = StateForConditionals(civInfo = attackerCiv, city=city, unit = attacker.unit, ourCombatant = attacker, attackedTile = city.getCenterTile())
for (unique in attacker.getMatchingUniques(UniqueType.CaptureCityPlunder, stateForConditionals, true)) {
attackerCiv.addStat(

View File

@ -154,6 +154,9 @@ class Spy() : IsPartOfGameInfoSerialization {
fun isSetUp() = action !in listOf(SpyAction.Moving, SpyAction.None, SpyAction.EstablishNetwork)
// Only returns true if the spy is doing a helpful and implemented action
fun isDoingWork() = action == SpyAction.StealingTech || action == SpyAction.EstablishNetwork
fun getLocation(): City? {
return civInfo.gameInfo.getCities().firstOrNull { it.id == location }
}