From dff8133a7a0af16bdfa66c1b4910de943e951653 Mon Sep 17 00:00:00 2001 From: Oskar Niesen Date: Wed, 19 Jun 2024 09:23:42 -0500 Subject: [PATCH] Defeated civilizations don't use spies (#11785) * Fixed a crash when rigging a city-state election the turn before meeting them * Civilizations move their spies back to the hideout when defeated * Players can't move spies when defeated * Removed some weird indents --- .../unciv/logic/civilization/Civilization.kt | 2 + .../diplomacy/CityStateFunctions.kt | 2 +- .../civilization/managers/EspionageManager.kt | 11 ++++- .../overviewscreen/EspionageOverviewScreen.kt | 42 +++++++++---------- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/core/src/com/unciv/logic/civilization/Civilization.kt b/core/src/com/unciv/logic/civilization/Civilization.kt index 48a09d3067..efc89135fc 100644 --- a/core/src/com/unciv/logic/civilization/Civilization.kt +++ b/core/src/com/unciv/logic/civilization/Civilization.kt @@ -909,6 +909,8 @@ class Civilization : IsPartOfGameInfoSerialization { for (tradeRequest in diplomacyManager.otherCiv().tradeRequests.filter { it.requestingCiv == civName }) diplomacyManager.otherCiv().tradeRequests.remove(tradeRequest) // it would be really weird to get a trade request from a dead civ } + if (gameInfo.isEspionageEnabled()) + espionageManager.removeAllSpies() } fun updateProximity(otherCiv: Civilization, preCalculated: Proximity? = null): Proximity = cache.updateProximity(otherCiv, preCalculated) diff --git a/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt b/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt index 06689a1cea..8568f53784 100644 --- a/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt +++ b/core/src/com/unciv/logic/civilization/diplomacy/CityStateFunctions.kt @@ -77,7 +77,7 @@ class CityStateFunctions(val civInfo: Civilization) { fun getVotesFromSpy(spy: Spy?): Float { if (spy == null) return 20f - var votes = (civInfo.getDiplomacyManager(spy.civInfo)!!.influence / 2) + var votes = (civInfo.getDiplomacyManagerOrMeet(spy.civInfo).influence / 2) votes += (spy.getSkillModifierPercent() * spy.getEfficiencyModifier()).toFloat() // ranges from 30 to 90 return votes } diff --git a/core/src/com/unciv/logic/civilization/managers/EspionageManager.kt b/core/src/com/unciv/logic/civilization/managers/EspionageManager.kt index e2ef0baada..14983e3083 100644 --- a/core/src/com/unciv/logic/civilization/managers/EspionageManager.kt +++ b/core/src/com/unciv/logic/civilization/managers/EspionageManager.kt @@ -45,7 +45,8 @@ class EspionageManager : IsPartOfGameInfoSerialization { fun getSpyName(): String { val usedSpyNames = spyList.map { it.name }.toHashSet() val validSpyNames = civInfo.nation.spyNames.filter { it !in usedSpyNames } - return validSpyNames.randomOrNull() ?: "Spy ${spyList.size+1}" // +1 as non-programmers count from 1 + return validSpyNames.randomOrNull() + ?: "Spy ${spyList.size + 1}" // +1 as non-programmers count from 1 } fun addSpy(): Spy { @@ -100,4 +101,12 @@ class EspionageManager : IsPartOfGameInfoSerialization { fun getIdleSpies(): List { return spyList.filterTo(mutableListOf()) { it.isIdle() } } + + /** + * Takes all spies away from their cities. + * Called when the civ is destroyed. + */ + fun removeAllSpies() { + spyList.forEach { it.moveTo(null) } + } } diff --git a/core/src/com/unciv/ui/screens/overviewscreen/EspionageOverviewScreen.kt b/core/src/com/unciv/ui/screens/overviewscreen/EspionageOverviewScreen.kt index c5c901931f..fe23072e70 100644 --- a/core/src/com/unciv/ui/screens/overviewscreen/EspionageOverviewScreen.kt +++ b/core/src/com/unciv/ui/screens/overviewscreen/EspionageOverviewScreen.kt @@ -85,9 +85,9 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS spySelectionTable.add(spy.name.toLabel()) spySelectionTable.add(spy.rank.toLabel()) spySelectionTable.add(spy.getLocationName().toLabel()) - val actionString = if (spy.action.showTurns) + val actionString = if (spy.action.showTurns) "[${spy.action.displayString}] ${spy.turnsRemainingForAction}${Fonts.turn}" - else spy.action.displayString + else spy.action.displayString spySelectionTable.add(actionString.toLabel()) val moveSpyButton = "Move".toTextButton() @@ -97,7 +97,7 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS moveSpyButton.onRightClick { onSpyRightClicked(spy) } - if (!worldScreen.canChangeState || !spy.isAlive()) { + if (!worldScreen.canChangeState || !spy.isAlive() || civInfo.isDefeated()) { // Spectators aren't allowed to move the spies of the Civs they are viewing moveSpyButton.disable() } @@ -124,18 +124,18 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS // Then add all cities val sortedCities = civInfo.gameInfo.getCities() - .filter { civInfo.hasExplored(it.getCenterTile()) } - .sortedWith( - compareBy { - it.civ != civInfo - }.thenBy { - it.civ.isCityState() - }.thenBy(collator) { - it.civ.civName.tr(hideIcons = true) - }.thenBy(collator) { - it.name.tr(hideIcons = true) - } - ) + .filter { civInfo.hasExplored(it.getCenterTile()) } + .sortedWith( + compareBy { + it.civ != civInfo + }.thenBy { + it.civ.isCityState() + }.thenBy(collator) { + it.civ.civName.tr(hideIcons = true) + }.thenBy(collator) { + it.name.tr(hideIcons = true) + } + ) for (city in sortedCities) { addCityToSelectionTable(city) } @@ -143,7 +143,7 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS private fun addCityToSelectionTable(city: City) { citySelectionTable.add(ImageGetter.getNationPortrait(city.civ.nation, 30f)) - .padLeft(20f) + .padLeft(20f) val label = city.name.toLabel(hideIcons = true) label.onClick { worldScreen.game.popScreen() // If a detour to this screen (i.e. not directly from worldScreen) is made possible, use resetToWorldScreen instead @@ -193,7 +193,7 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS add(starTable).center().padLeft(-4f) // Spectators aren't allowed to move the spies of the Civs they are viewing - if (worldScreen.canChangeState && spy.isAlive()) { + if (worldScreen.canChangeState && spy.isAlive() && !civInfo.isDefeated()) { onClick { onSpyClicked(moveSpyButtons[spy]!!, spy) } @@ -252,7 +252,7 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS button.setDirection(Align.right) } else { button.isVisible = city == null // hideout - || !city.espionage.hasSpyOf(civInfo) + || !city.espionage.hasSpyOf(civInfo) button.setDirection(Align.left) } } @@ -285,9 +285,9 @@ class EspionageOverviewScreen(val civInfo: Civilization, val worldScreen: WorldS val spy = selectedSpy!! if (!isCurrentAction) { ConfirmPopup(this@EspionageOverviewScreen, - "Do you want to stage a coup in [${city.civ.civName}] with a " + - "[${(selectedSpy!!.getCoupChanceOfSuccess(false) * 100f).toInt()}]% " + - "chance of success?", "Stage Coup") { + "Do you want to stage a coup in [${city.civ.civName}] with a " + + "[${(selectedSpy!!.getCoupChanceOfSuccess(false) * 100f).toInt()}]% " + + "chance of success?", "Stage Coup") { spy.setAction(SpyAction.Coup, 1) fist.color = Color.DARK_GRAY update()