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.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.graphics.glutils.FrameBuffer
import net.torvald.terrarum.App import net.torvald.terrarum.*
import net.torvald.terrarum.FlippingSpriteBatch
import net.torvald.terrarum.blendNormalStraightAlpha
import net.torvald.terrarum.inAction
import net.torvald.terrarum.langpack.Lang import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulecomputers.gameactors.FixtureHomeComputer import net.torvald.terrarum.modulecomputers.gameactors.FixtureHomeComputer
import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.ui.Toolkit
@@ -18,8 +15,8 @@ import net.torvald.tsvm.peripheral.GraphicsAdapter
import net.torvald.unicode.* import net.torvald.unicode.*
internal class UIHomeComputer : UICanvas( 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 toggleKeyLiteral = null,
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"), toggleButtonLiteral = "control_gamepad_start",
) { ) {
override var width = 640 override var width = 640
override var height = 480 override var height = 480
@@ -45,7 +42,7 @@ internal class UIHomeComputer : UICanvas(
private val fbo = FrameBuffer(Pixmap.Format.RGBA8888, width, height, false) private val fbo = FrameBuffer(Pixmap.Format.RGBA8888, width, height, false)
private val controlHelp = 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_T$KEYCAP_R Terminate\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_S Reset\u3000" + "$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_S Reset\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_Q SysRq" "$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_Q SysRq"

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",

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" : "재설정",

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

@@ -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", "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

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

View File

@@ -18,6 +18,9 @@ rm $DESTDIR/graphics/*.bat
rm $DESTDIR/keylayout/*.not_ime rm $DESTDIR/keylayout/*.not_ime
rm $DESTDIR/mods/basegame/blocks/*.gz rm $DESTDIR/mods/basegame/blocks/*.gz
rm $DESTDIR/mods/basegame/blocks/*.txt 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/basegame/sounds
rm -r $DESTDIR/mods/dwarventech 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

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

@@ -269,8 +269,8 @@ public class App implements ApplicationListener {
Gdx.gl20.glViewport(0, 0, width, height); Gdx.gl20.glViewport(0, 0, width, height);
} }
public static final int TICKS = 64; public static final int TICK_SPEED = 64;
public static final float UPDATE_RATE = 1f / TICKS; // apparent framerate will be limited by update rate 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;
@@ -1378,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

@@ -67,7 +67,7 @@ basegame
// 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 // 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 // // CONFIGURATION FOR TILE MAKER //

View File

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

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

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

@@ -255,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

View File

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

@@ -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() { 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
@@ -268,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"))
} }
@@ -348,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)) {
@@ -376,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

@@ -46,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

@@ -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 " +

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
@@ -162,10 +161,10 @@ class UIInventoryFull(
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" +
@@ -175,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" +
@@ -183,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"]}"
@@ -249,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)
@@ -263,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"]}"
@@ -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

@@ -18,14 +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"
) { ) {
init { init {
handler.allowESCtoClose = false 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 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,7 +41,7 @@ object UITitleRemoConYaml {
// todo add MENU_IO_IMPORT // todo add MENU_IO_IMPORT
val injectedMenuSingleCharSel = """ val injectedMenuSingleCharSel = """
- MENU_IO_IMPORT - 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
""" """

View File

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

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

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

@@ -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,8 +26,8 @@ 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,
@@ -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,8 +209,8 @@ 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)
} }
} }

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

@@ -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,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 var turbidity = 1.0; private set
private var gH = 1.8f * App.scr.height private var gH = 1.8f * App.scr.height
// private var gH = 0.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) { 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 parallaxDomainSize = 400f
@@ -223,20 +600,20 @@ 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("drawOffsetSize", 0f, gradY, App.scr.wf, gH) shaderAstrum.setUniformf("drawOffsetSize", 0f, gradY, App.scr.wf, gH)
shaderBlendMax.setUniform4fv("uvA", uvs, 0, 4) shaderAstrum.setUniform4fv("uvA", uvs, 0, 4)
shaderBlendMax.setUniform4fv("uvB", uvs, 4, 4) shaderAstrum.setUniform4fv("uvB", uvs, 4, 4)
shaderBlendMax.setUniform4fv("uvC", uvs, 8, 4) shaderAstrum.setUniform4fv("uvC", uvs, 8, 4)
shaderBlendMax.setUniform4fv("uvD", uvs, 12, 4) shaderAstrum.setUniform4fv("uvD", uvs, 12, 4)
shaderBlendMax.setUniform4fv("uvE", uvs, 16, 4) shaderAstrum.setUniform4fv("uvE", uvs, 16, 4)
shaderBlendMax.setUniform4fv("uvF", uvs, 20, 4) shaderAstrum.setUniform4fv("uvF", uvs, 20, 4)
shaderBlendMax.setUniform4fv("uvG", uvs, 24, 4) shaderAstrum.setUniform4fv("uvG", uvs, 24, 4)
shaderBlendMax.setUniform4fv("uvH", uvs, 28, 4) shaderAstrum.setUniform4fv("uvH", uvs, 28, 4)
shaderBlendMax.setUniformf("texBlend", mornNoonBlend, turbX, albX, 0f) shaderAstrum.setUniformf("texBlend", mornNoonBlend, turbX, albX, 0f)
shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY) shaderAstrum.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY)
shaderBlendMax.setUniformf("randomNumber", 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(),
@@ -366,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") }
@@ -394,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()
} }
} }

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 var worldHeight = 0
private set private set
inline val camVector: com.badlogic.gdx.math.Vector2
get() = com.badlogic.gdx.math.Vector2(gdxCamX, gdxCamY)
fun update(world: GameWorld, player: ActorWithBody?) { fun update(world: GameWorld, player: ActorWithBody?) {
if (player == null) return if (player == null) return

View File

@@ -1,4 +1,4 @@
#version 150 #version 400
#ifdef GL_ES #ifdef GL_ES
precision mediump float; precision mediump float;
#endif #endif
@@ -16,5 +16,6 @@ const vec2 boolean = vec2(0.0, 1.0);
void main(void) { void main(void) {
vec4 colorTex0 = texture(u_texture, v_texCoords); // lightmap (RGB) pre-mixed vec4 colorTex0 = texture(u_texture, v_texCoords); // lightmap (RGB) pre-mixed
vec4 colorTex1 = texture(tex1, v_texCoords); // lightmap (A) 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 #ifdef GL_ES
precision mediump float; precision mediump float;
#endif #endif
@@ -150,7 +150,10 @@ void main(void) {
); // c = c0..c1 ); // 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 = colorTex1;
// fragColor = randomness * boolean.yyyx + boolean.xxxy; // fragColor = randomness * boolean.yyyx + boolean.xxxy;
// fragColor = (randomness.rrrr + (colorTex1 * vec4(2.0, -2.0, 2.0, 1.0))) * 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 #ifdef GL_ES
precision mediump float; precision mediump float;
#endif #endif
@@ -41,7 +41,8 @@ vec4 getDitherredDot(vec4 inColor) {
void main(void) { 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 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 vec4 v_color;
in vec2 v_texCoords; in vec2 v_texCoords;
uniform sampler2D u_texture; uniform sampler2D u_texture;
@@ -11,5 +11,6 @@ void main(void) {
vec4 incolour = texture(u_texture, v_texCoords); vec4 incolour = texture(u_texture, v_texCoords);
float lum = dot(incolour * desaturate, boolean.yyyx) * 0.5 + 0.5; 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 * http://momentsingraphics.de/BlueNoise.html
*/ */
#version 150 #version 400
#ifdef GL_ES #ifdef GL_ES
precision mediump float; precision mediump float;
#endif #endif
@@ -79,7 +79,7 @@ void main(void) {
// Dither the output // Dither the output
vec4 graded = ycocg_to_rgb * newColour; vec4 graded = ycocg_to_rgb * newColour;
vec4 selvec = getDitherredDot(graded); 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; fragColor = outcol;
// ivec4 bytes = ivec4(255.0 * outcol); // ivec4 bytes = ivec4(255.0 * outcol);

View File

@@ -3,7 +3,7 @@
* http://momentsingraphics.de/BlueNoise.html * http://momentsingraphics.de/BlueNoise.html
*/ */
#version 150 #version 400
#ifdef GL_ES #ifdef GL_ES
precision mediump float; precision mediump float;
#endif #endif
@@ -60,5 +60,5 @@ void main(void) {
// Dither the output // Dither the output
vec4 graded = ycocg_to_rgb * newColour; 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 #ifdef GL_ES
precision mediump float; precision mediump float;
#endif #endif
@@ -107,7 +107,7 @@ void main() {
vec4 finalBreakage = drawBreakage * texture(tilesAtlas, finalUVCoordForBreakage); // drawBreakeage = 0 to not draw, = 1 to draw 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); fragColor = mix(colourFilter, colourFilter * finalColor, mulBlendIntensity);

View File

@@ -2,7 +2,7 @@
*/ */
#version 150 #version 400
#ifdef GL_ES #ifdef GL_ES
precision mediump float; precision mediump float;
#endif #endif
@@ -121,5 +121,5 @@ void main() {
vec2 entry = mod(gl_FragCoord.xy, vec2(bayerSize, bayerSize)); vec2 entry = mod(gl_FragCoord.xy, vec2(bayerSize, bayerSize));
float bayerThreshold = float(bayer[int(entry.y) * int(bayerSize) + int(entry.x)]) / bayerDivider; 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);
} }