Compare commits

..

80 Commits

Author SHA1 Message Date
minjaesong
453459e3b6 fix: some UIs won't fade in/out on open/close 2023-07-03 21:35:46 +09:00
minjaesong
bad72dd353 simple SAVING ui for teleportation 2023-07-03 20:26:30 +09:00
minjaesong
13185f0565 preliminary gui thing for teleportation 2023-07-03 17:46:57 +09:00
minjaesong
fcaf4c97f1 seemingly working world teleporter (no saving... ui tho) 2023-07-03 00:10:46 +09:00
minjaesong
9c396e7b8d new world via teleporter wip 2023-07-02 18:54:04 +09:00
minjaesong
afb7dff5d2 some comment elaboration 2023-07-02 01:25:34 +09:00
minjaesong
5d0514040c lang split into two files 2023-06-30 16:20:57 +09:00
minjaesong
7c1806946b worldportal: showing tooltip to tell why the button is disabled 2023-06-30 03:07:45 +09:00
minjaesong
e5e02681b8 weather only change on titlescreen 2023-06-30 00:53:46 +09:00
minjaesong
6db3baf691 clearing up interpolation functions 2023-06-30 00:14:28 +09:00
minjaesong
07cbcbe79b better title screen camera smoothing 2023-06-29 22:50:44 +09:00
minjaesong
57a9f7febc graph guidance colour scheme change; added easter egg where the camera might pan towards left 2023-06-29 02:56:33 +09:00
minjaesong
16cfaaea27 titlescreen follows the terrain better 2023-06-28 22:49:45 +09:00
minjaesong
72c742897e fix: load menu buttons are pushed when they should not listen to the touchdown event 2023-06-28 17:47:04 +09:00
minjaesong
23af64deb4 proper savegame backups sorting 2023-06-28 16:42:38 +09:00
minjaesong
bb017fa9b7 gui for load savegame 2023-06-28 16:10:15 +09:00
minjaesong
1745bb16db save deletion works but gui is still wip 2023-06-28 11:05:28 +09:00
minjaesong
370583d1af actual red button for DELETE 2023-06-28 00:55:36 +09:00
minjaesong
66b651c627 delete character file gui wip 2023-06-27 22:46:16 +09:00
minjaesong
c5874a7f3d finally working again: create new character
todo: make delete character work
2023-06-27 21:13:51 +09:00
minjaesong
057905c3b7 thumb generation for player saves 2023-06-27 01:21:05 +09:00
minjaesong
2b50562002 save juggling for autosaves 2023-06-26 23:10:52 +09:00
minjaesong
73a8198378 fix: loading a game would load the oldest backup save 2023-06-26 21:43:25 +09:00
minjaesong
1ef479124e actually working load manual/auto button 2023-06-26 20:18:00 +09:00
minjaesong
e5e8028b3f fix: clickOnceListener would not fired if screen is magnified 2023-06-26 19:07:25 +09:00
minjaesong
739b51af95 manual/auto selection for savegame loading 2023-06-26 18:18:59 +09:00
minjaesong
f9f49ab63c new savegame loader is not quite working yet 2023-06-26 01:09:47 +09:00
minjaesong
a497463349 some ui updates 2023-06-25 20:46:52 +09:00
minjaesong
253db56c4f the baloon now has opacity control 2023-06-25 13:58:37 +09:00
minjaesong
3d13941060 new savegame loading wip 2023-06-24 23:44:48 +09:00
minjaesong
592e489411 warning for apple rosetta 2023-06-24 02:06:22 +09:00
minjaesong
49b2011ea0 a little bit generalised titlescreen warning printing 2023-06-24 01:12:43 +09:00
minjaesong
61e6255b52 some warning for apple rosetta 2023-06-24 01:03:58 +09:00
minjaesong
2e956f89f5 fix for edge case where 64-bit x86 CPU not reporting itself as AMD64 2023-06-24 00:40:59 +09:00
minjaesong
e8ffd1f844 proper bootstrap codes 2023-06-23 18:44:05 +09:00
minjaesong
0882145f9c some autosave stuffs; bootloader to actually use bundled runtime 2023-06-23 17:29:49 +09:00
minjaesong
28e2179e44 don't xstartonfirstthread the bootstrapper 2023-06-23 12:11:19 +09:00
minjaesong
48eb1ffd8f printout child proc's out and err to console 2023-06-22 23:11:34 +09:00
minjaesong
6daccb2e62 locales 2023-06-22 22:27:56 +09:00
minjaesong
8c9d5a26fb more code trimming 2023-06-22 22:05:10 +09:00
minjaesong
ee3e5b14cd rm unused code snippet 2023-06-22 21:21:09 +09:00
minjaesong
5c39df9080 bootstrapper for the App so that the user can change the max heap in-game 2023-06-22 21:08:09 +09:00
minjaesong
5d77694316 windows build script now produces .exe 2023-06-21 23:49:42 +09:00
minjaesong
cf111d2507 world portal writing current world to actorvalue 'worldportaldict' if it's not there 2023-06-20 13:45:32 +09:00
minjaesong
724ace3f00 for now ui simply closes on teleport target selection 2023-06-20 13:22:14 +09:00
minjaesong
1457cbffb3 worldportal: submitting teleportrequest works, needs UI refinement 2023-06-20 00:15:53 +09:00
minjaesong
7a42066392 electric: rising/falling edge and level detection 2023-06-19 18:42:08 +09:00
minjaesong
528b975350 wiresim: signal sinking actors are only getting updated when the sim calls for 2023-06-19 16:34:39 +09:00
minjaesong
9e9064dd55 world portal: world search is now new world 2023-06-19 00:50:55 +09:00
minjaesong
138c6d22d2 some font stuffs for ui 2023-06-18 21:40:06 +09:00
minjaesong
a33f0e7ab4 world search ui integrated to world portal ui 2023-06-18 21:29:18 +09:00
minjaesong
93c427473d inventory backdrop is now image 2023-06-18 16:02:25 +09:00
minjaesong
6b8798a19e single screen ui for world portal 2023-06-18 01:28:51 +09:00
minjaesong
376595d7cd fix: scroll controller for portal listing is 2 px shorter that it should 2023-06-18 00:40:55 +09:00
minjaesong
4cc52b5585 fix: storage chest ui would be shifted to left and any mouse button would trigger the action 2023-06-17 23:19:09 +09:00
minjaesong
0ff71f39fe list scroll for portallisting 2023-06-17 17:10:13 +09:00
minjaesong
13f487a562 inventory navbar to its own uiitem 2023-06-17 16:46:15 +09:00
minjaesong
0599ce91b1 reflection function update 2023-06-12 15:08:42 +09:00
minjaesong
3d5fd984d7 another text pos change 2023-06-06 18:23:28 +09:00
minjaesong
8d0d84fbf8 font update/ime fix 2023-06-06 18:16:34 +09:00
minjaesong
eb2c716691 code for new itemsheet format 2023-06-06 14:37:54 +09:00
minjaesong
ac53f821e2 new format for item sprites; code upcoming ;) 2023-06-05 23:38:47 +09:00
minjaesong
cd6df71347 world portal listing: back tray for texts 2023-06-04 21:21:48 +09:00
minjaesong
ac553ed156 serialiser filters Companion objs 2023-06-01 17:50:39 +09:00
minjaesong
8c5c986cbf wider inventory cells 2023-05-31 21:07:57 +09:00
minjaesong
a0f597865e using Delete instead of Delete World 2023-05-30 01:07:07 +09:00
minjaesong
bafd0d9f7c ui theme updates 2023-05-30 00:51:32 +09:00
minjaesong
e259fc2f3b world size classification is now Tiny-Small-Big-Huge 2023-05-29 20:43:17 +09:00
minjaesong
ebbb121b8c fix: fixture pickup avail check is now done properly 2023-05-29 20:10:39 +09:00
minjaesong
331e89b4df world portal listing gui 2023-05-29 17:51:15 +09:00
minjaesong
98a6c9ae70 world portal gui wip 2023-05-29 02:41:59 +09:00
minjaesong
1646871ddf removing 'small' world size for new worlds: gamedesign choice 2023-05-28 21:31:14 +09:00
minjaesong
76bfc0fde4 world portal gui wip 2023-05-28 20:55:47 +09:00
minjaesong
ef6f39632d world portal wip 2023-05-28 18:41:21 +09:00
minjaesong
a3ecd4a4f4 some adjustments for the quickslot and pie 2023-05-28 01:47:17 +09:00
minjaesong
065f80224f fix: light/shadebox would get tilewise size of 2 when their hitbox size is set to 16 2023-05-28 00:31:29 +09:00
minjaesong
34fb046968 minor changes to the graphics control panel 2023-05-27 23:31:21 +09:00
minjaesong
43da6cc5d8 adding Char to QuickDirtyLint 2023-05-23 20:50:19 +09:00
minjaesong
fccc2162f6 quick and dirty but working linter 2023-05-23 00:09:11 +09:00
minjaesong
c554df9b98 trying the android linter for code inspection 2023-05-22 21:33:39 +09:00
203 changed files with 5393 additions and 1231 deletions

View File

@@ -13,6 +13,10 @@
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-controllers-desktop-2.2.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/GetCpuName.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jxinput-1.0.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/commons-csv-1.8.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/prtree.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/Terrarum_Joise.jar" path-in-jar="/" />
@@ -72,21 +76,17 @@
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.11.0-natives-armeabi-v7a.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.11.0-natives-desktop.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-platform-1.11.0-natives-x86_64.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/kotlin-stdlib-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.21/kotlin-stdlib-common-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-test/1.8.21/kotlin-test-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.8.21/kotlin-reflect-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/graal-sdk-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/icu4j-71.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-22.3.1-edit.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.8.21/kotlin-stdlib-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.8.21/kotlin-stdlib-common-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-test/1.8.21/kotlin-test-1.8.21.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-scriptengine-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/regex-22.3.1-edit.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-api-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/icu4j-71.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/regex-22.3.1-edit.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-22.3.1-edit.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/graal-sdk-22.3.1.jar" path-in-jar="/" />
</root>
</artifact>
</component>

14
.idea/libraries/io_github_classgraph.xml generated Normal file
View File

@@ -0,0 +1,14 @@
<component name="libraryTable">
<library name="io.github.classgraph" type="repository">
<properties maven-id="io.github.classgraph:classgraph:4.8.157" />
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/classgraph-4.8.157.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$PROJECT_DIR$/lib/classgraph-4.8.157-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/classgraph-4.8.157-sources.jar!/" />
</SOURCES>
</library>
</component>

19
.idea/runConfigurations/Principii.xml generated Normal file
View File

@@ -0,0 +1,19 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Principii" type="Application" factoryName="Application" nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="net.torvald.terrarum.Principii" />
<module name="TerrarumBuild" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="net.torvald.terrarum.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="BuildArtifacts" enabled="true">
<artifact name="ModuleComputers" />
<artifact name="TerrarumBuild" />
</option>
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -16,5 +16,6 @@
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="jetbrains.kotlin.reflect" level="project" />
<orderEntry type="library" name="jetbrains.kotlin.test" level="project" />
<orderEntry type="library" name="io.github.classgraph" level="project" />
</component>
</module>

View File

@@ -195,17 +195,10 @@ internal class UIHomeComputer : UICanvas(
}
override fun doOpening(delta: Float) {
super.doOpening(delta)
fixture.startVM()
}
override fun doClosing(delta: Float) {
}
override fun endOpening(delta: Float) {
}
override fun endClosing(delta: Float) {
}
override fun dispose() {
fbo.dispose()

View File

@@ -24,5 +24,6 @@
<orderEntry type="library" name="badlogicgames.gdx.backend.lwjgl3" level="project" />
<orderEntry type="library" name="jetbrains.kotlin.reflect" level="project" />
<orderEntry type="library" name="jetbrains.kotlin.test" level="project" />
<orderEntry type="library" name="io.github.classgraph" level="project" />
</component>
</module>

Binary file not shown.

BIN
assets/graphics/gui/hrule.tga LFS Normal file

Binary file not shown.

View File

@@ -7,12 +7,10 @@
"MENU_LABEL_PRESS_START_SYMBOL": "Press >",
"MENU_MODULES" : "Modules",
"MENU_CREDIT_GPL_DNT" : "GPL",
"MENU_LABEL_JVM_DNT" : "JVM",
"GAME_ACTION_MOVE_VERB" : "Move",
"GAME_ACTION_ZOOM" : "Zoom",
"MENU_LABEL_RESET" : "Reset",
"GAME_32BIT_WARNING1": "It looks like youre running a 32-Bit version of Java.",
"GAME_32BIT_WARNING2": "Please download and install the latest 64-Bit Java at:",
"GAME_32BIT_WARNING3": "https://www.java.com/en/download/",
"MENU_OPTION_STREAMERS_LAYOUT": "Chat Overlay",
"MENU_LABEL_RESTART_REQUIRED": "Restart Required",
"MENU_LABEL_KEYBOARD_LAYOUT": "Keyboard Layout",
@@ -21,10 +19,19 @@
"MENU_OPTIONS_BLUR": "Blur",
"MENU_OPTIONS_PARTICLES": "Particles",
"MENU_IO_IMPORT": "Import",
"APP_NOMODULE_1": "No Module is currently loaded.",
"APP_NOMODULE_2": "Please configure your Load Order and restart:",
"MENU_LABEL_KEYCONFIG_HELP1": "Click On the Keycap to Assign Actions",
"MENU_LABEL_IME_TOGGLE": "Toggle IME",
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "Paste from Cliboard",
"MENU_OPTIONS_PERFORMANCE": "Performance"
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "Paste from Clipboard",
"MENU_OPTIONS_PERFORMANCE": "Performance",
"MENU_LABEL_DELETE": "Delete",
"MENU_OPTIONS_JVM_HEAP_MAX": "Max Heap Memory",
"MENU_OPTIONS_AUTOSAVE": "Autosave",
"CONTEXT_TIME_MINUTE_PLURAL": "Minutes",
"CONTEXT_TIME_SECOND_PLURAL": "Seconds",
"MENU_LABEL_SYSTEM_INFO": "System Info",
"MENU_OPTIONS_NOTIFICATION_DISPLAY_DURATION": "Show notification for",
"MENU_LABEL_STREAMING": "Livestreaming",
"MENU_LABEL_EXTRA_JVM_ARGUMENTS": "Extra Arguments",
"MENU_IO_MANUAL_SAVE": "Manual Save",
"MENU_IO_AUTOSAVE": "Autosave",
"MENU_OPTIONS_DEBUG_CONSOLE": "Debug Console"
}

View File

@@ -0,0 +1,16 @@
{
"GAME_32BIT_WARNING1": "32비트 버전의 Java를 사용중인 것 같습니다.",
"GAME_32BIT_WARNING2": "아래 링크에서 최신 64비트 Java를 내려받아 설치해주세요.",
"GAME_32BIT_WARNING3": "https://www.java.com/ko/download/",
"GAME_APPLE_ROSETTA_WARNING1": "Apple Silicon이 탑재된 Mac을 사용 중이지만 x86 빌드의 게임을 실행 중입니다.",
"GAME_APPLE_ROSETTA_WARNING2": "최적의 성능과 게임 경험을 위해 Apple Silicon용 빌드의 게임을 이용해 주십시오.",
"APP_NOMODULE_1": "현재 불러와진 모듈이 없습니다.",
"APP_NOMODULE_2": "다음의 파일에서 불러오기 순서를 설정하고 게임을 재시작하십시오.",
"MENU_LABEL_KEYCONFIG_HELP1": "키캡을 클릭해 컨트롤을 배정하십시오",
"GAME_PREV_SAVE_WAS_LOADED1": "가장 최근에 저장된 게임이 손상되었습니다.",
"GAME_PREV_SAVE_WAS_LOADED2": "이전에 저장된 게임을 불러왔습니다.",
"GAME_MORE_RECENT_AUTOSAVE1": "자동 저장된 게임이 수동으로 저장한 게임보다 더 최신입니다.",
"GAME_MORE_RECENT_AUTOSAVE2": "불러올 게임을 선택해 주십시오.",
"MENU_LABEL_SAVE_WILL_BE_DELETED": "선택된 세이브가 삭제됩니다.",
"MENU_LABEL_UNSAVED_PROGRESSES_WILL_BE_LOST": "저장하지 않은 변동사항을 잃게 됩니다."
}

View File

@@ -2335,7 +2335,7 @@
},
{
"n": "MENU_OPTIONS",
"s": "Stillingar "
"s": "Valkostir "
},
{
"n": "MENU_OPTIONS_ADVANCEDGRAPHICS",
@@ -2395,11 +2395,11 @@
},
{
"n": "MENU_OPTIONS_GAMEPLAY",
"s": "Gameplay Options"
"s": "Leikvalkostir"
},
{
"n": "MENU_OPTIONS_GRAPHICS",
"s": "Grafíkstillingar"
"s": "Grafíkvalkostir"
},
{
"n": "MENU_OPTIONS_HUD",

View File

@@ -15,5 +15,15 @@
"MENU_OPTIONS_DITHER": "Dither",
"MENU_OPTIONS_BLUR": "Óskýrt",
"MENU_OPTIONS_PARTICLES": "Eind",
"MENU_IO_IMPORT": "Flytja inn"
"MENU_IO_IMPORT": "Flytja inn",
"APP_NOMODULE_1": "Engin eining er hlaðin eins og er.",
"APP_NOMODULE_2": "Vinsamlega stilltu hleðslupöntunina þína og endurræstu:",
"MENU_LABEL_KEYCONFIG_HELP1": "Smelltu á lyklalokið til að úthluta aðgerðum",
"MENU_LABEL_IME_TOGGLE": "Breyttu IME",
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "Límdu frá klemmuspjald",
"MENU_OPTIONS_PERFORMANCE": "Afköst",
"MENU_LABEL_DELETE": "Eyða",
"MENU_OPTIONS_JVM_HEAP_MAX": "Hámarks hrúguminni",
"MENU_OPTIONS_AUTOSAVE": "Sjálfvirk vistun",
"CONTEXT_TIME_MINUTE_PLURAL": "Mínútur"
}

View File

@@ -6,12 +6,11 @@
"APP_WARNING_HEALTH_AND_SAFETY": "경고—건강과 안전을 위하여",
"MENU_LABEL_PRESS_START_SYMBOL": ">을 누르세요",
"MENU_MODULES" : "모듈",
"GAME_ACTION_MOVE_VERB" : "이동하기",
"GAME_ACTION_ZOOM" : "확대·축소",
"MENU_LABEL_RESET" : "재설정",
"GAME_32BIT_WARNING1": "32비트 버전의 Java를 사용중인 것 같습니다.",
"GAME_32BIT_WARNING2": "아래 링크에서 최신 64비트 Java를 내려받아 설치해주세요.",
"GAME_32BIT_WARNING3": "https://www.java.com/ko/download/",
"MENU_OPTION_STREAMERS_LAYOUT": "채팅창 오버레이",
"MENU_LABEL_RESTART_REQUIRED": "재시작 필요",
"MENU_LABEL_KEYBOARD_LAYOUT": "자판 배열",
@@ -20,10 +19,19 @@
"MENU_OPTIONS_BLUR": "흐림",
"MENU_OPTIONS_PARTICLES": "입자 수",
"MENU_IO_IMPORT": "가져오기",
"APP_NOMODULE_1": "현재 불러와진 모듈이 없습니다.",
"APP_NOMODULE_2": "다음의 파일에서 불러오기 순서를 설정하고 게임을 재시작하십시오.",
"MENU_LABEL_KEYCONFIG_HELP1": "키캡을 클릭해 컨트롤을 배정하십시오",
"MENU_LABEL_IME_TOGGLE": "입력기 켜고 끄기",
"MENU_LABEL_PASTE_FROM_CLIPBOARD": "복사한 텍스트 붙여넣기",
"MENU_OPTIONS_PERFORMANCE": "성능"
"MENU_OPTIONS_PERFORMANCE": "성능",
"MENU_LABEL_DELETE": "삭제",
"MENU_OPTIONS_JVM_HEAP_MAX": "최대 힙 메모리",
"MENU_OPTIONS_AUTOSAVE": "자동 저장",
"CONTEXT_TIME_MINUTE_PLURAL": "분",
"CONTEXT_TIME_SECOND_PLURAL": "초",
"MENU_LABEL_SYSTEM_INFO": "시스템 정보",
"MENU_OPTIONS_NOTIFICATION_DISPLAY_DURATION": "알림 표시 시간",
"MENU_LABEL_STREAMING": "실시간 방송",
"MENU_LABEL_EXTRA_JVM_ARGUMENTS": "추가 명령 인수",
"MENU_IO_MANUAL_SAVE": "수동 저장",
"MENU_IO_AUTOSAVE": "자동 저장",
"MENU_OPTIONS_DEBUG_CONSOLE": "디버그 콘솔"
}

View File

@@ -0,0 +1,15 @@
{
"GAME_32BIT_WARNING1": "32비트 버전의 Java를 사용중인 것 같습니다.",
"GAME_32BIT_WARNING2": "아래 링크에서 최신 64비트 Java를 내려받아 설치해주세요.",
"GAME_32BIT_WARNING3": "https://www.java.com/ko/download/",
"GAME_APPLE_ROSETTA_WARNING1": "Apple Silicon이 탑재된 Mac을 사용 중이지만 x86 빌드의 게임을 실행 중입니다.",
"GAME_APPLE_ROSETTA_WARNING2": "최적의 성능과 게임 경험을 위해 Apple Silicon용 빌드의 게임을 이용해 주십시오.",
"APP_NOMODULE_1": "현재 불러와진 모듈이 없습니다.",
"APP_NOMODULE_2": "다음의 파일에서 불러오기 순서를 설정하고 게임을 재시작하십시오.",
"MENU_LABEL_KEYCONFIG_HELP1": "키캡을 클릭해 컨트롤을 배정하십시오",
"GAME_PREV_SAVE_WAS_LOADED1": "가장 최근에 저장된 게임이 손상되었습니다.",
"GAME_PREV_SAVE_WAS_LOADED2": "이전에 저장된 게임을 불러왔습니다.",
"GAME_MORE_RECENT_AUTOSAVE1": "자동 저장된 게임이 수동으로 저장한 게임보다 더 최신입니다.",
"GAME_MORE_RECENT_AUTOSAVE2": "불러올 게임을 선택해 주십시오.",
"MENU_LABEL_SAVE_WILL_BE_DELETED": "선택된 세이브가 삭제됩니다."
}

Binary file not shown.

View File

@@ -14,4 +14,6 @@ id;classname
258;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorBirch
259;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorRosewood
320;net.torvald.terrarum.modulebasegame.gameitems.ItemWorldPortal
999999;net.torvald.terrarum.modulebasegame.gameitems.ItemTapestry
1 id classname
14 259 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorRosewood
15 999999 320 net.torvald.terrarum.modulebasegame.gameitems.ItemTapestry net.torvald.terrarum.modulebasegame.gameitems.ItemWorldPortal
16 999999 net.torvald.terrarum.modulebasegame.gameitems.ItemTapestry
17
18
19

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -16,5 +16,8 @@
"GAME_ACTION_QUICKSEL": "Quick Select",
"GAME_ACTION_CRAFT": "Craft",
"GAME_CRAFTING": "Crafting",
"GAME_CRAFTABLE_ITEMS": "Craftable Items"
"GAME_CRAFTABLE_ITEMS": "Craftable Items",
"MENU_LABEL_RENAME": "Rename",
"GAME_ACTION_TELEPORT": "Teleport",
"CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "This is a world currently playing."
}

View File

@@ -17,5 +17,8 @@
"GAME_ACTION_QUICKSEL": "빠른 선택",
"GAME_ACTION_CRAFT": "제작하기",
"GAME_CRAFTING": "제작",
"GAME_CRAFTABLE_ITEMS": "제작 가능한 아이템"
"GAME_CRAFTABLE_ITEMS": "제작 가능한 아이템",
"MENU_LABEL_RENAME": "이름 바꾸기",
"GAME_ACTION_TELEPORT": "텔레포트하기",
"CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "현재 플레이 중인 월드입니다."
}

Binary file not shown.

View File

@@ -1,11 +1,11 @@
id;drop;name;renderclass;accept;inputcount;inputtype;outputtype;javaclass;inventoryimg;branching
8192;8192;WIRE_RED;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items16,1,9;1
8193;8193;WIRE_GREEN;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items16,1,10;1
8194;8194;WIRE_BLUE;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items16,1,11;1
#8195;8195;WIRE_BUNDLE;signal;digital_3bits;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items16,1,2;1
8196;8196;WIRE_POWER_LOW;power;power_low;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items16,2,9;1
8197;8197;WIRE_POWER_HIGH;power;power_high;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items16,3,9;1
8198;8198;WIRE_ETHERNET;network;10base2;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items16,4,9;1
8192;8192;WIRE_RED;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,0,4;1
8193;8193;WIRE_GREEN;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,1,4;1
8194;8194;WIRE_BLUE;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,2,4;1
#8195;8195;WIRE_BUNDLE;signal;digital_3bits;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,0,0;1
8196;8196;WIRE_POWER_LOW;power;power_low;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,3,4;1
8197;8197;WIRE_POWER_HIGH;power;power_high;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,4,4;1
8198;8198;WIRE_ETHERNET;network;10base2;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,5,4;1
# accept: which wiretype (defined elsewhere) the wires acceps. Use comma to separate multiple. N/A for electronic components (aka not wires)
1 id drop name renderclass accept inputcount inputtype outputtype javaclass inventoryimg branching
2 8192 8192 WIRE_RED signal digital_bit 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items16,1,9 basegame.items,0,4 1
3 8193 8193 WIRE_GREEN signal digital_bit 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items16,1,10 basegame.items,1,4 1
4 8194 8194 WIRE_BLUE signal digital_bit 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items16,1,11 basegame.items,2,4 1
5 #8195 8195 WIRE_BUNDLE signal digital_3bits 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items16,1,2 basegame.items,0,0 1
6 8196 8196 WIRE_POWER_LOW power power_low 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items16,2,9 basegame.items,3,4 1
7 8197 8197 WIRE_POWER_HIGH power power_high 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items16,3,9 basegame.items,4,4 1
8 8198 8198 WIRE_ETHERNET network 10base2 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items16,4,9 basegame.items,5,4 1
9 # accept: which wiretype (defined elsewhere) the wires acceps. Use comma to separate multiple. N/A for electronic components (aka not wires)
10 # inputcount: how many sides are input (outputcount is deduced from the inputcount). N/A for wires
11 # inputtype: which wiretype it accepts. N/A for wires

Binary file not shown.

View File

@@ -23,12 +23,13 @@ cp $SRCFILES/AppRun $DESTDIR/AppRun
chmod +x $DESTDIR/AppRun
# Copy over a Java runtime
cp -r "../out/$RUNTIME" $DESTDIR/
mkdir $DESTDIR/out
cp -r "../out/$RUNTIME" $DESTDIR/out/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/
mv $DESTDIR/assets_release $DESTDIR/assets
cp -r "../out/TerrarumBuild.jar" $DESTDIR/assets/
cp "../out/TerrarumBuild.jar" $DESTDIR/out/
# Pack everything to AppImage
ARCH=arm_aarch64 "./$APPIMAGETOOL" $DESTDIR "out/$DESTDIR.AppImage" || { echo 'Building AppImage failed' >&2; exit 1; }

View File

@@ -23,12 +23,13 @@ cp $SRCFILES/AppRun $DESTDIR/AppRun
chmod +x $DESTDIR/AppRun
# Copy over a Java runtime
cp -r "../out/$RUNTIME" $DESTDIR/
mkdir $DESTDIR/out
cp -r "../out/$RUNTIME" $DESTDIR/out/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/
mv $DESTDIR/assets_release $DESTDIR/assets
cp -r "../out/TerrarumBuild.jar" $DESTDIR/assets/
cp "../out/TerrarumBuild.jar" $DESTDIR/out/
# Pack everything to AppImage
"./$APPIMAGETOOL" $DESTDIR "out/$DESTDIR.AppImage" || { echo 'Building AppImage failed' >&2; exit 1; }

View File

@@ -25,11 +25,12 @@ cp $SRCFILES/Terrarum.sh $DESTDIR/Contents/MacOS/
chmod +x $DESTDIR/Contents/MacOS/Terrarum.sh
# Copy over a Java runtime
cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/
mkdir $DESTDIR/Contents/MacOS/out
cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/out/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/Contents/MacOS/
mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets
cp -r "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/assets/
cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/
echo "Build successful: $DESTDIR"

View File

@@ -25,11 +25,12 @@ cp $SRCFILES/Terrarum.sh $DESTDIR/Contents/MacOS/
chmod +x $DESTDIR/Contents/MacOS/Terrarum.sh
# Copy over a Java runtime
cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/
mkdir $DESTDIR/Contents/MacOS/out
cp -r "../out/$RUNTIME" $DESTDIR/Contents/MacOS/out/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/Contents/MacOS/
mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets
cp -r "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/assets/
cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/
echo "Build successful: $DESTDIR"

View File

@@ -16,15 +16,21 @@ rm -rf $DESTDIR || true
mkdir $DESTDIR
# Prepare an application
cp $SRCFILES/Terrarum.bat $DESTDIR/
if ! command -v x86_64-w64-mingw32-gcc &> /dev/null
then
echo 'Mingw32 not found; please install mingw64-cross-gcc (or similar) to your system' >&2; exit 1;
fi
x86_64-w64-mingw32-gcc -o $DESTDIR/Terrarum.exe $SRCFILES/Terrarum.c || { echo 'Building EXE failed' >&2; exit 1; }
# Copy over a Java runtime
cp -r "../out/$RUNTIME" $DESTDIR/
mkdir $DESTDIR/out
cp -r "../out/$RUNTIME" $DESTDIR/out/
# Copy over all the assets and a jarfile
cp -r "../assets_release" $DESTDIR/
mv $DESTDIR/assets_release $DESTDIR/assets
cp -r "../out/TerrarumBuild.jar" $DESTDIR/assets/
cp "../out/TerrarumBuild.jar" $DESTDIR/out/
# Temporary solution: zip everything
zip -r -9 -l "out/TerrarumWindows.x86.zip" $DESTDIR

View File

@@ -1,3 +1,3 @@
#!/bin/bash
cd "${0%/*}"
./runtime-linux-arm/bin/java -Xms1G -Xmx6G -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd -jar ./assets/TerrarumBuild.jar
./out/runtime-linux-arm/bin/java -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd -jar ./out/TerrarumBuild.jar

View File

@@ -1,3 +1,3 @@
#!/bin/bash
cd "${0%/*}"
./runtime-linux-x86/bin/java -Xms1G -Xmx6G -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd -jar ./assets/TerrarumBuild.jar
./out/runtime-linux-x86/bin/java -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd -jar ./out/TerrarumBuild.jar

View File

@@ -1,3 +1,3 @@
#!/bin/bash
cd "${0%/*}"
./runtime-osx-arm/bin/java -XstartOnFirstThread -Xms1G -Xmx6G -jar ./assets/TerrarumBuild.jar
./out/runtime-osx-arm/bin/java -jar ./out/TerrarumBuild.jar

View File

@@ -1,3 +1,3 @@
#!/bin/bash
cd "${0%/*}"
./runtime-osx-x86/bin/java -XstartOnFirstThread -Xms1G -Xmx6G -jar ./assets/TerrarumBuild.jar
./out/runtime-osx-x86/bin/java -jar ./out/TerrarumBuild.jar

View File

@@ -1,2 +0,0 @@
cd /D "%~dp0"
.\runtime-windows-x86\bin\java -Xms1G -Xmx6G -jar .\assets\TerrarumBuild.jar

View File

@@ -0,0 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
int main() {
return system(".\\out\\runtime-windows-x86\\bin\\java -jar .\\out\\TerrarumBuild.jar");
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/android-lint/kxml2-2.3.0.jar LFS Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.android.tools.lint</groupId>
<artifactId>lint</artifactId>
<version>31.0.1</version>
<name>com.android.tools.lint.lint</name>
<description>Lint tools. Both a Command line tool and a library to add lint features to other tools</description>
<url>http://tools.android.com/</url>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>The Android Open Source Project</name>
</developer>
</developers>
<dependencies>
<dependency>
<groupId>com.android.tools.lint</groupId>
<artifactId>lint-api</artifactId>
<version>31.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.android.tools.lint</groupId>
<artifactId>lint-checks</artifactId>
<version>31.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.android.tools.external.com-intellij</groupId>
<artifactId>intellij-core</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools.external.com-intellij</groupId>
<artifactId>kotlin-compiler</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools.external.org-jetbrains</groupId>
<artifactId>uast</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools.analytics-library</groupId>
<artifactId>protos</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools.analytics-library</groupId>
<artifactId>shared</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools.analytics-library</groupId>
<artifactId>tracker</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools.build</groupId>
<artifactId>manifest-merger</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools</groupId>
<artifactId>common</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools.layoutlib</groupId>
<artifactId>layoutlib-api</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools</groupId>
<artifactId>sdk-common</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.android.tools</groupId>
<artifactId>sdklib</artifactId>
<version>31.0.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.sf.kxml</groupId>
<artifactId>kxml2</artifactId>
<version>2.3.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>1.7.10</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>1.7.10</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/android-lint/uast-31.0.1.jar LFS Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/classgraph-4.8.157.jar LFS Normal file

Binary file not shown.

View File

@@ -1,3 +1,3 @@
Manifest-Version: 1.0
Main-Class: net.torvald.terrarum.App
Main-Class: net.torvald.terrarum.Principii

View File

@@ -231,6 +231,10 @@ final public class FastMath {
return (float) (((c4 * u + c3) * u + c2) * u + c1);
}
public static float interpolateCatmullRom(float u, float p0, float p1, float p2, float p3) {
return interpolateCatmullRom(u, 0.5f, p0, p1, p2, p3);
}
/**Interpolate a spline between at least 4 control points following the Catmull-Rom equation.
* here is the interpolation matrix
* m = [ 0.0 1.0 0.0 0.0 ]
@@ -316,24 +320,25 @@ final public class FastMath {
public static float interpolateHermite(float scale, float p0, float p1, float p2, float p3) {
return interpolateHermite(scale, p0, p1, p2, p3, 1f, 0f);
}
public static float interpolateHermite(float scale, float p0, float p1, float p2, float p3, float tension, float bias) {
// return interpolateHermite(scale, p0, p1, p2, p3, 0f, 0f);
float mu2 = scale * scale;
float mu3 = mu2 * scale;
float biasTensionTerms = 0.5f;//(1f + bias) * (1f - tension) / 2f;
float m0 = (p1 - p0) * (1f + bias) * (1f - tension) / 2f;
m0 += (p2 - p1) * (1f + bias) * (1f - tension) / 2f;
float m1 = (p2 - p1) * (1f + bias) * (1f - tension) / 2f;
m1 += (p3 - p2) * (1f + bias) * (1f - tension) / 2f;
float m0 = (p1 - p0) * biasTensionTerms;
float mTemp = (p2 - p1) * biasTensionTerms;
m0 += mTemp;
float m1 = mTemp;
m1 += (p3 - p2) * biasTensionTerms;
float a0 = 2 * mu3 - 3 * mu2 + 1;
float a1 = mu3 - 2 * mu2 + scale;
float a2 = mu3 - mu2;
float a3 = -2 * mu3 + 3 * mu2;
float a0 = 2*mu3 - 3*mu2 + 1;
float a1 = 1*mu3 - 2*mu2 + scale;
float a2 = 1*mu3 - 1*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) {}
/**

View File

@@ -18,7 +18,7 @@ public class XXHash32 {
public static int hashGeoCoord(int x, int y) {
int p = ((x & 65535) << 16) | (y & 65535);
return hash(new byte[]{(byte) p, (byte)(p >>> 8), (byte)(p >>> 16), (byte)(p >>> 24)}, 10000);
return hash(new byte[]{(byte) p, (byte)(p >>> 8), (byte)(p >>> 16), (byte)(p >>> 24)}, x ^ y);
}
public static int hash(byte[] data, int seed) {

View File

@@ -3,15 +3,15 @@ package net.torvald.reflection
/**
* Created by minjaesong on 2023-03-25.
*/
fun Any.extortField(name: String): Any? { // yes I'm deliberately using negative words for the function name
inline fun <reified T> Any.extortField(name: String): T? { // yes I'm deliberately using negative words for the function name
return this.javaClass.getDeclaredField(name).let {
it.isAccessible = true
it.get(this)
it.get(this) as T?
}
}
fun Any.forceInvoke(name: String, params: Array<Any>): Any? { // yes I'm deliberately using negative words for the function name
inline fun <reified T> Any.forceInvoke(name: String, params: Array<Any>): T? { // yes I'm deliberately using negative words for the function name
return this.javaClass.getDeclaredMethod(name, *(params.map { it.javaClass }.toTypedArray())).let {
it.isAccessible = true
it.invoke(this, *params)
it.invoke(this, *params) as T?
}
}

View File

@@ -142,6 +142,12 @@ public class App implements ApplicationListener {
public static final int GLOBAL_FRAMERATE_LIMIT = 300;
private static String undesirableConditions;
public static String getUndesirableConditions() {
return undesirableConditions;
}
/**
* These languages won't distinguish regional differences (e.g. enUS and enUK, frFR and frCA)
*/
@@ -201,11 +207,11 @@ public class App implements ApplicationListener {
* Sorted by the lastplaytime, in reverse order (index 0 is the most recent game played)
*/
public static ArrayList<UUID> sortedSavegameWorlds = new ArrayList();
public static HashMap<UUID, DiskSkimmer> savegameWorlds = new HashMap<>(); // UNSORTED even with the TreeMap
public static HashMap<UUID, SavegameCollection> savegameWorlds = new HashMap<>(); // UNSORTED even with the TreeMap
public static HashMap<UUID, String> savegameWorldsName = new HashMap<>();
public static ArrayList<UUID> sortedPlayers = new ArrayList();
public static HashMap<UUID, DiskSkimmer> savegamePlayers = new HashMap<>();
public static HashMap<UUID, SavegameCollection> savegamePlayers = new HashMap<>();
public static HashMap<UUID, String> savegamePlayersName = new HashMap<>();
public static void updateListOfSavegames() {
@@ -346,10 +352,13 @@ public class App implements ApplicationListener {
processorVendor = "Unknown CPU";
}
if (processor.startsWith("Apple M") && Objects.equals(systemArch, "aarch64")) {
if (processor.startsWith("Apple M") && systemArch.equals("aarch64")) {
isAppleM = true;
System.out.println("Apple Proprietary "+processor+" detected; don't expect smooth sailing...");
}
if (processor.startsWith("Apple M") && !systemArch.equals("aarch64")) {
undesirableConditions = "apple_execution_through_rosetta";
}
if (!IS_DEVELOPMENT_BUILD) {
var p = UnsafeHelper.INSTANCE.allocate(64);
@@ -374,8 +383,8 @@ public class App implements ApplicationListener {
ShaderProgram.pedantic = false;
scr = new TerrarumScreenSize(getConfigInt("screenwidth"), getConfigInt("screenheight"));
int width = (int) Math.round(scr.getWidth() * scr.getMagn());
int height = (int) Math.round(scr.getHeight() * scr.getMagn());
int width = scr.getWindowW();
int height = scr.getWindowH();
Lwjgl3ApplicationConfiguration appConfig = new Lwjgl3ApplicationConfiguration();
//appConfig.useGL30 = false; // https://stackoverflow.com/questions/46753218/libgdx-should-i-use-gl30
@@ -557,7 +566,7 @@ public class App implements ApplicationListener {
false,
64, false, 0.5f, false
);
fontUITitle.setInterchar(2);
fontUITitle.setInterchar(1);
fontGameFBO = new TerrarumSansBitmap(FONT_DIR, false, true, false,
false,
64, false, 203f/255f, false
@@ -631,7 +640,6 @@ public class App implements ApplicationListener {
// process screenshot request
if (screenshotRequested) {
FrameBufferManager.begin(postProcessorOutFBO);
screenshotRequested = false;
try {
Pixmap p = Pixmap.createFromFrameBuffer(0, 0, scr.getWidth(), scr.getHeight());
PixmapIO.writePNG(Gdx.files.absolute(defaultDir+"/Screenshot-"+String.valueOf(System.currentTimeMillis())+".png"), p, 9, true);
@@ -643,6 +651,7 @@ public class App implements ApplicationListener {
Terrarum.INSTANCE.getIngame().sendNotification("Failed to take screenshot: "+e.getMessage());
}
FrameBufferManager.end();
screenshotRequested = false;
}
@@ -767,12 +776,9 @@ public class App implements ApplicationListener {
@Override
public void resize(int w0, int h0) {
int w = (w0%2==0)?w0:w0+1;
int h = (h0%2==0)?h0:h0+1;
float magn = (float) getConfigDouble("screenmagnifying");
int width = Math.round(w / magn);
int height = Math.round(h / magn);
int width = (int) Math.floor(w0 / magn);
int height = (int) Math.floor(h0 / magn);
printdbg(this, "Resize called: "+width+","+height);
@@ -782,7 +788,7 @@ public class App implements ApplicationListener {
//initViewPort(width, height);
scr.setDimension(width, height, magn, w, h);
scr.setDimension(width, height, magn);
if (currentScreen != null) currentScreen.resize(scr.getWidth(), scr.getHeight());
TerrarumPostProcessor.INSTANCE.resize(scr.getWidth(), scr.getHeight());
@@ -855,7 +861,7 @@ public class App implements ApplicationListener {
fullscreenQuad.dispose();
logoBatch.dispose();
batch.dispose();
shapeRender.dispose();
// shapeRender.dispose();
fontGame.dispose();
fontGameFBO.dispose();
@@ -1133,6 +1139,11 @@ public class App implements ApplicationListener {
public static RunningEnvironment environment;
/** defaultDir + "/Recycled/Players" */
public static String recycledPlayersDir;
/** defaultDir + "/Recycled/Worlds" */
public static String recycledWorldsDir;
private static void getDefaultDirectory() {
String OS = OSName.toUpperCase();
if (OS.contains("WIN")) {
@@ -1162,6 +1173,8 @@ public class App implements ApplicationListener {
worldsDir = defaultDir + "/Worlds";
configDir = defaultDir + "/config.json";
loadOrderDir = defaultDir + "/LoadOrder.txt";
recycledPlayersDir = defaultDir + "/Recycled/Players";
recycledWorldsDir = defaultDir + "/Recycled/Worlds";
System.out.println(String.format("os.name = %s (with identifier %s)", OSName, operationSystem));
System.out.println(String.format("os.version = %s", OSVersion));
@@ -1171,10 +1184,12 @@ public class App implements ApplicationListener {
private static void createDirs() {
File[] dirs = {
new File(saveDir),
// new File(saveDir),
new File(saveSharedDir),
new File(playersDir),
new File(worldsDir)
new File(worldsDir),
new File(recycledPlayersDir),
new File(recycledWorldsDir),
};
for (File it : dirs) {

View File

@@ -118,6 +118,7 @@ object CommonResourcePool {
fun getAsTextureRegionPack(identifier: String) = getAs<TextureRegionPack>(identifier)
fun getAsTextureRegion(identifier: String) = getAs<TextureRegion>(identifier)
fun getAsTexture(identifier: String) = getAs<Texture>(identifier)
fun getAsItemSheet(identifier: String) = getAs<ItemSheet>(identifier)
fun dispose() {
pool.forEach { (name, u) ->

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum
import com.badlogic.gdx.Gdx
import net.torvald.unicode.BULLET
import net.torvald.unicode.ENDASH
@@ -223,6 +224,22 @@ Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
""").split('\n')
private val javaVersion = System.getProperty("java.version")
private val osName = App.OSName
private val osVersion = App.OSVersion
private val sysArch = App.systemArch
private val processor = App.processor
private val processorVendor = App.processorVendor
private val glinfo = Gdx.graphics.glVersion.debugVersionString
val systeminfo: List<String>; get() = """
JRE Version: $javaVersion
Operation System: $osName $osVersion
Architecture: $sysArch
Processor: $processor ($processorVendor)
GL Info: $glinfo
""".split('\n')
val gpl3: List<String>; get() = """ GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007

View File

@@ -10,6 +10,9 @@ import com.badlogic.gdx.Input
object DefaultConfig {
val hashMap = hashMapOf<String, Any>(
"jvm_xmx" to 4,
"jvm_extra_cmd" to "",
"displayfps" to 0, // 0: no limit, non-zero: limit
"displayfpsidle" to 0, // 0: no limit, non-zero: limit
"displaycolourdepth" to 8,
@@ -19,9 +22,9 @@ object DefaultConfig {
"atlastexsize" to 2048,
"language" to App.getSysLang(),
"notificationshowuptime" to 4096, // 4s
"selecteditemnameshowuptime" to 4096, // 4s
"autosaveinterval" to 262144, // 4m22s
"notificationshowuptime" to 4000, // 4s
"selecteditemnameshowuptime" to 4000, // 4s
"autosaveinterval" to 300000, // 5s
"multithread" to true,
"showhealthmessageonstartup" to true,

View File

@@ -7,6 +7,7 @@ import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameactors.ActorWithBody.Companion.PHYS_EPSILON_DIST
import net.torvald.terrarum.gameactors.BlockMarkerActor
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.gameitems.ItemID
@@ -26,6 +27,8 @@ import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.IOException
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.util.*
import java.util.concurrent.locks.Lock
import java.util.function.Consumer
@@ -49,8 +52,8 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
override fun getMax(axis: Int, t: ActorWithBody): Double =
when (axis) {
0 -> t.hitbox.endX
1 -> t.hitbox.endY
0 -> t.hitbox.endX - PHYS_EPSILON_DIST
1 -> t.hitbox.endY - PHYS_EPSILON_DIST
else -> throw IllegalArgumentException("nonexistent axis $axis for ${dimensions}-dimensional object")
}
}
@@ -406,6 +409,14 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
return uiTooltip.message
}
open fun requestForceSave(callback: () -> Unit) {
}
open fun saveTheGame(onSuccessful: () -> Unit, onError: (Throwable) -> Unit) {
}
/**
* Copies most recent `save` to `save.1`, leaving `save` for overwriting, previous `save.1` will be copied to `save.2`
*/
@@ -422,25 +433,55 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
// do not overwrite clean .2 with dirty .1
val flags3 = FileInputStream(file3).let { it.skip(49L); val r = it.read(); it.close(); r }
val flags2 = FileInputStream(file2).let { it.skip(49L); val r = it.read(); it.close(); r }
if (!(flags3 == 0 && flags2 != 0) || !file3.exists()) file2.copyTo(file3, true)
if (!(flags3 == 0 && flags2 != 0) || !file3.exists()) Files.move(file2.toPath(), file3.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
} catch (e: NoSuchFileException) {} catch (e: FileNotFoundException) {}
try {
// do not overwrite clean .2 with dirty .1
val flags2 = FileInputStream(file2).let { it.skip(49L); val r = it.read(); it.close(); r }
val flags1 = FileInputStream(file1).let { it.skip(49L); val r = it.read(); it.close(); r }
if (!(flags2 == 0 && flags1 != 0) || !file2.exists()) file1.copyTo(file2, true)
if (!(flags2 == 0 && flags1 != 0) || !file2.exists()) Files.move(file1.toPath(), file2.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
} catch (e: NoSuchFileException) {} catch (e: FileNotFoundException) {}
try {
if (file2.exists() && !file3.exists())
file2.copyTo(file3, true)
Files.move(file2.toPath(), file3.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
if (file1.exists() && !file2.exists())
file1.copyTo(file2, true)
Files.move(file1.toPath(), file2.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
file.copyTo(file1, true)
} catch (e: IOException) {}
}
fun makeSavegameBackupCopyAuto(file0: File): File {
val file1 = File("${file0.absolutePath}.a")
val file2 = File("${file0.absolutePath}.b")
val file3 = File("${file0.absolutePath}.c")
try {
// do not overwrite clean .2 with dirty .1
val flags3 = FileInputStream(file3).let { it.skip(49L); val r = it.read(); it.close(); r }
val flags2 = FileInputStream(file2).let { it.skip(49L); val r = it.read(); it.close(); r }
if (!(flags3 == 0 && flags2 != 0) || !file3.exists()) Files.move(file2.toPath(), file3.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
} catch (e: NoSuchFileException) {} catch (e: FileNotFoundException) {}
try {
// do not overwrite clean .2 with dirty .1
val flags2 = FileInputStream(file2).let { it.skip(49L); val r = it.read(); it.close(); r }
val flags1 = FileInputStream(file1).let { it.skip(49L); val r = it.read(); it.close(); r }
if (!(flags2 == 0 && flags1 != 0) || !file2.exists()) Files.move(file1.toPath(), file2.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
} catch (e: NoSuchFileException) {} catch (e: FileNotFoundException) {}
try {
if (file2.exists() && !file3.exists())
Files.move(file2.toPath(), file3.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
if (file1.exists() && !file2.exists())
Files.move(file1.toPath(), file2.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
file0.copyTo(file1, true)
} catch (e: IOException) {}
return file1
}
// simple euclidean norm, squared
private val actorDistanceCalculator = DistanceCalculator<ActorWithBody> { t: ActorWithBody, p: PointND ->
@@ -449,7 +490,7 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
val dist2 = (p.getOrd(0) - (t.hitbox.centeredX - world.width * TILE_SIZE)).sqr() + (p.getOrd(1) - t.hitbox.centeredY).sqr()
val dist3 = (p.getOrd(0) - (t.hitbox.centeredX + world.width * TILE_SIZE)).sqr() + (p.getOrd(1) - t.hitbox.centeredY).sqr()
minOf(dist1, minOf(dist2, dist3))
minOf(dist1, dist2, dist3)
}
/**
@@ -467,7 +508,7 @@ open class IngameInstance(val batch: FlippingSpriteBatch, val isMultiplayer: Boo
fun getActorsAt(worldX: Double, worldY: Double): List<ActorWithBody> {
val outList = ArrayList<ActorWithBody>()
try {
actorsRTree.find(worldX, worldY, worldX + 1.0, worldY + 1.0, outList)
actorsRTree.find(worldX, worldY, worldX, worldY, outList)
}
catch (e: NullPointerException) {}
return outList

View File

@@ -0,0 +1,49 @@
package net.torvald.terrarum
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.utils.Disposable
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2023-06-06.
*/
class ItemSheet(ref: FileHandle, tileW: Int = 48, tileH: Int = 48) : Disposable {
private val textureRegionPack = TextureRegionPack(ref, tileW, tileH + 1)
init {
val pixmap = Pixmap(ref)
for (y in 0 until textureRegionPack.verticalCount) {
for (x in 0 until textureRegionPack.horizontalCount) {
var w = 0
var h = 0
for (i in 0..7) {
// width
w = w or (pixmap.getPixel(x * tileW + i, y * (tileH + 1)).and(255) > 127).toInt(7 - i)
// height
h = h or (pixmap.getPixel(x * tileW + i + 8, y * (tileH + 1)).and(255) > 127).toInt(7 - i)
}
textureRegionPack.get(x, y).apply {
this.setRegion(x * tileW, y * (tileH + 1) + 1, w, h)
}
// println("[ItemSheet] ${ref.path()} ($x,$y) dim ($w,$h)")
}
}
pixmap.dispose()
}
val horizontalCount = textureRegionPack.horizontalCount
val verticalCount = textureRegionPack.verticalCount
fun get(x: Int, y: Int) = textureRegionPack.get(x, y)
fun forEach(action: (TextureRegion) -> Unit) = textureRegionPack.regions.forEach(action)
override fun dispose() {
textureRegionPack.dispose()
}
}

View File

@@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.utils.JsonValue
import net.torvald.terrarum.App.*
import net.torvald.terrarum.App.setToGameConfig
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.WireCodex
import net.torvald.terrarum.gameitems.GameItem

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum
import net.torvald.random.XXHash32
import org.dyn4j.geometry.Vector2
/**
@@ -115,6 +116,10 @@ class Point2i() {
return this
}
override fun hashCode(): Int = XXHash32.hashGeoCoord(x, y)
override fun toString() = "Point2i($x, $y)"
operator fun component1() = x
operator fun component2() = y
}

View File

@@ -0,0 +1,334 @@
package net.torvald.terrarum;
import com.badlogic.gdx.utils.JsonValue;
import net.torvald.terrarum.utils.JsonFetcher;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Bootstrapper that launches the bundled JVM and injects VM configs such as -Xmx
*
* Created by minjaesong on 2023-06-22.
*/
public class Principii {
private static KVHashMap gameConfig = new KVHashMap();
private static String OSName = System.getProperty("os.name");
private static String operationSystem;
/** %appdata%/Terrarum, without trailing slash */
private static String defaultDir;
/** defaultDir + "/config.json" */
private static String configDir;
public static void getDefaultDirRoot() {
String OS = OSName.toUpperCase();
if (OS.contains("WIN")) {
operationSystem = "WINDOWS";
defaultDir = System.getenv("APPDATA") + "/Terrarum";
}
else if (OS.contains("OS X") || OS.contains("MACOS")) { // OpenJDK for mac will still report "Mac OS X" with version number "10.16", even on Big Sur and beyond
operationSystem = "OSX";
defaultDir = System.getProperty("user.home") + "/Library/Application Support/Terrarum";
}
else if (OS.contains("NUX") || OS.contains("NIX") || OS.contains("BSD")) {
operationSystem = "LINUX";
defaultDir = System.getProperty("user.home") + "/.Terrarum";
}
else if (OS.contains("SUNOS")) {
operationSystem = "SOLARIS";
defaultDir = System.getProperty("user.home") + "/.Terrarum";
}
else {
operationSystem = "UNKNOWN";
defaultDir = System.getProperty("user.home") + "/.Terrarum";
}
}
public static void main(String[] args) {
boolean devMode = false;
// if -ea flag is set, turn on all the debug prints
try {
assert false;
}
catch (AssertionError e) {
devMode = true;
}
String extracmd = devMode ? " -ea" : "";
String OS = OSName.toUpperCase();
String CPUARCH = System.getProperty("os.arch").toUpperCase();
String runtimeRoot;
String runtimeArch;
if (!CPUARCH.equals("AMD64") && !CPUARCH.equals("X86_64") && !CPUARCH.equals("AARCH64")) { // macOS Rosetta2 reports X86_64
System.err.println("Unsupported CPU architecture: " + CPUARCH);
System.exit(1);
return;
}
else {
runtimeArch = (CPUARCH.equals("AMD64") || CPUARCH.equals("X86_64")) ? "x86" : "arm";
}
if (OS.contains("WIN")) {
runtimeRoot = "runtime-windows-" + runtimeArch;
}
else if (OS.contains("OS X") || OS.contains("MACOS")) { // OpenJDK for mac will still report "Mac OS X" with version number "10.16", even on Big Sur and beyond
runtimeRoot = "runtime-osx-" + runtimeArch;
extracmd += " -XstartOnFirstThread";
}
else {
runtimeRoot = "runtime-linux-" + runtimeArch;
extracmd += " -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd";
}
String runtime = new File("out/"+runtimeRoot+"/bin/java").getAbsolutePath();
System.out.println("Runtime path: "+runtime);
getDefaultDirRoot();
configDir = defaultDir + "/config.json";
initialiseConfig();
readConfigJson();
int xmx = getConfigInt("jvm_xmx");
String userDefinedExtraCmd = getConfigString("jvm_extra_cmd").trim();
if (!userDefinedExtraCmd.isEmpty()) userDefinedExtraCmd = " "+userDefinedExtraCmd;
try {
String[] cmd = (runtime+extracmd+userDefinedExtraCmd+" -Xms1G -Xmx"+xmx+"G -cp ./out/TerrarumBuild.jar net.torvald.terrarum.App").split(" ");
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.inheritIO();
System.exit(pb.start().waitFor());
}
catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// CONFIG //
/**
* Return config from config set. If the config does not exist, default value will be returned.
* @param key
* *
* @return Config from config set or default config if it does not exist.
* *
* @throws NullPointerException if the specified config simply does not exist.
*/
private static int getConfigInt(String key) {
Object cfg = getConfigMaster(key);
if (cfg instanceof Integer) return ((int) cfg);
double value = (double) cfg;
if (Math.abs(value % 1.0) < 0.00000001)
return (int) Math.round(value);
return ((int) cfg);
}
/**
* Return config from config set. If the config does not exist, default value will be returned.
* @param key
* *
* @return Config from config set or default config if it does not exist.
* *
* @throws NullPointerException if the specified config simply does not exist.
*/
private static double getConfigDouble(String key) {
Object cfg = getConfigMaster(key);
return (cfg instanceof Integer) ? (((Integer) cfg) * 1.0) : ((double) (cfg));
}
/**
* Return config from config set. If the config does not exist, default value will be returned.
* @param key
* *
* @return Config from config set or default config if it does not exist.
* *
* @throws NullPointerException if the specified config simply does not exist.
*/
private static String getConfigString(String key) {
Object cfg = getConfigMaster(key);
return ((String) cfg);
}
/**
* Return config from config set. If the config does not exist, default value will be returned.
* @param key
* *
* @return Config from config set or default config if it does not exist. If the default value is undefined, will return false.
*/
private static boolean getConfigBoolean(String key) {
try {
Object cfg = getConfigMaster(key);
return ((boolean) cfg);
}
catch (NullPointerException keyNotFound) {
return false;
}
}
/*private static int[] getConfigIntArray(String key) {
Object cfg = getConfigMaster(key);
if (cfg instanceof JsonArray) {
JsonArray jsonArray = ((JsonArray) cfg).getAsJsonArray();
//return IntArray(jsonArray.size(), { i -> jsonArray[i].asInt })
int[] intArray = new int[jsonArray.size()];
for (int i = 0; i < jsonArray.size(); i++) {
intArray[i] = jsonArray.get(i).getAsInt();
}
return intArray;
}
else
return ((int[]) cfg);
}*/
private static double[] getConfigDoubleArray(String key) {
Object cfg = getConfigMaster(key);
return ((double[]) cfg);
}
private static int[] getConfigIntArray(String key) {
double[] a = getConfigDoubleArray(key);
int[] r = new int[a.length];
for (int i = 0; i < a.length; i++) {
r[i] = ((int) a[i]);
}
return r;
}
/*private static String[] getConfigStringArray(String key) {
Object cfg = getConfigMaster(key);
if (cfg instanceof JsonArray) {
JsonArray jsonArray = ((JsonArray) cfg).getAsJsonArray();
//return IntArray(jsonArray.size(), { i -> jsonArray[i].asInt })
String[] intArray = new String[jsonArray.size()];
for (int i = 0; i < jsonArray.size(); i++) {
intArray[i] = jsonArray.get(i).getAsString();
}
return intArray;
}
else
return ((String[]) cfg);
}*/
/**
* Get config from config file. If the entry does not exist, get from defaults; if the entry is not in the default, NullPointerException will be thrown
*/
private static HashMap<String, Object> getDefaultConfig() {
return DefaultConfig.INSTANCE.getHashMap();
}
private static Object getConfigMaster(String key1) {
String key = key1.toLowerCase();
Object config;
try {
config = gameConfig.get(key);
}
catch (NullPointerException e) {
config = null;
}
Object defaults;
try {
defaults = getDefaultConfig().get(key);
}
catch (NullPointerException e) {
defaults = null;
}
if (config == null) {
if (defaults == null) {
throw new NullPointerException("key not found: '" + key + "'");
}
else {
return defaults;
}
}
else {
return config;
}
}
/**
*
* @return true on successful, false on failure.
*/
private static Boolean readConfigJson() {
System.out.println("Config file: " + configDir);
try {
// read from disk and build config from it
JsonValue map = JsonFetcher.INSTANCE.invoke(configDir);
// make config
for (JsonValue entry = map.child; entry != null; entry = entry.next) {
setToGameConfigForced(entry, null);
}
return true;
}
catch (IOException e) {
// write default config to game dir. Call th.is method again to read config from it.
e.printStackTrace();
return false;
}
}
/**
* Reads DefaultConfig to populate the gameConfig
*/
private static void initialiseConfig() {
for (Map.Entry<String, Object> entry : DefaultConfig.INSTANCE.getHashMap().entrySet()) {
gameConfig.set(entry.getKey(), entry.getValue());
}
}
/**
* Will forcibly overwrite previously loaded config value.
*
* Key naming convention will be 'modName:propertyName'; if modName is null, the key will be just propertyName.
*
* @param value JsonValue (the key-value pair)
* @param modName module name, nullable
*/
private static void setToGameConfigForced(JsonValue value, String modName) {
gameConfig.set((modName == null) ? value.name : modName+":"+value.name,
value.isArray() ? value.asDoubleArray() :
value.isDouble() ? value.asDouble() :
value.isBoolean() ? value.asBoolean() :
value.isLong() ? value.asInt() :
value.asString()
);
}
}

View File

@@ -0,0 +1,113 @@
package net.torvald.terrarum
import io.github.classgraph.ClassGraph
import io.github.classgraph.ClassInfo
import net.torvald.random.HQRNG
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameitems.GameItem
import java.util.*
/**
* Created by minjaesong on 2023-05-22.
*/
fun main() {
val superClasses = listOf(
Actor::class.java,
GameItem::class.java
)
val serialisablePrimitives = listOf(
// comparison: exact match
"Z",
"B",
"C",
"S",
"I",
"J",
"F",
"D",
).flatMap { listOf(it, "[$it") }
val serialisableTypes = listOf(
// arrays: has '[' prepended
// comparison: startsWith
// primitives
"Ljava/lang/String", // includes ZipCodedStr
"Ljava/lang/Boolean",
"Ljava/lang/Byte",
"Ljava/lang/Character",
"Ljava/lang/Short",
"Ljava/lang/Integer",
"Ljava/lang/Long",
"Ljava/lang/Float",
"Ljava/lang/Double",
// has serialiser on net.torvald.terrarum.serialise.Common
"Ljava/math/BigInteger",
"Ljava/util/UUID",
"Ljava/util/HashMap",
"Ljava/util/HashSet",
"Ljava/util/ArrayList",
"Lnet/torvald/terrarum/gameworld/BlockLayer",
"Lnet/torvald/terrarum/gameworld/WorldTime",
// complex types
"Lnet/torvald/gdx/graphics/Cvec",
"Lnet/torvald/random/HQRNG",
"Lnet/torvald/spriteanimation/SpriteAnimation",
"Lnet/torvald/terrarum/Codex",
"Lnet/torvald/terrarum/Point2d",
"Lnet/torvald/terrarum/Point2i",
"Lnet/torvald/terrarum/KVHashMap",
"Lnet/torvald/terrarum/gameactors/Actor\$RenderOrder",
"Lnet/torvald/terrarum/gameactors/ActorValue",
"Lnet/torvald/terrarum/gameactors/Hitbox",
"Lnet/torvald/terrarum/gameactors/Lightbox",
"Lnet/torvald/terrarum/gameactors/PhysProperties",
"Lnet/torvald/terrarum/gameitems/GameItem",
"Lnet/torvald/terrarum/utils/HashArray",
"Lnet/torvald/terrarum/utils/HashedWirings",
"Lnet/torvald/terrarum/utils/HashedWiringGraph",
"Lnet/torvald/terrarum/utils/WiringGraphMap",
"Lnet/torvald/terrarum/savegame/ByteArray64",
"Lnet/torvald/terrarum/modulebasegame/gameactors/ActorInventory",
"Lnet/torvald/terrarum/modulebasegame/gameactors/BlockBox",
"Lnet/torvald/terrarum/modulebasegame/gameactors/FixtureInventory",
"Lkotlin/ranges/IntRange",
"Lorg/dyn4j/geometry/Vector2",
"Lcom/badlogic/gdx/graphics/Color",
// subclasses
"Lnet/torvald/terrarum/gameactors/BlockMarkerActor",
"Lnet/torvald/terrarum/gameactors/Actor",
"Lnet/torvald/terrarum/gameactors/ActorWithBody",
"Lnet/torvald/terrarum/gameactors/ActorHumanoid",
// composite types
"Lkotlin/Pair<Ljava/lang/Integer;Ljava/lang/Integer;>"
).flatMap { listOf(it, "[$it") }
val classaNonGrata = listOf(
"net.torvald.terrarum.modulebasegame.MovableWorldCamera"
)
ClassGraph().acceptPackages("net.torvald.terrarum")/*.verbose()*/.enableAllInfo().scan().let { scan ->
val offendingFields = scan.allClasses.filter { classinfo ->
superClasses.any { classinfo.extendsSuperclass(it) || classinfo.name == it.canonicalName } &&
!classaNonGrata.contains(classinfo.name)
}.flatMap { clazz ->
clazz.declaredFieldInfo.filter { field ->
!field.isTransient &&
!field.isEnum &&
!serialisablePrimitives.contains(field.typeSignatureOrTypeDescriptorStr) &&
serialisableTypes.none { field.typeSignatureOrTypeDescriptorStr.startsWith(it) }
}
}
// println(offendingFields)
offendingFields.forEach {
println("\u001B[1m${it.name}\u001B[m\n\t\u001B[32mfrom: \u001B[m${it.className}\n\t\u001B[32mtype: \u001B[m${it.typeSignatureOrTypeDescriptorStr}")
}
}
}

View File

@@ -0,0 +1,184 @@
package net.torvald.terrarum
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.savegame.DiskSkimmer
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import kotlin.io.path.Path
/**
* Created by minjaesong on 2023-06-24.
*/
class SavegameCollection(files0: List<DiskSkimmer>) {
/** Sorted in reverse by the last modified time of the files, index zero being the most recent */
val files = files0.sortedByDescending {
it.getLastModifiedTime().shl(2) or
it.diskFile.extension.matches(Regex("^[abc]${'$'}")).toLong(1) or
it.diskFile.extension.isBlank().toLong(0)
}
/** Sorted in reverse by the last modified time of the files, index zero being the most recent */
val autoSaves = files.filter { it.diskFile.extension.matches(Regex("[a-z]")) }
/** Sorted in reverse by the last modified time of the files, index zero being the most recent */
val manualSaves = files.filter { !it.diskFile.extension.matches(Regex("[a-z]")) }
init {
files.forEach { it.rebuild() }
}
companion object {
fun collectFromBaseFilename(basedir: File, name: String): SavegameCollection {
val files = basedir.listFiles().filter { it.name.startsWith(name) }
.mapNotNull { try { DiskSkimmer(it, true) } catch (e: Throwable) { null } }
return SavegameCollection(files)
}
}
/**
* Returns the most recent not-corrupted file
*/
fun loadable(): DiskSkimmer {
return files.first()
}
fun moveToRecycle(basedir: String) {
files.forEach {
try {
Files.move(it.diskFile.toPath(), Path(basedir, it.diskFile.name), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
}
catch (e: IOException) {
e.printStackTrace()
}
}
}
fun getBaseFile(): DiskSkimmer {
return files.first { it.diskFile.extension.isBlank() }
}
}
class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollection?) {
private var manualPlayer: DiskSkimmer? = null
private var manualWorld: DiskSkimmer? = null
private var autoPlayer: DiskSkimmer? = null
private var autoWorld: DiskSkimmer? = null
var status = 0 // 0: none available, 1: loadable manual save is newer than loadable auto; 2: loadable autosave is newer than loadable manual
private set
var newerSaveIsDamaged = false // only when most recent save is corrupted
private set
init {
printdbg(this, "init ($player, $world)")
if (player != null && world != null) {
printdbg(this, "player files: " + player.files.joinToString { it.diskFile.name })
printdbg(this, "world files:" + world.files.joinToString { it.diskFile.name })
// if a pair of files were saved successfully, they must have identical lastModifiedTime()
var pc = 0; val pt = player.files[0].getLastModifiedTime()
var wc = 0; val wt = world.files[0].getLastModifiedTime()
while (pc < player.files.size && wc < world.files.size) {
val pcf = player.files[pc]
val pcm = pcf.getLastModifiedTime()
val wcf = world.files[wc]
val wcm = wcf.getLastModifiedTime()
printdbg(this, "pc=$pc, wc=$wc, pcm=$pcm, wcm=$wcm")
if (playerDiskNotDamaged(pcf) && worldDiskNotDamaged(wcf)) {
printdbg(this, "pcf.autosaved=${pcf.isAutosaved()}, wcf.autosaved=${wcf.isAutosaved()}")
when (pcf.isAutosaved().toInt(1) or wcf.isAutosaved().toInt()) {
3 -> {
if (autoPlayer == null && autoWorld == null) {
autoPlayer = pcf
autoWorld = wcf
}
pc += 1
wc += 1
}
0 -> {
if (manualPlayer == null && manualWorld == null) {
manualPlayer = pcf
manualWorld = wcf
}
pc += 1
wc += 1
}
else -> {
if (pcm > wcm)
pc += 1
else if (pcm == wcm) {
pc += 1
wc += 1
}
else
wc += 1
}
}
}
if (manualPlayer != null && manualWorld != null && autoPlayer != null && autoWorld != null)
break
}
if (manualPlayer != null && manualWorld != null && autoPlayer != null && autoWorld != null) {
status = if (manualPlayer!!.getLastModifiedTime() > autoPlayer!!.getLastModifiedTime()) 1 else 2
}
else if (manualPlayer != null && manualWorld != null || autoPlayer != null && autoWorld != null) {
status = 1
}
else {
status = 0
}
printdbg(this, "manualPlayer = ${manualPlayer?.diskFile?.path}")
printdbg(this, "manualWorld = ${manualWorld?.diskFile?.path}")
printdbg(this, "autoPlayer = ${autoPlayer?.diskFile?.path}")
printdbg(this, "autoWorld = ${autoWorld?.diskFile?.path}")
printdbg(this, "status = $status")
}
}
private fun DiskSkimmer.isAutosaved() = this.getSaveMode().and(0b0000_0010) != 0
private fun playerDiskNotDamaged(disk: DiskSkimmer): Boolean {
return true
}
private fun worldDiskNotDamaged(disk: DiskSkimmer): Boolean {
return true
}
fun moreRecentAutosaveAvailable() = (status == 2)
fun saveAvaliable() = (status > 0)
fun getManualSave(): DiskPair? {
if (status == 0) return null
return DiskPair(manualPlayer!!, manualWorld!!)
}
fun getAutoSave(): DiskPair? {
if (status != 2) return null
return DiskPair(autoPlayer!!, autoWorld!!)
}
fun getLoadableSave(): DiskPair? {
if (status == 0) return null
return if (manualPlayer != null && manualWorld != null)
DiskPair(manualPlayer!!, manualWorld!!)
else
DiskPair(autoPlayer!!, autoWorld!!)
}
}
data class DiskPair(val player: DiskSkimmer, val world: DiskSkimmer)

View File

@@ -31,6 +31,8 @@ import net.torvald.terrarum.savegame.DiskSkimmer
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarum.utils.forEachSiblings
import net.torvald.terrarum.worlddrawer.WorldCamera
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
import net.torvald.unsafe.UnsafeHelper
@@ -62,11 +64,11 @@ object Terrarum : Disposable {
*/
const val PLAYER_REF_ID: Int = 0x91A7E2
inline fun inShapeRenderer(shapeRendererType: ShapeRenderer.ShapeType = ShapeRenderer.ShapeType.Filled, action: (ShapeRenderer) -> Unit) {
/*inline fun inShapeRenderer(shapeRendererType: ShapeRenderer.ShapeType = ShapeRenderer.ShapeType.Filled, action: (ShapeRenderer) -> Unit) {
shapeRender.begin(shapeRendererType)
action(shapeRender)
shapeRender.end()
}
}*/
var blockCodex = BlockCodex(); internal set
@@ -627,7 +629,8 @@ fun Double.sqrt() = Math.sqrt(this)
fun Float.sqrt() = FastMath.sqrt(this)
fun Int.abs() = this.absoluteValue
fun Double.bipolarClamp(limit: Double) = this.coerceIn(-limit, limit)
fun Boolean.toInt() = if (this) 1 else 0
fun Boolean.toInt(shift: Int = 0) = if (this) 1.shl(shift) else 0
fun Boolean.toLong(shift: Int = 0) = if (this) 1L.shl(shift) else 0L
fun Int.bitCount() = java.lang.Integer.bitCount(this)
fun Long.bitCount() = java.lang.Long.bitCount(this)
@@ -797,15 +800,17 @@ fun AppUpdateListOfSavegames() {
}
}.sortedByDescending { it.getLastModifiedTime() }.forEachIndexed { index, it ->
println("${index+1}.\t${it.diskFile.absolutePath}")
it.rebuild() // disk skimmer was created without initialisation, so do it now
it.rebuild()
val jsonFile = it.getFile(SAVEGAMEINFO)!!
val json = JsonReader().parse(ByteArray64Reader(jsonFile.bytes, Common.CHARSET).readText())
val worldUUID = UUID.fromString(json.getString("worldIndex"))
var worldUUID: UUID? = null
JsonFetcher.readFromJsonString(ByteArray64Reader(jsonFile.bytes, Common.CHARSET)).forEachSiblings { name, value ->
if (name == "worldIndex") worldUUID = UUID.fromString(value.asString())
}
// if multiple valid savegames with same UUID exist, only the most recent one is retained
if (!App.savegameWorlds.contains(worldUUID)) {
App.savegameWorlds[worldUUID] = it
App.savegameWorlds[worldUUID] = SavegameCollection.collectFromBaseFilename(File(worldsDir), it.diskFile.name)
App.savegameWorldsName[worldUUID] = it.getDiskName(Common.CHARSET)
App.sortedSavegameWorlds.add(worldUUID)
}
@@ -827,15 +832,17 @@ fun AppUpdateListOfSavegames() {
}
}.sortedByDescending { it.getLastModifiedTime() }.forEachIndexed { index, it ->
println("${index+1}.\t${it.diskFile.absolutePath}")
it.rebuild() // disk skimmer was created without initialisation, so do it now
it.rebuild()
val jsonFile = it.getFile(SAVEGAMEINFO)!!
val json = JsonReader().parse(ByteArray64Reader(jsonFile.bytes, Common.CHARSET).readText())
val playerUUID = UUID.fromString(json.getString("uuid"))
var playerUUID: UUID? = null
JsonFetcher.readFromJsonString(ByteArray64Reader(jsonFile.bytes, Common.CHARSET)).forEachSiblings { name, value ->
if (name == "uuid") playerUUID = UUID.fromString(value.asString())
}
// if multiple valid savegames with same UUID exist, only the most recent one is retained
if (!App.savegamePlayers.contains(playerUUID)) {
App.savegamePlayers[playerUUID] = it
App.savegamePlayers[playerUUID] = SavegameCollection.collectFromBaseFilename(File(playersDir), it.diskFile.name)
App.savegamePlayersName[playerUUID] = it.getDiskName(Common.CHARSET)
App.sortedPlayers.add(playerUUID)
}

View File

@@ -31,7 +31,15 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
const val DEFAULT_LOADORDER_FILE = """# Load Order
# Modules are loaded from top to bottom.
# You can disable basegame, but we don't recommend.
# Name of the module corresponds with the name of the directory the module is stored in,
# typically under:
# 1. assets/mods of the installation path (the modules comes with the release of the game)
# 2. %APPDATA%/Modules (the modules installed by the user)
# where %APPDATA% is:
# Windows -- C:\Users\<username>\AppData\Roaming\Terrarum
# macOS -- /Users/<username>/Library/Application Support/Terrarum
# Linux -- /home/<username>/.Terrarum
# Please refrain from removing 'basegame' on the load order -- it may render the game unpalyable.
basegame
"""

View File

@@ -33,14 +33,16 @@ class TerrarumScreenSize(scrw: Int = defaultW, scrh: Int = defaultH) {
val tvSafeActionWidth: Int; get() = Math.round(width * TV_SAFE_ACTION)
val tvSafeActionHeight: Int; get() = Math.round(height * TV_SAFE_ACTION)
/** Apparent window size. `roundToEven(width * magn)` */
var windowW: Int = 0; private set
/** Apparent window size. `roundToEven(height * magn)` */
var windowH: Int = 0; private set
init {
setDimension(maxOf(minimumW, scrw), maxOf(minimumH, scrh), App.getConfigDouble("screenmagnifying").toFloat(), maxOf(minimumW, scrw), maxOf(minimumH, scrh))
setDimension(maxOf(minimumW, scrw), maxOf(minimumH, scrh), App.getConfigDouble("screenmagnifying").toFloat())
}
fun setDimension(scrw: Int, scrh: Int, magn: Float, ww: Int, wh: Int) {
fun setDimension(scrw: Int, scrh: Int, magn: Float,) {
width = scrw and 0x7FFFFFFE
height = scrh and 0x7FFFFFFE
wf = scrw.toFloat()
@@ -54,11 +56,11 @@ class TerrarumScreenSize(scrw: Int = defaultW, scrh: Int = defaultH) {
this.magn = magn
windowW = ww
windowH = wh
windowW = (scrw * magn).ceilInt() and 0x7FFFFFFE
windowH = (scrh * magn).ceilInt() and 0x7FFFFFFE
printdbg(this, "Window dim: $ww x $wh, called by:")
printdbg(this, "Window dim: $windowW x $windowH, called by:")
printStackTrace(this)
}

View File

@@ -136,11 +136,11 @@ class UIItemInventoryCatBar(
private val underlineColour = Color(0xeaeaea_40.toInt())
private val underlineHighlightColour = mainButtons[0].highlightCol
private var highlighterXPos = mainButtons[selectedIndex].posX.toFloat()
private var highlighterXPos = mainButtons[selectedIndex].posX
private var highlighterXStart = highlighterXPos
private var highlighterXEnd = highlighterXPos
private val highlighterYPos = catIcons.tileH + 4f
private val highlighterYPos = catIcons.tileH + 4
private var highlighterMoving = false
private val highlighterMoveDuration: Second = 0.15f
private var highlighterMoveTimer: Second = 0f
@@ -196,11 +196,11 @@ class UIItemInventoryCatBar(
highlighterMoveTimer += delta
highlighterXPos = Movement.moveQuick(
highlighterXStart,
highlighterXEnd,
highlighterXStart.toFloat(),
highlighterXEnd.toFloat(),
highlighterMoveTimer,
highlighterMoveDuration
)
).roundToInt()
if (highlighterMoveTimer > highlighterMoveDuration) {
highlighterMoveTimer = 0f
@@ -224,10 +224,10 @@ class UIItemInventoryCatBar(
// normal stuffs
val oldIndex = selectedIndex
highlighterXStart = mainButtons[selectedIndex].posX.toFloat() // using old selectedIndex
highlighterXStart = mainButtons[selectedIndex].posX // using old selectedIndex
selectedIndex = index
highlighterMoving = true
highlighterXEnd = mainButtons[selectedIndex].posX.toFloat() // using new selectedIndex
highlighterXEnd = mainButtons[selectedIndex].posX // using new selectedIndex
selectionChangeListener?.invoke(oldIndex, index)
}
@@ -290,12 +290,12 @@ class UIItemInventoryCatBar(
if (selectedPanel == 1) {
// indicator
batch.color = underlineHighlightColour
batch.draw(underlineIndTex, (highlighterXPos - buttonGapSize / 2).round(), posY + highlighterYPos)
batch.draw(underlineIndTex, (highlighterXPos - buttonGapSize / 2), posY + highlighterYPos.toFloat())
// label
batch.color = Color.WHITE
catIconsLabels[selectedIcon]().let {
App.fontGame.draw(batch, it, posX + ((width - App.fontGame.getWidth(it)) / 2).toFloat(), posY + highlighterYPos + 4)
App.fontGame.draw(batch, it, posX + ((width - App.fontGame.getWidth(it)) / 2), posY + highlighterYPos + 4)
}
}

View File

@@ -0,0 +1,97 @@
package net.torvald.terrarum
/*import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.client.api.UElementHandler
import com.android.tools.lint.detector.api.*
import net.torvald.random.HQRNG
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameworld.BlockLayer
import net.torvald.terrarum.gameworld.WorldTime
import net.torvald.terrarum.savegame.ByteArray64
import net.torvald.terrarum.utils.*
import org.jetbrains.uast.UClass
import org.jetbrains.uast.UField
import org.jetbrains.uast.util.classSetOf
import org.jetbrains.uast.util.isInstanceOf
import java.math.BigInteger
import java.util.*
/**
* https://medium.com/@vanniktech/writing-your-first-lint-check-39ad0e90b9e6
* https://medium.com/mobile-app-development-publication/making-custom-lint-for-kotlin-code-8a6c203bf474
* https://github.com/googlesamples/android-custom-lint-rules
* https://googlesamples.github.io/android-custom-lint-rules/api-guide.html
*
* Created by minjaesong on 2023-05-22.
*/
class UnserialisableTypeIssueRegistry : IssueRegistry() {
override val issues = listOf(ISSUE_SAVEGAME_UNSERIALISABLE_TYPE_USED_WITHOUT_TRANSIENT)
}
val ISSUE_SAVEGAME_UNSERIALISABLE_TYPE_USED_WITHOUT_TRANSIENT = Issue.create("TerrarumNonTransientUnserialisableType",
"Unserialisable Type Used Without Care",
"Unserialisable type is used on the potentially serialised class without @Transient annotation",
Category.CORRECTNESS,
9,
Severity.ERROR,
Implementation(TerrarumNonTransientUnserialisableType::class.java, EnumSet.of(Scope.JAVA_FILE))
)
class TerrarumNonTransientUnserialisableType : Detector(), Detector.UastScanner {
override fun getApplicablePsiTypes() = listOf(UClass::class.java)
override fun createUastHandler(context: JavaContext) = TerrarumNonTransientUnserialisableTypeHandler(context)
class TerrarumNonTransientUnserialisableTypeHandler(private val context: JavaContext) : UElementHandler() {
override fun visitClass(clazz: UClass) {
/*if (clazz.name?.isDefinedCamelCase() == false) {
context.report(ISSUE_SAVEGAME_UNSERIALISABLE_TYPE_USED_WITHOUT_TRANSIENT, clazz,
context.getNameLocation(clazz),
"Not named in defined camel case.")
}*/
}
override fun visitField(node: UField) {
if (node.uastParent.isInstanceOf(classSetOf(Actor::class.java, GameItem::class.java)) &&
!node.hasAnnotation("Transient") &&
!node.isInstanceOf(classSetOf(
// primitives
String::class.java,
Array<String>::class.java,
Boolean::class.java,
Byte::class.java,
ByteArray::class.java,
ByteArray64::class.java,
Short::class.java,
ShortArray::class.java,
Int::class.java,
IntArray::class.java,
Long::class.java,
LongArray::class.java,
Float::class.java,
FloatArray::class.java,
Double::class.java,
DoubleArray::class.java,
// has serialiser on net.torvald.terrarum.serialise.Common
BigInteger::class.java,
ZipCodedStr::class.java,
BlockLayer::class.java,
WorldTime::class.java,
HashArray::class.java,
HashedWirings::class.java,
HashedWiringGraph::class.java,
WiringGraphMap::class.java,
UUID::class.java,
HQRNG::class.java,
))) {
context.report(
ISSUE_SAVEGAME_UNSERIALISABLE_TYPE_USED_WITHOUT_TRANSIENT,
node,
context.getNameLocation(node),
"Unserialisable type is used on the potentially serialised class without @Transient annotation"
)
}
}
}
}*/

View File

@@ -151,4 +151,8 @@ class WireCodex {
printdbg(this, "Setting prop ${prop.id} ->>\t${prop.nameKey}")
}
fun getAllWiresThatAccepts(accept: String): List<Pair<ItemID, WireProp>> {
return wireProps.filter { it.value.accepts == accept }.toList()
}
}

View File

@@ -16,7 +16,7 @@ object ScreencapNogui: ConsoleCommand {
PixmapIO2.writeTGA(Gdx.files.absolute(App.defaultDir + "/Exports/${args[1]}.tga"), p, true)
p.dispose()
}
IngameRenderer.screencapRequested = true
IngameRenderer.requestScreencap()
Echo("FBO exported to$ccG Exports/${args[1]}.tga")
}
else {

View File

@@ -148,5 +148,5 @@ object AVKey {
*
* example value: `"SIxM+kGlrjZgLx5Zeqz7,;:UIZ5Q=2WT35SgKpOp.,vvf'fNW3G<ROimy(Y;E<,-mdtr5|^RGOqr0x*T*lC,YABr1oQwErKG)pGC'gUG"`
*/
const val WORLD_PORTAL_DICT = ""
const val WORLD_PORTAL_DICT = "worldportaldict"
}

View File

@@ -1,8 +1,10 @@
package net.torvald.terrarum.gameactors
import net.torvald.random.HQRNG
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.ReferencingRanges
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.terrarum.savegame.toBigEndian
import net.torvald.terrarum.utils.PasswordBase32
@@ -82,6 +84,9 @@ abstract class Actor : Comparable<Actor>, Runnable {
if (this is Pocketed)
inventory.actor = this
if (this is ActorHumanoid && vehicleRidingActorID != null) {
vehicleRiding = INGAME.getActorByID(vehicleRidingActorID!!) as Controllable
}
}
/**

View File

@@ -13,10 +13,20 @@ import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.floor
/**
* Used as construction markers and fixture ghost images
* Used as construction markers and fixture ghost images.
*
* `isVisible` behaves differently by `markerMode`.
* - FIXTURE_GHOST: `isVisible` toggles if the ghost is being updated. FALSE - will not be updated and also not visible
* - BLOCK_MARKER: `isVisible` controls the visibility. FALSE - invisible, TRUE - always visible
*
* MarkerMode must be set manually after calling `setGhost` -- the `unsetGhost` will not reset the field.
*/
class BlockMarkerActor : ActorWithBody(Actor.RenderOrder.OVERLAY, physProp = PhysProperties.MOBILE_OBJECT), NoSerialise {
enum class MarkerMode {
FIXTURE_GHOST, BLOCK_MARKER
}
private val defaultSize = 16.0
override var referenceID: ActorID = 2147483647 // custom refID
@@ -29,7 +39,7 @@ class BlockMarkerActor : ActorWithBody(Actor.RenderOrder.OVERLAY, physProp = Phy
get() = CommonResourcePool.getAsTextureRegionPack("blockmarkings_common")
private var ghost: SpriteAnimation? = null
private var hasGhost = false
var markerMode: MarkerMode = MarkerMode.FIXTURE_GHOST
var ghostColour = Color.WHITE
@@ -38,9 +48,10 @@ class BlockMarkerActor : ActorWithBody(Actor.RenderOrder.OVERLAY, physProp = Phy
renderOrder = Actor.RenderOrder.OVERLAY // for some reason the constructor didn't work
}
override fun drawBody(batch: SpriteBatch) {
if (isVisible) {
if (hasGhost) {
if (markerMode == MarkerMode.FIXTURE_GHOST) {
if (INGAME.actorNowPlaying != null) {
mouseInInteractableRange(INGAME.actorNowPlaying!!) {
batch.shader = App.shaderGhastlyWhite
@@ -59,7 +70,7 @@ class BlockMarkerActor : ActorWithBody(Actor.RenderOrder.OVERLAY, physProp = Phy
}
}
}
else {
else if (markerMode == MarkerMode.BLOCK_MARKER) {
batch.shader = null
batch.color = markerColour
batch.draw(blockMarkings.get(markerShape, 0), hitbox.startX.toFloat(), hitbox.startY.toFloat())
@@ -91,13 +102,12 @@ class BlockMarkerActor : ActorWithBody(Actor.RenderOrder.OVERLAY, physProp = Phy
fun setGhost(actor: ActorWithBody) {
ghost = actor.sprite
hasGhost = true
markerMode = MarkerMode.FIXTURE_GHOST
hitbox.setDimension(actor.baseHitboxW.toDouble(), actor.baseHitboxH.toDouble())
}
fun unsetGhost() {
ghost = null
hasGhost = false
setGhostColourNone()
hitbox.setDimension(TILE_SIZED, TILE_SIZED)
}

View File

@@ -241,6 +241,7 @@ object IME {
TerrarumIMEConf(name, copying, lang, candidatesCount, if (keysymtable == null) keysyms else null, if (keysymtable == null) null else keysymtable, mode),
{ headkey, shifted, alted, lowLayerKeysym ->
val a = jsval.invokeMember("accept", headkey, shifted, alted, lowLayerKeysym)
// println(a.getArrayElement(0).asString().map { it.code.toString(16) })
a.getArrayElement(0).asString().toCanditates() to a.getArrayElement(1).asString()
}, {
jsval.invokeMember("backspace").asString().toCanditates()

View File

@@ -29,7 +29,7 @@ class IMEDictionary(private val filename: String) {
private var dictLoaded = false
private fun loadDict() {
val reader = FileReader(File("assets/keylayout/", filename))
val reader = FileReader(File("assets/keylayout/", filename), Charsets.UTF_8)
reader.forEachLine {
if (it.contains(',')) {
val (key, value) = it.split(',')

View File

@@ -13,6 +13,9 @@ import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameactors.ActorInventory
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
import net.torvald.terrarum.savegame.ByteArray64
import net.torvald.terrarum.utils.HashArray
import net.torvald.terrarum.utils.ZipCodedStr
import org.dyn4j.geometry.Vector2
typealias ItemID = String

View File

@@ -39,10 +39,12 @@ class SimpleGameWorld : GameWorld() {
override lateinit var layerTerrain: BlockLayer
}
open class GameWorld() : Disposable {
open class GameWorld(
val worldIndex: UUID // should not be immutable as JSON loader will want to overwrite it
) : Disposable {
constructor() : this(UUID.randomUUID())
// var worldName: String = "New World"
var worldIndex: UUID = UUID.randomUUID() // should not be immutable as JSON loader will want to overwrite it
var worldCreator: UUID = UUID(0L,0L) // TODO record a value to this
var width: Int = 999; private set
var height: Int = 999; private set
@@ -387,14 +389,14 @@ open class GameWorld() : Disposable {
return wiringGraph[blockAddr]?.get(itemID)?.emt
}
fun getWireRecvStateOf(x: Int, y: Int, itemID: ItemID): ArrayList<WireRecvState>? {
fun getWireReceptionStateOf(x: Int, y: Int, itemID: ItemID): ArrayList<WireReceptionState>? {
val (x, y) = coerceXY(x, y)
val blockAddr = LandUtil.getBlockAddr(this, x, y)
return getWireRecvStateUnsafe(blockAddr, itemID)
return getWireReceptionStateUnsafe(blockAddr, itemID)
}
fun getWireRecvStateUnsafe(blockAddr: BlockAddress, itemID: ItemID): ArrayList<WireRecvState>? {
return wiringGraph[blockAddr]?.get(itemID)?.rcv
fun getWireReceptionStateUnsafe(blockAddr: BlockAddress, itemID: ItemID): ArrayList<WireReceptionState>? {
return wiringGraph[blockAddr]?.get(itemID)?.rcp
}
fun setWireGraphOf(x: Int, y: Int, itemID: ItemID, cnx: Int) {
@@ -427,7 +429,7 @@ open class GameWorld() : Disposable {
wiringGraph[blockAddr]!![itemID]!!.emt.set(vector)
}
fun addWireRecvStateOf(x: Int, y: Int, itemID: ItemID, state: WireRecvState) {
fun addWireRecvStateOf(x: Int, y: Int, itemID: ItemID, state: WireReceptionState) {
val (x, y) = coerceXY(x, y)
val blockAddr = LandUtil.getBlockAddr(this, x, y)
return addWireRecvStateOfUnsafe(blockAddr, itemID, state)
@@ -439,13 +441,13 @@ open class GameWorld() : Disposable {
return clearAllWireRecvStateUnsafe(blockAddr)
}
fun addWireRecvStateOfUnsafe(blockAddr: BlockAddress, itemID: ItemID, state: WireRecvState) {
fun addWireRecvStateOfUnsafe(blockAddr: BlockAddress, itemID: ItemID, state: WireReceptionState) {
if (wiringGraph[blockAddr] == null)
wiringGraph[blockAddr] = WiringGraphMap()
if (wiringGraph[blockAddr]!![itemID] == null)
wiringGraph[blockAddr]!![itemID] = WiringSimCell(0)
wiringGraph[blockAddr]!![itemID]!!.rcv.add(state)
wiringGraph[blockAddr]!![itemID]!!.rcp.add(state)
}
fun getAllWiringGraph(x: Int, y: Int): HashMap<ItemID, WiringSimCell>? {
@@ -460,7 +462,7 @@ open class GameWorld() : Disposable {
fun clearAllWireRecvStateUnsafe(blockAddr: BlockAddress) {
wiringGraph[blockAddr]?.forEach {
it.value.rcv.clear()
it.value.rcp.clear()
}
}
@@ -652,7 +654,7 @@ open class GameWorld() : Disposable {
val ws: SortedArrayList<ItemID> = SortedArrayList<ItemID>() // what could possibly go wrong bloating up the RAM footprint when it's practically infinite these days?
)
data class WireRecvState(
data class WireReceptionState(
var dist: Int = -1, // how many tiles it took to traverse
var src: Point2i = Point2i(0,0) // xy position
// to get the state, use the src to get the state of the source emitter directly, then use dist to apply attenuation
@@ -664,7 +666,7 @@ open class GameWorld() : Disposable {
data class WiringSimCell(
var cnx: Int = 0, // connections. [1, 2, 4, 8] = [RIGHT, DOWN, LEFT, UP]
val emt: Vector2 = Vector2(0.0, 0.0), // i'm emitting this much power
val rcv: ArrayList<WireRecvState> = ArrayList() // how far away are the power sources
val rcp: ArrayList<WireReceptionState> = ArrayList() // how far away are the power sources
)
fun getTemperature(worldTileX: Int, worldTileY: Int): Float? {

View File

@@ -3,6 +3,7 @@ package net.torvald.terrarum.gameworld
import com.badlogic.gdx.utils.Queue
import net.torvald.terrarum.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.gameactors.ActorWithBody
@@ -468,14 +469,15 @@ object WorldSimulator {
/**
* @return List of FixtureBases, safe to cast into Electric
*/
private fun wiresimGetSourceBlocks(): List<FixtureBase> =
INGAME.actorContainerActive.filterIsInstance<FixtureBase>().filter {
it is Electric && it.inUpdateRange(world) && it.wireEmitterTypes.isNotEmpty()
private fun wiresimGetSourceBlocks(): List<Electric> =
INGAME.actorContainerActive.filterIsInstance<Electric>().filter {
it.inUpdateRange(world) && it.wireEmitterTypes.isNotEmpty()
}
private val wireSimMarked = HashSet<Long>()
private val wireSimPoints = Queue<WireGraphCursor>()
private val oldTraversedNodes = ArrayList<WireGraphCursor>()
private val fixtureCache = HashMap<Point2i, Pair<Electric, WireEmissionType>>() // also instance of Electric
private fun simulateWires(delta: Float) {
// unset old wires before we begin
@@ -484,14 +486,15 @@ object WorldSimulator {
}
oldTraversedNodes.clear()
fixtureCache.clear()
wiresimGetSourceBlocks().let { sources ->
// signal-emitting fixtures must set emitState of its own tiles via update()
sources.forEach {
(it as Electric).wireEmitterTypes.forEach { wireType, bbi ->
it.wireEmitterTypes.forEach { bbi, wireType ->
val startingPoint = it.worldBlockPos!! + it.blockBoxIndexToPoint2i(bbi)
val signal = (it as Electric).wireEmission[bbi] ?: Vector2(0.0, 0.0)
val signal = it.wireEmission[bbi] ?: Vector2(0.0, 0.0)
world.getAllWiringGraph(startingPoint.x, startingPoint.y)?.keys?.filter { WireCodex[it].accepts == wireType }?.forEach { wire ->
val simStartingPoint = WireGraphCursor(startingPoint, wire)
@@ -506,6 +509,8 @@ object WorldSimulator {
private fun traverseWireGraph(world: GameWorld, wire: ItemID, startingPoint: WireGraphCursor, signal: Vector2) {
val emissionType = WireCodex[wire].accepts
fun getAdjacent(cnx: Int, point: WireGraphCursor): List<WireGraphCursor> {
val r = ArrayList<WireGraphCursor>()
for (dir in intArrayOf(RIGHT, DOWN, LEFT, UP)) {
@@ -540,6 +545,28 @@ object WorldSimulator {
enq(x)
}
}
// do something with the power receiver
val tilePoint = Point2i(point.x, point.y)
var fixture = fixtureCache[tilePoint]
var tileOffsetFromFixture: Point2i? = null
if (fixture == null) {
INGAME.getActorsAt(point.x * TILE_SIZED, point.y * TILE_SIZED).filterIsInstance<Electric>().firstOrNull().let { found ->
if (found != null) {
// get offset from the fixture's origin
tileOffsetFromFixture = found.intTilewiseHitbox.let { Point2i(it.startX.toInt(), it.startY.toInt()) } - tilePoint
// println("$tilePoint; ${found.javaClass.canonicalName}, $tileOffsetFromFixture, ${found.getWireSinkAt(tileOffsetFromFixture!!)}")
if (found.getWireSinkAt(tileOffsetFromFixture!!) == emissionType) {
fixtureCache[tilePoint] = found to emissionType
fixture = found to emissionType
}
}
}
}
fixture?.first?.updateOnWireGraphTraversal(tileOffsetFromFixture!!.x, tileOffsetFromFixture!!.y, fixture!!.second)
}
}
}

View File

@@ -123,6 +123,9 @@ object Lang {
private val bindOp = ">>="
fun getOrNull(key: String?, capitalise: Boolean = true) =
if (key == null) null else get(key, capitalise)
/**
* Syntax example:
*

View File

@@ -28,14 +28,8 @@ class EntryPoint : ModuleEntryPoint() {
printdbg(this, "Hello, world!")
// load common resources to the AssetsManager
CommonResourcePool.addToLoadingList("$moduleName.items16") {
TextureRegionPack(ModMgr.getGdxFile(moduleName, "items/items.tga"), 16, 16)
}
CommonResourcePool.addToLoadingList("$moduleName.items24") {
TextureRegionPack(ModMgr.getGdxFile(moduleName, "items/items24.tga"), 24, 24)
}
CommonResourcePool.addToLoadingList("$moduleName.items48") {
TextureRegionPack(ModMgr.getGdxFile(moduleName, "items/items48.tga"), 48, 48)
CommonResourcePool.addToLoadingList("$moduleName.items") {
ItemSheet(ModMgr.getGdxFile(moduleName, "items/items.tga"))
}
CommonResourcePool.loadAll()

View File

@@ -11,7 +11,7 @@ import com.badlogic.gdx.utils.Disposable
import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.random.HQRNG
import net.torvald.terrarum.*
import net.torvald.terrarum.App.measureDebugTime
import net.torvald.terrarum.App.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF
import net.torvald.terrarum.gameactors.ActorWithBody
@@ -21,6 +21,7 @@ import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.weather.WeatherMixer
import net.torvald.terrarum.weather.WeatherMixer.render
import net.torvald.terrarum.worlddrawer.BlocksDrawer
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarum.worlddrawer.LightmapRenderer
@@ -204,7 +205,7 @@ object IngameRenderer : Disposable {
actorsRenderOverlay: List<ActorWithBody>,
particlesContainer : CircularArray<ParticleBase>,
player: ActorWithBody? = null,
uiContainer: UIContainer? = null
uiContainer: UIContainer? = null,
) {
renderingActorsCount = (actorsRenderBehind.size) +
(actorsRenderMiddle.size) +
@@ -365,13 +366,19 @@ object IngameRenderer : Disposable {
///////////////////////////////////////////////////////////////////////
if (screencapRequested) {
screencapRequested = false
printdbg(this, "Screencap was requested, processing...")
var hasError = false
try {
screencapExportCallback(fboMixedOut)
}
catch (e: Throwable) {
printdbgerr(this, "An error occured while taking screencap:")
e.printStackTrace()
hasError = true
}
printdbg(this, "Screencap ${if (hasError) "failed" else "successful"}")
screencapBusy = false
screencapRequested = false
}
///////////////////////////////////////////////////////////////////////
@@ -416,11 +423,18 @@ object IngameRenderer : Disposable {
* This "screencap" will capture the game WITHOUT gui and postprocessors!
* To capture the entire game, use [App.requestScreenshot]
*/
@Volatile internal var screencapRequested = false
@Volatile internal var fboRGBexportedLatch = false
@Volatile private var screencapRequested = false
@Volatile internal var screencapBusy = false; private set
@Volatile internal var screencapExportCallback: (FrameBuffer) -> Unit = {}
@Volatile internal lateinit var fboRGBexport: Pixmap
fun requestScreencap() {
screencapRequested = true
screencapBusy = true
printdbg(this, "requestScreencap called from:")
printStackTrace(this)
}
private fun drawToRGB(
actorsRenderBehind: List<ActorWithBody>?,
actorsRenderMiddle: List<ActorWithBody>?,

View File

@@ -3,6 +3,7 @@ package net.torvald.terrarum.modulebasegame
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.*
import net.torvald.terrarum.App.*
@@ -26,6 +27,7 @@ import net.torvald.terrarum.gameitems.mouseInInteractableRange
import net.torvald.terrarum.gameparticles.ParticleBase
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.WorldSimulator
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameactors.*
import net.torvald.terrarum.modulebasegame.gameactors.physicssolver.CollisionSolver
import net.torvald.terrarum.modulebasegame.gameitems.PickaxeCore
@@ -34,6 +36,7 @@ import net.torvald.terrarum.modulebasegame.serialise.LoadSavegame
import net.torvald.terrarum.modulebasegame.serialise.ReadActor
import net.torvald.terrarum.modulebasegame.serialise.WriteSavegame
import net.torvald.terrarum.modulebasegame.ui.*
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.gradEndCol
import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser
import net.torvald.terrarum.modulebasegame.worldgenerator.Worldgen
import net.torvald.terrarum.modulebasegame.worldgenerator.WorldgenParams
@@ -42,6 +45,7 @@ import net.torvald.terrarum.savegame.VDUtil
import net.torvald.terrarum.savegame.VirtualDisk
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.Toolkit.hdrawWidth
import net.torvald.terrarum.ui.UIAutosaveNotifier
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.weather.WeatherMixer
@@ -53,6 +57,7 @@ import net.torvald.unicode.EMDASH
import net.torvald.util.CircularArray
import org.khelekore.prtree.PRTree
import java.util.*
import kotlin.math.roundToInt
/**
@@ -144,10 +149,11 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
val SIZE_NORMAL = Point2i(9000, 2250)
val SIZE_LARGE = Point2i(13500, 2970)
val SIZE_HUGE = Point2i(22500, 4500)
val WORLDSIZE = if (App.IS_DEVELOPMENT_BUILD)
arrayOf(Point2i(2880, 1350), SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE)
val NEW_WORLD_SIZE = if (App.IS_DEVELOPMENT_BUILD)
arrayOf(Point2i(2880, 1350), /*SIZE_SMALL, */SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE)
else
arrayOf(SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE)
arrayOf(/*SIZE_SMALL, */SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE)
val WORLDPORTAL_NEW_WORLD_SIZE = arrayOf(SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE)
val worldgenThreadExecutor = ThreadExecutor()
}
@@ -259,7 +265,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
}
IngameRenderer.setRenderedWorld(world)
blockMarkingActor.isVisible = true
super.show() // this function sets gameInitialised = true
}
@@ -417,10 +423,15 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
// 2. cannot sync up the "counter" to determine whether both are finished
uiAutosaveNotifier.setAsOpen()
val saveTime_t = App.getTIME_T()
printdbg(this, "Immediate Save")
WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.PLAYER, playerDisk, getPlayerSaveFiledesc(playerSavefileName), this, true, autosaveOnErrorAction) {
printdbg(this, "immediate save callback from PLAYER")
makeSavegameBackupCopy(getPlayerSaveFiledesc(playerSavefileName))
WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.WORLD, worldDisk, getWorldSaveFiledesc(worldSavefileName), this, true, autosaveOnErrorAction) {
printdbg(this, "immediate save callback from WORLD")
makeSavegameBackupCopy(getWorldSaveFiledesc(worldSavefileName)) // don't put it on the postInit() or render(); must be called using callback
uiAutosaveNotifier.setAsClose()
}
@@ -469,13 +480,14 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
world.worldCreator = UUID.fromString(player.uuid.toString())
printdbg(this, "new woridIndex: ${world.worldIndex}")
printdbg(this, "new worldIndex: ${world.worldIndex}")
printdbg(this, "worldCurrentlyPlaying: ${player.worldCurrentlyPlaying}")
actorNowPlaying = player
actorGamer = player
forceAddActor(player)
WeatherMixer.internalReset()
}
KeyToggler.forceSet(Input.Keys.Q, false)
@@ -520,7 +532,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
// pie menu
uiPieMenu = UIQuickslotPie()
uiPieMenu.setPosition(drawWidth / 2, App.scr.halfh)
uiPieMenu.setPosition(hdrawWidth, App.scr.halfh)
// vital metre
// fill in getter functions by
@@ -654,21 +666,21 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
// what if there's multiple of such fixtures? whatever, you are supposed to DISALLOW such situation.
for (kk in actorsUnderMouse.indices) {
if (mouseInInteractableRange(actor) {
actorsUnderMouse[kk].mainUI?.let {
uiOpened = true
actorsUnderMouse[kk].mainUI?.let {
uiOpened = true
// property 'uiFixture' is a dedicated property that the TerrarumIngame recognises.
// when it's not null, the UI will be updated and rendered
// when the UI is closed, it'll be replaced with a null value
uiFixture = it
it.setPosition(
(Toolkit.drawWidth - it.width) / 4,
(App.scr.height - it.height) / 4 // what the fuck?
)
it.setAsOpen()
}
0L
} == 0L) break
// property 'uiFixture' is a dedicated property that the TerrarumIngame recognises.
// when it's not null, the UI will be updated and rendered
// when the UI is closed, it'll be replaced with a null value
uiFixture = it
it.setPosition(
(Toolkit.drawWidth - it.width) / 4,
(App.scr.height - it.height) / 4 // what the fuck?
)
it.setAsOpen()
}
0L
} == 0L) break
}
if (!uiOpened) {
@@ -714,6 +726,10 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
gameUpdateGovernor.reset()
if (UILoadGovernor.previousSaveWasLoaded) {
sendNotification(listOf(Lang["GAME_PREV_SAVE_WAS_LOADED1"], Lang["GAME_PREV_SAVE_WAS_LOADED2"]))
}
gameFullyLoaded = true
}
@@ -850,7 +866,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
//notifier.update(delta)
// open/close fake blur UI according to what's opened
if (uiInventoryPlayer.isVisible ||
getUIFixture.get()?.isVisible == true) {
getUIFixture.get()?.isVisible == true || worldTransitionOngoing) {
uiBlur.setAsOpen()
}
else {
@@ -866,6 +882,26 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
//println("paused = $paused")
if ((!paused && !App.isScreenshotRequested()) && newWorldLoadedLatch) newWorldLoadedLatch = false
if (doThingsAfterSave) {
saveRequested2 = false
doThingsAfterSave = false
saveCallback!!()
}
if (saveRequested2) {
saveRequested2 = false
doForceSave()
}
if (worldTransitionPauseRequested > 0) { // let a frame to update before locking (=pausing) entirely
worldTransitionPauseRequested -= 1
}
else if (worldTransitionPauseRequested == 0) {
paused = true
}
}
@@ -900,6 +936,93 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
actorNowPlaying,
uiContainer// + uiFixture
)
// quick and dirty way to show
if (worldTransitionOngoing) {
batch.inUse {
batch.color = gradEndCol
Toolkit.fillArea(batch, 0, 0, App.scr.width, App.scr.height)
batch.color = Color.WHITE
val t = Lang["MENU_IO_SAVING"]
val circleSheet = CommonResourcePool.getAsTextureRegionPack("loading_circle_64")
Toolkit.drawTextCentered(batch, App.fontGame, t, Toolkit.drawWidth, 0, ((App.scr.height - circleSheet.tileH) / 2) - 40)
// -1..63
val index =
((WriteSavegame.saveProgress / WriteSavegame.saveProgressMax) * circleSheet.horizontalCount * circleSheet.verticalCount).roundToInt() - 1
if (index >= 0) {
val sx = index % circleSheet.horizontalCount
val sy = index / circleSheet.horizontalCount
// q&d fix for ArrayIndexOutOfBoundsException caused when saving huge world... wut?
if (sx in 0 until circleSheet.horizontalCount && sy in 0 until circleSheet.horizontalCount) {
batch.draw(
circleSheet.get(sx, sy),
((Toolkit.drawWidth - circleSheet.tileW) / 2).toFloat(),
((App.scr.height - circleSheet.tileH) / 2).toFloat()
)
}
}
}
}
}
private var worldTransitionOngoing = false
private var worldTransitionPauseRequested = -1
private var saveRequested2 = false
private var saveCallback: (() -> Unit)? = null
private var doThingsAfterSave = false
override fun requestForceSave(callback: () -> Unit) {
saveCallback = callback
worldTransitionOngoing = true
saveRequested2 = true
worldTransitionPauseRequested = 1
blockMarkingActor.isVisible = false
}
internal fun doForceSave() {
// TODO show appropriate UI
// uiBlur.setAsOpen()
saveTheGame({ // onSuccessful
System.gc()
autosaveTimer = 0f
// TODO hide appropriate UI
uiBlur.setAsClose()
doThingsAfterSave = true
}, { // onError
// TODO show failure message
// TODO hide appropriate UI
uiBlur.setAsClose()
})
}
override fun saveTheGame(onSuccessful: () -> Unit, onError: (Throwable) -> Unit) {
val saveTime_t = App.getTIME_T()
val playerSavefile = getPlayerSaveFiledesc(INGAME.playerSavefileName)
val worldSavefile = getWorldSaveFiledesc(INGAME.worldSavefileName)
INGAME.makeSavegameBackupCopy(playerSavefile)
WriteSavegame(saveTime_t, WriteSavegame.SaveMode.PLAYER, INGAME.playerDisk, playerSavefile, INGAME as TerrarumIngame, false, onError) {
INGAME.makeSavegameBackupCopy(worldSavefile)
WriteSavegame(saveTime_t, WriteSavegame.SaveMode.WORLD, INGAME.worldDisk, worldSavefile, INGAME as TerrarumIngame, false, onError) {
// callback:
// rebuild the disk skimmers
INGAME.actorContainerActive.filterIsInstance<IngamePlayer>().forEach {
printdbg(this, "Game Save callback -- rebuilding the disk skimmer for IngamePlayer ${it.actorValue.getAsString(AVKey.NAME)}")
// it.rebuildingDiskSkimmer?.rebuild()
}
// return to normal state
onSuccessful()
}
}
}
private val maxRenderableWires = ReferencingRanges.ACTORS_WIRES.last - ReferencingRanges.ACTORS_WIRES.first + 1
@@ -1106,13 +1229,13 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
uiAutosaveNotifier.setAsOpen()
val saveTime_t = App.getTIME_T()
val playerSavefile = getPlayerSaveFiledesc(INGAME.playerSavefileName)
val worldSavefile = getWorldSaveFiledesc(INGAME.worldSavefileName)
val playerSavefile0 = getPlayerSaveFiledesc(INGAME.playerSavefileName)
val worldSavefile0 = getWorldSaveFiledesc(INGAME.worldSavefileName)
INGAME.makeSavegameBackupCopy(playerSavefile)
val playerSavefile = INGAME.makeSavegameBackupCopyAuto(playerSavefile0)
WriteSavegame(saveTime_t, WriteSavegame.SaveMode.PLAYER, INGAME.playerDisk, playerSavefile, INGAME as TerrarumIngame, true, autosaveOnErrorAction) {
INGAME.makeSavegameBackupCopy(worldSavefile)
val worldSavefile = INGAME.makeSavegameBackupCopyAuto(worldSavefile0)
WriteSavegame(saveTime_t, WriteSavegame.SaveMode.QUICK_WORLD, INGAME.worldDisk, worldSavefile, INGAME as TerrarumIngame, true, autosaveOnErrorAction) {
// callback:
// rebuild the disk skimmers
@@ -1263,9 +1386,9 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
val fixturesUnderHand = ArrayList<FixtureBase>()
val mobsUnderHand = ArrayList<ActorWithBody>()
actorsUnderMouse.forEach {
if (it is FixtureBase && it.mainUI == null)
if (it is FixtureBase) // && it.mainUI == null) // pickup avail check must be done against fixture.canBeDespawned
fixturesUnderHand.add(it)
else
else if (it !is FixtureBase)
mobsUnderHand.add(it)
}

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.modulebasegame
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.InputAdapter
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.OrthographicCamera
@@ -13,17 +14,20 @@ import net.torvald.random.HQRNG
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.App.printdbgerr
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF
import net.torvald.terrarum.console.CommandDict
import net.torvald.terrarum.gameactors.*
import net.torvald.terrarum.gameactors.ai.ActorAI
import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
import net.torvald.terrarum.gameparticles.ParticleBase
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.WorldTime
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.ui.UILoadGovernor
import net.torvald.terrarum.modulebasegame.ui.UIRemoCon
import net.torvald.terrarum.modulebasegame.ui.UITitleRemoConYaml
import net.torvald.terrarum.realestate.LandUtil
@@ -64,50 +68,72 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
private lateinit var demoWorld: GameWorld
private lateinit var cameraNodes: FloatArray // camera Y-pos
private val cameraNodeWidth = 15
private val lookaheadDist = cameraNodeWidth * TILE_SIZED
private fun getPointAt(px: Double): Double {
val ww = TILE_SIZEF * demoWorld.width
val x = px % ww
val indexThis = ((x / ww * cameraNodes.size).floorInt())
val xwstart: Double = indexThis.toDouble() / cameraNodes.size * ww
val xwend: Double = ((indexThis + 1).toDouble() / cameraNodes.size) * ww
val xw: Double = xwend - xwstart
val xperc: Double = (x - xwstart) / xw
// return FastMath.interpolateLinear(xperc.toFloat(), cameraNodes[indexThis fmod cameraNodes.size], cameraNodes[(indexThis + 1) fmod cameraNodes.size]).toDouble()
return FastMath.interpolateCatmullRom(xperc.toFloat(),
cameraNodes[(indexThis - 1) fmod cameraNodes.size],
cameraNodes[(indexThis - 0) fmod cameraNodes.size],
cameraNodes[(indexThis + 1) fmod cameraNodes.size],
cameraNodes[(indexThis + 2) fmod cameraNodes.size]
).toDouble()
}
private val cameraAI = object : ActorAI {
private var firstTime = true
private val lookaheadDist = 100.0
private fun getPointAt(px: Double): Float {
val ww = TILE_SIZEF * demoWorld.width
val x = px % ww
val indexThis = ((x / ww * cameraNodes.size).floorInt()) fmod cameraNodes.size
val xwstart: Float = indexThis.toFloat() / cameraNodes.size * ww
val xwend: Float = ((indexThis + 1).toFloat() / cameraNodes.size) * ww
val xw: Float = xwend - xwstart
val xperc: Double = (x - xwstart) / xw
return FastMath.interpolateLinear(xperc.toFloat(), cameraNodes[indexThis], cameraNodes[(indexThis + 1) % cameraNodes.size])
}
override fun update(actor: Actor, delta: Float) {
val ww = TILE_SIZEF * demoWorld.width
val actor = actor as CameraPlayer
val px: Double = actor.hitbox.canonicalX + actor.actorValue.getAsDouble(AVKey.SPEED)!!
val pxP = px - lookaheadDist * cos(actor.targetBearing)
val pxN = px + lookaheadDist * cos(actor.targetBearing)
val stride = cos(actor.bearing1A) * actor.actorValue.getAsDouble(AVKey.SPEED)!!
val yP = getPointAt(pxP)
val yN = getPointAt(pxN)
val x1 = actor.hitbox.startX// + stride
val y1 = actor.hitbox.startY
val y = (yP + yN) / 2f
val px1L = x1 - lookaheadDist
val px1C = x1
val px1R = x1 + lookaheadDist
val py1L = getPointAt(px1L)
val py1C = getPointAt(px1C)
val py1R = getPointAt(px1R)
val px2L = (px1L + px1C) / 2.0
val px2R = (px1C + px1R) / 2.0
val py2L = (py1L + py1C) / 2.0
val py2R = (py1C + py1R) / 2.0
val x2 = (px2L + px2R) / 2
val y2 = (py2L + py2R) / 2
val theta = atan2(py2R - py2L, px2R - px2L)
if (firstTime) {
firstTime = false
actor.hitbox.setPositionY(y - 8.0)
actor.hitbox.setPosition(x1, getPointAt(x1))
}
else {
//actor.moveTo(px, y - 8.0)
//actor.hitbox.setPosition(px, y - 8.0)
actor.moveTo(atan2((yN - yP).toDouble(), pxN - pxP))
actor.bearing1A = atan2(py1C - py1L, px1C - px1L)
actor.bearing1B = atan2(py1R - py1C, px1R - px1C)
actor.bearing2A = atan2(py2R - py2L, px2R - px2L)
actor.moveTo(theta)
// actor.hitbox.setPosition(x2, y2) // there is no reason it would work -- speed is wildly inconsistent as the angle reaches 90deg
}
if (actor.hitbox.canonicalX > ww) {
if (actor.hitbox.startX > ww) {
actor.hitbox.translatePosX(-ww.toDouble())
}
}
}
private lateinit var cameraPlayer: ActorWithBody
@@ -123,9 +149,9 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
private lateinit var worldFBO: FloatFrameBuffer
private val warning32bitJavaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/32_bit_warning.tga")))
private val warningAppleRosettaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/apple_rosetta_warning.tga")))
init {
warning32bitJavaIcon.flip(false, false)
gameUpdateGovernor = ConsistentUpdateRate.also { it.reset() }
}
@@ -139,7 +165,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
//ReadWorld.readWorldAndSetNewWorld(Terrarum.ingame!! as TerrarumIngame, reader)
val world = ReadSimpleWorld(reader, file)
demoWorld = world
demoWorld.worldTime.timeDelta = 0//60
demoWorld.worldTime.timeDelta = 330 // a year = 8 minutes
printdbg(this, "Demo world loaded")
}
catch (e: IOException) {
@@ -153,7 +179,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
demoWorld.worldTime.addTime(WorldTime.DAY_LENGTH * 32)
// construct camera nodes
val nodeCount = demoWorld.width / 10
val nodeCount = demoWorld.width / cameraNodeWidth
cameraNodes = kotlin.FloatArray(nodeCount) {
val tileXPos = (demoWorld.width.toFloat() * it / nodeCount).floorInt()
var travelDownCounter = 0
@@ -238,6 +264,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
printdbg(this, "update list of savegames")
// to show "Continue" and "Load" on the titlescreen, uncomment this line
App.updateListOfSavegames()
UILoadGovernor.reset()
loadThingsWhileIntroIsVisible()
@@ -254,9 +281,14 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
}
private val updateScreen = { delta: Float ->
demoWorld.globalLight = WeatherMixer.globalLightNow
// TODO: desynched weather and time-of-day change
val forcedTime = 39693
// demoWorld.globalLight = WeatherMixer.globalLightNow
demoWorld.globalLight = WeatherMixer.getGlobalLightOfTime(demoWorld, forcedTime)
demoWorld.updateWorldTime(delta)
WeatherMixer.update(delta, cameraPlayer, demoWorld)
// WeatherMixer.update(delta, cameraPlayer, demoWorld)
WeatherMixer.forceTimeAt = forcedTime
cameraPlayer.update(delta)
// worldcamera update AFTER cameraplayer in this case; the other way is just an exception for actual ingame SFX
@@ -269,6 +301,24 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
private val particles = CircularArray<ParticleBase>(16, true)
private fun drawLineOnWorld(x1: Float, y1: Float, x2: Float, y2: Float) {
val w = 2.0f
App.shapeRender.rectLine(
x1 - WorldCamera.x, App.scr.height - (y1 - WorldCamera.y),
x2 - WorldCamera.x, App.scr.height - (y2 - WorldCamera.y),
w
)
}
private fun drawLineOnWorld(x1: Double, y1: Double, x2: Double, y2: Double) {
val ww = demoWorld.width * TILE_SIZE
drawLineOnWorld(x1.toFloat(), y1.toFloat(), x2.toFloat(), y2.toFloat())
drawLineOnWorld(x1.toFloat() + ww, y1.toFloat(), x2.toFloat() + ww, y2.toFloat())
}
private val baseSlopeCol = Color(0f, 0.9f, 0.85f, 1f)
private val firstOrderSlopeCol = Color(0f, 0.4f, 0f, 1f)
private val secondOrderSlopeCol = Color(1f, 0.3f, 0.6f, 1f)
private val renderScreen = { delta: Float ->
Gdx.graphics.setTitle(TerrarumIngame.getCanonicalTitle())
@@ -291,6 +341,52 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
particles,
uiContainer = uiContainer
)
if (KeyToggler.isOn(Input.Keys.F10)) {
App.shapeRender.inUse {
val actor = cameraPlayer as CameraPlayer
val x1 = actor.hitbox.startX
val y1 = actor.hitbox.startY
val px1L = x1 - lookaheadDist// * cos(actor.bearing1A)
val px1C = x1
val px1R = x1 + lookaheadDist// * cos(actor.bearing1B)
val py1L = getPointAt(px1L)
val py1C = getPointAt(px1C)
val py1R = getPointAt(px1R)
val px2L = (px1L + px1C) / 2.0
val px2R = (px1C + px1R) / 2.0
val py2L = (py1L + py1C) / 2.0
val py2R = (py1C + py1R) / 2.0
it.color = firstOrderSlopeCol
drawLineOnWorld(px1L, py1L, px1C, py1C)
drawLineOnWorld(px1C, py1C, px1R, py1R)
/*(1..cameraNodes.lastIndex + 16).forEach { index0 ->
val x1 = (index0 - 1) * cameraNodeWidth * TILE_SIZEF; val x2 = (index0 - 0) * cameraNodeWidth * TILE_SIZEF
val y1 = cameraNodes[(index0 - 1) fmod cameraNodes.size]; val y2 = cameraNodes[index0 fmod cameraNodes.size]
drawLineOnWorld(x1, y1, x2, y2)
}*/
it.color = baseSlopeCol
val points = (0..App.scr.width).map { x ->
val worldX = (WorldCamera.x + x).toDouble()
worldX to getPointAt(worldX)
}
points.forEachIndexed { index, (x, y) ->
if (index > 0) {
drawLineOnWorld(points[index - 1].first, points[index - 1].second, x, y)
}
}
it.color = secondOrderSlopeCol
drawLineOnWorld(px2L, py2L, px2R, py2R)
}
}
}
else {
printdbgerr(this, "Demoworld is already been destroyed")
@@ -333,16 +429,34 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
batch.color = Color.WHITE
if (App.is32BitJVM && uiRemoCon.currentRemoConContents.parent == null) {
// if (uiRemoCon.currentRemoConContents.parent == null) {
val linegap = 4
val imgTxtGap = 10
val yoff = App.scr.height - App.scr.tvSafeGraphicsHeight - 64 - (3*(20+linegap)) - imgTxtGap - 9
Toolkit.drawCentered(batch, warning32bitJavaIcon, yoff)
for (i in 0..2) {
val text = Lang.get("GAME_32BIT_WARNING${i+1}", (i != 2))
if (i == 2) batch.color = Toolkit.Theme.COL_SELECTED
App.fontGame.draw(batch, text, ((drawWidth - App.fontGame.getWidth(text)) / 2).toFloat(), yoff + imgTxtGap + 64f + linegap + i*(20+linegap))
// warn: 32-bit
val linegap = 4
val imgTxtGap = 10
val yoff = App.scr.height - App.scr.tvSafeGraphicsHeight - 64 - (3*(20+linegap)) - imgTxtGap - 9
if (uiRemoCon.currentRemoConContents.parent == null) {
var texts = emptyList<String>()
var textcols = emptyList<Color>()
if (App.is32BitJVM) {
Toolkit.drawCentered(batch, warning32bitJavaIcon, yoff)
texts = (1..3).map { Lang.get("GAME_32BIT_WARNING$it", (it != 3)) }
textcols = (1..3).map { if (it == 3) Toolkit.Theme.COL_SELECTED else Color.WHITE }
}
// warn: rosetta on Apple M-chips
else if (App.getUndesirableConditions() == "apple_execution_through_rosetta") {
Toolkit.drawCentered(batch, warningAppleRosettaIcon, yoff)
texts = (1..2).map { Lang.get("GAME_APPLE_ROSETTA_WARNING$it") }
textcols = texts.map { Color.WHITE }
}
texts.forEachIndexed { i, text ->
batch.color = textcols[i]
App.fontGame.draw(
batch,
text,
((drawWidth - App.fontGame.getWidth(text)) / 2).toFloat(),
yoff + imgTxtGap + 64f + linegap + i * (20 + linegap)
)
}
}
}
@@ -380,6 +494,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
uiRemoCon.dispose()
demoWorld.dispose()
warning32bitJavaIcon.texture.dispose()
warningAppleRosettaIcon.texture.dispose()
}
override fun inputStrobed(e: TerrarumKeyboardEvent) {
@@ -436,7 +551,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
override val hitbox = Hitbox(0.0, 0.0, 2.0, 2.0)
init {
actorValue[AVKey.SPEED] = 1.666
actorValue[AVKey.SPEED] = 1.666 * (if (Math.random() < 1.0 / 65536.0) -1 else 1) // some easter egg
hitbox.setPosition(
HQRNG().nextInt(demoWorld.width) * TILE_SIZED,
0.0 // Y pos: placeholder; camera AI will take it over
@@ -476,7 +591,14 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
}
var targetBearing = 0.0
var currentBearing = Double.NaN
var currentBearing = 0.0
var bearing1A = 0.0
var bearing1B = 0.0
var bearing1C = 0.0
var bearing2A = 0.0
var bearing2B = 0.0
var bearing3A = 0.0
override fun moveTo(bearing: Double) {
targetBearing = bearing
@@ -496,6 +618,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
val xdiff = if (xdiff1 < 0) xdiff2 else xdiff1
val ydiff = toY - hitbox.canonicalY
moveTo(atan2(ydiff, xdiff))
hitbox.setPositionX(hitbox.canonicalX % ww)
}

View File

@@ -37,8 +37,8 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
this.physProp = physProp
}
var vehicleRiding: Controllable? = null // usually player only
@Transient internal var vehicleRiding: Controllable? = null // usually player only
var vehicleRidingActorID: ActorID? = null
/** Must be set by PlayerFactory */

View File

@@ -14,11 +14,111 @@ import org.dyn4j.geometry.Vector2
import java.util.*
typealias BlockBoxIndex = Int
typealias WireEmissionType = String
interface Electric {
val wireEmitterTypes: HashMap<String, BlockBoxIndex>
val wireEmission: HashMap<BlockBoxIndex, Vector2>
val wireConsumption: HashMap<BlockBoxIndex, Vector2>
open class Electric : FixtureBase {
protected constructor() : super() {
oldSinkStatus = Array(blockBox.width * blockBox.height) { Vector2() }
}
/**
* Making the sprite: do not address the CommonResourcePool directly; just do it like this snippet:
*
* ```makeNewSprite(FixtureBase.getSpritesheet("basegame", "sprites/fixtures/tiki_torch.tga", 16, 32))```
*/
constructor(
blockBox0: BlockBox,
blockBoxProps: BlockBoxProps = BlockBoxProps(0),
renderOrder: RenderOrder = RenderOrder.MIDDLE,
nameFun: () -> String,
mainUI: UICanvas? = null,
inventory: FixtureInventory? = null,
id: ActorID? = null
) : super(renderOrder, PhysProperties.IMMOBILE, id) {
blockBox = blockBox0
setHitboxDimension(TILE_SIZE * blockBox.width, TILE_SIZE * blockBox.height, 0, 0)
this.blockBoxProps = blockBoxProps
this.renderOrder = renderOrder
this.nameFun = nameFun
this.mainUI = mainUI
this.inventory = inventory
if (mainUI != null)
App.disposables.add(mainUI)
oldSinkStatus = Array(blockBox.width * blockBox.height) { Vector2() }
}
companion object {
const val ELECTIC_THRESHOLD_HIGH = 0.9
const val ELECTRIC_THRESHOLD_LOW = 0.1
const val ELECTRIC_THRESHOLD_EDGE_DELTA = 0.7
}
fun getWireEmitterAt(point: Point2i) = this.wireEmitterTypes[pointToBlockBoxIndex(point)]
fun getWireEmitterAt(x: Int, y: Int) = this.wireEmitterTypes[pointToBlockBoxIndex(x, y)]
fun getWireSinkAt(point: Point2i) = this.wireSinkTypes[pointToBlockBoxIndex(point)]
fun getWireSinkAt(x: Int, y: Int) = this.wireSinkTypes[pointToBlockBoxIndex(x, y)]
fun setWireEmitterAt(x: Int, y: Int, type: WireEmissionType) { wireEmitterTypes[pointToBlockBoxIndex(x, y)] = type }
fun setWireSinkAt(x: Int, y: Int, type: WireEmissionType) { wireSinkTypes[pointToBlockBoxIndex(x, y)] = type }
fun setWireEmissionAt(x: Int, y: Int, emission: Vector2) { wireEmission[pointToBlockBoxIndex(x, y)] = emission }
fun setWireConsumptionAt(x: Int, y: Int, consumption: Vector2) { wireConsumption[pointToBlockBoxIndex(x, y)] = consumption }
@Transient val wireEmitterTypes: HashMap<BlockBoxIndex, WireEmissionType> = HashMap()
@Transient val wireSinkTypes: HashMap<BlockBoxIndex, WireEmissionType> = HashMap()
@Transient val wireEmission: HashMap<BlockBoxIndex, Vector2> = HashMap()
@Transient val wireConsumption: HashMap<BlockBoxIndex, Vector2> = HashMap()
/** Triggered when 'digital_bit' rises from low to high. Edge detection only considers the real component (labeled as 'x') of the vector */
open fun onRisingEdge(readFrom: BlockBoxIndex) {}
/** Triggered when 'digital_bit' rises from high to low. Edge detection only considers the real component (labeled as 'x') of the vector */
open fun onFallingEdge(readFrom: BlockBoxIndex) {}
/** Triggered when 'digital_bit' is held high. This function WILL NOT be triggered simultaneously with the rising edge. Level detection only considers the real component (labeled as 'x') of the vector */
open fun onSignalHigh(readFrom: BlockBoxIndex) {}
/** Triggered when 'digital_bit' is held low. This function WILL NOT be triggered simultaneously with the falling edge. Level detection only considers the real component (labeled as 'x') of the vector */
open fun onSignalLow(readFrom: BlockBoxIndex) {}
private val oldSinkStatus: Array<Vector2>
open fun updateOnWireGraphTraversal(offsetX: Int, offsetY: Int, sinkType: WireEmissionType) {
val index = pointToBlockBoxIndex(offsetX, offsetY)
val old = oldSinkStatus[index]
val wx = offsetX + intTilewiseHitbox.startX.toInt()
val wy = offsetY + intTilewiseHitbox.startY.toInt()
val new = WireCodex.getAllWiresThatAccepts("digital_bit").fold(Vector2()) { acc, (id, _) ->
INGAME.world.getWireEmitStateOf(wx, wy, id).let {
Vector2(acc.x + (it?.x ?: 0.0), acc.y + (it?.y ?: 0.0))
}
}
if (sinkType == "digital_bit") {
if (new.x - old.x >= ELECTRIC_THRESHOLD_EDGE_DELTA && new.x >= ELECTIC_THRESHOLD_HIGH)
onRisingEdge(index)
else if (old.x - new.x >= ELECTRIC_THRESHOLD_EDGE_DELTA && new.x <= ELECTRIC_THRESHOLD_LOW)
onFallingEdge(index)
else if (new.x >= ELECTIC_THRESHOLD_HIGH)
onSignalHigh(index)
else if (new.y <= ELECTRIC_THRESHOLD_LOW)
onSignalLow(index)
}
}
override fun update(delta: Float) {
super.update(delta)
oldSinkStatus.indices.forEach { index ->
val wx = (index % blockBox.width) + intTilewiseHitbox.startX.toInt()
val wy = (index / blockBox.width) + intTilewiseHitbox.startY.toInt()
val new = WireCodex.getAllWiresThatAccepts(getWireSinkAt(index % blockBox.width, index / blockBox.width) ?: "").fold(Vector2()) { acc, (id, _) ->
INGAME.world.getWireEmitStateOf(wx, wy, id).let {
Vector2(acc.x + (it?.x ?: 0.0), acc.y + (it?.y ?: 0.0))
}
}
oldSinkStatus[index].set(new)
}
}
}
/**
@@ -35,15 +135,20 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
protected set
lateinit var blockBox: BlockBox // something like TapestryObject will want to redefine this
fun blockBoxIndexToPoint2i(it: BlockBoxIndex): Point2i = this.blockBox.width.let { w -> Point2i(it % w, it / w) }
var blockBoxProps: BlockBoxProps = BlockBoxProps(0)
fun pointToBlockBoxIndex(point: Point2i) = point.y * this.blockBox.width + point.x
fun pointToBlockBoxIndex(x: Int, y: Int) = y * this.blockBox.width + x
@Transient var blockBoxProps: BlockBoxProps = BlockBoxProps(0)
@Transient var nameFun: () -> String = { "" }
@Transient var mainUI: UICanvas? = null
var inventory: FixtureInventory? = null
protected var actorThatInstalledThisFixture: UUID? = null
private constructor() : super(RenderOrder.BEHIND, PhysProperties.IMMOBILE, null)
protected constructor() : super(RenderOrder.BEHIND, PhysProperties.IMMOBILE, null)
protected constructor(renderOrder: RenderOrder, physProp: PhysProperties, id: ActorID?) : super(renderOrder, physProp, id)
/**
@@ -244,7 +349,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
if (canBeDespawned) {
printdbg(this, "despawn at T${INGAME.WORLD_UPDATE_TIMER}: ${nameFun()}")
printStackTrace(this)
// printStackTrace(this)
// remove filler block
forEachBlockbox { x, y, _, _ ->
@@ -312,7 +417,13 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
}
override fun flagDespawn() {
if (canBeDespawned) flagDespawn = true
if (canBeDespawned) {
printdbg(this, "Fixture at (${this.intTilewiseHitbox}) flagging despawn: ${this.javaClass.canonicalName}")
flagDespawn = true
}
else {
printdbg(this, "Fixture at (${this.intTilewiseHitbox}) CANNOT be despawned: ${this.javaClass.canonicalName}")
}
}
/**
@@ -351,10 +462,10 @@ interface CuedByWireChange {
* Standard 32-bit binary flags.
*
* (LSB)
* - 0: fluid resist - when FALSE, the fixture will break itself to item/nothing.
* For example, crops has this flag FALSE.
* - 1: don't drop item when broken - when TRUE, the fixture will simply disappear instead of
* dropping itself. For example, crop has this flag TRUE.
* - 0: fluid intolerance - when SET, the fixture will break itself to item/nothing (depends on the flag #1).
* For example, crops have this flag SET.
* - 1: no drops - when SET, the fixture will simply disappear instead of dropping itself.
* For example, crops have this flag SET.
*
* (MSB)
*

View File

@@ -1,9 +1,5 @@
package net.torvald.terrarum.modulebasegame.gameactors
import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.langpack.Lang
@@ -11,12 +7,7 @@ import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import org.dyn4j.geometry.Vector2
class FixtureLogicSignalEmitter : FixtureBase, Electric {
override val wireEmitterTypes: HashMap<String, BlockBoxIndex> = HashMap()
override val wireEmission: HashMap<BlockBoxIndex, Vector2> = HashMap()
override val wireConsumption: HashMap<BlockBoxIndex, Vector2> = HashMap()
class FixtureLogicSignalEmitter : Electric {
constructor() : super(
BlockBox(BlockBox.NO_COLLISION, 1, 1),
@@ -27,21 +18,16 @@ class FixtureLogicSignalEmitter : FixtureBase, Electric {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("basegame", "sprites/fixtures/signal_source.tga")
density = 1400.0
setHitboxDimension(TILE_SIZE, TILE_SIZE, 0, -1)
setHitboxDimension(TILE_SIZE, TILE_SIZE, 0, 1)
makeNewSprite(TextureRegionPack(itemImage.texture, TILE_SIZE, TILE_SIZE)).let {
it.setRowsAndFrames(1,1)
}
actorValue[AVKey.BASEMASS] = MASS
}
override fun update(delta: Float) {
// the values does not get preserved on save reload??
wireEmitterTypes["digital_bit"] = 0
wireEmission[0] = Vector2(1.0, 0.0)
super.update(delta)
setWireEmitterAt(0, 0, "digital_bit")
setWireEmissionAt(0, 0, Vector2(1.0, 0.0))
}
override fun dispose() { }

View File

@@ -23,7 +23,7 @@ internal class FixtureStorageChest : FixtureBase {
(mainUI as UIStorageChest).chestInventory = this.inventory!!
(mainUI as UIStorageChest).chestNameFun = this.nameFun
setHitboxDimension(TILE_SIZE, TILE_SIZE, 0, -1)
setHitboxDimension(TILE_SIZE, TILE_SIZE, 0, 1)
makeNewSprite(TextureRegionPack(CommonResourcePool.getAsTextureRegion("itemplaceholder_16").texture, 16, 16)).let {
it.setRowsAndFrames(1,1)

View File

@@ -126,7 +126,7 @@ open class FixtureSwingingDoorBase : FixtureBase {
(if (isOpacityActuallyLuminosity) lightBoxList else shadeBoxList)[0].light = opacity
// define physical size
setHitboxDimension(TILE_SIZE * tilewiseHitboxWidth, TILE_SIZE * tilewiseHitboxHeight, 0, 0)
setHitboxDimension(TILE_SIZE * tilewiseHitboxWidth, TILE_SIZE * tilewiseHitboxHeight, 0, 1)
blockBox = BlockBox(BlockBox.FULL_COLLISION, tilewiseHitboxWidth, tilewiseHitboxHeight)
doorHoldLength = hashMapOf(

View File

@@ -0,0 +1,124 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.random.XXHash64
import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.WireCodex
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.WorldgenLoadScreen
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory.Companion.CAPACITY_MODE_WEIGHT
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarum.modulebasegame.serialise.LoadSavegame
import net.torvald.terrarum.modulebasegame.serialise.ReadActor
import net.torvald.terrarum.modulebasegame.ui.UILoadGovernor
import net.torvald.terrarum.modulebasegame.ui.UIWorldPortal
import net.torvald.terrarum.savegame.ByteArray64Reader
import net.torvald.terrarum.savegame.DiskSkimmer
import net.torvald.terrarum.savegame.VDFileID
import net.torvald.terrarum.serialise.Common
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import org.dyn4j.geometry.Vector2
import java.util.HashMap
/**
* Created by minjaesong on 2023-05-28.
*/
class FixtureWorldPortal : Electric {
constructor() : super(
BlockBox(BlockBox.NO_COLLISION, 5, 2),
nameFun = { Lang["ITEM_WORLD_PORTAL"] },
mainUI = UIWorldPortal(),
// inventory = FixtureInventory(200, CAPACITY_MODE_WEIGHT)
) {
// TODO do something with (mainUI as UIWorldPortal).***
// (mainUI as UIWorldPortal).let { ui ->
// ui.transitionalCargo.chestInventory = this.inventory!!
// ui.transitionalCargo.chestNameFun = this.nameFun
// }
(mainUI as UIWorldPortal).host = this
}
init {
val itemImage = FixtureItemBase.getItemImageFromSheet("basegame", "sprites/fixtures/portal_device.tga", 80, 32)
density = 2900.0
setHitboxDimension(80, 32, 0, 0)
makeNewSprite(TextureRegionPack(itemImage.texture, 80, 32)).let {
it.setRowsAndFrames(1,1)
}
actorValue[AVKey.BASEMASS] = FixtureLogicSignalEmitter.MASS
setWireSinkAt(2, 1, "digital_bit")
}
@Transient internal var teleportRequest: TeleportRequest? = null
override fun update(delta: Float) {
super.update(delta)
}
override fun onRisingEdge(readFrom: BlockBoxIndex) {
printdbg(this, "teleport! $teleportRequest")
teleportRequest?.let {
if (it.worldDiskToLoad != null && it.worldLoadParam != null) {
throw InternalError("Contradiction -- worldDiskToLoad and worldLoadParam are both not null: $teleportRequest")
}
val player = INGAME.actorGamer
// load existing
val jobAfterSave: () -> Unit
if (it.worldDiskToLoad != null) {
UILoadGovernor.worldDisk = it.worldDiskToLoad
UILoadGovernor.playerDisk = App.savegamePlayers[player.uuid]!!.files[0]
jobAfterSave = {
UILoadGovernor.playerDisk!!.rebuild()
LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk!!)
}
}
// create new
else {
jobAfterSave = {
val wx = it.worldLoadParam!!.width
val wy = it.worldLoadParam!!.height
val seed = it.worldLoadParam!!.worldGenSeed
val name = it.worldLoadParam!!.savegameName
printdbg(this, "generate for teleportation! Size=${wx}x${wy}, Name=$name, Seed=$seed")
val ingame = TerrarumIngame(App.batch)
val worldParam = TerrarumIngame.NewGameParams(player, it.worldLoadParam)
ingame.gameLoadInfoPayload = worldParam
ingame.gameLoadMode = TerrarumIngame.GameLoadMode.CREATE_NEW
Terrarum.setCurrentIngameInstance(ingame)
val loadScreen = WorldgenLoadScreen(ingame, wx, wy)
App.setLoadScreen(loadScreen)
}
}
INGAME.requestForceSave(jobAfterSave)
teleportRequest = null
}
}
override fun reload() {
super.reload()
// TODO do something with (mainUI as UIWorldPortal).***
}
internal data class TeleportRequest(
val worldDiskToLoad: DiskSkimmer?, // for loading existing worlds
val worldLoadParam: TerrarumIngame.NewWorldParameters? // for creating new world
)
}

View File

@@ -16,7 +16,8 @@ import net.torvald.terrarum.itemproperties.Material
*/
open class HumanoidNPC : ActorHumanoid, AIControlled, CanBeAnItem {
override lateinit var ai: ActorAI
var aiName: String = ""
@Transient override lateinit var ai: ActorAI
companion object {
val DEFAULT_COLLISION_TYPE = COLLISION_DYNAMIC
@@ -26,6 +27,7 @@ open class HumanoidNPC : ActorHumanoid, AIControlled, CanBeAnItem {
constructor(ai: ActorAI, born: Long) : super(born) {
this.ai = ai
this.aiName = ai::class.java.canonicalName
}
init {

View File

@@ -25,7 +25,7 @@ class PhysTestBall : ActorWithBody(RenderOrder.MIDDLE, PhysProperties.PHYSICS_OB
}
override fun drawBody(batch: SpriteBatch) {
Terrarum.inShapeRenderer {
/*Terrarum.inShapeRenderer {
it.color = color
it.circle(
hitbox.startX.toFloat() - 1f,
@@ -44,7 +44,7 @@ class PhysTestBall : ActorWithBody(RenderOrder.MIDDLE, PhysProperties.PHYSICS_OB
hitbox.startY.toFloat() - 1f,
hitbox.width.toFloat()
)
}
}*/
//println(moveDelta)
}

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