From 015b8343c769ed5aa8f02aa79fec49e952f2c40a Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Fri, 30 Aug 2019 14:46:52 +0300 Subject: [PATCH] Hoh boy so many changes to ensure the regular game works with multiplayer... --- android/build.gradle | 4 +- core/src/com/unciv/UnCivGame.kt | 22 +++--- core/src/com/unciv/logic/GameInfo.kt | 9 +++ .../logic/civilization/CivilizationInfo.kt | 3 +- core/src/com/unciv/logic/map/TileInfo.kt | 4 +- .../unciv/logic/map/UnitMovementAlgorithms.kt | 69 ++++++++++--------- .../com/unciv/models/metadata/GameSettings.kt | 4 +- .../com/unciv/ui/cityscreen/CityTileGroup.kt | 8 +-- .../ui/mapeditor/MapEditorOptionsTable.kt | 2 +- .../com/unciv/ui/mapeditor/MapEditorScreen.kt | 4 +- .../ui/mapeditor/TileEditorOptionsTable.kt | 4 +- .../unciv/ui/newgamescreen/NewGameScreen.kt | 3 + .../NewGameScreenOptionsTable.kt | 5 +- .../ui/newgamescreen/PlayerPickerTable.kt | 2 +- core/src/com/unciv/ui/tilegroups/TileGroup.kt | 23 +++++-- .../com/unciv/ui/tilegroups/WorldTileGroup.kt | 11 +-- .../com/unciv/ui/worldscreen/TileMapHolder.kt | 29 +++----- .../com/unciv/ui/worldscreen/WorldScreen.kt | 66 +++++++++--------- .../ui/worldscreen/bottombar/BattleTable.kt | 2 +- .../ui/worldscreen/optionstable/DropBox.kt | 9 +-- .../optionstable/WorldScreenMenuTable.kt | 5 +- .../ui/worldscreen/unit/UnitContextMenu.kt | 25 ++++--- 22 files changed, 169 insertions(+), 144 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index fb12bde1c8..c00e9beb8b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,8 +21,8 @@ android { applicationId "com.unciv.app" minSdkVersion 14 targetSdkVersion 28 - versionCode 290 - versionName "2.19.9" + versionCode 292 + versionName "2.19.10" } // Had to add this crap for Travis to build, it wanted to sign the app diff --git a/core/src/com/unciv/UnCivGame.kt b/core/src/com/unciv/UnCivGame.kt index 059eb14749..45eee8cc46 100644 --- a/core/src/com/unciv/UnCivGame.kt +++ b/core/src/com/unciv/UnCivGame.kt @@ -1,5 +1,6 @@ package com.unciv +import com.badlogic.gdx.Application import com.badlogic.gdx.Game import com.badlogic.gdx.Gdx import com.badlogic.gdx.Input @@ -12,6 +13,7 @@ import com.unciv.models.metadata.GameSettings import com.unciv.ui.LanguagePickerScreen import com.unciv.ui.utils.ImageGetter import com.unciv.ui.worldscreen.WorldScreen +import java.util.* class UnCivGame(val version: String) : Game() { var gameInfo: GameInfo = GameInfo() @@ -21,18 +23,25 @@ class UnCivGame(val version: String) : Game() { * This exists so that when debugging we can see the entire map. * Remember to turn this to false before commit and upload! */ - val viewEntireMapForDebug = false + var viewEntireMapForDebug = false /** For when you need to test something in an advanced game and don't have time to faff around */ val superchargedForDebug = false + val mutiplayerEnabled = false lateinit var worldScreen: WorldScreen override fun create() { Current = this + if(Gdx.app.type!= Application.ApplicationType.Desktop) + viewEntireMapForDebug=false Gdx.input.setCatchKey(Input.Keys.BACK, true) GameBasics.run { } // just to initialize the GameBasics settings = GameSaver().getGeneralSettings() + if(settings.userId=="") { // assign permanent user id + settings.userId = UUID.randomUUID().toString() + settings.save() + } if (GameSaver().getSave("Autosave").exists()) { try { loadGame("Autosave") @@ -45,7 +54,8 @@ class UnCivGame(val version: String) : Game() { fun loadGame(gameInfo:GameInfo){ this.gameInfo = gameInfo - worldScreen = WorldScreen(gameInfo.currentPlayerCiv) + + worldScreen = WorldScreen(gameInfo.getPlayerToViewAs()) setWorldScreen() } @@ -55,10 +65,7 @@ class UnCivGame(val version: String) : Game() { fun startNewGame() { val newGame = GameStarter().startNewGame(GameParameters().apply { difficulty="Chieftain" }) - gameInfo = newGame - - worldScreen = WorldScreen(gameInfo.currentPlayerCiv) - setWorldScreen() + loadGame(newGame) } fun setWorldScreen() { @@ -80,8 +87,7 @@ class UnCivGame(val version: String) : Game() { return create() if(::worldScreen.isInitialized) worldScreen.dispose() // I hope this will solve some of the many OuOfMemory exceptions... - worldScreen = WorldScreen(gameInfo.currentPlayerCiv) - setWorldScreen() + loadGame(gameInfo) } // Maybe this will solve the resume error on chrome OS, issue 322? Worth a shot diff --git a/core/src/com/unciv/logic/GameInfo.kt b/core/src/com/unciv/logic/GameInfo.kt index 0f30deb01a..9c4231717f 100644 --- a/core/src/com/unciv/logic/GameInfo.kt +++ b/core/src/com/unciv/logic/GameInfo.kt @@ -2,6 +2,7 @@ package com.unciv.logic import com.badlogic.gdx.graphics.Color import com.unciv.Constants +import com.unciv.UnCivGame import com.unciv.logic.automation.NextTurnAutomation import com.unciv.logic.city.CityConstructions import com.unciv.logic.civilization.CivilizationInfo @@ -39,9 +40,17 @@ class GameInfo { toReturn.turns = turns toReturn.difficulty=difficulty toReturn.gameParameters = gameParameters + toReturn.gameId = gameId return toReturn } + fun getPlayerToViewAs(): CivilizationInfo { + if (!gameParameters.isOnlineMultiplayer) return currentPlayerCiv // non-online, play as human player + val userId = UnCivGame.Current.settings.userId + if (civilizations.any { it.playerId == userId}) return civilizations.first { it.playerId == userId } + else return getBarbarianCivilization()// you aren't anyone. How did you even get this game? Can you spectate? + } + fun getCivilization(civName:String) = civilizations.first { it.civName==civName } fun getCurrentPlayerCivilization() = currentPlayerCiv fun getBarbarianCivilization() = getCivilization("Barbarians") diff --git a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt index 25a59e2c73..aecfc69f35 100644 --- a/core/src/com/unciv/logic/civilization/CivilizationInfo.kt +++ b/core/src/com/unciv/logic/civilization/CivilizationInfo.kt @@ -76,13 +76,14 @@ class CivilizationInfo { val toReturn = CivilizationInfo() toReturn.gold = gold toReturn.playerType = playerType + toReturn.playerId = playerId toReturn.civName = civName toReturn.tech = tech.clone() toReturn.policies = policies.clone() toReturn.goldenAges = goldenAges.clone() toReturn.greatPeople = greatPeople.clone() toReturn.victoryManager = victoryManager.clone() - for(diplomacyManager in diplomacy.values.map { it.clone() }) + for (diplomacyManager in diplomacy.values.map { it.clone() }) toReturn.diplomacy.put(diplomacyManager.otherCivName, diplomacyManager) toReturn.cities = cities.map { it.clone() } diff --git a/core/src/com/unciv/logic/map/TileInfo.kt b/core/src/com/unciv/logic/map/TileInfo.kt index b8b4f22a80..73bdc6db5f 100644 --- a/core/src/com/unciv/logic/map/TileInfo.kt +++ b/core/src/com/unciv/logic/map/TileInfo.kt @@ -310,10 +310,10 @@ open class TileInfo { turnsToImprovement = improvement.getTurnsToBuild(civInfo) } - fun hasEnemySubmarine(): Boolean { + fun hasEnemySubmarine(viewingCiv:CivilizationInfo): Boolean { val unitsInTile = getUnits() if (unitsInTile.isEmpty()) return false - if (!unitsInTile.first().civInfo.isPlayerCivilization() && + if (unitsInTile.first().civInfo!=viewingCiv && unitsInTile.firstOrNull { it.isInvisible() } != null) { return true } diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 0b1355af54..c650781f55 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -135,44 +135,45 @@ class UnitMovementAlgorithms(val unit:MapUnit) { } } + fun getTileToMoveToThisTurn(finalDestination:TileInfo): TileInfo { + + val currentTile = unit.getTile() + if (currentTile == finalDestination) return currentTile + + if(unit.type.isAirUnit()){ + return finalDestination // head there directly + } + + + val distanceToTiles = getDistanceToTiles() + + if (distanceToTiles.containsKey(finalDestination)) { // we should be able to get there this turn + if (canMoveTo(finalDestination)) + return finalDestination + + // Someone is blocking to the path to the final tile... + val destinationNeighbors = finalDestination.neighbors + if (destinationNeighbors.contains(currentTile)) // We're right nearby anyway, no need to move + return currentTile + + val reachableDestinationNeighbors = destinationNeighbors + .filter { distanceToTiles.containsKey(it) && canMoveTo(it) } + if (reachableDestinationNeighbors.isEmpty()) // We can't get closer... + return currentTile + + return reachableDestinationNeighbors.minBy { distanceToTiles[it]!!.totalDistance }!! // we can get a little closer + } // If the tile is far away, we need to build a path how to get there, and then take the first step + val path = getShortestPath(finalDestination) + class UnreachableDestinationException : Exception() + if (path.isEmpty()) throw UnreachableDestinationException() + return path.first() + } + /** * @return The tile that we reached this turn */ fun headTowards(destination: TileInfo): TileInfo { - val currentTile = unit.getTile() - if (currentTile == destination) return currentTile - - if(unit.type.isAirUnit()){ - moveToTile(destination) - return destination - } - - val distanceToTiles = getDistanceToTiles() - - val destinationTileThisTurn: TileInfo - if (distanceToTiles.containsKey(destination)) { // we can get there this turn - if (canMoveTo(destination)) - destinationTileThisTurn = destination - else // Someone is blocking to the path to the final tile... - { - val destinationNeighbors = destination.neighbors - if (destinationNeighbors.contains(currentTile)) // We're right nearby anyway, no need to move - return currentTile - - val reachableDestinationNeighbors = destinationNeighbors - .filter { distanceToTiles.containsKey(it) && canMoveTo(it) } - if (reachableDestinationNeighbors.isEmpty()) // We can't get closer... - return currentTile - - destinationTileThisTurn = reachableDestinationNeighbors.minBy { distanceToTiles[it]!!.totalDistance }!! - } - } else { // If the tile is far away, we need to build a path how to get there, and then take the first step - val path = getShortestPath(destination) - class UnreachableDestinationException : Exception() - if (path.isEmpty()) throw UnreachableDestinationException() - destinationTileThisTurn = path.first() - } - + val destinationTileThisTurn = getTileToMoveToThisTurn(destination) moveToTile(destinationTileThisTurn) return destinationTileThisTurn } diff --git a/core/src/com/unciv/models/metadata/GameSettings.kt b/core/src/com/unciv/models/metadata/GameSettings.kt index 213838b711..d377815648 100644 --- a/core/src/com/unciv/models/metadata/GameSettings.kt +++ b/core/src/com/unciv/models/metadata/GameSettings.kt @@ -1,8 +1,6 @@ package com.unciv.models.metadata import com.unciv.logic.GameSaver -import java.util.* -import kotlin.collections.ArrayList class GameSettings { var showWorkedTiles: Boolean = false @@ -20,7 +18,7 @@ class GameSettings { var autoAssignCityProduction: Boolean = true var userName:String="" - var userId = UUID.randomUUID().toString() + var userId = "" fun save(){ GameSaver().setGeneralSettings(this) diff --git a/core/src/com/unciv/ui/cityscreen/CityTileGroup.kt b/core/src/com/unciv/ui/cityscreen/CityTileGroup.kt index fcb4d6bf36..37d68e0725 100644 --- a/core/src/com/unciv/ui/cityscreen/CityTileGroup.kt +++ b/core/src/com/unciv/ui/cityscreen/CityTileGroup.kt @@ -2,7 +2,6 @@ package com.unciv.ui.cityscreen import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.utils.Align -import com.unciv.UnCivGame import com.unciv.logic.city.CityInfo import com.unciv.logic.map.TileInfo import com.unciv.ui.tilegroups.TileGroup @@ -26,12 +25,7 @@ class CityTileGroup(private val city: CityInfo, tileInfo: TileInfo, tileSetStrin } fun update() { - val canSeeTile = UnCivGame.Current.viewEntireMapForDebug - || city.civInfo.viewableTiles.contains(tileInfo) - val showSubmarine = UnCivGame.Current.viewEntireMapForDebug - || city.civInfo.viewableInvisibleUnitsTiles.contains(tileInfo) - || (!tileInfo.hasEnemySubmarine()) - super.update(canSeeTile,true, showSubmarine) + super.update(city.civInfo,true) // this needs to happen on update, because we can buy tiles, which changes the definition of the bought tiles... when { diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorOptionsTable.kt b/core/src/com/unciv/ui/mapeditor/MapEditorOptionsTable.kt index 2b72bf3161..3bd211c70d 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorOptionsTable.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorOptionsTable.kt @@ -37,7 +37,7 @@ class MapEditorOptionsTable(mapEditorScreen: MapEditorScreen): PopupTable(mapEdi tile.roadStatus=RoadStatus.None tile.setTransients() - tileGroup.update(true,true,true) + tileGroup.update() } } add(clearCurrentMapButton).row() diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt b/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt index d8cff12d9c..9a1e1e0ccd 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt @@ -66,13 +66,13 @@ class MapEditorScreen(): CameraStageBaseScreen(){ val tileGroups = tileMap.values.map { TileGroup(it, tileSetStrings) } for (tileGroup in tileGroups) { tileGroup.showEntireMap = true - tileGroup.update(true, true, true) + tileGroup.update() tileGroup.onClick { val tileInfo = tileGroup.tileInfo tileEditorOptions.updateTileWhenClicked(tileInfo) tileGroup.tileInfo.setTransients() - tileGroup.update(true, true, true) + tileGroup.update() } } diff --git a/core/src/com/unciv/ui/mapeditor/TileEditorOptionsTable.kt b/core/src/com/unciv/ui/mapeditor/TileEditorOptionsTable.kt index f1e1fc3d81..72f1e7a568 100644 --- a/core/src/com/unciv/ui/mapeditor/TileEditorOptionsTable.kt +++ b/core/src/com/unciv/ui/mapeditor/TileEditorOptionsTable.kt @@ -122,7 +122,7 @@ class TileEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(Camera val group = TileGroup(tileInfo, TileSetStrings()) group.showEntireMap=true group.forMapEditorIcon=true - group.update(true,true,true) + group.update() group.onClick { clearSelection() @@ -249,7 +249,7 @@ class TileEditorOptionsTable(val mapEditorScreen: MapEditorScreen): Table(Camera .apply { showEntireMap=true forMapEditorIcon=true - update(true,true,true) + update() } setCurrentHex(tileGroup) } diff --git a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt index a8b62579e7..31e451e93d 100644 --- a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt +++ b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt @@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Skin import com.badlogic.gdx.utils.Array import com.unciv.UnCivGame import com.unciv.logic.GameInfo +import com.unciv.logic.GameSaver import com.unciv.logic.GameStarter import com.unciv.logic.civilization.PlayerType import com.unciv.models.gamebasics.tr @@ -66,8 +67,10 @@ class NewGameScreen: PickerScreen(){ try { newGame = GameStarter().startNewGame(newGameParameters) if (newGameParameters.isOnlineMultiplayer) { + newGame!!.isUpToDate=true // So we don't try to download it from dropbox the second after we upload it - the file is not yet ready for loading! try { OnlineMultiplayer().tryUploadGame(newGame!!) + GameSaver().autoSave(newGame!!){} } catch (ex: Exception) { val cantUploadNewGamePopup = PopupTable(this) cantUploadNewGamePopup.addGoodSizedLabel("Can't upload the new game!") diff --git a/core/src/com/unciv/ui/newgamescreen/NewGameScreenOptionsTable.kt b/core/src/com/unciv/ui/newgamescreen/NewGameScreenOptionsTable.kt index 484e23cb6e..5d976adf11 100644 --- a/core/src/com/unciv/ui/newgamescreen/NewGameScreenOptionsTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/NewGameScreenOptionsTable.kt @@ -6,6 +6,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.SelectBox import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener import com.badlogic.gdx.utils.Array +import com.unciv.UnCivGame import com.unciv.logic.MapSaver import com.unciv.logic.map.MapType import com.unciv.models.gamebasics.GameBasics @@ -25,7 +26,9 @@ class NewGameScreenOptionsTable(val newGameParameters: GameParameters, val onMul addCityStatesSelectBox() addVictoryTypeCheckboxes() addBarbariansCheckbox() - //addIsOnlineMultiplayerCheckbox() + + if(UnCivGame.Current.mutiplayerEnabled) + addIsOnlineMultiplayerCheckbox() pack() } diff --git a/core/src/com/unciv/ui/newgamescreen/PlayerPickerTable.kt b/core/src/com/unciv/ui/newgamescreen/PlayerPickerTable.kt index 43a0f7b35a..5c2c3b7967 100644 --- a/core/src/com/unciv/ui/newgamescreen/PlayerPickerTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/PlayerPickerTable.kt @@ -59,7 +59,7 @@ class PlayerPickerTable(val newGameScreen: NewGameScreen, val newGameParameters: val playerIdTable = Table() playerIdTable.add("Player ID:".toLabel()) - val playerIdTextfield = TextField("", CameraStageBaseScreen.skin) + val playerIdTextfield = TextField(player.playerId, CameraStageBaseScreen.skin) playerIdTable.add(playerIdTextfield) val errorLabel = "Not a valid user id!".toLabel().setFontColor(Color.RED) errorLabel.isVisible=false diff --git a/core/src/com/unciv/ui/tilegroups/TileGroup.kt b/core/src/com/unciv/ui/tilegroups/TileGroup.kt index 443b646008..3416816238 100644 --- a/core/src/com/unciv/ui/tilegroups/TileGroup.kt +++ b/core/src/com/unciv/ui/tilegroups/TileGroup.kt @@ -185,33 +185,42 @@ open class TileGroup(var tileInfo: TileInfo, var tileSetStrings:TileSetStrings) } } + fun showMilitaryUnit(viewingCiv: CivilizationInfo) = showEntireMap + || viewingCiv.viewableInvisibleUnitsTiles.contains(tileInfo) + || (!tileInfo.hasEnemySubmarine(viewingCiv)) - open fun update(isViewable: Boolean, showResourcesAndImprovements:Boolean, showSubmarine: Boolean) { + fun isViewable(viewingCiv: CivilizationInfo) = showEntireMap + || viewingCiv.viewableTiles.contains(tileInfo) + + open fun update(viewingCiv:CivilizationInfo?=null, showResourcesAndImprovements: Boolean=true) { hideCircle() - if (!showEntireMap - && !tileInfo.tileMap.gameInfo.getCurrentPlayerCivilization().exploredTiles.contains(tileInfo.position)) { + if (viewingCiv!=null && !showEntireMap + && !viewingCiv.exploredTiles.contains(tileInfo.position)) { tileBaseImage.color = Color.DARK_GRAY return } + val tileIsViewable = viewingCiv==null || isViewable(viewingCiv) + val showMilitaryUnit = viewingCiv==null || showMilitaryUnit(viewingCiv) + updateTileImage(true) updateTerrainBaseImage() updateTerrainFeatureImage() updateCityImage() - updateTileColor(isViewable) + updateTileColor(tileIsViewable) updateResourceImage(showResourcesAndImprovements) updateImprovementImage(showResourcesAndImprovements) - civilianUnitImage = newUnitImage(tileInfo.civilianUnit, civilianUnitImage, isViewable, -20f) - militaryUnitImage = newUnitImage(tileInfo.militaryUnit, militaryUnitImage, isViewable && showSubmarine, 20f) + civilianUnitImage = newUnitImage(tileInfo.civilianUnit, civilianUnitImage, tileIsViewable, -20f) + militaryUnitImage = newUnitImage(tileInfo.militaryUnit, militaryUnitImage, tileIsViewable && showMilitaryUnit, 20f) updateRoadImages() updateBorderImages() crosshairImage.isVisible = false - fogImage.isVisible = !(isViewable || showEntireMap) + fogImage.isVisible = !(tileIsViewable || showEntireMap) } private fun updateTerrainBaseImage() { diff --git a/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt b/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt index aee8de97cd..70b514e3df 100644 --- a/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt +++ b/core/src/com/unciv/ui/tilegroups/WorldTileGroup.kt @@ -2,6 +2,7 @@ package com.unciv.ui.tilegroups import com.unciv.UnCivGame import com.unciv.logic.city.CityInfo +import com.unciv.logic.civilization.CivilizationInfo import com.unciv.logic.map.MapUnit import com.unciv.logic.map.TileInfo import com.unciv.ui.utils.CameraStageBaseScreen @@ -20,21 +21,21 @@ class WorldTileGroup(internal val worldScreen: WorldScreen, tileInfo: TileInfo, unitImage?.selectUnit() } - fun update(isViewable: Boolean, showSubmarine: Boolean) { + fun update(viewingCiv: CivilizationInfo) { val city = tileInfo.getCity() removePopulationIcon() - if (isViewable && tileInfo.isWorked() && UnCivGame.Current.settings.showWorkedTiles + val tileIsViewable = isViewable(viewingCiv) + if (tileIsViewable && tileInfo.isWorked() && UnCivGame.Current.settings.showWorkedTiles && city!!.civInfo.isPlayerCivilization()) addPopulationIcon() val currentPlayerCiv = worldScreen.viewingCiv if (UnCivGame.Current.viewEntireMapForDebug || currentPlayerCiv.exploredTiles.contains(tileInfo.position)) - updateCityButton(city, isViewable || UnCivGame.Current.viewEntireMapForDebug) // needs to be before the update so the units will be above the city button + updateCityButton(city, tileIsViewable || UnCivGame.Current.viewEntireMapForDebug) // needs to be before the update so the units will be above the city button - super.update(isViewable || UnCivGame.Current.viewEntireMapForDebug, - UnCivGame.Current.settings.showResourcesAndImprovements, showSubmarine) + super.update(viewingCiv, UnCivGame.Current.settings.showResourcesAndImprovements) // order by z index! diff --git a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt index a183770e6a..a1e16a853c 100644 --- a/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt +++ b/core/src/com/unciv/ui/worldscreen/TileMapHolder.kt @@ -32,7 +32,6 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: val tileGroups = HashMap() var unitActionOverlay :Actor?=null - var removeUnitActionOverlay=false // Used to transfer data on the "move here" button that should be created, from the side thread to the main thread class MoveHereButtonDto(val unit: MapUnit, val tileInfo: TileInfo, val turnsToGetThere: Int) @@ -170,7 +169,7 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: worldScreen.bottomBar.unitTable.selectedUnit = unit worldScreen.bottomBar.unitTable.selectedCity = null worldScreen.shouldUpdate = true - removeUnitActionOverlay = true + unitActionOverlay?.remove() } table.add(unitGroup) } @@ -238,32 +237,22 @@ class TileMapHolder(internal val worldScreen: WorldScreen, internal val tileMap: } - internal fun updateTiles(civInfo: CivilizationInfo) { - if(removeUnitActionOverlay){ - removeUnitActionOverlay=false - unitActionOverlay?.remove() - } + internal fun updateTiles(viewingCiv: CivilizationInfo) { - val playerViewableTilePositions = civInfo.viewableTiles.map { it.position }.toHashSet() - val playerViewableInvisibleUnitsTilePositions = civInfo.viewableInvisibleUnitsTiles.map { it.position }.toHashSet() + val playerViewableTilePositions = viewingCiv.viewableTiles.map { it.position }.toHashSet() + val playerViewableInvisibleUnitsTilePositions = viewingCiv.viewableInvisibleUnitsTiles.map { it.position }.toHashSet() for (tileGroup in tileGroups.values){ - val canSeeTile = UnCivGame.Current.viewEntireMapForDebug - || playerViewableTilePositions.contains(tileGroup.tileInfo.position) - - val showSubmarine = UnCivGame.Current.viewEntireMapForDebug - || playerViewableInvisibleUnitsTilePositions.contains(tileGroup.tileInfo.position) - || (!tileGroup.tileInfo.hasEnemySubmarine()) - tileGroup.update(canSeeTile, showSubmarine) + tileGroup.update(viewingCiv) if(tileGroup.tileInfo.improvement==Constants.barbarianEncampment - && tileGroup.tileInfo.position in civInfo.exploredTiles) + && tileGroup.tileInfo.position in viewingCiv.exploredTiles) tileGroup.showCircle(Color.RED) val unitsInTile = tileGroup.tileInfo.getUnits() - val canSeeEnemy = unitsInTile.isNotEmpty() && unitsInTile.first().civInfo.isAtWarWith(civInfo) - && (showSubmarine || unitsInTile.firstOrNull {!it.isInvisible()}!=null) - if(canSeeTile && canSeeEnemy) + val canSeeEnemy = unitsInTile.isNotEmpty() && unitsInTile.first().civInfo.isAtWarWith(viewingCiv) + && tileGroup.showMilitaryUnit(viewingCiv) + if(tileGroup.isViewable(viewingCiv) && canSeeEnemy) tileGroup.showCircle(Color.RED) // Display ALL viewable enemies with a red circle so that users don't need to go "hunting" for enemy units } diff --git a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt index 88dde519c4..15d1470835 100644 --- a/core/src/com/unciv/ui/worldscreen/WorldScreen.kt +++ b/core/src/com/unciv/ui/worldscreen/WorldScreen.kt @@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.scenes.scene2d.Touchable +import com.badlogic.gdx.scenes.scene2d.actions.Actions import com.badlogic.gdx.scenes.scene2d.ui.Button import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextButton @@ -113,23 +114,36 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { update() - if(gameInfo.gameParameters.isOnlineMultiplayer && !gameInfo.isUpToDate) { + if(gameInfo.gameParameters.isOnlineMultiplayer && !gameInfo.isUpToDate) isPlayersTurn = false // until we're up to date, don't let the player do anything - val loadingGamePopup = PopupTable(this) - loadingGamePopup.add("Loading latest game state...") - loadingGamePopup.open() - thread { - try { - val latestGame = OnlineMultiplayer().tryDownloadGame(gameInfo.gameId) - latestGame.isUpToDate=true - game.loadGame(latestGame) - } catch (ex: Exception) { + if(gameInfo.gameParameters.isOnlineMultiplayer && !isPlayersTurn) { + stage.addAction(Actions.forever(Actions.sequence(Actions.run { + loadLatestMultiplayerState() + }, Actions.delay(10f)))) // delay is in seconds + } + } + + fun loadLatestMultiplayerState(){ + val loadingGamePopup = PopupTable(this) + loadingGamePopup.add("Loading latest game state...") + loadingGamePopup.open() + thread { + try { + val latestGame = OnlineMultiplayer().tryDownloadGame(gameInfo.gameId) + if(gameInfo.isUpToDate && gameInfo.currentPlayer==latestGame.currentPlayer) { // we were trying to download this to see when it's our turn...nothing changed loadingGamePopup.close() - val couldntDownloadLatestGame = PopupTable(this) - couldntDownloadLatestGame.addGoodSizedLabel("Couldn't download the latest game state!") - couldntDownloadLatestGame.addCloseButton() - couldntDownloadLatestGame.open() + return@thread } + latestGame.isUpToDate=true + // Since we're making a screen this needs to run from the man thread which has a GL context + Gdx.app.postRunnable { game.loadGame(latestGame) } + + } catch (ex: Exception) { + loadingGamePopup.close() + val couldntDownloadLatestGame = PopupTable(this) + couldntDownloadLatestGame.addGoodSizedLabel("Couldn't download the latest game state!") + couldntDownloadLatestGame.addCloseButton() + couldntDownloadLatestGame.open() } } } @@ -138,23 +152,13 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { // That way, not only do we save a lot of unnecessary updates, we also ensure that all updates are called from the main GL thread // and we don't get any silly concurrency problems! private fun update() { - // many of the display functions will be called with the game clone and not the actual game, - // because that's guaranteed to stay the exact same and so we won't get any concurrent modification exceptions - - val gameClone = gameInfo.clone() - gameClone.setTransients() - val cloneCivilization = gameClone.getCurrentPlayerCivilization() - thread { - gameInfo.civilizations.forEach { it.setCitiesConnectedToCapitalTransients() } - } - - displayTutorialsOnUpdate(cloneCivilization, gameClone) + displayTutorialsOnUpdate(viewingCiv, gameInfo) bottomBar.update(tileMapHolder.selectedTile) // has to come before tilemapholder update because the tilemapholder actions depend on the selected unit! battleTable.update() - minimapWrapper.update(cloneCivilization) + minimapWrapper.update(viewingCiv) minimapWrapper.y = bottomBar.height // couldn't be bothered to create a separate val for minimap wrapper unitActionsTable.update(bottomBar.unitTable.selectedUnit) @@ -165,12 +169,12 @@ 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) tileMapHolder.updateTiles(viewingCiv) - topBar.update(cloneCivilization) + topBar.update(viewingCiv) - updateTechButton(cloneCivilization) + updateTechButton(viewingCiv) techPolicyandVictoryHolder.pack() techPolicyandVictoryHolder.setPosition(10f, topBar.y - techPolicyandVictoryHolder.height - 5f) - updateDiplomacyButton(cloneCivilization) + updateDiplomacyButton(viewingCiv) notificationsScroll.update(viewingCiv.notifications) notificationsScroll.setPosition(stage.width - notificationsScroll.width - 5f, @@ -309,7 +313,7 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { shouldUpdate = true - thread { + thread { // on a separate thread so the user can explore their world while we're passing the turn val gameInfoClone = gameInfo.clone() gameInfoClone.setTransients() try { @@ -342,7 +346,7 @@ class WorldScreen(val viewingCiv:CivilizationInfo) : CameraStageBaseScreen() { Gdx.app.postRunnable { fun createNewWorldScreen(){ - val newWorldScreen = WorldScreen(gameInfoClone.currentPlayerCiv) + val newWorldScreen = WorldScreen(gameInfoClone.getPlayerToViewAs()) newWorldScreen.tileMapHolder.scrollX = tileMapHolder.scrollX newWorldScreen.tileMapHolder.scrollY = tileMapHolder.scrollY newWorldScreen.tileMapHolder.scaleX = tileMapHolder.scaleX diff --git a/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt b/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt index 071cc0ad47..99dae100aa 100644 --- a/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt +++ b/core/src/com/unciv/ui/worldscreen/bottombar/BattleTable.kt @@ -167,7 +167,7 @@ class BattleTable(val worldScreen: WorldScreen): Table() { attackButton.onClick { try { battle.moveAndAttack(attacker, attackableEnemy) - worldScreen.tileMapHolder.removeUnitActionOverlay = true // the overlay was one of attacking + worldScreen.tileMapHolder.unitActionOverlay?.remove() // the overlay was one of attacking worldScreen.shouldUpdate = true } catch (ex:Exception){ diff --git a/core/src/com/unciv/ui/worldscreen/optionstable/DropBox.kt b/core/src/com/unciv/ui/worldscreen/optionstable/DropBox.kt index 5bc2867c96..45b62c92df 100644 --- a/core/src/com/unciv/ui/worldscreen/optionstable/DropBox.kt +++ b/core/src/com/unciv/ui/worldscreen/optionstable/DropBox.kt @@ -58,9 +58,10 @@ class DropBox(){ return response } - fun uploadFile(fileName: String, data: String){ + fun uploadFile(fileName: String, data: String, overwrite:Boolean=false){ + val overwriteModeString = if(!overwrite) "" else ""","mode":{".tag":"overwrite"}""" val response = dropboxApi("https://content.dropboxapi.com/2/files/upload", - data,"application/octet-stream","{\"path\":\"$fileName\"}") + data,"application/octet-stream", """{"path":"$fileName"$overwriteModeString}""") } @@ -80,11 +81,11 @@ class OnlineMultiplayer(){ fun tryUploadGame(gameInfo: GameInfo){ val zippedGameInfo = Gzip.zip(GameSaver().json().toJson(gameInfo)) - DropBox().uploadFile(getGameLocation(gameInfo.gameId),zippedGameInfo) + DropBox().uploadFile(getGameLocation(gameInfo.gameId),zippedGameInfo,true) } fun tryDownloadGame(gameId: String): GameInfo { - val zippedGameInfo = DropBox().downloadFile(gameId) + val zippedGameInfo = DropBox().downloadFile(getGameLocation(gameId)) return GameSaver().gameInfoFromString(Gzip.unzip(zippedGameInfo)) } } \ No newline at end of file diff --git a/core/src/com/unciv/ui/worldscreen/optionstable/WorldScreenMenuTable.kt b/core/src/com/unciv/ui/worldscreen/optionstable/WorldScreenMenuTable.kt index 9a22801471..b34d3f5cd7 100644 --- a/core/src/com/unciv/ui/worldscreen/optionstable/WorldScreenMenuTable.kt +++ b/core/src/com/unciv/ui/worldscreen/optionstable/WorldScreenMenuTable.kt @@ -53,10 +53,11 @@ class WorldScreenMenuTable(val worldScreen: WorldScreen) : PopupTable(worldScree addButton("Start new game".tr()){ UnCivGame.Current.screen = NewGameScreen() } if(worldScreen.gameInfo.gameParameters.isOnlineMultiplayer){ - addButton("Copy game ID to clipboard".tr()){ Gdx.app.clipboard.contents = worldScreen.gameInfo.gameId } + addButton("Copy game ID".tr()){ Gdx.app.clipboard.contents = worldScreen.gameInfo.gameId } } -// addJoinMultiplayerButton() + if(UnCivGame.Current.mutiplayerEnabled) + addJoinMultiplayerButton() addButton("Victory status".tr()) { UnCivGame.Current.screen = VictoryScreen() } diff --git a/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt b/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt index 3b1b5ed387..e1f072c804 100644 --- a/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt +++ b/core/src/com/unciv/ui/worldscreen/unit/UnitContextMenu.kt @@ -1,5 +1,6 @@ package com.unciv.ui.worldscreen.unit +import com.badlogic.gdx.Gdx import com.badlogic.gdx.scenes.scene2d.Actor import com.badlogic.gdx.scenes.scene2d.ui.Button import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup @@ -39,7 +40,7 @@ class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUni addButton(icon, label) { selectedUnit.mapUnitAction = action selectedUnit.mapUnitAction?.doPreTurnAction() - tileMapHolder.removeUnitActionOverlay = true + tileMapHolder.unitActionOverlay?.remove() tileMapHolder.worldScreen.shouldUpdate = true } } @@ -57,28 +58,32 @@ class UnitContextMenu(val tileMapHolder: TileMapHolder, val selectedUnit: MapUni fun onMoveButtonClick() { // this can take a long time, because of the unit-to-tile calculation needed, so we put it in a different thread thread { - if (selectedUnit.movement.canReach(targetTile)) { + // these are the heavy parts, finding where we want to go + if (!selectedUnit.movement.canReach(targetTile)) return@thread // can't move here + val tileToMoveTo = selectedUnit.movement.getTileToMoveToThisTurn(targetTile) + + Gdx.app.postRunnable { try { // Because this is darned concurrent (as it MUST be to avoid ANRs), // there are edge cases where the canReach is true, // but until it reaches the headTowards the board has changed and so the headTowards fails. // I can't think of any way to avoid this, // but it's so rare and edge-case-y that ignoring its failure is actually acceptable, hence the empty catch - selectedUnit.movement.headTowards(targetTile) + selectedUnit.movement.moveToTile(tileToMoveTo) Sounds.play("whoosh") if (selectedUnit.currentTile != targetTile) selectedUnit.action = "moveTo " + targetTile.position.x.toInt() + "," + targetTile.position.y.toInt() - if(selectedUnit.currentMovement>0){ - tileMapHolder.worldScreen.bottomBar.unitTable.selectedUnit=selectedUnit + if (selectedUnit.currentMovement > 0) { + tileMapHolder.worldScreen.bottomBar.unitTable.selectedUnit = selectedUnit } + + // we don't update it directly because we're on a different thread; instead, we tell it to update itself + tileMapHolder.worldScreen.shouldUpdate = true + tileMapHolder.unitActionOverlay?.remove() } catch (e: Exception) { } + } - - // we don't update it directly because we're on a different thread; instead, we tell it to update itself - tileMapHolder.worldScreen.shouldUpdate = true - - tileMapHolder.removeUnitActionOverlay=true } }