From 0f70726c10237f09c19abdf44fed188bb81401cb Mon Sep 17 00:00:00 2001 From: Alexander Korolyov <49795502+alkorolyov@users.noreply.github.com> Date: Thu, 23 Jul 2020 13:27:39 +0200 Subject: [PATCH] Spectator can view other civ stats: Tech, Trades, Cities, Units, Gold (#2877) * Select units/cities in spectator mode * Can enter EmpireOverviewScreen in spectator mode * keep selectedCiv after nextTurn * Add current civilization Label and Icon * Clickable Civilization label * Victory screen and next turn update --- .../ui/overviewscreen/EmpireOverviewScreen.kt | 3 +- .../ui/pickerscreens/PolicyPickerScreen.kt | 2 +- .../ui/pickerscreens/PromotionPickerScreen.kt | 7 +- .../ui/pickerscreens/TechPickerScreen.kt | 8 ++- .../unciv/ui/victoryscreen/VictoryScreen.kt | 7 +- .../unciv/ui/worldscreen/WorldMapHolder.kt | 8 ++- .../com/unciv/ui/worldscreen/WorldScreen.kt | 25 ++++++- .../unciv/ui/worldscreen/WorldScreenTopBar.kt | 66 +++++++++++++++---- .../ui/worldscreen/bottombar/BattleTable.kt | 2 +- .../ui/worldscreen/unit/UnitActionsTable.kt | 2 +- .../unciv/ui/worldscreen/unit/UnitTable.kt | 14 ++-- 11 files changed, 112 insertions(+), 32 deletions(-) diff --git a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt index 1418de230a..f9463b7777 100644 --- a/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt +++ b/core/src/com/unciv/ui/overviewscreen/EmpireOverviewScreen.kt @@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.utils.Align import com.unciv.Constants +import com.unciv.UncivGame import com.unciv.logic.HexMath import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.civilization.diplomacy.DiplomaticStatus @@ -21,7 +22,7 @@ import java.text.DecimalFormat import kotlin.math.* import com.unciv.ui.utils.AutoScrollPane as ScrollPane -class EmpireOverviewScreen(private val viewingPlayer:CivilizationInfo, defaultPage: String = "Cities") : CameraStageBaseScreen(){ +class EmpireOverviewScreen(private var viewingPlayer:CivilizationInfo, defaultPage: String = "Cities") : CameraStageBaseScreen(){ private val topTable = Table().apply { defaults().pad(10f) } private val centerTable = Table().apply { defaults().pad(20f) } diff --git a/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt index bebf4af183..7eec52aaf3 100644 --- a/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PolicyPickerScreen.kt @@ -42,7 +42,7 @@ class PolicyPickerScreen(val worldScreen: WorldScreen, civInfo: CivilizationInfo } else game.setScreen(PolicyPickerScreen(worldScreen)) // update policies } - if(!UncivGame.Current.worldScreen.isPlayersTurn) + if(!UncivGame.Current.worldScreen.canChangeState) rightSideButton.disable() diff --git a/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt index 47f4ce67dd..4a8b0bf963 100644 --- a/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/PromotionPickerScreen.kt @@ -36,7 +36,8 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen() { acceptPromotion(selectedPromotion) } val canBePromoted = unit.promotions.canBePromoted() - if(!canBePromoted) + val canChangeState = game.worldScreen.canChangeState + if(!canBePromoted || !canChangeState) rightSideButton.disable() val availablePromotionsGroup = Table() @@ -60,7 +61,7 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen() { selectPromotionButton.onClick { selectedPromotion = promotion rightSideButton.setText(promotion.name.tr()) - if(canBePromoted && isPromotionAvailable && !unitHasPromotion) + if(canBePromoted && isPromotionAvailable && !unitHasPromotion && canChangeState) rightSideButton.enable() else rightSideButton.disable() @@ -70,7 +71,7 @@ class PromotionPickerScreen(val unit: MapUnit) : PickerScreen() { availablePromotionsGroup.add(selectPromotionButton) - if(canBePromoted && isPromotionAvailable) { + if(canBePromoted && isPromotionAvailable && canChangeState) { val pickNow = "Pick now!".toLabel() pickNow.setAlignment(Align.center) pickNow.onClick { diff --git a/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt b/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt index 7613c27327..6090a8fbd0 100644 --- a/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/TechPickerScreen.kt @@ -61,7 +61,6 @@ class TechPickerScreen(internal val civInfo: CivilizationInfo, centerOnTech: Tec dispose() } - // per default show current/recent technology, // and possibly select it to show description, // which is very helpful when just discovered and clicking the notification @@ -212,6 +211,13 @@ class TechPickerScreen(internal val civInfo: CivilizationInfo, centerOnTech: Tec return } + if (!UncivGame.Current.worldScreen.canChangeState) { + rightSideButton.disable() + return + } + + + tempTechsToResearch.clear() tempTechsToResearch.addAll(civTech.getRequiredTechsToDestination(tech)) diff --git a/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt b/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt index c64ab3467c..dcc0ff18e2 100644 --- a/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt +++ b/core/src/com/unciv/ui/victoryscreen/VictoryScreen.kt @@ -32,7 +32,7 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() { val tabsTable = Table().apply { defaults().pad(10f) } val setMyVictoryButton = "Our status".toTextButton().onClick { setMyVictoryTable() } - tabsTable.add(setMyVictoryButton) + if (!playerCivInfo.isSpectator()) tabsTable.add(setMyVictoryButton) val setGlobalVictoryButton = "Global status".toTextButton().onClick { setGlobalVictoryTable() } tabsTable.add(setGlobalVictoryButton) val setCivRankingsButton = "Rankings".toTextButton().onClick { setCivRankingsTable() } @@ -41,7 +41,10 @@ class VictoryScreen(val worldScreen: WorldScreen) : PickerScreen() { topTable.addSeparator() topTable.add(contentsTable) - setMyVictoryTable() + if (playerCivInfo.isSpectator()) + setGlobalVictoryTable() + else + setMyVictoryTable() rightSideButton.isVisible=false diff --git a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt index 44bdf0a512..e24d8729f1 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldMapHolder.kt @@ -126,13 +126,15 @@ class WorldMapHolder(internal val worldScreen: WorldScreen, internal val tileMap private fun addTileOverlays(tileInfo: TileInfo, moveHereDto:MoveHereButtonDto?=null){ val table = Table().apply { defaults().pad(10f) } - if(moveHereDto!=null) + if(moveHereDto!=null && worldScreen.canChangeState) table.add(getMoveHereButton(moveHereDto)) val unitList = ArrayList() - if (tileInfo.isCityCenter() && tileInfo.getOwner()==worldScreen.viewingCiv) { + if (tileInfo.isCityCenter() + && (tileInfo.getOwner()==worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())) { unitList.addAll(tileInfo.getCity()!!.getCenterTile().getUnits()) - } else if (tileInfo.airUnits.isNotEmpty() && tileInfo.airUnits.first().civInfo==worldScreen.viewingCiv) { + } else if (tileInfo.airUnits.isNotEmpty() + && (tileInfo.airUnits.first().civInfo==worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())) { unitList.addAll(tileInfo.getUnits()) } diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 4f761bf70b..3e86471593 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -41,6 +41,7 @@ import kotlin.concurrent.thread class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { val gameInfo = game.gameInfo var isPlayersTurn = viewingCiv == gameInfo.currentPlayerCiv // todo this should be updated when passing turns + var selectedCiv = viewingCiv // Selected civilization, used only in spectator mode val canChangeState = isPlayersTurn && !viewingCiv.isSpectator() private var waitingForAutosave = false @@ -63,6 +64,7 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { private val notificationsScroll: NotificationsScroll var shouldUpdate = false + private var backButtonListener : InputListener // An initialized val always turned out to illegally be null... @@ -122,7 +124,12 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { viewingCiv.getCivUnits().any() -> viewingCiv.getCivUnits().first().getTile().position else -> Vector2.Zero } - mapHolder.setCenterPosition(tileToCenterOn,true) + + // Don't select unit and change selectedCiv when centering as spectator + if (viewingCiv.isSpectator()) + mapHolder.setCenterPosition(tileToCenterOn,true, false) + else + mapHolder.setCenterPosition(tileToCenterOn,true, true) if(gameInfo.gameParameters.isOnlineMultiplayer && !gameInfo.isUpToDate) @@ -275,6 +282,8 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { bottomTileInfoTable.y = if (game.settings.showMinimap) minimapWrapper.height else 0f battleTable.update() + updateSelectedCiv() + tutorialTaskTable.clear() val tutorialTask = getCurrentTutorialTask() if (tutorialTask == "" || !game.settings.showTutorials || viewingCiv.isDefeated()) { @@ -298,7 +307,10 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { // it causes a bug when we move a unit to an unexplored tile (for instance a cavalry unit which can move far) mapHolder.updateTiles(viewingCiv) - topBar.update(viewingCiv) + if (viewingCiv.isSpectator()) + topBar.update(selectedCiv) + else + topBar.update(viewingCiv) updateTechButton() techPolicyAndVictoryHolder.pack() @@ -440,6 +452,14 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { techButtonHolder.pack() //setSize(techButtonHolder.prefWidth, techButtonHolder.prefHeight) } + private fun updateSelectedCiv() { + if (bottomUnitTable.selectedUnit != null) + selectedCiv = bottomUnitTable.selectedUnit!!.civInfo + else if (bottomUnitTable.selectedCity != null) + selectedCiv = bottomUnitTable.selectedCity!!.civInfo + else viewingCiv + } + private fun createNextTurnButton(): TextButton { val nextTurnButton = TextButton("", skin) // text is set in update() @@ -500,6 +520,7 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { newWorldScreen.mapHolder.scaleX = mapHolder.scaleX newWorldScreen.mapHolder.scaleY = mapHolder.scaleY newWorldScreen.mapHolder.updateVisualScroll() + newWorldScreen.selectedCiv = gameInfoClone.getCivilization(selectedCiv.civName) game.worldScreen = newWorldScreen game.setWorldScreen() } diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt b/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt index 8427d997c9..68ed6b51a0 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreenTopBar.kt @@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.Group import com.badlogic.gdx.scenes.scene2d.ui.Image import com.badlogic.gdx.scenes.scene2d.ui.Label import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.badlogic.gdx.scenes.scene2d.ui.TextButton import com.unciv.logic.civilization.CivilizationInfo import com.unciv.models.metadata.GameSpeed import com.unciv.models.ruleset.tile.ResourceType @@ -23,6 +24,8 @@ import kotlin.math.roundToInt class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() { + private var selectedCivTable = Table() + private val turnsLabel = "Turns: 0/400".toLabel() private val goldLabel = "Gold:".toLabel(colorFromRGB(225, 217, 71)) private val scienceLabel = "Science:".toLabel(colorFromRGB(78, 140, 151)) @@ -48,13 +51,9 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() { pack() addActor(getMenuButton()) // needs to be after pack - val overviewButton = "Overview".toTextButton() - overviewButton.labelCell.pad(10f) - overviewButton.pack() - overviewButton.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.viewingCiv)) } - overviewButton.center(this) - overviewButton.x = worldScreen.stage.width - overviewButton.width - 10 - addActor(overviewButton) + addSelectedCivilizationTable() + + addActor(getOverviewButton()) } private fun getResourceTable(): Table { @@ -70,7 +69,9 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() { val resourceLabel = "0".toLabel() resourceLabels[resource.name] = resourceLabel resourceTable.add(resourceLabel) - val invokeResourcesPage = { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.viewingCiv, "Resources")) } + val invokeResourcesPage = { + worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Resources")) + } resourceLabel.onClick(invokeResourcesPage) resourceImage.onClick(invokeResourcesPage) } @@ -86,27 +87,35 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() { statsTable.add(goldLabel) val goldImage = ImageGetter.getStatIcon("Gold") statsTable.add(goldImage).padRight(20f).size(20f) - val invokeStatsPage = { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.viewingCiv, "Stats")) } + val invokeStatsPage = { + worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Stats")) + } goldLabel.onClick(invokeStatsPage) goldImage.onClick(invokeStatsPage) statsTable.add(scienceLabel) //.apply { setAlignment(Align.center) }).align(Align.top) val scienceImage = ImageGetter.getStatIcon("Science") statsTable.add(scienceImage).padRight(20f).size(20f) - val invokeTechScreen = { worldScreen.game.setScreen(TechPickerScreen(worldScreen.viewingCiv)) } + val invokeTechScreen = { + worldScreen.game.setScreen(TechPickerScreen(worldScreen.selectedCiv)) + } scienceLabel.onClick(invokeTechScreen) scienceImage.onClick(invokeTechScreen) statsTable.add(happinessImage).size(20f) statsTable.add(happinessLabel).padRight(20f)//.apply { setAlignment(Align.center) }).align(Align.top) - val invokeResourcesPage = { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.viewingCiv, "Resources")) } + val invokeResourcesPage = { + worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv, "Resources")) + } happinessImage.onClick(invokeResourcesPage) happinessLabel.onClick(invokeResourcesPage) statsTable.add(cultureLabel)//.apply { setAlignment(Align.center) }).align(Align.top) val cultureImage = ImageGetter.getStatIcon("Culture") statsTable.add(cultureImage).size(20f) - val invokePoliciesPage = { worldScreen.game.setScreen(PolicyPickerScreen(worldScreen)) } + val invokePoliciesPage = { + worldScreen.game.setScreen(PolicyPickerScreen(worldScreen, worldScreen.selectedCiv)) + } cultureLabel.onClick(invokePoliciesPage) cultureImage.onClick(invokePoliciesPage) @@ -130,6 +139,23 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() { return menuButton } + private fun getOverviewButton(): TextButton { + val overviewButton = "Overview".toTextButton() + overviewButton.labelCell.pad(10f) + overviewButton.pack() + overviewButton.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) } + overviewButton.center(this) + overviewButton.x = worldScreen.stage.width - overviewButton.width - 10 + return overviewButton + } + + private fun addSelectedCivilizationTable() { + selectedCivTable.centerY(this) + selectedCivTable.left() + selectedCivTable.x = getMenuButton().width + 20f + updateSelectedCivTabel() + addActor(selectedCivTable) + } internal fun update(civInfo: CivilizationInfo) { val revealedStrategicResources = civInfo.gameInfo.ruleSet.tileResources.values @@ -168,6 +194,22 @@ class WorldScreenTopBar(val worldScreen: WorldScreen) : Table() { } cultureLabel.setText(getCultureText(civInfo, nextTurnStats)) + + updateSelectedCivTabel() + } + + private fun updateSelectedCivTabel() { + selectedCivTable.clear() + + val selectedCivLabel = worldScreen.selectedCiv.civName.toLabel() + selectedCivLabel.setFontSize(25) + selectedCivLabel.onClick { worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) } + selectedCivTable.add(selectedCivLabel).padRight(10f) + + val nation = worldScreen.gameInfo.ruleSet.nations[worldScreen.selectedCiv.civName]!! + selectedCivTable.add(ImageGetter.getNationIndicator(nation, 35f).onClick { + worldScreen.game.setScreen(EmpireOverviewScreen(worldScreen.selectedCiv)) + }) } private fun getCultureText(civInfo: CivilizationInfo, nextTurnStats: Stats): String { diff --git a/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt b/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt index 036adbe0d4..1ace2c5e9a 100644 --- a/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt +++ b/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt @@ -42,7 +42,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() { isVisible = true val attacker = tryGetAttacker() - if(attacker==null){ hide(); return } + if(attacker==null || !worldScreen.canChangeState){ hide(); return } if (attacker.getUnitType()==UnitType.Missile) { val selectedTile = worldScreen.mapHolder.selectedTile diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt index e11f6ae47a..b0a208843c 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitActionsTable.kt @@ -62,7 +62,7 @@ class UnitActionsTable(val worldScreen: WorldScreen) : Table() { fun update(unit: MapUnit?) { clear() if (unit == null) return - if (!worldScreen.isPlayersTurn) return // No actions when it's not your turn! + if (!worldScreen.canChangeState) return // No actions when it's not your turn or spectator! for (button in UnitActions.getUnitActions(unit, worldScreen).map { getUnitActionButton(it) }) add(button).left().padBottom(2f).row() pack() diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt index f442270ed7..77127d8127 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitTable.kt @@ -74,7 +74,7 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ fun update() { if(selectedUnit!=null) { isVisible=true - if (selectedUnit!!.civInfo != worldScreen.viewingCiv) { // The unit that was selected, was captured. It exists but is no longer ours. + if (selectedUnit!!.civInfo != worldScreen.viewingCiv && !worldScreen.viewingCiv.isSpectator()) { // The unit that was selected, was captured. It exists but is no longer ours. selectedUnit = null selectedCity = null selectedUnitHasChanged = true @@ -194,17 +194,21 @@ class UnitTable(val worldScreen: WorldScreen) : Table(){ fun tileSelected(selectedTile: TileInfo) { val previouslySelectedUnit = selectedUnit - if(selectedTile.isCityCenter() && selectedTile.getOwner()==worldScreen.viewingCiv){ + + if(selectedTile.isCityCenter() + && (selectedTile.getOwner()==worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator())){ citySelected(selectedTile.getCity()!!) } - else if(selectedTile.militaryUnit!=null && selectedTile.militaryUnit!!.civInfo == worldScreen.viewingCiv + else if(selectedTile.militaryUnit!=null + && (selectedTile.militaryUnit!!.civInfo == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator()) && selectedUnit!=selectedTile.militaryUnit && (selectedTile.civilianUnit==null || selectedUnit!=selectedTile.civilianUnit)){ selectedUnit = selectedTile.militaryUnit selectedCity = null } - else if (selectedTile.civilianUnit!=null && selectedTile.civilianUnit!!.civInfo == worldScreen.viewingCiv - && selectedUnit!=selectedTile.civilianUnit){ + else if (selectedTile.civilianUnit!=null + && (selectedTile.civilianUnit!!.civInfo == worldScreen.viewingCiv || worldScreen.viewingCiv.isSpectator()) + && selectedUnit!=selectedTile.civilianUnit){ selectedUnit = selectedTile.civilianUnit selectedCity = null } else if(selectedTile == previouslySelectedUnit?.currentTile) {