world portal listing gui

This commit is contained in:
minjaesong
2023-05-29 17:51:15 +09:00
parent 98a6c9ae70
commit 331e89b4df
6 changed files with 186 additions and 39 deletions

View File

@@ -18,5 +18,6 @@
"GAME_CRAFTING": "Crafting",
"GAME_CRAFTABLE_ITEMS": "Craftable Items",
"CONTEXT_WORLD_SEARCH": "World Search",
"CONTEXT_WORLD_LIST": "Worlds List"
"CONTEXT_WORLD_LIST": "Worlds List",
"MENU_LABEL_RENAME": "Rename"
}

View File

@@ -148,5 +148,5 @@ object AVKey {
*
* example value: `"SIxM+kGlrjZgLx5Zeqz7,;:UIZ5Q=2WT35SgKpOp.,vvf'fNW3G<ROimy(Y;E<,-mdtr5|^RGOqr0x*T*lC,YABr1oQwErKG)pGC'gUG"`
*/
const val WORLD_PORTAL_DICT = ""
const val WORLD_PORTAL_DICT = "worldportaldict"
}

View File

@@ -123,6 +123,9 @@ object Lang {
private val bindOp = ">>="
fun getOrNull(key: String?, capitalise: Boolean = true) =
if (key == null) null else get(key, capitalise)
/**
* Syntax example:
*

View File

@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.*
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_Y
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.YPOS_CORRECTION
@@ -12,6 +13,7 @@ import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.internal
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.internalWidth
import net.torvald.terrarum.ui.*
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.unicode.getKeycapConsole
import net.torvald.unicode.getKeycapPC
/**
@@ -57,9 +59,12 @@ class UIWorldPortal : UICanvas(
private val SP = "\u3000 "
val portalListingControlHelp: String
get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
"${getKeycapPC(App.getConfigInt("control_key_up"))}${getKeycapPC(App.getConfigInt("control_key_down"))}" +
" ${Lang["MENU_CONTROLS_SCROLL"]}" +
"$SP${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
else
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}" +
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}" +
"$SP${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}" +
"$SP${App.gamepadLabelLT} ${Lang["GAME_WORLD_SEARCH"]}" +
"$SP${App.gamepadLabelRT} ${Lang["GAME_INVENTORY"]}"
@@ -105,6 +110,12 @@ class UIWorldPortal : UICanvas(
transitionPanel.render(batch, camera)
}
override fun show() {
super.show()
transitionPanel.show()
INGAME.setTooltipMessage(null)
}
override fun dispose() {
catBar.dispose()
}
@@ -153,7 +164,7 @@ class UIItemWorldPortalTopBar(
init {
CommonResourcePool.addToLoadingList("terrarum-basegame-worldportalicons") {
TextureRegionPack(ModMgr.getGdxFile("basegame", "gui/worldportal_catbar.tga"), 20, 20)
TextureRegionPack(ModMgr.getGdxFile("basegame", "gui/worldportal_catbar.tga"), 30, 20)
}
CommonResourcePool.loadAll()
}

View File

@@ -2,17 +2,19 @@ package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.terrarum.*
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_Y
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.getCellCountVertically
import net.torvald.terrarum.realestate.LandUtil.CHUNK_H
import net.torvald.terrarum.realestate.LandUtil.CHUNK_W
import net.torvald.terrarum.savegame.ByteArray64Reader
import net.torvald.terrarum.savegame.DiskSkimmer
import net.torvald.terrarum.savegame.EntryFile
import net.torvald.terrarum.savegame.*
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.serialise.ascii85toUUID
import net.torvald.terrarum.ui.Toolkit
@@ -22,7 +24,10 @@ import net.torvald.terrarum.ui.UIItemTextButton
import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.unicode.EMDASH
import java.time.Instant
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.zip.GZIPInputStream
/**
* Created by minjaesong on 2023-05-19.
@@ -36,18 +41,18 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
private val buttonHeight = 24
private val gridGap = 10
private val worldList = ArrayList<WorldInfo>()
private val thumbw = 360
private val thumbh = 240
private val thumbw = 378
private val textAreaW = thumbw - 32
private val thumbh = 252
private val hx = Toolkit.drawWidth.div(2)
private val y = INVENTORY_CELLS_OFFSET_Y() + 1
private val listCount = getCellCountVertically(UIItemWorldCellsSimple.height, gridGap)
private val listHeight = UIItemWorldCellsSimple.height + (listCount - 1) * (UIItemWorldCellsSimple.height + gridGap)
private val memoryGaugeWidth = 360
private val memoryGaugeWidth = textAreaW
private val deleteButtonWidth = (memoryGaugeWidth - gridGap) / 2
private val buttonDeleteWorld = UIItemTextButton(this,
"MENU_LABEL_DELETE_WORLD",
@@ -68,16 +73,24 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
alignment = UIItemTextButton.Companion.Alignment.CENTRE
)
private val worldList = ArrayList<WorldInfo>()
data class WorldInfo(
val uuid: UUID,
val diskSkimmer: DiskSkimmer,
val dimensionInChunks: Int,
val seed: Long
)
val seed: Long,
val lastPlayedString: String,
val totalPlayedString: String,
val screenshot: TextureRegion?,
) {
fun dispose() {
screenshot?.texture?.dispose()
}
}
init {
CommonResourcePool.addToLoadingList("terrarum-basegame-worldportalicons") {
TextureRegionPack(ModMgr.getGdxFile("basegame", "gui/worldportal_catbar.tga"), 20, 20)
TextureRegionPack(ModMgr.getGdxFile("basegame", "gui/worldportal_catbar.tga"), 30, 20)
}
CommonResourcePool.loadAll()
@@ -92,27 +105,51 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
private lateinit var worldCells: Array<UIItemWorldCellsSimple>
private var selected: UIItemWorldCellsSimple? = null
private var selectedIndex: Int? = null
override fun show() {
worldList.clear()
worldList.addAll((INGAME.actorGamer.actorValue.getAsString(AVKey.WORLD_PORTAL_DICT) ?: "").split(",").filter { it.isNotBlank() }.map {
(INGAME.actorGamer.actorValue.getAsString(AVKey.WORLD_PORTAL_DICT) ?: "").split(",").filter { it.isNotBlank() }.map {
it.ascii85toUUID().let { it to App.savegameWorlds[it] }
}.filter { it.second != null }.map { (uuid, disk) ->
}.filter { it.second != null }.mapIndexed { index, (uuid, disk) ->
var chunksCount = 0
var seed = 0L
worldList.forEach {
var w = 0
var h = 0
JsonFetcher.readFromJsonString(ByteArray64Reader((disk!!.requestFile(-1)!!.contents.getContent() as EntryFile).bytes, Common.CHARSET)).let {
JsonFetcher.forEachSiblings(it) { name, value ->
if (name == "width") w = value.asInt()
if (name == "height") h = value.asInt()
if (name == "generatorSeed") seed = value.asLong()
}
var lastPlayed = 0L
var totalPlayed = 0L
var w = 0
var h = 0
var thumb: TextureRegion? = null
JsonFetcher.readFromJsonString(ByteArray64Reader(disk!!.requestFile(-1)!!.contents.getContent() as ByteArray64, Common.CHARSET)).let {
JsonFetcher.forEachSiblings(it) { name, value ->
if (name == "width") w = value.asInt()
if (name == "height") h = value.asInt()
if (name == "generatorSeed") seed = value.asLong()
if (name == "lastPlayTime") lastPlayed = value.asLong()
if (name == "totalPlayTime") totalPlayed = value.asLong()
}
chunksCount = (w / CHUNK_W) * (h / CHUNK_H)
}
WorldInfo(uuid, disk!!, chunksCount, seed)
} as List<WorldInfo>)
chunksCount = (w / CHUNK_W) * (h / CHUNK_H)
disk.requestFile(-2)?.let {
val zippedTga = (it.contents as EntryFile).bytes
val gzin = GZIPInputStream(ByteArray64InputStream(zippedTga))
val tgaFileContents = gzin.readAllBytes(); gzin.close()
val thumbPixmap = Pixmap(tgaFileContents, 0, tgaFileContents.size)
val thumbTex = Texture(thumbPixmap)
thumbTex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
thumb = TextureRegion(thumbTex)
}
WorldInfo(uuid, disk, chunksCount, seed, lastPlayed.toTimestamp(), totalPlayed.toDurationStamp(), thumb)
}.let {
worldList.addAll(it)
}
chunksUsed = worldList.sumOf { it.dimensionInChunks }
@@ -123,11 +160,42 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
y + (gridGap + UIItemWorldCellsSimple.height) * it,
worldList.getOrNull(it),
worldList.getOrNull(it)?.diskSkimmer?.getDiskName(Common.CHARSET)
)
).also { button ->
button.clickOnceListener = { _, _, _ ->
selected = button
selectedIndex = it
updateUIbyButtonSelection()
}
}
}
uiItems.forEach { it.show() }
worldCells.forEach { it.show() }
selected = null
updateUIbyButtonSelection()
}
private fun Long.toTimestamp() = Instant.ofEpochSecond(this)
.atZone(TimeZone.getDefault().toZoneId())
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
private fun Long.toDurationStamp(): String {
val s = this % 60
val m = (this / 60) % 60
val h = (this / 3600) % 24
val d = this / 86400
return if (d == 0L)
"${h.toString().padStart(2,'0')}h${m.toString().padStart(2,'0')}m${s.toString().padStart(2,'0')}s"
else
"${d}d${h.toString().padStart(2,'0')}h${m.toString().padStart(2,'0')}m${s.toString().padStart(2,'0')}s"
}
private val nullTimestamp = "0000-00-00 --:--:--"
private val nullDurationStamp = "--h--m--s"
private fun Int.chunkCountToWorldSize() = when(this) {
in 0 until 2000 -> "CONTEXT_DESCRIPTION_SMALL"
in 2000 until 4500 -> "MENU_SETTING_MEDIUM"
in 4500 until 10000 -> "CONTEXT_DESCRIPTION_BIG"
else -> "CONTEXT_DESCRIPTION_HUGE"
}
override fun updateUI(delta: Float) {
@@ -135,11 +203,35 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
worldCells.forEach { it.update(delta) }
}
private val iconGap = 12f
private val iconSize = 30f
private val textualListHeight = 30f
private val iconSizeGap = iconSize + iconGap
private fun updateUIbyButtonSelection() {
worldTexts = listOf(
// last played
icons.get(2, 1) to (selected?.worldInfo?.lastPlayedString ?: nullTimestamp),
// total played
icons.get(0, 2) to (selected?.worldInfo?.totalPlayedString ?: nullDurationStamp),
// world size
icons.get(1, 2) to (Lang.getOrNull(selected?.worldInfo?.dimensionInChunks?.chunkCountToWorldSize()) ?: "$EMDASH"),
)
selectedWorldThumb = selected?.worldInfo?.screenshot
worldCells.forEach {
it.highlighted = (selected == it && selected?.worldInfo != null)
}
}
private lateinit var worldTexts: List<Pair<TextureRegion, String>>
private var selectedWorldThumb: TextureRegion? = null
val icons = CommonResourcePool.getAsTextureRegionPack("terrarum-basegame-worldportalicons")
override fun renderUI(batch: SpriteBatch, camera: Camera) {
val memoryGaugeXpos = hx - memoryGaugeWidth - gridGap/2
val memoryGaugeYpos = y + listHeight - buttonHeight - gridGap - buttonHeight
val icons = CommonResourcePool.getAsTextureRegionPack("terrarum-basegame-worldportalicons")
// draw background //
// screencap panel
@@ -157,13 +249,30 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
val barCol = UIItemInventoryCellCommonRes.getHealthMeterColour(chunksMax - chunksUsed, 0, chunksMax)
val barBack = barCol mul UIItemInventoryCellCommonRes.meterBackDarkening
batch.color = Color.WHITE
batch.draw(icons.get(2, 2), memoryGaugeXpos - 32f, memoryGaugeYpos + 2f)
batch.draw(icons.get(2, 2), memoryGaugeXpos - iconSizeGap, memoryGaugeYpos + 2f)
Toolkit.drawBoxBorder(batch, memoryGaugeXpos - 1, memoryGaugeYpos - 1, memoryGaugeWidth + 2, buttonHeight + 2)
batch.color = barBack
Toolkit.fillArea(batch, memoryGaugeXpos, memoryGaugeYpos, memoryGaugeWidth, buttonHeight)
batch.color = barCol
Toolkit.fillArea(batch, memoryGaugeXpos, memoryGaugeYpos, (memoryGaugeWidth * (chunksUsed / chunksMax.toFloat())).ceilInt(), buttonHeight)
batch.color = Color.WHITE
if (selected?.worldInfo != null) {
// some texts
worldTexts.forEachIndexed { index, (icon, str) ->
batch.draw(icon, memoryGaugeXpos - iconSizeGap, y + thumbh + 16f + textualListHeight * index)
App.fontGame.draw(batch, str, memoryGaugeXpos.toFloat(), y + thumbh + 16f + textualListHeight * index)
}
// size indicator on the memory gauge
Toolkit.fillArea(batch, memoryGaugeXpos, memoryGaugeYpos, (memoryGaugeWidth * (selected?.worldInfo!!.dimensionInChunks / chunksMax.toFloat())).ceilInt(), buttonHeight)
// thumbnail
selected?.worldInfo?.screenshot?.let {
batch.draw(it, (hx - thumbw - gridGap/2).toFloat(), y.toFloat(), thumbw.toFloat(), thumbh.toFloat())
}
}
uiItems.forEach { it.render(batch, camera) }
@@ -171,7 +280,7 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
// control hints
batch.color = Color.WHITE
App.fontGame.draw(batch, full.portalListingControlHelp, (hx - thumbw - gridGap/2).toInt(), (full.yEnd - 20).toInt())
App.fontGame.draw(batch, full.portalListingControlHelp, (hx - textAreaW - gridGap/2).toInt(), (full.yEnd - 20).toInt())
}
override fun hide() {
@@ -179,11 +288,33 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
worldCells.forEach { it.hide() }
worldCells.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
worldList.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
}
override fun dispose() {
uiItems.forEach { it.dispose() }
worldCells.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
worldList.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
if (this.isVisible && mouseInScreen(screenX, screenY)) {
uiItems.forEach { it.touchDown(screenX, screenY, pointer, button) }
worldCells.forEach { it.touchDown(screenX, screenY, pointer, button) }
handler.subUIs.forEach { it.touchDown(screenX, screenY, pointer, button) }
return true
}
else return false
}
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
if (this.isVisible) {
uiItems.forEach { it.touchUp(screenX, screenY, pointer, button) }
worldCells.forEach { it.touchUp(screenX, screenY, pointer, button) }
handler.subUIs.forEach { it.touchUp(screenX, screenY, pointer, button) }
return true
}
else return false
}
}
@@ -197,15 +328,16 @@ class UIItemWorldCellsSimple(
) : UIItem(parent, initialX, initialY) {
companion object {
const val width = 360
const val width = 378
const val height = 46
}
override val width: Int = 360
override val width: Int = 378
override val height: Int = 46
private val icons = CommonResourcePool.getAsTextureRegionPack("terrarum-basegame-worldportalicons")
var highlighted = false
override fun show() {
super.show()
@@ -228,7 +360,7 @@ class UIItemWorldCellsSimple(
Toolkit.fillArea(batch, posX, posY, width, height)
// draw border
val bcol = if (mouseUp && mousePushed) Toolkit.Theme.COL_SELECTED
val bcol = if (highlighted || mouseUp && mousePushed) Toolkit.Theme.COL_SELECTED
else if (mouseUp) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_LIST_DEFAULT
batch.color = bcol
Toolkit.drawBoxBorder(batch, posX - 1, posY - 1, width + 2, height + 2)
@@ -236,7 +368,7 @@ class UIItemWorldCellsSimple(
batch.draw(icons.get(0, 1), posX + 4f, posY + 1f)
App.fontGame.draw(batch, worldName ?: "$EMDASH", posX + 32, posY + 1)
batch.draw(icons.get(1, 1), posX + 4f, posY + 25f)
App.fontGame.draw(batch, if (worldInfo?.seed == null) "$EMDASH" else "${(if (worldInfo.seed > 0) " + " else "")}${worldInfo.seed}" , posX + 32, posY + 25)
App.fontGame.draw(batch, if (worldInfo?.seed == null) "$EMDASH" else "${(if (worldInfo.seed > 0) "+" else "")}${worldInfo.seed}" , posX + 32, posY + 25)
// text separator
batch.color = bcol.cpy().sub(0f,0f,0f,0.65f)
Toolkit.fillArea(batch, posX + 2, posY + 23, width - 4, 1)