Compare commits

..

30 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
68 changed files with 1294 additions and 218 deletions

View File

@@ -5,10 +5,7 @@ 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.App
import net.torvald.terrarum.FlippingSpriteBatch
import net.torvald.terrarum.blendNormalStraightAlpha
import net.torvald.terrarum.inAction
import net.torvald.terrarum.*
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulecomputers.gameactors.FixtureHomeComputer
import net.torvald.terrarum.ui.Toolkit
@@ -18,8 +15,8 @@ import net.torvald.tsvm.peripheral.GraphicsAdapter
import net.torvald.unicode.*
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"),
toggleKeyLiteral = null,
toggleButtonLiteral = "control_gamepad_start",
) {
override var width = 640
override var height = 480
@@ -45,7 +42,7 @@ internal class UIHomeComputer : UICanvas(
private val fbo = FrameBuffer(Pixmap.Format.RGBA8888, width, height, false)
private val controlHelp =
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
"${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"

View File

@@ -8,6 +8,7 @@
"GAME_ACTION_MOVE_VERB" : "Move",
"GAME_ACTION_ZOOM" : "Zoom",
"MENU_IO_AUTOSAVE": "Autosave",
"MENU_IO_CLEAR": "Clear",
"MENU_IO_IMPORT": "Import",
"MENU_IO_MANUAL_SAVE": "Manual Save",
"MENU_LABEL_COPYRIGHT": "Copyright",
@@ -16,6 +17,7 @@
"MENU_LABEL_IME": "IME",
"MENU_LABEL_IME_TOGGLE": "Toggle IME",
"MENU_LABEL_KEYBOARD_LAYOUT": "Keyboard Layout",
"MENU_LABEL_PASTE": "Paste",
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "Paste from Clipboard",
"MENU_LABEL_PRESS_START_SYMBOL": "Press >",
"MENU_LABEL_RESET" : "Reset",

View File

@@ -8,6 +8,7 @@
"GAME_ACTION_MOVE_VERB" : "이동하기",
"GAME_ACTION_ZOOM" : "확대·축소",
"MENU_IO_AUTOSAVE": "자동 저장",
"MENU_IO_CLEAR": "지우기",
"MENU_IO_IMPORT": "가져오기",
"MENU_IO_MANUAL_SAVE": "수동 저장",
"MENU_LABEL_COPYRIGHT": "저작권",
@@ -16,6 +17,7 @@
"MENU_LABEL_IME": "입력기",
"MENU_LABEL_IME_TOGGLE": "입력기 켜고 끄기",
"MENU_LABEL_KEYBOARD_LAYOUT": "자판 배열",
"MENU_LABEL_PASTE": "붙여넣기",
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "복사한 텍스트 붙여넣기",
"MENU_LABEL_PRESS_START_SYMBOL": ">을 누르세요",
"MENU_LABEL_RESET" : "재설정",

View File

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

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

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

View File

@@ -2,7 +2,21 @@
"skyboxGradColourMap": "generic_skybox.tga",
"daylightClut": "clut_daylight.tga",
"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

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

View File

@@ -18,6 +18,9 @@ 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 <stdlib.h>
#include <tchar.h>
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

@@ -248,6 +248,10 @@ final public class FastMath {
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.
* here is the interpolation matrix
* m = [ 0.0 1.0 0.0 0.0 ]
@@ -331,8 +335,7 @@ final public class FastMath {
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);
float mu2 = scale * scale;
float mu3 = mu2 * scale;
@@ -350,7 +353,7 @@ final public class FastMath {
float a3 = -2*mu3 + 3*mu2 + 0;
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) {}

View File

@@ -269,8 +269,8 @@ public class App implements ApplicationListener {
Gdx.gl20.glViewport(0, 0, width, height);
}
public static final int TICKS = 64;
public static final float UPDATE_RATE = 1f / TICKS; // 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 final float showupTime = 100f / 1000f;
@@ -1378,6 +1378,8 @@ public class App implements ApplicationListener {
* @throws NullPointerException if the specified config simply does not exist.
*/
public static int getConfigInt(String key) {
if (key == null) return -1;
Object cfg = getConfigMaster(key);
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
"control_preset_keyboard" to "WASD",
"control_gamepad_keyn" to 3,
"control_gamepad_keyw" to 2,
"control_gamepad_keys" to 0,

View File

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

View File

@@ -67,7 +67,7 @@ basegame
// 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 = "test001"
const val VERSION_TAG: String = "test002"
//////////////////////////////////////////////////////////
// CONFIGURATION FOR TILE MAKER //

View File

@@ -688,7 +688,7 @@ open class ActorWithBody : Actor {
}
private fun displaceHitbox() {
val printdbg1 = true && App.IS_DEVELOPMENT_BUILD
val printdbg1 = false && App.IS_DEVELOPMENT_BUILD
// // HOW IT SHOULD WORK // //
// ////////////////////////
// combineVeloToMoveDelta now

View File

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

View File

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

View File

@@ -255,7 +255,9 @@ object IngameRenderer : Disposable {
gdxClearAndEnableBlend(0f, 0f, 0f, 0f)
// draw sky
WeatherMixer.render(camera, batch, world)
measureDebugTime("WeatherMixer.render") {
WeatherMixer.render(camera, batch, world)
}
// normal behaviour

View File

@@ -166,12 +166,12 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
//ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader)
val world = ReadSimpleWorld(reader, file)
demoWorld = world
demoWorld.worldTime.timeDelta = 330 // a year = 8 minutes
demoWorld.worldTime.timeDelta = 30
printdbg(this, "Demo world loaded")
}
catch (e: IOException) {
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")
}
@@ -210,6 +210,8 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
IngameRenderer.setRenderedWorld(demoWorld)
WeatherMixer.internalReset()
WeatherMixer.titleScreenInitWeather()
// load a half-gradient texture that would be used throughout the titlescreen and its sub UIs
@@ -285,20 +287,15 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
}
private val updateScreen = { delta: Float ->
// TODO: desynched weather and time-of-day change
val forcedTime = 32880 // 9h08m
demoWorld.globalLight = WeatherMixer.globalLightNow
// demoWorld.globalLight = WeatherMixer.getGlobalLightOfTimeOfNoon()
demoWorld.updateWorldTime(delta)
// WeatherMixer.update(delta, cameraPlayer, demoWorld)
WeatherMixer.forceTimeAt = forcedTime
WeatherMixer.update(delta, cameraPlayer, demoWorld)
cameraPlayer.update(delta)
// worldcamera update AFTER cameraplayer in this case; the other way is just an exception for actual ingame SFX
WorldCamera.update(demoWorld, cameraPlayer)
// update UIs //
uiContainer.forEach { it?.update(delta) }
}

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

@@ -254,11 +254,11 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
private fun updateGamerControlBox() {
if (isGamer) {
isUpDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_up"))
isLeftDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_left"))
isDownDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_down"))
isRightDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_right"))
isJumpDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_jump"))
isUpDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_up"))
isLeftDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_left"))
isDownDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_down"))
isRightDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_right"))
isJumpDown = Gdx.input.isKeyPressed(ControlPresets.getKey("control_key_jump"))
val gamepad = App.gamepad
@@ -268,7 +268,7 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
axisRX = gamepad.getAxis(App.getConfigInt("control_gamepad_axisrx"))
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"))
}
@@ -348,11 +348,11 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
// ↑F, ↓S
if (isRightDown && !isLeftDown) {
walkHorizontal(false, AXIS_KEYBOARD)
prevHMoveKey = App.getConfigInt("control_key_right")
prevHMoveKey = ControlPresets.getKey("control_key_right")
} // ↓F, ↑S
else if (isLeftDown && !isRightDown) {
walkHorizontal(true, AXIS_KEYBOARD)
prevHMoveKey = App.getConfigInt("control_key_left")
prevHMoveKey = ControlPresets.getKey("control_key_left")
} // ↓F, ↓S
/*else if (isLeftDown && isRightDown) {
if (prevHMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_LEFT)) {
@@ -376,11 +376,11 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
// ↑E, ↓D
if (isDownDown && !isUpDown) {
walkVertical(false, AXIS_KEYBOARD)
prevVMoveKey = App.getConfigInt("control_key_down")
prevVMoveKey = ControlPresets.getKey("control_key_down")
} // ↓E, ↑D
else if (isUpDown && !isDownDown) {
walkVertical(true, AXIS_KEYBOARD)
prevVMoveKey = App.getConfigInt("control_key_up")
prevVMoveKey = ControlPresets.getKey("control_key_up")
} // ↓E, ↓D
/*else if (isUpDown && isDownDown) {
if (prevVMoveKey == KeyMap.getKeyCode(EnumKeyFunc.MOVE_UP)) {

View File

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

View File

@@ -78,7 +78,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
private val controlHelp: String
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
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
"$gamepadLabelLEFTRIGHT ${Lang["GAME_OBJECTIVE_MULTIPLIER"]}\u3000 " +

View File

@@ -8,6 +8,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.unicode.EMDASH
import net.torvald.terrarum.App
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ControlPresets
import net.torvald.terrarum.gamecontroller.*
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.linearSearch
@@ -318,7 +319,7 @@ private class UIItemInputKeycap(
else if (parent.shiftin && keysymsLow[1]?.isNotEmpty() == true) keysymsLow[1]
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)
else {
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.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.jme3.math.FastMath
import net.torvald.terrarum.*
import net.torvald.terrarum.App.*
@@ -19,7 +18,7 @@ import net.torvald.unicode.*
* Created by minjaesong on 2017-10-21.
*/
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))
customPositioning: Boolean = false, // mainly used by vital meter
doNotWarnConstant: Boolean = false
@@ -162,10 +161,10 @@ class UIInventoryFull(
private val SP = "\u3000 "
val listControlHelp: String
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_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
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" +
"$gamepadLabelLT ${Lang["CONTEXT_ITEM_MAP"]}$SP" +
@@ -175,7 +174,7 @@ class UIInventoryFull(
"$gamepadLabelEast ${Lang["GAME_INVENTORY_DROP"]}"
val minimapControlHelp: String
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"]}"
else
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" +
@@ -183,7 +182,7 @@ class UIInventoryFull(
"$gamepadLabelRT ${Lang["GAME_INVENTORY"]}"
val gameMenuControlHelp: String
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
"$gamepadLabelStart ${Lang["GAME_ACTION_CLOSE"]}$SP" +
"$gamepadLabelLT ${Lang["GAME_INVENTORY"]}"
@@ -249,7 +248,7 @@ class UIInventoryFull(
}
// 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 {
if (it.isClosed) {
INGAME.setTooltipMessage(null)
@@ -263,7 +262,7 @@ class UIInventoryFull(
}
// 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 {
if (it.isClosed) {
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.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.App
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.DefaultConfig
import net.torvald.terrarum.*
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.*
@@ -37,16 +35,16 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
private val keycaps = hashMapOf(
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_2 to UIItemKeycap(this, 65,1, Input.Keys.NUM_2, oneu, "2,3"),
Input.Keys.NUM_3 to UIItemKeycap(this, 97,1, Input.Keys.NUM_3, oneu, "3,3"),
Input.Keys.NUM_4 to UIItemKeycap(this, 129,1, Input.Keys.NUM_4, oneu, "4,3"),
Input.Keys.NUM_5 to UIItemKeycap(this, 161,1, Input.Keys.NUM_5, oneu, "5,3"),
Input.Keys.NUM_6 to UIItemKeycap(this, 193,1, Input.Keys.NUM_6, oneu, "6,3"),
Input.Keys.NUM_7 to UIItemKeycap(this, 225,1, Input.Keys.NUM_7, oneu, "7,3"),
Input.Keys.NUM_8 to UIItemKeycap(this, 257,1, Input.Keys.NUM_8, oneu, "8,3"),
Input.Keys.NUM_9 to UIItemKeycap(this, 289,1, Input.Keys.NUM_9, oneu, "9,3"),
Input.Keys.NUM_0 to UIItemKeycap(this, 321,1, Input.Keys.NUM_0, oneu, "0,3"),
Input.Keys.NUM_1 to UIItemKeycap(this, 33,1, null, oneu, "1,3"),
Input.Keys.NUM_2 to UIItemKeycap(this, 65,1, null, oneu, "2,3"),
Input.Keys.NUM_3 to UIItemKeycap(this, 97,1, null, oneu, "3,3"),
Input.Keys.NUM_4 to UIItemKeycap(this, 129,1, null, oneu, "4,3"),
Input.Keys.NUM_5 to UIItemKeycap(this, 161,1, null, oneu, "5,3"),
Input.Keys.NUM_6 to UIItemKeycap(this, 193,1, null, oneu, "6,3"),
Input.Keys.NUM_7 to UIItemKeycap(this, 225,1, null, oneu, "7,3"),
Input.Keys.NUM_8 to UIItemKeycap(this, 257,1, null, oneu, "8,3"),
Input.Keys.NUM_9 to UIItemKeycap(this, 289,1, null, oneu, "9,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.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"),
@@ -105,25 +103,39 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
) // end of keycaps
private val resetButtonWidth = 140
private val buttonReset = UIItemTextButton(this,
private val presetButtonWidth = 200
/*private val buttonReset = UIItemTextButton(this,
{ Lang["MENU_LABEL_RESET"] },
kbx + (width - resetButtonWidth) / 2,
kby + 162 + 12,
kby + 162 + 16,
resetButtonWidth,
hasBorder = true,
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 {
keycaps.values.forEach { addUIitem(it) }
updateKeycaps()
buttonReset.clickOnceListener = { x, y ->
/*buttonReset.clickOnceListener = { x, y ->
resetKeyConfig()
updateKeycaps()
}*/
presetSelector.selectionChangeListener = { index ->
App.setConfig("control_preset_keyboard", ControlPresets.presetLabels[index])
updateKeycaps()
}
// addUIitem(keyboardLayoutSelection)
@@ -152,18 +164,18 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
private fun updateKeycaps() {
keycaps.values.forEach { it.symbolControl = null }
// read config and put icons. Item order irrelevant
keycaps[App.getConfigInt("control_key_up")]?.symbolControl = Keebsym.UP
keycaps[App.getConfigInt("control_key_left")]?.symbolControl = Keebsym.LEFT
keycaps[App.getConfigInt("control_key_down")]?.symbolControl = Keebsym.DOWN
keycaps[App.getConfigInt("control_key_right")]?.symbolControl = Keebsym.RIGHT
keycaps[App.getConfigInt("control_key_jump")]?.symbolControl = Keebsym.JUMP
keycaps[App.getConfigInt("control_key_zoom")]?.symbolControl = Keebsym.ZOOM
keycaps[App.getConfigInt("control_key_inventory")]?.symbolControl = Keebsym.INVENTORY
keycaps[App.getConfigInt("control_key_movementaux")]?.symbolControl = Keebsym.HOOK
keycaps[App.getConfigInt("control_key_quicksel")]?.symbolControl = Keebsym.PIE
keycaps[App.getConfigInt("control_key_gamemenu")]?.symbolControl = Keebsym.MENU
keycaps[App.getConfigInt("control_key_toggleime")]?.symbolControl = Keebsym.IME()
keycaps[App.getConfigInt("control_key_crafting")]?.symbolControl = Keebsym.CRAFTING
keycaps[ControlPresets.getKey("control_key_up")]?.symbolControl = Keebsym.UP
keycaps[ControlPresets.getKey("control_key_left")]?.symbolControl = Keebsym.LEFT
keycaps[ControlPresets.getKey("control_key_down")]?.symbolControl = Keebsym.DOWN
keycaps[ControlPresets.getKey("control_key_right")]?.symbolControl = Keebsym.RIGHT
keycaps[ControlPresets.getKey("control_key_jump")]?.symbolControl = Keebsym.JUMP
keycaps[ControlPresets.getKey("control_key_zoom")]?.symbolControl = Keebsym.ZOOM
keycaps[ControlPresets.getKey("control_key_inventory")]?.symbolControl = Keebsym.INVENTORY
keycaps[ControlPresets.getKey("control_key_movementaux")]?.symbolControl = Keebsym.HOOK
keycaps[ControlPresets.getKey("control_key_quicksel")]?.symbolControl = Keebsym.PIE
keycaps[ControlPresets.getKey("control_key_gamemenu")]?.symbolControl = Keebsym.MENU
keycaps[ControlPresets.getKey("control_key_toggleime")]?.symbolControl = Keebsym.IME()
keycaps[ControlPresets.getKey("control_key_crafting")]?.symbolControl = Keebsym.CRAFTING
}
internal var keycapClicked = -13372
@@ -176,14 +188,14 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
override fun updateUI(delta: Float) {
uiItems.forEach {
it.update(delta)
if (it is UIItemKeycap && it.mousePushed) {
if (it is UIItemKeycap && it.mousePushed && ControlPresets.presetLabels[presetSelector.selection] == "Custom") {
it.selected = true
// println("key ${it.key}; selected = ${it.selected}")
keycapClicked = it.key ?: -13372
}
}
buttonReset.update(delta)
presetSelector.update(delta)
controlPalette.update(delta)
}
@@ -194,7 +206,7 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
// batch.color = fillCol
// Toolkit.fillArea(batch, drawX, drawY, width, height)
uiItems.forEach { it.render(batch, camera) }
buttonReset.render(batch, camera)
presetSelector.render(batch, camera)
// title
// todo show "Keyboard"/"Gamepad" accordingly
@@ -215,21 +227,56 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
}
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) {
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()
Terrarum.ingame?.let {
it.onConfigChange()
}
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
super.touchDown(screenX, screenY, pointer, button)
buttonReset.touchDown(screenX, screenY, pointer, button)
presetSelector.touchDown(screenX, screenY, pointer, button)
return true
}
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
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
}

View File

@@ -141,7 +141,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : Advanceable() {
private val controlHelp: String
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"]}"
else
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
@@ -408,12 +408,12 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : Advanceable() {
if (this.isVisible) {
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
scrollTarget -= 1
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
scrollTarget += 1
scrollAnimCounter = 0f

View File

@@ -25,7 +25,7 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() {
private val controlHelp: String
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"]}"
else
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
@@ -249,12 +249,12 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() {
if (this.isVisible) {
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
scrollTarget -= 1
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
scrollTarget += 1
scrollAnimCounter = 0f

View File

@@ -227,8 +227,8 @@ class UILoadManage(val full: UILoadSavegame) : UICanvas() {
}
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_ARE_YOU_SURE"], Toolkit.drawWidth, 0, full.titleTopGradEnd + full.cellInterval + SAVE_CELL_HEIGHT + 36)
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 + 24)
delButtons.forEach { it.render(batch, camera) }
}

View File

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

View File

@@ -19,8 +19,8 @@ import kotlin.math.min
* Created by minjaesong on 2019-07-08.
*/
internal class UIStorageChest : UICanvas(
toggleKeyLiteral = App.getConfigInt("control_key_inventory"),
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"),
toggleKeyLiteral = "control_key_inventory",
toggleButtonLiteral = "control_gamepad_start",
), HasInventory {
lateinit var chestInventory: FixtureInventory
@@ -175,7 +175,7 @@ internal class UIStorageChest : UICanvas(
private val controlHelp: String
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
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]} "

View File

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

View File

@@ -53,7 +53,7 @@ class UITitleModules(val remoCon: UIRemoCon) : UICanvas() {
private val controlHelp: String
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"]}"
else
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
@@ -206,12 +206,12 @@ class UITitleModules(val remoCon: UIRemoCon) : UICanvas() {
override fun keyDown(keycode: Int): Boolean {
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
scrollTarget -= 1
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
scrollTarget += 1
scrollAnimCounter = 0f

View File

@@ -41,7 +41,7 @@ object UITitleRemoConYaml {
// todo add MENU_IO_IMPORT
val injectedMenuSingleCharSel = """
- MENU_IO_IMPORT
- MENU_IO_IMPORT : net.torvald.terrarum.modulebasegame.ui.UIImportAvatar
- CONTEXT_CHARACTER_NEW : net.torvald.terrarum.modulebasegame.ui.UINewCharacter
- MENU_LABEL_RETURN
"""

View File

@@ -3,10 +3,7 @@ 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.App
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.RunningEnvironment
import net.torvald.terrarum.floorToInt
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
@@ -18,8 +15,8 @@ import net.torvald.unicode.getKeycapPC
* Created by minjaesong on 2023-08-15.
*/
class UIWallCalendar : UICanvas(
toggleKeyLiteral = App.getConfigInt("control_key_inventory"),
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"),
toggleKeyLiteral = "control_key_inventory",
toggleButtonLiteral = "control_gamepad_start",
) {
private val yearCellWidth = 200
private val cellWidth = 80
@@ -36,7 +33,7 @@ class UIWallCalendar : UICanvas(
private val SP = "\u3000 "
val controlHelp: String
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
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}"

View File

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

View File

@@ -167,7 +167,7 @@ class UIWorldPortalCargo(val full: UIWorldPortal) : UICanvas(), HasInventory {
private val controlHelp: String
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
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]} "

View File

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

View File

@@ -199,7 +199,7 @@ class BasicDebugInfoWindow : UICanvas() {
val soldeg = WeatherMixer.forceSolarElev ?: world?.worldTime?.solarElevationDeg
val soldegStr = (soldeg ?: 0.0).toIntAndFrac(3,2)
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 turbCol = if (WeatherMixer.forceTurbidity != null) ccO else ccG
@@ -292,15 +292,17 @@ class BasicDebugInfoWindow : UICanvas() {
if (ingame != null) {
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}",
(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}",
(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) {
App.fontSmallNumbers.draw(batch, "${ccM}Particles $ccG${(ingame as TerrarumIngame).particlesActive}",
(TinyAlphNum.W * 2 + 41 * 8).toFloat(), App.scr.height - TinyAlphNum.H * 2f)
App.fontSmallNumbers.draw(batch, "${ccM}Particles $ccG${(ingame as TerrarumIngame).particlesActive}$ccY/$ccG${(ingame as TerrarumIngame).PARTICLES_MAX}",
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}",

View File

@@ -59,7 +59,7 @@ import kotlin.math.roundToInt
* Created by minjaesong on 2015-12-31.
*/
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))
customPositioning: Boolean = false, // mainly used by vital meter
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.utils.Disposable
import net.torvald.terrarum.App
import net.torvald.terrarum.ControlPresets
import net.torvald.terrarum.FlippingSpriteBatch
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gamecontroller.KeyToggler
@@ -25,8 +26,8 @@ import net.torvald.terrarum.modulebasegame.TerrarumIngame
* Created by minjaesong on 2015-12-31.
*/
class UIHandler(//var UI: UICanvas,
var toggleKeyLiteral: Int? = null,
var toggleButtonLiteral: Int? = null,
var toggleKeyLiteral: String? = null, // string key of the config
var toggleButtonLiteral: String? = null, // string key of the config
// UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int))
var customPositioning: Boolean = false, // mainly used by vital meter
var doNotWarnConstant: Boolean = false,
@@ -130,12 +131,13 @@ void main() {
//UI.handler = this
}
private val toggleKey: Int?; get() = toggleKeyLiteral // to support in-screen keybind changing
private val toggleButton: Int?; get() = toggleButtonLiteral // to support in-screen keybind changing
// ControlPresets.getKey(toggleKeyLiteral)
val toggleKey: Int?; get() = ControlPresets.getKey(toggleKeyLiteral).let { if (it == -1) null else it } // 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.
@@ -207,8 +209,8 @@ void main() {
else uiTogglerFunctionDefault!!.invoke(this)
}
toggleKeyExtra.forEachIndexed { index, getKey ->
if (Gdx.input.isKeyJustPressed(getKey())) {
toggleKeyExtra.forEachIndexed { index, control ->
if (Gdx.input.isKeyJustPressed(ControlPresets.getKey(control))) {
toggleKeyExtraAction[index].invoke(this)
}
}

View File

@@ -443,7 +443,7 @@ class UIItemTextLineInput(
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()
imeOn = !imeOn
KeyToggler.forceSet(App.getConfigInt("control_key_toggleime"), imeOn)
KeyToggler.forceSet(ControlPresets.getKey("control_key_toggleime"), imeOn)
}
private fun resetIME() {

View File

@@ -1,8 +1,13 @@
package net.torvald.terrarum.weather
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 java.util.*
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.absoluteValue
/**
* Note: Colour maps are likely to have sparse data points
@@ -12,10 +17,49 @@ import java.util.*
* Created by minjaesong on 2016-07-11.
*/
data class BaseModularWeather(
var skyboxGradColourMap: GdxColorMap, // row 0: skybox grad top, row 1: skybox grad bottom, row 2: sunlight (RGBA)
val daylightClut: GdxColorMap,
val classification: String,
var extraImages: ArrayList<Texture>,
val mixFrom: String? = null,
val mixPercentage: Double? = null
)
val json: JsonValue,
var skyboxGradColourMap: GdxColorMap, // row 0: skybox grad top, row 1: skybox grad bottom, row 2: sunlight (RGBA)
val daylightClut: GdxColorMap,
val classification: String,
val cloudChance: Float,
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.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
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 net.torvald.gdx.graphics.Cvec
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.clut.Skybox
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.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.util.SortedArrayList
import java.io.File
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
@@ -50,10 +63,11 @@ internal object WeatherMixer : RNGConsumer {
lateinit var mixedWeather: BaseModularWeather
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
const val WEATHER_GENERIC = "generic"
const val WEATHER_GENERIC_RAIN = "genericrain"
const val WEATHER_OVERCAST = "overcast"
// TODO add weather classification indices manually
// 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)
}
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 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) {
super.loadFromSave(s0, s1)
currentWeather = weatherList[WEATHER_GENERIC]!![0]
internalReset(s0, s1)
initClouds()
}
fun internalReset() = internalReset(RNG.state0, RNG.state1)
@@ -90,6 +111,15 @@ internal object WeatherMixer : RNGConsumer {
astrumOffX = s0.and(0xFFFFL).toFloat() / 65535f * starmapTex.regionWidth
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 {
@@ -121,6 +151,7 @@ internal object WeatherMixer : RNGConsumer {
// initialise
try {
weatherList["titlescreen"] = arrayListOf(weatherList[WEATHER_GENERIC]!![0].copy(windSpeed = 1f))
currentWeather = weatherList[WEATHER_GENERIC]!![0]
nextWeather = getRandomWeather(WEATHER_GENERIC)
}
@@ -128,10 +159,16 @@ internal object WeatherMixer : RNGConsumer {
e.printStackTrace()
val defaultWeather = BaseModularWeather(
JsonValue(JsonValue.ValueType.`object`),
GdxColorMap(1, 3, Color(0x55aaffff), Color(0xaaffffff.toInt()), Color.WHITE),
GdxColorMap(2, 2, Color.WHITE, Color.WHITE, Color.WHITE, Color.WHITE),
"default",
ArrayList<Texture>()
0f,
0f,
0f,
Vector2(1f, 1f),
Vector2(0f, 0f),
listOf()
)
currentWeather = defaultWeather
@@ -147,6 +184,9 @@ internal object WeatherMixer : RNGConsumer {
// currentWeather = weatherList[WEATHER_GENERIC]!![0] // force set weather
updateWind(delta, world)
updateClouds(delta, world)
if (!globalLightOverridden) {
world.globalLight = WeatherMixer.globalLightNow
@@ -154,6 +194,328 @@ internal object WeatherMixer : RNGConsumer {
}
private fun FloatArray.shiftAndPut(f: Float) {
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
@@ -166,6 +528,21 @@ internal object WeatherMixer : RNGConsumer {
*/
internal fun render(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) {
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
@@ -223,20 +600,20 @@ internal object WeatherMixer : RNGConsumer {
val astrumY = ((world.worldTime.TIME_T / WorldTime.DIURNAL_MOTION_LENGTH) % 1f) * starmapTex.regionHeight
batch.inUse {
batch.shader = shaderBlendMax
shaderBlendMax.setUniformi("tex1", 1)
shaderBlendMax.setUniformf("drawOffsetSize", 0f, gradY, App.scr.wf, gH)
shaderBlendMax.setUniform4fv("uvA", uvs, 0, 4)
shaderBlendMax.setUniform4fv("uvB", uvs, 4, 4)
shaderBlendMax.setUniform4fv("uvC", uvs, 8, 4)
shaderBlendMax.setUniform4fv("uvD", uvs, 12, 4)
shaderBlendMax.setUniform4fv("uvE", uvs, 16, 4)
shaderBlendMax.setUniform4fv("uvF", uvs, 20, 4)
shaderBlendMax.setUniform4fv("uvG", uvs, 24, 4)
shaderBlendMax.setUniform4fv("uvH", uvs, 28, 4)
shaderBlendMax.setUniformf("texBlend", mornNoonBlend, turbX, albX, 0f)
shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY)
shaderBlendMax.setUniformf("randomNumber",
batch.shader = shaderAstrum
shaderAstrum.setUniformi("tex1", 1)
shaderAstrum.setUniformf("drawOffsetSize", 0f, gradY, App.scr.wf, gH)
shaderAstrum.setUniform4fv("uvA", uvs, 0, 4)
shaderAstrum.setUniform4fv("uvB", uvs, 4, 4)
shaderAstrum.setUniform4fv("uvC", uvs, 8, 4)
shaderAstrum.setUniform4fv("uvD", uvs, 12, 4)
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(37L) xor 862L + 31L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(23L) xor 1639L + 29L).and(1023).toFloat(),
@@ -366,20 +743,36 @@ internal object WeatherMixer : RNGConsumer {
val skyboxInJson = JSON.getString("skyboxGradColourMap")
val lightbox = JSON.getString("daylightClut")
val extraImagesPath = JSON.get("extraImages").asStringArray()
val skybox = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${skyboxInJson}"))
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 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?
try { mixFrom = JSON.getString("mixFrom") }
@@ -394,20 +787,28 @@ internal object WeatherMixer : RNGConsumer {
return BaseModularWeather(
json = JSON,
skyboxGradColourMap = skybox,
daylightClut = daylight,
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() {
weatherList.values.forEach { list ->
list.forEach { weather ->
weather.extraImages.forEach { it.tryDispose() }
weather.clouds.forEach { it.spriteSheet.dispose() }
}
}
starmapTex.texture.dispose()
shaderBlendMax.dispose()
shaderAstrum.dispose()
shaderClouds.dispose()
}
}

View File

@@ -0,0 +1,33 @@
package net.torvald.terrarum.weather
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.math.Vector3
import com.badlogic.gdx.utils.Disposable
/**
* Created by minjaesong on 2023-08-21.
*/
abstract class WeatherObject : Disposable {
/** vec3(posX, posY, scale) */
var pos: Vector3 = Vector3(0f, 0f, 1f)
var posX: Float
get() = pos.x
set(value) { pos.x = value }
var posY: Float
get() = pos.y
set(value) { pos.y = value }
var posZ: Float
get() = pos.z
set(value) { pos.z = value }
var scale: Float = 1f
var alpha: Float = 1f
var flagToDespawn = false
abstract fun update()
abstract fun render(batch: SpriteBatch, offsetX: Float, offsetY: Float)
}

View File

@@ -0,0 +1,137 @@
package net.torvald.terrarum.weather
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.math.Vector3
import com.jme3.math.FastMath
import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.math.sign
/**
* Created by minjaesong on 2023-08-21.
*/
class WeatherObjectCloud(private val texture: TextureRegion, private val flipW: Boolean) : WeatherObject(), Comparable<WeatherObjectCloud> {
override fun update() {
throw UnsupportedOperationException()
}
var life = 0; private set
var despawnCode = ""; private set
private val lifespan = 40000 + ((Math.random() + Math.random()) * 20000).roundToInt() // triangular distibution of 40000..80000
private fun getZflowMult(z: Float) = z / ((z / 4f).pow(1.5f))
private var eigenAlpha = 0f
/**
* FlowVector: In which direction the cloud flows. Vec3(dX, dY, dScale)
* Resulting vector: (x + dX, y + dY, scale * dScale)
*/
fun update(flowVector: Vector3) {
pos.add(
flowVector.cpy().
scl(1f, 1f, getZflowMult(posZ)). // this will break the perspective if flowVector.z.abs() is close to 1, but it has to be here to "keep the distance"
scl(vecMult)
)
eigenAlpha = if (posZ < 1f) posZ.pow(0.5f) else -((posZ - 1f) / ALPHA_ROLLOFF_Z) + 1f
alpha = eigenAlpha * if (life < lifespan) 1f else 1f - (life - lifespan) / OLD_AGE_DECAY
val lrCoord = screenCoordBottomLRforDespawnCalculation
if (lrCoord.x > WeatherMixer.oobMarginR || lrCoord.z < WeatherMixer.oobMarginL || posZ !in 0.0001f..ALPHA_ROLLOFF_Z + 1f || alpha < 0f) {
flagToDespawn = true
despawnCode = if (lrCoord.x > WeatherMixer.oobMarginR) "OUT_OF_SCREEN_RIGHT"
else if (lrCoord.z < WeatherMixer.oobMarginL) "OUT_OF_SCREEN_LEFT"
else if (posZ < 0.0001f) "OUT_OF_SCREEN_TOO_CLOSE"
else if (posZ > ALPHA_ROLLOFF_Z + 1f) "OUT_OF_SCREEN_TOO_FAR"
else if (life >= lifespan + OLD_AGE_DECAY) "OLD_AGE"
else if (alpha < 0f) "ALPHA_BELOW_ZERO"
else "UNKNOWN"
}
else {
life += 1
}
}
private val w = App.scr.halfwf
private val h = App.scr.hf * 0.5f
private val vecMult = Vector3(1f, 1f, 1f / (4f * h))
/**
* X/Y position is a bottom-centre point of the image
* Shader must be prepared prior to the render() call
*/
override fun render(batch: SpriteBatch, offsetX: Float, offsetY: Float) {
val sc = screenCoord
if (flipW)
batch.draw(texture, sc.x + texture.regionWidth / posZ, sc.y, -texture.regionWidth * sc.z, texture.regionHeight * sc.z)
else
batch.draw(texture, sc.x, sc.y, texture.regionWidth * sc.z, texture.regionHeight * sc.z)
}
/**
* vec3(screen X, screenY, draw scale)
*/
val screenCoord: Vector3
get() {
val x = posX - texture.regionWidth * scale * 0.5f
val y = posY - texture.regionHeight * scale
val z = posZ // must be at least 1.0
val drawX = (x + w * (z-1)) / z
val drawY = (y + h * (z-1)) / z
val drawScale = scale / z
return Vector3(drawX, drawY, drawScale)
}
/**
* vec3(screen-X of bottom-left point, screen-Y, screen-X of bottom-right point)
*/
val screenCoordBottomLR: Vector3
get() {
val xL = posX - texture.regionWidth * scale * 0.5f
val xR = posX + texture.regionWidth * scale * 0.5f
val y = posY - texture.regionHeight * scale
val z = posZ // must be larger than 0
val drawXL = (xL + w * (z-1)) / z
val drawXR = (xR + w * (z-1)) / z
val drawY = (y + h * (z-1)) / z
return Vector3(drawXL, drawY, drawXR)
}
private val screenCoordBottomLRforDespawnCalculation: Vector3
get() {
val xL = posX - texture.regionWidth * scale * 0.5f
val xR = posX + texture.regionWidth * scale * 0.5f
val y = posY - texture.regionHeight * scale
val z = FastMath.interpolateLinear(posZ / ALPHA_ROLLOFF_Z, ALPHA_ROLLOFF_Z / 4f, ALPHA_ROLLOFF_Z)
val drawXL = (xL + w * (z-1)) / z
val drawXR = (xR + w * (z-1)) / z
val drawY = (y + h * (z-1)) / z
return Vector3(drawXL, drawY, drawXR)
}
override fun dispose() { /* cloud texture will be disposed of by the WeatherMixer */ }
override fun compareTo(other: WeatherObjectCloud): Int = (other.posZ - this.posZ).sign.toInt()
companion object {
fun screenXtoWorldX(screenX: Float, z: Float) = screenX * z - App.scr.halfwf * (z - 1f)
const val ALPHA_ROLLOFF_Z = 64f
const val OLD_AGE_DECAY = 4000f
}
}

View File

@@ -70,6 +70,9 @@ object WorldCamera {
var worldHeight = 0
private set
inline val camVector: com.badlogic.gdx.math.Vector2
get() = com.badlogic.gdx.math.Vector2(gdxCamX, gdxCamY)
fun update(world: GameWorld, player: ActorWithBody?) {
if (player == null) return

View File

@@ -1,4 +1,4 @@
#version 150
#version 400
#ifdef GL_ES
precision mediump float;
#endif
@@ -16,5 +16,6 @@ const vec2 boolean = vec2(0.0, 1.0);
void main(void) {
vec4 colorTex0 = texture(u_texture, v_texCoords); // lightmap (RGB) pre-mixed
vec4 colorTex1 = texture(tex1, v_texCoords); // lightmap (A) pre-mixed
fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + (colorTex0 * boolean.xxxy);
// fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + (colorTex0 * boolean.xxxy);
fragColor = fma(max(colorTex0, colorTex1), boolean.yyyx, colorTex0 * boolean.xxxy);
}

View File

@@ -1,4 +1,4 @@
#version 150
#version 400
#ifdef GL_ES
precision mediump float;
#endif
@@ -150,7 +150,10 @@ void main(void) {
); // c = c0..c1
fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + boolean.xxxy;
// fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + boolean.xxxy;
fragColor = fma(max(colorTex0, colorTex1), boolean.yyyx, boolean.xxxy);
// fragColor = colorTex1;
// fragColor = randomness * boolean.yyyx + boolean.xxxy;
// fragColor = (randomness.rrrr + (colorTex1 * vec4(2.0, -2.0, 2.0, 1.0))) * boolean.yyyx + boolean.xxxy;

30
src/shaders/clouds.frag Normal file
View File

@@ -0,0 +1,30 @@
#version 400
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif
in LOWP vec4 v_color; // lightCol.rgb + cloud's alpha
in vec2 v_texCoords;
uniform sampler2D u_texture;
out vec4 fragColor;
const vec2 boolean = vec2(0.0, 1.0);
uniform vec2 gamma = vec2(10, 2.0); // vec2(gamma for RGB, gamma for A)
uniform LOWP vec4 shadeCol;
void main() {
// r: bw diffuse map, g: normal, b: normal, a: bw diffuse alpha
vec4 inCol = texture(u_texture, v_texCoords);
vec4 rawCol = pow(inCol, gamma.xxxy);
// do gradient mapping here
vec4 outCol = fma(mix(shadeCol, v_color, rawCol.r), boolean.yyyx, rawCol * boolean.xxxy);
fragColor = outCol * fma(v_color, boolean.xxxy, boolean.yyyx);
}

View File

@@ -1,4 +1,4 @@
#version 150
#version 400
#ifdef GL_ES
precision mediump float;
#endif
@@ -41,7 +41,8 @@ vec4 getDitherredDot(vec4 inColor) {
void main(void) {
float scale = v_texCoords.y * (1.0 - parallax_size) + (parallax_size / 2.0) + (parallax * parallax_size / 2.0);
float parallaxAdder = 0.5 * (parallax + 1.0) * parallax_size;
float scale = fma(v_texCoords.y, 1.0 - parallax_size, parallaxAdder);
float zoomSamplePoint = (1.0 - zoomInv) / 2.0;// will never quite exceed 0.5

View File

@@ -1,4 +1,4 @@
#version 150
#version 400
in vec4 v_color;
in vec2 v_texCoords;
uniform sampler2D u_texture;
@@ -11,5 +11,6 @@ void main(void) {
vec4 incolour = texture(u_texture, v_texCoords);
float lum = dot(incolour * desaturate, boolean.yyyx) * 0.5 + 0.5;
fragColor = v_color * (vec4(lum) * boolean.yyyx + incolour * boolean.xxxy);
// fragColor = v_color * (vec4(lum) * boolean.yyyx + incolour * boolean.xxxy);
fragColor = v_color * fma(vec4(lum), boolean.yyyx, incolour * boolean.xxxy);
}

View File

@@ -3,7 +3,7 @@
* http://momentsingraphics.de/BlueNoise.html
*/
#version 150
#version 400
#ifdef GL_ES
precision mediump float;
#endif
@@ -79,7 +79,7 @@ void main(void) {
// Dither the output
vec4 graded = ycocg_to_rgb * newColour;
vec4 selvec = getDitherredDot(graded);
vec4 outcol = selvec * boolean.yyyx + boolean.xxxy; // use quantised RGB but not the A
vec4 outcol = fma(selvec, boolean.yyyx, boolean.xxxy); // use quantised RGB but not the A
fragColor = outcol;
// ivec4 bytes = ivec4(255.0 * outcol);

View File

@@ -3,7 +3,7 @@
* http://momentsingraphics.de/BlueNoise.html
*/
#version 150
#version 400
#ifdef GL_ES
precision mediump float;
#endif
@@ -60,5 +60,5 @@ void main(void) {
// Dither the output
vec4 graded = ycocg_to_rgb * newColour;
fragColor = graded * boolean.yyyx + boolean.xxxy; // use quantised RGB but not the A
fragColor = fma(graded, boolean.yyyx, boolean.xxxy); // use quantised RGB but not the A
}

View File

@@ -2,7 +2,7 @@
*/
#version 150
#version 400
#ifdef GL_ES
precision mediump float;
#endif
@@ -107,7 +107,7 @@ void main() {
vec4 finalBreakage = drawBreakage * texture(tilesAtlas, finalUVCoordForBreakage); // drawBreakeage = 0 to not draw, = 1 to draw
vec4 finalColor =mix(finalTile, finalBreakage, finalBreakage.a) * bc.xxxy + (finalTile * bc.yyyx);
vec4 finalColor = fma(mix(finalTile, finalBreakage, finalBreakage.a), bc.xxxy, finalTile * bc.yyyx);
fragColor = mix(colourFilter, colourFilter * finalColor, mulBlendIntensity);

View File

@@ -2,7 +2,7 @@
*/
#version 150
#version 400
#ifdef GL_ES
precision mediump float;
#endif
@@ -121,5 +121,5 @@ void main() {
vec2 entry = mod(gl_FragCoord.xy, vec2(bayerSize, bayerSize));
float bayerThreshold = float(bayer[int(entry.y) * int(bayerSize) + int(entry.x)]) / bayerDivider;
fragColor = undithered * bc.xxxy + vec4((undithered.a > bayerThreshold) ? 1.0 : 0.0) * bc.yyyx;
fragColor = fma(undithered, bc.xxxy, vec4((undithered.a > bayerThreshold) ? 1.0 : 0.0) * bc.yyyx);
}