Civs with a spy set up in a foreign city can view some information (#11396)

* Civs with a spy set up can view some foreign city information

* Spying Civs can only see city tiles that they are aware of

* Refactored EspionageManager getCitiesWithOurSpies

Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>

* Removed extra parenthesis

---------

Co-authored-by: Yair Morgenstern <yairm210@hotmail.com>
This commit is contained in:
Oskar Niesen 2024-04-09 15:11:12 -05:00 committed by GitHub
parent 60bb8704ad
commit 892b54f651
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 30 additions and 11 deletions

View File

@ -96,7 +96,7 @@ class DiplomacyAction(
if (showTrade && currentCiv.isAtWarWith(otherCiv))
showTrade = false // Can't trade right now
worldScreen.game.pushScreen(DiplomacyScreen(currentCiv, otherCiv, showTrade = showTrade))
}
}

View File

@ -70,5 +70,11 @@ class EspionageManager : IsPartOfGameInfoSerialization {
return spyList.filter { it.getLocation() == city }.toMutableList()
}
/**
* Returns a list of all cities with our spies in them.
* The list needs to be stable accross calls on the same turn.
*/
fun getCitiesWithOurSpies(): List<City> = spyList.filter { it.isSetUp() }.mapNotNull { it.getLocation() }
fun getSpyAssignedToCity(city: City): Spy? = spyList.firstOrNull {it.getLocation() == city}
}

View File

@ -531,7 +531,8 @@ class CityButton(val city: City, private val tileGroup: TileGroup) : Table(BaseS
// second tap on the button will go to the city screen
// if this city belongs to you and you are not iterating though the air units
if (DebugUtils.VISIBLE_MAP || viewingPlayer.isSpectator()
|| (belongsToViewingCiv() && !tileGroup.tile.airUnits.contains(unitTable.selectedUnit))) {
|| belongsToViewingCiv() && !tileGroup.tile.airUnits.contains(unitTable.selectedUnit)
|| city.civ.gameInfo.isEspionageEnabled() && viewingPlayer.espionageManager.getSpyAssignedToCity(city)?.isSetUp() == true) {
GUI.pushScreen(CityScreen(city))
} else if (viewingPlayer.knows(city.civ)) {
foreignCityInfoPopup()

View File

@ -8,6 +8,7 @@ import com.unciv.GUI
import com.unciv.UncivGame
import com.unciv.logic.automation.Automation
import com.unciv.logic.city.City
import com.unciv.logic.civilization.Civilization
import com.unciv.logic.map.tile.Tile
import com.unciv.models.TutorialTrigger
import com.unciv.models.UncivSound
@ -60,8 +61,19 @@ class CityScreen(
const val wltkIconSize = 40f
}
private val selectedCiv: Civilization = GUI.getWorldScreen().selectedCiv
private val isSpying = selectedCiv.gameInfo.isEspionageEnabled() && selectedCiv != city.civ
/**
* This is the regular civ city list if we are not spying, if we are spying then it is every foreign city that our spies are in
*/
val viewableCities = if (isSpying) selectedCiv.espionageManager.getCitiesWithOurSpies()
.filter { it.civ != GUI.getWorldScreen().selectedCiv }
else city.civ.cities
/** Toggles or adds/removes all state changing buttons */
val canChangeState = GUI.isAllowedChangeState()
val canChangeState = GUI.isAllowedChangeState() && !isSpying
// Clockwise from the top-left
@ -136,6 +148,7 @@ class CityScreen(
//stage.setDebugTableUnderMouse(true)
stage.addActor(cityStatsTable)
// If we are spying then we shoulden't be able to see their construction screen.
constructionsTable.addActorsToStage()
stage.addActor(selectedConstructionTable)
stage.addActor(tileTable)
@ -151,7 +164,7 @@ class CityScreen(
// Recalculate Stats
city.cityStats.update()
constructionsTable.isVisible = true
constructionsTable.isVisible = !isSpying
constructionsTable.update(selectedConstruction)
updateWithoutConstructionAndMap()
@ -313,7 +326,7 @@ class CityScreen(
private fun addTiles() {
val tileSetStrings = TileSetStrings()
val cityTileGroups = city.getCenterTile().getTilesInDistance(5)
.filter { city.civ.hasExplored(it) }
.filter { selectedCiv.hasExplored(it) }
.map { CityTileGroup(city, it, tileSetStrings) }
for (tileGroup in cityTileGroups) {
@ -501,12 +514,11 @@ class CityScreen(
// Normal order is create new, then dispose old. But CityAmbiencePlayer delegates to a single instance of MusicController,
// leading to one extra play followed by a stop for the city ambience sounds. To avoid that, we pass our player on and relinquish control.
val civInfo = city.civ
val numCities = civInfo.cities.size
val numCities = viewableCities.size
if (numCities == 0) return
val indexOfCity = civInfo.cities.indexOf(city)
val indexOfCity = viewableCities.indexOf(city)
val indexOfNextCity = (indexOfCity + delta + numCities) % numCities
val newCityScreen = CityScreen(civInfo.cities[indexOfNextCity], ambiencePlayer = passOnCityAmbiencePlayer())
val newCityScreen = CityScreen(viewableCities[indexOfNextCity], ambiencePlayer = passOnCityAmbiencePlayer())
newCityScreen.update()
game.replaceCurrentScreen(newCityScreen)
}

View File

@ -21,7 +21,7 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() {
background = BaseScreen.skinStrings.getUiBackground("CityScreen/CityPickerTable", BaseScreen.skinStrings.roundedEdgeRectangleShape, civInfo.nation.getOuterColor())
clear()
if (civInfo.cities.size > 1) {
if (cityScreen.viewableCities.size > 1) {
val prevCityButton = Table() // so we get a wider clickable area than just the image itself
val image = ImageGetter.getImage("OtherIcons/BackArrow")
image.color = civInfo.nation.getInnerColor()
@ -74,7 +74,7 @@ class CityScreenCityPickerTable(private val cityScreen: CityScreen) : Table() {
add(cityNameTable).width(stage.width / 4)
if (civInfo.cities.size > 1) {
if (cityScreen.viewableCities.size > 1) {
val nextCityButton = Table() // so we gt a wider clickable area than just the image itself
val image = ImageGetter.getImage("OtherIcons/BackArrow")
image.setSize(25f, 25f)