save load screen

This commit is contained in:
minjaesong
2021-09-12 22:47:31 +09:00
parent cdf82270dc
commit b99d1a760e
10 changed files with 296 additions and 137 deletions

View File

@@ -1,11 +1,14 @@
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.Input
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.graphics.glutils.ShaderProgram
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import net.torvald.getKeycapConsole
import net.torvald.getKeycapPC
import net.torvald.terrarum.*
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.serialise.Common
@@ -14,15 +17,13 @@ import net.torvald.terrarum.serialise.ReadMeta
import net.torvald.terrarum.tvda.ByteArray64InputStream
import net.torvald.terrarum.tvda.VDUtil
import net.torvald.terrarum.tvda.VirtualDisk
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItem
import net.torvald.terrarum.ui.UIItemTextButton
import net.torvald.terrarum.ui.*
import java.io.File
import java.time.Instant
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.zip.GZIPInputStream
import kotlin.math.roundToInt
/**
* Created by minjaesong on 2021-09-09.
@@ -37,6 +38,38 @@ class UILoadDemoSavefiles : UICanvas() {
set(value) {}
override var openCloseTime: Second = 0f
private val shaderAlfamul = ShaderProgram(vert, frag)
private val shapeRenderer = ShapeRenderer()
private val transparent = Color(1f,1f,1f,0f)
private val white = Color(1f, 1f, 1f, 1f)
private val uiWidth = UIItemDemoSaveCells.WIDTH // 480
private val uiX = (width - uiWidth) / 2
private val textH = App.fontGame.lineHeight.toInt()
private val cellGap = 24
private val cellInterval = cellGap + UIItemDemoSaveCells.HEIGHT
private val gradAreaHeight = 32
private val titleTextPosY: Int = App.scr.tvSafeGraphicsHeight + 10
private val titleTopGradStart: Int = titleTextPosY + textH
private val titleTopGradEnd: Int = titleTopGradStart + gradAreaHeight
private val titleBottomGradStart: Int = height - App.scr.tvSafeGraphicsHeight - gradAreaHeight
private val titleBottomGradEnd: Int = titleBottomGradStart + gradAreaHeight
private val controlHelperY: Int = titleBottomGradStart + gradAreaHeight - textH
private val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("config_keyup"))}${getKeycapPC(App.getConfigInt("config_keydown"))}" +
" ${Lang["MENU_CONTROLS_SCROLL"]}"
else
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
// read savegames
init {
File(App.defaultSaveDir).listFiles().map { file ->
@@ -48,23 +81,139 @@ class UILoadDemoSavefiles : UICanvas() {
null
}
}.filter { it != null }.sortedByDescending { (it as VirtualDisk).entries[0]!!.modificationDate }.forEachIndexed { index, disk ->
val x = (width - UIItemDemoSaveCells.WIDTH) / 2
val y = 144 + (24 + UIItemDemoSaveCells.HEIGHT) * index
val x = uiX
val y = titleTopGradEnd + cellInterval * index
addUIitem(UIItemDemoSaveCells(this, x, y, disk as VirtualDisk))
}
}
private var scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
private var listScroll = 0 // only update when animation is finished
private var savesVisible = (scrollAreaHeight + cellGap) / cellInterval
private var uiScroll = 0f
private var scrollFrom = 0
private var scrollTarget = 0
private var scrollAnimCounter = 0f
private val scrollAnimLen = 0.1f
private var sliderFBO = FrameBuffer(Pixmap.Format.RGBA8888, uiWidth + 10, height, true)
private var sliderFBO2 = FrameBuffer(Pixmap.Format.RGBA8888, width, height, true)
override fun updateUI(delta: Float) {
uiItems.forEach { it.update(delta) }
if (scrollTarget != listScroll) {
if (scrollAnimCounter < scrollAnimLen) {
scrollAnimCounter += delta
uiScroll = Movement.fastPullOut(
scrollAnimCounter / scrollAnimLen,
listScroll * cellInterval.toFloat(),
scrollTarget * cellInterval.toFloat()
)
}
else {
scrollAnimCounter = 0f
listScroll = scrollTarget
uiScroll = cellInterval.toFloat() * scrollTarget
}
}
uiItems.forEachIndexed { index, it ->
if (index in listScroll - 2 until listScroll + savesVisible + 2) {
// re-position
it.posY = (it.initialY - uiScroll).roundToInt()
it.update(delta)
}
}
}
override fun renderUI(batch: SpriteBatch, camera: Camera) {
uiItems.forEach { it.render(batch, camera) }
val loadGameTitleStr = Lang["MENU_IO_LOAD_GAME"]
App.fontGame.draw(batch, loadGameTitleStr, (width - App.fontGame.getWidth(loadGameTitleStr)).div(2).toFloat(), 62f)
batch.end()
lateinit var savePixmap: Pixmap
sliderFBO.inAction(camera as OrthographicCamera, batch) {
gdxClearAndSetBlend(0f,0f,0f,0f)
setCameraPosition(batch, camera, 0f, 0f)
batch.color = Color.WHITE
batch.inUse {
uiItems.forEachIndexed { index, it ->
if (index in listScroll - 2 until listScroll + savesVisible + 2) {
it.render(batch, camera)
}
}
}
savePixmap = Pixmap.createFromFrameBuffer(0, 0, sliderFBO.width, sliderFBO.height)
savePixmap.blending = Pixmap.Blending.None
}
// implement "wipe-out" by CPU-rendering (*deep exhale*)
//savePixmap.setColor(1f,1f,1f,0f)
savePixmap.setColor(0f,0f,0f,0f)
savePixmap.fillRectangle(0, savePixmap.height - titleTopGradStart, savePixmap.width, titleTopGradStart)
// top grad
for (y in titleTopGradStart until titleTopGradEnd) {
val alpha = (y - titleTopGradStart).toFloat() / gradAreaHeight
for (x in 0 until savePixmap.width) {
val col = savePixmap.getPixel(x, savePixmap.height - y)
val blendAlpha = (col.and(0xFF) * alpha).roundToInt()
savePixmap.drawPixel(x, savePixmap.height - y, col.and(0xFFFFFF00.toInt()) or blendAlpha)
}
}
// bottom grad
for (y in titleBottomGradStart until titleBottomGradEnd) {
val alpha = 1f - ((y - titleBottomGradStart).toFloat() / gradAreaHeight)
for (x in 0 until savePixmap.width) {
val col = savePixmap.getPixel(x, savePixmap.height - y)
val blendAlpha = (col.and(0xFF) * alpha).roundToInt()
savePixmap.drawPixel(x, savePixmap.height - y, col.and(0xFFFFFF00.toInt()) or blendAlpha)
}
}
savePixmap.setColor(0f,0f,0f,0f)
savePixmap.fillRectangle(0, 0, savePixmap.width, height - titleBottomGradEnd + 1)
setCameraPosition(batch, camera, 0f, 0f)
val saveTex = Texture(savePixmap)
batch.inUse {
batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f)
// draw texts
val loadGameTitleStr = Lang["MENU_IO_LOAD_GAME"]
App.fontGame.draw(batch, loadGameTitleStr, (width - App.fontGame.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat())
App.fontGame.draw(batch, controlHelp, uiX.toFloat(), controlHelperY.toFloat())
}
saveTex.dispose()
savePixmap.dispose()
batch.begin()
}
override fun keyDown(keycode: Int): Boolean {
if (this.isVisible) {
if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("config_keyup")) && scrollTarget > 0) {
scrollFrom = listScroll
scrollTarget -= 1
scrollAnimCounter = 0f
}
else if ((keycode == Input.Keys.DOWN || keycode == App.getConfigInt("config_keydown")) && scrollTarget < uiItems.size - savesVisible) {
scrollFrom = listScroll
scrollTarget += 1
scrollAnimCounter = 0f
}
}
return true
}
override fun doOpening(delta: Float) {
}
@@ -75,9 +224,80 @@ class UILoadDemoSavefiles : UICanvas() {
}
override fun endClosing(delta: Float) {
listScroll = 0
scrollTarget = 0
uiScroll = 0f
}
override fun dispose() {}
override fun dispose() {
shapeRenderer.dispose()
sliderFBO.dispose()
sliderFBO2.dispose()
shaderAlfamul.dispose()
}
override fun resize(width: Int, height: Int) {
super.resize(width, height)
scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
savesVisible = (scrollAreaHeight + cellInterval) / (cellInterval + UIItemDemoSaveCells.HEIGHT)
listScroll = 0
scrollTarget = 0
uiScroll = 0f
sliderFBO.dispose()
sliderFBO = FrameBuffer(Pixmap.Format.RGBA8888, uiWidth + 10, height, true)
sliderFBO2.dispose()
sliderFBO2 = FrameBuffer(Pixmap.Format.RGBA8888, width, height, true)
}
private fun setCameraPosition(batch: SpriteBatch, camera: Camera, newX: Float, newY: Float) {
camera.position.set((-newX + App.scr.halfw).round(), (-newY + App.scr.halfh).round(), 0f)
camera.update()
batch.projectionMatrix = camera.combined
}
companion object {
private val vert = """
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
uniform mat4 u_projTrans; // camera.combined
varying vec4 v_color;
varying vec2 v_texCoords;
void main() {
v_color = a_color;
v_texCoords = a_texCoord0;
gl_Position = u_projTrans * a_position;
}
""".trimIndent()
private val frag = """
#version 120
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
uniform sampler2D tex1;
void main(void) {
vec4 colorTex0 = texture2D(u_texture, v_texCoords); // dest
vec4 colorTex1 = texture2D(tex1, v_texCoords); // src mask; must be (1,1,1,a)
// gl_FragColor = colorTex0 * colorTex1;
gl_FragColor = colorTex1.aaar;
}
""".trimIndent()
}
}
class UIItemDemoSaveCells(
@@ -88,7 +308,7 @@ class UIItemDemoSaveCells(
companion object {
const val WIDTH = 480
const val HEIGHT = 160
const val HEIGHT = 120
}
override val width: Int = WIDTH
@@ -100,9 +320,6 @@ class UIItemDemoSaveCells(
private val meta = ReadMeta(disk)
private val x = initialX.toFloat()
private val y = initialY.toFloat()
private fun parseDuration(seconds: Long): String {
val s = seconds % 60
val m = (seconds / 60) % 60
@@ -130,7 +347,7 @@ class UIItemDemoSaveCells(
val thumbTex = Texture(thumbPixmap)
thumbTex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
thumb = TextureRegion(thumbTex)
thumb.setRegion(0, thumbTex.height / 4, thumbTex.width, thumbTex.height / 2)
thumb.setRegion(0, (thumbTex.height - 2 * height) / 2, width * 2, height * 2)
}
catch (e: NullPointerException) {
// use stock texture
@@ -145,11 +362,13 @@ class UIItemDemoSaveCells(
override fun render(batch: SpriteBatch, camera: Camera) {
val highlightCol = if (mouseUp) UIItemTextButton.defaultActiveCol else Color.WHITE
val x = posX.toFloat()
val y = posY.toFloat()
// TODO draw border
batch.color = highlightCol
Toolkit.drawBoxBorder(batch, x.toInt()-1, y.toInt()-1, width+2, height+2)
Toolkit.drawBoxBorder(batch, posX-1, posY-1, width+2, height+2)
// draw thumbnail
batch.color = Color.WHITE
@@ -157,14 +376,14 @@ class UIItemDemoSaveCells(
batch.draw(thumb, x, y + height, width.toFloat(), -height.toFloat())
// draw gradient
blendMul(batch)
batch.draw(grad, x + width, y, -width.toFloat(), height.toFloat())
batch.draw(grad, x + width.toFloat(), y, -width.toFloat(), height.toFloat())
// draw texts
batch.color = highlightCol
// draw timestamp
blendNormal(batch)
val tlen = App.fontSmallNumbers.getWidth(lastPlayedTimestamp)
App.fontSmallNumbers.draw(batch, lastPlayedTimestamp, posX + (width - tlen) - 3f, posY + height - 16f)
App.fontSmallNumbers.draw(batch, lastPlayedTimestamp, x + (width - tlen) - 3f, y + height - 16f)
// draw savegame name
// App.fontGame.draw(batch, disk.getDiskNameString(Common.CHARSET), posX + 3f, posY + 1f)