Fog of war implementation (#2887)
* Initial implementation fog of war for spectators * Corrected fog of war for main map * Fix borders seen in unexplored tiles during spectating * Small refactor * Fixes after code review * Fixes after code review * wierd bug fix, android studio "eats" spaces
This commit is contained in:
parent
275348c70e
commit
86f500b266
4 changed files with 84 additions and 27 deletions
|
@ -509,6 +509,7 @@ Turn =
|
||||||
turns =
|
turns =
|
||||||
turn =
|
turn =
|
||||||
Next unit =
|
Next unit =
|
||||||
|
Fog of War =
|
||||||
Pick a policy =
|
Pick a policy =
|
||||||
Movement =
|
Movement =
|
||||||
Strength =
|
Strength =
|
||||||
|
|
|
@ -251,6 +251,7 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings)
|
||||||
|
|
||||||
private fun updateTileImage(viewingCiv: CivilizationInfo?) {
|
private fun updateTileImage(viewingCiv: CivilizationInfo?) {
|
||||||
val tileBaseImageLocations = getTileBaseImageLocations(viewingCiv)
|
val tileBaseImageLocations = getTileBaseImageLocations(viewingCiv)
|
||||||
|
|
||||||
val identifier = tileBaseImageLocations.joinToString(";")
|
val identifier = tileBaseImageLocations.joinToString(";")
|
||||||
if (identifier == tileImagesIdentifier) return
|
if (identifier == tileImagesIdentifier) return
|
||||||
|
|
||||||
|
@ -285,11 +286,31 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings)
|
||||||
|
|
||||||
fun isViewable(viewingCiv: CivilizationInfo) = showEntireMap
|
fun isViewable(viewingCiv: CivilizationInfo) = showEntireMap
|
||||||
|| viewingCiv.viewableTiles.contains(tileInfo)
|
|| viewingCiv.viewableTiles.contains(tileInfo)
|
||||||
|
|| viewingCiv.isSpectator()
|
||||||
|
|
||||||
|
fun isExplored(viewingCiv: CivilizationInfo) = showEntireMap
|
||||||
|
|| viewingCiv.exploredTiles.contains(tileInfo.position)
|
||||||
|
|| viewingCiv.isSpectator()
|
||||||
|
|
||||||
open fun update(viewingCiv: CivilizationInfo? = null, showResourcesAndImprovements: Boolean = true) {
|
open fun update(viewingCiv: CivilizationInfo? = null, showResourcesAndImprovements: Boolean = true) {
|
||||||
|
|
||||||
|
fun clearUnexploredTiles() {
|
||||||
|
updateTileImage(null)
|
||||||
|
updateRivers(false,false, false)
|
||||||
|
|
||||||
|
updatePixelMilitaryUnit(false)
|
||||||
|
updatePixelCivilianUnit(false)
|
||||||
|
|
||||||
|
if (borderImages.isNotEmpty()) clearBorders()
|
||||||
|
|
||||||
|
icons.update(false, false, false, null)
|
||||||
|
|
||||||
|
fogImage.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
hideCircle()
|
hideCircle()
|
||||||
if (viewingCiv != null && !showEntireMap
|
if (viewingCiv != null && !isExplored(viewingCiv)) {
|
||||||
&& !viewingCiv.exploredTiles.contains(tileInfo.position)) {
|
clearUnexploredTiles()
|
||||||
for(image in tileBaseImages) image.color = Color.DARK_GRAY
|
for(image in tileBaseImages) image.color = Color.DARK_GRAY
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -393,19 +414,24 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun clearBorders() {
|
||||||
|
for (images in borderImages.values)
|
||||||
|
for (image in images)
|
||||||
|
image.remove()
|
||||||
|
|
||||||
|
borderImages.clear()
|
||||||
|
}
|
||||||
|
|
||||||
var previousTileOwner: CivilizationInfo? = null
|
var previousTileOwner: CivilizationInfo? = null
|
||||||
private fun updateBorderImages() {
|
private fun updateBorderImages() {
|
||||||
// This is longer than it could be, because of performance -
|
// This is longer than it could be, because of performance -
|
||||||
// before fixing, about half (!) the time of update() was wasted on
|
// before fixing, about half (!) the time of update() was wasted on
|
||||||
// removing all the border images and putting them back again!
|
// removing all the border images and putting them back again!
|
||||||
val tileOwner = tileInfo.getOwner()
|
val tileOwner = tileInfo.getOwner()
|
||||||
if (previousTileOwner != tileOwner) {
|
|
||||||
for (images in borderImages.values)
|
|
||||||
for (image in images)
|
|
||||||
image.remove()
|
|
||||||
|
|
||||||
borderImages.clear()
|
|
||||||
}
|
if (previousTileOwner != tileOwner) clearBorders()
|
||||||
|
|
||||||
previousTileOwner = tileOwner
|
previousTileOwner = tileOwner
|
||||||
if (tileOwner == null) return
|
if (tileOwner == null) return
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,17 @@ class WorldTileGroup(internal val worldScreen: WorldScreen, tileInfo: TileInfo,
|
||||||
|
|
||||||
icons.removePopulationIcon()
|
icons.removePopulationIcon()
|
||||||
val tileIsViewable = isViewable(viewingCiv)
|
val tileIsViewable = isViewable(viewingCiv)
|
||||||
|
val showEntireMap = UncivGame.Current.viewEntireMapForDebug
|
||||||
|
|
||||||
if (tileIsViewable && tileInfo.isWorked() && UncivGame.Current.settings.showWorkedTiles
|
if (tileIsViewable && tileInfo.isWorked() && UncivGame.Current.settings.showWorkedTiles
|
||||||
&& city!!.civInfo == viewingCiv)
|
&& city!!.civInfo == viewingCiv)
|
||||||
icons.addPopulationIcon()
|
icons.addPopulationIcon()
|
||||||
|
// update city buttons in explored tiles or entire map
|
||||||
val currentPlayerCiv = worldScreen.viewingCiv
|
if (showEntireMap || viewingCiv.exploredTiles.contains(tileInfo.position))
|
||||||
if (UncivGame.Current.viewEntireMapForDebug
|
updateCityButton(city, tileIsViewable || showEntireMap) // needs to be before the update so the units will be above the city button
|
||||||
|| currentPlayerCiv.exploredTiles.contains(tileInfo.position))
|
else if (worldScreen.viewingCiv.isSpectator())
|
||||||
updateCityButton(city, tileIsViewable || UncivGame.Current.viewEntireMapForDebug) // needs to be before the update so the units will be above the city button
|
// remove city buttons in unexplored tiles during spectating/fog of war
|
||||||
|
updateCityButton(null, showEntireMap)
|
||||||
|
|
||||||
super.update(viewingCiv, UncivGame.Current.settings.showResourcesAndImprovements)
|
super.update(viewingCiv, UncivGame.Current.settings.showResourcesAndImprovements)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,10 @@ import kotlin.concurrent.thread
|
||||||
|
|
||||||
class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
val gameInfo = game.gameInfo
|
val gameInfo = game.gameInfo
|
||||||
|
|
||||||
var isPlayersTurn = viewingCiv == gameInfo.currentPlayerCiv // todo this should be updated when passing turns
|
var isPlayersTurn = viewingCiv == gameInfo.currentPlayerCiv // todo this should be updated when passing turns
|
||||||
var selectedCiv = viewingCiv // Selected civilization, used only in spectator mode
|
var selectedCiv = viewingCiv // Selected civilization, used in spectator and replay mode, equals viewingCiv in ordinary games
|
||||||
|
var fogOfWar = true
|
||||||
val canChangeState = isPlayersTurn && !viewingCiv.isSpectator()
|
val canChangeState = isPlayersTurn && !viewingCiv.isSpectator()
|
||||||
private var waitingForAutosave = false
|
private var waitingForAutosave = false
|
||||||
|
|
||||||
|
@ -56,7 +58,8 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
|
|
||||||
private val techPolicyAndVictoryHolder = Table()
|
private val techPolicyAndVictoryHolder = Table()
|
||||||
private val techButtonHolder = Table()
|
private val techButtonHolder = Table()
|
||||||
private val diplomacyButtonWrapper = Table()
|
private val diplomacyButtonHolder = Table()
|
||||||
|
private val fogOfWarButton = createFogOfWarButton()
|
||||||
private val nextTurnButton = createNextTurnButton()
|
private val nextTurnButton = createNextTurnButton()
|
||||||
private var nextTurnAction:()->Unit= {}
|
private var nextTurnAction:()->Unit= {}
|
||||||
private val tutorialTaskTable = Table().apply { background=ImageGetter.getBackground(ImageGetter.getBlue().lerp(Color.BLACK, 0.5f)) }
|
private val tutorialTaskTable = Table().apply { background=ImageGetter.getBackground(ImageGetter.getBlue().lerp(Color.BLACK, 0.5f)) }
|
||||||
|
@ -70,6 +73,7 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
// An initialized val always turned out to illegally be null...
|
// An initialized val always turned out to illegally be null...
|
||||||
lateinit var keyPressDispatcher: HashMap<Char,(() -> Unit)>
|
lateinit var keyPressDispatcher: HashMap<Char,(() -> Unit)>
|
||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
topBar.setPosition(0f, stage.height - topBar.height)
|
topBar.setPosition(0f, stage.height - topBar.height)
|
||||||
topBar.width = stage.width
|
topBar.width = stage.width
|
||||||
|
@ -88,6 +92,9 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
}
|
}
|
||||||
techPolicyAndVictoryHolder.add(techButtonHolder)
|
techPolicyAndVictoryHolder.add(techButtonHolder)
|
||||||
|
|
||||||
|
fogOfWarButton.isVisible = viewingCiv.isSpectator()
|
||||||
|
fogOfWarButton.setPosition(10f, topBar.y - fogOfWarButton.height - 10f)
|
||||||
|
|
||||||
// Don't show policies until they become relevant
|
// Don't show policies until they become relevant
|
||||||
if(viewingCiv.policies.adoptedPolicies.isNotEmpty() || viewingCiv.policies.canAdoptPolicy()) {
|
if(viewingCiv.policies.adoptedPolicies.isNotEmpty() || viewingCiv.policies.canAdoptPolicy()) {
|
||||||
val policyScreenButton = Button(skin)
|
val policyScreenButton = Button(skin)
|
||||||
|
@ -106,8 +113,9 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
stage.addActor(tutorialTaskTable)
|
stage.addActor(tutorialTaskTable)
|
||||||
|
|
||||||
|
|
||||||
diplomacyButtonWrapper.defaults().pad(5f)
|
diplomacyButtonHolder.defaults().pad(5f)
|
||||||
stage.addActor(diplomacyButtonWrapper)
|
stage.addActor(fogOfWarButton)
|
||||||
|
stage.addActor(diplomacyButtonHolder)
|
||||||
stage.addActor(bottomUnitTable)
|
stage.addActor(bottomUnitTable)
|
||||||
stage.addActor(bottomTileInfoTable)
|
stage.addActor(bottomTileInfoTable)
|
||||||
battleTable.width = stage.width/3
|
battleTable.width = stage.width/3
|
||||||
|
@ -284,6 +292,8 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
|
|
||||||
updateSelectedCiv()
|
updateSelectedCiv()
|
||||||
|
|
||||||
|
fogOfWarButton.isEnabled = !selectedCiv.isSpectator()
|
||||||
|
|
||||||
tutorialTaskTable.clear()
|
tutorialTaskTable.clear()
|
||||||
val tutorialTask = getCurrentTutorialTask()
|
val tutorialTask = getCurrentTutorialTask()
|
||||||
if (tutorialTask == "" || !game.settings.showTutorials || viewingCiv.isDefeated()) {
|
if (tutorialTask == "" || !game.settings.showTutorials || viewingCiv.isDefeated()) {
|
||||||
|
@ -297,7 +307,9 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
tutorialTaskTable.y = topBar.y - tutorialTaskTable.height
|
tutorialTaskTable.y = topBar.y - tutorialTaskTable.height
|
||||||
}
|
}
|
||||||
|
|
||||||
minimapWrapper.update(viewingCiv)
|
if (fogOfWar) minimapWrapper.update(selectedCiv)
|
||||||
|
else minimapWrapper.update(viewingCiv)
|
||||||
|
|
||||||
cleanupKeyDispatcher()
|
cleanupKeyDispatcher()
|
||||||
unitActionsTable.update(bottomUnitTable.selectedUnit)
|
unitActionsTable.update(bottomUnitTable.selectedUnit)
|
||||||
unitActionsTable.y = bottomUnitTable.height
|
unitActionsTable.y = bottomUnitTable.height
|
||||||
|
@ -305,18 +317,17 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
// if we use the clone, then when we update viewable tiles
|
// if we use the clone, then when we update viewable tiles
|
||||||
// it doesn't update the explored tiles of the civ... need to think about that harder
|
// it doesn't update the explored tiles of the civ... need to think about that harder
|
||||||
// it causes a bug when we move a unit to an unexplored tile (for instance a cavalry unit which can move far)
|
// 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)
|
if (fogOfWar) mapHolder.updateTiles(selectedCiv)
|
||||||
|
else mapHolder.updateTiles(viewingCiv)
|
||||||
|
|
||||||
if (viewingCiv.isSpectator())
|
topBar.update(selectedCiv)
|
||||||
topBar.update(selectedCiv)
|
|
||||||
else
|
|
||||||
topBar.update(viewingCiv)
|
|
||||||
|
|
||||||
updateTechButton()
|
updateTechButton()
|
||||||
techPolicyAndVictoryHolder.pack()
|
techPolicyAndVictoryHolder.pack()
|
||||||
techPolicyAndVictoryHolder.setPosition(10f, topBar.y - techPolicyAndVictoryHolder.height - 5f)
|
techPolicyAndVictoryHolder.setPosition(10f, topBar.y - techPolicyAndVictoryHolder.height - 5f)
|
||||||
updateDiplomacyButton(viewingCiv)
|
updateDiplomacyButton(viewingCiv)
|
||||||
|
|
||||||
|
|
||||||
if (!hasOpenPopups() && isPlayersTurn) {
|
if (!hasOpenPopups() && isPlayersTurn) {
|
||||||
when {
|
when {
|
||||||
!gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.civilizations.any { it.victoryManager.hasWon() }) ->
|
!gameInfo.oneMoreTurnMode && (viewingCiv.isDefeated() || gameInfo.civilizations.any { it.victoryManager.hasWon() }) ->
|
||||||
|
@ -408,7 +419,7 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateDiplomacyButton(civInfo: CivilizationInfo) {
|
private fun updateDiplomacyButton(civInfo: CivilizationInfo) {
|
||||||
diplomacyButtonWrapper.clear()
|
diplomacyButtonHolder.clear()
|
||||||
if(!civInfo.isDefeated() && !civInfo.isSpectator() && civInfo.getKnownCivs()
|
if(!civInfo.isDefeated() && !civInfo.isSpectator() && civInfo.getKnownCivs()
|
||||||
.filterNot { it==viewingCiv || it.isBarbarian() }
|
.filterNot { it==viewingCiv || it.isBarbarian() }
|
||||||
.any()) {
|
.any()) {
|
||||||
|
@ -417,10 +428,10 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
btn.onClick { game.setScreen(DiplomacyScreen(viewingCiv)) }
|
btn.onClick { game.setScreen(DiplomacyScreen(viewingCiv)) }
|
||||||
btn.label.setFontSize(30)
|
btn.label.setFontSize(30)
|
||||||
btn.labelCell.pad(10f)
|
btn.labelCell.pad(10f)
|
||||||
diplomacyButtonWrapper.add(btn)
|
diplomacyButtonHolder.add(btn)
|
||||||
}
|
}
|
||||||
diplomacyButtonWrapper.pack()
|
diplomacyButtonHolder.pack()
|
||||||
diplomacyButtonWrapper.y = techPolicyAndVictoryHolder.y - 20 - diplomacyButtonWrapper.height
|
diplomacyButtonHolder.y = techPolicyAndVictoryHolder.y - 20 - diplomacyButtonHolder.height
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTechButton() {
|
private fun updateTechButton() {
|
||||||
|
@ -460,6 +471,19 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
else viewingCiv
|
else viewingCiv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createFogOfWarButton(): TextButton {
|
||||||
|
val fogOfWarButton = "Fog of War".toTextButton()
|
||||||
|
fogOfWarButton.label.setFontSize(30)
|
||||||
|
fogOfWarButton.labelCell.pad(10f)
|
||||||
|
fogOfWarButton.pack()
|
||||||
|
fogOfWarButton.onClick {
|
||||||
|
fogOfWar = !fogOfWar
|
||||||
|
shouldUpdate = true
|
||||||
|
}
|
||||||
|
return fogOfWarButton
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private fun createNextTurnButton(): TextButton {
|
private fun createNextTurnButton(): TextButton {
|
||||||
|
|
||||||
val nextTurnButton = TextButton("", skin) // text is set in update()
|
val nextTurnButton = TextButton("", skin) // text is set in update()
|
||||||
|
@ -520,7 +544,10 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
||||||
newWorldScreen.mapHolder.scaleX = mapHolder.scaleX
|
newWorldScreen.mapHolder.scaleX = mapHolder.scaleX
|
||||||
newWorldScreen.mapHolder.scaleY = mapHolder.scaleY
|
newWorldScreen.mapHolder.scaleY = mapHolder.scaleY
|
||||||
newWorldScreen.mapHolder.updateVisualScroll()
|
newWorldScreen.mapHolder.updateVisualScroll()
|
||||||
|
|
||||||
newWorldScreen.selectedCiv = gameInfoClone.getCivilization(selectedCiv.civName)
|
newWorldScreen.selectedCiv = gameInfoClone.getCivilization(selectedCiv.civName)
|
||||||
|
newWorldScreen.fogOfWar = fogOfWar
|
||||||
|
|
||||||
game.worldScreen = newWorldScreen
|
game.worldScreen = newWorldScreen
|
||||||
game.setWorldScreen()
|
game.setWorldScreen()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue