From 2a8202d8b6682cac4b4f559449b3f2686f09b0df Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Thu, 27 Aug 2020 14:43:49 +0300 Subject: [PATCH] Can put maps in mods! All Map based logic changed to by-FileHandle instead of by-name to support this --- core/src/com/unciv/logic/GameStarter.kt | 6 +- core/src/com/unciv/logic/MapSaver.kt | 25 +++++---- .../logic/map/{Scenario.kt => ScenarioMap.kt} | 2 +- .../ui/mapeditor/GameParametersScreen.kt | 6 +- .../com/unciv/ui/mapeditor/LoadMapScreen.kt | 16 +++--- .../unciv/ui/mapeditor/MapEditorMenuPopup.kt | 8 +-- .../com/unciv/ui/mapeditor/MapEditorScreen.kt | 22 ++++---- .../ui/newgamescreen/GameOptionsTable.kt | 3 +- .../unciv/ui/newgamescreen/MapOptionsTable.kt | 56 +++++++++++-------- .../unciv/ui/newgamescreen/NewGameScreen.kt | 2 + .../ui/pickerscreens/ModManagementScreen.kt | 34 ++++++----- 11 files changed, 103 insertions(+), 77 deletions(-) rename core/src/com/unciv/logic/map/{Scenario.kt => ScenarioMap.kt} (95%) diff --git a/core/src/com/unciv/logic/GameStarter.kt b/core/src/com/unciv/logic/GameStarter.kt index a17a905a75..98bbca2a71 100644 --- a/core/src/com/unciv/logic/GameStarter.kt +++ b/core/src/com/unciv/logic/GameStarter.kt @@ -23,8 +23,10 @@ object GameStarter { if (gameSetupInfo.mapParameters.type == MapType.scenarioMap) gameInfo.tileMap = MapSaver.loadScenario(gameSetupInfo.mapParameters.name).tileMap - else if (gameSetupInfo.mapParameters.name != "") - gameInfo.tileMap = MapSaver.loadMap(gameSetupInfo.mapParameters.name) + else if (gameSetupInfo.mapParameters.name != "") { + gameInfo.tileMap = MapSaver.loadMap(gameSetupInfo.mapFile!!) + } + else gameInfo.tileMap = MapGenerator(ruleset).generateMap(gameSetupInfo.mapParameters) gameInfo.tileMap.mapParameters = gameSetupInfo.mapParameters diff --git a/core/src/com/unciv/logic/MapSaver.kt b/core/src/com/unciv/logic/MapSaver.kt index 29c27c5cae..d550b455ea 100644 --- a/core/src/com/unciv/logic/MapSaver.kt +++ b/core/src/com/unciv/logic/MapSaver.kt @@ -1,7 +1,8 @@ package com.unciv.logic import com.badlogic.gdx.Gdx -import com.unciv.logic.map.Scenario +import com.badlogic.gdx.files.FileHandle +import com.unciv.logic.map.ScenarioMap import com.unciv.logic.map.TileMap import com.unciv.ui.saves.Gzip @@ -19,29 +20,33 @@ object MapSaver { getMap(mapName).writeString(Gzip.zip(json().toJson(tileMap)), false) } - fun saveScenario(scenarioName:String, scenario: Scenario) { - getScenario(scenarioName).writeString(Gzip.zip(json().toJson(scenario)), false) + fun saveScenario(scenarioName:String, scenarioMap: ScenarioMap) { + getScenario(scenarioName).writeString(Gzip.zip(json().toJson(scenarioMap)), false) } - fun loadMap(mapName: String): TileMap { - val gzippedString = getMap(mapName).readString() + fun loadMap(mapName: String) = loadMap(getMap(mapName)) + + fun loadMap(mapFile:FileHandle):TileMap{ + val gzippedString = mapFile.readString() val unzippedJson = Gzip.unzip(gzippedString) return json().fromJson(TileMap::class.java, unzippedJson) } - fun loadScenario(scenarioName: String): Scenario { - val gzippedString = getScenario(scenarioName).readString() + fun loadScenario(scenarioName: String) = loadScenario(getScenario(scenarioName)) + + fun loadScenario(scenarioFile: FileHandle): ScenarioMap { + val gzippedString = scenarioFile.readString() val unzippedJson = Gzip.unzip(gzippedString) - return json().fromJson(Scenario::class.java, unzippedJson) + return json().fromJson(ScenarioMap::class.java, unzippedJson) } fun deleteMap(mapName: String) = getMap(mapName).delete() fun deleteScenario(scenarioName: String) = getScenario(scenarioName).delete() - fun getMaps() = Gdx.files.local(mapsFolder).list().map { it.name() } + fun getMaps() = Gdx.files.local(mapsFolder).list() - fun getScenarios() = Gdx.files.local(scenariosFolder).list().map { it.name() } + fun getScenarios() = Gdx.files.local(scenariosFolder).list() fun mapFromJson(json:String): TileMap = json().fromJson(TileMap::class.java, json) } \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/Scenario.kt b/core/src/com/unciv/logic/map/ScenarioMap.kt similarity index 95% rename from core/src/com/unciv/logic/map/Scenario.kt rename to core/src/com/unciv/logic/map/ScenarioMap.kt index 9b8d6711c7..6451cba8b1 100644 --- a/core/src/com/unciv/logic/map/Scenario.kt +++ b/core/src/com/unciv/logic/map/ScenarioMap.kt @@ -2,7 +2,7 @@ package com.unciv.logic.map import com.unciv.models.metadata.GameParameters -class Scenario { +class ScenarioMap { lateinit var tileMap: TileMap lateinit var gameParameters: GameParameters diff --git a/core/src/com/unciv/ui/mapeditor/GameParametersScreen.kt b/core/src/com/unciv/ui/mapeditor/GameParametersScreen.kt index 9c36bcd6dc..4c5714a20e 100644 --- a/core/src/com/unciv/ui/mapeditor/GameParametersScreen.kt +++ b/core/src/com/unciv/ui/mapeditor/GameParametersScreen.kt @@ -1,12 +1,10 @@ package com.unciv.ui.mapeditor import com.unciv.UncivGame -import com.unciv.logic.map.Scenario -import com.unciv.models.ruleset.Ruleset +import com.unciv.logic.map.ScenarioMap import com.unciv.models.ruleset.RulesetCache import com.unciv.models.translations.tr import com.unciv.ui.newgamescreen.GameOptionsTable -import com.unciv.ui.newgamescreen.GameSetupInfo import com.unciv.ui.newgamescreen.PlayerPickerTable import com.unciv.ui.newgamescreen.IPreviousScreen import com.unciv.ui.pickerscreens.PickerScreen @@ -36,7 +34,7 @@ class GameParametersScreen(var mapEditorScreen: MapEditorScreen): IPreviousScree rightSideButton.setText("OK".tr()) rightSideButton.onClick { mapEditorScreen.gameSetupInfo = gameSetupInfo - mapEditorScreen.scenario = Scenario(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters) + mapEditorScreen.scenarioMap = ScenarioMap(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters) mapEditorScreen.ruleset.clear() mapEditorScreen.ruleset.add(ruleset) mapEditorScreen.tileEditorOptions.update() diff --git a/core/src/com/unciv/ui/mapeditor/LoadMapScreen.kt b/core/src/com/unciv/ui/mapeditor/LoadMapScreen.kt index 0e5113882a..c20ed474d7 100644 --- a/core/src/com/unciv/ui/mapeditor/LoadMapScreen.kt +++ b/core/src/com/unciv/ui/mapeditor/LoadMapScreen.kt @@ -1,6 +1,7 @@ package com.unciv.ui.mapeditor import com.badlogic.gdx.Gdx +import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.scenes.scene2d.ui.Table import com.badlogic.gdx.scenes.scene2d.ui.TextButton @@ -15,7 +16,7 @@ import com.unciv.ui.utils.* import com.unciv.ui.utils.AutoScrollPane as ScrollPane class LoadMapScreen(previousMap: TileMap?) : PickerScreen(){ - var chosenMap = "" + var chosenMap:FileHandle? = null val deleteButton = "Delete map".toTextButton() var scenarioMap = false val mapsTable = Table().apply { defaults().pad(10f) } @@ -34,8 +35,8 @@ class LoadMapScreen(previousMap: TileMap?) : PickerScreen(){ rightSideButton.setText("Load map".tr()) rightSideButton.onClick { - val mapEditorScreen = if (scenarioMap) MapEditorScreen(MapSaver.loadScenario(chosenMap), chosenMap) - else MapEditorScreen(chosenMap) + val mapEditorScreen = if (scenarioMap) MapEditorScreen(MapSaver.loadScenario(chosenMap!!), chosenMap!!.name()) + else MapEditorScreen(chosenMap!!) UncivGame.Current.setScreen(mapEditorScreen) dispose() } @@ -71,8 +72,7 @@ class LoadMapScreen(previousMap: TileMap?) : PickerScreen(){ deleteButton.onClick { YesNoPopup("Are you sure you want to delete this map?", { - if (scenarioMap) MapSaver.deleteScenario(chosenMap) - else MapSaver.deleteMap(chosenMap) + chosenMap!!.delete() UncivGame.Current.setScreen(LoadMapScreen(previousMap)) }, this).open() } @@ -86,7 +86,7 @@ class LoadMapScreen(previousMap: TileMap?) : PickerScreen(){ } fun update() { - chosenMap = "" + chosenMap = null deleteButton.disable() deleteButton.color = Color.RED @@ -96,7 +96,7 @@ class LoadMapScreen(previousMap: TileMap?) : PickerScreen(){ mapsTable.clear() for (scenario in MapSaver.getScenarios()) { - val loadScenarioButton = TextButton(scenario, skin) + val loadScenarioButton = TextButton(scenario.name(), skin) loadScenarioButton.onClick { rightSideButton.enable() chosenMap = scenario @@ -111,7 +111,7 @@ class LoadMapScreen(previousMap: TileMap?) : PickerScreen(){ mapsTable.clear() for (map in MapSaver.getMaps()) { - val loadMapButton = TextButton(map, skin) + val loadMapButton = TextButton(map.name(), skin) loadMapButton.onClick { rightSideButton.enable() chosenMap = map diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorMenuPopup.kt b/core/src/com/unciv/ui/mapeditor/MapEditorMenuPopup.kt index a61ada13bf..186dd72581 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorMenuPopup.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorMenuPopup.kt @@ -9,7 +9,7 @@ import com.unciv.UncivGame import com.unciv.logic.MapSaver import com.unciv.logic.map.MapType import com.unciv.logic.map.RoadStatus -import com.unciv.logic.map.Scenario +import com.unciv.logic.map.ScenarioMap import com.unciv.logic.map.TileMap import com.unciv.models.translations.tr import com.unciv.models.metadata.Player @@ -82,10 +82,10 @@ class MapEditorMenuPopup(var mapEditorScreen: MapEditorScreen): Popup(mapEditorS try { if(mapEditorScreen.hasScenario()) { mapEditorScreen.tileMap.mapParameters.type = MapType.scenarioMap - mapEditorScreen.scenario = Scenario(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters) - mapEditorScreen.scenario!!.gameParameters.godMode = true // so we can edit this scenario when loading from the map + mapEditorScreen.scenarioMap = ScenarioMap(mapEditorScreen.tileMap, mapEditorScreen.gameSetupInfo.gameParameters) + mapEditorScreen.scenarioMap!!.gameParameters.godMode = true // so we can edit this scenario when loading from the map mapEditorScreen.scenarioName = mapNameEditor.text - MapSaver.saveScenario(mapNameEditor.text, mapEditorScreen.scenario!!) + MapSaver.saveScenario(mapNameEditor.text, mapEditorScreen.scenarioMap!!) } else { MapSaver.saveMap(mapEditorScreen.mapName, mapEditorScreen.tileMap) diff --git a/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt b/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt index ed59d338bd..b351652c37 100644 --- a/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt +++ b/core/src/com/unciv/ui/mapeditor/MapEditorScreen.kt @@ -1,5 +1,6 @@ package com.unciv.ui.mapeditor +import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.math.Vector2 import com.badlogic.gdx.scenes.scene2d.InputEvent @@ -9,21 +10,20 @@ import com.badlogic.gdx.scenes.scene2d.ui.SelectBox import com.badlogic.gdx.scenes.scene2d.ui.Skin import com.badlogic.gdx.utils.Array import com.unciv.logic.MapSaver -import com.unciv.logic.map.Scenario +import com.unciv.logic.map.ScenarioMap import com.unciv.logic.map.TileInfo import com.unciv.logic.map.TileMap import com.unciv.models.ruleset.Ruleset import com.unciv.models.ruleset.RulesetCache import com.unciv.models.translations.tr import com.unciv.ui.newgamescreen.GameSetupInfo -import com.unciv.ui.newgamescreen.IPreviousScreen import com.unciv.ui.utils.* class MapEditorScreen(): CameraStageBaseScreen() { var mapName = "" var tileMap = TileMap() var scenarioName = "" // when loading map: mapName is taken as default for scenarioName - var scenario: Scenario? = null // main indicator whether scenario information is present + var scenarioMap: ScenarioMap? = null // main indicator whether scenario information is present var ruleset = Ruleset().apply { add(RulesetCache.getBaseRuleset()) } // Since we change this in scenarios, we can't take the base ruleset directly var gameSetupInfo = GameSetupInfo() @@ -39,7 +39,7 @@ class MapEditorScreen(): CameraStageBaseScreen() { if (mapToLoad == null) { val existingSaves = MapSaver.getMaps() if (existingSaves.isNotEmpty()) - mapToLoad = existingSaves.first() + mapToLoad = existingSaves.first().name() } if (mapToLoad != null) { @@ -51,22 +51,24 @@ class MapEditorScreen(): CameraStageBaseScreen() { initialize() } + constructor(mapFile:FileHandle):this(MapSaver.loadMap(mapFile)) + constructor(map: TileMap) : this() { tileMap = map initialize() } - constructor(scenario: Scenario, scenarioName: String = "") : this() { - tileMap = scenario.tileMap + constructor(scenarioMap: ScenarioMap, scenarioName: String = "") : this() { + tileMap = scenarioMap.tileMap mapName = scenarioName - this.scenario = scenario + this.scenarioMap = scenarioMap this.scenarioName = scenarioName - gameSetupInfo.gameParameters = scenario.gameParameters + gameSetupInfo.gameParameters = scenarioMap.gameParameters // Since the ruleset is referenced directly from other places, we can't just replace it directly ruleset.clear() - ruleset.add(RulesetCache.getComplexRuleset(scenario.gameParameters)) + ruleset.add(RulesetCache.getComplexRuleset(scenarioMap.gameParameters)) initialize() } @@ -177,7 +179,7 @@ class MapEditorScreen(): CameraStageBaseScreen() { } fun hasScenario(): Boolean { - return this.scenario != null + return this.scenarioMap != null } } diff --git a/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt b/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt index 5bcfa74b07..4dc6959939 100644 --- a/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/GameOptionsTable.kt @@ -178,7 +178,8 @@ class GameOptionsTable(val previousScreen: IPreviousScreen, val updatePlayerPick } fun Table.addModCheckboxes() { - val modRulesets = RulesetCache.values.filter { it.name != "" } + val modRulesets = RulesetCache.values.filter { it.name != "" + && (it.name in gameParameters.mods || !it.modOptions.uniques.contains("Scenario only")) } // Don't allow scenario mods for a regular 'new game' if (modRulesets.isEmpty()) return add("Mods:".toLabel(fontSize = 24)).padTop(16f).colspan(2).row() diff --git a/core/src/com/unciv/ui/newgamescreen/MapOptionsTable.kt b/core/src/com/unciv/ui/newgamescreen/MapOptionsTable.kt index 44d0f9c9bd..e109e08581 100644 --- a/core/src/com/unciv/ui/newgamescreen/MapOptionsTable.kt +++ b/core/src/com/unciv/ui/newgamescreen/MapOptionsTable.kt @@ -74,11 +74,6 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() { scenarioMapOptionsTable.add(scenarioMapSelectBox).maxWidth(newGameScreen.stage.width / 2) .right().row() - // The SelectBox auto displays the text a object.toString(), which on the FileHandle itself includes the folder path. - // So we wrap it in another object with a custom toString() - class FileHandleWrapper(val fileHandle: FileHandle){ - override fun toString() = fileHandle.name() - } val scenarioFiles = getScenarioFiles() val scenarioSelectBox = SelectBox(CameraStageBaseScreen.skin) @@ -98,14 +93,14 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() { mapTypeSpecificTable.clear() if (mapTypeSelectBox.selected.value == MapType.custom) { mapParameters.type = MapType.custom - mapParameters.name = mapFileSelectBox.selected + mapParameters.name = mapFileSelectBox.selected.toString() mapTypeSpecificTable.add(savedMapOptionsTable) newGameScreen.gameSetupInfo.gameParameters.godMode = false newGameScreen.unlockTables() newGameScreen.updateTables() } else if (mapTypeSelectBox.selected.value == MapType.scenarioMap) { mapParameters.type = MapType.scenarioMap - mapParameters.name = scenarioMapSelectBox.selected + mapParameters.name = scenarioMapSelectBox.selected.toString() mapTypeSpecificTable.add(scenarioMapOptionsTable) val scenario = MapSaver.loadScenario(mapParameters.name) newGameScreen.gameSetupInfo.gameParameters = scenario.gameParameters @@ -137,26 +132,39 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() { add(mapTypeSpecificTable).colspan(2).row() } - private fun getMapFileSelectBox(): SelectBox { - val mapFileSelectBox = SelectBox(CameraStageBaseScreen.skin) - val mapNames = Array() - for (mapName in MapSaver.getMaps()) mapNames.add(mapName) - mapFileSelectBox.items = mapNames - if (mapParameters.name in mapNames) mapFileSelectBox.selected = mapParameters.name + private fun getMapFileSelectBox(): SelectBox { + val mapFileSelectBox = SelectBox(CameraStageBaseScreen.skin) + val mapFiles = Array() + for (mapFile in MapSaver.getMaps()) + mapFiles.add(FileHandleWrapper(mapFile)) + for(mod in Gdx.files.local("mods").list()){ + val mapsFolder = mod.child("maps") + if(mapsFolder.exists()) + for(map in mapsFolder.list()) + mapFiles.add(FileHandleWrapper(map)) + } + mapFileSelectBox.items = mapFiles + val selectedItem = mapFiles.firstOrNull { it.fileHandle.name()==mapParameters.name } + if(selectedItem!=null) mapFileSelectBox.selected = selectedItem - mapFileSelectBox.onChange { mapParameters.name = mapFileSelectBox.selected!! } + mapFileSelectBox.onChange { + val mapFile = mapFileSelectBox.selected.fileHandle + mapParameters.name = mapFile.name() + newGameScreen.gameSetupInfo.mapFile = mapFile + } return mapFileSelectBox } - private fun getScenarioFileSelectBox(): SelectBox { - val scenarioFileSelectBox = SelectBox(CameraStageBaseScreen.skin) - val scenarioNames = Array() - for (scenarioName in MapSaver.getScenarios()) scenarioNames.add(scenarioName) - scenarioFileSelectBox.items = scenarioNames - if (mapParameters.name in scenarioNames) scenarioFileSelectBox.selected = mapParameters.name + private fun getScenarioFileSelectBox(): SelectBox { + val scenarioFileSelectBox = SelectBox(CameraStageBaseScreen.skin) + val scenarioFiles = Array() + for (scenarioName in MapSaver.getScenarios()) scenarioFiles.add(FileHandleWrapper(scenarioName)) + scenarioFileSelectBox.items = scenarioFiles + val selectedItem = scenarioFiles.firstOrNull { it.fileHandle.name()==mapParameters.name } + if(selectedItem!=null ) scenarioFileSelectBox.selected = selectedItem scenarioFileSelectBox.onChange { - mapParameters.name = scenarioFileSelectBox.selected!! + mapParameters.name = scenarioFileSelectBox.selected!!.fileHandle.name() val scenario = MapSaver.loadScenario(mapParameters.name) newGameScreen.apply { gameSetupInfo.gameParameters = scenario.gameParameters @@ -168,5 +176,9 @@ class MapOptionsTable(val newGameScreen: NewGameScreen): Table() { return scenarioFileSelectBox } - + // The SelectBox auto displays the text a object.toString(), which on the FileHandle itself includes the folder path. + // So we wrap it in another object with a custom toString() + class FileHandleWrapper(val fileHandle: FileHandle){ + override fun toString() = fileHandle.name() + } } \ No newline at end of file diff --git a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt index 8d96148db9..96990c2c6e 100644 --- a/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt +++ b/core/src/com/unciv/ui/newgamescreen/NewGameScreen.kt @@ -2,6 +2,7 @@ package com.unciv.ui.newgamescreen import com.unciv.ui.utils.AutoScrollPane as ScrollPane import com.badlogic.gdx.Gdx +import com.badlogic.gdx.files.FileHandle import com.badlogic.gdx.scenes.scene2d.ui.SelectBox import com.badlogic.gdx.scenes.scene2d.ui.Skin import com.badlogic.gdx.utils.Array @@ -21,6 +22,7 @@ import kotlin.concurrent.thread class GameSetupInfo(var gameId:String, var gameParameters: GameParameters, var mapParameters: MapParameters) { + var mapFile: FileHandle? = null constructor() : this("", GameParameters(), MapParameters()) constructor(gameInfo: GameInfo) : this("", gameInfo.gameParameters.clone(), gameInfo.tileMap.mapParameters) constructor(gameParameters: GameParameters, mapParameters: MapParameters) : this("", gameParameters, mapParameters) diff --git a/core/src/com/unciv/ui/pickerscreens/ModManagementScreen.kt b/core/src/com/unciv/ui/pickerscreens/ModManagementScreen.kt index eb98dd5e35..5809cc5b87 100644 --- a/core/src/com/unciv/ui/pickerscreens/ModManagementScreen.kt +++ b/core/src/com/unciv/ui/pickerscreens/ModManagementScreen.kt @@ -31,27 +31,31 @@ class ModManagementScreen: PickerScreen() { repoList = Github.tryGetGithubReposWithTopic() } catch (ex: Exception) { - ResponsePopup("Could not download mod list", this) + Gdx.app.postRunnable { + ResponsePopup("Could not download mod list", this) + } return@thread } - for (repo in repoList) { - repo.name = repo.name.replace('-',' ') - val downloadButton = repo.name.toTextButton() - downloadButton.onClick { - descriptionLabel.setText(repo.description + "\n" + "[${repo.stargazers_count}] Stars".tr()) - removeRightSideClickListeners() - rightSideButton.enable() - rightSideButton.setText("Download [${repo.name}]".tr()) - rightSideButton.onClick { - rightSideButton.setText("Downloading...") - rightSideButton.disable() - downloadMod(repo.svn_url){ - rightSideButton.setText("Done!".tr()) + Gdx.app.postRunnable { + for (repo in repoList) { + repo.name = repo.name.replace('-', ' ') + val downloadButton = repo.name.toTextButton() + downloadButton.onClick { + descriptionLabel.setText(repo.description + "\n" + "[${repo.stargazers_count}] Stars".tr()) + removeRightSideClickListeners() + rightSideButton.enable() + rightSideButton.setText("Download [${repo.name}]".tr()) + rightSideButton.onClick { + rightSideButton.setText("Downloading...") + rightSideButton.disable() + downloadMod(repo.svn_url) { + rightSideButton.setText("Done!".tr()) + } } } + downloadTable.add(downloadButton).row() } - downloadTable.add(downloadButton).row() } }