From b1e45f1743d62b3e6cce4d49104419d78e97a01a Mon Sep 17 00:00:00 2001 From: minjaesong Date: Fri, 25 Aug 2023 00:24:12 +0900 Subject: [PATCH] character import wip --- assets/locales/en/terrarum.json | 2 + assets/locales/koKR/terrarum.json | 2 + .../mods/basegame/locales/en/sentences.json | 4 +- .../mods/basegame/locales/koKR/sentences.json | 4 +- .../basegame/weathers/WeatherGeneric.json | 2 +- .../modulebasegame/ui/UIImportAvatar.kt | 161 ++++++++++++++++++ .../modulebasegame/ui/UITitleRemoConYaml.kt | 2 +- src/net/torvald/terrarum/serialise/Ascii85.kt | 39 +++-- 8 files changed, 195 insertions(+), 21 deletions(-) create mode 100644 src/net/torvald/terrarum/modulebasegame/ui/UIImportAvatar.kt diff --git a/assets/locales/en/terrarum.json b/assets/locales/en/terrarum.json index edb97085c..4850b8f9b 100644 --- a/assets/locales/en/terrarum.json +++ b/assets/locales/en/terrarum.json @@ -8,6 +8,7 @@ "GAME_ACTION_MOVE_VERB" : "Move", "GAME_ACTION_ZOOM" : "Zoom", "MENU_IO_AUTOSAVE": "Autosave", + "MENU_IO_CLEAR": "Clear", "MENU_IO_IMPORT": "Import", "MENU_IO_MANUAL_SAVE": "Manual Save", "MENU_LABEL_COPYRIGHT": "Copyright", @@ -16,6 +17,7 @@ "MENU_LABEL_IME": "IME", "MENU_LABEL_IME_TOGGLE": "Toggle IME", "MENU_LABEL_KEYBOARD_LAYOUT": "Keyboard Layout", + "MENU_LABEL_PASTE": "Paste", "MENU_LABEL_PASTE_FROM_CLIPBOARD": "Paste from Clipboard", "MENU_LABEL_PRESS_START_SYMBOL": "Press >", "MENU_LABEL_RESET" : "Reset", diff --git a/assets/locales/koKR/terrarum.json b/assets/locales/koKR/terrarum.json index 095e13e74..3db16d2e8 100644 --- a/assets/locales/koKR/terrarum.json +++ b/assets/locales/koKR/terrarum.json @@ -8,6 +8,7 @@ "GAME_ACTION_MOVE_VERB" : "이동하기", "GAME_ACTION_ZOOM" : "확대·축소", "MENU_IO_AUTOSAVE": "자동 저장", + "MENU_IO_CLEAR": "지우기", "MENU_IO_IMPORT": "가져오기", "MENU_IO_MANUAL_SAVE": "수동 저장", "MENU_LABEL_COPYRIGHT": "저작권", @@ -16,6 +17,7 @@ "MENU_LABEL_IME": "입력기", "MENU_LABEL_IME_TOGGLE": "입력기 켜고 끄기", "MENU_LABEL_KEYBOARD_LAYOUT": "자판 배열", + "MENU_LABEL_PASTE": "붙여넣기", "MENU_LABEL_PASTE_FROM_CLIPBOARD": "복사한 텍스트 붙여넣기", "MENU_LABEL_PRESS_START_SYMBOL": ">을 누르세요", "MENU_LABEL_RESET" : "재설정", diff --git a/assets/mods/basegame/locales/en/sentences.json b/assets/mods/basegame/locales/en/sentences.json index c8dcc92fe..812c07ce4 100644 --- a/assets/mods/basegame/locales/en/sentences.json +++ b/assets/mods/basegame/locales/en/sentences.json @@ -1,3 +1,5 @@ { - "CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "This is a world currently playing." + "CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "This is a world currently playing.", + "CONTEXT_IMPORT_AVATAR_INSTRUCTION_1": "Copy the Avatar Code into the clipboard, then hit the Paste button below.", + "CONTEXT_IMPORT_AVATAR_INSTRUCTION_2": "" } \ No newline at end of file diff --git a/assets/mods/basegame/locales/koKR/sentences.json b/assets/mods/basegame/locales/koKR/sentences.json index 0497bb310..f6129f9aa 100644 --- a/assets/mods/basegame/locales/koKR/sentences.json +++ b/assets/mods/basegame/locales/koKR/sentences.json @@ -1,3 +1,5 @@ { - "CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "현재 플레이 중인 월드입니다." + "CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "현재 플레이 중인 월드입니다.", + "CONTEXT_IMPORT_AVATAR_INSTRUCTION_1": "아바타 코드를 클립보드에 복사한 다음, 아래의 붙여넣기 버튼을 눌러주세요.", + "CONTEXT_IMPORT_AVATAR_INSTRUCTION_2": "" } \ No newline at end of file diff --git a/assets/mods/basegame/weathers/WeatherGeneric.json b/assets/mods/basegame/weathers/WeatherGeneric.json index 141896edb..441618e49 100644 --- a/assets/mods/basegame/weathers/WeatherGeneric.json +++ b/assets/mods/basegame/weathers/WeatherGeneric.json @@ -5,7 +5,7 @@ "cloudChance": 250, "cloudGamma": [0.48, 1.8], "cloudGammaVariance": [0.1, 0.1], - "windSpeed": 0.25, + "windSpeed": 10.25, "windSpeedVariance": 1.0, "clouds": { "cumulonimbus": { diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UIImportAvatar.kt b/src/net/torvald/terrarum/modulebasegame/ui/UIImportAvatar.kt new file mode 100644 index 000000000..8cca62ba6 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/ui/UIImportAvatar.kt @@ -0,0 +1,161 @@ +package net.torvald.terrarum.modulebasegame.ui + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.Input +import com.badlogic.gdx.graphics.Camera +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import net.torvald.terrarum.App +import net.torvald.terrarum.Second +import net.torvald.terrarum.ceilToInt +import net.torvald.terrarum.gamecontroller.* +import net.torvald.terrarum.langpack.Lang +import net.torvald.terrarum.serialise.Ascii85Codec +import net.torvald.terrarum.ui.Toolkit +import net.torvald.terrarum.ui.UIItem +import net.torvald.terrarum.ui.UIItemTextButton +import net.torvald.terrarum.utils.Clipboard + +/** + * Created by minjaesong on 2023-08-24. + */ +class UIImportAvatar(val remoCon: UIRemoCon) : Advanceable() { + + override var width = 480 // SAVE_CELL_WIDTH + override var height = 480 + override var openCloseTime: Second = OPENCLOSE_GENERIC + + private val drawX = (Toolkit.drawWidth - width) / 2 + private val drawY = (App.scr.height - height) / 2 + private val cols = 80 + private val rows = 30 + private val goButtonWidth = 180 + + + private val codeBox = UIItemCodeBox(this, (Toolkit.drawWidth - App.fontSmallNumbers.W * cols) / 2, drawY, cols, rows) + + private val clearButton = UIItemTextButton(this, + { Lang["MENU_IO_CLEAR"] }, drawX + (width/2 - goButtonWidth) / 2, drawY + height - 24 - 34, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true) + private val pasteButton = UIItemTextButton(this, + { Lang["MENU_LABEL_PASTE"] }, drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24 - 34, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true) + + + private val backButton = UIItemTextButton(this, + { Lang["MENU_LABEL_BACK"] }, drawX + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true) + private val goButton = UIItemTextButton(this, + { Lang["MENU_IO_IMPORT"] }, drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true) + + init { + addUIitem(codeBox) + addUIitem(clearButton) + addUIitem(pasteButton) + addUIitem(backButton) + addUIitem(goButton) + + clearButton.clickOnceListener = { _,_ -> + codeBox.clearTextBuffer() + } + pasteButton.clickOnceListener = { _,_ -> + codeBox.pasteFromClipboard() + } + backButton.clickOnceListener = { _,_ -> + remoCon.openUI(UILoadSavegame(remoCon)) + } + goButton.clickOnceListener = { _,_ -> + doImport() + } + } + + + override fun updateUI(delta: Float) { + uiItems.forEach { it.update(delta) } + } + + override fun renderUI(batch: SpriteBatch, camera: Camera) { + uiItems.forEach { it.render(batch, camera) } + } + + override fun dispose() { + } + + override fun advanceMode(button: UIItem) { + } + + private fun doImport() { + val rawStr = codeBox.textBuffer.toString() + // sanity check + + + val ascii85codec = Ascii85Codec((33..117).map { it.toChar() }.joinToString("")) + val ascii85str = rawStr.substring(2 until rawStr.length - 2).replace("z", "!!!!!") + } +} + + +class UIItemCodeBox(parent: UIImportAvatar, initialX: Int, initialY: Int, val cols: Int = 80, val rows: Int = 24) : UIItem(parent, initialX, initialY) { + + override val width = App.fontSmallNumbers.W * cols + override val height = App.fontSmallNumbers.H * rows + override fun dispose() { + } + + private var highlightCol: Color = Toolkit.Theme.COL_INACTIVE + + var selected = false + + private var textCursorPosition = 0 + internal var textBuffer = StringBuilder() + + fun pasteFromClipboard() { + textBuffer.clear() + Clipboard.fetch().forEach { + if (it.code in 33..122) textBuffer.append(it) + } + textCursorPosition += textBuffer.length + } + + override fun update(delta: Float) { + super.update(delta) +// if (!selected && mousePushed) +// selected = true +// else if (selected && mouseDown && !mouseUp) +// selected = false + + if (textBuffer.isEmpty() && (Gdx.input.isKeyJustPressed(Input.Keys.V) && (Gdx.input.isKeyPressed(Input.Keys.CONTROL_LEFT) || Gdx.input.isKeyPressed(Input.Keys.CONTROL_RIGHT)))) { + pasteFromClipboard() + } + } + + override fun render(batch: SpriteBatch, camera: Camera) { + // draw box backgrounds + batch.color = UIInventoryFull.CELL_COL + Toolkit.fillArea(batch, posX, posY, width, height) + + // draw borders + batch.color = if (selected) Toolkit.Theme.COL_SELECTED else if (mouseUp) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_INACTIVE + Toolkit.drawBoxBorder(batch, posX - 1, posY - 1, width + 2, height + 2) + + // draw texts + if (textBuffer.isEmpty()) { + batch.color = Toolkit.Theme.COL_INACTIVE + App.fontGame.draw(batch, Lang["CONTEXT_IMPORT_AVATAR_INSTRUCTION_1"], posX + 5, posY) + App.fontGame.draw(batch, Lang["CONTEXT_IMPORT_AVATAR_INSTRUCTION_2"], posX + 5f, posY + App.fontGame.lineHeight) + } + else { + batch.color = Color.WHITE + val scroll = ((textBuffer.length.toDouble() / cols).ceilToInt() - rows).coerceAtLeast(0) + for (i in scroll * cols until textBuffer.length) { + val c = textBuffer[i] + val x = ((i - scroll * cols) % cols) * App.fontSmallNumbers.W.toFloat() + val y = ((i - scroll * cols) / cols) * App.fontSmallNumbers.H.toFloat() + + App.fontSmallNumbers.draw(batch, "$c", posX + x, posY + y) + } + } + } + + fun clearTextBuffer() { + textBuffer.clear() + textCursorPosition = 0 + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/ui/UITitleRemoConYaml.kt b/src/net/torvald/terrarum/modulebasegame/ui/UITitleRemoConYaml.kt index 9880adabf..eb42a4f04 100644 --- a/src/net/torvald/terrarum/modulebasegame/ui/UITitleRemoConYaml.kt +++ b/src/net/torvald/terrarum/modulebasegame/ui/UITitleRemoConYaml.kt @@ -41,7 +41,7 @@ object UITitleRemoConYaml { // todo add MENU_IO_IMPORT val injectedMenuSingleCharSel = """ -- MENU_IO_IMPORT +- MENU_IO_IMPORT : net.torvald.terrarum.modulebasegame.ui.UIImportAvatar - CONTEXT_CHARACTER_NEW : net.torvald.terrarum.modulebasegame.ui.UINewCharacter - MENU_LABEL_RETURN """ diff --git a/src/net/torvald/terrarum/serialise/Ascii85.kt b/src/net/torvald/terrarum/serialise/Ascii85.kt index 3df000ef9..1a9c83b53 100644 --- a/src/net/torvald/terrarum/serialise/Ascii85.kt +++ b/src/net/torvald/terrarum/serialise/Ascii85.kt @@ -4,27 +4,26 @@ import net.torvald.terrarum.savegame.toBigEndian import java.util.UUID import kotlin.math.ceil -/** - * Ascii85 implementation with my own character table based on RFC 1924. Will NOT truncate '00000' into something else; - * just gzip the inputstream instead! +/** My own string set that: + * - no "/": avoids nonstandard JSON comment key which GDX will happily parse away + * - no "\": you know what I mean\\intention + * - no "$": avoids Kotlin string template + * - no "[{]},": even the dumbest parser can comprehend the output */ -object Ascii85 { - /** My own string set that: - * - no "/": avoids nonstandard JSON comment key which GDX will happily parse away - * - no "\": you know what I mean\\intention - * - no "$": avoids Kotlin string template - * - no "[{]},": even the dumbest parser can comprehend the output - */ - private const val CHAR_TABLE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#%&'()*+-.:;<=>?@^_`|~" +open class Ascii85Codec(private val CHAR_TABLE: String = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#%&'()*+-.:;<=>?@^_`|~") { + init { + if (CHAR_TABLE.length != 85) throw IllegalArgumentException("CHAR_TABLE is not 85 chars long") + } + /** As per Adobe standard */ //private val CHAR_TABLE = (33 until (33+85)).toList().map { it.toChar() }.joinToString("") // testing only! private val INVERSE_TABLE = LongArray(127) /** Int of `-1` */ - const val PAD_BYTE = -1 + val PAD_BYTE = -1 /** Null-character (`\0`) */ - const val PAD_CHAR = 0.toChar() + val PAD_CHAR = 0.toChar() private val INTERNAL_PAD_BYTE = 0 private val INTERNAL_PAD_CHAR = CHAR_TABLE.last() @@ -97,10 +96,10 @@ object Ascii85 { } val sum = (INVERSE_TABLE[s1.toInt()] * 52200625) + - (INVERSE_TABLE[s2.toInt()] * 614125) + - (INVERSE_TABLE[s3.toInt()] * 7225) + - (INVERSE_TABLE[s4.toInt()] * 85) + - INVERSE_TABLE[s5.toInt()] + (INVERSE_TABLE[s2.toInt()] * 614125) + + (INVERSE_TABLE[s3.toInt()] * 7225) + + (INVERSE_TABLE[s4.toInt()] * 85) + + INVERSE_TABLE[s5.toInt()] return ByteArray(4 - padLen) { sum.ushr((3 - it) * 8).and(255).toByte() } } @@ -137,6 +136,12 @@ object Ascii85 { } } +/** + * Ascii85 implementation with my own character table based on RFC 1924. Will NOT truncate '00000' into something else; + * just gzip the inputstream instead! + */ +object Ascii85 : Ascii85Codec() + fun UUID.toAscii85() = Ascii85.encodeBytes(this.mostSignificantBits.toBigEndian() + this.leastSignificantBits.toBigEndian()) fun String.ascii85toUUID(): UUID {