From b0876935f5b4637e66059285cedd8f25fbaa9ebd Mon Sep 17 00:00:00 2001 From: SomeTroglodyte <63000004+SomeTroglodyte@users.noreply.github.com> Date: Thu, 4 May 2023 08:31:43 +0200 Subject: [PATCH] Show a preview of custom maps on new game screen (#9234) * Show a preview of custom maps on new game screen * Show a preview of custom maps on new game screen - step 2 * Show a preview of custom maps on new game screen V2 --- .../newgamescreen/MapFileSelectTable.kt | 175 ++++++++++++++++++ .../screens/newgamescreen/MapOptionsTable.kt | 101 +--------- .../newgamescreen/MapParametersTable.kt | 7 +- .../ui/screens/newgamescreen/NewGameScreen.kt | 14 +- .../ui/screens/victoryscreen/ReplayMap.kt | 127 ++++++++++--- .../victoryscreen/VictoryScreenReplay.kt | 2 +- 6 files changed, 302 insertions(+), 124 deletions(-) create mode 100644 core/src/com/unciv/ui/screens/newgamescreen/MapFileSelectTable.kt diff --git a/core/src/com/unciv/ui/screens/newgamescreen/MapFileSelectTable.kt b/core/src/com/unciv/ui/screens/newgamescreen/MapFileSelectTable.kt new file mode 100644 index 0000000000..87bd3cf708 --- /dev/null +++ b/core/src/com/unciv/ui/screens/newgamescreen/MapFileSelectTable.kt @@ -0,0 +1,175 @@ +package com.unciv.ui.screens.newgamescreen + +import com.badlogic.gdx.files.FileHandle +import com.badlogic.gdx.scenes.scene2d.Group +import com.badlogic.gdx.scenes.scene2d.actions.Actions +import com.badlogic.gdx.scenes.scene2d.ui.Container +import com.badlogic.gdx.scenes.scene2d.ui.SelectBox +import com.badlogic.gdx.scenes.scene2d.ui.Table +import com.badlogic.gdx.utils.Array as GdxArray +import com.unciv.UncivGame +import com.unciv.logic.UncivShowableException +import com.unciv.logic.files.MapSaver +import com.unciv.logic.map.MapParameters +import com.unciv.models.ruleset.RulesetCache +import com.unciv.ui.components.extensions.onChange +import com.unciv.ui.components.extensions.pad +import com.unciv.ui.components.extensions.toLabel +import com.unciv.ui.popups.Popup +import com.unciv.ui.screens.basescreen.BaseScreen +import com.unciv.ui.screens.victoryscreen.LoadMapPreview +import com.unciv.utils.concurrency.Concurrency +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive + +class MapFileSelectTable( + private val newGameScreen: NewGameScreen, + private val mapParameters: MapParameters +) : Table() { + + private val mapFileSelectBox = SelectBox(BaseScreen.skin) + private val miniMapWrapper = Container() + private var mapPreviewJob: Job? = null + + private val mapFilesSequence = sequence { + yieldAll(MapSaver.getMaps().asSequence()) + for (modFolder in RulesetCache.values.mapNotNull { it.folderLocation }) { + val mapsFolder = modFolder.child(MapSaver.mapsFolder) + if (mapsFolder.exists()) + yieldAll(mapsFolder.list().asSequence()) + } + }.map { FileHandleWrapper(it) } + + private val columnWidth = newGameScreen.getColumnWidth() + + init { + defaults().pad(5f, 10f) // Must stay same as in MapParametersTable + val mapFileLabel = "{Map file}:".toLabel() + add(mapFileLabel).left() + add(mapFileSelectBox) + // because SOME people gotta give the hugest names to their maps + .maxWidth((columnWidth - mapFileLabel.prefWidth).coerceAtLeast(120f)) + .right().row() + add(miniMapWrapper) + .pad(15f) + .colspan(2).center().row() + + mapFileSelectBox.onChange { onSelectBoxChange() } + } + + // 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() + private class FileHandleWrapper(val fileHandle: FileHandle) { + override fun toString(): String = fileHandle.name() + } + + fun isNotEmpty() = mapFilesSequence.any() + fun recentlySavedMapExists() = mapFilesSequence.any { + it.fileHandle.lastModified() > System.currentTimeMillis() - 900000 + } + + fun fillMapFileSelectBox() { + if (!mapFileSelectBox.items.isEmpty) return + + val mapFiles = GdxArray() + mapFilesSequence + .sortedWith(compareBy(UncivGame.Current.settings.getCollatorFromLocale()) { it.toString() }) + .forEach { mapFiles.add(it) } + mapFileSelectBox.items = mapFiles + + // Pre-select: a) map saved within last 15min or b) map named in mapParameters or c) alphabetically first + // This is a kludge - the better way would be to have a "play this map now" menu button in the editor + // (which would ideally not even require a save file - which makes implementation non-trivial) + val selectedItem = + mapFiles.maxByOrNull { it.fileHandle.lastModified() } + ?.takeIf { it.fileHandle.lastModified() > System.currentTimeMillis() - 900000 } + ?: mapFiles.firstOrNull { it.fileHandle.name() == mapParameters.name } + ?: mapFiles.firstOrNull() + ?: return + mapFileSelectBox.selected = selectedItem + mapParameters.name = selectedItem.toString() + newGameScreen.gameSetupInfo.mapFile = selectedItem.fileHandle + } + + private fun onSelectBoxChange() { + cancelBackgroundJobs() + val mapFile = mapFileSelectBox.selected.fileHandle + val mapParams = try { + MapSaver.loadMapParameters(mapFile) + } catch (ex:Exception){ + ex.printStackTrace() + Popup(newGameScreen).apply { + addGoodSizedLabel("Could not load map!").row() + if (ex is UncivShowableException) + addGoodSizedLabel(ex.message).row() + addCloseButton() + open() + } + return + } + mapParameters.name = mapFile.name() + newGameScreen.gameSetupInfo.mapFile = mapFile + val mapMods = mapParams.mods.partition { RulesetCache[it]?.modOptions?.isBaseRuleset == true } + newGameScreen.gameSetupInfo.gameParameters.mods = LinkedHashSet(mapMods.second) + newGameScreen.gameSetupInfo.gameParameters.baseRuleset = mapMods.first.firstOrNull() ?: mapParams.baseRuleset + newGameScreen.updateRuleset() + newGameScreen.updateTables() + hideMiniMap() + startMapPreview(mapFile) + } + + private fun startMapPreview(mapFile: FileHandle) { + mapPreviewJob = Concurrency.run { + try { + val map = MapSaver.loadMap(mapFile) + if (!isActive) return@run + map.setTransients(newGameScreen.ruleset, false) + if (!isActive) return@run + // ReplyMap still paints outside its bounds - so we subtract padding and a little extra + val size = (columnWidth - 40f).coerceAtMost(500f) + val miniMap = LoadMapPreview(map, size, size) + if (!isActive) return@run + Concurrency.runOnGLThread { + showMinimap(miniMap) + } + } catch (_: Throwable) {} + }.apply { + invokeOnCompletion { + mapPreviewJob = null + } + } + } + + internal fun cancelBackgroundJobs() { + mapPreviewJob?.cancel() + mapPreviewJob = null + miniMapWrapper.clearActions() + } + + private fun showMinimap(miniMap: LoadMapPreview) { + if (miniMapWrapper.actor == miniMap) return + miniMapWrapper.clearActions() + miniMapWrapper.color.a = 0f + miniMapWrapper.actor = miniMap + miniMapWrapper.invalidateHierarchy() + miniMapWrapper.addAction(Actions.fadeIn(0.2f)) + } + + private fun hideMiniMap() { + if (miniMapWrapper.actor !is LoadMapPreview) return + miniMapWrapper.clearActions() + miniMapWrapper.addAction( + Actions.sequence( + Actions.fadeOut(0.4f), + Actions.run { + // in portrait, simply removing the map preview will cause the layout to "jump". + // with a dummy holding the empty space, it jumps later and not as far. + val dummy = Group().apply { + setSize(miniMapWrapper.actor!!.width, miniMapWrapper.actor!!.height) + } + miniMapWrapper.actor = dummy + } + ) + ) + } +} diff --git a/core/src/com/unciv/ui/screens/newgamescreen/MapOptionsTable.kt b/core/src/com/unciv/ui/screens/newgamescreen/MapOptionsTable.kt index 81a4d6157a..7b5f81975c 100644 --- a/core/src/com/unciv/ui/screens/newgamescreen/MapOptionsTable.kt +++ b/core/src/com/unciv/ui/screens/newgamescreen/MapOptionsTable.kt @@ -1,66 +1,35 @@ package com.unciv.ui.screens.newgamescreen -import com.badlogic.gdx.files.FileHandle -import com.badlogic.gdx.scenes.scene2d.ui.SelectBox import com.badlogic.gdx.scenes.scene2d.ui.Table -import com.badlogic.gdx.utils.Array -import com.unciv.UncivGame -import com.unciv.logic.files.MapSaver -import com.unciv.logic.UncivShowableException import com.unciv.logic.map.MapGeneratedMainType -import com.unciv.models.ruleset.RulesetCache -import com.unciv.ui.popups.Popup -import com.unciv.ui.screens.basescreen.BaseScreen import com.unciv.ui.components.extensions.onChange import com.unciv.ui.components.extensions.toLabel +import com.unciv.ui.screens.basescreen.BaseScreen class MapOptionsTable(private val newGameScreen: NewGameScreen): Table() { private val mapParameters = newGameScreen.gameSetupInfo.mapParameters private var mapTypeSpecificTable = Table() - val generatedMapOptionsTable = MapParametersTable(newGameScreen, mapParameters, MapGeneratedMainType.generated) + internal val generatedMapOptionsTable = MapParametersTable(newGameScreen, mapParameters, MapGeneratedMainType.generated) private val randomMapOptionsTable = MapParametersTable(newGameScreen, mapParameters, MapGeneratedMainType.randomGenerated) - private val savedMapOptionsTable = Table() - lateinit var mapTypeSelectBox: TranslatedSelectBox - private val mapFileSelectBox = createMapFileSelectBox() - - private val mapFilesSequence = sequence { - yieldAll(MapSaver.getMaps().asSequence()) - for (modFolder in RulesetCache.values.mapNotNull { it.folderLocation }) { - val mapsFolder = modFolder.child(MapSaver.mapsFolder) - if (mapsFolder.exists()) - yieldAll(mapsFolder.list().asSequence()) - } - }.map { FileHandleWrapper(it) } + private val savedMapOptionsTable = MapFileSelectTable(newGameScreen, mapParameters) + internal val mapTypeSelectBox: TranslatedSelectBox init { //defaults().pad(5f) - each nested table having the same can give 'stairs' effects, // better control directly. Besides, the first Labels/Buttons should have 10f to look nice - addMapTypeSelection() background = BaseScreen.skinStrings.getUiBackground("NewGameScreen/MapOptionsTable", tintColor = BaseScreen.skinStrings.skinConfig.clearColor) - } - private fun addMapTypeSelection() { val mapTypes = arrayListOf(MapGeneratedMainType.generated, MapGeneratedMainType.randomGenerated) - if (mapFilesSequence.any()) mapTypes.add(MapGeneratedMainType.custom) + if (savedMapOptionsTable.isNotEmpty()) mapTypes.add(MapGeneratedMainType.custom) mapTypeSelectBox = TranslatedSelectBox(mapTypes, "Generated", BaseScreen.skin) - savedMapOptionsTable.defaults().pad(5f) - savedMapOptionsTable.add("{Map file}:".toLabel()).left() - // because SOME people gotta give the hugest names to their maps - val columnWidth = newGameScreen.stage.width / (if (newGameScreen.isNarrowerThan4to3()) 1 else 3) - savedMapOptionsTable.add(mapFileSelectBox) - .maxWidth((columnWidth - 120f).coerceAtLeast(120f)) - .right().row() - - fun updateOnMapTypeChange() { mapTypeSpecificTable.clear() when (mapTypeSelectBox.selected.value) { MapGeneratedMainType.custom -> { - fillMapFileSelectBox() + savedMapOptionsTable.fillMapFileSelectBox() mapParameters.type = MapGeneratedMainType.custom - mapParameters.name = mapFileSelectBox.selected.toString() mapTypeSpecificTable.add(savedMapOptionsTable) newGameScreen.unlockTables() } @@ -82,7 +51,7 @@ class MapOptionsTable(private val newGameScreen: NewGameScreen): Table() { } // Pre-select custom if any map saved within last 15 minutes - if (mapFilesSequence.any { it.fileHandle.lastModified() > System.currentTimeMillis() - 900000 }) + if (savedMapOptionsTable.recentlySavedMapExists()) mapTypeSelectBox.selected = TranslatedSelectBox.TranslatedString(MapGeneratedMainType.custom) @@ -98,59 +67,5 @@ class MapOptionsTable(private val newGameScreen: NewGameScreen): Table() { add(mapTypeSpecificTable).row() } - private fun createMapFileSelectBox(): SelectBox { - val mapFileSelectBox = SelectBox(BaseScreen.skin) - mapFileSelectBox.onChange { - val mapFile = mapFileSelectBox.selected.fileHandle - val mapParams = try { - MapSaver.loadMapParameters(mapFile) - } catch (ex:Exception){ - ex.printStackTrace() - Popup(newGameScreen).apply { - addGoodSizedLabel("Could not load map!").row() - if (ex is UncivShowableException) - addGoodSizedLabel(ex.message).row() - addCloseButton() - open() - } - return@onChange - } - mapParameters.name = mapFile.name() - newGameScreen.gameSetupInfo.mapFile = mapFile - val mapMods = mapParams.mods.partition { RulesetCache[it]?.modOptions?.isBaseRuleset == true } - newGameScreen.gameSetupInfo.gameParameters.mods = LinkedHashSet(mapMods.second) - newGameScreen.gameSetupInfo.gameParameters.baseRuleset = mapMods.first.firstOrNull() ?: mapParams.baseRuleset - newGameScreen.updateRuleset() - newGameScreen.updateTables() - } - return mapFileSelectBox - } - - private fun fillMapFileSelectBox() { - if (!mapFileSelectBox.items.isEmpty) return - val mapFiles = Array() - mapFilesSequence - .sortedWith(compareBy(UncivGame.Current.settings.getCollatorFromLocale()) { it.toString() }) - .forEach { mapFiles.add(it) } - mapFileSelectBox.items = mapFiles - - // Pre-select: a) map saved within last 15min or b) map named in mapParameters or c) alphabetically first - // This is a kludge - the better way would be to have a "play this map now" menu button in the editor - // (which would ideally not even require a save file - which makes implementation non-trivial) - val selectedItem = - mapFiles.maxByOrNull { it.fileHandle.lastModified() } - ?.takeIf { it.fileHandle.lastModified() > System.currentTimeMillis() - 900000 } - ?: mapFiles.firstOrNull { it.fileHandle.name() == mapParameters.name } - ?: mapFiles.firstOrNull() - ?: return - mapFileSelectBox.selected = selectedItem - newGameScreen.gameSetupInfo.mapFile = selectedItem.fileHandle - } - - // 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(): String = fileHandle.name() - } - + internal fun cancelBackgroundJobs() = savedMapOptionsTable.cancelBackgroundJobs() } diff --git a/core/src/com/unciv/ui/screens/newgamescreen/MapParametersTable.kt b/core/src/com/unciv/ui/screens/newgamescreen/MapParametersTable.kt index 4da6916ca2..25f4e14988 100644 --- a/core/src/com/unciv/ui/screens/newgamescreen/MapParametersTable.kt +++ b/core/src/com/unciv/ui/screens/newgamescreen/MapParametersTable.kt @@ -80,7 +80,12 @@ class MapParametersTable( skin = BaseScreen.skin defaults().pad(5f, 10f) if (mapGeneratedMainType == MapGeneratedMainType.randomGenerated) { - add("{Which options should be available to the random selection?}".toLabel()).colspan(2).grow().row() + val prompt = "Which options should be available to the random selection?" + val width = (previousScreen as? NewGameScreen)?.getColumnWidth() ?: 200f + val label = WrappableLabel(prompt, width - 20f) // 20 is the defaults() padding + label.setAlignment(Align.center) + label.wrap = true + add(label).colspan(2).grow().row() } addMapShapeSelectBox() addMapTypeSelectBox() diff --git a/core/src/com/unciv/ui/screens/newgamescreen/NewGameScreen.kt b/core/src/com/unciv/ui/screens/newgamescreen/NewGameScreen.kt index 32c820ed77..15dce0fdfa 100644 --- a/core/src/com/unciv/ui/screens/newgamescreen/NewGameScreen.kt +++ b/core/src/com/unciv/ui/screens/newgamescreen/NewGameScreen.kt @@ -20,10 +20,13 @@ import com.unciv.models.metadata.GameSetupInfo import com.unciv.models.ruleset.RulesetCache import com.unciv.models.translations.tr import com.unciv.ui.components.ExpanderTab +import com.unciv.ui.components.KeyCharAndCode import com.unciv.ui.components.extensions.addSeparator import com.unciv.ui.components.extensions.addSeparatorVertical import com.unciv.ui.components.extensions.disable import com.unciv.ui.components.extensions.enable +import com.unciv.ui.components.extensions.keyShortcuts +import com.unciv.ui.components.extensions.onActivation import com.unciv.ui.components.extensions.onClick import com.unciv.ui.components.extensions.pad import com.unciv.ui.components.extensions.toLabel @@ -76,7 +79,11 @@ class NewGameScreen( updatePlayerPickerRandomLabel = { playerPickerTable.updateRandomNumberLabel() } ) mapOptionsTable = MapOptionsTable(this) - setDefaultCloseAction() + pickerPane.closeButton.onActivation { + mapOptionsTable.cancelBackgroundJobs() + game.popScreen() + } + pickerPane.closeButton.keyShortcuts.add(KeyCharAndCode.BACK) if (isPortrait) initPortrait() else initLandscape() @@ -104,6 +111,7 @@ class NewGameScreen( } private fun onStartGameClicked() { + mapOptionsTable.cancelBackgroundJobs() if (gameSetupInfo.gameParameters.isOnlineMultiplayer) { if (!checkConnectionToMultiplayerServer()) { val noInternetConnectionPopup = Popup(this) @@ -206,6 +214,10 @@ class NewGameScreen( } } + /** Subtables may need an upper limit to their width - they can ask this function. */ + // In sync with isPortrait in init, here so UI details need not know about 3-column vs 1-column layout + internal fun getColumnWidth() = stage.width / (if (isNarrowerThan4to3()) 1 else 3) + private fun initLandscape() { scrollPane.setScrollingDisabled(true,true) diff --git a/core/src/com/unciv/ui/screens/victoryscreen/ReplayMap.kt b/core/src/com/unciv/ui/screens/victoryscreen/ReplayMap.kt index 64b95ba286..5a49ec4aa5 100644 --- a/core/src/com/unciv/ui/screens/victoryscreen/ReplayMap.kt +++ b/core/src/com/unciv/ui/screens/victoryscreen/ReplayMap.kt @@ -2,55 +2,126 @@ package com.unciv.ui.screens.victoryscreen import com.badlogic.gdx.scenes.scene2d.Group import com.unciv.logic.civilization.Civilization +import com.unciv.logic.map.MapShape import com.unciv.logic.map.TileMap +import com.unciv.logic.map.tile.Tile import com.unciv.ui.screens.worldscreen.minimap.MinimapTile import com.unciv.ui.screens.worldscreen.minimap.MinimapTileUtil import kotlin.math.min import kotlin.math.sqrt // Mostly copied from MiniMap -class ReplayMap( - val tileMap: TileMap, - val viewingCiv: Civilization, - private val replayMapWidth: Float, - private val replayMapHeight: Float + +@Suppress("LeakingThis") +/** + * Base for a MiniMap not intertwined with a WorldScreen. + * For a _minimal_ implementation see [LoadMapPreview] + * + * TODO: Analyze why MiniMap needs the tight WorldScreen integration and clean up / merge + */ +abstract class IndependentMiniMap( + val tileMap: TileMap ) : Group() { - private val tileLayer = Group() - private val minimapTiles: List + protected lateinit var minimapTiles: List - init { - val tileSize = calcTileSize() + /** Call this in the init of derived classes. + * + * Needs to be deferred only to allow [calcTileSize] or [includeTileFilter] to use class parameters added in the derived class. */ + protected open fun deferredInit(maxWidth: Float, maxHeight: Float) { + val tileSize = calcTileSize(maxWidth, maxHeight) minimapTiles = createReplayMap(tileSize) - val tileExtension = MinimapTileUtil.spreadOutMinimapTiles(tileLayer, minimapTiles, tileSize) + val tileExtension = MinimapTileUtil.spreadOutMinimapTiles(this, minimapTiles, tileSize) - for (group in tileLayer.children) { + for (group in children) { group.moveBy(-tileExtension.x, -tileExtension.y) } - // there are tiles "below the zero", - // so we zero out the starting position of the whole board so they will be displayed as well - tileLayer.setSize(tileExtension.width, tileExtension.height) - setSize(tileLayer.width, tileLayer.height) - addActor(tileLayer) + setSize(tileExtension.width, tileExtension.height) } - private fun calcTileSize(): Float { + /** Calculate a tile radius in screen coordinates so that the resulting map, after distributimg + * the tiles using spreadOutMinimapTiles, will not exceed the bounds ([maxWidth],[maxHeight]) */ + protected abstract fun calcTileSize(maxWidth: Float, maxHeight: Float): Float + + /** Controls which tiles are included */ + protected open fun includeTileFilter(tile: Tile): Boolean = true + + private fun createReplayMap(tileSize: Float): List { + val doNothing = fun(){} + val tiles = ArrayList(tileMap.values.size) + for (tile in tileMap.values.filter(::includeTileFilter) ) { + val minimapTile = MinimapTile(tile, tileSize, doNothing) + minimapTile.updateColor(false, null) + tiles.add(minimapTile) + } + tiles.trimToSize() + return tiles + } +} + +/** + * A minimap with no WorldScreen dependencies, always shows the entire map. + * + * @param tileMap Map to display minimap-style + * @param maxWidth Resulting Group will not exceed this width + * @param maxHeight Resulting Group will not exceed this height + */ +class LoadMapPreview( + tileMap: TileMap, + maxWidth: Float, + maxHeight: Float +) : IndependentMiniMap(tileMap) { + init { + deferredInit(maxWidth, maxHeight) + } + + override fun calcTileSize(maxWidth: Float, maxHeight: Float): Float { + val height: Float + val width: Float + val mapSize = tileMap.mapParameters.mapSize + if (tileMap.mapParameters.shape != MapShape.rectangular) { + height = mapSize.radius * 2 + 1f + width = height + } else { + height = mapSize.height.toFloat() + width = mapSize.width.toFloat() + } + // See HexMath.worldFromLatLong, the 0.6 is empiric to avoid rounding to cause the map to spill over + return min( + maxWidth / (width + 0.6f) / 1.5f * 2f, + maxHeight / (height + 0.6f) / sqrt(3f) * 2f, + ) + } +} + +/** + * A minimap with no WorldScreen dependencies, with the ability to show historical states. + * + * @param tileMap Map to display minimap-style + * @param viewingCiv used to determine tile visibility and explored area + * @param maxWidth Resulting Group should not exceed this width + * @param maxHeight Resulting Group should not exceed this height + */ +class ReplayMap( + tileMap: TileMap, + val viewingCiv: Civilization, + maxWidth: Float, + maxHeight: Float +) : IndependentMiniMap(tileMap) { + init { + deferredInit(maxWidth, maxHeight) + } + + override fun calcTileSize(maxWidth: Float, maxHeight: Float): Float { val height = viewingCiv.exploredRegion.getHeight().toFloat() val width = viewingCiv.exploredRegion.getWidth().toFloat() - return min( - replayMapHeight / (height + 1.5f) / sqrt(3f) * 4f, // 1.5 - padding, hex height = sqrt(3) / 2 * d / 2 -> d = height / sqrt(3) * 2 * 2 - replayMapWidth / (width + 0.5f) / 0.75f // 0.5 - padding, hex width = 0.75 * d -> d = width / 0.75 + return min ( + maxHeight / (height + 1.5f) / sqrt(3f) * 4f, // 1.5 - padding, hex height = sqrt(3) / 2 * d / 2 -> d = height / sqrt(3) * 2 * 2 + maxWidth / (width + 0.5f) / 0.75f // 0.5 - padding, hex width = 0.75 * d -> d = width / 0.75 ) } - private fun createReplayMap(tileSize: Float): List { - val tiles = ArrayList() - for (tile in tileMap.values.filter { it.isExplored(viewingCiv) }) { - val minimapTile = MinimapTile(tile, tileSize) {} - tiles.add(minimapTile) - } - return tiles - } + override fun includeTileFilter(tile: Tile) = tile.isExplored(viewingCiv) fun update(turn: Int) { val viewingCivIsDefeated = viewingCiv.gameInfo.victoryData != null || !viewingCiv.isAlive() diff --git a/core/src/com/unciv/ui/screens/victoryscreen/VictoryScreenReplay.kt b/core/src/com/unciv/ui/screens/victoryscreen/VictoryScreenReplay.kt index 2b3cda0807..82d4eeff0f 100644 --- a/core/src/com/unciv/ui/screens/victoryscreen/VictoryScreenReplay.kt +++ b/core/src/com/unciv/ui/screens/victoryscreen/VictoryScreenReplay.kt @@ -51,7 +51,7 @@ class VictoryScreenReplay( gameInfo.tileMap, worldScreen.viewingCiv, worldScreen.stage.width - 50, - worldScreen.stage.height - 250 + worldScreen.stage.height - 250 // Empiric: `stage.height - pager.contentScroll_field.height` after init is 244. ) playImage.setSize(24f)