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 =
|
||||
turn =
|
||||
Next unit =
|
||||
Fog of War =
|
||||
Pick a policy =
|
||||
Movement =
|
||||
Strength =
|
||||
|
|
|
@ -251,6 +251,7 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings)
|
|||
|
||||
private fun updateTileImage(viewingCiv: CivilizationInfo?) {
|
||||
val tileBaseImageLocations = getTileBaseImageLocations(viewingCiv)
|
||||
|
||||
val identifier = tileBaseImageLocations.joinToString(";")
|
||||
if (identifier == tileImagesIdentifier) return
|
||||
|
||||
|
@ -285,11 +286,31 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings)
|
|||
|
||||
fun isViewable(viewingCiv: CivilizationInfo) = showEntireMap
|
||||
|| 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) {
|
||||
|
||||
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()
|
||||
if (viewingCiv != null && !showEntireMap
|
||||
&& !viewingCiv.exploredTiles.contains(tileInfo.position)) {
|
||||
if (viewingCiv != null && !isExplored(viewingCiv)) {
|
||||
clearUnexploredTiles()
|
||||
for(image in tileBaseImages) image.color = Color.DARK_GRAY
|
||||
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
|
||||
private fun updateBorderImages() {
|
||||
// This is longer than it could be, because of performance -
|
||||
// before fixing, about half (!) the time of update() was wasted on
|
||||
// removing all the border images and putting them back again!
|
||||
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
|
||||
if (tileOwner == null) return
|
||||
|
||||
|
|
|
@ -25,14 +25,17 @@ class WorldTileGroup(internal val worldScreen: WorldScreen, tileInfo: TileInfo,
|
|||
|
||||
icons.removePopulationIcon()
|
||||
val tileIsViewable = isViewable(viewingCiv)
|
||||
val showEntireMap = UncivGame.Current.viewEntireMapForDebug
|
||||
|
||||
if (tileIsViewable && tileInfo.isWorked() && UncivGame.Current.settings.showWorkedTiles
|
||||
&& city!!.civInfo == viewingCiv)
|
||||
icons.addPopulationIcon()
|
||||
|
||||
val currentPlayerCiv = worldScreen.viewingCiv
|
||||
if (UncivGame.Current.viewEntireMapForDebug
|
||||
|| currentPlayerCiv.exploredTiles.contains(tileInfo.position))
|
||||
updateCityButton(city, tileIsViewable || UncivGame.Current.viewEntireMapForDebug) // needs to be before the update so the units will be above the city button
|
||||
// update city buttons in explored tiles or entire map
|
||||
if (showEntireMap || viewingCiv.exploredTiles.contains(tileInfo.position))
|
||||
updateCityButton(city, tileIsViewable || showEntireMap) // needs to be before the update so the units will be above the city button
|
||||
else if (worldScreen.viewingCiv.isSpectator())
|
||||
// remove city buttons in unexplored tiles during spectating/fog of war
|
||||
updateCityButton(null, showEntireMap)
|
||||
|
||||
super.update(viewingCiv, UncivGame.Current.settings.showResourcesAndImprovements)
|
||||
}
|
||||
|
|
|
@ -40,8 +40,10 @@ 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
|
||||
var selectedCiv = viewingCiv // Selected civilization, used in spectator and replay mode, equals viewingCiv in ordinary games
|
||||
var fogOfWar = true
|
||||
val canChangeState = isPlayersTurn && !viewingCiv.isSpectator()
|
||||
private var waitingForAutosave = false
|
||||
|
||||
|
@ -56,7 +58,8 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
|||
|
||||
private val techPolicyAndVictoryHolder = Table()
|
||||
private val techButtonHolder = Table()
|
||||
private val diplomacyButtonWrapper = Table()
|
||||
private val diplomacyButtonHolder = Table()
|
||||
private val fogOfWarButton = createFogOfWarButton()
|
||||
private val nextTurnButton = createNextTurnButton()
|
||||
private var nextTurnAction:()->Unit= {}
|
||||
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...
|
||||
lateinit var keyPressDispatcher: HashMap<Char,(() -> Unit)>
|
||||
|
||||
|
||||
init {
|
||||
topBar.setPosition(0f, stage.height - topBar.height)
|
||||
topBar.width = stage.width
|
||||
|
@ -88,6 +92,9 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
|||
}
|
||||
techPolicyAndVictoryHolder.add(techButtonHolder)
|
||||
|
||||
fogOfWarButton.isVisible = viewingCiv.isSpectator()
|
||||
fogOfWarButton.setPosition(10f, topBar.y - fogOfWarButton.height - 10f)
|
||||
|
||||
// Don't show policies until they become relevant
|
||||
if(viewingCiv.policies.adoptedPolicies.isNotEmpty() || viewingCiv.policies.canAdoptPolicy()) {
|
||||
val policyScreenButton = Button(skin)
|
||||
|
@ -106,8 +113,9 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
|||
stage.addActor(tutorialTaskTable)
|
||||
|
||||
|
||||
diplomacyButtonWrapper.defaults().pad(5f)
|
||||
stage.addActor(diplomacyButtonWrapper)
|
||||
diplomacyButtonHolder.defaults().pad(5f)
|
||||
stage.addActor(fogOfWarButton)
|
||||
stage.addActor(diplomacyButtonHolder)
|
||||
stage.addActor(bottomUnitTable)
|
||||
stage.addActor(bottomTileInfoTable)
|
||||
battleTable.width = stage.width/3
|
||||
|
@ -284,6 +292,8 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
|||
|
||||
updateSelectedCiv()
|
||||
|
||||
fogOfWarButton.isEnabled = !selectedCiv.isSpectator()
|
||||
|
||||
tutorialTaskTable.clear()
|
||||
val tutorialTask = getCurrentTutorialTask()
|
||||
if (tutorialTask == "" || !game.settings.showTutorials || viewingCiv.isDefeated()) {
|
||||
|
@ -297,7 +307,9 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
|||
tutorialTaskTable.y = topBar.y - tutorialTaskTable.height
|
||||
}
|
||||
|
||||
minimapWrapper.update(viewingCiv)
|
||||
if (fogOfWar) minimapWrapper.update(selectedCiv)
|
||||
else minimapWrapper.update(viewingCiv)
|
||||
|
||||
cleanupKeyDispatcher()
|
||||
unitActionsTable.update(bottomUnitTable.selectedUnit)
|
||||
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
|
||||
// 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)
|
||||
mapHolder.updateTiles(viewingCiv)
|
||||
if (fogOfWar) mapHolder.updateTiles(selectedCiv)
|
||||
else mapHolder.updateTiles(viewingCiv)
|
||||
|
||||
if (viewingCiv.isSpectator())
|
||||
topBar.update(selectedCiv)
|
||||
else
|
||||
topBar.update(viewingCiv)
|
||||
topBar.update(selectedCiv)
|
||||
|
||||
updateTechButton()
|
||||
techPolicyAndVictoryHolder.pack()
|
||||
techPolicyAndVictoryHolder.setPosition(10f, topBar.y - techPolicyAndVictoryHolder.height - 5f)
|
||||
updateDiplomacyButton(viewingCiv)
|
||||
|
||||
|
||||
if (!hasOpenPopups() && isPlayersTurn) {
|
||||
when {
|
||||
!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) {
|
||||
diplomacyButtonWrapper.clear()
|
||||
diplomacyButtonHolder.clear()
|
||||
if(!civInfo.isDefeated() && !civInfo.isSpectator() && civInfo.getKnownCivs()
|
||||
.filterNot { it==viewingCiv || it.isBarbarian() }
|
||||
.any()) {
|
||||
|
@ -417,10 +428,10 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
|||
btn.onClick { game.setScreen(DiplomacyScreen(viewingCiv)) }
|
||||
btn.label.setFontSize(30)
|
||||
btn.labelCell.pad(10f)
|
||||
diplomacyButtonWrapper.add(btn)
|
||||
diplomacyButtonHolder.add(btn)
|
||||
}
|
||||
diplomacyButtonWrapper.pack()
|
||||
diplomacyButtonWrapper.y = techPolicyAndVictoryHolder.y - 20 - diplomacyButtonWrapper.height
|
||||
diplomacyButtonHolder.pack()
|
||||
diplomacyButtonHolder.y = techPolicyAndVictoryHolder.y - 20 - diplomacyButtonHolder.height
|
||||
}
|
||||
|
||||
private fun updateTechButton() {
|
||||
|
@ -460,6 +471,19 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() {
|
|||
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 {
|
||||
|
||||
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.scaleY = mapHolder.scaleY
|
||||
newWorldScreen.mapHolder.updateVisualScroll()
|
||||
|
||||
newWorldScreen.selectedCiv = gameInfoClone.getCivilization(selectedCiv.civName)
|
||||
newWorldScreen.fogOfWar = fogOfWar
|
||||
|
||||
game.worldScreen = newWorldScreen
|
||||
game.setWorldScreen()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue