Unciv/core/src/com/unciv/ui/MultiplayerScreen.kt
GGGuenni 4fc5c5de64
New screen for "Add Game" [MultiplayerScreen] (#2431)
* Adding AddGame Screen

Old button was confusing for new players

* Adding translation file entry
2020-04-16 14:44:52 +03:00

414 lines
16 KiB
Kotlin

package com.unciv.ui
import com.unciv.ui.utils.AutoScrollPane as ScrollPane
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.scenes.scene2d.ui.*
import com.unciv.UncivGame
import com.unciv.logic.GameInfo
import com.unciv.logic.GameSaver
import com.unciv.logic.IdChecker
import com.unciv.models.translations.tr
import com.unciv.ui.pickerscreens.PickerScreen
import com.unciv.ui.utils.*
import com.unciv.ui.worldscreen.mainmenu.OnlineMultiplayer
import java.util.*
import kotlin.concurrent.thread
class MultiplayerScreen() : PickerScreen() {
private lateinit var selectedGame: GameInfo
private lateinit var selectedGameName: String
private var multiplayerGameList = mutableMapOf<String, String>()
private val rightSideTable = Table()
private val leftSideTable = Table()
private val editButtonText = "Edit Game Info".tr()
private val addGameText = "Add Multiplayer Game".tr()
private val copyGameIdText = "Copy Game ID".tr()
private val copyUserIdText = "Copy User ID".tr()
private val refreshText = "Refresh List".tr()
private val editButton = TextButton(editButtonText, skin).apply { disable() }
private val addGameButton = TextButton(addGameText, skin)
private val copyGameIdButton = TextButton(copyGameIdText, skin).apply { disable() }
private val copyUserIdButton = TextButton(copyUserIdText, skin)
private val refreshButton = TextButton(refreshText, skin)
init {
setDefaultCloseAction()
//Help Button Setup
val tab = Table()
val helpButton = TextButton("?", skin)
helpButton.onClick {
val helpPopup = Popup(this)
helpPopup.addGoodSizedLabel("To create a multiplayer game, check the 'multiplayer' toggle in the New Game screen, and for each human player insert that player's user ID.".tr()).row()
helpPopup.addGoodSizedLabel("You can assign your own user ID there easily, and other players can copy their user IDs here and send them to you for you to include them in the game.".tr()).row()
helpPopup.addGoodSizedLabel("").row()
helpPopup.addGoodSizedLabel("Once you've created your game, the Game ID gets automatically copied to your clipboard so you can send it to the other players.".tr()).row()
helpPopup.addGoodSizedLabel("Players can enter your game by copying the game ID to the clipboard, and clicking on the 'Add Multiplayer Game' button".tr()).row()
helpPopup.addGoodSizedLabel("").row()
helpPopup.addGoodSizedLabel("The symbol of your nation will appear next to the game when it's your turn".tr()).row()
helpPopup.addCloseButton()
helpPopup.open()
}
tab.add(helpButton)
tab.x = (stage.width - helpButton.width)
tab.y = (stage.height - helpButton.height)
stage.addActor(tab)
//TopTable Setup
//Have to put it into a separate Table to be able to add another copyGameID button
val mainTable = Table()
mainTable.add(ScrollPane(leftSideTable).apply { setScrollingDisabled(true, false) }).height(stage.height*2/3)
mainTable.add(rightSideTable)
topTable.add(mainTable).row()
scrollPane.setScrollingDisabled(false, true)
//leftTable Setup
reloadGameListUI()
//A Button to add the currently running game to multiplayerGameList if not yet done
addCurrentGameButton()
//rightTable Setup
copyUserIdButton.onClick {
Gdx.app.clipboard.contents = UncivGame.Current.settings.userId
ResponsePopup("UserID copied to clipboard".tr(), this)
}
rightSideTable.add(copyUserIdButton).pad(10f).padBottom(30f).row()
copyGameIdButton.onClick {
Gdx.app.clipboard.contents = selectedGame.gameId
ResponsePopup("GameID copied to clipboard".tr(), this)
}
rightSideTable.add(copyGameIdButton).pad(10f).row()
editButton.onClick {
UncivGame.Current.setScreen(EditMultiplayerGameInfoScreen(selectedGame, selectedGameName, this))
//game must be unselected in case the game gets deleted inside the EditScreen
unselectGame()
}
rightSideTable.add(editButton).pad(10f).row()
addGameButton.onClick {
UncivGame.Current.setScreen(AddMultiplayerGameScreen(this))
}
rightSideTable.add(addGameButton).pad(10f).padBottom(30f).row()
refreshButton.onClick {
redownloadAllGames()
}
rightSideTable.add(refreshButton).pad(10f).row()
//RightSideButton Setup
rightSideButton.setText("Join Game".tr())
rightSideButton.onClick {
joinMultiplaerGame()
}
}
//Adds a new Multiplayer game to the List
//gameId must be nullable because clipboard content could be null
fun addMultiplayerGame(gameId: String?, gameName: String = ""){
try {
//since the gameId is a String it can contain anything and has to be checked
UUID.fromString(IdChecker.checkAndReturnGameUuid(gameId!!))
} catch (ex: Exception) {
val errorPopup = Popup(this)
errorPopup.addGoodSizedLabel("Invalid game ID!".tr())
errorPopup.row()
errorPopup.addCloseButton()
errorPopup.open()
return
}
if (gameIsAlreadySavedAsMultiplayer(gameId)) {
ResponsePopup("Game is already added".tr(), this)
return
}
thread(name="MultiplayerDownload") {
addGameButton.setText("Working...".tr())
addGameButton.disable()
try {
// The tryDownload can take more than 500ms. Therefore, to avoid ANRs,
// we need to run it in a different thread.
val game = OnlineMultiplayer().tryDownloadGame(gameId.trim())
if (gameName == "")
GameSaver.saveGame(game, game.gameId, true)
else
GameSaver.saveGame(game, gameName, true)
reloadGameListUI()
} catch (ex: Exception) {
val errorPopup = Popup(this)
errorPopup.addGoodSizedLabel("Could not download game!".tr())
errorPopup.row()
errorPopup.addCloseButton()
errorPopup.open()
}
addGameButton.setText(addGameText)
addGameButton.enable()
}
}
//just loads the game from savefile
//the game will be downloaded opon joining it anyway
private fun joinMultiplaerGame(){
try {
UncivGame.Current.loadGame(selectedGame)
} catch (ex: Exception) {
val errorPopup = Popup(this)
errorPopup.addGoodSizedLabel("Could not download game!".tr())
errorPopup.row()
errorPopup.addCloseButton()
errorPopup.open()
}
}
private fun gameIsAlreadySavedAsMultiplayer(gameId: String) : Boolean{
return multiplayerGameList.containsKey(gameId)
}
//reloads all gameFiles to refresh UI
fun reloadGameListUI(){
val leftSubTable = Table()
val gameSaver = GameSaver
var savedGames : List<String>?
try {
savedGames = gameSaver.getSaves(true)
}catch (ex: Exception) {
val errorPopup = Popup(this)
errorPopup.addGoodSizedLabel("Could not refresh!".tr())
errorPopup.row()
errorPopup.addCloseButton()
errorPopup.open()
return
}
for (gameSaveName in savedGames) {
try {
val gameTable = Table()
val game = gameSaver.loadGameByName(gameSaveName, true)
//Add games to list so saves don't have to be loaded as Files so often
if (!gameIsAlreadySavedAsMultiplayer(game.gameId))
multiplayerGameList.put(game.gameId, gameSaveName)
if (isUsersTurn(game)) {
gameTable.add(ImageGetter.getNationIndicator(game.currentPlayerCiv.nation, 45f))
}else{
gameTable.add()
}
val lastModifiedMillis = gameSaver.getSave(gameSaveName, true).lastModified()
val gameButton = TextButton(gameSaveName, skin)
gameButton.onClick {
selectedGame = game
selectedGameName = gameSaveName
copyGameIdButton.enable()
editButton.enable()
rightSideButton.enable()
//get Minutes since last modified
val lastSavedMinutesAgo = (System.currentTimeMillis() - lastModifiedMillis) / 60000
var descriptionText = "Last refresh: [$lastSavedMinutesAgo] minutes ago".tr() + "\r\n"
descriptionText += "Current Turn:".tr() + " ${selectedGame.currentPlayer}\r\n"
descriptionLabel.setText(descriptionText)
}
gameTable.add(gameButton).pad(5f).row()
leftSubTable.add(gameTable).row()
}catch (ex: Exception) {
//skipping one save is not fatal
ResponsePopup("Could not refresh!".tr(), this)
continue
}
}
leftSideTable.clear()
leftSideTable.add(leftSubTable)
}
//redownload all games to update the list
//can maybe replaced when notification support gets introduced
private fun redownloadAllGames(){
addGameButton.disable()
refreshButton.setText("Working...".tr())
refreshButton.disable()
//One thread for all downloads
thread (name = "multiplayerGameDownload") {
for (gameId in multiplayerGameList.keys) {
try {
val game = OnlineMultiplayer().tryDownloadGame(gameId)
GameSaver.saveGame(game, multiplayerGameList.getValue(gameId), true)
} catch (ex: Exception) {
//skipping one is not fatal
//Trying to use as many prev. used strings as possible
ResponsePopup("Could not download game!".tr() + " ${multiplayerGameList.getValue(gameId)}", this)
continue
}
}
//Reset UI
addGameButton.enable()
refreshButton.setText(refreshText)
refreshButton.enable()
unselectGame()
reloadGameListUI()
}
}
//Adds a Button to add the currently running game to multiplayerGameList
private fun addCurrentGameButton(){
val currentlyRunningGame = UncivGame.Current.gameInfo
if (!currentlyRunningGame.gameParameters.isOnlineMultiplayer || gameIsAlreadySavedAsMultiplayer(currentlyRunningGame.gameId))
return
val currentGameButton = TextButton("Add Currently Running Game".tr(), skin)
currentGameButton.onClick {
if (gameIsAlreadySavedAsMultiplayer(currentlyRunningGame.gameId))
return@onClick
try {
GameSaver.saveGame(currentlyRunningGame, currentlyRunningGame.gameId, true)
reloadGameListUI()
} catch (ex: Exception) {
val errorPopup = Popup(this)
errorPopup.addGoodSizedLabel("Could not save game!".tr())
errorPopup.row()
errorPopup.addCloseButton()
errorPopup.open()
}
}
topTable.add(currentGameButton)
}
//It doesn't really unselect the game because selectedGame cant be null
//It just disables everything a selected game has set
private fun unselectGame(){
editButton.disable()
copyGameIdButton.disable()
rightSideButton.disable()
descriptionLabel.setText("")
}
//check if its the users turn
private fun isUsersTurn(game: GameInfo) : Boolean{
return (game.currentPlayerCiv.playerId == UncivGame.Current.settings.userId)
}
fun removeFromList(gameId: String){
multiplayerGameList.remove(gameId)
}
}
//Subscreen of MultiplayerScreen to edit and delete saves
//backScreen is used for getting back to the MultiplayerScreen so it doesn't have to be created over and over again
class EditMultiplayerGameInfoScreen(game: GameInfo, gameName: String, backScreen: MultiplayerScreen): PickerScreen(){
init {
val textField = TextField(gameName, skin)
topTable.add(Label("Rename".tr(), skin)).row()
topTable.add(textField).pad(10f).padBottom(30f).width(stage.width/2).row()
//TODO Change delete to "give up"
//->turn a player into an AI so everyone can still play without the user
//->should only be possible on the users turn because it has to be uploaded afterwards
val deleteButton = TextButton("Delete save".tr(), skin)
deleteButton.onClick {
val askPopup = Popup(this)
askPopup.addGoodSizedLabel("Are you sure you want to delete this map?".tr()).row()
askPopup.addButton("Yes"){
try {
GameSaver.deleteSave(gameName, true)
UncivGame.Current.setScreen(backScreen)
backScreen.reloadGameListUI()
}catch (ex: Exception) {
askPopup.close()
ResponsePopup("Could not delete game!".tr(), this)
}
}
askPopup.addButton("No"){
askPopup.close()
}
askPopup.open()
}.apply { color = Color.RED }
topTable.add(deleteButton)
//CloseButton Setup
closeButton.setText("Back".tr())
closeButton.onClick {
UncivGame.Current.setScreen(backScreen)
}
//RightSideButton Setup
rightSideButton.setText("Save game".tr())
rightSideButton.enable()
rightSideButton.onClick {
rightSideButton.setText("Saving...".tr())
try {
backScreen.removeFromList(game.gameId)
//using addMultiplayerGame will download the game from Dropbox so the descriptionLabel displays the right things
backScreen.addMultiplayerGame(game.gameId, textField.text)
GameSaver.deleteSave(gameName, true)
UncivGame.Current.setScreen(backScreen)
backScreen.reloadGameListUI()
}catch (ex: Exception) {
val errorPopup = Popup(this)
errorPopup.addGoodSizedLabel("Could not save game!".tr())
errorPopup.row()
errorPopup.addCloseButton()
errorPopup.open()
}
}
}
}
class AddMultiplayerGameScreen(backScreen: MultiplayerScreen) : PickerScreen(){
init {
val gameNameTextField = TextField("", skin)
val gameIDTextField = TextField("", skin)
val pasteGameIDButton = TextButton("Paste gameID from clipboard", skin)
pasteGameIDButton.onClick {
gameIDTextField.text = Gdx.app.clipboard.contents
}
topTable.add(Label("GameID".tr(), skin)).row()
val gameIDTable = Table()
gameIDTable.add(gameIDTextField).pad(10f).width(2*stage.width/3 - pasteGameIDButton.width)
gameIDTable.add(pasteGameIDButton)
topTable.add(gameIDTable).padBottom(30f).row()
topTable.add(Label("Game name".tr(), skin)).row()
topTable.add(gameNameTextField).pad(10f).padBottom(30f).width(stage.width/2).row()
//CloseButton Setup
closeButton.setText("Back".tr())
closeButton.onClick {
UncivGame.Current.setScreen(backScreen)
}
//RightSideButton Setup
rightSideButton.setText("Save game".tr())
rightSideButton.enable()
rightSideButton.onClick {
try {
UUID.fromString(IdChecker.checkAndReturnGameUuid(gameIDTextField.text))
}catch (ex: Exception){
ResponsePopup("Invalid game ID!".tr(), this)
return@onClick
}
backScreen.addMultiplayerGame(gameIDTextField.text.trim(), gameNameTextField.text.trim())
UncivGame.Current.setScreen(backScreen)
}
}
}