diff --git a/assets/graphics/code_input_cells.tga b/assets/graphics/code_input_cells.tga new file mode 100644 index 000000000..a3619ef92 --- /dev/null +++ b/assets/graphics/code_input_cells.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2aabf5f054d00112b20caee427e28eb23cc3abb054641162af0a7fad88cfc10 +size 4242 diff --git a/src/net/torvald/terrarum/Terrarum.kt b/src/net/torvald/terrarum/Terrarum.kt index 5026d6601..ba12d17d1 100644 --- a/src/net/torvald/terrarum/Terrarum.kt +++ b/src/net/torvald/terrarum/Terrarum.kt @@ -1044,7 +1044,7 @@ fun distBetween(a: ActorWithBody, bpos: Vector2): Double { val dist = min(min(bpos.distanceSquared(apos1), bpos.distanceSquared(apos2)), bpos.distanceSquared(apos3)) return dist.sqrt() } -const val hashStrMap = "YBNDRFG8EJKMCPQXOTLVWIS2A345H769" +const val hashStrMap = "YBNDRFGWEJKMCPQXOTLVUIS2A345H769" fun getHashStr(length: Int = 5) = (0 until length).map { hashStrMap[Math.random().times(32).toInt()] }.joinToString("") fun List.cartesianProduct(other: List) = this.flatMap { thisIt -> diff --git a/src/net/torvald/terrarum/modulebasegame/gameworld/GamePostalService.kt b/src/net/torvald/terrarum/modulebasegame/gameworld/GamePostalService.kt index 1368e30e3..03f8ca494 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameworld/GamePostalService.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameworld/GamePostalService.kt @@ -108,7 +108,7 @@ HEADER: Notes: post is either: 1. always pre-paid when posted 2. not yet encrypted when in not-posted status (can always calculate postage on the fly) - so no postage information is needed for the Serialised Format + so no postage information is needed for the Serialised Format CONTENTSRAW - Uint48 unzipped size diff --git a/src/net/torvald/terrarum/ui/UIItemRedeemCodeArea.kt b/src/net/torvald/terrarum/ui/UIItemRedeemCodeArea.kt new file mode 100644 index 000000000..03d184af9 --- /dev/null +++ b/src/net/torvald/terrarum/ui/UIItemRedeemCodeArea.kt @@ -0,0 +1,133 @@ +package net.torvald.terrarum.ui + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.OrthographicCamera +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import net.torvald.terrarum.CommonResourcePool +import net.torvald.terrarum.imagefont.BigAlphNum +import net.torvald.terrarum.utils.PasswordBase32 +import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack + +/** + * Created by minjaesong on 2025-01-15. + */ +class UIItemRedeemCodeArea( + parentUI: UICanvas, + initialX: Int, + initialY: Int, + val textCols: Int, + val textRows: Int, + +) : UIItem(parentUI, initialX, initialY) { + + private val CELL_W = 16 + private val CELL_H = 22 + + override val width = textCols * CELL_W + override val height = textRows * CELL_H + + init { + CommonResourcePool.addToLoadingList("spritesheet:terrarum_redeem_code_form") { + TextureRegionPack(Gdx.files.internal("assets/graphics/code_input_cells.tga"), CELL_W, CELL_H) + } + CommonResourcePool.loadAll() + } + + private var inputFormTiles = CommonResourcePool.getAsTextureRegionPack("spritesheet:terrarum_redeem_code_form") + + + private val inputText = StringBuilder(textCols * textRows) + private var textCaret = 0 + fun clearInput() { inputText.clear(); textCaret = 0 } + fun acceptChar(char: Char): Boolean { + if (textCaret in 0 until textCols * textRows) { + inputText.insert(textCaret, char) + textCaret++ + return true + } + else return false + } + fun backspace(): Boolean { + if (textCaret in 1 until textCols * textRows) { + inputText.deleteCharAt(textCaret - 1) + textCaret-- + return true + } + else return false + } + fun reverseBackspace(): Boolean { + if (textCaret in 0 until (textCols * textRows - 1)) { + inputText.deleteCharAt(textCaret) + return true + } + else return false + } + fun __moveCursorBackward(delta: Int = 1) { + textCaret = (textCaret - 1).coerceIn(0, textCols * textRows) + } + fun __moveCursorForward(delta: Int = 1) { + textCaret = (textCaret + 1).coerceIn(0, textCols * textRows) + } + + fun getInputAsString() = inputText.toString() + fun getInputAsBinary(codebook: RedeemCodebook) = codebook.toBinary(inputText.toString()) + + private val caretCol = Toolkit.Theme.COL_SELECTED + + override fun render(frameDelta: Float, batch: SpriteBatch, camera: OrthographicCamera) { + super.render(frameDelta, batch, camera) + + val lineCol = if (isActive) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_INACTIVE + + // draw border + batch.color = lineCol + Toolkit.drawBoxBorder(batch, posX, posY, width, height) + + // draw cells + for (y in 0 until textRows) { + for (x in 0 until textCols) { + batch.draw(inputFormTiles.get(if (x == 0) 0 else if (x == textCols - 1) 2 else 1, 0), + posX.toFloat() + CELL_W * x, posY.toFloat() + CELL_H * y + ) + } + } + + // draw texts + batch.color = Color.WHITE + for (y in 0 until textRows) { + for (x in 0 until textCols) { + BigAlphNum.draw( + batch, + "${inputText[y * textRows + x]}", + posX + CELL_W * x + 2f, + posY + CELL_H * y + 3f + ) + } + } + + // draw caret + batch.color = caretCol + val cx = textCaret % textCols + val cy = textCaret / textCols + Toolkit.drawStraightLine(batch, + posX + CELL_W * cx - 1, + posY + CELL_H * cy + 1, + posY + CELL_H * cy + 1 + 20, 2, true) + } + + override fun dispose() { + } +} + +interface RedeemCodebook { + fun toBinary(inputString: String): ByteArray + + companion object { + object Base32RedeemCodebook : RedeemCodebook { + override fun toBinary(inputString: String): ByteArray { + return PasswordBase32.decode(inputString, inputString.length) + } + } + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/utils/PasswordBase32.kt b/src/net/torvald/terrarum/utils/PasswordBase32.kt index 23fd4620c..9d7b36f92 100644 --- a/src/net/torvald/terrarum/utils/PasswordBase32.kt +++ b/src/net/torvald/terrarum/utils/PasswordBase32.kt @@ -12,7 +12,7 @@ import kotlin.experimental.xor object PasswordBase32 { private val si = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=" - private val so = "YBNDRFG8EJKMCPQXOTLVUIS2A345H769 " + private val so = "YBNDRFGWEJKMCPQXOTLVUIS2A345H769 " private val standardToModified = HashMap(32) private val modifiedToStandard = HashMap(32) @@ -26,6 +26,7 @@ object PasswordBase32 { modifiedToStandard['0'] = modifiedToStandard['O']!! modifiedToStandard['1'] = modifiedToStandard['I']!! modifiedToStandard['Z'] = modifiedToStandard['2']!! + modifiedToStandard['8'] = modifiedToStandard['B']!! } private val nullPw = byteArrayOf(0.toByte())