Compare commits

...

70 Commits

Author SHA1 Message Date
minjaesong
b1e45f1743 character import wip 2023-08-25 00:24:12 +09:00
minjaesong
8dda7ac79b linear filter for clouds 2023-08-24 20:27:10 +09:00
minjaesong
74b8cc20b7 fix: bad cloud spawn position on certain directions 2023-08-24 17:59:55 +09:00
minjaesong
f75a7dd812 control presets 2023-08-24 17:24:39 +09:00
minjaesong
b2454e4ca2 changing ui toggle keys in-world should work now 2023-08-24 16:08:18 +09:00
minjaesong
45af955488 wide cloud for overcast weather 2023-08-24 15:08:30 +09:00
minjaesong
1f39b9d448 cloud texture touchups 2023-08-23 22:38:01 +09:00
minjaesong
26a4cdbce1 clouds will despawn of old age 2023-08-23 21:22:46 +09:00
minjaesong
bf87dc04cb randomised weather but i'm just faking it rn 2023-08-23 18:12:32 +09:00
minjaesong
8535b0ce13 forgot to enable the initial clouds spawning 2023-08-23 13:01:20 +09:00
minjaesong
6e0004f165 clouds can spawn and drift in any direction 2023-08-23 12:49:33 +09:00
minjaesong
845333f33d timeflow on the titlescreen need not be that complex 2023-08-23 10:10:01 +09:00
minjaesong
6988feb731 day-night cycle on the titlescreen 2023-08-22 23:39:09 +09:00
minjaesong
ac2c7b1148 clouds on titlescreen 2023-08-22 23:17:49 +09:00
minjaesong
d6145fd0da even more clouds 2023-08-22 22:27:39 +09:00
minjaesong
194089827c more cloud texture 2023-08-22 21:27:33 +09:00
minjaesong
d69d032f74 more cloud texture 2023-08-22 20:27:25 +09:00
minjaesong
a9dbea3d16 more clouds 2023-08-22 20:06:22 +09:00
minjaesong
52938a4b60 improved cloud draw perf 2023-08-22 17:43:53 +09:00
minjaesong
a21f986f30 cloud spawn z-pos probability change 2023-08-22 16:33:39 +09:00
minjaesong
547158a313 more depth to the distant clouds 2023-08-22 16:20:35 +09:00
minjaesong
0a8b5f33f4 clouds parallax and can drift in 3D 2023-08-22 09:50:03 +09:00
minjaesong
da8d620766 clouds with just right depths 2023-08-22 02:55:31 +09:00
minjaesong
7dd520393c more clouds 2023-08-22 00:06:03 +09:00
minjaesong
dc83e12170 more clouds 2023-08-21 21:39:11 +09:00
minjaesong
d6b2940d8f more clouds 2023-08-21 01:26:16 +09:00
minjaesong
c5dfe46b76 cloud wip 2023-08-20 19:10:43 +09:00
minjaesong
3d3926c08b windows build 2023-08-18 20:30:57 +09:00
minjaesong
9a90bf69d4 trying to use the proper method of running exe on exe 2023-08-18 10:57:40 +09:00
minjaesong
0ed5472d8a adjusting pos for 'save will be deleted' msg 2023-08-18 00:05:03 +09:00
minjaesong
97a7a36030 version tag on app configuration 2023-08-17 15:47:31 +09:00
minjaesong
662069466a skybox size adjustment 2023-08-17 02:04:05 +09:00
minjaesong
52cff00338 commit 2023-08-16 22:39:31 +09:00
minjaesong
1a40334f8e auto zip for .app packaging 2023-08-16 22:26:27 +09:00
minjaesong
763f512419 script for assets_release 2023-08-16 22:17:09 +09:00
minjaesong
620a1c6956 GL_UNSIGNED_SHORT works just fine on macOS?! 2023-08-16 21:56:04 +09:00
minjaesong
5f4fcdba69 apple m chip workaround (2) 2023-08-16 21:46:54 +09:00
minjaesong
7a79f444b2 apple m chip workaround 2023-08-16 21:38:23 +09:00
minjaesong
e4b947ce69 Float16FrameBuffer 2023-08-16 17:51:31 +09:00
minjaesong
fdfec960ca reverse gravity adaptation of the last commit 2023-08-16 16:33:53 +09:00
minjaesong
75021ecfa2 fixing issue #48 2023-08-16 16:03:54 +09:00
minjaesong
c90ef21bfa calendar UI mouseover 2023-08-15 19:02:25 +09:00
minjaesong
3fce5d7e95 calendar UI 2023-08-15 14:58:50 +09:00
minjaesong
8db1228e70 calendar wip 2023-08-15 04:54:43 +09:00
minjaesong
5f7f724058 tileatlas: atlas size will automatically expand if it's too small 2023-08-14 18:47:39 +09:00
minjaesong
fab4179068 fixing issue #47 using new tag on blocks.csv 2023-08-14 18:16:05 +09:00
minjaesong
32803b6f18 incomplete fix for horizontal bouncing on T-shaped platform arrangement 2023-08-14 04:10:10 +09:00
minjaesong
f8f75fb7b6 options to change atlas texture size 2023-08-14 03:53:25 +09:00
minjaesong
9919a99032 fix: phys ccd would only trigger on its last iteration due to a stupid oversight 2023-08-13 16:54:46 +09:00
minjaesong
6a43d1a5bd calendar sprite removing a spring 2023-08-13 15:36:24 +09:00
minjaesong
24c971e4b8 oh wait thats not it 2023-08-11 21:08:35 +09:00
minjaesong
62f0fd7c68 hiding the phys artefact by forcefully holding down-key for long enough 2023-08-11 21:05:47 +09:00
minjaesong
3dec312989 phys glitch kinda fixed but i'm no longer sure about that 2023-08-11 17:03:51 +09:00
minjaesong
77b51a45dd no highp on blur shader 2023-08-11 13:03:45 +09:00
minjaesong
d1b4ce3404 something's fixed but have no idea what 2023-08-11 12:35:36 +09:00
minjaesong
fd7b88307c . 2023-08-11 09:29:30 +09:00
minjaesong
579b6b5b29 somehow fixed but now jumping while walking against wall changes jump behav 2023-08-11 01:04:34 +09:00
minjaesong
cef58f6a73 phys debugging; see L818@ActorWithBody.kt 2023-08-10 23:49:43 +09:00
minjaesong
c0c98c3b80 some locale changes; remoCon changes on load menu 2023-08-08 17:44:23 +09:00
minjaesong
88d844cc09 actor draw planting offset 2023-08-08 17:14:13 +09:00
minjaesong
2411db17a7 calendar fixture wip 2023-08-08 16:41:40 +09:00
minjaesong
53d372be38 there's no way #33 is fixed so easily but im committing it anyway 2023-08-08 12:09:22 +09:00
minjaesong
88831051c8 revised ingame date format 2023-08-08 09:01:34 +09:00
minjaesong
87d92ecb74 some random ideas for future self 2023-08-08 01:39:33 +09:00
minjaesong
6672dffdbc new passwd for new version 2023-08-07 14:45:07 +09:00
minjaesong
cd00ab4c7f fix: hq2x results graphical issue on some systems 2023-08-07 14:30:36 +09:00
minjaesong
014306c209 2k skybox tex; trilinear blending of atmos vars 2023-08-07 13:59:45 +09:00
minjaesong
30fb57eca3 skybox: two different setup for AM/PN 2023-08-06 18:37:56 +09:00
minjaesong
52ad8f0c46 improved skybox model 2023-08-05 17:20:35 +09:00
minjaesong
1b08039018 updating numbers for v0.3.3 2023-08-05 00:45:35 +09:00
119 changed files with 2988 additions and 1001 deletions

View File

@@ -1,26 +1,17 @@
package net.torvald.terrarum.modulecomputers.gameactors package net.torvald.terrarum.modulecomputers.gameactors
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import kotlin.coroutines.*
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameactors.BlockBox import net.torvald.terrarum.modulebasegame.gameactors.BlockBox
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.modulecomputers.ui.UIHomeComputer
import net.torvald.terrarum.ui.UICanvas
import net.torvald.tsvm.* import net.torvald.tsvm.*
import net.torvald.tsvm.peripheral.AdapterConfig import net.torvald.tsvm.peripheral.AdapterConfig
import net.torvald.tsvm.peripheral.GraphicsAdapter import net.torvald.tsvm.peripheral.GraphicsAdapter
import net.torvald.tsvm.peripheral.VMProgramRom import net.torvald.tsvm.peripheral.VMProgramRom
import net.torvald.unicode.*
/** /**
* Created by minjaesong on 2021-12-04. * Created by minjaesong on 2021-12-04.
@@ -131,77 +122,3 @@ class FixtureHomeComputer : FixtureBase {
} }
} }
internal class UIHomeComputer : UICanvas(
toggleKeyLiteral = Input.Keys.ESCAPE, // FIXME why do I have specify ESC for it to function? ESC should be work as the default key
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"),
) {
override var width = 640
override var height = 480
override var openCloseTime = 0f
private val drawOffX = (width - 560).div(2).toFloat()
private val drawOffY = (height - 448).div(2).toFloat()
private var batch: FlippingSpriteBatch
private var camera: OrthographicCamera
internal lateinit var vm: VM
internal lateinit var fixture: FixtureHomeComputer
init {
batch = FlippingSpriteBatch()
camera = OrthographicCamera(width.toFloat(), height.toFloat())
//val m = Matrix4()
//m.setToOrtho2D(0f, 0f, width.toFloat(), height.toFloat())
batch.projectionMatrix = camera.combined
}
private val fbo = FrameBuffer(Pixmap.Format.RGBA8888, width, height, false)
private val controlHelp =
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_T$KEYCAP_R Terminate\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_S Reset\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_Q SysRq"
override fun updateUI(delta: Float) {
}
override fun renderUI(otherBatch: SpriteBatch, otherCamera: Camera) {
otherBatch.end()
fbo.inAction(camera, batch) {
Gdx.gl.glClearColor(0f,0f,0f,1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) // to hide the crap might be there
(vm.peripheralTable[1].peripheral as? GraphicsAdapter)?.let { gpu ->
val clearCol = gpu.getBackgroundColour()
Gdx.gl.glClearColor(clearCol.r, clearCol.g, clearCol.b, clearCol.a)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
gpu.render(Gdx.graphics.deltaTime, batch, drawOffX, drawOffY, true, fbo) // gpu.render will internally end() the fbo then begin() again before using the batch I've fed in
}
}
otherBatch.begin()
otherBatch.shader = null
blendNormalStraightAlpha(otherBatch)
otherBatch.color = Color.WHITE
otherBatch.draw(fbo.colorBufferTexture, posX.toFloat(), posY.toFloat(), width.toFloat(), height.toFloat())
otherBatch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(otherBatch, posX - 1, posY - 1, width + 2, height + 2)
App.fontGame.draw(otherBatch, controlHelp, posX, posY + height + 4)
}
override fun doOpening(delta: Float) {
super.doOpening(delta)
fixture.startVM()
}
override fun dispose() {
fbo.dispose()
}
}

View File

@@ -0,0 +1,90 @@
package net.torvald.terrarum.modulecomputers.ui
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import net.torvald.terrarum.*
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulecomputers.gameactors.FixtureHomeComputer
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
import net.torvald.tsvm.VM
import net.torvald.tsvm.peripheral.GraphicsAdapter
import net.torvald.unicode.*
internal class UIHomeComputer : UICanvas(
toggleKeyLiteral = null,
toggleButtonLiteral = "control_gamepad_start",
) {
override var width = 640
override var height = 480
override var openCloseTime = 0f
private val drawOffX = (width - 560).div(2).toFloat()
private val drawOffY = (height - 448).div(2).toFloat()
private var batch: FlippingSpriteBatch
private var camera: OrthographicCamera
internal lateinit var vm: VM
internal lateinit var fixture: FixtureHomeComputer
init {
batch = FlippingSpriteBatch()
camera = OrthographicCamera(width.toFloat(), height.toFloat())
//val m = Matrix4()
//m.setToOrtho2D(0f, 0f, width.toFloat(), height.toFloat())
batch.projectionMatrix = camera.combined
}
private val fbo = FrameBuffer(Pixmap.Format.RGBA8888, width, height, false)
private val controlHelp =
"${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_T$KEYCAP_R Terminate\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_S Reset\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_Q SysRq"
override fun updateUI(delta: Float) {
}
override fun renderUI(otherBatch: SpriteBatch, otherCamera: Camera) {
otherBatch.end()
fbo.inAction(camera, batch) {
Gdx.gl.glClearColor(0f,0f,0f,1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) // to hide the crap might be there
(vm.peripheralTable[1].peripheral as? GraphicsAdapter)?.let { gpu ->
val clearCol = gpu.getBackgroundColour()
Gdx.gl.glClearColor(clearCol.r, clearCol.g, clearCol.b, clearCol.a)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
gpu.render(Gdx.graphics.deltaTime, batch, drawOffX, drawOffY, true, fbo) // gpu.render will internally end() the fbo then begin() again before using the batch I've fed in
}
}
otherBatch.begin()
otherBatch.shader = null
blendNormalStraightAlpha(otherBatch)
otherBatch.color = Color.WHITE
otherBatch.draw(fbo.colorBufferTexture, posX.toFloat(), posY.toFloat(), width.toFloat(), height.toFloat())
otherBatch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(otherBatch, posX - 1, posY - 1, width + 2, height + 2)
App.fontGame.draw(otherBatch, controlHelp, posX, posY + height + 4)
}
override fun doOpening(delta: Float) {
super.doOpening(delta)
fixture.startVM()
}
override fun dispose() {
fbo.dispose()
}
}

Binary file not shown.

View File

@@ -8,6 +8,7 @@
"GAME_ACTION_MOVE_VERB" : "Move", "GAME_ACTION_MOVE_VERB" : "Move",
"GAME_ACTION_ZOOM" : "Zoom", "GAME_ACTION_ZOOM" : "Zoom",
"MENU_IO_AUTOSAVE": "Autosave", "MENU_IO_AUTOSAVE": "Autosave",
"MENU_IO_CLEAR": "Clear",
"MENU_IO_IMPORT": "Import", "MENU_IO_IMPORT": "Import",
"MENU_IO_MANUAL_SAVE": "Manual Save", "MENU_IO_MANUAL_SAVE": "Manual Save",
"MENU_LABEL_COPYRIGHT": "Copyright", "MENU_LABEL_COPYRIGHT": "Copyright",
@@ -16,6 +17,7 @@
"MENU_LABEL_IME": "IME", "MENU_LABEL_IME": "IME",
"MENU_LABEL_IME_TOGGLE": "Toggle IME", "MENU_LABEL_IME_TOGGLE": "Toggle IME",
"MENU_LABEL_KEYBOARD_LAYOUT": "Keyboard Layout", "MENU_LABEL_KEYBOARD_LAYOUT": "Keyboard Layout",
"MENU_LABEL_PASTE": "Paste",
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "Paste from Clipboard", "MENU_LABEL_PASTE_FROM_CLIPBOARD": "Paste from Clipboard",
"MENU_LABEL_PRESS_START_SYMBOL": "Press >", "MENU_LABEL_PRESS_START_SYMBOL": "Press >",
"MENU_LABEL_RESET" : "Reset", "MENU_LABEL_RESET" : "Reset",
@@ -23,6 +25,7 @@
"MENU_LABEL_STREAMING": "Livestreaming", "MENU_LABEL_STREAMING": "Livestreaming",
"MENU_LABEL_SYSTEM_INFO": "System Info", "MENU_LABEL_SYSTEM_INFO": "System Info",
"MENU_MODULES" : "Modules", "MENU_MODULES" : "Modules",
"MENU_OPTIONS_ATLAS_TEXTURE_SIZE": "Atlas Texture Size",
"MENU_OPTIONS_AUTOSAVE": "Autosave", "MENU_OPTIONS_AUTOSAVE": "Autosave",
"MENU_OPTIONS_BLUR": "Blur", "MENU_OPTIONS_BLUR": "Blur",
"MENU_OPTIONS_DEBUG_CONSOLE": "Debug Console", "MENU_OPTIONS_DEBUG_CONSOLE": "Debug Console",

View File

@@ -8,6 +8,7 @@
"GAME_ACTION_MOVE_VERB" : "이동하기", "GAME_ACTION_MOVE_VERB" : "이동하기",
"GAME_ACTION_ZOOM" : "확대·축소", "GAME_ACTION_ZOOM" : "확대·축소",
"MENU_IO_AUTOSAVE": "자동 저장", "MENU_IO_AUTOSAVE": "자동 저장",
"MENU_IO_CLEAR": "지우기",
"MENU_IO_IMPORT": "가져오기", "MENU_IO_IMPORT": "가져오기",
"MENU_IO_MANUAL_SAVE": "수동 저장", "MENU_IO_MANUAL_SAVE": "수동 저장",
"MENU_LABEL_COPYRIGHT": "저작권", "MENU_LABEL_COPYRIGHT": "저작권",
@@ -16,6 +17,7 @@
"MENU_LABEL_IME": "입력기", "MENU_LABEL_IME": "입력기",
"MENU_LABEL_IME_TOGGLE": "입력기 켜고 끄기", "MENU_LABEL_IME_TOGGLE": "입력기 켜고 끄기",
"MENU_LABEL_KEYBOARD_LAYOUT": "자판 배열", "MENU_LABEL_KEYBOARD_LAYOUT": "자판 배열",
"MENU_LABEL_PASTE": "붙여넣기",
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "복사한 텍스트 붙여넣기", "MENU_LABEL_PASTE_FROM_CLIPBOARD": "복사한 텍스트 붙여넣기",
"MENU_LABEL_PRESS_START_SYMBOL": ">을 누르세요", "MENU_LABEL_PRESS_START_SYMBOL": ">을 누르세요",
"MENU_LABEL_RESET" : "재설정", "MENU_LABEL_RESET" : "재설정",
@@ -23,6 +25,7 @@
"MENU_LABEL_STREAMING": "실시간 방송", "MENU_LABEL_STREAMING": "실시간 방송",
"MENU_LABEL_SYSTEM_INFO": "시스템 정보", "MENU_LABEL_SYSTEM_INFO": "시스템 정보",
"MENU_MODULES" : "모듈", "MENU_MODULES" : "모듈",
"MENU_OPTIONS_ATLAS_TEXTURE_SIZE": "아틀라스 텍스처 크기",
"MENU_OPTIONS_AUTOSAVE": "자동 저장", "MENU_OPTIONS_AUTOSAVE": "자동 저장",
"MENU_OPTIONS_BLUR": "흐림", "MENU_OPTIONS_BLUR": "흐림",
"MENU_OPTIONS_DEBUG_CONSOLE": "디버그 콘솔", "MENU_OPTIONS_DEBUG_CONSOLE": "디버그 콘솔",

View File

@@ -1,5 +1,5 @@
"id";"drop";"spawn";"name";"shdr";"shdg";"shdb";"shduv";"str";"dsty";"mate";"solid";"wall";"grav";"dlfn";"fv";"fr";"lumr";"lumg";"lumb";"lumuv";"colour";"vscs";"refl";"tags" "id";"drop";"spawn";"name";"shdr";"shdg";"shdb";"shduv";"str";"dsty";"mate";"solid";"wall";"grav";"dlfn";"fv";"fr";"lumr";"lumg";"lumb";"lumuv";"colour";"vscs";"refl";"tags"
"0";"0";"0";"BLOCK_AIR";"0.0312";"0.0312";"0.0312";"0.0312";"1";"1";"NULL";"0";"1";"N/A";"0";"0";"4";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"" "0";"0";"0";"BLOCK_AIR";"0.0312";"0.0312";"0.0312";"0.0312";"1";"1";"NULL";"0";"1";"N/A";"0";"0";"4";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"INCONSEQUENTIAL"
"16";"17";"17";"BLOCK_STONE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL" "16";"17";"17";"BLOCK_STONE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL"
"17";"17";"17";"BLOCK_STONE_QUARRIED";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK" "17";"17";"17";"BLOCK_STONE_QUARRIED";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK"
"18";"18";"18";"BLOCK_STONE_TILE_WHITE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.18";"STONE" "18";"18";"18";"BLOCK_STONE_TILE_WHITE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.18";"STONE"
@@ -188,6 +188,7 @@
## Some tags are reserved for internal use, which are: ## Some tags are reserved for internal use, which are:
## - INTERNAL: denotes that the tile is internal-use. Will not be rendered unless debug window is on. ## - INTERNAL: denotes that the tile is internal-use. Will not be rendered unless debug window is on.
## - DORENDER: this internal tile must go through the standard-issue tile drawing routine. ## - DORENDER: this internal tile must go through the standard-issue tile drawing routine.
## - INCONSEQUENTIAL: denotes that this tile can be overwritten without dropping it. Usually used with flower tiles.
# #
# #
## References ## ## References ##
Can't render this file because it contains an unexpected character in line 179 and column 37.

View File

@@ -32,4 +32,5 @@ Teleport
ToggleNoClip ToggleNoClip
Zoom Zoom
DynToStatic DynToStatic
DebugFillInventory DebugFillInventory
Uuid
1 CatStdout
32 ToggleNoClip
33 Zoom
34 DynToStatic
35 DebugFillInventory
36 Uuid

View File

@@ -8,6 +8,7 @@ id;classname
8;net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalEmitter 8;net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalEmitter
9;net.torvald.terrarum.modulebasegame.gameitems.WireCutterAll 9;net.torvald.terrarum.modulebasegame.gameitems.WireCutterAll
10;net.torvald.terrarum.modulebasegame.gameitems.ItemTypewriter 10;net.torvald.terrarum.modulebasegame.gameitems.ItemTypewriter
11;net.torvald.terrarum.modulebasegame.gameitems.ItemWallCalendar
256;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak 256;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak
257;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony 257;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony
1 id classname
8 8 net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalEmitter
9 9 net.torvald.terrarum.modulebasegame.gameitems.WireCutterAll
10 10 net.torvald.terrarum.modulebasegame.gameitems.ItemTypewriter
11 11 net.torvald.terrarum.modulebasegame.gameitems.ItemWallCalendar
12 256 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak
13 257 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony
14 258 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorBirch

View File

@@ -0,0 +1,34 @@
{
"MENU_CALENDAR_CALENDAR": "Calendar",
"MENU_CALENDAR_EVENTS": "Events",
"MENU_CALENDAR_ADD_NEW_EVENT": "Add New Event",
"CONTEXT_CALENDAR_SEASON_SPRING": "Spring",
"CONTEXT_CALENDAR_SEASON_SUMMER": "Summer",
"CONTEXT_CALENDAR_SEASON_AUTUMN": "Autumn",
"CONTEXT_CALENDAR_SEASON_WINTER": "Winter",
"CONTEXT_CALENDAR_SEASON_SPRI": "Spri",
"CONTEXT_CALENDAR_SEASON_SUMM": "Summ",
"CONTEXT_CALENDAR_SEASON_AUTM": "Autm",
"CONTEXT_CALENDAR_SEASON_WINT": "Wint",
"CONTEXT_CALENDAR_DATE_FORMAT_Y": "Year {0}",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD": "Year {0} {1} {2}",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD_DDD": "Year {0} {1} {2} {3}",
"CONTEXT_CALENDAR_DAY_MONDAG_DNT": "Mondag",
"CONTEXT_CALENDAR_DAY_TYSDAG_DNT": "Tysdag",
"CONTEXT_CALENDAR_DAY_MIDTVEKE_DNT": "Midtveke",
"CONTEXT_CALENDAR_DAY_TORSDAG_DNT": "Torsdag",
"CONTEXT_CALENDAR_DAY_FREDAG_DNT": "Fredag",
"CONTEXT_CALENDAR_DAY_LAURDAG_DNT": "Laurdag",
"CONTEXT_CALENDAR_DAY_SUNDAG_DNT": "Sundag",
"CONTEXT_CALENDAR_DAY_VERDDAG_DNT": "Verddag",
"CONTEXT_CALENDAR_DAY_MON_DNT": "Mon",
"CONTEXT_CALENDAR_DAY_TYS_DNT": "Tys",
"CONTEXT_CALENDAR_DAY_MID_DNT": "Mid",
"CONTEXT_CALENDAR_DAY_TOR_DNT": "Tor",
"CONTEXT_CALENDAR_DAY_FRE_DNT": "Fre",
"CONTEXT_CALENDAR_DAY_LAU_DNT": "Lau",
"CONTEXT_CALENDAR_DAY_SUN_DNT": "Sun",
"CONTEXT_CALENDAR_DAY_VER_DNT": "Ver",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD_SHORT_DNT": "ɣ{0}-{1}-{2}"
}

View File

@@ -1,23 +1,24 @@
{ {
"CONTEXT_GENERATOR_SEED": "Seed",
"CONTEXT_ITEM_MAP": "Map",
"CONTEXT_ITEM_TOOL_PLURAL": "Tools",
"CONTEXT_PLACE_COORDINATE": "Coordinate",
"CONTEXT_WORLD_COUNT": "Worlds: ",
"CONTEXT_WORLD_NEW": "New World", "CONTEXT_WORLD_NEW": "New World",
"MENU_LABEL_DELETE_WORLD": "Delete World", "MENU_LABEL_DELETE_WORLD": "Delete World",
"CONTEXT_WORLD_COUNT": "Worlds: ",
"GAME_INVENTORY_INGREDIENTS": "Ingredients",
"GAME_INVENTORY_POTIONS": "Potions",
"GAME_INVENTORY_BLOCKS": "Blocks",
"GAME_INVENTORY_WALLS": "Walls",
"CONTEXT_ITEM_TOOL_PLURAL": "Tools",
"GAME_INVENTORY_FAVORITES": "Favorites",
"GAME_INVENTORY_REGISTER": "Register",
"CONTEXT_ITEM_MAP": "Map",
"MENU_LABEL_MENU": "Menu", "MENU_LABEL_MENU": "Menu",
"CONTEXT_GENERATOR_SEED": "Seed", "MENU_LABEL_PREV_SAVES": "Previous Saves",
"MENU_LABEL_RENAME": "Rename",
"GAME_ACTION_CRAFT": "Craft",
"GAME_ACTION_GRAPPLE": "Grapple", "GAME_ACTION_GRAPPLE": "Grapple",
"GAME_ACTION_QUICKSEL": "Quick Select", "GAME_ACTION_QUICKSEL": "Quick Select",
"GAME_ACTION_CRAFT": "Craft",
"GAME_CRAFTING": "Crafting",
"GAME_CRAFTABLE_ITEMS": "Craftable Items",
"MENU_LABEL_RENAME": "Rename",
"GAME_ACTION_TELEPORT": "Teleport", "GAME_ACTION_TELEPORT": "Teleport",
"CONTEXT_PLACE_COORDINATE": "Coordinate" "GAME_CRAFTABLE_ITEMS": "Craftable Items",
"GAME_CRAFTING": "Crafting",
"GAME_INVENTORY_BLOCKS": "Blocks",
"GAME_INVENTORY_FAVORITES": "Favorites",
"GAME_INVENTORY_INGREDIENTS": "Ingredients",
"GAME_INVENTORY_POTIONS": "Potions",
"GAME_INVENTORY_REGISTER": "Register",
"GAME_INVENTORY_WALLS": "Walls"
} }

View File

@@ -1,18 +1,20 @@
{ {
"BLOCK_STONE_DEEP": "Deepstone",
"BLOCK_SCAFFOLDING_NORMAL": "Scaffolding",
"BLOCK_STONE_MARBLE": "Marble",
"ITEM_CALENDAR": "Calendar",
"ITEM_LOGIC_SIGNAL_EMITTER": "Logic Signal Emitter",
"ITEM_STORAGE_CHEST": "Storage Chest",
"ITEM_TIKI_TORCH": "Tiki Torch",
"ITEM_TYPEWRITER": "Typewriter",
"ITEM_WIRE": "Wire",
"ITEM_WIRE_CUTTER": "Wire Cutter",
"ACTORBLOCK_ALLOW_MOVE_DOWN": "Urist Arôlcustith", "ACTORBLOCK_ALLOW_MOVE_DOWN": "Urist Arôlcustith",
"ACTORBLOCK_FULL_COLLISION": "Urist Berdanrifot", "ACTORBLOCK_FULL_COLLISION": "Urist Berdanrifot",
"ACTORBLOCK_NO_COLLISION": "Urist Zafal", "ACTORBLOCK_NO_COLLISION": "Urist Zafal",
"ACTORBLOCK_NO_PASS_RIGHT": "Urist McPassLeft", "ACTORBLOCK_NO_PASS_RIGHT": "Urist McPassLeft",
"ACTORBLOCK_NO_PASS_LEFT": "Urist McPassRight", "ACTORBLOCK_NO_PASS_LEFT": "Urist McPassRight",
"ACTORBLOCK_TILING_PLACEHOLDER": "Urist Berdanurdim", "ACTORBLOCK_TILING_PLACEHOLDER": "Urist Berdanurdim"
"BLOCK_STONE_DEEP": "Deepstone",
"BLOCK_SCAFFOLDING_NORMAL": "Scaffolding",
"BLOCK_STONE_MARBLE": "Marble",
"ITEM_STORAGE_CHEST": "Storage Chest",
"ITEM_WIRE": "Wire",
"ITEM_WIRE_CUTTER": "Wire Cutter",
"ITEM_LOGIC_SIGNAL_EMITTER": "Logic Signal Emitter",
"ITEM_TIKI_TORCH": "Tiki Torch"
} }

View File

@@ -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": ""
} }

View File

@@ -0,0 +1,16 @@
{
"MENU_CALENDAR_CALENDAR": "달력",
"MENU_CALENDAR_EVENTS": "일정",
"MENU_CALENDAR_ADD_NEW_EVENT": "새 일정 추가",
"CONTEXT_CALENDAR_SEASON_SPRING": "봄",
"CONTEXT_CALENDAR_SEASON_SUMMER": "여름",
"CONTEXT_CALENDAR_SEASON_AUTUMN": "가을",
"CONTEXT_CALENDAR_SEASON_WINTER": "겨울",
"CONTEXT_CALENDAR_SEASON_SPRI": "봄",
"CONTEXT_CALENDAR_SEASON_SUMM": "여름",
"CONTEXT_CALENDAR_SEASON_AUTM": "가을",
"CONTEXT_CALENDAR_SEASON_WINT": "겨울",
"CONTEXT_CALENDAR_DATE_FORMAT_Y": "{0}년",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD": "{0}년 {1} {2}일",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD_DDD": "{0}년 {1} {2}일 {3}"
}

View File

@@ -1,24 +1,25 @@
{ {
"CONTEXT_GENERATOR_SEED": "시드",
"CONTEXT_ITEM_MAP": "지도",
"CONTEXT_ITEM_TOOL_PLURAL": "도구",
"CONTEXT_PLACE_COORDINATE": "좌표",
"CONTEXT_WORLD_COUNT": "새계: ",
"CONTEXT_WORLD_NEW": "새 세계", "CONTEXT_WORLD_NEW": "새 세계",
"MENU_LABEL_DELETE_WORLD": "새계 삭제", "MENU_LABEL_DELETE_WORLD": "새계 삭제",
"CONTEXT_WORLD_COUNT": "새계: ",
"MENU_MONITOR_CALI_TITLE": "모니터 확인",
"GAME_INVENTORY_INGREDIENTS": "재료",
"GAME_INVENTORY_POTIONS": "물약",
"GAME_INVENTORY_BLOCKS": "블록",
"GAME_INVENTORY_WALLS": "벽지",
"CONTEXT_ITEM_TOOL_PLURAL": "도구",
"GAME_INVENTORY_FAVORITES": "즐겨찾기",
"GAME_INVENTORY_REGISTER": "등록하기",
"MENU_LABEL_MENU": "메뉴", "MENU_LABEL_MENU": "메뉴",
"CONTEXT_ITEM_MAP": "지도", "MENU_LABEL_PREV_SAVES": "이전 세이브",
"CONTEXT_GENERATOR_SEED": "시드", "MENU_LABEL_RENAME": "이름 바꾸기",
"MENU_MONITOR_CALI_TITLE": "모니터 확인",
"GAME_ACTION_CRAFT": "제작하기",
"GAME_ACTION_GRAPPLE": "매달리기", "GAME_ACTION_GRAPPLE": "매달리기",
"GAME_ACTION_QUICKSEL": "빠른 선택", "GAME_ACTION_QUICKSEL": "빠른 선택",
"GAME_ACTION_CRAFT": "제작하기",
"GAME_CRAFTING": "제작",
"GAME_CRAFTABLE_ITEMS": "제작 가능한 아이템",
"MENU_LABEL_RENAME": "이름 바꾸기",
"GAME_ACTION_TELEPORT": "텔레포트하기", "GAME_ACTION_TELEPORT": "텔레포트하기",
"CONTEXT_PLACE_COORDINATE": "좌표" "GAME_CRAFTABLE_ITEMS": "제작 가능한 아이템",
"GAME_CRAFTING": "제작",
"GAME_INVENTORY_BLOCKS": "블록",
"GAME_INVENTORY_FAVORITES": "즐겨찾기",
"GAME_INVENTORY_INGREDIENTS": "재료",
"GAME_INVENTORY_POTIONS": "물약",
"GAME_INVENTORY_REGISTER": "등록하기",
"GAME_INVENTORY_WALLS": "벽지"
} }

View File

@@ -3,9 +3,11 @@
"BLOCK_SCAFFOLDING_NORMAL": "발판", "BLOCK_SCAFFOLDING_NORMAL": "발판",
"BLOCK_STONE_MARBLE": "대리석", "BLOCK_STONE_MARBLE": "대리석",
"ITEM_STORAGE_CHEST": "보관상자", "ITEM_CALENDAR": "달력",
"ITEM_WIRE": "전선",
"ITEM_WIRE_CUTTER": "전선 절단기",
"ITEM_LOGIC_SIGNAL_EMITTER": "신호발생기", "ITEM_LOGIC_SIGNAL_EMITTER": "신호발생기",
"ITEM_TIKI_TORCH": "티키 토치" "ITEM_STORAGE_CHEST": "보관상자",
"ITEM_TIKI_TORCH": "티키 토치",
"ITEM_TYPEWRITER": "타자기",
"ITEM_WIRE": "전선",
"ITEM_WIRE_CUTTER": "전선 절단기"
} }

View File

@@ -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": ""
} }

Binary file not shown.

View File

@@ -2,7 +2,21 @@
"skyboxGradColourMap": "generic_skybox.tga", "skyboxGradColourMap": "generic_skybox.tga",
"daylightClut": "clut_daylight.tga", "daylightClut": "clut_daylight.tga",
"classification": "generic", "classification": "generic",
"extraImages": [ "cloudChance": 250,
"cloudGamma": [0.48, 1.8],
] "cloudGammaVariance": [0.1, 0.1],
"windSpeed": 10.25,
"windSpeedVariance": 1.0,
"clouds": {
"cumulonimbus": {
"filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.2,
"baseScale": 2.0, "scaleVariance": 0.3333333,
"altLow": 80, "altHigh": 120
},
"cumulus": {
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 1.0,
"baseScale": 1.0, "scaleVariance": 0.6,
"altLow": 80, "altHigh": 800
}
}
} }

View File

@@ -1,10 +0,0 @@
{
"globalLight": "generic_light.tga",
"skyboxGradColourMap": "generic_skybox.tga",
"classification": "genericrain",
"extraImages": [
"raindrop.tga"
],
"mixFrom": "__CURRENTWEATHER",
"mixPercentage": 80.0
}

View File

@@ -0,0 +1,27 @@
{
"skyboxGradColourMap": "generic_skybox.tga",
"daylightClut": "clut_daylight.tga",
"classification": "overcast",
"cloudChance": 300,
"cloudGamma": [2.2, 2.0],
"cloudGammaVariance": [0.1, 0.1],
"windSpeed": 10.45,
"windSpeedVariance": 1.0,
"clouds": {
"cumulus": {
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 0.1,
"baseScale": 0.8, "scaleVariance": 0.6,
"altLow": 80, "altHigh": 800
},
"cumulonimbus": {
"filename": "cloud_large.png", "tw": 2048, "th": 1024, "probability": 0.4,
"baseScale": 2.0, "scaleVariance": 0.3333333,
"altLow": 90, "altHigh": 120
},
"nimbostratus": {
"filename": "cloud_wide.png", "tw": 4096, "th": 1024, "probability": 1.0,
"baseScale": 4.0, "scaleVariance": 0.1,
"altLow": 100, "altHigh": 100
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,26 @@
## actually blending
1. On Krita, load the cloudmap
2. Select and isolate suitable piece of the cloud
3. With liquefying brush, make the bottom flat
4. On new tab, load the solidmap
5. Resize the solidmap to 1024x1024
6. Rotate the solidmap by 30 deg or so
7. Overlay the processed solidmap to the cloudmap, opacity=30%
8. With airbrush tool, manually add the shade (see cloud_normal.kra to get the gist of it)
### cloudmap
1. On GIMP, prepare 4096x4096 canvas
2. Create Simplex Noise with following parameters:
- Scale: arbitrary (0.05 - 0.2)
- Iterations: MAX
- Seed: anything but 0
### solidmap
1. On GIMP, prepare 4096x4096 canvas
2. Create Solid Noise with following parameters:
- XY Size: 16.0
- Detail: MAX
- Tileable: check
- Turbulent: check
- Seed: anything but 0

Binary file not shown.

Binary file not shown.

View File

@@ -3,7 +3,8 @@ if (( $EUID == 0 )); then echo "The build process is not meant to be run with ro
cd "${0%/*}" cd "${0%/*}"
SRCFILES="terrarummac_arm" SRCFILES="terrarummac_arm"
DESTDIR="out/TerrarumMac.arm.app" APPDIR="./TerrarumMac.arm.app"
DESTDIR="out/$APPDIR"
RUNTIME="runtime-osx-arm" RUNTIME="runtime-osx-arm"
if [ ! -d "../assets_release" ]; then if [ ! -d "../assets_release" ]; then
@@ -34,4 +35,8 @@ cp -r "../assets_release" $DESTDIR/Contents/MacOS/
mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets
cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/ cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/
cd "out"
rm $APPDIR.zip
7z a -tzip $APPDIR.zip $APPDIR
echo "Build successful: $DESTDIR" echo "Build successful: $DESTDIR"

View File

@@ -3,7 +3,8 @@ if (( $EUID == 0 )); then echo "The build process is not meant to be run with ro
cd "${0%/*}" cd "${0%/*}"
SRCFILES="terrarummac_x86" SRCFILES="terrarummac_x86"
DESTDIR="out/TerrarumMac.x86.app" APPDIR="./TerrarumMac.x86.app"
DESTDIR="out/$APPDIR"
RUNTIME="runtime-osx-x86" RUNTIME="runtime-osx-x86"
if [ ! -d "../assets_release" ]; then if [ ! -d "../assets_release" ]; then
@@ -34,4 +35,8 @@ cp -r "../assets_release" $DESTDIR/Contents/MacOS/
mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets
cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/ cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/
cd "out"
rm $APPDIR.zip
7z a -tzip $APPDIR.zip $APPDIR
echo "Build successful: $DESTDIR" echo "Build successful: $DESTDIR"

View File

@@ -35,6 +35,7 @@ mv $DESTDIR/assets_release $DESTDIR/assets
cp "../out/TerrarumBuild.jar" $DESTDIR/out/ cp "../out/TerrarumBuild.jar" $DESTDIR/out/
# Temporary solution: zip everything # Temporary solution: zip everything
rm "out/TerrarumWindows.x86.zip"
zip -r -9 -l "out/TerrarumWindows.x86.zip" $DESTDIR zip -r -9 -l "out/TerrarumWindows.x86.zip" $DESTDIR
rm -rf $DESTDIR || true rm -rf $DESTDIR || true
echo "Build successful: $DESTDIR" echo "Build successful: $DESTDIR"

28
buildapp/make_assets_release.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
if (( $EUID == 0 )); then echo "The build process is not meant to be run with root privilege, exiting now." >&2; exit 1; fi
cd "${0%/*}"
DESTDIR="../assets_release"
rm -r $DESTDIR
cp -r "../assets" $DESTDIR
rm $DESTDIR/loopey.wav
rm $DESTDIR/ktGrepExample.kts
rm $DESTDIR/batchtest.txt
rm $DESTDIR/test_texture.tga
rm $DESTDIR/worldbacktest.tga
rm -r $DESTDIR/books
rm $DESTDIR/clut/skybox.tga
rm $DESTDIR/graphics/*.bat
rm $DESTDIR/keylayout/*.not_ime
rm $DESTDIR/mods/basegame/blocks/*.gz
rm $DESTDIR/mods/basegame/blocks/*.txt
rm $DESTDIR/mods/basegame/weathers/*.txt
rm $DESTDIR/mods/basegame/weathers/*.md
rm $DESTDIR/mods/basegame/weathers/*.kra
rm -r $DESTDIR/mods/basegame/sounds
rm -r $DESTDIR/mods/dwarventech

View File

@@ -1,6 +1,11 @@
#include <windows.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <tchar.h>
int main() { int main() {
return system(".\\out\\runtime-windows-x86\\bin\\Terrarum.exe -jar .\\out\\TerrarumBuild.jar");
ShellExecute(NULL, "open", "\".\\out\\runtime-windows-x86\\bin\\Terrarum.exe\"", "-jar \".\\out\\TerrarumBuild.jar\"", NULL, SW_HIDE);
return 0;
//return system(".\\out\\runtime-windows-x86\\bin\\Terrarum.exe -jar .\\out\\TerrarumBuild.jar");
} }

View File

@@ -0,0 +1,72 @@
package com.badlogic.gdx.graphics.glutils;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.utils.GdxRuntimeException;
import net.torvald.terrarum.App;
// typealias Float16FrameBuffer = FloatFrameBuffer
/**
* Created by minjaesong on 2023-08-16.
*/
public class Float16FrameBuffer extends FrameBuffer {
Float16FrameBuffer () {
}
/** Creates a GLFrameBuffer from the specifications provided by bufferBuilder
*
* @param bufferBuilder **/
protected Float16FrameBuffer (GLFrameBufferBuilder<? extends GLFrameBuffer<Texture>> bufferBuilder) {
super(bufferBuilder);
}
/** Creates a new FrameBuffer with a float backing texture, having the given dimensions and potentially a depth buffer
* attached.
*
* @param width the width of the framebuffer in pixels
* @param height the height of the framebuffer in pixels
* @param hasDepth whether to attach a depth buffer
* @throws GdxRuntimeException in case the FrameBuffer could not be created */
public Float16FrameBuffer (int width, int height, boolean hasDepth) {
if (App.isAppleM) { // disable float framebuffer for Apple M chips
FrameBufferBuilder bufferBuilder = new FrameBufferBuilder(width, height);
bufferBuilder.addColorTextureAttachment(GL20.GL_RGBA, GL20.GL_RGBA, GL20.GL_UNSIGNED_SHORT); // but 16bpp int works perfectly?!
if (hasDepth) bufferBuilder.addBasicDepthRenderBuffer();
this.bufferBuilder = bufferBuilder;
}
else {
FloatFrameBufferBuilder bufferBuilder = new FloatFrameBufferBuilder(width, height);
bufferBuilder.addFloatAttachment(GL30.GL_RGBA16F, GL30.GL_RGBA, GL30.GL_FLOAT, false); // FIXME sporadic black screen on GL_RGBA16F? or maybe it was Plasma bugging out?
if (hasDepth) bufferBuilder.addBasicDepthRenderBuffer();
this.bufferBuilder = bufferBuilder;
}
build();
}
@Override
protected Texture createTexture (FrameBufferTextureAttachmentSpec attachmentSpec) {
if (App.isAppleM) {
GLOnlyTextureData data = new GLOnlyTextureData(bufferBuilder.width, bufferBuilder.height, 0, attachmentSpec.internalFormat,
attachmentSpec.format, attachmentSpec.type);
Texture result = new Texture(data);
result.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
result.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
return result;
}
else {
FloatTextureData data = new FloatTextureData(bufferBuilder.width, bufferBuilder.height, attachmentSpec.internalFormat,
attachmentSpec.format, attachmentSpec.type, attachmentSpec.isGpuOnly);
Texture result = new Texture(data);
result.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
result.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
return result;
}
}
}

View File

@@ -248,6 +248,10 @@ final public class FastMath {
return interpolateCatmullRom(u, 0.5f, p0, p1, p2, p3); return interpolateCatmullRom(u, 0.5f, p0, p1, p2, p3);
} }
public static float interpolateCatmullRom(float u, float[] ps) {
return interpolateCatmullRom(u, 0.5f, ps[0], ps[1], ps[2], ps[3]);
}
/**Interpolate a spline between at least 4 control points following the Catmull-Rom equation. /**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
* here is the interpolation matrix * here is the interpolation matrix
* m = [ 0.0 1.0 0.0 0.0 ] * m = [ 0.0 1.0 0.0 0.0 ]
@@ -331,8 +335,7 @@ final public class FastMath {
return l; return l;
} }
/*public static float interpolateHermite(float scale, float p0, float p1, float p2, float p3) {
public static float interpolateHermite(float scale, float p0, float p1, float p2, float p3) {
// return interpolateHermite(scale, p0, p1, p2, p3, 0f, 0f); // return interpolateHermite(scale, p0, p1, p2, p3, 0f, 0f);
float mu2 = scale * scale; float mu2 = scale * scale;
float mu3 = mu2 * scale; float mu3 = mu2 * scale;
@@ -350,7 +353,7 @@ final public class FastMath {
float a3 = -2*mu3 + 3*mu2 + 0; float a3 = -2*mu3 + 3*mu2 + 0;
return a0*p1 + a1*m0 + a2*m1 + a3*p2; return a0*p1 + a1*m0 + a2*m1 + a3*p2;
} }*/
//public static float interpolateHermite(float scale, float p0, float p1, float p2, float p3, float tension, float bias) {} //public static float interpolateHermite(float scale, float p0, float p1, float p2, float p3, float tension, float bias) {}

View File

@@ -192,6 +192,16 @@ fun CIEXYZ.toColorRaw(): Color {
return Color(rgb.r, rgb.g, rgb.b, rgb.alpha) return Color(rgb.r, rgb.g, rgb.b, rgb.alpha)
} }
fun CIEXYZ.toYXY(): CIEYXY {
val dot = this.X + this.Y + this.Z
return CIEYXY(
this.Y,
this.X / dot,
this.Y / dot,
this.alpha
)
}
fun CIEYXY.toXYZ(): CIEXYZ { fun CIEYXY.toXYZ(): CIEXYZ {
return CIEXYZ(x * yy / y, yy, (1f - x - y) * yy / y) return CIEXYZ(x * yy / y, yy, (1f - x - y) * yy / y)
} }

View File

@@ -12,16 +12,14 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.unicode.EMDASH import net.torvald.unicode.EMDASH
import net.torvald.colourutil.* import net.torvald.colourutil.*
import net.torvald.parametricsky.datasets.DatasetCIEXYZ import net.torvald.parametricsky.datasets.DatasetCIEXYZ
import net.torvald.parametricsky.datasets.DatasetRGB
import net.torvald.parametricsky.datasets.DatasetSpectral
import net.torvald.terrarum.abs import net.torvald.terrarum.abs
import net.torvald.terrarum.clut.Skybox
import net.torvald.terrarum.clut.Skybox.coerceInSmoothly
import net.torvald.terrarum.clut.Skybox.mapCircle
import net.torvald.terrarum.inUse import net.torvald.terrarum.inUse
import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI
import net.torvald.terrarum.modulebasegame.worldgenerator.TWO_PI
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.Dimension import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.GridLayout
import java.lang.Math.pow import java.lang.Math.pow
import javax.swing.* import javax.swing.*
import kotlin.math.* import kotlin.math.*
@@ -94,7 +92,16 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
if (turbidity <= 0) throw IllegalStateException() if (turbidity <= 0) throw IllegalStateException()
// we need to use different model-state to accommodate different albedo for each spectral band but oh well... // we need to use different model-state to accommodate different albedo for each spectral band but oh well...
genTexLoop(model) genTexLoop(model, elevation)
// println("$elevation\t${ymaxDisp.text}\t${ymaxDisp2.text}")
/*for (elev in -75..75) {
val elevation = Math.toRadians(elev.toDouble())
val model = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevation.abs())
genTexLoop(model, elevation)
println("$elev\t${ymaxDisp.text}\t${ymaxDisp2.text}")
}*/
val tex = Texture(oneScreen) val tex = Texture(oneScreen)
@@ -127,7 +134,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
} }
val outTexWidth = 1 val outTexWidth = 1
val outTexHeight = 256 val outTexHeight = 128
private fun Float.scaleFun() = private fun Float.scaleFun() =
(1f - 1f / 2f.pow(this/6f)) * 0.97f (1f - 1f / 2f.pow(this/6f)) * 0.97f
@@ -142,14 +149,20 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
) )
} }
else { else {
val elevation1 = -Math.toDegrees(elevation) // maths model: https://www.desmos.com/calculator/cwi7iyzygg
val elevation2 = -Math.toDegrees(elevation) / 28.5
val scale = (1f - (1f - 1f / 1.8.pow(elevation1)) * 0.97f).toFloat() val x = -Math.toDegrees(elevation).toFloat()
val scale2 = (1.0 - (elevation2.pow(E) / E.pow(elevation2))*0.8).toFloat() // val elevation2 = -Math.toDegrees(elevation) / 28.5
val p = 3.5f
val q = 7.5f
val s = -0.2f
val f = (1f - (1f - 1f / 1.8f.pow(x)) * 0.97f).toFloat()
// val g = (1.0 - (elevation2.pow(E) / E.pow(elevation2))*0.8).toFloat()
val h = ((x / q).pow(p) + 1f).pow(s)
CIEXYZ( CIEXYZ(
this.X.scaleFun() * scale * scale2, this.X.scaleFun() * f * h,
this.Y.scaleFun() * scale * scale2, this.Y.scaleFun() * f * h,
this.Z.scaleFun() * scale * scale2, this.Z.scaleFun() * f * h,
this.alpha this.alpha
) )
} }
@@ -161,7 +174,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
* Generated texture is as if you took the panorama picture of sky: up 70deg to horizon, east-south-west; * Generated texture is as if you took the panorama picture of sky: up 70deg to horizon, east-south-west;
* with sun not moving (sun is at exact south, sun's height is adjustable) * with sun not moving (sun is at exact south, sun's height is adjustable)
*/ */
private fun genTexLoop(state: ArHosekSkyModelState) { private fun genTexLoop(state: ArHosekSkyModelState, elevation: Double) {
fun normaliseY(y: Double): Float { fun normaliseY(y: Double): Float {
var v = y.coerceAtLeast(0.0) var v = y.coerceAtLeast(0.0)
@@ -175,6 +188,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val ys2 = ArrayList<Float>() val ys2 = ArrayList<Float>()
val halfHeight = oneScreen.height * 0.5 val halfHeight = oneScreen.height * 0.5
val elevationDeg = Math.toDegrees(elevation)
for (x in 0 until oneScreen.width) { for (x in 0 until oneScreen.width) {
for (y in 0 until oneScreen.height) { for (y in 0 until oneScreen.height) {
@@ -186,17 +200,19 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val theta = sqrt(xf*xf + yf*yf) * HALF_PI*/ val theta = sqrt(xf*xf + yf*yf) * HALF_PI*/
// AM-PM mapping (use with WIDTH=1) // AM-PM mapping (use with WIDTH=1)
var yf = (y * 2.0 / oneScreen.height) % 1.0 val yp = y % (oneScreen.height / 2)
if (elevation < 0) yf *= 1.0 - pow(-elevation / HALF_PI, 0.333) val yi = yp - 3
val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)
if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf)
val theta = yf * HALF_PI
val gamma = if (y < halfHeight) HALF_PI else 3 * HALF_PI val gamma = if (y < halfHeight) HALF_PI else 3 * HALF_PI
val theta = yf.mapCircle() * HALF_PI
val xyz = CIEXYZ( val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat() ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat(),
) )
val xyz2 = xyz.scaleToFit(elevation) val xyz2 = xyz.scaleToFit(elevation)
ys.add(xyz.Y) ys.add(xyz.Y)
@@ -204,12 +220,12 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val rgb = xyz2.toRGB().toColor() val rgb = xyz2.toRGB().toColor()
rgb.a = 1f rgb.a = 1f
val rgb2 = Color( /*val rgb2 = Color(
((rgb.r * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.r * 255f).roundToInt() xor 0xAA) / 255f,
((rgb.g * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.g * 255f).roundToInt() xor 0xAA) / 255f,
((rgb.b * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.b * 255f).roundToInt() xor 0xAA) / 255f,
rgb.a rgb.a
) )*/
oneScreen.setColor(rgb) oneScreen.setColor(rgb)
oneScreen.drawPixel(x, y) oneScreen.drawPixel(x, y)

View File

@@ -62,9 +62,11 @@ public class App implements ApplicationListener {
public static final String GAME_NAME = TerrarumAppConfiguration.GAME_NAME; public static final String GAME_NAME = TerrarumAppConfiguration.GAME_NAME;
public static final long VERSION_RAW = TerrarumAppConfiguration.VERSION_RAW; public static final long VERSION_RAW = TerrarumAppConfiguration.VERSION_RAW;
public static final String VERSION_TAG = TerrarumAppConfiguration.VERSION_TAG;
public static final String getVERSION_STRING() { public static final String getVERSION_STRING() {
return String.format("%d.%d.%d", VERSION_RAW >>> 48, (VERSION_RAW & 0xffff000000L) >>> 24, VERSION_RAW & 0xffffffL); return String.format("%d.%d.%d", VERSION_RAW >>> 48, (VERSION_RAW & 0xffff000000L) >>> 24, VERSION_RAW & 0xffffffL) +
(VERSION_TAG.isBlank() ? "" : "-"+VERSION_TAG);
} }
/** /**
@@ -267,12 +269,13 @@ public class App implements ApplicationListener {
Gdx.gl20.glViewport(0, 0, width, height); Gdx.gl20.glViewport(0, 0, width, height);
} }
public static final float UPDATE_RATE = 1f / 64f; // apparent framerate will be limited by update rate public static final int TICK_SPEED = 64;
public static final float UPDATE_RATE = 1f / TICK_SPEED; // apparent framerate will be limited by update rate
private static float loadTimer = 0f; private static float loadTimer = 0f;
private static final float showupTime = 100f / 1000f; private static final float showupTime = 100f / 1000f;
private static FloatFrameBuffer renderFBO; private static Float16FrameBuffer renderFBO;
public static HashSet<File> tempFilePool = new HashSet<>(); public static HashSet<File> tempFilePool = new HashSet<>();
@@ -609,19 +612,21 @@ public class App implements ApplicationListener {
private static void processScreenshotRequest(FrameBuffer fb) { private static void processScreenshotRequest(FrameBuffer fb) {
if (screenshotRequested) { if (screenshotRequested) {
String msg = "Screenshot taken";
FrameBufferManager.begin(fb); FrameBufferManager.begin(fb);
try { try {
Pixmap p = Pixmap.createFromFrameBuffer(0, 0, fb.getWidth(), fb.getHeight()); Pixmap p = Pixmap.createFromFrameBuffer(0, 0, fb.getWidth(), fb.getHeight());
PixmapIO.writePNG(Gdx.files.absolute(defaultDir+"/Screenshot-"+String.valueOf(System.currentTimeMillis())+".png"), p, 9, true); PixmapIO.writePNG(Gdx.files.absolute(defaultDir+"/Screenshot-"+String.valueOf(System.currentTimeMillis())+".png"), p, 9, true);
p.dispose(); p.dispose();
Terrarum.INSTANCE.getIngame().sendNotification("Screenshot taken");
} }
catch (Throwable e) { catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
Terrarum.INSTANCE.getIngame().sendNotification("Failed to take screenshot: "+e.getMessage()); msg = ("Failed to take screenshot: "+e.getMessage());
} }
FrameBufferManager.end(); FrameBufferManager.end();
screenshotRequested = false; screenshotRequested = false;
Terrarum.INSTANCE.getIngame().sendNotification(msg);
} }
} }
@@ -750,12 +755,12 @@ public class App implements ApplicationListener {
(renderFBO.getWidth() != scr.getWidth() || (renderFBO.getWidth() != scr.getWidth() ||
renderFBO.getHeight() != scr.getHeight()) renderFBO.getHeight() != scr.getHeight())
) { ) {
renderFBO = new FloatFrameBuffer( renderFBO = new Float16FrameBuffer(
scr.getWidth(), scr.getWidth(),
scr.getHeight(), scr.getHeight(),
false false
); );
postProcessorOutFBO2 = new FloatFrameBuffer( postProcessorOutFBO2 = new Float16FrameBuffer(
scr.getWidth() * 2, scr.getWidth() * 2,
scr.getHeight() * 2, scr.getHeight() * 2,
false false
@@ -1373,6 +1378,8 @@ public class App implements ApplicationListener {
* @throws NullPointerException if the specified config simply does not exist. * @throws NullPointerException if the specified config simply does not exist.
*/ */
public static int getConfigInt(String key) { public static int getConfigInt(String key) {
if (key == null) return -1;
Object cfg = getConfigMaster(key); Object cfg = getConfigMaster(key);
if (cfg instanceof Integer) return ((int) cfg); if (cfg instanceof Integer) return ((int) cfg);

View File

@@ -0,0 +1,90 @@
package net.torvald.terrarum
import com.badlogic.gdx.Input
/**
* Created by minjaesong on 2023-08-24.
*/
object ControlPresets {
val wasd = hashMapOf<String, Int>(
"control_key_up" to Input.Keys.W,
"control_key_left" to Input.Keys.A,
"control_key_down" to Input.Keys.S,
"control_key_right" to Input.Keys.D,
"control_key_jump" to Input.Keys.SPACE,
"control_key_movementaux" to Input.Keys.SHIFT_LEFT, // movement-auxiliary, or hookshot
"control_key_inventory" to Input.Keys.Q,
"control_key_interact" to Input.Keys.R,
"control_key_discard" to Input.Keys.F,
"control_key_close" to Input.Keys.X, // this or hard-coded ESC
"control_key_zoom" to Input.Keys.Z,
"control_key_gamemenu" to Input.Keys.TAB,
"control_key_crafting" to Input.Keys.E,
"control_key_quicksel" to Input.Keys.CONTROL_LEFT, // pie menu is now LShift because CapsLock is actually used by the my bespoke keyboard input
)
val esdf = hashMapOf<String, Int>(
"control_key_up" to Input.Keys.E,
"control_key_left" to Input.Keys.S,
"control_key_down" to Input.Keys.D,
"control_key_right" to Input.Keys.F, // ESDF Masterrace
"control_key_jump" to Input.Keys.SPACE,
"control_key_movementaux" to Input.Keys.A, // movement-auxiliary, or hookshot
"control_key_inventory" to Input.Keys.Q,
"control_key_interact" to Input.Keys.R,
"control_key_discard" to Input.Keys.T,
"control_key_close" to Input.Keys.C, // this or hard-coded ESC
"control_key_zoom" to Input.Keys.Z,
"control_key_gamemenu" to Input.Keys.TAB,
"control_key_crafting" to Input.Keys.W,
"control_key_quicksel" to Input.Keys.SHIFT_LEFT, // pie menu is now LShift because CapsLock is actually used by the my bespoke keyboard input
)
val ijkl = hashMapOf<String, Int>(
"control_key_up" to Input.Keys.I,
"control_key_left" to Input.Keys.J,
"control_key_down" to Input.Keys.K,
"control_key_right" to Input.Keys.L,
"control_key_jump" to Input.Keys.SPACE,
"control_key_movementaux" to Input.Keys.SEMICOLON, // movement-auxiliary, or hookshot
"control_key_inventory" to Input.Keys.P,
"control_key_interact" to Input.Keys.U,
"control_key_discard" to Input.Keys.Y,
"control_key_close" to Input.Keys.M, // this or hard-coded ESC
"control_key_zoom" to Input.Keys.SLASH,
"control_key_gamemenu" to Input.Keys.LEFT_BRACKET,
"control_key_crafting" to Input.Keys.O,
"control_key_quicksel" to Input.Keys.APOSTROPHE, // pie menu is now LShift because CapsLock is actually used by the my bespoke keyboard input
)
val empty = hashMapOf<String, Int>()
val presets = hashMapOf( // unordered
"WASD" to wasd,
"ESDF" to esdf,
"IJKL" to ijkl,
"Custom" to empty,
)
val presetLabels = listOf( // ordered
"WASD",
"ESDF",
"IJKL",
"Custom",
)
fun getKey(label: String?): Int {
if (label == null) return -1
val presetName = App.getConfigString("control_preset_keyboard") ?: "Custom"
return (presets[presetName] ?: throw IllegalStateException("No such keyboard preset: $presetName")).getOrDefault(label, App.getConfigInt(label))
}
}

View File

@@ -31,6 +31,8 @@ object DefaultConfig {
"usexinput" to true, // when FALSE, LT+RT input on xbox controller is impossible "usexinput" to true, // when FALSE, LT+RT input on xbox controller is impossible
"control_preset_keyboard" to "WASD",
"control_gamepad_keyn" to 3, "control_gamepad_keyn" to 3,
"control_gamepad_keyw" to 2, "control_gamepad_keyw" to 2,
"control_gamepad_keys" to 0, "control_gamepad_keys" to 0,

View File

@@ -531,6 +531,9 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
else else
null null
} }
fun onConfigChange() {
}
} }
inline fun Lock.lock(body: () -> Unit) { inline fun Lock.lock(body: () -> Unit) {

View File

@@ -62,9 +62,12 @@ basegame
* e.g. 0x02010034 will be translated as 2.1.52 * e.g. 0x02010034 will be translated as 2.1.52
* *
*/ */
const val VERSION_RAW: Long = 0x0000_000003_000002 const val VERSION_RAW: Long = 0x0000_000003_000003
// Commit counts up to the Release 0.3.0: 2259 // Commit counts up to the Release 0.3.0: 2259
// Commit counts up to the Release 0.3.1: 2278 // Commit counts up to the Release 0.3.1: 2278
// Commit counts up to the Release 0.3.2: 2732
const val VERSION_TAG: String = "test002"
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
// CONFIGURATION FOR TILE MAKER // // CONFIGURATION FOR TILE MAKER //

View File

@@ -9,6 +9,7 @@ import com.badlogic.gdx.math.Matrix4
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.App.IS_DEVELOPMENT_BUILD
import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.ui.BasicDebugInfoWindow import net.torvald.terrarum.ui.BasicDebugInfoWindow
import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.ui.Toolkit
@@ -150,9 +151,10 @@ object TerrarumPostProcessor : Disposable {
} }
// draw dev build notifiers // draw dev build notifiers
if (App.IS_DEVELOPMENT_BUILD && Terrarum.ingame != null) { // omitting this screws up HQ2X render for some reason
if (Terrarum.ingame != null) {
batch.inUse { batch.inUse {
batch.color = safeAreaCol batch.color = if (IS_DEVELOPMENT_BUILD) safeAreaCol else colourNull
App.fontGame.draw(it, thisIsDebugStr, 5f, App.scr.height - 24f) App.fontGame.draw(it, thisIsDebugStr, 5f, App.scr.height - 24f)
} }
} }
@@ -192,6 +194,7 @@ object TerrarumPostProcessor : Disposable {
return outFBO return outFBO
} }
private val rng = HQRNG() private val rng = HQRNG()
private val colourNull = Color(0)
private fun Double.format(digits: Int) = "%.${digits}f".format(this) private fun Double.format(digits: Int) = "%.${digits}f".format(this)
private fun Float.format(digits: Int) = "%.${digits}f".format(this) private fun Float.format(digits: Int) = "%.${digits}f".format(this)

View File

@@ -209,8 +209,8 @@ class BlockCodex {
prop.isSolid = record.boolVal("solid") prop.isSolid = record.boolVal("solid")
//prop.isClear = record.boolVal("clear") //prop.isClear = record.boolVal("clear")
prop.isPlatform = prop.tags.contains("PLATFORM") prop.isPlatform = prop.hasTag("PLATFORM")
prop.isActorBlock = prop.tags.contains("ACTORBLOCK") prop.isActorBlock = prop.hasTag("ACTORBLOCK")
prop.isWallable = record.boolVal("wall") prop.isWallable = record.boolVal("wall")
prop.maxSupport = record.intVal("grav") prop.maxSupport = record.intVal("grav")

View File

@@ -75,6 +75,8 @@ class BlockProp {
BlockCodex[BlockCodex.tileToVirtual[id]!![offset]]._lumCol.lane(channel) BlockCodex[BlockCodex.tileToVirtual[id]!![offset]]._lumCol.lane(channel)
} }
fun hasTag(s: String) = tags.contains(s)
/** /**
* @param luminosity * @param luminosity
*/ */

View File

@@ -19,7 +19,7 @@ fun main() {
// y: increasing turbidity (1.0 .. 10.0, in steps of 0.333) // y: increasing turbidity (1.0 .. 10.0, in steps of 0.333)
// x: elevations (-75 .. 75 in steps of 1, then albedo of [0.1, 0.3, 0.5, 0.7, 0.9]) // x: elevations (-75 .. 75 in steps of 1, then albedo of [0.1, 0.3, 0.5, 0.7, 0.9])
val texh = Skybox.gradSize * Skybox.turbCnt val texh = Skybox.gradSize * Skybox.turbCnt
val texw = Skybox.elevCnt * Skybox.albedoCnt val texw = Skybox.elevCnt * Skybox.albedoCnt * 2
val TGA_HEADER_SIZE = 18 val TGA_HEADER_SIZE = 18
val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26) val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26)
@@ -44,47 +44,50 @@ fun main() {
println("Generating texture atlas ($texw x $texh)...") println("Generating texture atlas ($texw x $texh)...")
// write pixels // write pixels
for (albedo0 in 0 until Skybox.albedoCnt) { for (gammaPair in 0..1) {
val albedo = Skybox.albedos[albedo0] for (albedo0 in 0 until Skybox.albedoCnt) {
println("Albedo=$albedo") val albedo = Skybox.albedos[albedo0]
for (turb0 in 0 until Skybox.turbCnt) { println("Albedo=$albedo")
val turbidity = Skybox.turbiditiesD[turb0] for (turb0 in 0 until Skybox.turbCnt) {
println("....... Turbidity=$turbidity") val turbidity = Skybox.turbiditiesD[turb0]
for (elev0 in 0 until Skybox.elevCnt) { println("....... Turbidity=$turbidity")
val elevationDeg = Skybox.elevationsD[elev0] for (elev0 in 0 until Skybox.elevCnt) {
val elevationRad = Math.toRadians(elevationDeg) val elevationDeg = Skybox.elevationsD[elev0]
val elevationRad = Math.toRadians(elevationDeg)
// println("... Elevation: $elevationDeg") // println("... Elevation: $elevationDeg")
val state = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs()) val state =
ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs())
for (yp in 0 until Skybox.gradSize) { for (yp in 0 until Skybox.gradSize) {
val yi = yp - 3 val yi = yp - 10
val xf = -elevationDeg / 90.0 val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95) var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)
// experiments visualisation: https://www.desmos.com/calculator/5crifaekwa // experiments visualisation: https://www.desmos.com/calculator/5crifaekwa
// if (elevationDeg < 0) yf *= 1.0 - pow(xf, 0.333) // if (elevationDeg < 0) yf *= 1.0 - pow(xf, 0.333)
// if (elevationDeg < 0) yf *= -2.0 * asin(xf - 1.0) / PI // if (elevationDeg < 0) yf *= -2.0 * asin(xf - 1.0) / PI
if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf) if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf)
val theta = yf * HALF_PI val theta = yf * HALF_PI
// vertical angle, where 0 is zenith, ±90 is ground (which is odd) // vertical angle, where 0 is zenith, ±90 is ground (which is odd)
// println("$yp\t$theta") // println("$yp\t$theta")
val xyz = CIEXYZ( val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 0).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 1).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 2).toFloat() ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 2).toFloat()
) )
val xyz2 = xyz.scaleToFit(elevationDeg) val xyz2 = xyz.scaleToFit(elevationDeg)
val rgb = xyz2.toRGB().toColor() val rgb = xyz2.toRGB().toColor()
val colour = rgb.toIntBits().toLittle() val colour = rgb.toIntBits().toLittle()
val imgOffX = (albedo0 * Skybox.elevCnt + elev0) val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair
val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp) val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp)
val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX) val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX)
for (i in 0..3) { for (i in 0..3) {
bytes[fileOffset + i] = colour[bytesLut[i]] bytes[fileOffset + i] = colour[bytesLut[i]]
}
} }
} }
} }
@@ -93,7 +96,7 @@ fun main() {
println("Atlas generation done!") println("Atlas generation done!")
File("./assets/mods/basegame/weathers/main_skybox.tga").writeBytes(bytes) File("./assets/clut/skybox.tga").writeBytes(bytes)
} }
private val bytesLut = arrayOf(2,1,0,3,2,1,0,3) // For some reason BGRA order is what makes it work private val bytesLut = arrayOf(2,1,0,3,2,1,0,3) // For some reason BGRA order is what makes it work

View File

@@ -13,6 +13,8 @@ import net.torvald.parametricsky.ArHosekSkyModel
import net.torvald.terrarum.App import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.abs import net.torvald.terrarum.abs
import net.torvald.terrarum.floorToInt
import net.torvald.terrarum.toInt
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.* import kotlin.math.*
@@ -25,7 +27,7 @@ object Skybox : Disposable {
private const val PI = 3.141592653589793 private const val PI = 3.141592653589793
private const val TWO_PI = 6.283185307179586 private const val TWO_PI = 6.283185307179586
const val gradSize = 64 const val gradSize = 78
private lateinit var gradTexBinLowAlbedo: Array<TextureRegion> private lateinit var gradTexBinLowAlbedo: Array<TextureRegion>
private lateinit var gradTexBinHighAlbedo: Array<TextureRegion> private lateinit var gradTexBinHighAlbedo: Array<TextureRegion>
@@ -37,6 +39,7 @@ object Skybox : Disposable {
fun loadlut() { fun loadlut() {
tex = Texture(Gdx.files.internal("assets/clut/skybox.png")) tex = Texture(Gdx.files.internal("assets/clut/skybox.png"))
tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear) tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
tex.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat)
texRegions = TextureRegionPack(tex, 2, gradSize - 2, 0, 2, 0, 1) texRegions = TextureRegionPack(tex, 2, gradSize - 2, 0, 2, 0, 1)
texStripRegions = TextureRegionPack(tex, elevCnt, gradSize - 2, 0, 2, 0, 1) texStripRegions = TextureRegionPack(tex, elevCnt, gradSize - 2, 0, 2, 0, 1)
} }
@@ -50,28 +53,67 @@ object Skybox : Disposable {
}*/ }*/
// use external LUT // use external LUT
operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double): TextureRegion { operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double, isAfternoon: Boolean): TextureRegion {
val elev = elevationDeg.coerceIn(-elevMax, elevMax).roundToInt().plus(elevMax).roundToInt() TODO()
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(turbDivisor).roundToInt()
val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(turbDivisor).roundToInt()
//printdbg(this, "elev $elevationDeg->$elev; turb $turbidity->$turb; alb $albedo->$alb")
return texRegions.get(alb * elevCnt + elev, turb)
} }
fun getUV(elevationDeg: Double, turbidity: Double, albedo: Double): Pair<Texture, FloatArray> { data class SkyboxRenderInfo(
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(turbDivisor).roundToInt() val texture: Texture,
val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(turbDivisor).roundToInt() val uvs: FloatArray,
val region = texStripRegions.get(alb, turb) val turbidityPoint: Float,
val albedoPoint: Float,
)
val elev = elevationDeg.coerceIn(-elevMax, elevMax).plus(elevMax).div(elevations.last.toDouble()).div(albedoCnt).times((elevCnt - 1.0) / elevCnt) fun getUV(elevationDeg: Double, turbidity: Double, albedo: Double): SkyboxRenderInfo {
val turb = turbidity.coerceIn(turbiditiesD.first(), turbiditiesD.last()).minus(1.0).times(turbDivisor)
val turbLo = turb.floorToInt()
val turbHi = min(turbCnt - 1, turbLo + 1)
val alb = albedo.coerceIn(albedos.first(), albedos.last()).times(5.0)
val albLo = alb.floorToInt()
val albHi = min(albedoCnt - 1, albLo + 1)
val elev = elevationDeg.coerceIn(-elevMax, elevMax).plus(elevMax).div(elevations.last.toDouble()).div(albedoCnt * 2).times((elevCnt - 1.0) / elevCnt)
val u = region.u + (0.5f / tex.width) + elev.toFloat() // because of the nature of bilinear interpolation, half pixels from the edges must be discarded // A: morn, turbLow, albLow
// B: noon, turbLow, albLow
// C: morn, turbHigh, albLow
// D: noon, turbHigh, albLow
// E: morn, turbLow, albHigh
// F: noon, turbLow, albHigh
// G: morn, turbHigh, albHigh
// H: noon, turbHigh, albHigh
return tex to floatArrayOf( val regionA = texStripRegions.get(albLo + albedoCnt * 0, turbLo)
u, val regionB = texStripRegions.get(albLo + albedoCnt * 1, turbLo)
region.v, val regionC = texStripRegions.get(albLo + albedoCnt * 0, turbHi)
u, val regionD = texStripRegions.get(albLo + albedoCnt * 1, turbHi)
region.v2 val regionE = texStripRegions.get(albHi + albedoCnt * 0, turbLo)
val regionF = texStripRegions.get(albHi + albedoCnt * 1, turbLo)
val regionG = texStripRegions.get(albHi + albedoCnt * 0, turbHi)
val regionH = texStripRegions.get(albHi + albedoCnt * 1, turbHi)
// (0.5f / tex.width): because of the nature of bilinear interpolation, half pixels from the edges must be discarded
val uA = regionA.u + (0.5f / tex.width) + elev.toFloat()
val uB = regionB.u + (0.5f / tex.width) + elev.toFloat()
val uC = regionC.u + (0.5f / tex.width) + elev.toFloat()
val uD = regionD.u + (0.5f / tex.width) + elev.toFloat()
val uE = regionE.u + (0.5f / tex.width) + elev.toFloat()
val uF = regionF.u + (0.5f / tex.width) + elev.toFloat()
val uG = regionG.u + (0.5f / tex.width) + elev.toFloat()
val uH = regionH.u + (0.5f / tex.width) + elev.toFloat()
return SkyboxRenderInfo(
tex,
floatArrayOf(
uA, regionA.v, uA, regionA.v2,
uB, regionB.v, uB, regionB.v2,
uC, regionC.v, uC, regionC.v2,
uD, regionD.v, uD, regionD.v2,
uE, regionE.v, uE, regionE.v2,
uF, regionF.v, uF, regionF.v2,
uG, regionG.v, uG, regionG.v2,
uH, regionH.v, uH, regionH.v2,
),
(turb - turbLo).toFloat(),
(alb - albLo).toFloat(),
) )
} }
@@ -88,15 +130,20 @@ object Skybox : Disposable {
) )
} }
else { else {
val deg1 = (-elevationDeg / elevMax).pow(0.93).times(-elevMax) // maths model: https://www.desmos.com/calculator/cwi7iyzygg
val elevation1 = -deg1
val elevation2 = -deg1 / 28.5 val x = -elevationDeg.toFloat()
val scale = (1f - (1f - 1f / 1.8.pow(elevation1)) * 0.97f).toFloat() // val elevation2 = elevationDeg.toFloat() / 28.5f
val scale2 = (1.0 - (elevation2.pow(E) / E.pow(elevation2))*0.8).toFloat() val p = 3.5f
val q = 7.5f
val s = -0.2f
val f = (1f - (1f - 1f / 1.8f.pow(x)) * 0.97f).toFloat()
// val g = (1.0 - (elevation2.pow(E) / E.pow(elevation2))*0.8).toFloat()
val h = ((x / q).pow(p) + 1f).pow(s)
CIEXYZ( CIEXYZ(
this.X.scaleFun() * scale * scale2, this.X.scaleFun() * f * h,
this.Y.scaleFun() * scale * scale2, this.Y.scaleFun() * f * h,
this.Z.scaleFun() * scale * scale2, this.Z.scaleFun() * f * h,
this.alpha this.alpha
) )
} }
@@ -105,15 +152,13 @@ object Skybox : Disposable {
val elevations = (0..150) val elevations = (0..150)
val elevMax = elevations.last / 2.0 val elevMax = elevations.last / 2.0
val elevationsD = elevations.map { -elevMax + it } // -75, -74, -73, ..., 74, 75 // (specifically using whole number of angles because angle units any finer than 1.0 would make "hack" sunsut happen too fast) val elevationsD = elevations.map { -elevMax + it } // -75, -74, -73, ..., 74, 75 // (specifically using whole number of angles because angle units any finer than 1.0 would make "hack" sunsut happen too fast)
val turbidities = (0..45) // 1, 1.2, 1.4, 1.6, ..., 10.0 val turbidities = (0..25) // 1, 1.2, 1.4, 1.6, ..., 6.0
val turbDivisor = 5.0 val turbDivisor = 5.0
val turbiditiesD = turbidities.map { 1.0 + it / turbDivisor } val turbiditiesD = turbidities.map { 1.0 + it / turbDivisor }
val albedos = arrayOf(0.1, 0.3, 0.5, 0.7, 0.9) val albedos = arrayOf(0.0, 0.2, 0.4, 0.6, 0.8, 1.0)
val elevCnt = elevations.count() val elevCnt = elevations.count()
val turbCnt = turbidities.count() val turbCnt = turbidities.count()
val albedoCnt = albedos.size val albedoCnt = albedos.size
val albedoLow = 0.1
val albedoHight = 0.8 // for theoretical "winter wonderland"?
val gamma = HALF_PI val gamma = HALF_PI
internal fun Double.mapCircle() = sin(HALF_PI * this) internal fun Double.mapCircle() = sin(HALF_PI * this)
@@ -121,8 +166,7 @@ object Skybox : Disposable {
internal fun initiate() { internal fun initiate() {
printdbg(this, "Initialising skybox model") printdbg(this, "Initialising skybox model")
gradTexBinLowAlbedo = getTexturmaps(albedoLow) TODO()
gradTexBinHighAlbedo = getTexturmaps(albedoHight)
App.disposables.add(this) App.disposables.add(this)
@@ -170,6 +214,11 @@ object Skybox : Disposable {
/** /**
* To get the idea what the fuck is going on here, please refer to https://www.desmos.com/calculator/snqglcu2wl * To get the idea what the fuck is going on here, please refer to https://www.desmos.com/calculator/snqglcu2wl
*
* Sidenote: the original model involved two cosine curves, but since its Taylor series begins with x^2, I figured
* quadratic curve ought to be good enough, and the error against the original model was below 1/255 for
* reasonable range of p, and that's the reason I stopped at x^2 rather than also taking x^4 into the approximated
* model that is the code below.
*/ */
internal fun smoothLinear(p: Double, x0: Double): Double { internal fun smoothLinear(p: Double, x0: Double): Double {
val x = x0 - 0.5 val x = x0 - 0.5
@@ -198,7 +247,7 @@ object Skybox : Disposable {
// printdbg(this, "elev $elevationDeg turb $turbidity") // printdbg(this, "elev $elevationDeg turb $turbidity")
for (yp in 0 until gradSize) { for (yp in 0 until gradSize) {
val yi = yp - 3 val yi = yp - 10
val xf = -elevationDeg / 90.0 val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95) var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)

View File

@@ -36,7 +36,7 @@ internal object Authenticator : ConsoleCommand {
println("auth passwd: '$pwd'") println("auth passwd: '$pwd'")
println("hash: $hashedPwd") println("hash: $hashedPwd")
if ("09ccf5067db6f58265b004829e33e715e819ba0984f1e1fcef49c36fcd5f745f".equals(hashedPwd, ignoreCase = true)) { if ("2d962f949f55906ac47f16095ded190c9e44d95920259b8f36c2e54bd75df173".equals(hashedPwd, ignoreCase = true)) {
// beedle // beedle
val msg = if (a) "Locked" else "Authenticated" val msg = if (a) "Locked" else "Authenticated"
Echo(msg) Echo(msg)

File diff suppressed because it is too large Load Diff

View File

@@ -145,6 +145,9 @@ class Hitbox {
return this return this
} }
/**
* Resize the hitbox centred around the "canonical" point.
*/
fun canonicalResize(w: Double, h: Double): Hitbox { fun canonicalResize(w: Double, h: Double): Hitbox {
// sx_1 + 0.5w_1 = sx_2 + 0.5w_2 // equals because the final point must not move. sx_1: old start-x, sx_2: new start-x which is what we want // sx_1 + 0.5w_1 = sx_2 + 0.5w_2 // equals because the final point must not move. sx_1: old start-x, sx_2: new start-x which is what we want
// sx_2 = sx_1 + 0.5w_1 - 0.5w_2 // move variables to right-hand side to derive final value sx_2 // sx_2 = sx_1 + 0.5w_1 - 0.5w_2 // move variables to right-hand side to derive final value sx_2

View File

@@ -5,14 +5,11 @@ import com.badlogic.gdx.Input
import com.badlogic.gdx.InputAdapter import com.badlogic.gdx.InputAdapter
import com.badlogic.gdx.controllers.Controllers import com.badlogic.gdx.controllers.Controllers
import com.badlogic.gdx.utils.GdxRuntimeException import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.terrarum.App import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.App.printdbgerr import net.torvald.terrarum.App.printdbgerr
import net.torvald.terrarum.ItemCodex
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.controller.TerrarumController import net.torvald.terrarum.controller.TerrarumController
import net.torvald.terrarum.floorToInt
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.gameworld.fmod
@@ -168,7 +165,7 @@ class IngameController(val terrarumIngame: TerrarumIngame) : InputAdapter() {
// pie menu // pie menu
if (App.getConfigIntArray("control_key_quickselalt").contains(keycode) if (App.getConfigIntArray("control_key_quickselalt").contains(keycode)
|| keycode == App.getConfigInt("control_key_quicksel")) { || keycode == ControlPresets.getKey("control_key_quicksel")) {
terrarumIngame.uiPieMenu.setAsOpen() terrarumIngame.uiPieMenu.setAsOpen()
terrarumIngame.uiQuickBar.setAsClose() terrarumIngame.uiQuickBar.setAsClose()
} }
@@ -194,7 +191,7 @@ class IngameController(val terrarumIngame: TerrarumIngame) : InputAdapter() {
private fun tKeyUp(keycode: Int): Boolean { private fun tKeyUp(keycode: Int): Boolean {
if (App.getConfigIntArray("control_key_quickselalt").contains(keycode) if (App.getConfigIntArray("control_key_quickselalt").contains(keycode)
|| keycode == App.getConfigInt("control_key_quicksel")) { || keycode == ControlPresets.getKey("control_key_quicksel")) {
terrarumIngame.uiPieMenu.setAsClose() terrarumIngame.uiPieMenu.setAsClose()
terrarumIngame.uiQuickBar.setAsOpen() terrarumIngame.uiQuickBar.setAsOpen()
} }

View File

@@ -359,6 +359,8 @@ abstract class GameItem(val originalID: ItemID) : Comparable<GameItem>, Cloneabl
return this return this
} }
fun hasTag(s: String) = tags.contains(s)
companion object { companion object {

View File

@@ -1,8 +1,7 @@
package net.torvald.terrarum.gameworld package net.torvald.terrarum.gameworld
import net.torvald.terrarum.modulebasegame.worldgenerator.TWO_PI import net.torvald.terrarum.langpack.Lang
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin
/** /**
@@ -57,8 +56,10 @@ import kotlin.math.sin
*/ */
class WorldTime(initTime: Long = 0L) { class WorldTime(initTime: Long = 0L) {
@Transient private val TWO_PI = Math.PI * 2.0
/** It is not recommended to directly modify the TIME_T. Use provided methods instead. */ /** It is not recommended to directly modify the TIME_T. Use provided methods instead. */
var TIME_T = 0L // Epoch: Year 125 Spring 1st, 0h00:00 (Mondag) // 125-01-01 var TIME_T = 0L // Epoch: Year 1 Spring 1st, 0h00:00 (Mondag) // 0001-01-01
init { init {
TIME_T = initTime TIME_T = initTime
@@ -151,21 +152,7 @@ class WorldTime(initTime: Long = 0L) {
@Transient private val REAL_SEC_TO_GAME_SECS = 1.0 / GAME_MIN_TO_REAL_SEC // how slow is real-life clock (second-wise) relative to the ingame one @Transient private val REAL_SEC_TO_GAME_SECS = 1.0 / GAME_MIN_TO_REAL_SEC // how slow is real-life clock (second-wise) relative to the ingame one
// NOTE: ingame calendars (the fixture with GUI) should use symbols AND fullnames; the watch already uses shot daynames // NOTE: ingame calendars (the fixture with GUI) should use symbols AND fullnames; the watch already uses shot daynames
val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o)
"Mondag", "Tysdag", "Midtveke" //middle-week
, "Torsdag", "Fredag", "Laurdag", "Sundag", "Verddag" //From Norsk word 'verd'
)
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
// dwarven calendar of 12 monthes
/*val MONTH_NAMES = arrayOf(
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"
)
val MONTH_NAMES_SHORT = arrayOf("Opal", "Obsi", "Gran", "Slat", "Fels", "Hema",
"Mala", "Gale", "Lime", "Sand", "Timb", "Moon")*/
val MONTH_NAMES = arrayOf("Spring", "Summer", "Autumn", "Winter")
val MONTH_NAMES_SHORT = arrayOf("Spri", "Summ", "Autm", "Wint")
companion object { companion object {
/** Each day is displayed as 24 hours, but in real-life clock it's 22 mins long */ /** Each day is displayed as 24 hours, but in real-life clock it's 22 mins long */
@@ -181,7 +168,7 @@ class WorldTime(initTime: Long = 0L) {
const val MONTH_LENGTH = 30 // ingame calendar specific const val MONTH_LENGTH = 30 // ingame calendar specific
const val EPOCH_YEAR = 125 const val EPOCH_YEAR = 1
val YEAR_SECONDS = DAY_LENGTH * YEAR_DAYS val YEAR_SECONDS = DAY_LENGTH * YEAR_DAYS
@@ -203,6 +190,31 @@ class WorldTime(initTime: Long = 0L) {
val LUNAR_CYCLE: Int = 29 * DAY_LENGTH + 12 * HOUR_SEC + 44 * MINUTE_SEC + 3 // 29 days, 12 hours, 44 minutes, and 3 seconds in-game calendar val LUNAR_CYCLE: Int = 29 * DAY_LENGTH + 12 * HOUR_SEC + 44 * MINUTE_SEC + 3 // 29 days, 12 hours, 44 minutes, and 3 seconds in-game calendar
const val DIURNAL_MOTION_LENGTH = 86636f const val DIURNAL_MOTION_LENGTH = 86636f
val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o)
"MONDAG", "TYSDAG", "MIDTVEKE" //middle-week
, "TORSDAG", "FREDAG", "LAURDAG", "SUNDAG", "VERDDAG" //From Norsk word 'verd'
)
val DAY_NAMES_SHORT = arrayOf("MON", "TYS", "MID", "TOR", "FRE", "LAU", "SUN", "VER")
// dwarven calendar of 12 monthes
/*val MONTH_NAMES = arrayOf(
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"
)
val MONTH_NAMES_SHORT = arrayOf("Opal", "Obsi", "Gran", "Slat", "Fels", "Hema",
"Mala", "Gale", "Lime", "Sand", "Timb", "Moon")*/
val MONTH_NAMES = arrayOf("SPRING", "SUMMER", "AUTUMN", "WINTER")
val MONTH_NAMES_SHORT = arrayOf("SPRI", "SUMM", "AUTM", "WINT")
val DAY_NAMES_LANG_KEYS = DAY_NAMES.map { "CONTEXT_CALENDAR_DAY_${it}_DNT" }
val DAY_NAMES_SHORT_LANG_KEYS = DAY_NAMES_SHORT.map { "CONTEXT_CALENDAR_DAY_${it}_DNT" }
val MONTH_NAMES_LANG_KEYS = MONTH_NAMES.map { "CONTEXT_CALENDAR_SEASON_${it}" }
val MONTH_NAMES_SHORT_LANG_KEYS = MONTH_NAMES_SHORT.map { "CONTEXT_CALENDAR_SEASON_${it}" }
fun getDayName(index: Int) = Lang[DAY_NAMES_LANG_KEYS[index]]
fun getDayNameShort(index: Int) = Lang[DAY_NAMES_SHORT_LANG_KEYS[index]]
fun getMonthName(index: Int) = Lang[MONTH_NAMES_LANG_KEYS[index - 1]]
fun getMonthNameShort(index: Int) = Lang[MONTH_NAMES_SHORT_LANG_KEYS[index - 1]]
} }
fun update(delta: Float) { fun update(delta: Float) {
@@ -228,27 +240,30 @@ class WorldTime(initTime: Long = 0L) {
TIME_T += t TIME_T += t
} }
val dayName: String
get() = DAY_NAMES[dayOfWeek]
fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt() fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt()
fun Long.abs() = Math.abs(this) fun Long.abs() = Math.abs(this)
/** Format: "%A, %Y %B %d %X" */ /** Format: "ɣ%Y %B %d %A, %X" */
fun getFormattedTime() = "${getDayNameShort()}, " + fun getFormattedTime() =
"$years " + "ɣ$years " +
"${getMonthNameFull()} " + "${getMonthNameFull()} " +
"$calendarDay " + "$calendarDay " +
"${String.format("%02d", hours)}:" + "${getDayNameFull()}, " +
"${String.format("%02d", minutes)}:" + "${String.format("%02d", hours)}:" +
"${String.format("%02d", seconds)}" "${String.format("%02d", minutes)}:" +
"${String.format("%02d", seconds)}"
fun getFormattedCalendarDay() =
"ɣ$years " +
"${getMonthNameFull()} " +
"$calendarDay " +
"${getDayNameFull()}"
fun getShortTime() = "${years.toString().padStart(4, '0')}-${getMonthNameShort()}-${calendarDay.toString().padStart(2, '0')}" fun getShortTime() = "${years.toString().padStart(4, '0')}-${getMonthNameShort()}-${calendarDay.toString().padStart(2, '0')}"
fun getFilenameTime() = "${years.toString().padStart(4, '0')}${calendarMonth.toString().padStart(2, '0')}${calendarDay.toString().padStart(2, '0')}" fun getFilenameTime() = "${years.toString().padStart(4, '0')}${calendarMonth.toString().padStart(2, '0')}${calendarDay.toString().padStart(2, '0')}"
fun getDayNameFull() = DAY_NAMES[dayOfWeek] fun getDayNameFull() = getDayName(dayOfWeek)
fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek] fun getDayNameShort() = getDayNameShort(dayOfWeek)
fun getMonthNameFull() = MONTH_NAMES[calendarMonth - 1] fun getMonthNameFull() = getMonthName(calendarMonth)
fun getMonthNameShort() = MONTH_NAMES_SHORT[calendarMonth - 1] fun getMonthNameShort() = getMonthNameShort(calendarMonth)
override fun toString() = getFormattedTime() override fun toString() = getFormattedTime()
} }

View File

@@ -152,6 +152,15 @@ object Lang {
return sb.toString() return sb.toString()
} }
fun getAndUseTemplate(key: String, capitalise: Boolean = true, vararg arguments: Any?): String {
var raw = get(key, capitalise)
arguments.forEachIndexed { index, it0 ->
val it = if (capitalise) it0.toString().capitalize() else it0.toString()
raw = raw.replace("{${index}}", it)
}
return raw
}
/** /**
* Does NOT parse the operators * Does NOT parse the operators
*/ */

View File

@@ -302,6 +302,7 @@ class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) {
IngameRenderer.setRenderedWorld(gameWorld) IngameRenderer.setRenderedWorld(gameWorld)
WeatherMixer.internalReset()
} }
override fun show() { override fun show() {

View File

@@ -4,11 +4,10 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FloatFrameBuffer import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer
import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.graphics.glutils.ShaderProgram import com.badlogic.gdx.graphics.glutils.ShaderProgram
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.* import net.torvald.terrarum.App.*
@@ -53,20 +52,20 @@ object IngameRenderer : Disposable {
private lateinit var blurWriteQuad2: Mesh private lateinit var blurWriteQuad2: Mesh
// private lateinit var blurWriteQuad4: Mesh // private lateinit var blurWriteQuad4: Mesh
private lateinit var lightmapFbo: FloatFrameBuffer private lateinit var lightmapFbo: Float16FrameBuffer
private lateinit var fboRGB: FloatFrameBuffer private lateinit var fboRGB: Float16FrameBuffer
private lateinit var fboRGB_lightMixed: FloatFrameBuffer private lateinit var fboRGB_lightMixed: Float16FrameBuffer
private lateinit var fboA: FloatFrameBuffer private lateinit var fboA: Float16FrameBuffer
private lateinit var fboA_lightMixed: FloatFrameBuffer private lateinit var fboA_lightMixed: Float16FrameBuffer
private lateinit var fboMixedOut: FloatFrameBuffer private lateinit var fboMixedOut: Float16FrameBuffer
private lateinit var rgbTex: TextureRegion private lateinit var rgbTex: TextureRegion
private lateinit var aTex: TextureRegion private lateinit var aTex: TextureRegion
private lateinit var mixedOutTex: TextureRegion private lateinit var mixedOutTex: TextureRegion
private lateinit var lightTex: TextureRegion private lateinit var lightTex: TextureRegion
private lateinit var blurTex: TextureRegion private lateinit var blurTex: TextureRegion
private lateinit var fboBlurHalf: FloatFrameBuffer private lateinit var fboBlurHalf: Float16FrameBuffer
// private lateinit var fboBlurQuarter: FloatFrameBuffer // private lateinit var fboBlurQuarter: Float16FrameBuffer
// you must have lightMixed FBO; otherwise you'll be reading from unbaked FBO and it freaks out GPU // you must have lightMixed FBO; otherwise you'll be reading from unbaked FBO and it freaks out GPU
@@ -256,7 +255,9 @@ object IngameRenderer : Disposable {
gdxClearAndEnableBlend(0f, 0f, 0f, 0f) gdxClearAndEnableBlend(0f, 0f, 0f, 0f)
// draw sky // draw sky
WeatherMixer.render(camera, batch, world) measureDebugTime("WeatherMixer.render") {
WeatherMixer.render(camera, batch, world)
}
// normal behaviour // normal behaviour
@@ -694,7 +695,7 @@ object IngameRenderer : Disposable {
private const val KAWASE_POWER = 1.5f private const val KAWASE_POWER = 1.5f
fun processNoBlur(outFbo: FloatFrameBuffer) { fun processNoBlur(outFbo: Float16FrameBuffer) {
blurtex0.dispose() blurtex0.dispose()
@@ -710,7 +711,7 @@ object IngameRenderer : Disposable {
} }
} }
fun processKawaseBlur(outFbo: FloatFrameBuffer) { fun processKawaseBlur(outFbo: Float16FrameBuffer) {
blurtex0.dispose() blurtex0.dispose()
@@ -807,12 +808,12 @@ object IngameRenderer : Disposable {
//fboBlurQuarter.dispose() //fboBlurQuarter.dispose()
} }
fboRGB = FloatFrameBuffer(width, height, false) fboRGB = Float16FrameBuffer(width, height, false)
fboRGB_lightMixed = FloatFrameBuffer(width, height, false) fboRGB_lightMixed = Float16FrameBuffer(width, height, false)
fboA = FloatFrameBuffer(width, height, false) fboA = Float16FrameBuffer(width, height, false)
fboA_lightMixed = FloatFrameBuffer(width, height, false) fboA_lightMixed = Float16FrameBuffer(width, height, false)
fboMixedOut = FloatFrameBuffer(width, height, false) fboMixedOut = Float16FrameBuffer(width, height, false)
lightmapFbo = FloatFrameBuffer( lightmapFbo = Float16FrameBuffer(
LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt(), LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt(),
LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt(), LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt(),
false false
@@ -823,13 +824,13 @@ object IngameRenderer : Disposable {
blurTex = TextureRegion() blurTex = TextureRegion()
mixedOutTex = TextureRegion(fboMixedOut.colorBufferTexture) mixedOutTex = TextureRegion(fboMixedOut.colorBufferTexture)
fboBlurHalf = FloatFrameBuffer( fboBlurHalf = Float16FrameBuffer(
LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2, LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2,
LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2, LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2,
false false
) )
/*fboBlurQuarter = FloatFrameBuffer( /*fboBlurQuarter = Float16FrameBuffer(
LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 4, LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 4,
LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 4, LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 4,
false false

View File

@@ -8,7 +8,7 @@ import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FloatFrameBuffer import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.* import net.torvald.terrarum.*
@@ -147,7 +147,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
internal lateinit var uiRemoCon: UIRemoCon internal lateinit var uiRemoCon: UIRemoCon
internal lateinit var uiFakeBlurOverlay: UICanvas internal lateinit var uiFakeBlurOverlay: UICanvas
private lateinit var worldFBO: FloatFrameBuffer private lateinit var worldFBO: Float16FrameBuffer
private val warning32bitJavaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/32_bit_warning.tga"))) private val warning32bitJavaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/32_bit_warning.tga")))
private val warningAppleRosettaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/apple_rosetta_warning.tga"))) private val warningAppleRosettaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/apple_rosetta_warning.tga")))
@@ -166,12 +166,12 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
//ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader) //ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader)
val world = ReadSimpleWorld(reader, file) val world = ReadSimpleWorld(reader, file)
demoWorld = world demoWorld = world
demoWorld.worldTime.timeDelta = 330 // a year = 8 minutes demoWorld.worldTime.timeDelta = 30
printdbg(this, "Demo world loaded") printdbg(this, "Demo world loaded")
} }
catch (e: IOException) { catch (e: IOException) {
demoWorld = GameWorld(LandUtil.CHUNK_W, LandUtil.CHUNK_H, 0L, 0L) demoWorld = GameWorld(LandUtil.CHUNK_W, LandUtil.CHUNK_H, 0L, 0L)
demoWorld.worldTime.timeDelta = 60 demoWorld.worldTime.timeDelta = 30
printdbg(this, "Demo world not found, using empty world") printdbg(this, "Demo world not found, using empty world")
} }
@@ -210,6 +210,8 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
IngameRenderer.setRenderedWorld(demoWorld) IngameRenderer.setRenderedWorld(demoWorld)
WeatherMixer.internalReset()
WeatherMixer.titleScreenInitWeather()
// load a half-gradient texture that would be used throughout the titlescreen and its sub UIs // load a half-gradient texture that would be used throughout the titlescreen and its sub UIs
@@ -262,7 +264,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
Gdx.input.inputProcessor = TitleScreenController(this) Gdx.input.inputProcessor = TitleScreenController(this)
worldFBO = FloatFrameBuffer(App.scr.width, App.scr.height, false) worldFBO = Float16FrameBuffer(App.scr.width, App.scr.height, false)
// load list of savegames // load list of savegames
printdbg(this, "update list of savegames") printdbg(this, "update list of savegames")
@@ -285,20 +287,15 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
} }
private val updateScreen = { delta: Float -> private val updateScreen = { delta: Float ->
// TODO: desynched weather and time-of-day change
val forcedTime = 32880 // 9h08m
demoWorld.globalLight = WeatherMixer.globalLightNow demoWorld.globalLight = WeatherMixer.globalLightNow
// demoWorld.globalLight = WeatherMixer.getGlobalLightOfTimeOfNoon()
demoWorld.updateWorldTime(delta) demoWorld.updateWorldTime(delta)
// WeatherMixer.update(delta, cameraPlayer, demoWorld) WeatherMixer.update(delta, cameraPlayer, demoWorld)
WeatherMixer.forceTimeAt = forcedTime
cameraPlayer.update(delta) cameraPlayer.update(delta)
// worldcamera update AFTER cameraplayer in this case; the other way is just an exception for actual ingame SFX // worldcamera update AFTER cameraplayer in this case; the other way is just an exception for actual ingame SFX
WorldCamera.update(demoWorld, cameraPlayer) WorldCamera.update(demoWorld, cameraPlayer)
// update UIs // // update UIs //
uiContainer.forEach { it?.update(delta) } uiContainer.forEach { it?.update(delta) }
} }

View File

@@ -14,16 +14,21 @@ import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
*/ */
internal object Inventory : ConsoleCommand { internal object Inventory : ConsoleCommand {
private var targetID: ActorID = INGAME.actorNowPlaying?.referenceID ?: PLAYER_REF_ID private var targetID: ActorID = 0
private fun tryTargetActivePlayer() {
targetID = INGAME.actorNowPlaying?.referenceID ?: 0
}
override fun execute(args: Array<String>) { override fun execute(args: Array<String>) {
if (args.size == 1) { if (args.size == 1) {
printUsage() printUsage()
} }
else if (args[1] == "target") { else if (args[1] == "target") {
targetID = if (args[2].lowercase() == "player") (INGAME.actorNowPlaying?.referenceID ?: PLAYER_REF_ID) else args[2].toInt() if (args[2].lowercase() == "player") tryTargetActivePlayer() else targetID = args[2].toInt()
} }
else { else {
if (targetID == 0) tryTargetActivePlayer()
val actor = getActor() val actor = getActor()
if (actor != null) { if (actor != null) {
when (args[1]) { when (args[1]) {
@@ -36,7 +41,7 @@ internal object Inventory : ConsoleCommand {
} }
} }
else { else {
Echo("Actor $targetID is not Pocketed or does not exists") Echo("Actor $targetID is not Pocketed or does not exist")
} }
} }
} }

View File

@@ -0,0 +1,22 @@
package net.torvald.terrarum.modulebasegame.console
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.ccG
import net.torvald.terrarum.ccY
import net.torvald.terrarum.console.ConsoleCommand
import net.torvald.terrarum.console.Echo
/**
* Created by minjaesong on 2023-08-22.
*/
object Uuid : ConsoleCommand {
override fun execute(args: Array<String>) {
val worldUUID = INGAME.world.worldIndex
val playerUUID = INGAME.actorGamer.uuid
Echo("${ccY}World UUID: ${ccG}$worldUUID")
Echo("${ccY}Player UUID: ${ccG}$playerUUID")
}
override fun printUsage() {
}
}

View File

@@ -12,7 +12,6 @@ import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gameactors.*
import net.torvald.terrarum.gameactors.faction.Faction import net.torvald.terrarum.gameactors.faction.Faction
import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import org.dyn4j.geometry.Vector2 import org.dyn4j.geometry.Vector2
@@ -116,6 +115,8 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
@Transient const val SPRITE_ROW_IDLE = 0 @Transient const val SPRITE_ROW_IDLE = 0
@Transient const val SPRITE_ROW_WALK = 1 @Transient const val SPRITE_ROW_WALK = 1
@Transient const val downDownMinLengthBase = 12
} }
//////////////////////////////// ////////////////////////////////
@@ -173,7 +174,7 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
var isUpDown = false; protected set var isUpDown = false; protected set
var isDownDown = false; protected set var isDownDown = false; protected set
var downDownVirtually = false; internal set var downButtonHeld = 0; internal set
var isLeftDown = false; protected set var isLeftDown = false; protected set
var isRightDown = false; protected set var isRightDown = false; protected set
var isJumpDown = false; protected set var isJumpDown = false; protected set
@@ -221,7 +222,7 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
if (isNoClip) { if (isNoClip) {
//grounded = true //grounded = true
// platformToIgnore = null // platformToIgnore = null
downDownVirtually = false downButtonHeld = 0
} }
// reset control box of AI // reset control box of AI
@@ -248,13 +249,16 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
} }
} }
private val downDownMinLength: Int
get() = (downDownMinLengthBase * (gravitation.y.abs() / 9.8).sqrt()).ceilToInt()
private fun updateGamerControlBox() { private fun updateGamerControlBox() {
if (isGamer) { if (isGamer) {
isUpDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_up")) isUpDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_up"))
isLeftDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_left")) isLeftDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_left"))
isDownDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_down")) isDownDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_down"))
isRightDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_right")) isRightDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_right"))
isJumpDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_jump")) isJumpDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_jump"))
val gamepad = App.gamepad val gamepad = App.gamepad
@@ -264,7 +268,7 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
axisRX = gamepad.getAxis(App.getConfigInt("control_gamepad_axisrx")) axisRX = gamepad.getAxis(App.getConfigInt("control_gamepad_axisrx"))
axisRY = gamepad.getAxis(App.getConfigInt("control_gamepad_axisry")) axisRY = gamepad.getAxis(App.getConfigInt("control_gamepad_axisry"))
isJumpDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_jump")) || isJumpDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_jump")) ||
gamepad.getButton(App.getConfigInt("control_gamepad_ltrigger")) gamepad.getButton(App.getConfigInt("control_gamepad_ltrigger"))
} }
@@ -288,17 +292,14 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
} }
// platform-related hacks // platform-related hacks
// allow latching down downDownVirtually only when standing on a platform AND not jumping upwards if (isDownDown || downButtonHeld in 1 until downDownMinLength) {
val occupyingTileHasPlatform = bodyTiles.filterNotNull().any { it.isPlatform } downButtonHeld += 1
val feetTileHasPlatform = feetTiles.filterNotNull().any { it.isPlatform }
val feetTileIsAllPlatform = feetTiles.filterNotNull().all { it.isPlatform }
if (isDownDown && feetTileIsAllPlatform && (controllerV?.y ?: 0.0) >= 0.0) {// ||
// occupyingTileHasPlatform && !feetTileHasPlatform) { // FIXME commenting this out enables platform-ladder but falldown gets slowed down if the body passes thru the platform but I think this behav might be beneficial for player?
downDownVirtually = true
} }
if (downDownVirtually && !occupyingTileHasPlatform && !feetTileIsAllPlatform) { else {
downDownVirtually = false downButtonHeld = 0
} }
// TODO just disable "snap to ground" on collision solver if {player's body overlaps with the platform/downDownVirtually}?
// the point is: disable snap (or don't consider offending tiles as solid) for certain Y-pos only, tiles on Y+1 are still solid
} }
private inline val hasController: Boolean private inline val hasController: Boolean
@@ -347,11 +348,11 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
// ↑F, ↓S // ↑F, ↓S
if (isRightDown && !isLeftDown) { if (isRightDown && !isLeftDown) {
walkHorizontal(false, AXIS_KEYBOARD) walkHorizontal(false, AXIS_KEYBOARD)
prevHMoveKey = App.getConfigInt("control_key_right") prevHMoveKey = ControlPresets.getKey("control_key_right")
} // ↓F, ↑S } // ↓F, ↑S
else if (isLeftDown && !isRightDown) { else if (isLeftDown && !isRightDown) {
walkHorizontal(true, AXIS_KEYBOARD) walkHorizontal(true, AXIS_KEYBOARD)
prevHMoveKey = App.getConfigInt("control_key_left") prevHMoveKey = ControlPresets.getKey("control_key_left")
} // ↓F, ↓S } // ↓F, ↓S
/*else if (isLeftDown && isRightDown) { /*else if (isLeftDown && isRightDown) {
if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) { if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) {
@@ -375,11 +376,11 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
// ↑E, ↓D // ↑E, ↓D
if (isDownDown && !isUpDown) { if (isDownDown && !isUpDown) {
walkVertical(false, AXIS_KEYBOARD) walkVertical(false, AXIS_KEYBOARD)
prevVMoveKey = App.getConfigInt("control_key_down") prevVMoveKey = ControlPresets.getKey("control_key_down")
} // ↓E, ↑D } // ↓E, ↑D
else if (isUpDown && !isDownDown) { else if (isUpDown && !isDownDown) {
walkVertical(true, AXIS_KEYBOARD) walkVertical(true, AXIS_KEYBOARD)
prevVMoveKey = App.getConfigInt("control_key_up") prevVMoveKey = ControlPresets.getKey("control_key_up")
} // ↓E, ↓D } // ↓E, ↓D
/*else if (isUpDown && isDownDown) { /*else if (isUpDown && isDownDown) {
if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) { if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) {

View File

@@ -248,7 +248,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
forEachBlockbox { x, y, _, _ -> forEachBlockbox { x, y, _, _ ->
if (!hasCollision) { if (!hasCollision) {
val tile = world!!.getTileFromTerrain(x, y) val tile = world!!.getTileFromTerrain(x, y)
if (BlockCodex[tile].isSolid || BlockCodex[tile].isActorBlock) { if (!BlockCodex[tile].hasTag("INCONSEQUENTIAL")) {
hasCollision = true hasCollision = true
} }
} }
@@ -311,7 +311,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
forEachBlockbox { x, y, _, _ -> forEachBlockbox { x, y, _, _ ->
if (!hasCollision) { if (!hasCollision) {
val tile = world!!.getTileFromTerrain(x, y) val tile = world!!.getTileFromTerrain(x, y)
if (BlockCodex[tile].isSolid || BlockCodex[tile].isActorBlock) { if (!BlockCodex[tile].hasTag("INCONSEQUENTIAL")) {
hasCollision = true hasCollision = true
} }
} }

View File

@@ -0,0 +1,41 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarum.modulebasegame.ui.UIWallCalendar
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2023-08-08.
*/
class FixtureWallCalendar : FixtureBase {
constructor() : super(
BlockBox(BlockBox.NO_COLLISION, 1, 1),
nameFun = { Lang["ITEM_CALENDAR"] },
mainUI = UIWallCalendar()
) {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("basegame", "sprites/fixtures/calendar.tga")
density = 600.0
setHitboxDimension(TILE_SIZE, TILE_SIZE, 0, 1)
makeNewSprite(TextureRegionPack(itemImage.texture, TILE_SIZE, TILE_SIZE)).let {
it.setRowsAndFrames(1,1)
}
actorValue[AVKey.BASEMASS] = 1.0
}
override var tooltipText: String?
get() = Lang.getAndUseTemplate("CONTEXT_CALENDAR_DATE_FORMAT_YMD_DDD", false,
world!!.worldTime.years,
world!!.worldTime.getMonthNameFull(),
world!!.worldTime.calendarDay,
world!!.worldTime.getDayNameFull()
)//INGAME.world.worldTime.getFormattedCalendarDay()
set(value) {}
}

View File

@@ -51,7 +51,7 @@ object BlockBase {
// return false if there is a "solid" tile already // return false if there is a "solid" tile already
if (isWall && BlockCodex[wallUnderCursor].isSolid || if (isWall && BlockCodex[wallUnderCursor].isSolid ||
!isWall && (BlockCodex[terrainUnderCursor].isSolid || BlockCodex[terrainUnderCursor].isActorBlock)) !isWall && !BlockCodex[terrainUnderCursor].hasTag("INCONSEQUENTIAL"))
return@mouseInInteractableRange -1L return@mouseInInteractableRange -1L
// filter passed, do the job // filter passed, do the job

View File

@@ -0,0 +1,29 @@
package net.torvald.terrarum.modulebasegame.gameitems
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.gameitems.ItemID
/**
* Created by minjaesong on 2023-08-08.
*/
class ItemWallCalendar(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulebasegame.gameactors.FixtureWallCalendar") {
override var dynamicID: ItemID = originalID
override val originalName = "ITEM_CALENDAR"
override var baseMass = 1.0
override var stackable = true
override var inventoryCategory = Category.MISC
override val isUnique = false
override val isDynamic = false
override val materialId = ""
override val itemImage: TextureRegion
get() = getItemImageFromSingleImage("basegame", "sprites/fixtures/calendar.tga")
override var baseToolSize: Double? = baseMass
init {
equipPosition = EquipPosition.HAND_GRIP
}
}

View File

@@ -133,7 +133,7 @@ class QuickSingleplayerWorldSavingThread(
// Write Actors // // Write Actors //
actorsList.forEachIndexed { count, it -> actorsList.forEachIndexed { count, it ->
printdbg(this, "Writing actors... ${count+1}/${actorsList.size}") printdbg(this, "Writing actors... ${count+1}/${actorsList.size} (${it.javaClass.canonicalName})")
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it)) val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent) val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent)

View File

@@ -56,10 +56,10 @@ object ControlPanelCommon {
val initialSel = optionsList.indexOf(App.getConfigString(optionName)) val initialSel = optionsList.indexOf(App.getConfigString(optionName))
println("labelFuns = ${labelFuns.map { it.invoke() }}") // println("labelFuns = ${labelFuns.map { it.invoke() }}")
println("optionsList = $optionsList") // println("optionsList = $optionsList")
println("optionName = $optionName; value = ${App.getConfigString(optionName)}") // println("optionName = $optionName; value = ${App.getConfigString(optionName)}")
println("initialSel = $initialSel") // println("initialSel = $initialSel")
if (initialSel < 0) throw IllegalArgumentException("config value '${App.getConfigString(optionName)}' for option '$optionName' is not found on the options list") if (initialSel < 0) throw IllegalArgumentException("config value '${App.getConfigString(optionName)}' for option '$optionName' is not found on the options list")
@@ -69,6 +69,19 @@ object ControlPanelCommon {
} }
} }
} }
else if (args.startsWith("spinnersel,")) {
val labelFuns = arg.subList(1, arg.size).map { { it } }
val optionsList = arg.subList(1, arg.size).map { it.toInt() }
val initialSel = optionsList.indexOf(App.getConfigInt(optionName))
if (initialSel < 0) throw IllegalArgumentException("config value '${App.getConfigInt(optionName)}' for option '$optionName' is not found on the options list")
UIItemTextSelector(parent, x, y, labelFuns, initialSel, CONFIG_SPINNER_WIDTH, clickToShowPalette = false, useSpinnerButtons = true) to { it: UIItem, optionStr: String ->
(it as UIItemTextSelector).selectionChangeListener = {
App.setConfig(optionStr, optionsList[it])
}
}
}
else if (args.startsWith("spinner,")) { else if (args.startsWith("spinner,")) {
UIItemSpinner(parent, x, y, App.getConfigInt(optionName), arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), CONFIG_SPINNER_WIDTH, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String -> UIItemSpinner(parent, x, y, App.getConfigInt(optionName), arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), CONFIG_SPINNER_WIDTH, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String ->
(it as UIItemSpinner).selectionChangeListener = { (it as UIItemSpinner).selectionChangeListener = {

View File

@@ -17,6 +17,10 @@ import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
*/ */
class UIBasicInfo() : UICanvas() { class UIBasicInfo() : UICanvas() {
init {
handler.allowESCtoClose = false
}
val player: ActorHumanoid? val player: ActorHumanoid?
get() = Terrarum.ingame?.actorNowPlaying get() = Terrarum.ingame?.actorNowPlaying
@@ -42,7 +46,7 @@ class UIBasicInfo() : UICanvas() {
ELuptimer += delta ELuptimer += delta
} }
if (mouseUp || Gdx.input.isKeyPressed(App.getConfigInt("control_key_interact"))) { if (mouseUp || Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_interact"))) {
ELuptimer = 0f ELuptimer = 0f
ELon = true ELon = true
} }

View File

@@ -14,6 +14,10 @@ import net.torvald.terrarum.ui.UICanvas
*/ */
class UICheatDetected : UICanvas() { class UICheatDetected : UICanvas() {
init {
handler.allowESCtoClose = false
}
override var width: Int override var width: Int
get() = App.scr.width get() = App.scr.width
set(value) { throw UnsupportedOperationException() } set(value) { throw UnsupportedOperationException() }

View File

@@ -78,7 +78,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
private val controlHelp: String private val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}" "${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
else else
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}\u3000 " + "$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
"$gamepadLabelLEFTRIGHT ${Lang["GAME_OBJECTIVE_MULTIPLIER"]}\u3000 " + "$gamepadLabelLEFTRIGHT ${Lang["GAME_OBJECTIVE_MULTIPLIER"]}\u3000 " +
@@ -125,7 +125,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
// If the player has the required item, use it; otherwise, will take an item from the ItemCodex // If the player has the required item, use it; otherwise, will take an item from the ItemCodex
player.itemList.filter { (itm, qty) -> player.itemList.filter { (itm, qty) ->
ItemCodex[itm]?.tags?.contains(ingredient.key) == true && qty >= ingredient.qty ItemCodex[itm]?.tags?.contains(ingredient.key) == true && qty >= ingredient.qty
}.maxByOrNull { it.qty }?.itm ?: ((ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.tags.contains(ingredient.key)) it.key else null }) ?: throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed")) }.maxByOrNull { it.qty }?.itm ?: ((ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.hasTag(ingredient.key)) it.key else null }) ?: throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed"))
} }
else { else {
ingredient.key ingredient.key
@@ -234,7 +234,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
// don't rely on highlightedness of the button to determine the item on the button is the selected // don't rely on highlightedness of the button to determine the item on the button is the selected
// ingredient (because I don't fully trust my code lol) // ingredient (because I don't fully trust my code lol)
val targetItemToAlter = recipe.ingredients.filter { // altering recipe doesn't make sense if player selected a recipe that requires no tag-ingredients val targetItemToAlter = recipe.ingredients.filter { // altering recipe doesn't make sense if player selected a recipe that requires no tag-ingredients
(it.keyMode == CraftingCodex.CraftingItemKeyMode.TAG && gameItem.tags.contains(it.key)) (it.keyMode == CraftingCodex.CraftingItemKeyMode.TAG && gameItem.hasTag(it.key))
}.let { }.let {
if (it.size > 1) if (it.size > 1)
println("[UICrafting] Your recipe seems to have two similar ingredients defined\n" + println("[UICrafting] Your recipe seems to have two similar ingredients defined\n" +
@@ -245,7 +245,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
targetItemToAlter?.let { targetItemToAlter?.let {
val oldItem = _getItemListIngredients().getInventory().itemList.first { itemPair -> val oldItem = _getItemListIngredients().getInventory().itemList.first { itemPair ->
(it.keyMode == CraftingCodex.CraftingItemKeyMode.TAG && ItemCodex[itemPair.itm]!!.tags.contains(it.key)) (it.keyMode == CraftingCodex.CraftingItemKeyMode.TAG && ItemCodex[itemPair.itm]!!.hasTag(it.key))
} }
changeIngredient(oldItem, itemID) changeIngredient(oldItem, itemID)
refreshCraftButtonStatus() refreshCraftButtonStatus()
@@ -279,7 +279,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
// If the player has the required item, use it; otherwise, will take an item from the ItemCodex // If the player has the required item, use it; otherwise, will take an item from the ItemCodex
val selectedItem = playerInventory.itemList.filter { (itm, qty) -> val selectedItem = playerInventory.itemList.filter { (itm, qty) ->
ItemCodex[itm]?.tags?.contains(ingredient.key) == true && qty >= ingredient.qty ItemCodex[itm]?.tags?.contains(ingredient.key) == true && qty >= ingredient.qty
}.maxByOrNull { it.qty }?.itm ?: ((ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.tags.contains(ingredient.key)) it.key else null }) ?: throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed")) }.maxByOrNull { it.qty }?.itm ?: ((ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.hasTag(ingredient.key)) it.key else null }) ?: throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed"))
// printdbg(this, "Adding ingredients by tag ${selectedItem} (${ingredient.qty})") // printdbg(this, "Adding ingredients by tag ${selectedItem} (${ingredient.qty})")
selectedItem selectedItem

View File

@@ -24,18 +24,18 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
init { init {
ControlPanelCommon.register(this, width, "basegame.graphicscontrolpanel", arrayOf( ControlPanelCommon.register(this, width, "basegame.graphicscontrolpanel", arrayOf(
arrayOf("", { Lang["CREDITS_VFX"] }, "h1"), arrayOf("", { Lang["CREDITS_VFX"] }, "h1"),
arrayOf("fx_dither", { Lang["MENU_OPTIONS_DITHER"] }, "toggle"), arrayOf("fx_dither", { Lang["MENU_OPTIONS_DITHER"] }, "toggle"),
arrayOf("fx_backgroundblur", { Lang["MENU_OPTIONS_BLUR"] }, "toggle"), arrayOf("fx_backgroundblur", { Lang["MENU_OPTIONS_BLUR"] }, "toggle"),
arrayOf("maxparticles", { Lang["MENU_OPTIONS_PARTICLES"] }, "spinner,256,1024,256"), arrayOf("maxparticles", { Lang["MENU_OPTIONS_PARTICLES"] }, "spinner,256,1024,256"),
arrayOf("", { Lang["MENU_OPTIONS_DISPLAY"] }, "h1"), arrayOf("", { Lang["MENU_OPTIONS_DISPLAY"] }, "h1"),
arrayOf("screenwidth,screenheight", { Lang["MENU_OPTIONS_RESOLUTION"] }, "typeinres"), arrayOf("screenwidth,screenheight", { Lang["MENU_OPTIONS_RESOLUTION"] }, "typeinres"),
arrayOf("screenmagnifying", { Lang["GAME_ACTION_ZOOM"] }, "spinnerd,1.0,2.0,0.05"), arrayOf("screenmagnifying", { Lang["GAME_ACTION_ZOOM"] }, "spinnerd,1.0,2.0,0.05"),
arrayOf("screenmagnifyingfilter", { Lang["MENU_OPTIONS_FILTERING_MODE"] }, "textsel,none=MENU_OPTIONS_NONE,bilinear=MENU_OPTIONS_FILTERING_BILINEAR,hq2x=MENU_OPTIONS_FILTERING_HQ2X_DNT"), arrayOf("screenmagnifyingfilter", { Lang["MENU_OPTIONS_FILTERING_MODE"] }, "textsel,none=MENU_OPTIONS_NONE,bilinear=MENU_OPTIONS_FILTERING_BILINEAR,hq2x=MENU_OPTIONS_FILTERING_HQ2X_DNT"),
arrayOf("displayfps", { Lang["MENU_LABEL_FRAMESPERSEC"] }, "spinner,0,300,2"), arrayOf("displayfps", { Lang["MENU_LABEL_FRAMESPERSEC"] }, "spinner,0,300,2"),
arrayOf("usevsync", { Lang["MENU_OPTIONS_VSYNC"] }, "toggle"), arrayOf("usevsync", { Lang["MENU_OPTIONS_VSYNC"] }, "toggle"),
arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"), arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"),
arrayOf("", { Lang["MENU_LABEL_STREAMING"] }, "h1"), arrayOf("", { Lang["MENU_LABEL_STREAMING"] }, "h1"),
arrayOf("fx_streamerslayout", { Lang["MENU_OPTIONS_STREAMERS_LAYOUT"] }, "toggle"), arrayOf("fx_streamerslayout", { Lang["MENU_OPTIONS_STREAMERS_LAYOUT"] }, "toggle"),
)) ))
} }

View File

@@ -8,6 +8,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.unicode.EMDASH import net.torvald.unicode.EMDASH
import net.torvald.terrarum.App import net.torvald.terrarum.App
import net.torvald.terrarum.CommonResourcePool import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ControlPresets
import net.torvald.terrarum.gamecontroller.* import net.torvald.terrarum.gamecontroller.*
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.linearSearch import net.torvald.terrarum.linearSearch
@@ -318,7 +319,7 @@ private class UIItemInputKeycap(
else if (parent.shiftin && keysymsLow[1]?.isNotEmpty() == true) keysymsLow[1] else if (parent.shiftin && keysymsLow[1]?.isNotEmpty() == true) keysymsLow[1]
else keysymsLow[0]) ?: "" else keysymsLow[0]) ?: ""
val keysym0: Array<String?> = if (KeyToggler.isOn(App.getConfigInt("control_key_toggleime"))) { val keysym0: Array<String?> = if (KeyToggler.isOn(ControlPresets.getKey("control_key_toggleime"))) {
if (parent.highlayer == null) arrayOf(keysymLow,keysymLow,keysymLow,keysymLow) if (parent.highlayer == null) arrayOf(keysymLow,keysymLow,keysymLow,keysymLow)
else { else {
val keysyms = parent.highlayer!!.config.symbols val keysyms = parent.highlayer!!.config.symbols

View File

@@ -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
}
}

View File

@@ -3,7 +3,6 @@ package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.* import net.torvald.terrarum.App.*
@@ -19,7 +18,7 @@ import net.torvald.unicode.*
* Created by minjaesong on 2017-10-21. * Created by minjaesong on 2017-10-21.
*/ */
class UIInventoryFull( class UIInventoryFull(
toggleKeyLiteral: Int? = App.getConfigInt("control_key_inventory"), toggleButtonLiteral: Int? = App.getConfigInt("control_gamepad_start"), toggleKeyLiteral: String? = "control_key_inventory", toggleButtonLiteral: String? = "control_gamepad_start",
// UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int)) // UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int))
customPositioning: Boolean = false, // mainly used by vital meter customPositioning: Boolean = false, // mainly used by vital meter
doNotWarnConstant: Boolean = false doNotWarnConstant: Boolean = false
@@ -157,16 +156,15 @@ class UIInventoryFull(
//val INVENTORY_CELLS_OFFSET_Y: Int = 107 + (AppLoader.terrarumAppConfig.screenH - internalHeight) / 2 //val INVENTORY_CELLS_OFFSET_Y: Int = 107 + (AppLoader.terrarumAppConfig.screenH - internalHeight) / 2
init { init {
handler.allowESCtoClose = true
} }
private val SP = "\u3000 " private val SP = "\u3000 "
val listControlHelp: String val listControlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}$SP" + "${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}$SP" +
"$KEYCAP_LEFT_MOUSE ${Lang["GAME_INVENTORY_USE"]}$SP" + "$KEYCAP_LEFT_MOUSE ${Lang["GAME_INVENTORY_USE"]}$SP" +
"$KEYCAP_1$ENDASH\u2009$KEYCAP_0 ${Lang["GAME_INVENTORY_REGISTER"]}$SP" + "$KEYCAP_1$ENDASH\u2009$KEYCAP_0 ${Lang["GAME_INVENTORY_REGISTER"]}$SP" +
"${getKeycapPC(App.getConfigInt("control_key_discard"))} ${Lang["GAME_INVENTORY_DROP"]}" "${getKeycapPC(ControlPresets.getKey("control_key_discard"))} ${Lang["GAME_INVENTORY_DROP"]}"
else else
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" + "$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" +
"$gamepadLabelLT ${Lang["CONTEXT_ITEM_MAP"]}$SP" + "$gamepadLabelLT ${Lang["CONTEXT_ITEM_MAP"]}$SP" +
@@ -176,7 +174,7 @@ class UIInventoryFull(
"$gamepadLabelEast ${Lang["GAME_INVENTORY_DROP"]}" "$gamepadLabelEast ${Lang["GAME_INVENTORY_DROP"]}"
val minimapControlHelp: String val minimapControlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}$SP" + "${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}$SP" +
"$KEYCAP_LEFT_MOUSE$KEYCAP_MOVE ${Lang["GAME_ACTION_MOVE_VERB"]}" "$KEYCAP_LEFT_MOUSE$KEYCAP_MOVE ${Lang["GAME_ACTION_MOVE_VERB"]}"
else else
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" + "$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" +
@@ -184,7 +182,7 @@ class UIInventoryFull(
"$gamepadLabelRT ${Lang["GAME_INVENTORY"]}" "$gamepadLabelRT ${Lang["GAME_INVENTORY"]}"
val gameMenuControlHelp: String val gameMenuControlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}" "${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
else else
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" + "$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" +
"$gamepadLabelLT ${Lang["GAME_INVENTORY"]}" "$gamepadLabelLT ${Lang["GAME_INVENTORY"]}"
@@ -250,7 +248,7 @@ class UIInventoryFull(
} }
// allow "control_key_gamemenu" to open this UI and bring system menu immediately // allow "control_key_gamemenu" to open this UI and bring system menu immediately
this.handler.toggleKeyExtra.add { App.getConfigInt("control_key_gamemenu") } this.handler.toggleKeyExtra.add("control_key_gamemenu" )
this.handler.toggleKeyExtraAction.add { this.handler.toggleKeyExtraAction.add {
if (it.isClosed) { if (it.isClosed) {
INGAME.setTooltipMessage(null) INGAME.setTooltipMessage(null)
@@ -264,7 +262,7 @@ class UIInventoryFull(
} }
// allow "control_key_crafting" to open this UI and bring system menu immediately // allow "control_key_crafting" to open this UI and bring system menu immediately
this.handler.toggleKeyExtra.add { App.getConfigInt("control_key_crafting") } this.handler.toggleKeyExtra.add("control_key_crafting")
this.handler.toggleKeyExtraAction.add { this.handler.toggleKeyExtraAction.add {
if (it.isClosed) { if (it.isClosed) {
INGAME.setTooltipMessage(null) INGAME.setTooltipMessage(null)

View File

@@ -6,9 +6,7 @@ import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.App import net.torvald.terrarum.*
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.DefaultConfig
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.* import net.torvald.terrarum.ui.*
@@ -37,16 +35,16 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
private val keycaps = hashMapOf( private val keycaps = hashMapOf(
Input.Keys.GRAVE to UIItemKeycap(this, 1, 1, null, oneu, ""), Input.Keys.GRAVE to UIItemKeycap(this, 1, 1, null, oneu, ""),
Input.Keys.NUM_1 to UIItemKeycap(this, 33,1, Input.Keys.NUM_1, oneu, "1,3"), Input.Keys.NUM_1 to UIItemKeycap(this, 33,1, null, oneu, "1,3"),
Input.Keys.NUM_2 to UIItemKeycap(this, 65,1, Input.Keys.NUM_2, oneu, "2,3"), Input.Keys.NUM_2 to UIItemKeycap(this, 65,1, null, oneu, "2,3"),
Input.Keys.NUM_3 to UIItemKeycap(this, 97,1, Input.Keys.NUM_3, oneu, "3,3"), Input.Keys.NUM_3 to UIItemKeycap(this, 97,1, null, oneu, "3,3"),
Input.Keys.NUM_4 to UIItemKeycap(this, 129,1, Input.Keys.NUM_4, oneu, "4,3"), Input.Keys.NUM_4 to UIItemKeycap(this, 129,1, null, oneu, "4,3"),
Input.Keys.NUM_5 to UIItemKeycap(this, 161,1, Input.Keys.NUM_5, oneu, "5,3"), Input.Keys.NUM_5 to UIItemKeycap(this, 161,1, null, oneu, "5,3"),
Input.Keys.NUM_6 to UIItemKeycap(this, 193,1, Input.Keys.NUM_6, oneu, "6,3"), Input.Keys.NUM_6 to UIItemKeycap(this, 193,1, null, oneu, "6,3"),
Input.Keys.NUM_7 to UIItemKeycap(this, 225,1, Input.Keys.NUM_7, oneu, "7,3"), Input.Keys.NUM_7 to UIItemKeycap(this, 225,1, null, oneu, "7,3"),
Input.Keys.NUM_8 to UIItemKeycap(this, 257,1, Input.Keys.NUM_8, oneu, "8,3"), Input.Keys.NUM_8 to UIItemKeycap(this, 257,1, null, oneu, "8,3"),
Input.Keys.NUM_9 to UIItemKeycap(this, 289,1, Input.Keys.NUM_9, oneu, "9,3"), Input.Keys.NUM_9 to UIItemKeycap(this, 289,1, null, oneu, "9,3"),
Input.Keys.NUM_0 to UIItemKeycap(this, 321,1, Input.Keys.NUM_0, oneu, "0,3"), Input.Keys.NUM_0 to UIItemKeycap(this, 321,1, null, oneu, "0,3"),
Input.Keys.MINUS to UIItemKeycap(this, 353,1, Input.Keys.MINUS, oneu, "10,3"), Input.Keys.MINUS to UIItemKeycap(this, 353,1, Input.Keys.MINUS, oneu, "10,3"),
Input.Keys.EQUALS to UIItemKeycap(this, 385,1, Input.Keys.EQUALS, oneu, "11,3"), Input.Keys.EQUALS to UIItemKeycap(this, 385,1, Input.Keys.EQUALS, oneu, "11,3"),
Input.Keys.BACKSPACE to UIItemKeycap(this, 417,1, Input.Keys.BACKSPACE, 60, "24,5"), Input.Keys.BACKSPACE to UIItemKeycap(this, 417,1, Input.Keys.BACKSPACE, 60, "24,5"),
@@ -105,25 +103,39 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
) // end of keycaps ) // end of keycaps
private val resetButtonWidth = 140 private val resetButtonWidth = 140
private val buttonReset = UIItemTextButton(this, private val presetButtonWidth = 200
/*private val buttonReset = UIItemTextButton(this,
{ Lang["MENU_LABEL_RESET"] }, { Lang["MENU_LABEL_RESET"] },
kbx + (width - resetButtonWidth) / 2, kbx + (width - resetButtonWidth) / 2,
kby + 162 + 12, kby + 162 + 16,
resetButtonWidth, resetButtonWidth,
hasBorder = true, hasBorder = true,
alignment = UIItemTextButton.Companion.Alignment.CENTRE alignment = UIItemTextButton.Companion.Alignment.CENTRE
)*/
private val presetSelector = UIItemTextSelector(this,
kbx + (width - presetButtonWidth) / 2,
kby + 162 + 16,
ControlPresets.presetLabels.map { { it } },
ControlPresets.presetLabels.indexOf(App.getConfigString("control_preset_keyboard")),
presetButtonWidth,
clickToShowPalette = false,
) )
private val controlPalette = UIItemControlPaletteBaloon(this, (Toolkit.drawWidth - 500) / 2, kby + 219) private val controlPalette = UIItemControlPaletteBaloon(this, (Toolkit.drawWidth - 500) / 2, kby + 227)
init { init {
keycaps.values.forEach { addUIitem(it) } keycaps.values.forEach { addUIitem(it) }
updateKeycaps() updateKeycaps()
buttonReset.clickOnceListener = { x, y -> /*buttonReset.clickOnceListener = { x, y ->
resetKeyConfig() resetKeyConfig()
updateKeycaps() updateKeycaps()
}*/
presetSelector.selectionChangeListener = { index ->
App.setConfig("control_preset_keyboard", ControlPresets.presetLabels[index])
updateKeycaps()
} }
// addUIitem(keyboardLayoutSelection) // addUIitem(keyboardLayoutSelection)
@@ -152,18 +164,18 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
private fun updateKeycaps() { private fun updateKeycaps() {
keycaps.values.forEach { it.symbolControl = null } keycaps.values.forEach { it.symbolControl = null }
// read config and put icons. Item order irrelevant // read config and put icons. Item order irrelevant
keycaps[App.getConfigInt("control_key_up")]?.symbolControl = Keebsym.UP keycaps[ControlPresets.getKey("control_key_up")]?.symbolControl = Keebsym.UP
keycaps[App.getConfigInt("control_key_left")]?.symbolControl = Keebsym.LEFT keycaps[ControlPresets.getKey("control_key_left")]?.symbolControl = Keebsym.LEFT
keycaps[App.getConfigInt("control_key_down")]?.symbolControl = Keebsym.DOWN keycaps[ControlPresets.getKey("control_key_down")]?.symbolControl = Keebsym.DOWN
keycaps[App.getConfigInt("control_key_right")]?.symbolControl = Keebsym.RIGHT keycaps[ControlPresets.getKey("control_key_right")]?.symbolControl = Keebsym.RIGHT
keycaps[App.getConfigInt("control_key_jump")]?.symbolControl = Keebsym.JUMP keycaps[ControlPresets.getKey("control_key_jump")]?.symbolControl = Keebsym.JUMP
keycaps[App.getConfigInt("control_key_zoom")]?.symbolControl = Keebsym.ZOOM keycaps[ControlPresets.getKey("control_key_zoom")]?.symbolControl = Keebsym.ZOOM
keycaps[App.getConfigInt("control_key_inventory")]?.symbolControl = Keebsym.INVENTORY keycaps[ControlPresets.getKey("control_key_inventory")]?.symbolControl = Keebsym.INVENTORY
keycaps[App.getConfigInt("control_key_movementaux")]?.symbolControl = Keebsym.HOOK keycaps[ControlPresets.getKey("control_key_movementaux")]?.symbolControl = Keebsym.HOOK
keycaps[App.getConfigInt("control_key_quicksel")]?.symbolControl = Keebsym.PIE keycaps[ControlPresets.getKey("control_key_quicksel")]?.symbolControl = Keebsym.PIE
keycaps[App.getConfigInt("control_key_gamemenu")]?.symbolControl = Keebsym.MENU keycaps[ControlPresets.getKey("control_key_gamemenu")]?.symbolControl = Keebsym.MENU
keycaps[App.getConfigInt("control_key_toggleime")]?.symbolControl = Keebsym.IME() keycaps[ControlPresets.getKey("control_key_toggleime")]?.symbolControl = Keebsym.IME()
keycaps[App.getConfigInt("control_key_crafting")]?.symbolControl = Keebsym.CRAFTING keycaps[ControlPresets.getKey("control_key_crafting")]?.symbolControl = Keebsym.CRAFTING
} }
internal var keycapClicked = -13372 internal var keycapClicked = -13372
@@ -176,14 +188,14 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
override fun updateUI(delta: Float) { override fun updateUI(delta: Float) {
uiItems.forEach { uiItems.forEach {
it.update(delta) it.update(delta)
if (it is UIItemKeycap && it.mousePushed) { if (it is UIItemKeycap && it.mousePushed && ControlPresets.presetLabels[presetSelector.selection] == "Custom") {
it.selected = true it.selected = true
// println("key ${it.key}; selected = ${it.selected}") // println("key ${it.key}; selected = ${it.selected}")
keycapClicked = it.key ?: -13372 keycapClicked = it.key ?: -13372
} }
} }
buttonReset.update(delta) presetSelector.update(delta)
controlPalette.update(delta) controlPalette.update(delta)
} }
@@ -194,7 +206,7 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
// batch.color = fillCol // batch.color = fillCol
// Toolkit.fillArea(batch, drawX, drawY, width, height) // Toolkit.fillArea(batch, drawX, drawY, width, height)
uiItems.forEach { it.render(batch, camera) } uiItems.forEach { it.render(batch, camera) }
buttonReset.render(batch, camera) presetSelector.render(batch, camera)
// title // title
// todo show "Keyboard"/"Gamepad" accordingly // todo show "Keyboard"/"Gamepad" accordingly
@@ -215,21 +227,56 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
} }
fun setControlOf(key: Int, control: Int) { fun setControlOf(key: Int, control: Int) {
if (App.getConfigString("control_preset_keyboard") != "Custom") {
System.err.println("[UIKeyboardControlPanel] cannot set a control if the preset is not 'Custom' (current preset: ${App.getConfigString("control_preset_keyboard")})")
return
}
if (control >= 0) { if (control >= 0) {
App.setConfig(UIItemControlPaletteBaloon.indexToConfigKey[control]!!, key) val controlName = UIItemControlPaletteBaloon.indexToConfigKey[control]!!
val conflicts = App.gameConfig.keySet.filter {
(it as String).startsWith("control_key_")
}.map {
(it as String).let { it to
try { (App.getConfigInt(it) == key) }
catch (_: ClassCastException) { false }
}
}.filter { it.second }.map { it.first }.firstOrNull()
println("[UIKeyboardControlPanel] key=$key, control=$controlName")
if (conflicts != null) {
val oldValue = App.getConfigInt(controlName)
App.setConfig(conflicts, oldValue)
println("[UIKeyboardControlPanel] set config $conflicts=$oldValue")
}
App.setConfig(controlName, key)
println("[UIKeyboardControlPanel] set config $controlName=$key")
} }
updateKeycaps() updateKeycaps()
Terrarum.ingame?.let {
it.onConfigChange()
}
} }
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
super.touchDown(screenX, screenY, pointer, button) super.touchDown(screenX, screenY, pointer, button)
buttonReset.touchDown(screenX, screenY, pointer, button) presetSelector.touchDown(screenX, screenY, pointer, button)
return true return true
} }
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
super.touchUp(screenX, screenY, pointer, button) super.touchUp(screenX, screenY, pointer, button)
buttonReset.touchUp(screenX, screenY, pointer, button) presetSelector.touchUp(screenX, screenY, pointer, button)
return true
}
override fun scrolled(amountX: Float, amountY: Float): Boolean {
super.scrolled(amountX, amountY)
presetSelector.scrolled(amountX, amountY)
return true return true
} }

View File

@@ -141,7 +141,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : Advanceable() {
private val controlHelp: String private val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_up"))}${getKeycapPC(App.getConfigInt("control_key_down"))}" + "${getKeycapPC(ControlPresets.getKey("control_key_up"))}${getKeycapPC(ControlPresets.getKey("control_key_down"))}" +
" ${Lang["MENU_CONTROLS_SCROLL"]}" " ${Lang["MENU_CONTROLS_SCROLL"]}"
else else
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}" "${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
@@ -408,12 +408,12 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : Advanceable() {
if (this.isVisible) { if (this.isVisible) {
val cells = getCells() val cells = getCells()
if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) { if ((keycode == Input.Keys.UP || keycode == ControlPresets.getKey("control_key_up")) && scrollTarget > 0) {
scrollFrom = listScroll scrollFrom = listScroll
scrollTarget -= 1 scrollTarget -= 1
scrollAnimCounter = 0f scrollAnimCounter = 0f
} }
else if ((keycode == Input.Keys.DOWN || keycode == App.getConfigInt("control_key_down")) && scrollTarget < cells.size - savesVisible) { else if ((keycode == Input.Keys.DOWN || keycode == ControlPresets.getKey("control_key_down")) && scrollTarget < cells.size - savesVisible) {
scrollFrom = listScroll scrollFrom = listScroll
scrollTarget += 1 scrollTarget += 1
scrollAnimCounter = 0f scrollAnimCounter = 0f

View File

@@ -25,7 +25,7 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() {
private val controlHelp: String private val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_up"))}${getKeycapPC(App.getConfigInt("control_key_down"))}" + "${getKeycapPC(ControlPresets.getKey("control_key_up"))}${getKeycapPC(ControlPresets.getKey("control_key_down"))}" +
" ${Lang["MENU_CONTROLS_SCROLL"]}" " ${Lang["MENU_CONTROLS_SCROLL"]}"
else else
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}" "${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
@@ -235,7 +235,7 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() {
if (showSpinner) { if (showSpinner) {
val spin = spinner.get(spinnerFrame % 8, spinnerFrame / 8) val spin = spinner.get(spinnerFrame % 8, spinnerFrame / 8)
val offX = UIRemoCon.menubarOffX - UIRemoCon.UIRemoConElement.paddingLeft + 72 + 1 val offX = UIRemoCon.menubarOffX - UIRemoCon.UIRemoConElement.paddingLeft + 72 + 1
val offY = UIRemoCon.menubarOffY - UIRemoCon.UIRemoConElement.lineHeight * 3 + 16 val offY = UIRemoCon.menubarOffY - UIRemoCon.UIRemoConElement.lineHeight * 4 + 16
batch.draw(spin, offX.toFloat(), offY.toFloat()) batch.draw(spin, offX.toFloat(), offY.toFloat())
} }
} }
@@ -249,12 +249,12 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() {
if (this.isVisible) { if (this.isVisible) {
val cells = playerCells val cells = playerCells
if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) { if ((keycode == Input.Keys.UP || keycode == ControlPresets.getKey("control_key_up")) && scrollTarget > 0) {
scrollFrom = listScroll scrollFrom = listScroll
scrollTarget -= 1 scrollTarget -= 1
scrollAnimCounter = 0f scrollAnimCounter = 0f
} }
else if ((keycode == Input.Keys.DOWN || keycode == App.getConfigInt("control_key_down")) && scrollTarget < cells.size - savesVisible) { else if ((keycode == Input.Keys.DOWN || keycode == ControlPresets.getKey("control_key_down")) && scrollTarget < cells.size - savesVisible) {
scrollFrom = listScroll scrollFrom = listScroll
scrollTarget += 1 scrollTarget += 1
scrollAnimCounter = 0f scrollAnimCounter = 0f

View File

@@ -227,8 +227,8 @@ class UILoadManage(val full: UILoadSavegame) : UICanvas() {
} }
MODE_DELETE -> { MODE_DELETE -> {
Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_SAVE_WILL_BE_DELETED"], Toolkit.drawWidth, 0, full.titleTopGradEnd + full.cellInterval - 46) Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_SAVE_WILL_BE_DELETED"], Toolkit.drawWidth, 0, full.titleTopGradEnd + full.cellInterval + SAVE_CELL_HEIGHT + 36)
Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_ARE_YOU_SURE"], Toolkit.drawWidth, 0, full.titleTopGradEnd + full.cellInterval + SAVE_CELL_HEIGHT + 36) Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_ARE_YOU_SURE"], Toolkit.drawWidth, 0, full.titleTopGradEnd + full.cellInterval + SAVE_CELL_HEIGHT + 36 + 24)
delButtons.forEach { it.render(batch, camera) } delButtons.forEach { it.render(batch, camera) }
} }

View File

@@ -90,8 +90,17 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
listOf(NullUI/*, transitionalAutosave*/) listOf(NullUI/*, transitionalAutosave*/)
) )
internal fun queueUpManageScr() { transitionPanel.setCentreUIto(0) } private val nodesForListing = Yaml(UITitleRemoConYaml.injectedMenuSingleCharSel).parse()
internal fun queueUpNewCharScr() { transitionPanel.setCentreUIto(1) } private val nodesForManage = Yaml(UITitleRemoConYaml.injectedMenuSingleSaveManage).parse()
internal fun queueUpManageScr() {
transitionPanel.setCentreUIto(0)
remoCon.setNewRemoConContents(nodesForManage)
}
internal fun queueUpNewCharScr() {
transitionPanel.setCentreUIto(1)
remoCon.setNewRemoConContents(nodesForListing)
}
// internal fun bringAutosaveSelectorUp() { transitionPanel.setRightUIto(1) } // internal fun bringAutosaveSelectorUp() { transitionPanel.setRightUIto(1) }
// internal fun takeAutosaveSelectorDown() { transitionPanel.setRightUIto(0) } // internal fun takeAutosaveSelectorDown() { transitionPanel.setRightUIto(0) }
@@ -102,6 +111,10 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
internal fun changePanelTo(index: Int) { internal fun changePanelTo(index: Int) {
transitionPanel.requestTransition(index) transitionPanel.requestTransition(index)
if (index == 1)
remoCon.setNewRemoConContents(nodesForManage)
else
remoCon.setNewRemoConContents(nodesForListing)
} }
override fun advanceMode(button: UIItem) { override fun advanceMode(button: UIItem) {
@@ -118,6 +131,9 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
// takeAutosaveSelectorDown() // takeAutosaveSelectorDown()
transitionPanel.show() transitionPanel.show()
nodesForListing.parent = remoCon.treeRoot
nodesForManage.parent = remoCon.treeRoot
} }
override fun hide() { override fun hide() {

View File

@@ -26,11 +26,14 @@ class UIPerformanceControlPanel(remoCon: UIRemoCon?) : UICanvas() {
arrayOf("", { Lang["MENU_OPTIONS_GAMEPLAY"] }, "h1"), arrayOf("", { Lang["MENU_OPTIONS_GAMEPLAY"] }, "h1"),
arrayOf("autosaveinterval", { Lang["MENU_OPTIONS_AUTOSAVE"] + " (${Lang["CONTEXT_TIME_MINUTE_PLURAL"]})" }, "spinnerimul,1,120,1,60000"), arrayOf("autosaveinterval", { Lang["MENU_OPTIONS_AUTOSAVE"] + " (${Lang["CONTEXT_TIME_MINUTE_PLURAL"]})" }, "spinnerimul,1,120,1,60000"),
arrayOf("notificationshowuptime", { Lang["MENU_OPTIONS_NOTIFICATION_DISPLAY_DURATION"] + " (${Lang["CONTEXT_TIME_SECOND_PLURAL"]})" }, "spinnerimul,2,10,1,1000"), arrayOf("notificationshowuptime", { Lang["MENU_OPTIONS_NOTIFICATION_DISPLAY_DURATION"] + " (${Lang["CONTEXT_TIME_SECOND_PLURAL"]})" }, "spinnerimul,2,10,1,1000"),
arrayOf("", { Lang["MENU_LABEL_GRAPHICS"] }, "h1"),
arrayOf("atlastexsize", { Lang["MENU_OPTIONS_ATLAS_TEXTURE_SIZE"] }, "spinnersel,1024,2048,4096,8192"),
arrayOf("", { Lang["MENU_LABEL_JVM_DNT"] }, "h1"), arrayOf("", { Lang["MENU_LABEL_JVM_DNT"] }, "h1"),
arrayOf("jvm_xmx", { Lang["MENU_OPTIONS_JVM_HEAP_MAX"] + " (GB)" }, "spinner,2,32,1"), arrayOf("jvm_xmx", { Lang["MENU_OPTIONS_JVM_HEAP_MAX"] + " (GB)" }, "spinner,2,32,1"),
arrayOf("jvm_extra_cmd", { Lang["MENU_LABEL_EXTRA_JVM_ARGUMENTS"] }, "typein"), arrayOf("jvm_extra_cmd", { Lang["MENU_LABEL_EXTRA_JVM_ARGUMENTS"] }, "typein"),
arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"), arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"),
)) )
)
} }
override var height = ControlPanelCommon.getMenuHeight("basegame.performancecontrolpanel") override var height = ControlPanelCommon.getMenuHeight("basegame.performancecontrolpanel")

View File

@@ -18,6 +18,11 @@ import kotlin.math.roundToInt
* Created by minjaesong on 2016-07-20. * Created by minjaesong on 2016-07-20.
*/ */
class UIQuickslotBar : UICanvas() { class UIQuickslotBar : UICanvas() {
init {
handler.allowESCtoClose = false
}
private val cellSize = ItemSlotImageFactory.slotImage.tileW // 38 private val cellSize = ItemSlotImageFactory.slotImage.tileW // 38
private val gutter = 10 - 6 // do -6 to get a gutter size of not-enlarged cells private val gutter = 10 - 6 // do -6 to get a gutter size of not-enlarged cells

View File

@@ -21,6 +21,11 @@ import kotlin.math.roundToInt
* Created by minjaesong on 2016-07-20. * Created by minjaesong on 2016-07-20.
*/ */
class UIQuickslotPie : UICanvas() { class UIQuickslotPie : UICanvas() {
init {
handler.allowESCtoClose = false
}
private val cellSize = ItemSlotImageFactory.slotImage.tileW private val cellSize = ItemSlotImageFactory.slotImage.tileW
private val slotCount = UIQuickslotBar.SLOT_COUNT private val slotCount = UIQuickslotBar.SLOT_COUNT

View File

@@ -18,10 +18,14 @@ import kotlin.math.roundToInt
* Created by minjaesong on 2019-08-11. * Created by minjaesong on 2019-08-11.
*/ */
class UIScreenZoom : UICanvas( class UIScreenZoom : UICanvas(
App.getConfigInt("control_key_zoom") "control_key_zoom"
) { ) {
val zoomText = "${getKeycapPC(handler.toggleKeyLiteral!!)} $EMDASH Zoom Out" init {
handler.allowESCtoClose = false
}
val zoomText = "${getKeycapPC(handler.toggleKey!!)} $EMDASH Zoom Out"
override var width = App.fontGame.getWidth(zoomText) override var width = App.fontGame.getWidth(zoomText)
override var height = App.fontGame.lineHeight.toInt() override var height = App.fontGame.lineHeight.toInt()

View File

@@ -19,8 +19,8 @@ import kotlin.math.min
* Created by minjaesong on 2019-07-08. * Created by minjaesong on 2019-07-08.
*/ */
internal class UIStorageChest : UICanvas( internal class UIStorageChest : UICanvas(
toggleKeyLiteral = App.getConfigInt("control_key_inventory"), toggleKeyLiteral = "control_key_inventory",
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"), toggleButtonLiteral = "control_gamepad_start",
), HasInventory { ), HasInventory {
lateinit var chestInventory: FixtureInventory lateinit var chestInventory: FixtureInventory
@@ -175,7 +175,7 @@ internal class UIStorageChest : UICanvas(
private val controlHelp: String private val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}" "${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
else else
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]} " "${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]} "

View File

@@ -45,7 +45,7 @@ class UITierOneWatch() : UICanvas() {
ELuptimer += delta ELuptimer += delta
} }
if (mouseUp || Gdx.input.isKeyPressed(App.getConfigInt("control_key_interact"))) { if (mouseUp || Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_interact"))) {
ELuptimer = 0f ELuptimer = 0f
ELon = true ELon = true
} }

View File

@@ -53,7 +53,7 @@ class UITitleModules(val remoCon: UIRemoCon) : UICanvas() {
private val controlHelp: String private val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_up"))}${getKeycapPC(App.getConfigInt("control_key_down"))}" + "${getKeycapPC(ControlPresets.getKey("control_key_up"))}${getKeycapPC(ControlPresets.getKey("control_key_down"))}" +
" ${Lang["MENU_CONTROLS_SCROLL"]}" " ${Lang["MENU_CONTROLS_SCROLL"]}"
else else
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}" "${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
@@ -206,12 +206,12 @@ class UITitleModules(val remoCon: UIRemoCon) : UICanvas() {
override fun keyDown(keycode: Int): Boolean { override fun keyDown(keycode: Int): Boolean {
if (this.isVisible) { if (this.isVisible) {
if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) { if ((keycode == Input.Keys.UP || keycode == ControlPresets.getKey("control_key_up")) && scrollTarget > 0) {
scrollFrom = listScroll scrollFrom = listScroll
scrollTarget -= 1 scrollTarget -= 1
scrollAnimCounter = 0f scrollAnimCounter = 0f
} }
else if ((keycode == Input.Keys.DOWN || keycode == App.getConfigInt("control_key_down")) && scrollTarget < moduleCells.size - savesVisible) { else if ((keycode == Input.Keys.DOWN || keycode == ControlPresets.getKey("control_key_down")) && scrollTarget < moduleCells.size - savesVisible) {
scrollFrom = listScroll scrollFrom = listScroll
scrollTarget += 1 scrollTarget += 1
scrollAnimCounter = 0f scrollAnimCounter = 0f

View File

@@ -41,6 +41,7 @@ object UITitleRemoConYaml {
// todo add MENU_IO_IMPORT // todo add MENU_IO_IMPORT
val injectedMenuSingleCharSel = """ val injectedMenuSingleCharSel = """
- MENU_IO_IMPORT : net.torvald.terrarum.modulebasegame.ui.UIImportAvatar
- CONTEXT_CHARACTER_NEW : net.torvald.terrarum.modulebasegame.ui.UINewCharacter - CONTEXT_CHARACTER_NEW : net.torvald.terrarum.modulebasegame.ui.UINewCharacter
- MENU_LABEL_RETURN - MENU_LABEL_RETURN
""" """
@@ -48,6 +49,12 @@ object UITitleRemoConYaml {
val injectedMenuSingleWorldSel = """ val injectedMenuSingleWorldSel = """
- CONTEXT_WORLD_NEW : net.torvald.terrarum.modulebasegame.ui.UINewWorld - CONTEXT_WORLD_NEW : net.torvald.terrarum.modulebasegame.ui.UINewWorld
- MENU_LABEL_RETURN - MENU_LABEL_RETURN
"""
val injectedMenuSingleSaveManage = """
- MENU_MODULES
- MENU_LABEL_PREV_SAVES
- MENU_LABEL_RETURN
""" """
operator fun invoke(hasSave: Boolean) = operator fun invoke(hasSave: Boolean) =

View File

@@ -14,6 +14,10 @@ import net.torvald.terrarum.ui.UICanvas
*/ */
class UITooltip : UICanvas() { class UITooltip : UICanvas() {
init {
handler.allowESCtoClose = false
}
override var openCloseTime: Second = 0f override var openCloseTime: Second = 0f
private val tooltipBackCol = Color.WHITE private val tooltipBackCol = Color.WHITE

View File

@@ -21,6 +21,10 @@ class UIVitalMetre(
val order: Int val order: Int
) : UICanvas() { ) : UICanvas() {
init {
handler.allowESCtoClose = false
}
init { init {
// semitransparent // semitransparent
color?.a = 0.91f color?.a = 0.91f

View File

@@ -0,0 +1,382 @@
package net.torvald.terrarum.modulebasegame.ui
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.gameworld.WorldTime
import net.torvald.terrarum.gameworld.WorldTime.Companion.MONTH_LENGTH
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
import net.torvald.unicode.getKeycapPC
/**
* Created by minjaesong on 2023-08-15.
*/
class UIWallCalendar : UICanvas(
toggleKeyLiteral = "control_key_inventory",
toggleButtonLiteral = "control_gamepad_start",
) {
private val yearCellWidth = 200
private val cellWidth = 80
private val cellHeight = 24
override var width: Int = Toolkit.drawWidth
override var height: Int = App.scr.height
private val y = UIInventoryFull.INVENTORY_CELLS_OFFSET_Y() + 1 - 34
private val drawStartX = (Toolkit.drawWidth - cellWidth * 8) / 2 - 4
private val cellsStartY = y + 34
private val SP = "\u3000 "
val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
else
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}"
private var todayCell = -1
private val cellBackCols = listOf(
Color(0x3f1e22_C8), // OKLCh 14, 5, 18
Color(0x022f3a_C8), // OKLCh 218, 5, 18
Color(0x2d2b09_C8), // OKLCh 105, 5, 18
Color(0x252934_C8) // OKLCh 265, 2, 18
)
private val seasonMarkers = listOf(
7 to "CONTEXT_CALENDAR_SEASON_SPRING",
39 to "CONTEXT_CALENDAR_SEASON_SUMMER",
71 to "CONTEXT_CALENDAR_SEASON_AUTUMN",
103 to "CONTEXT_CALENDAR_SEASON_WINTER"
)
private var mouseOverCell = -1
private var mouseOverSeason = -1
override fun updateUI(delta: Float) {
mouseOverCell = if (relativeMouseX in drawStartX until drawStartX + 8 * (cellWidth + 1) &&
relativeMouseY in cellsStartY - 1 until cellsStartY - 1 + 17 * (cellHeight + 3)) {
val x = (relativeMouseX - drawStartX) / (cellWidth + 1)
val y = (relativeMouseY - cellsStartY + 1) / (cellHeight + 3)
// disable highlighting on invalid date (verddag and not winter 30)
if (x == 7 && y < 16) -1
else y * 8 + x
}
else -1
mouseOverSeason = when (mouseOverCell) {
-1 -> -1
in 0 until 34 -> 0
in 34 until 68 -> 1
in 68 until 102 -> 2
else -> 3
}
}
override fun renderUI(batch: SpriteBatch, camera: Camera) {
UIInventoryFull.drawBackground(batch, 1f)
val thisYear = INGAME.world.worldTime.years
val today = INGAME.world.worldTime.ordinalDay + 1
val todayOfWeek = INGAME.world.worldTime.dayOfWeek
// cell background
batch.color = Toolkit.Theme.COL_CELL_FILL
Toolkit.fillArea(batch, (width - yearCellWidth) / 2, y - 34, yearCellWidth, 24)
for (week in 0..7) {
Toolkit.fillArea(batch, drawStartX + (cellWidth + 1) * week + 1, y, cellWidth - 2, 24)
}
for (cellNum in 0 until 17 * 8) {
batch.color = when (cellNum) {
in 0 until 34 -> cellBackCols[0]
in 34 until 68 -> cellBackCols[1]
in 68 until 102 -> cellBackCols[2]
else -> cellBackCols[3]
}
if (cellNum % 8 != 7 || cellNum == 17 * 8 - 1) {
Toolkit.fillArea(batch, drawStartX + (cellWidth + 1) * (cellNum % 8) + 1, cellsStartY + (cellHeight + 3) * (cellNum / 8), cellWidth - 2, cellHeight)
}
}
// season name cell background
for (k in 0..3) {
batch.color = cellBackCols[k]
Toolkit.fillArea(batch, drawStartX + (cellWidth + 1) * 7 + 1, cellsStartY + (cellHeight + 3) * (k * 4), cellWidth - 2, (cellHeight + 3) * 4 - 3)
}
// cell border
batch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(batch, (width - yearCellWidth) / 2 - 1, y - 35, yearCellWidth + 2, 26)
Toolkit.drawBoxBorder(batch, drawStartX, y - 1, 8 * (cellWidth + 1) - 1, 26)
for (week in 0..7) {
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * week, y - 1, cellWidth, 26)
}
// highlight a day name of mouse-up
batch.color = Toolkit.Theme.COL_MOUSE_UP
if (mouseOverCell >= 0) Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * (mouseOverCell % 8), y - 1, cellWidth, 26)
// highlight today's week name
batch.color = Toolkit.Theme.COL_SELECTED
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * todayOfWeek, y - 1, cellWidth, 26)
// draw days grid
batch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(batch, drawStartX, cellsStartY - 1, 8 * (cellWidth + 1) - 1, 17 * (cellHeight + 3) - 1)
// non-season-name-cells
for (cellNum in 0 until 17 * 8) {
if (cellNum % 8 != 7 || cellNum == 17 * 8 - 1) {
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * (cellNum % 8), cellsStartY + (cellHeight + 3) * (cellNum / 8) - 1, cellWidth, cellHeight + 2)
}
}
// season-name-cells
for (k in 0..3) {
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * 7, cellsStartY + (cellHeight + 3) * (k * 4) - 1, cellWidth, (cellHeight + 3) * 4 - 1)
}
// highlight a day of mouse-up
batch.color = Toolkit.Theme.COL_MOUSE_UP
if (mouseOverCell >= 0) Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * (mouseOverCell % 8), cellsStartY + (cellHeight + 3) * (mouseOverCell / 8) - 1, cellWidth, cellHeight + 2)
// season border
batch.color = Toolkit.Theme.COL_MOUSE_UP
if (mouseOverSeason == 0) {
Toolkit.drawStraightLine(batch,
drawStartX,
cellsStartY - 2,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
Toolkit.drawStraightLine(
batch,
drawStartX - 1,
cellsStartY - 1,
cellsStartY + 1 + (cellHeight + 3) * 5 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 8 - 1,
cellsStartY - 1,
cellsStartY + 1 + (cellHeight + 3) * 4 - 3,
1,
true
)
}
if (mouseOverSeason in 0..1) {
Toolkit.drawStraightLine(
batch,
drawStartX,
cellsStartY + 1 + (cellHeight + 3) * 5 - 3,
drawStartX + (cellWidth + 1) * 2 - 1,
1,
false
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 2 - 1,
cellsStartY + 1 + (cellHeight + 3) * 4 - 2,
cellsStartY + 1 + (cellHeight + 3) * 5 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 2,
cellsStartY + 1 + (cellHeight + 3) * 4 - 3,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
}
if (mouseOverSeason == 1) {
Toolkit.drawStraightLine(
batch,
drawStartX - 1,
cellsStartY + 1 + (cellHeight + 3) * 5 - 2,
cellsStartY + 1 + (cellHeight + 3) * 9 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 8 - 1,
cellsStartY + 1 + (cellHeight + 3) * 4 - 2,
cellsStartY + 1 + (cellHeight + 3) * 8 - 3,
1,
true
)
}
if (mouseOverSeason in 1..2) {
Toolkit.drawStraightLine(
batch,
drawStartX,
cellsStartY + 1 + (cellHeight + 3) * 9 - 3,
drawStartX + (cellWidth + 1) * 4 - 1,
1,
false
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 4 - 1,
cellsStartY + 1 + (cellHeight + 3) * 8 - 2,
cellsStartY + 1 + (cellHeight + 3) * 9 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 4,
cellsStartY + 1 + (cellHeight + 3) * 8 - 3,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
}
if (mouseOverSeason == 2) {
Toolkit.drawStraightLine(
batch,
drawStartX - 1,
cellsStartY + 1 + (cellHeight + 3) * 9 - 2,
cellsStartY + 1 + (cellHeight + 3) * 13 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 8 - 1,
cellsStartY + 1 + (cellHeight + 3) * 8 - 2,
cellsStartY + 1 + (cellHeight + 3) * 12 - 3,
1,
true
)
}
if (mouseOverSeason in 2..3) {
Toolkit.drawStraightLine(
batch,
drawStartX,
cellsStartY + 1 + (cellHeight + 3) * 13 - 3,
drawStartX + (cellWidth + 1) * 6 - 1,
1,
false
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 6 - 1,
cellsStartY + 1 + (cellHeight + 3) * 12 - 2,
cellsStartY + 1 + (cellHeight + 3) * 13 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 6,
cellsStartY + 1 + (cellHeight + 3) * 12 - 3,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
}
if (mouseOverSeason == 3) {
Toolkit.drawStraightLine(
batch,
drawStartX - 1,
cellsStartY + 1 + (cellHeight + 3) * 13 - 2,
cellsStartY + 1 + (cellHeight + 3) * 17 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 8 - 1,
cellsStartY + 1 + (cellHeight + 3) * 12 - 2,
cellsStartY + 1 + (cellHeight + 3) * 17 - 3,
1,
true
)
Toolkit.drawStraightLine(batch,
drawStartX,
cellsStartY + 1 + (cellHeight + 3) * 17 - 3,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
}
// cell texts
batch.color = Toolkit.Theme.COL_LIST_DEFAULT
Toolkit.drawTextCentered(batch, App.fontGame, Lang.getAndUseTemplate("CONTEXT_CALENDAR_DATE_FORMAT_Y", false, thisYear), yearCellWidth, (width - yearCellWidth) / 2, y - 34)
for (week in 0..7) {
// highlight this week and the mouse-up
batch.color = if (week == todayOfWeek) Toolkit.Theme.COL_SELECTED else if (week == mouseOverCell % 8) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_LIST_DEFAULT
val t = WorldTime.getDayName(week)
val tlen = App.fontGame.getWidth(t)
App.fontGame.draw(batch, t, drawStartX + (cellWidth + 1) * week + (cellWidth - tlen) / 2, y)
}
var dayAkku = 1
for (cellNum in 0 until 17 * 8) {
val day = if (cellNum == 17*8-1) 120 else if (cellNum % 8 == 7) 0 else dayAkku
if (day > 0) {
// highlight today and the mouse-up
batch.color = if (day == today) Toolkit.Theme.COL_SELECTED else if (cellNum == mouseOverCell) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_LIST_DEFAULT
val t = "${(day % MONTH_LENGTH).let { if (it == 0) MONTH_LENGTH else it }}".padStart(2, '\u2007')
App.fontGame.draw(batch, t, drawStartX + (cellWidth + 1) * (cellNum % 8) - 20 + cellWidth - 4, cellsStartY + (cellHeight + 3) * (cellNum / 8))
if (day == today) todayCell = cellNum
dayAkku += 1
}
}
// draw seasonal names
seasonMarkers.forEachIndexed { index, (cellNum, key) ->
batch.color = if (index == mouseOverSeason) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_INACTIVE
Toolkit.drawTextCentered(batch, App.fontGame, Lang[key], cellWidth, drawStartX + (cellWidth + 1) * (cellNum % 8), cellsStartY + (cellHeight + 3) * (cellNum / 8) + ((cellHeight + 3) * 1.5f).floorToInt())
}
// highlight today cell
if (todayCell >= 0) {
batch.color = Toolkit.Theme.COL_SELECTED
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * (todayCell % 8), cellsStartY + (cellHeight + 3) * (todayCell / 8) - 1, cellWidth, cellHeight + 2)
}
// control hints
batch.color = Color.WHITE
App.fontGame.draw(batch, controlHelp, drawStartX + 2, cellsStartY+ 17 * (cellHeight + 3) + 6)
}
override fun doOpening(delta: Float) {
super.doOpening(delta)
INGAME.pause()
INGAME.setTooltipMessage(null)
}
override fun doClosing(delta: Float) {
super.doClosing(delta)
INGAME.resume()
INGAME.setTooltipMessage(null)
}
override fun endOpening(delta: Float) {
super.endOpening(delta)
UIItemInventoryItemGrid.tooltipShowing.clear()
INGAME.setTooltipMessage(null) // required!
}
override fun endClosing(delta: Float) {
super.endClosing(delta)
UIItemInventoryItemGrid.tooltipShowing.clear()
INGAME.setTooltipMessage(null) // required!
}
override fun dispose() {
}
}

View File

@@ -1,7 +1,6 @@
package net.torvald.terrarum.modulebasegame.ui package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.terrarum.* import net.torvald.terrarum.*
@@ -31,8 +30,8 @@ import java.util.UUID
* Created by minjaesong on 2023-05-19. * Created by minjaesong on 2023-05-19.
*/ */
class UIWorldPortal : UICanvas( class UIWorldPortal : UICanvas(
toggleKeyLiteral = App.getConfigInt("control_key_inventory"), toggleKeyLiteral = "control_key_inventory",
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"), toggleButtonLiteral = "control_gamepad_start",
) { ) {
override var width: Int = Toolkit.drawWidth override var width: Int = Toolkit.drawWidth
@@ -54,7 +53,7 @@ class UIWorldPortal : UICanvas(
private val SP = "\u3000 " private val SP = "\u3000 "
val portalListingControlHelp: String val portalListingControlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}" "${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
else else
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}" + "${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}" +
"$SP${App.gamepadLabelLT} ${Lang["GAME_WORLD_SEARCH"]}" + "$SP${App.gamepadLabelLT} ${Lang["GAME_WORLD_SEARCH"]}" +

View File

@@ -167,7 +167,7 @@ class UIWorldPortalCargo(val full: UIWorldPortal) : UICanvas(), HasInventory {
private val controlHelp: String private val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC) get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}" "${getKeycapPC(ControlPresets.getKey("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
else else
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]} " "${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]} "

View File

@@ -4,27 +4,26 @@ import net.torvald.terrarum.savegame.toBigEndian
import java.util.UUID import java.util.UUID
import kotlin.math.ceil import kotlin.math.ceil
/** /** My own string set that:
* Ascii85 implementation with my own character table based on RFC 1924. Will NOT truncate '00000' into something else; * - no "/": avoids nonstandard JSON comment key which GDX will happily parse away
* just gzip the inputstream instead! * - no "\": you know what I mean\\intention
* - no "$": avoids Kotlin string template
* - no "[{]},": even the dumbest parser can comprehend the output
*/ */
object Ascii85 { open class Ascii85Codec(private val CHAR_TABLE: String = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#%&'()*+-.:;<=>?@^_`|~") {
/** My own string set that: init {
* - no "/": avoids nonstandard JSON comment key which GDX will happily parse away if (CHAR_TABLE.length != 85) throw IllegalArgumentException("CHAR_TABLE is not 85 chars long")
* - 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!#%&'()*+-.:;<=>?@^_`|~"
/** As per Adobe standard */ /** As per Adobe standard */
//private val CHAR_TABLE = (33 until (33+85)).toList().map { it.toChar() }.joinToString("") // testing only! //private val CHAR_TABLE = (33 until (33+85)).toList().map { it.toChar() }.joinToString("") // testing only!
private val INVERSE_TABLE = LongArray(127) private val INVERSE_TABLE = LongArray(127)
/** Int of `-1` */ /** Int of `-1` */
const val PAD_BYTE = -1 val PAD_BYTE = -1
/** Null-character (`\0`) */ /** Null-character (`\0`) */
const val PAD_CHAR = 0.toChar() val PAD_CHAR = 0.toChar()
private val INTERNAL_PAD_BYTE = 0 private val INTERNAL_PAD_BYTE = 0
private val INTERNAL_PAD_CHAR = CHAR_TABLE.last() private val INTERNAL_PAD_CHAR = CHAR_TABLE.last()
@@ -97,10 +96,10 @@ object Ascii85 {
} }
val sum = (INVERSE_TABLE[s1.toInt()] * 52200625) + val sum = (INVERSE_TABLE[s1.toInt()] * 52200625) +
(INVERSE_TABLE[s2.toInt()] * 614125) + (INVERSE_TABLE[s2.toInt()] * 614125) +
(INVERSE_TABLE[s3.toInt()] * 7225) + (INVERSE_TABLE[s3.toInt()] * 7225) +
(INVERSE_TABLE[s4.toInt()] * 85) + (INVERSE_TABLE[s4.toInt()] * 85) +
INVERSE_TABLE[s5.toInt()] INVERSE_TABLE[s5.toInt()]
return ByteArray(4 - padLen) { sum.ushr((3 - it) * 8).and(255).toByte() } 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() = fun UUID.toAscii85() =
Ascii85.encodeBytes(this.mostSignificantBits.toBigEndian() + this.leastSignificantBits.toBigEndian()) Ascii85.encodeBytes(this.mostSignificantBits.toBigEndian() + this.leastSignificantBits.toBigEndian())
fun String.ascii85toUUID(): UUID { fun String.ascii85toUUID(): UUID {

View File

@@ -165,7 +165,7 @@ class BasicDebugInfoWindow : UICanvas() {
App.fontSmallNumbers.draw(batch, "${if (player.jumping) "$ccG" else "$ccK"}JM", gap + 7f*(jX + 8), line(jY)) App.fontSmallNumbers.draw(batch, "${if (player.jumping) "$ccG" else "$ccK"}JM", gap + 7f*(jX + 8), line(jY))
App.fontSmallNumbers.draw(batch, "${if (player.isJumpDown) "$ccG" else "$ccK"}KY", gap + 7f*(jX + 8), line(jY+1)) App.fontSmallNumbers.draw(batch, "${if (player.isJumpDown) "$ccG" else "$ccK"}KY", gap + 7f*(jX + 8), line(jY+1))
App.fontSmallNumbers.draw(batch, "${if (player.downDownVirtually) "$ccG" else "$ccK"}$ARROW_DOWN", gap + 7f*(jX + 11), line(jY+1)) App.fontSmallNumbers.draw(batch, "${if (player.downButtonHeld > 0) "$ccG" else "$ccK"}$ARROW_DOWN", gap + 7f*(jX + 11), line(jY+1))
App.fontSmallNumbers.draw(batch, "$WIDTH$ccG${player.hitbox.width.toString().padEnd(5).substring(0,5).trim()}$ccY$HEIGHT$ccG${player.hitbox.height.toString().padEnd(5).substring(0,5)}", gap + 7f*(jX + 13), line(jY)) App.fontSmallNumbers.draw(batch, "$WIDTH$ccG${player.hitbox.width.toString().padEnd(5).substring(0,5).trim()}$ccY$HEIGHT$ccG${player.hitbox.height.toString().padEnd(5).substring(0,5)}", gap + 7f*(jX + 13), line(jY))
App.fontSmallNumbers.draw(batch, "$MASS$ccG${player.mass.toString().padEnd(8).substring(0,8)}", gap + 7f*(jX + 13), line(jY+1)) App.fontSmallNumbers.draw(batch, "$MASS$ccG${player.mass.toString().padEnd(8).substring(0,8)}", gap + 7f*(jX + 13), line(jY+1))
@@ -199,7 +199,7 @@ class BasicDebugInfoWindow : UICanvas() {
val soldeg = WeatherMixer.forceSolarElev ?: world?.worldTime?.solarElevationDeg val soldeg = WeatherMixer.forceSolarElev ?: world?.worldTime?.solarElevationDeg
val soldegStr = (soldeg ?: 0.0).toIntAndFrac(3,2) val soldegStr = (soldeg ?: 0.0).toIntAndFrac(3,2)
val soldegNeg = ((soldeg ?: 0.0) >= 0.0).toInt() val soldegNeg = ((soldeg ?: 0.0) >= 0.0).toInt()
val turbidity = WeatherMixer.forceTurbidity ?: WeatherMixer.turbidity val turbidity = (WeatherMixer.forceTurbidity ?: WeatherMixer.turbidity).toIntAndFrac(1, 4)
val soldegCol = if (WeatherMixer.forceSolarElev != null) ccO else ccG val soldegCol = if (WeatherMixer.forceSolarElev != null) ccO else ccG
val turbCol = if (WeatherMixer.forceTurbidity != null) ccO else ccG val turbCol = if (WeatherMixer.forceTurbidity != null) ccO else ccG
@@ -292,15 +292,17 @@ class BasicDebugInfoWindow : UICanvas() {
if (ingame != null) { if (ingame != null) {
App.fontSmallNumbers.draw(batch, "${ccY}Actors total $ccG${ingame!!.actorContainerActive.size + ingame!!.actorContainerInactive.size}", App.fontSmallNumbers.draw(batch, "${ccY}Actors total $ccG${ingame!!.actorContainerActive.size + ingame!!.actorContainerInactive.size}",
TinyAlphNum.W * 2f, App.scr.height - TinyAlphNum.H * 2f) TinyAlphNum.W * 2f, App.scr.height - TinyAlphNum.H * 2f)
App.fontSmallNumbers.draw(batch, "${ccY}Active $ccG${ingame!!.actorContainerActive.size}", App.fontSmallNumbers.draw(batch, "${ccY}Active $ccG${ingame!!.actorContainerActive.size}",
(TinyAlphNum.W * 2 + 17 * 8).toFloat(), App.scr.height - TinyAlphNum.H * 2f) TinyAlphNum.W * 2f + (17 * 8), App.scr.height - TinyAlphNum.H * 2f)
App.fontSmallNumbers.draw(batch, "${ccY}Dormant $ccG${ingame!!.actorContainerInactive.size}", App.fontSmallNumbers.draw(batch, "${ccY}Dormant $ccG${ingame!!.actorContainerInactive.size}",
(TinyAlphNum.W * 2 + 28 * 8).toFloat(), App.scr.height - TinyAlphNum.H * 2f) TinyAlphNum.W * 2f + (28 * 8), App.scr.height - TinyAlphNum.H * 2f)
if (ingame is TerrarumIngame) { if (ingame is TerrarumIngame) {
App.fontSmallNumbers.draw(batch, "${ccM}Particles $ccG${(ingame as TerrarumIngame).particlesActive}", App.fontSmallNumbers.draw(batch, "${ccM}Particles $ccG${(ingame as TerrarumIngame).particlesActive}$ccY/$ccG${(ingame as TerrarumIngame).PARTICLES_MAX}",
(TinyAlphNum.W * 2 + 41 * 8).toFloat(), App.scr.height - TinyAlphNum.H * 2f) TinyAlphNum.W * 2f, App.scr.height - TinyAlphNum.H * 4f)
} }
App.fontSmallNumbers.draw(batch, "${ccM}Clouds $ccG${WeatherMixer.cloudsSpawned}$ccY/$ccG${WeatherMixer.cloudSpawnMax}",
TinyAlphNum.W * 2f + (18 * 8), App.scr.height - TinyAlphNum.H * 4f)
} }
App.fontSmallNumbers.draw(batch, "${ccY}Actors rendering $ccG${IngameRenderer.renderingActorsCount}", App.fontSmallNumbers.draw(batch, "${ccY}Actors rendering $ccG${IngameRenderer.renderingActorsCount}",

View File

@@ -5,7 +5,7 @@ import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.BitmapFont import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FloatFrameBuffer import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
@@ -48,9 +48,9 @@ object Toolkit : Disposable {
private val shaderBoxDown = App.loadShaderFromClasspath("shaders/default.vert", "shaders/boxdown.frag") private val shaderBoxDown = App.loadShaderFromClasspath("shaders/default.vert", "shaders/boxdown.frag")
private val shaderBoxUp = App.loadShaderFromClasspath("shaders/default.vert", "shaders/boxup.frag") private val shaderBoxUp = App.loadShaderFromClasspath("shaders/default.vert", "shaders/boxup.frag")
private lateinit var fboBlur: FloatFrameBuffer private lateinit var fboBlur: Float16FrameBuffer
private lateinit var fboBlurHalf: FloatFrameBuffer private lateinit var fboBlurHalf: Float16FrameBuffer
private lateinit var fboBlurQuarter: FloatFrameBuffer private lateinit var fboBlurQuarter: Float16FrameBuffer
private lateinit var blurWriteQuad: Mesh private lateinit var blurWriteQuad: Mesh
private lateinit var blurWriteQuad2: Mesh private lateinit var blurWriteQuad2: Mesh
private lateinit var blurWriteQuad4: Mesh private lateinit var blurWriteQuad4: Mesh
@@ -326,17 +326,17 @@ object Toolkit : Disposable {
val fw = App.scr.width//MathUtils.nextPowerOfTwo(App.scr.width) val fw = App.scr.width//MathUtils.nextPowerOfTwo(App.scr.width)
val fh = App.scr.height//MathUtils.nextPowerOfTwo(App.scr.height) val fh = App.scr.height//MathUtils.nextPowerOfTwo(App.scr.height)
fboBlur = FloatFrameBuffer( fboBlur = Float16FrameBuffer(
fw, fw,
fh, fh,
false false
) )
fboBlurHalf = FloatFrameBuffer( fboBlurHalf = Float16FrameBuffer(
fw / 2, fw / 2,
fh / 2, fh / 2,
false false
) )
fboBlurQuarter = FloatFrameBuffer( fboBlurQuarter = Float16FrameBuffer(
fw / 4, fw / 4,
fh / 4, fh / 4,
false false

View File

@@ -59,7 +59,7 @@ import kotlin.math.roundToInt
* Created by minjaesong on 2015-12-31. * Created by minjaesong on 2015-12-31.
*/ */
abstract class UICanvas( abstract class UICanvas(
toggleKeyLiteral: Int? = null, toggleButtonLiteral: Int? = null, toggleKeyLiteral: String? = null, toggleButtonLiteral: String? = null,
// UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int)) // UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int))
customPositioning: Boolean = false, // mainly used by vital meter customPositioning: Boolean = false, // mainly used by vital meter
doNotWarnConstant: Boolean = false doNotWarnConstant: Boolean = false

View File

@@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarum.App import net.torvald.terrarum.App
import net.torvald.terrarum.ControlPresets
import net.torvald.terrarum.FlippingSpriteBatch import net.torvald.terrarum.FlippingSpriteBatch
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gamecontroller.KeyToggler
@@ -25,12 +26,12 @@ import net.torvald.terrarum.modulebasegame.TerrarumIngame
* Created by minjaesong on 2015-12-31. * Created by minjaesong on 2015-12-31.
*/ */
class UIHandler(//var UI: UICanvas, class UIHandler(//var UI: UICanvas,
var toggleKeyLiteral: Int? = null, var toggleKeyLiteral: String? = null, // string key of the config
var toggleButtonLiteral: Int? = null, var toggleButtonLiteral: String? = null, // string key of the config
// UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int)) // UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int))
var customPositioning: Boolean = false, // mainly used by vital meter var customPositioning: Boolean = false, // mainly used by vital meter
var doNotWarnConstant: Boolean = false, var doNotWarnConstant: Boolean = false,
internal var allowESCtoClose: Boolean = false, internal var allowESCtoClose: Boolean = true,
var uiTogglerFunctionDefault: ((UIHandler) -> Unit)? = null var uiTogglerFunctionDefault: ((UIHandler) -> Unit)? = null
): Disposable { ): Disposable {
@@ -130,12 +131,13 @@ void main() {
//UI.handler = this //UI.handler = this
} }
// ControlPresets.getKey(toggleKeyLiteral)
private val toggleKey: Int?; get() = toggleKeyLiteral // to support in-screen keybind changing val toggleKey: Int?; get() = ControlPresets.getKey(toggleKeyLiteral).let { if (it == -1) null else it } // to support in-screen keybind changing
private val toggleButton: Int?; get() = toggleButtonLiteral // to support in-screen keybind changing // ControlPresets.getKey(toggleButtonLiteral)
val toggleButton: Int?; get() = ControlPresets.getKey(toggleButtonLiteral).let { if (it == -1) null else it } // to support in-screen keybind changing
val toggleKeyExtra: ArrayList<() -> Int> = arrayListOf() val toggleKeyExtra: ArrayList<String> = arrayListOf()
/** /**
* Takes a function that works with UIHandler. * Takes a function that works with UIHandler.
@@ -207,14 +209,14 @@ void main() {
else uiTogglerFunctionDefault!!.invoke(this) else uiTogglerFunctionDefault!!.invoke(this)
} }
toggleKeyExtra.forEachIndexed { index, getKey -> toggleKeyExtra.forEachIndexed { index, control ->
if (Gdx.input.isKeyJustPressed(getKey())) { if (Gdx.input.isKeyJustPressed(ControlPresets.getKey(control))) {
toggleKeyExtraAction[index].invoke(this) toggleKeyExtraAction[index].invoke(this)
} }
} }
// ESC is a master key for closing // ESC is a master key for closing
if (allowESCtoClose && Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE) && isOpened) { if (!alwaysVisible && allowESCtoClose && Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE) && isOpened) {
setAsClose() setAsClose()
} }
} }

View File

@@ -17,15 +17,15 @@ import kotlin.math.roundToInt
*/ */
class UIItemSpinner( class UIItemSpinner(
parentUI: UICanvas, parentUI: UICanvas,
initialX: Int, initialY: Int, initialX: Int, initialY: Int,
private var initialValue: Number, private var initialValue: Number,
val min: Number, val min: Number,
val max: Number, val max: Number,
val step: Number, val step: Number,
override val width: Int, override val width: Int,
private val drawBorder: Boolean = true, private val drawBorder: Boolean = true,
private val numberToTextFunction: (Number) -> String = { "$it" } private val numberToTextFunction: (Number) -> String = { "$it" }
) : UIItem(parentUI, initialX, initialY) { ) : UIItem(parentUI, initialX, initialY) {
// to alleviate floating point errors adding up as the spinner is being used // to alleviate floating point errors adding up as the spinner is being used

View File

@@ -443,7 +443,7 @@ class UIItemTextLineInput(
if (!mouseDown) mouseLatched = false if (!mouseDown) mouseLatched = false
imeOn = KeyToggler.isOn(App.getConfigInt("control_key_toggleime")) imeOn = KeyToggler.isOn(ControlPresets.getKey("control_key_toggleime"))
} }
} }
@@ -468,7 +468,7 @@ class UIItemTextLineInput(
endComposing() endComposing()
imeOn = !imeOn imeOn = !imeOn
KeyToggler.forceSet(App.getConfigInt("control_key_toggleime"), imeOn) KeyToggler.forceSet(ControlPresets.getKey("control_key_toggleime"), imeOn)
} }
private fun resetIME() { private fun resetIME() {

View File

@@ -17,13 +17,14 @@ import net.torvald.terrarum.gameworld.fmod
* Created by minjaesong on 2021-10-21. * Created by minjaesong on 2021-10-21.
*/ */
class UIItemTextSelector( class UIItemTextSelector(
parentUI: UICanvas, parentUI: UICanvas,
initialX: Int, initialY: Int, initialX: Int, initialY: Int,
val labelfuns: List<() -> String>, val labelfuns: List<() -> String>,
initialSelection: Int, initialSelection: Int,
override val width: Int, override val width: Int,
private val drawBorder: Boolean = true, private val drawBorder: Boolean = true,
private val clickToShowPalette: Boolean = true private val clickToShowPalette: Boolean = true,
private val useSpinnerButtons: Boolean = false,
) : UIItem(parentUI, initialX, initialY) { ) : UIItem(parentUI, initialX, initialY) {
init { init {
@@ -127,6 +128,9 @@ class UIItemTextSelector(
else if (!Terrarum.mouseDown) mouseLatched = false else if (!Terrarum.mouseDown) mouseLatched = false
} }
private val leftIcon = if (useSpinnerButtons) labels.get(9,2) else labels.get(16,0)
private val rightIcon = if (useSpinnerButtons) labels.get(10,2) else labels.get(17,0)
override fun render(batch: SpriteBatch, camera: Camera) { override fun render(batch: SpriteBatch, camera: Camera) {
labelCache = labelfuns.map { it() } labelCache = labelfuns.map { it() }
@@ -184,12 +188,12 @@ class UIItemTextSelector(
// left button icon // left button icon
batch.color = if (mouseOnButton == 1 && mousePushed) Toolkit.Theme.COL_SELECTED batch.color = if (mouseOnButton == 1 && mousePushed) Toolkit.Theme.COL_SELECTED
else if (mouseOnButton == 1) Toolkit.Theme.COL_MOUSE_UP else UIItemTextLineInput.TEXTINPUT_COL_TEXT else if (mouseOnButton == 1) Toolkit.Theme.COL_MOUSE_UP else UIItemTextLineInput.TEXTINPUT_COL_TEXT
batch.draw(labels.get(16,0), posX + (buttonW - labels.tileW) / 2f, posY + (height - labels.tileH) / 2f) batch.draw(leftIcon, posX + (buttonW - labels.tileW) / 2f, posY + (height - labels.tileH) / 2f)
// right button icon // right button icon
batch.color = if (mouseOnButton == 2 && mousePushed) Toolkit.Theme.COL_SELECTED batch.color = if (mouseOnButton == 2 && mousePushed) Toolkit.Theme.COL_SELECTED
else if (mouseOnButton == 2) Toolkit.Theme.COL_MOUSE_UP else UIItemTextLineInput.TEXTINPUT_COL_TEXT else if (mouseOnButton == 2) Toolkit.Theme.COL_MOUSE_UP else UIItemTextLineInput.TEXTINPUT_COL_TEXT
batch.draw(labels.get(17,0), posX + width - buttonW + (buttonW - labels.tileW) / 2f, posY + (height - labels.tileH) / 2f) batch.draw(rightIcon, posX + width - buttonW + (buttonW - labels.tileW) / 2f, posY + (height - labels.tileH) / 2f)
// draw text // draw text
if (!paletteShowing) { if (!paletteShowing) {

View File

@@ -1,8 +1,13 @@
package net.torvald.terrarum.weather package net.torvald.terrarum.weather
import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.math.Vector3
import com.badlogic.gdx.utils.JsonValue
import com.jme3.math.FastMath
import net.torvald.terrarum.GdxColorMap import net.torvald.terrarum.GdxColorMap
import java.util.* import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.absoluteValue
/** /**
* Note: Colour maps are likely to have sparse data points * Note: Colour maps are likely to have sparse data points
@@ -12,10 +17,49 @@ import java.util.*
* Created by minjaesong on 2016-07-11. * Created by minjaesong on 2016-07-11.
*/ */
data class BaseModularWeather( data class BaseModularWeather(
var skyboxGradColourMap: GdxColorMap, // row 0: skybox grad top, row 1: skybox grad bottom, row 2: sunlight (RGBA) val json: JsonValue,
val daylightClut: GdxColorMap, var skyboxGradColourMap: GdxColorMap, // row 0: skybox grad top, row 1: skybox grad bottom, row 2: sunlight (RGBA)
val classification: String, val daylightClut: GdxColorMap,
var extraImages: ArrayList<Texture>, val classification: String,
val mixFrom: String? = null, val cloudChance: Float,
val mixPercentage: Double? = null val windSpeed: Float,
) val windSpeedVariance: Float,
val cloudGamma: Vector2,
val cloudGammaVariance: Vector2,
var clouds: List<CloudProps>, // sorted by CloudProps.probability
val mixFrom: String? = null,
val mixPercentage: Double? = null,
var forceWindVec: Vector3? = null
) {
/**
* @param rnd random number between -1 and +1
*/
fun getRandomWindSpeed(rnd: Float): Float {
val v = 1f + rnd.absoluteValue * windSpeedVariance
return if (rnd < 0) windSpeed / v else windSpeed * v
}
}
data class CloudProps(
val category: String,
val spriteSheet: TextureRegionPack,
val probability: Float,
val baseScale: Float,
val scaleVariance: Float,
val altLow: Float,
val altHigh: Float,
) {
init {
spriteSheet.texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
}
/**
* @param rnd random number between -1 and +1
*/
fun getCloudScaleVariance(rnd: Float): Float {
val v = 1f + rnd.absoluteValue * scaleVariance
return if (rnd < 0) baseScale / v else baseScale * v
}
}

View File

@@ -2,7 +2,11 @@ package net.torvald.terrarum.weather
import com.badlogic.gdx.Gdx import com.badlogic.gdx.Gdx
import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.math.Vector3
import com.badlogic.gdx.utils.JsonValue
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.gdx.graphics.Cvec import net.torvald.gdx.graphics.Cvec
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
@@ -16,9 +20,18 @@ import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH
import net.torvald.terrarum.RNGConsumer import net.torvald.terrarum.RNGConsumer
import net.torvald.terrarum.clut.Skybox import net.torvald.terrarum.clut.Skybox
import net.torvald.terrarum.utils.JsonFetcher import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarum.utils.forEachSiblings
import net.torvald.terrarum.weather.WeatherObjectCloud.Companion.ALPHA_ROLLOFF_Z
import net.torvald.terrarum.worlddrawer.WorldCamera import net.torvald.terrarum.worlddrawer.WorldCamera
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.util.SortedArrayList
import java.io.File import java.io.File
import java.io.FileFilter import java.io.FileFilter
import java.lang.Double.doubleToLongBits
import java.lang.Math.toDegrees
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.math.*
/** /**
* Currently there is a debate whether this module must be part of the engine or the basegame * Currently there is a debate whether this module must be part of the engine or the basegame
@@ -50,10 +63,11 @@ internal object WeatherMixer : RNGConsumer {
lateinit var mixedWeather: BaseModularWeather lateinit var mixedWeather: BaseModularWeather
val globalLightNow = Cvec(0) val globalLightNow = Cvec(0)
private val moonlightMax = Cvec(0.23f, 0.24f, 0.25f, 0.21f) // actual moonlight is around ~4100K but our mesopic vision makes it appear blueish (wikipedia: Purkinje effect)
// Weather indices // Weather indices
const val WEATHER_GENERIC = "generic" const val WEATHER_GENERIC = "generic"
const val WEATHER_GENERIC_RAIN = "genericrain" const val WEATHER_OVERCAST = "overcast"
// TODO add weather classification indices manually // TODO add weather classification indices manually
// TODO to save from GL overhead, store lightmap to array; use GdxColorMap // TODO to save from GL overhead, store lightmap to array; use GdxColorMap
@@ -68,16 +82,23 @@ internal object WeatherMixer : RNGConsumer {
it.texture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat) it.texture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat)
} }
private val shaderBlendMax = App.loadShaderFromClasspath("shaders/blendSkyboxStars.vert", "shaders/blendSkyboxStars.frag") private val shaderAstrum = App.loadShaderFromClasspath("shaders/blendSkyboxStars.vert", "shaders/blendSkyboxStars.frag")
private val shaderClouds = App.loadShaderFromClasspath("shaders/default.vert", "shaders/clouds.frag")
private var astrumOffX = 0f private var astrumOffX = 0f
private var astrumOffY = 0f private var astrumOffY = 0f
private val moonlightMax = Cvec(0.23f, 0.24f, 0.25f, 0.21f) // actual moonlight is around ~4100K but our mesopic vision makes it appear blueish (wikipedia: Purkinje effect) private val clouds = SortedArrayList<WeatherObjectCloud>()
var cloudsSpawned = 0; private set
private var windVector = Vector3(-1f, 0f, 0.1f) // this is a direction vector
val cloudSpawnMax: Int
get() = 256 shl (App.getConfigInt("maxparticles") / 256)
override fun loadFromSave(s0: Long, s1: Long) { override fun loadFromSave(s0: Long, s1: Long) {
super.loadFromSave(s0, s1) super.loadFromSave(s0, s1)
currentWeather = weatherList[WEATHER_GENERIC]!![0]
internalReset(s0, s1) internalReset(s0, s1)
initClouds()
} }
fun internalReset() = internalReset(RNG.state0, RNG.state1) fun internalReset() = internalReset(RNG.state0, RNG.state1)
@@ -90,6 +111,15 @@ internal object WeatherMixer : RNGConsumer {
astrumOffX = s0.and(0xFFFFL).toFloat() / 65535f * starmapTex.regionWidth astrumOffX = s0.and(0xFFFFL).toFloat() / 65535f * starmapTex.regionWidth
astrumOffY = s1.and(0xFFFFL).toFloat() / 65535f * starmapTex.regionHeight astrumOffY = s1.and(0xFFFFL).toFloat() / 65535f * starmapTex.regionHeight
clouds.clear()
cloudsSpawned = 0
windVector = Vector3(-0.98f, 0f, 0.21f)
windDirWindow = null
windSpeedWindow = null
oldCamPos.set(WorldCamera.camVector)
} }
init { init {
@@ -121,6 +151,7 @@ internal object WeatherMixer : RNGConsumer {
// initialise // initialise
try { try {
weatherList["titlescreen"] = arrayListOf(weatherList[WEATHER_GENERIC]!![0].copy(windSpeed = 1f))
currentWeather = weatherList[WEATHER_GENERIC]!![0] currentWeather = weatherList[WEATHER_GENERIC]!![0]
nextWeather = getRandomWeather(WEATHER_GENERIC) nextWeather = getRandomWeather(WEATHER_GENERIC)
} }
@@ -128,10 +159,16 @@ internal object WeatherMixer : RNGConsumer {
e.printStackTrace() e.printStackTrace()
val defaultWeather = BaseModularWeather( val defaultWeather = BaseModularWeather(
JsonValue(JsonValue.ValueType.`object`),
GdxColorMap(1, 3, Color(0x55aaffff), Color(0xaaffffff.toInt()), Color.WHITE), GdxColorMap(1, 3, Color(0x55aaffff), Color(0xaaffffff.toInt()), Color.WHITE),
GdxColorMap(2, 2, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE), GdxColorMap(2, 2, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE),
"default", "default",
ArrayList<Texture>() 0f,
0f,
0f,
Vector2(1f, 1f),
Vector2(0f, 0f),
listOf()
) )
currentWeather = defaultWeather currentWeather = defaultWeather
@@ -147,6 +184,9 @@ internal object WeatherMixer : RNGConsumer {
// currentWeather = weatherList[WEATHER_GENERIC]!![0] // force set weather // currentWeather = weatherList[WEATHER_GENERIC]!![0] // force set weather
updateWind(delta, world)
updateClouds(delta, world)
if (!globalLightOverridden) { if (!globalLightOverridden) {
world.globalLight = WeatherMixer.globalLightNow world.globalLight = WeatherMixer.globalLightNow
@@ -154,8 +194,330 @@ internal object WeatherMixer : RNGConsumer {
} }
var turbidity = 4.0; private set private fun FloatArray.shiftAndPut(f: Float) {
private var gH = 1.4f * App.scr.height for (k in 1 until this.size) {
this[k-1] = this[k]
}
this[this.lastIndex] = f
}
private val PI = 3.1415927f
private val TWO_PI = 6.2831855f
private val THREE_PI = 9.424778f
private var windDirWindow: FloatArray? = null
private var windSpeedWindow: FloatArray? = null
private val WIND_DIR_TIME_UNIT = 14400 // every 4hr
private val WIND_SPEED_TIME_UNIT = 3600 // every 1hr
private var windDirAkku = 0 // only needed if timeDelta is not divisible by WIND_TIME_UNIT
private var windSpeedAkku = 0
// see: https://stackoverflow.com/questions/2708476/rotation-interpolation/14498790#14498790
private fun getShortestAngle(start: Float, end: Float) =
(((((end - if (start < 0f) TWO_PI + start else start) % TWO_PI) + THREE_PI) % TWO_PI) - PI).let {
if (it > PI) it - TWO_PI else it
}
private fun updateWind(delta: Float, world: GameWorld) {
if (windDirWindow == null) {
windDirWindow = FloatArray(4) { takeUniformRand(-PI..PI) } // completely random regardless of the seed
}
if (windSpeedWindow == null) {
windSpeedWindow = FloatArray(4) { currentWeather.getRandomWindSpeed(takeTriangularRand(-1f..1f)) } // completely random regardless of the seed
}
val windDirStep = windDirAkku / WIND_DIR_TIME_UNIT.toFloat()
val windSpeedStep = windSpeedAkku / WIND_SPEED_TIME_UNIT.toFloat()
// val angle0 = windDirWindow[0]
// val angle1 = getShortestAngle(angle0, windDirWindow[1])
// val angle2 = getShortestAngle(angle1, windDirWindow[2])
// val angle3 = getShortestAngle(angle2, windDirWindow[3])
// val fixedAngles = floatArrayOf(angle0, angle1, angle2, angle3)
val currentWindDir = FastMath.interpolateCatmullRom(windDirStep, windDirWindow)
val currentWindSpeed = FastMath.interpolateCatmullRom(windSpeedStep, windSpeedWindow)
/*
printdbg(this,
"dir ${Math.toDegrees(currentWindDir.toDouble()).roundToInt()}\t" +
"spd ${currentWindSpeed.times(10f).roundToInt().div(10f)}\t " +
"dirs ${windDirWindow!!.map { Math.toDegrees(it.toDouble()).roundToInt() }} ${windDirStep.times(100).roundToInt()}\t" +
"spds ${windSpeedWindow!!.map { it.times(10f).roundToInt().div(10f) }} ${windSpeedStep.times(100).roundToInt()}"
)*/
if (currentWeather.forceWindVec != null) {
windVector.set(currentWeather.forceWindVec)
}
else {
windVector.set(
cos(currentWindDir) * currentWindSpeed,
0f,
sin(currentWindDir) * currentWindSpeed
)
}
while (windDirAkku >= WIND_DIR_TIME_UNIT) {
windDirAkku -= WIND_DIR_TIME_UNIT
windDirWindow!!.shiftAndPut(takeUniformRand(-PI..PI))
}
while (windSpeedAkku >= WIND_SPEED_TIME_UNIT) {
windSpeedAkku -= WIND_SPEED_TIME_UNIT
windSpeedWindow!!.shiftAndPut(currentWeather.getRandomWindSpeed(takeTriangularRand(-1f..1f)))
}
windDirAkku += world.worldTime.timeDelta
windSpeedAkku += world.worldTime.timeDelta
}
private val cloudParallaxMultY = -0.035f
private val cloudParallaxMultX = -0.035f
private var cloudUpdateAkku = 0f
private val oldCamPos = Vector2(0f, 0f)
private val camDelta = Vector2(0f, 0f)
val oobMarginR = 1.5f * App.scr.wf
val oobMarginL = -0.5f * App.scr.wf
private val oobMarginY = -0.5f * App.scr.hf
private val DEBUG_CAUSE_OF_DESPAWN = false
private fun updateClouds(delta: Float, world: GameWorld) {
val camvec = WorldCamera.camVector
val camvec2 = camvec.cpy()
val testCamDelta = camvec.cpy().sub(oldCamPos)
if (testCamDelta.x.absoluteValue > world.width * TILE_SIZEF / 2f) {
if (testCamDelta.x >= 0)
camvec2.x -= world.width * TILE_SIZEF
else
camvec2.x += world.width * TILE_SIZEF
testCamDelta.set(camvec2.sub(oldCamPos))
}
camDelta.set(testCamDelta)
val cloudChanceEveryMin = 60f / (currentWeather.cloudChance * currentWeather.windSpeed) // if chance = 0, the result will be +inf
while (cloudUpdateAkku >= cloudChanceEveryMin) {
cloudUpdateAkku -= cloudChanceEveryMin
tryToSpawnCloud(currentWeather)
}
var immDespawnCount = 0
val immDespawnCauses = ArrayList<String>()
clouds.forEach {
// do parallax scrolling
it.posX += camDelta.x * cloudParallaxMultX
it.posY += camDelta.y * cloudParallaxMultY
it.update(windVector)
if (DEBUG_CAUSE_OF_DESPAWN && it.life == 0) {
immDespawnCount += 1
immDespawnCauses.add(it.despawnCode)
}
}
// printdbg(this, "Newborn cloud death rate: $immDespawnCount/$cloudsSpawned")
if (DEBUG_CAUSE_OF_DESPAWN && App.IS_DEVELOPMENT_BUILD && immDespawnCount > cloudsSpawned / 4) {
val despawnCauseStats = HashMap<String, Int>()
immDespawnCauses.forEach {
if (despawnCauseStats[it] != null) {
despawnCauseStats[it] = despawnCauseStats[it]!! + 1
}
else {
despawnCauseStats[it] = 1
}
}
despawnCauseStats.forEach { s, i ->
printdbg(this, "Cause of death -- $s: $i")
}
System.exit(0)
}
// remove clouds that are marked to be despawn
var i = 0
while (true) {
if (i >= clouds.size) break
if (clouds[i].flagToDespawn) {
clouds.removeAt(i)
i -= 1
cloudsSpawned -= 1
}
i += 1
}
cloudUpdateAkku += delta
oldCamPos.set(camvec)
}
private val scrHscaler = App.scr.height / 720f
private val cloudSizeMult = App.scr.wf / TerrarumScreenSize.defaultW
private fun takeUniformRand(range: ClosedFloatingPointRange<Float>) =
FastMath.interpolateLinear(Math.random().toFloat(), range.start, range.endInclusive)
private fun takeTriangularRand(range: ClosedFloatingPointRange<Float>) =
FastMath.interpolateLinear((Math.random() + Math.random()).div(2f).toFloat(), range.start, range.endInclusive)
private fun takeGaussianRand(range: ClosedFloatingPointRange<Float>) =
FastMath.interpolateLinear((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random()).div(8f).toFloat(), range.start, range.endInclusive)
/**
* Returns random point for clouds to spawn from, in the opposite side of the current wind vector
*/
private fun getCloudSpawningPosition(cloud: CloudProps, halfCloudSize: Float, windVector: Vector3): Vector3 {
val Z_LIM = ALPHA_ROLLOFF_Z/2f
val y = takeUniformRand(-cloud.altHigh..-cloud.altLow) * scrHscaler
var windVectorDir = toDegrees(atan2(windVector.z.toDouble(), windVector.x.toDouble())).toFloat() + 180f
if (windVectorDir < 0f) windVectorDir += 360f
windVectorDir /= 90f // full circle: 4
// an "edge" is a line of length 1 drawn into the edge of the square of size 1 (its total edge length will be 4)
// when the windVectorDir is not an integer, the "edge" will take the shape similar to this: ¬
// 'rr' is a point on the "edge", where 0.5 is a middle point in its length
// val rl = (windVectorDir % 1f).let { if (it < 0.5f) -it else it - 1f }.toInt()
// val rh = 1 + (windVectorDir % 1f).let { if (it < 0.5f) it else 1f - it }.toInt()
// choose between rl and rh using (windVectorDir % 1f) as a pivot
// if pivot = 0.3, rL is 70%, and rR is 30% likely
// plug the vote result into the when()
val selectedQuadrant = takeUniformRand(windVectorDir..windVectorDir + 1f)
// printdbg(this, "Dir: $windVectorDir, Rand(${windVectorDir}..${windVectorDir + 1f}) = ${selectedQuadrant.floorToInt()}($selectedQuadrant)")
val rr = takeUniformRand(0f..1f)
return when (selectedQuadrant.floorToInt()) {
-4, 0, 4 -> { // right side of the screen
val z = FastMath.interpolateLinear(rr, 1f, ALPHA_ROLLOFF_Z).pow(1.5f) // clouds are more likely to spawn with low Z-value
val posXscr = App.scr.width + halfCloudSize
val x = WeatherObjectCloud.screenXtoWorldX(posXscr, z)
Vector3(x, y, z)
}
-3, 1, 5 -> { // z = inf
val z = ALPHA_ROLLOFF_Z
val posXscr = FastMath.interpolateLinear(rr, App.scr.width + halfCloudSize, -halfCloudSize)
val x = WeatherObjectCloud.screenXtoWorldX(posXscr, Z_LIM)
Vector3(x, y, z)
}
-2, 2, 6 -> { // left side of the screen
val z = FastMath.interpolateLinear(rr, ALPHA_ROLLOFF_Z, 1f).pow(1.5f) // clouds are more likely to spawn with low Z-value
val posXscr = -halfCloudSize
val x = WeatherObjectCloud.screenXtoWorldX(posXscr, z)
Vector3(x, y, z)
}
-1, 3, 7 -> { // z = 0
val z = 0.1f
val posXscr = FastMath.interpolateLinear(rr, -halfCloudSize, App.scr.width + halfCloudSize)
val x = WeatherObjectCloud.screenXtoWorldX(posXscr, Z_LIM)
Vector3(x, y, z)
}
else -> throw InternalError()
}
}
private fun tryToSpawnCloud(currentWeather: BaseModularWeather, precalculatedPos: Vector3? = null) {
// printdbg(this, "Trying to spawn a cloud... (${cloudsSpawned} / ${cloudSpawnMax})")
if (cloudsSpawned < cloudSpawnMax) {
val flip = Math.random() < 0.5
val rC = takeUniformRand(0f..1f)
// val rZ = takeUniformRand(1f..ALPHA_ROLLOFF_Z/4f).pow(1.5f) // clouds are more likely to spawn with low Z-value
// val rY = takeUniformRand(-1f..1f)
val rT1 = takeTriangularRand(-1f..1f)
val (rA, rB) = doubleToLongBits(Math.random()).let {
it.ushr(20).and(0xFFFF).toInt() to it.ushr(36).and(0xFFFF).toInt()
}
var cloudsToSpawn: CloudProps? = null
var c = 0
while (c < currentWeather.clouds.size) {
if (rC < currentWeather.clouds[c].probability) {
cloudsToSpawn = currentWeather.clouds[c]
break
}
c += 1
}
cloudsToSpawn?.let { cloud ->
val cloudScale = cloud.getCloudScaleVariance(rT1)
val hCloudSize = (cloud.spriteSheet.tileW * cloudScale) / 2f + 1f
// val posXscr = initX ?: if (cloudDriftVector.x < 0) (App.scr.width + hCloudSize) else -hCloudSize
// val posX = WeatherObjectCloud.screenXtoWorldX(posXscr, rZ)
// val posY = randomPosWithin(-cloud.altHigh..-cloud.altLow, rY) * scrHscaler
val sheetX = rA % cloud.spriteSheet.horizontalCount
val sheetY = rB % cloud.spriteSheet.verticalCount
WeatherObjectCloud(cloud.spriteSheet.get(sheetX, sheetY), flip).also {
it.scale = cloudScale * cloudSizeMult
it.pos.set(precalculatedPos ?: getCloudSpawningPosition(cloud, hCloudSize, windVector))
// further set the random altitude if required
if (precalculatedPos != null) {
it.pos.y = takeUniformRand(-cloud.altHigh..-cloud.altLow) * scrHscaler
}
clouds.add(it)
cloudsSpawned += 1
// printdbg(this, "... Spawning ${cloud.category}($sheetX, $sheetY) cloud at pos ${it.pos}, scale ${it.scale}, invGamma ${it.darkness}")
}
}
}
}
private fun initClouds() {
val hCloudSize = 1024f
// multiplier is an empirical value that depends on the 'rZ'
// it does converge at ~6, but having it as an initial state does not make it stay converged
repeat((currentWeather.cloudChance * 1.333f).ceilToInt()) {
val z = takeUniformRand(0.1f..ALPHA_ROLLOFF_Z / 4f - 0.1f).pow(1.5f) // clouds are more likely to spawn with low Z-value
val zz = FastMath.interpolateLinear((z / ALPHA_ROLLOFF_Z) * 0.8f + 0.1f, ALPHA_ROLLOFF_Z / 4f, ALPHA_ROLLOFF_Z)
val x = WeatherObjectCloud.screenXtoWorldX(takeUniformRand(0f..App.scr.wf), zz)
tryToSpawnCloud(currentWeather, Vector3(x, 0f, z))
}
}
internal fun titleScreenInitWeather() {
currentWeather = weatherList["titlescreen"]!![0]
currentWeather.forceWindVec = Vector3(-0.98f, 0f, -0.21f)
initClouds()
}
private fun <T> Array<T?>.addAtFreeSpot(obj: T) {
var c = 0
while (true) {
if (this[c] == null) break
c += 1
}
this[c] = obj
}
var turbidity = 1.0; private set
private var gH = 1.8f * App.scr.height
// private var gH = 0.8f * App.scr.height // private var gH = 0.8f * App.scr.height
internal var parallaxPos = 0f; private set internal var parallaxPos = 0f; private set
@@ -166,11 +528,28 @@ internal object WeatherMixer : RNGConsumer {
*/ */
internal fun render(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) { internal fun render(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) {
drawSkybox(camera, batch, world) drawSkybox(camera, batch, world)
drawClouds(batch)
batch.color = Color.WHITE
} }
private fun drawClouds(batch: SpriteBatch) {
batch.inUse { _ ->
batch.shader = shaderClouds
batch.shader.setUniformf("gamma", currentWeather.cloudGamma)
batch.shader.setUniformf("shadeCol", 0.06f, 0.07f, 0.08f, 1f) // TODO temporary value
clouds.forEach {
batch.color = Color(globalLightNow.r, globalLightNow.g, globalLightNow.b, it.alpha)
it.render(batch, 0f, 0f)
}
}
}
private val parallaxDomainSize = 400f
private val turbidityDomainSize = 533.3333f
private fun drawSkybox(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) { private fun drawSkybox(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) {
val parallaxZeroPos = (world.height / 3f) val parallaxZeroPos = (world.height / 3f)
val parallaxDomainSize = 300f
// we will not care for nextSkybox for now // we will not care for nextSkybox for now
val timeNow = (forceTimeAt ?: world.worldTime.TIME_T.toInt()) % WorldTime.DAY_LENGTH val timeNow = (forceTimeAt ?: world.worldTime.TIME_T.toInt()) % WorldTime.DAY_LENGTH
@@ -199,17 +578,20 @@ internal object WeatherMixer : RNGConsumer {
-+ <- 0.0 = -+ <- 0.0 =
*/ */
val parallax = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / parallaxDomainSize).times(-1f).coerceIn(-1f, 1f) val parallax = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / parallaxDomainSize).times(-1f).coerceIn(-1f, 1f)
val turbidityCoeff = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / turbidityDomainSize).times(-1f).coerceIn(-1f, 1f)
parallaxPos = parallax parallaxPos = parallax
// println(parallax) // parallax value works as intended. // println(parallax) // parallax value works as intended.
gdxBlendNormalStraightAlpha() gdxBlendNormalStraightAlpha()
turbidity = (3.5 + turbidityCoeff * 2.5).coerceIn(1.0, 6.0)
val thisTurbidity = forceTurbidity ?: turbidity val thisTurbidity = forceTurbidity ?: turbidity
// TODO trilinear with (deg, turb, alb)
val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f) val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f)
val (tex, uvs) = Skybox.getUV(solarElev, thisTurbidity, 0.3)
val (tex, uvs, turbX, albX) = Skybox.getUV(solarElev, thisTurbidity, 0.3)
val mornNoonBlend = (1f/4000f * (timeNow - 43200) + 0.5f).coerceIn(0f, 1f) // 0.0 at T41200; 0.5 at T43200; 1.0 at T45200;
starmapTex.texture.bind(1) starmapTex.texture.bind(1)
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it
@@ -218,22 +600,28 @@ internal object WeatherMixer : RNGConsumer {
val astrumY = ((world.worldTime.TIME_T / WorldTime.DIURNAL_MOTION_LENGTH) % 1f) * starmapTex.regionHeight val astrumY = ((world.worldTime.TIME_T / WorldTime.DIURNAL_MOTION_LENGTH) % 1f) * starmapTex.regionHeight
batch.inUse { batch.inUse {
batch.shader = shaderBlendMax batch.shader = shaderAstrum
shaderBlendMax.setUniformi("tex1", 1) shaderAstrum.setUniformi("tex1", 1)
shaderBlendMax.setUniformf("drawOffset", 0f, gradY) shaderAstrum.setUniformf("drawOffsetSize", 0f, gradY, App.scr.wf, gH)
shaderBlendMax.setUniformf("drawOffsetSize", App.scr.wf, gH) shaderAstrum.setUniform4fv("uvA", uvs, 0, 4)
shaderBlendMax.setUniform2fv("skyboxUV1", uvs, 0, 2) shaderAstrum.setUniform4fv("uvB", uvs, 4, 4)
shaderBlendMax.setUniform2fv("skyboxUV2", uvs, 2, 2) shaderAstrum.setUniform4fv("uvC", uvs, 8, 4)
shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY) shaderAstrum.setUniform4fv("uvD", uvs, 12, 4)
shaderBlendMax.setUniformf("randomNumber", shaderAstrum.setUniform4fv("uvE", uvs, 16, 4)
shaderAstrum.setUniform4fv("uvF", uvs, 20, 4)
shaderAstrum.setUniform4fv("uvG", uvs, 24, 4)
shaderAstrum.setUniform4fv("uvH", uvs, 28, 4)
shaderAstrum.setUniformf("texBlend", mornNoonBlend, turbX, albX, 0f)
shaderAstrum.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY)
shaderAstrum.setUniformf("randomNumber",
// (world.worldTime.TIME_T.plus(31L) xor 1453L + 31L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(31L) xor 1453L + 31L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(37L) xor 862L + 31L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(37L) xor 862L + 31L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(23L) xor 1639L + 29L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(23L) xor 1639L + 29L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(29L) xor 2971L + 41L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(29L) xor 2971L + 41L).and(1023).toFloat(),
world.worldTime.TIME_T.div(+12.1f).plus(31L), world.worldTime.TIME_T.div(+14.1f).plus(31L),
world.worldTime.TIME_T.div(-11.8f).plus(37L), world.worldTime.TIME_T.div(-13.8f).plus(37L),
world.worldTime.TIME_T.div(+11.9f).plus(23L), world.worldTime.TIME_T.div(+13.9f).plus(23L),
world.worldTime.TIME_T.div(-12.3f).plus(29L), world.worldTime.TIME_T.div(-14.3f).plus(29L),
) )
batch.color = Color.WHITE batch.color = Color.WHITE
@@ -355,20 +743,36 @@ internal object WeatherMixer : RNGConsumer {
val skyboxInJson = JSON.getString("skyboxGradColourMap") val skyboxInJson = JSON.getString("skyboxGradColourMap")
val lightbox = JSON.getString("daylightClut") val lightbox = JSON.getString("daylightClut")
val extraImagesPath = JSON.get("extraImages").asStringArray()
val skybox = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${skyboxInJson}")) val skybox = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${skyboxInJson}"))
val daylight = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${lightbox}")) val daylight = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${lightbox}"))
val extraImages = ArrayList<Texture>()
for (i in extraImagesPath)
extraImages.add(Texture(ModMgr.getGdxFile(modname, "$pathToImage/${i}")))
val classification = JSON.getString("classification") val classification = JSON.getString("classification")
val cloudsMap = ArrayList<CloudProps>()
val clouds = JSON["clouds"]
clouds.forEachSiblings { name, json ->
cloudsMap.add(CloudProps(
name,
TextureRegionPack(ModMgr.getGdxFile(modname, "$pathToImage/${json.getString("filename")}"), json.getInt("tw"), json.getInt("th")),
json.getFloat("probability"),
json.getFloat("baseScale"),
json.getFloat("scaleVariance"),
json.getFloat("altLow"),
json.getFloat("altHigh"),
))
}
cloudsMap.sortBy { it.probability }
var mixFrom: String? var mixFrom: String?
try { mixFrom = JSON.getString("mixFrom") } try { mixFrom = JSON.getString("mixFrom") }
@@ -383,20 +787,28 @@ internal object WeatherMixer : RNGConsumer {
return BaseModularWeather( return BaseModularWeather(
json = JSON,
skyboxGradColourMap = skybox, skyboxGradColourMap = skybox,
daylightClut = daylight, daylightClut = daylight,
classification = classification, classification = classification,
extraImages = extraImages cloudChance = JSON.getFloat("cloudChance"),
windSpeed = JSON.getFloat("windSpeed"),
windSpeedVariance = JSON.getFloat("windSpeedVariance"),
cloudGamma = JSON["cloudGamma"].asFloatArray().let { Vector2(it[0], it[1]) },
cloudGammaVariance = JSON["cloudGammaVariance"].asFloatArray().let { Vector2(it[0], it[1]) },
clouds = cloudsMap,
) )
} }
fun dispose() { fun dispose() {
weatherList.values.forEach { list -> weatherList.values.forEach { list ->
list.forEach { weather -> list.forEach { weather ->
weather.extraImages.forEach { it.tryDispose() } weather.clouds.forEach { it.spriteSheet.dispose() }
} }
} }
starmapTex.texture.dispose() starmapTex.texture.dispose()
shaderBlendMax.dispose() shaderAstrum.dispose()
shaderClouds.dispose()
} }
} }

Some files were not shown because too many files have changed in this diff Show More