mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-06 08:38:30 +09:00
Compare commits
58 Commits
v0.3.2-tes
...
v0.3.2-tes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
453459e3b6 | ||
|
|
bad72dd353 | ||
|
|
13185f0565 | ||
|
|
fcaf4c97f1 | ||
|
|
9c396e7b8d | ||
|
|
afb7dff5d2 | ||
|
|
5d0514040c | ||
|
|
7c1806946b | ||
|
|
e5e02681b8 | ||
|
|
6db3baf691 | ||
|
|
07cbcbe79b | ||
|
|
57a9f7febc | ||
|
|
16cfaaea27 | ||
|
|
72c742897e | ||
|
|
23af64deb4 | ||
|
|
bb017fa9b7 | ||
|
|
1745bb16db | ||
|
|
370583d1af | ||
|
|
66b651c627 | ||
|
|
c5874a7f3d | ||
|
|
057905c3b7 | ||
|
|
2b50562002 | ||
|
|
73a8198378 | ||
|
|
1ef479124e | ||
|
|
e5e8028b3f | ||
|
|
739b51af95 | ||
|
|
f9f49ab63c | ||
|
|
a497463349 | ||
|
|
253db56c4f | ||
|
|
3d13941060 | ||
|
|
592e489411 | ||
|
|
49b2011ea0 | ||
|
|
61e6255b52 | ||
|
|
2e956f89f5 | ||
|
|
e8ffd1f844 | ||
|
|
0882145f9c | ||
|
|
28e2179e44 | ||
|
|
48eb1ffd8f | ||
|
|
6daccb2e62 | ||
|
|
8c9d5a26fb | ||
|
|
ee3e5b14cd | ||
|
|
5c39df9080 | ||
|
|
5d77694316 | ||
|
|
cf111d2507 | ||
|
|
724ace3f00 | ||
|
|
1457cbffb3 | ||
|
|
7a42066392 | ||
|
|
528b975350 | ||
|
|
9e9064dd55 | ||
|
|
138c6d22d2 | ||
|
|
a33f0e7ab4 | ||
|
|
93c427473d | ||
|
|
6b8798a19e | ||
|
|
376595d7cd | ||
|
|
4cc52b5585 | ||
|
|
0ff71f39fe | ||
|
|
13f487a562 | ||
|
|
0599ce91b1 |
24
.idea/artifacts/TerrarumBuild.xml
generated
24
.idea/artifacts/TerrarumBuild.xml
generated
@@ -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>
|
||||
19
.idea/runConfigurations/Principii.xml
generated
Normal file
19
.idea/runConfigurations/Principii.xml
generated
Normal 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>
|
||||
@@ -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()
|
||||
|
||||
BIN
assets/graphics/gui/apple_rosetta_warning.tga
LFS
Normal file
BIN
assets/graphics/gui/apple_rosetta_warning.tga
LFS
Normal file
Binary file not shown.
@@ -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 you’re 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,11 +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_LABEL_PASTE_FROM_CLIPBOARD": "Paste from Clipboard",
|
||||
"MENU_OPTIONS_PERFORMANCE": "Performance",
|
||||
"MENU_LABEL_DELETE": "Delete"
|
||||
"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"
|
||||
}
|
||||
16
assets/locales/en/terrarum_sentences.json
Normal file
16
assets/locales/en/terrarum_sentences.json
Normal 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": "저장하지 않은 변동사항을 잃게 됩니다."
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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": "디버그 콘솔"
|
||||
}
|
||||
|
||||
15
assets/locales/koKR/terrarum_sentences.json
Normal file
15
assets/locales/koKR/terrarum_sentences.json
Normal 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": "선택된 세이브가 삭제됩니다."
|
||||
}
|
||||
BIN
assets/mods/basegame/gui/backdrop01.tga
LFS
Normal file
BIN
assets/mods/basegame/gui/backdrop01.tga
LFS
Normal file
Binary file not shown.
@@ -17,7 +17,7 @@
|
||||
"GAME_ACTION_CRAFT": "Craft",
|
||||
"GAME_CRAFTING": "Crafting",
|
||||
"GAME_CRAFTABLE_ITEMS": "Craftable Items",
|
||||
"CONTEXT_WORLD_SEARCH": "World Search",
|
||||
"CONTEXT_WORLD_LIST": "Worlds List",
|
||||
"MENU_LABEL_RENAME": "Rename"
|
||||
"MENU_LABEL_RENAME": "Rename",
|
||||
"GAME_ACTION_TELEPORT": "Teleport",
|
||||
"CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING": "This is a world currently playing."
|
||||
}
|
||||
@@ -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": "현재 플레이 중인 월드입니다."
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
cd /D "%~dp0"
|
||||
.\runtime-windows-x86\bin\java -Xms1G -Xmx6G -jar .\assets\TerrarumBuild.jar
|
||||
6
buildapp/terrarumwindows_x86/Terrarum.c
Normal file
6
buildapp/terrarumwindows_x86/Terrarum.c
Normal 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");
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: net.torvald.terrarum.App
|
||||
Main-Class: net.torvald.terrarum.Principii
|
||||
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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?
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
334
src/net/torvald/terrarum/Principii.java
Normal file
334
src/net/torvald/terrarum/Principii.java
Normal 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()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
184
src/net/torvald/terrarum/SavegameCollection.kt
Normal file
184
src/net/torvald/terrarum/SavegameCollection.kt
Normal 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)
|
||||
@@ -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
|
||||
@@ -628,6 +630,7 @@ fun Float.sqrt() = FastMath.sqrt(this)
|
||||
fun Int.abs() = this.absoluteValue
|
||||
fun Double.bipolarClamp(limit: Double) = this.coerceIn(-limit, limit)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>?,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
/**
|
||||
@@ -260,7 +265,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
}
|
||||
|
||||
IngameRenderer.setRenderedWorld(world)
|
||||
|
||||
blockMarkingActor.isVisible = true
|
||||
|
||||
super.show() // this function sets gameInitialised = true
|
||||
}
|
||||
@@ -418,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()
|
||||
}
|
||||
@@ -470,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)
|
||||
@@ -521,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
|
||||
@@ -715,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
|
||||
}
|
||||
|
||||
@@ -851,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 {
|
||||
@@ -867,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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -901,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
|
||||
@@ -1107,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
|
||||
|
||||
@@ -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,49 +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
|
||||
@@ -122,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() }
|
||||
}
|
||||
|
||||
@@ -138,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) {
|
||||
@@ -152,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
|
||||
@@ -237,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()
|
||||
@@ -253,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
|
||||
@@ -268,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())
|
||||
|
||||
@@ -290,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")
|
||||
@@ -332,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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,6 +494,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
uiRemoCon.dispose()
|
||||
demoWorld.dispose()
|
||||
warning32bitJavaIcon.texture.dispose()
|
||||
warningAppleRosettaIcon.texture.dispose()
|
||||
}
|
||||
|
||||
override fun inputStrobed(e: TerrarumKeyboardEvent) {
|
||||
@@ -435,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
|
||||
@@ -475,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
|
||||
@@ -495,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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
/**
|
||||
@@ -357,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)
|
||||
*
|
||||
|
||||
@@ -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),
|
||||
@@ -34,14 +25,9 @@ class FixtureLogicSignalEmitter : FixtureBase, Electric {
|
||||
}
|
||||
|
||||
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() { }
|
||||
|
||||
@@ -1,22 +1,47 @@
|
||||
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 : FixtureBase {
|
||||
class FixtureWorldPortal : Electric {
|
||||
|
||||
constructor() : super(
|
||||
BlockBox(BlockBox.NO_COLLISION, 5, 2),
|
||||
nameFun = { Lang["ITEM_WORLD_PORTAL"] },
|
||||
mainUI = UIWorldPortal()
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +55,60 @@ class FixtureWorldPortal : FixtureBase {
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -37,4 +116,9 @@ class FixtureWorldPortal : FixtureBase {
|
||||
|
||||
// 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
|
||||
)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ open class FixtureItemBase(originalID: ItemID, val fixtureClassName: String) : G
|
||||
|
||||
(INGAME as TerrarumIngame).blockMarkingActor.let {
|
||||
it.setGhost(ghostItem.get())
|
||||
it.isVisible = true
|
||||
it.update(delta)
|
||||
it.setGhostColourBlock()
|
||||
mouseInInteractableRange(actor) { it.setGhostColourAllow(); 0L }
|
||||
@@ -80,7 +79,6 @@ open class FixtureItemBase(originalID: ItemID, val fixtureClassName: String) : G
|
||||
|
||||
(INGAME as TerrarumIngame).blockMarkingActor.let {
|
||||
it.unsetGhost()
|
||||
it.isVisible = false
|
||||
it.setGhostColourNone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class WireGraphDebugger(originalID: ItemID) : GameItem(originalID) {
|
||||
val wireName = WireCodex[itemID].nameKey
|
||||
|
||||
val emit = simCell.emt
|
||||
val recv = simCell.rcv
|
||||
val recv = simCell.rcp
|
||||
|
||||
sb.append("$connexionIcon $wireName")
|
||||
sb.append("\nE: $emit")
|
||||
|
||||
@@ -1,35 +1,5 @@
|
||||
package net.torvald.terrarum.modulebasegame.serialise
|
||||
|
||||
import net.torvald.gdx.graphics.PixmapIO2
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.ItemCodex
|
||||
import net.torvald.terrarum.ModMgr
|
||||
import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM
|
||||
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.toInt
|
||||
import net.torvald.terrarum.savegame.*
|
||||
import net.torvald.terrarum.savegame.VDFileID.LOADORDER
|
||||
import net.torvald.terrarum.savegame.VDFileID.ROOT
|
||||
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
||||
import net.torvald.terrarum.savegame.VDFileID.THUMBNAIL
|
||||
import net.torvald.terrarum.serialise.Common
|
||||
import java.io.File
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
/**
|
||||
* Will happily overwrite existing entry
|
||||
*/
|
||||
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
|
||||
disk.entries[file.entryID] = file
|
||||
file.parentEntryID = 0
|
||||
val dir = VDUtil.getAsDirectory(disk, 0)
|
||||
if (!dir.contains(file.entryID)) dir.add(file.entryID)
|
||||
}
|
||||
|
||||
abstract class SavingThread(private val errorHandler: (Throwable) -> Unit) : Runnable {
|
||||
abstract fun save()
|
||||
@@ -45,196 +15,4 @@ abstract class SavingThread(private val errorHandler: (Throwable) -> Unit) : Run
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2021-09-14.
|
||||
*/
|
||||
class WorldSavingThread(
|
||||
val time_t: Long,
|
||||
val disk: VirtualDisk,
|
||||
val outFile: File,
|
||||
val ingame: TerrarumIngame,
|
||||
val hasThumbnail: Boolean,
|
||||
val isAuto: Boolean,
|
||||
val callback: () -> Unit,
|
||||
val errorHandler: (Throwable) -> Unit
|
||||
) : SavingThread(errorHandler) {
|
||||
|
||||
override fun save() {
|
||||
|
||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||
disk.saveKind = VDSaveKind.WORLD_DATA
|
||||
|
||||
if (hasThumbnail) {
|
||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
}
|
||||
|
||||
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
||||
|
||||
val playersList: List<IngamePlayer> = allTheActors.filterIsInstance<IngamePlayer>()
|
||||
val actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) }
|
||||
val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) }
|
||||
val cw = ingame.world.width / LandUtil.CHUNK_W
|
||||
val ch = ingame.world.height / LandUtil.CHUNK_H
|
||||
|
||||
WriteSavegame.saveProgress = 0f
|
||||
WriteSavegame.saveProgressMax = 3f + (cw * ch * layers.size) + actorsList.size
|
||||
|
||||
|
||||
val tgaout = ByteArray64GrowableOutputStream()
|
||||
val gzout = GZIPOutputStream(tgaout)
|
||||
|
||||
printdbg(this, "Writing metadata...")
|
||||
|
||||
val creation_t = ingame.world.creationTime
|
||||
|
||||
|
||||
// Write subset of Ingame.ItemCodex
|
||||
// The existing ItemCodex must be rewritten to clear out obsolete records
|
||||
|
||||
// We're assuming the dynamic item generated by players does exist in the world, and it's recorded
|
||||
// into the world's dynamicToStaticTable, therefore every item recorded into the world's dynamicToStaticTable
|
||||
// can be found in this world without need to look up the players
|
||||
ingame.world.dynamicToStaticTable.clear()
|
||||
ingame.world.dynamicItemInventory.clear()
|
||||
actorsList.filterIsInstance<Pocketed>().forEach { actor ->
|
||||
actor.inventory.forEach { (itemid, _) ->
|
||||
|
||||
printdbg(this, "World side dynamicitem: $itemid contained in $actor")
|
||||
|
||||
if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) {
|
||||
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
|
||||
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
|
||||
}
|
||||
}
|
||||
}
|
||||
actorsList.filterIsInstance<FixtureBase>().forEach { fixture ->
|
||||
fixture.inventory?.forEach { (itemid, _) ->
|
||||
|
||||
printdbg(this, "World side dynamicitem: $itemid contained in $fixture")
|
||||
|
||||
if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) {
|
||||
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
|
||||
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (hasThumbnail) {
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
}
|
||||
|
||||
WriteSavegame.saveProgress += 1f
|
||||
|
||||
// Write World //
|
||||
|
||||
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList))
|
||||
val world = DiskEntry(SAVEGAMEINFO, ROOT, creation_t, time_t, worldMeta)
|
||||
addFile(disk, world)
|
||||
|
||||
WriteSavegame.saveProgress += 1f
|
||||
|
||||
|
||||
for (layer in layers.indices) {
|
||||
for (cx in 0 until cw) {
|
||||
for (cy in 0 until ch) {
|
||||
val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong()
|
||||
|
||||
// Echo("Writing chunks... ${(cw*ch*layer) + chunkNumber + 1}/${cw*ch*layers.size}")
|
||||
|
||||
val chunkBytes = WriteWorld.encodeChunk(layers[layer]!!, cx, cy)
|
||||
val entryID = 0x1_0000_0000L or layer.toLong().shl(24) or chunkNumber
|
||||
|
||||
val entryContent = EntryFile(chunkBytes)
|
||||
val entry = DiskEntry(entryID, ROOT, creation_t, time_t, entryContent)
|
||||
// "W1L0-92,15"
|
||||
addFile(disk, entry)
|
||||
|
||||
WriteSavegame.saveProgress += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write Actors //
|
||||
actorsList.forEachIndexed { count, it ->
|
||||
// Echo("Writing actors... ${count+1}/${actorsList.size}")
|
||||
|
||||
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
|
||||
val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent)
|
||||
addFile(disk, actor)
|
||||
|
||||
WriteSavegame.saveProgress += 1
|
||||
}
|
||||
|
||||
|
||||
// write loadorder //
|
||||
val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET)
|
||||
loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n"))
|
||||
loadOrderBa64Writer.flush(); loadOrderBa64Writer.close()
|
||||
val loadOrderText = loadOrderBa64Writer.toByteArray64()
|
||||
val loadOrderContents = EntryFile(loadOrderText)
|
||||
addFile(disk, DiskEntry(LOADORDER, ROOT, creation_t, time_t, loadOrderContents))
|
||||
|
||||
|
||||
|
||||
// Echo("Writing file to disk...")
|
||||
|
||||
disk.entries[0]!!.modificationDate = time_t
|
||||
// entry zero MUST NOT be used to get lastPlayDate, but we'll update it anyway
|
||||
// use entry -1 for that purpose!
|
||||
disk.capacity = 0
|
||||
VDUtil.dumpToRealMachine(disk, outFile)
|
||||
|
||||
|
||||
|
||||
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
||||
|
||||
|
||||
if (hasThumbnail) IngameRenderer.fboRGBexportedLatch = false
|
||||
WriteSavegame.savingStatus = 255
|
||||
|
||||
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function called means the "Avatar" was not externally created and thus has no sprite-bodypart-name-to-entry-number-map
|
||||
*
|
||||
* Created by minjaesong on 2021-10-08
|
||||
*/
|
||||
class PlayerSavingThread(
|
||||
val time_t: Long,
|
||||
val disk: VirtualDisk,
|
||||
val outFile: File,
|
||||
val ingame: TerrarumIngame,
|
||||
val hasThumbnail: Boolean,
|
||||
val isAuto: Boolean,
|
||||
val callback: () -> Unit,
|
||||
val errorHandler: (Throwable) -> Unit
|
||||
) : SavingThread(errorHandler) {
|
||||
|
||||
override fun save() {
|
||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||
disk.saveKind = VDSaveKind.PLAYER_DATA
|
||||
disk.capacity = 0L
|
||||
|
||||
WriteSavegame.saveProgress = 0f
|
||||
|
||||
printdbg(this, "Writing The Player...")
|
||||
WritePlayer(ingame.actorGamer, disk, ingame, time_t)
|
||||
disk.entries[0]!!.modificationDate = time_t
|
||||
VDUtil.dumpToRealMachine(disk, outFile)
|
||||
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const val SCREENCAP_WAIT_TRY_MAX = 256
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package net.torvald.terrarum.modulebasegame.serialise
|
||||
|
||||
import net.torvald.gdx.graphics.PixmapIO2
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.savegame.*
|
||||
import net.torvald.terrarum.toInt
|
||||
import java.io.File
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
/**
|
||||
* This function called means the "Avatar" was not externally created and thus has no sprite-bodypart-name-to-entry-number-map
|
||||
*
|
||||
* Created by minjaesong on 2021-10-08
|
||||
*/
|
||||
class PlayerSavingThread(
|
||||
val time_t: Long,
|
||||
val disk: VirtualDisk,
|
||||
val outFile: File,
|
||||
val ingame: TerrarumIngame,
|
||||
val isAuto: Boolean,
|
||||
val callback: () -> Unit,
|
||||
val errorHandler: (Throwable) -> Unit
|
||||
) : SavingThread(errorHandler) {
|
||||
/**
|
||||
* Will happily overwrite existing entry
|
||||
*/
|
||||
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
|
||||
disk.entries[file.entryID] = file
|
||||
file.parentEntryID = 0
|
||||
val dir = VDUtil.getAsDirectory(disk, 0)
|
||||
if (!dir.contains(file.entryID)) dir.add(file.entryID)
|
||||
}
|
||||
|
||||
|
||||
override fun save() {
|
||||
App.printdbg(this, "outFile: ${outFile.path}")
|
||||
|
||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||
disk.saveKind = VDSaveKind.PLAYER_DATA
|
||||
disk.capacity = 0L
|
||||
|
||||
WriteSavegame.saveProgress = 0f
|
||||
|
||||
// wait for screencap
|
||||
var emergencyStopCnt = 0
|
||||
while (IngameRenderer.screencapBusy) {
|
||||
// printdbg(this, "spinning for screencap to be taken")
|
||||
Thread.sleep(4L)
|
||||
emergencyStopCnt += 1
|
||||
if (emergencyStopCnt >= SCREENCAP_WAIT_TRY_MAX) throw InterruptedException("Waiting screencap to be taken for too long")
|
||||
}
|
||||
|
||||
// write screencap
|
||||
val tgaout = ByteArray64GrowableOutputStream()
|
||||
val gzout = GZIPOutputStream(tgaout)
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
// App.disposables.add(IngameRenderer.fboRGBexport)
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb =
|
||||
DiskEntry(VDFileID.PLAYER_SCREENSHOT, VDFileID.ROOT, ingame.world.creationTime, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
|
||||
|
||||
|
||||
App.printdbg(this, "Writing The Player...")
|
||||
WritePlayer(ingame.actorGamer, disk, ingame, time_t)
|
||||
disk.entries[0]!!.modificationDate = time_t
|
||||
VDUtil.dumpToRealMachine(disk, outFile)
|
||||
|
||||
|
||||
// IngameRenderer.screencapBusy = false
|
||||
|
||||
callback()
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ class QuickSingleplayerWorldSavingThread(
|
||||
val disk: VirtualDisk,
|
||||
val outFile: File,
|
||||
val ingame: TerrarumIngame,
|
||||
val hasThumbnail: Boolean,
|
||||
val isAuto: Boolean,
|
||||
val callback: () -> Unit,
|
||||
val errorHandler: (Throwable) -> Unit
|
||||
@@ -46,12 +45,17 @@ class QuickSingleplayerWorldSavingThread(
|
||||
|
||||
|
||||
override fun save() {
|
||||
printdbg(this, "outFile: ${outFile.path}")
|
||||
|
||||
val skimmer = DiskSkimmer(outFile)
|
||||
|
||||
if (hasThumbnail) {
|
||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
||||
Thread.sleep(1L)
|
||||
}
|
||||
// wait for screencap
|
||||
var emergencyStopCnt = 0
|
||||
while (IngameRenderer.screencapBusy) {
|
||||
// printdbg(this, "spinning for screencap to be taken")
|
||||
Thread.sleep(4L)
|
||||
emergencyStopCnt += 1
|
||||
if (emergencyStopCnt >= SCREENCAP_WAIT_TRY_MAX) throw InterruptedException("Waiting screencap to be taken for too long")
|
||||
}
|
||||
|
||||
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
||||
@@ -76,14 +80,14 @@ class QuickSingleplayerWorldSavingThread(
|
||||
val creation_t = ingame.world.creationTime
|
||||
|
||||
|
||||
if (hasThumbnail) {
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
|
||||
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
}
|
||||
|
||||
WriteSavegame.saveProgress += 1f
|
||||
|
||||
@@ -150,7 +154,7 @@ class QuickSingleplayerWorldSavingThread(
|
||||
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
||||
|
||||
|
||||
if (hasThumbnail) IngameRenderer.fboRGBexportedLatch = false
|
||||
// IngameRenderer.screencapBusy = false
|
||||
WriteSavegame.savingStatus = 255
|
||||
ingame.clearModifiedChunks()
|
||||
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
package net.torvald.terrarum.modulebasegame.serialise
|
||||
|
||||
import net.torvald.gdx.graphics.PixmapIO2
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
|
||||
import net.torvald.terrarum.realestate.LandUtil
|
||||
import net.torvald.terrarum.savegame.*
|
||||
import net.torvald.terrarum.serialise.Common
|
||||
import java.io.File
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2021-09-14.
|
||||
*/
|
||||
class WorldSavingThread(
|
||||
val time_t: Long,
|
||||
val disk: VirtualDisk,
|
||||
val outFile: File,
|
||||
val ingame: TerrarumIngame,
|
||||
val isAuto: Boolean,
|
||||
val callback: () -> Unit,
|
||||
val errorHandler: (Throwable) -> Unit
|
||||
) : SavingThread(errorHandler) {
|
||||
/**
|
||||
* Will happily overwrite existing entry
|
||||
*/
|
||||
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
|
||||
disk.entries[file.entryID] = file
|
||||
file.parentEntryID = 0
|
||||
val dir = VDUtil.getAsDirectory(disk, 0)
|
||||
if (!dir.contains(file.entryID)) dir.add(file.entryID)
|
||||
}
|
||||
|
||||
|
||||
override fun save() {
|
||||
App.printdbg(this, "outFile: ${outFile.path}")
|
||||
|
||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||
disk.saveKind = VDSaveKind.WORLD_DATA
|
||||
|
||||
// wait for screencap
|
||||
var emergencyStopCnt = 0
|
||||
while (IngameRenderer.screencapBusy) {
|
||||
// printdbg(this, "spinning for screencap to be taken")
|
||||
Thread.sleep(4L)
|
||||
emergencyStopCnt += 1
|
||||
if (emergencyStopCnt >= SCREENCAP_WAIT_TRY_MAX) throw InterruptedException("Waiting screencap to be taken for too long")
|
||||
}
|
||||
|
||||
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
||||
|
||||
val playersList: List<IngamePlayer> = allTheActors.filterIsInstance<IngamePlayer>()
|
||||
val actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) }
|
||||
val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) }
|
||||
val cw = ingame.world.width / LandUtil.CHUNK_W
|
||||
val ch = ingame.world.height / LandUtil.CHUNK_H
|
||||
|
||||
WriteSavegame.saveProgress = 0f
|
||||
WriteSavegame.saveProgressMax = 3f + (cw * ch * layers.size) + actorsList.size
|
||||
|
||||
|
||||
val tgaout = ByteArray64GrowableOutputStream()
|
||||
val gzout = GZIPOutputStream(tgaout)
|
||||
|
||||
App.printdbg(this, "Writing metadata...")
|
||||
|
||||
val creation_t = ingame.world.creationTime
|
||||
|
||||
|
||||
// Write subset of Ingame.ItemCodex
|
||||
// The existing ItemCodex must be rewritten to clear out obsolete records
|
||||
|
||||
// We're assuming the dynamic item generated by players does exist in the world, and it's recorded
|
||||
// into the world's dynamicToStaticTable, therefore every item recorded into the world's dynamicToStaticTable
|
||||
// can be found in this world without need to look up the players
|
||||
ingame.world.dynamicToStaticTable.clear()
|
||||
ingame.world.dynamicItemInventory.clear()
|
||||
actorsList.filterIsInstance<Pocketed>().forEach { actor ->
|
||||
actor.inventory.forEach { (itemid, _) ->
|
||||
|
||||
App.printdbg(this, "World side dynamicitem: $itemid contained in $actor")
|
||||
|
||||
if (itemid.startsWith("${ReferencingRanges.PREFIX_DYNAMICITEM}:")) {
|
||||
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
|
||||
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
|
||||
}
|
||||
}
|
||||
}
|
||||
actorsList.filterIsInstance<FixtureBase>().forEach { fixture ->
|
||||
fixture.inventory?.forEach { (itemid, _) ->
|
||||
|
||||
App.printdbg(this, "World side dynamicitem: $itemid contained in $fixture")
|
||||
|
||||
if (itemid.startsWith("${ReferencingRanges.PREFIX_DYNAMICITEM}:")) {
|
||||
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
|
||||
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||
IngameRenderer.fboRGBexport.dispose()
|
||||
|
||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||
val thumb = DiskEntry(VDFileID.THUMBNAIL, VDFileID.ROOT, creation_t, time_t, thumbContent)
|
||||
addFile(disk, thumb)
|
||||
|
||||
|
||||
|
||||
WriteSavegame.saveProgress += 1f
|
||||
|
||||
// Write World //
|
||||
|
||||
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList))
|
||||
val world = DiskEntry(VDFileID.SAVEGAMEINFO, VDFileID.ROOT, creation_t, time_t, worldMeta)
|
||||
addFile(disk, world)
|
||||
|
||||
WriteSavegame.saveProgress += 1f
|
||||
|
||||
|
||||
for (layer in layers.indices) {
|
||||
for (cx in 0 until cw) {
|
||||
for (cy in 0 until ch) {
|
||||
val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong()
|
||||
|
||||
// Echo("Writing chunks... ${(cw*ch*layer) + chunkNumber + 1}/${cw*ch*layers.size}")
|
||||
|
||||
val chunkBytes = WriteWorld.encodeChunk(layers[layer]!!, cx, cy)
|
||||
val entryID = 0x1_0000_0000L or layer.toLong().shl(24) or chunkNumber
|
||||
|
||||
val entryContent = EntryFile(chunkBytes)
|
||||
val entry = DiskEntry(entryID, VDFileID.ROOT, creation_t, time_t, entryContent)
|
||||
// "W1L0-92,15"
|
||||
addFile(disk, entry)
|
||||
|
||||
WriteSavegame.saveProgress += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write Actors //
|
||||
actorsList.forEachIndexed { count, it ->
|
||||
// Echo("Writing actors... ${count+1}/${actorsList.size}")
|
||||
|
||||
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
|
||||
val actor = DiskEntry(it.referenceID.toLong(), VDFileID.ROOT, creation_t, time_t, actorContent)
|
||||
addFile(disk, actor)
|
||||
|
||||
WriteSavegame.saveProgress += 1
|
||||
}
|
||||
|
||||
|
||||
// write loadorder //
|
||||
val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET)
|
||||
loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n"))
|
||||
loadOrderBa64Writer.flush(); loadOrderBa64Writer.close()
|
||||
val loadOrderText = loadOrderBa64Writer.toByteArray64()
|
||||
val loadOrderContents = EntryFile(loadOrderText)
|
||||
addFile(disk, DiskEntry(VDFileID.LOADORDER, VDFileID.ROOT, creation_t, time_t, loadOrderContents))
|
||||
|
||||
|
||||
|
||||
// Echo("Writing file to disk...")
|
||||
|
||||
disk.entries[0]!!.modificationDate = time_t
|
||||
// entry zero MUST NOT be used to get lastPlayDate, but we'll update it anyway
|
||||
// use entry -1 for that purpose!
|
||||
disk.capacity = 0
|
||||
VDUtil.dumpToRealMachine(disk, outFile)
|
||||
|
||||
|
||||
|
||||
App.printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
||||
|
||||
|
||||
// IngameRenderer.screencapBusy = false
|
||||
WriteSavegame.savingStatus = 255
|
||||
|
||||
|
||||
callback()
|
||||
}
|
||||
}
|
||||
@@ -151,8 +151,16 @@ object ReadPlayer {
|
||||
object ReadActor {
|
||||
|
||||
operator fun invoke(disk: SimpleFileSystem, dataStream: Reader): Actor =
|
||||
Common.jsoner.fromJson<Actor?>(null, dataStream).also {
|
||||
fillInDetails(disk, it)
|
||||
try {
|
||||
Common.jsoner.fromJson<Actor?>(null, dataStream).also {
|
||||
fillInDetails(disk, it)
|
||||
}
|
||||
}
|
||||
catch (e: ClassCastException) {
|
||||
System.err.println(e.message)
|
||||
System.err.println("The JSON:")
|
||||
System.err.println(dataStream.readText())
|
||||
throw e
|
||||
}
|
||||
|
||||
private fun fillInDetails(disk: SimpleFileSystem, actor: Actor) {
|
||||
|
||||
@@ -36,43 +36,45 @@ object WriteSavegame {
|
||||
@Volatile var saveProgress = 0f
|
||||
@Volatile var saveProgressMax = 1f
|
||||
|
||||
private fun getSaveThread(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, hasThumbnail: Boolean, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) = when (mode) {
|
||||
SaveMode.WORLD -> WorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler)
|
||||
SaveMode.PLAYER -> PlayerSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler)
|
||||
SaveMode.QUICK_WORLD -> QuickSingleplayerWorldSavingThread(time_t, disk, outFile, ingame, hasThumbnail, isAuto, callback, errorHandler)
|
||||
private fun getSaveThread(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) = when (mode) {
|
||||
SaveMode.WORLD -> WorldSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler)
|
||||
SaveMode.PLAYER -> PlayerSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler)
|
||||
SaveMode.QUICK_WORLD -> QuickSingleplayerWorldSavingThread(time_t, disk, outFile, ingame, isAuto, callback, errorHandler)
|
||||
else -> throw IllegalArgumentException("$mode")
|
||||
}
|
||||
|
||||
private fun installScreencap() {
|
||||
IngameRenderer.screencapExportCallback = { fb ->
|
||||
printdbg(this, "Generating thumbnail...")
|
||||
|
||||
val w = 960
|
||||
val h = 640
|
||||
|
||||
val cx = /*1-*/(WorldCamera.x % 2)
|
||||
val cy = /*1-*/(WorldCamera.y % 2)
|
||||
|
||||
val x = (fb.width - w) / 2 - cx // force the even-numbered position
|
||||
val y = (fb.height - h) / 2 - cy // force the even-numbered position
|
||||
|
||||
val p = Pixmap.createFromFrameBuffer(x, y, w, h)
|
||||
IngameRenderer.fboRGBexport = p
|
||||
//PixmapIO2._writeTGA(gzout, p, true, true)
|
||||
//p.dispose()
|
||||
|
||||
printdbg(this, "Done thumbnail generation")
|
||||
}
|
||||
}
|
||||
|
||||
operator fun invoke(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) {
|
||||
savingStatus = 0
|
||||
val hasThumbnail = (mode == SaveMode.WORLD || mode == SaveMode.QUICK_WORLD)
|
||||
printdbg(this, "Save queued")
|
||||
|
||||
if (hasThumbnail) {
|
||||
IngameRenderer.screencapExportCallback = { fb ->
|
||||
printdbg(this, "Generating thumbnail...")
|
||||
installScreencap()
|
||||
try { printdbg(this, "ScreencapExport installed: ${IngameRenderer.screencapExportCallback}") }
|
||||
catch (e: UninitializedPropertyAccessException) { printdbg(this, "ScreencapExport installed: no") }
|
||||
IngameRenderer.requestScreencap()
|
||||
|
||||
val w = 960
|
||||
val h = 640
|
||||
|
||||
val cx = /*1-*/(WorldCamera.x % 2)
|
||||
val cy = /*1-*/(WorldCamera.y % 2)
|
||||
|
||||
val x = (fb.width - w) / 2 - cx // force the even-numbered position
|
||||
val y = (fb.height - h) / 2 - cy // force the even-numbered position
|
||||
|
||||
val p = Pixmap.createFromFrameBuffer(x, y, w, h)
|
||||
IngameRenderer.fboRGBexport = p
|
||||
//PixmapIO2._writeTGA(gzout, p, true, true)
|
||||
//p.dispose()
|
||||
IngameRenderer.fboRGBexportedLatch = true
|
||||
|
||||
printdbg(this, "Done thumbnail generation")
|
||||
}
|
||||
IngameRenderer.screencapRequested = true
|
||||
}
|
||||
|
||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, hasThumbnail, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||
savingThread.start()
|
||||
|
||||
// it is caller's job to keep the game paused or keep a "save in progress" ui up
|
||||
@@ -81,12 +83,15 @@ object WriteSavegame {
|
||||
|
||||
|
||||
fun immediate(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) {
|
||||
|
||||
savingStatus = 0
|
||||
|
||||
printdbg(this, "Immediate save fired")
|
||||
|
||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, false, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||
installScreencap()
|
||||
try { printdbg(this, "ScreencapExport installed: ${IngameRenderer.screencapExportCallback}") }
|
||||
catch (e: UninitializedPropertyAccessException) { printdbg(this, "ScreencapExport installed: no") }
|
||||
IngameRenderer.requestScreencap()
|
||||
|
||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||
savingThread.start()
|
||||
|
||||
// it is caller's job to keep the game paused or keep a "save in progress" ui up
|
||||
@@ -126,7 +131,7 @@ object LoadSavegame {
|
||||
printdbg(this, "Player localhash: ${player.localHashStr}, hasSprite: ${player.sprite != null}")
|
||||
|
||||
val currentWorldId = player.worldCurrentlyPlaying
|
||||
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!
|
||||
val worldDisk = worldDisk0 ?: App.savegameWorlds[currentWorldId]!!.loadable()
|
||||
val world = ReadWorld(ByteArray64Reader(worldDisk.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET), worldDisk.diskFile)
|
||||
|
||||
world.layerTerrain = BlockLayer(world.width, world.height)
|
||||
|
||||
@@ -48,12 +48,9 @@ class Notification : UICanvas() {
|
||||
}
|
||||
}
|
||||
|
||||
private val drawColor = Color(1f, 1f, 1f, 1f)
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
blendNormalStraightAlpha(batch)
|
||||
drawColor.a = handler.opacity
|
||||
fontCol.a = handler.opacity
|
||||
fontCol.a = handler.opacity * OPACITY
|
||||
|
||||
val realTextWidth = 12 + if (message.size == 1)
|
||||
App.fontGame.getWidth(message[0])
|
||||
@@ -63,12 +60,11 @@ class Notification : UICanvas() {
|
||||
|
||||
// force the UI to the centre of the screen
|
||||
this.posX = (App.scr.width - displayedTextWidth) / 2
|
||||
|
||||
val textHeight = message.size * App.fontGame.lineHeight
|
||||
|
||||
batch.color = drawColor
|
||||
|
||||
Toolkit.drawBaloon(batch, 0f, -textHeight, displayedTextWidth.toFloat(), textHeight)
|
||||
|
||||
Toolkit.drawBaloon(batch, 0f, -textHeight, displayedTextWidth.toFloat(), textHeight, handler.opacity * OPACITY)
|
||||
|
||||
batch.color = fontCol
|
||||
message.forEachIndexed { index, s ->
|
||||
@@ -79,7 +75,6 @@ class Notification : UICanvas() {
|
||||
|
||||
|
||||
// dunno why, it doesn't work without this.
|
||||
drawColor.a = 1f
|
||||
fontCol.a = 1f
|
||||
}
|
||||
|
||||
@@ -111,6 +106,7 @@ class Notification : UICanvas() {
|
||||
|
||||
companion object {
|
||||
// private int messagesShowingIndex = 0;
|
||||
val OPEN_CLOSE_TIME = 0.16f
|
||||
const val OPEN_CLOSE_TIME = 0.16f
|
||||
const val OPACITY = 0.9f
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class UIBuildingMakerBlockChooser(val parent: BuildingMaker): UICanvas() {
|
||||
activeCol = Color.WHITE
|
||||
)
|
||||
|
||||
paletteItem.clickOnceListener = { _, _, _ ->
|
||||
paletteItem.clickOnceListener = { _, _ ->
|
||||
parent.setPencilColour(prop.id)
|
||||
}
|
||||
|
||||
|
||||
@@ -100,11 +100,9 @@ class UIBuildingMakerPenMenu(val parent: BuildingMaker): UICanvas() {
|
||||
toolButtons.forEachIndexed { index, button ->
|
||||
uiItems.add(button)
|
||||
|
||||
button.clickOnceListener = { _, _, b ->
|
||||
if (b == App.getConfigInt("config_mouseprimary")) {
|
||||
toolButtonsJob[index].invoke()
|
||||
closeGracefully()
|
||||
}
|
||||
button.clickOnceListener = { _, _ ->
|
||||
toolButtonsJob[index].invoke()
|
||||
closeGracefully()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import net.torvald.terrarum.itemproperties.CraftingCodex
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.InventoryPair
|
||||
import net.torvald.terrarum.modulebasegame.ui.*
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIItemInventoryItemGrid.Companion.listGap
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
@@ -203,8 +202,8 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
|
||||
)
|
||||
|
||||
// make sure grid buttons for ingredients do nothing (even if they are hidden!)
|
||||
itemListIngredients.gridModeButtons[0].touchDownListener = { _,_,_,_ -> }
|
||||
itemListIngredients.gridModeButtons[1].touchDownListener = { _,_,_,_ -> }
|
||||
itemListIngredients.navRemoCon.listButtonListener = { _,_, -> }
|
||||
itemListIngredients.navRemoCon.gridButtonListener = { _,_, -> }
|
||||
itemListIngredients.isCompactMode = true
|
||||
itemListIngredients.setCustomHighlightRuleSub {
|
||||
it.item?.let { ingredient ->
|
||||
@@ -252,8 +251,8 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
|
||||
} } }
|
||||
)
|
||||
// make grid mode buttons work together
|
||||
// itemListPlayer.gridModeButtons[0].touchDownListener = { _,_,_,_ -> setCompact(false) }
|
||||
// itemListPlayer.gridModeButtons[1].touchDownListener = { _,_,_,_ -> setCompact(true) }
|
||||
// itemListPlayer.gridModeButtons[0].clickOnceListener = { _,_ -> setCompact(false) }
|
||||
// itemListPlayer.gridModeButtons[1].clickOnceListener = { _,_ -> setCompact(true) }
|
||||
|
||||
|
||||
|
||||
@@ -317,7 +316,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
|
||||
}
|
||||
|
||||
|
||||
buttonCraft.touchDownListener = { _,_,_,_ ->
|
||||
buttonCraft.clickOnceListener = { _,_ ->
|
||||
getPlayerInventory().let { player -> recipeClicked?.let { recipe ->
|
||||
// check if player has enough amount of ingredients
|
||||
val itemCraftable = itemListIngredients.getInventory().itemList.all { (itm, qty) ->
|
||||
@@ -340,8 +339,8 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
|
||||
refreshCraftButtonStatus()
|
||||
}
|
||||
// make grid mode buttons work together
|
||||
// itemListCraftable.gridModeButtons[0].touchDownListener = { _,_,_,_ -> setCompact(false) }
|
||||
// itemListCraftable.gridModeButtons[1].touchDownListener = { _,_,_,_ -> setCompact(true) }
|
||||
// itemListCraftable.gridModeButtons[0].clickOnceListener = { _,_ -> setCompact(false) }
|
||||
// itemListCraftable.gridModeButtons[1].clickOnceListener = { _,_ -> setCompact(true) }
|
||||
|
||||
handler.allowESCtoClose = true
|
||||
|
||||
@@ -387,7 +386,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
|
||||
}
|
||||
}
|
||||
|
||||
buttonCraft.isActive = itemCraftable
|
||||
buttonCraft.isEnabled = itemCraftable
|
||||
}
|
||||
|
||||
// reset whatever player has selected to null and bring UI to its initial state
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.CommonResourcePool
|
||||
import net.torvald.terrarum.INGAME
|
||||
import net.torvald.terrarum.ceilInt
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.CELL_COL
|
||||
@@ -29,7 +30,7 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
private val h1MarginBottom = 4
|
||||
|
||||
private val options = arrayOf(
|
||||
arrayOf("", { Lang["MENU_OPTIONS_PERFORMANCE"] }, "h1"),
|
||||
arrayOf("", { Lang["CREDITS_VFX"] }, "h1"),
|
||||
arrayOf("fx_dither", { Lang["MENU_OPTIONS_DITHER"] }, "toggle"),
|
||||
arrayOf("fx_backgroundblur", { Lang["MENU_OPTIONS_BLUR"] }, "toggle"),
|
||||
arrayOf("maxparticles", { Lang["MENU_OPTIONS_PARTICLES"] }, "spinner,256,1024,256"),
|
||||
@@ -39,7 +40,7 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
arrayOf("displayfps", { Lang["MENU_LABEL_FRAMESPERSEC"] }, "spinner,0,300,2"),
|
||||
arrayOf("usevsync", { Lang["MENU_OPTIONS_VSYNC"] }, "toggle"),
|
||||
arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"),
|
||||
arrayOf("", { Lang["GAME_GENRE_MISC"] }, "h1"),
|
||||
arrayOf("", { Lang["MENU_LABEL_STREAMING"] }, "h1"),
|
||||
arrayOf("fx_streamerslayout", { Lang["MENU_OPTION_STREAMERS_LAYOUT"] }, "toggle"),
|
||||
)
|
||||
|
||||
@@ -90,7 +91,7 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
}
|
||||
else if (args.startsWith("toggle")) {
|
||||
UIItemToggleButton(this, x, y, spinnerWidth, App.getConfigBoolean(optionName)) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemToggleButton).clickOnceListener = { _, _, _ ->
|
||||
(it as UIItemToggleButton).clickOnceListener = { _, _ ->
|
||||
it.toggle()
|
||||
App.setConfig(optionStr, it.getStatus())
|
||||
}
|
||||
@@ -109,11 +110,16 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
UIItemSpinner(this, x, y, App.getConfigDouble(optionName), arg[1].toDouble(), arg[2].toDouble(), arg[3].toDouble(), spinnerWidth, numberToTextFunction = { "${((it as Double)*100).toInt()}%" }) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemSpinner).selectionChangeListener = {
|
||||
App.setConfig(optionStr, it)
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.startsWith("typeinint")) {
|
||||
// val arg = args.split(',') // args: none
|
||||
UIItemTextLineInput(this, x, y, spinnerWidth, { "${App.getConfigInt(optionName)}" }, InputLenCap(4, InputLenCap.CharLenUnit.CODEPOINTS), { it.headkey in Input.Keys.NUM_0..Input.Keys.NUM_9 || it.headkey == Input.Keys.BACKSPACE }) to { it: UIItem, optionStr: String ->
|
||||
UIItemTextLineInput(this, x, y, spinnerWidth,
|
||||
defaultValue = { "${App.getConfigInt(optionName)}" },
|
||||
maxLen = InputLenCap(4, InputLenCap.CharLenUnit.CODEPOINTS),
|
||||
keyFilter = { it.headkey in Input.Keys.NUM_0..Input.Keys.NUM_9 || it.headkey == Input.Keys.BACKSPACE }
|
||||
) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemTextLineInput).textCommitListener = {
|
||||
App.setConfig(optionStr, it.toInt()) // HAXXX!!!
|
||||
}
|
||||
@@ -122,7 +128,12 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
else if (args.startsWith("typeinres")) {
|
||||
val keyWidth = optionName.substringBefore(',')
|
||||
val keyHeight = optionName.substringAfter(',')
|
||||
UIItemTextLineInput(this, x, y, spinnerWidth, { "${App.getConfigInt(keyWidth)}x${App.getConfigInt(keyHeight)}" }, InputLenCap(9, InputLenCap.CharLenUnit.CODEPOINTS), { it.headkey == Input.Keys.ENTER || it.headkey == Input.Keys.BACKSPACE || it.character?.matches(Regex("[0-9xX]")) == true }, UIItemTextButton.Companion.Alignment.CENTRE) to { it: UIItem, optionStr: String ->
|
||||
UIItemTextLineInput(this, x, y, spinnerWidth,
|
||||
defaultValue = { "${App.getConfigInt(keyWidth)}x${App.getConfigInt(keyHeight)}" },
|
||||
maxLen = InputLenCap(9, InputLenCap.CharLenUnit.CODEPOINTS),
|
||||
keyFilter = { it.headkey == Input.Keys.ENTER || it.headkey == Input.Keys.BACKSPACE || it.character?.matches(Regex("[0-9xX]")) == true },
|
||||
alignment = UIItemTextButton.Companion.Alignment.CENTRE
|
||||
) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemTextLineInput).textCommitListener = { text ->
|
||||
val text = text.lowercase()
|
||||
if (text.matches(Regex("""[0-9]+x[0-9]+"""))) {
|
||||
@@ -167,8 +178,11 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
|
||||
options.forEachIndexed { index, strings ->
|
||||
val mode = strings[2]
|
||||
|
||||
val font = if (mode == "h1") App.fontUITitle else App.fontGame
|
||||
|
||||
val label = (strings[1] as () -> String).invoke()
|
||||
val labelWidth = App.fontGame.getWidth(label)
|
||||
val labelWidth = font.getWidth(label)
|
||||
batch.color = when (mode) {
|
||||
"h1" -> Toolkit.Theme.COL_MOUSE_UP
|
||||
"p" -> Color.LIGHT_GRAY
|
||||
@@ -180,7 +194,7 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
else
|
||||
drawX + width/2 - panelgap - labelWidth // right aligned at the middle of the panel, offsetted by panelgap
|
||||
|
||||
App.fontGame.draw(batch, label, xpos.toFloat(), drawY + optionsYpos[index] - 2f)
|
||||
font.draw(batch, label, xpos.toFloat(), drawY + optionsYpos[index] - 2f)
|
||||
|
||||
// draw hrule
|
||||
if (mode == "h1") {
|
||||
@@ -209,6 +223,14 @@ class UIGraphicsControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
return super.touchDown(screenX, screenY, pointer, button)
|
||||
}
|
||||
|
||||
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
return super.touchUp(screenX, screenY, pointer, button)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
}
|
||||
@@ -110,14 +110,14 @@ class UIIMEConfig(remoCon: UIRemoCon?) : UICanvas() {
|
||||
private val selDrawX = (Toolkit.drawWidth - selectorWidth) / 2
|
||||
private val halfw = width / 2
|
||||
|
||||
private val y1 = 400
|
||||
private val y2 = y1 + 40
|
||||
// private val y1 = 400
|
||||
// private val y2 = y1 + 40
|
||||
|
||||
private val lowLayerCodes = IME.getAllLowLayers().sorted()
|
||||
private val lowLayerNames = lowLayerCodes.map { { IME.getLowLayerByName(it).name } }
|
||||
private val keyboardLayoutSelection = UIItemTextSelector(this,
|
||||
selDrawX + (halfselw - textSelWidth) / 2,
|
||||
y2,
|
||||
kby + 260,
|
||||
lowLayerNames,
|
||||
lowLayerCodes.linearSearch { it == App.getConfigString("basekeyboardlayout") } ?: throw IME.LayoutNotFound(App.getConfigString("basekeyboardlayout")),
|
||||
textSelWidth
|
||||
@@ -127,8 +127,8 @@ class UIIMEConfig(remoCon: UIRemoCon?) : UICanvas() {
|
||||
private val imeCodes = listOf("none") + imeCodes0
|
||||
private val imeNames = listOf({"$EMDASH"}) + imeCodes0.map { { IME.getHighLayerByName(it).name } }
|
||||
private val imeSelection = UIItemTextSelector(this,
|
||||
selDrawX + halfselw + (halfselw - textSelWidth) / 2,
|
||||
y2,
|
||||
selDrawX + halfselw + (halfselw - textSelWidth) / 2,
|
||||
kby + 260,
|
||||
imeNames,
|
||||
imeCodes.linearSearch { it == App.getConfigString("inputmethod") } ?: throw IME.LayoutNotFound(App.getConfigString("inputmethod")),
|
||||
textSelWidth
|
||||
@@ -138,7 +138,7 @@ class UIIMEConfig(remoCon: UIRemoCon?) : UICanvas() {
|
||||
|
||||
private val keyboardTestPanel = UIItemTextLineInput(this,
|
||||
drawX + (width - 480) / 2 + 3,
|
||||
height - 40,
|
||||
drawY + height - 120,
|
||||
474
|
||||
)
|
||||
|
||||
@@ -171,23 +171,23 @@ class UIIMEConfig(remoCon: UIRemoCon?) : UICanvas() {
|
||||
batch.color = Color.WHITE
|
||||
|
||||
val txt1 = Lang["MENU_LABEL_KEYBOARD_LAYOUT"]; val tw1 = App.fontGame.getWidth(txt1)
|
||||
App.fontGame.draw(batch, txt1, selDrawX + (halfselw - tw1) / 2, y1)
|
||||
App.fontGame.draw(batch, txt1, selDrawX + (halfselw - tw1) / 2, keyboardLayoutSelection.posY - 40)
|
||||
|
||||
val txt2 = Lang["MENU_LABEL_IME"]; val tw2 = App.fontGame.getWidth(txt2)
|
||||
App.fontGame.draw(batch, txt2, selDrawX + halfselw + (halfselw - tw2) / 2, y1)
|
||||
App.fontGame.draw(batch, txt2, selDrawX + halfselw + (halfselw - tw2) / 2, keyboardLayoutSelection.posY - 40)
|
||||
|
||||
// title
|
||||
// todo show "Keyboard"/"Gamepad" accordingly
|
||||
val title = Lang["MENU_CONTROLS_KEYBOARD"]
|
||||
batch.color = Color.WHITE
|
||||
App.fontGame.draw(batch, title, drawX.toFloat() + (width - App.fontGame.getWidth(title)) / 2, drawY.toFloat())
|
||||
App.fontUITitle.draw(batch, title, drawX.toFloat() + (width - App.fontUITitle.getWidth(title)) / 2, drawY.toFloat())
|
||||
|
||||
|
||||
// button help for string input UI
|
||||
val help1 = "↓ ${Lang["MENU_LABEL_IME_TOGGLE"]}"
|
||||
App.fontGame.draw(batch, help1, drawX + 10f, height - 40f - 28f)
|
||||
App.fontGame.draw(batch, help1, drawX + 10f, keyboardTestPanel.posY - 28f)
|
||||
val help2 = "${Lang["MENU_LABEL_PASTE_FROM_CLIPBOARD"]} ↑"
|
||||
App.fontGame.draw(batch, help2, drawX + keyboardTestPanel.width - 4f - App.fontGame.getWidth(help2), height - 40f + 30f)
|
||||
App.fontGame.draw(batch, help2, drawX + keyboardTestPanel.width - 4f - App.fontGame.getWidth(help2), keyboardTestPanel.posY + 30f)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class UIInventoryEscMenu(val full: UIInventoryFull) : UICanvas() {
|
||||
defaultSelection = null
|
||||
)
|
||||
private val areYouSureMainMenuButtons = UIItemTextButtonList(
|
||||
this, DEFAULT_LINE_HEIGHT, arrayOf("MENU_LABEL_QUIT_CONFIRM", "MENU_LABEL_QUIT", "MENU_LABEL_CANCEL"),
|
||||
this, DEFAULT_LINE_HEIGHT, arrayOf("MENU_LABEL_QUIT_CONFIRM", "MENU_LABEL_UNSAVED_PROGRESSES_WILL_BE_LOST", "MENU_LABEL_QUIT", "MENU_LABEL_CANCEL"),
|
||||
(width - gameMenuListWidth) / 2,
|
||||
INVENTORY_CELLS_OFFSET_Y() + (INVENTORY_CELLS_UI_HEIGHT - (DEFAULT_LINE_HEIGHT * 3)) / 2,
|
||||
gameMenuListWidth, DEFAULT_LINE_HEIGHT * 3,
|
||||
@@ -60,7 +60,12 @@ class UIInventoryEscMenu(val full: UIInventoryFull) : UICanvas() {
|
||||
highlightBackCol = Color(0),
|
||||
inactiveCol = Color.WHITE,
|
||||
defaultSelection = null
|
||||
)
|
||||
).also {
|
||||
listOf(it.buttons[0], it.buttons[1]).forEach {
|
||||
it.skipUpdate = true
|
||||
it.isActive = false
|
||||
}
|
||||
}
|
||||
/*private val areYouSureQuitButtons = UIItemTextButtonList(
|
||||
this, DEFAULT_LINE_HEIGHT, arrayOf("MENU_LABEL_DESKTOP_QUESTION", "MENU_LABEL_DESKTOP", "MENU_LABEL_CANCEL"),
|
||||
(width - gameMenuListWidth) / 2,
|
||||
@@ -105,11 +110,19 @@ class UIInventoryEscMenu(val full: UIInventoryFull) : UICanvas() {
|
||||
full.unlockTransition()
|
||||
}
|
||||
|
||||
val saveTime_t = App.getTIME_T()
|
||||
val onSuccessful = {
|
||||
// return to normal state
|
||||
System.gc()
|
||||
screen = 0
|
||||
full.handler.unlockToggle()
|
||||
full.unlockTransition()
|
||||
(INGAME as TerrarumIngame).autosaveTimer = 0f
|
||||
}
|
||||
|
||||
|
||||
/*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) {
|
||||
|
||||
@@ -123,13 +136,12 @@ class UIInventoryEscMenu(val full: UIInventoryFull) : UICanvas() {
|
||||
}
|
||||
|
||||
// return to normal state
|
||||
System.gc()
|
||||
screen = 0
|
||||
full.handler.unlockToggle()
|
||||
full.unlockTransition()
|
||||
(INGAME as TerrarumIngame).autosaveTimer = 0f
|
||||
onSuccessful()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
INGAME.saveTheGame(onSuccessful, onError)
|
||||
|
||||
|
||||
}
|
||||
1 -> {
|
||||
@@ -148,11 +160,11 @@ class UIInventoryEscMenu(val full: UIInventoryFull) : UICanvas() {
|
||||
}
|
||||
areYouSureMainMenuButtons.selectionChangeListener = { _, new ->
|
||||
when (new) {
|
||||
1 -> {
|
||||
2 -> {
|
||||
areYouSureMainMenuButtons.deselect()
|
||||
App.setScreen(TitleScreen(App.batch))
|
||||
}
|
||||
2 -> {
|
||||
3 -> {
|
||||
screen = 0; areYouSureMainMenuButtons.deselect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.terrarum.ui.UIItemHorizontalFadeSlide
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||
import net.torvald.unicode.*
|
||||
|
||||
/**
|
||||
@@ -31,8 +32,20 @@ class UIInventoryFull(
|
||||
override var height: Int = App.scr.height
|
||||
|
||||
companion object {
|
||||
private var shapeRenderer: ShapeRenderer? = null
|
||||
// private var shapeRenderer: ShapeRenderer? = null
|
||||
|
||||
private val backDropsLoaded = Array<Boolean>(16) { false }
|
||||
private val backdrop01: TextureRegionPack
|
||||
get() {
|
||||
if (!backDropsLoaded[0]) {
|
||||
CommonResourcePool.addToLoadingList("basegame.uibackdrop01") {
|
||||
TextureRegionPack(ModMgr.getGdxFile("basegame", "gui/backdrop01.tga"), 2, 140)
|
||||
}
|
||||
CommonResourcePool.loadAll()
|
||||
backDropsLoaded[0] = true
|
||||
}
|
||||
return CommonResourcePool.getAsTextureRegionPack("basegame.uibackdrop01")
|
||||
}
|
||||
|
||||
val CELL_COL = Toolkit.Theme.COL_CELL_FILL
|
||||
|
||||
@@ -58,6 +71,9 @@ class UIInventoryFull(
|
||||
val INVENTORY_CELLS_OFFSET_X = { 0 + (Toolkit.drawWidth - internalWidth) / 2 }
|
||||
val INVENTORY_CELLS_OFFSET_Y = { -YPOS_CORRECTION + 107 + (App.scr.height - internalHeight) / 2 }
|
||||
|
||||
fun getWidthOfCells(count: Int, cellWidth: Int = UIItemInventoryElemWide.height, gapWidth: Int = UIItemInventoryItemGrid.listGap) =
|
||||
(cellWidth * count) + (gapWidth * (count - 1))
|
||||
|
||||
val catBarWidth = 330
|
||||
|
||||
val gradStartCol = Color(0x404040_60)
|
||||
@@ -69,9 +85,11 @@ class UIInventoryFull(
|
||||
private val gsta = Color(gradStartCol)
|
||||
private val gend = Color(gradEndCol)
|
||||
|
||||
|
||||
private val drawBackgroundColourBuffer = Color(1f,1f,1f,1f)
|
||||
fun drawBackground(batch: SpriteBatch, opacity: Float) {
|
||||
batch.end()
|
||||
gdxBlendNormalStraightAlpha()
|
||||
/*batch.end()
|
||||
|
||||
if (shapeRenderer == null) {
|
||||
shapeRenderer = App.makeShapeRenderer()
|
||||
@@ -101,7 +119,23 @@ class UIInventoryFull(
|
||||
it.rect(0f, h, w, -(h - gradBottomEnd), gsta, gsta, gsta, gsta)
|
||||
}
|
||||
|
||||
batch.begin()
|
||||
batch.begin()*/
|
||||
// drawBackgroundColourBuffer.a = opacity
|
||||
batch.color = drawBackgroundColourBuffer
|
||||
val w = App.scr.wf
|
||||
val h = App.scr.hf
|
||||
val gradTopStart = (-YPOS_CORRECTION + (App.scr.height - internalHeight).div(2).toFloat()) * App.scr.magn
|
||||
|
||||
val hTop = gradTopStart
|
||||
val hTopRem = hTop - 64f
|
||||
|
||||
val hMid = h - 2 * (hTopRem + 140f)
|
||||
batch.draw(backdrop01.get(0, 0), 0f, 0f, w, hTopRem)
|
||||
batch.draw(backdrop01.get(0, 1), 0f, hTopRem, w, 140f)
|
||||
batch.draw(backdrop01.get(0, 2), 0f, hTopRem + 140f, w, hMid)
|
||||
batch.draw(backdrop01.get(0, 3), 0f, hTopRem + 140f + hMid, w, 140f)
|
||||
batch.draw(backdrop01.get(0, 4), 0f, hTopRem + 280f + hMid, w, hTopRem)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +315,7 @@ class UIInventoryFull(
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
|
||||
drawBackground(batch, handler.opacity)
|
||||
drawBackground(batch, 1f)
|
||||
|
||||
// UI items
|
||||
catBar.render(batch, camera)
|
||||
|
||||
@@ -86,13 +86,18 @@ open class UIItemInventoryItemGrid(
|
||||
|
||||
private val inventoryUI = parentUI
|
||||
|
||||
var itemPage = 0
|
||||
var itemPage
|
||||
set(value) {
|
||||
field = if (itemPageCount == 0) 0 else (value).fmod(itemPageCount)
|
||||
navRemoCon.itemPage = if (itemPageCount == 0) 0 else (value).fmod(itemPageCount)
|
||||
rebuild(currentFilter)
|
||||
}
|
||||
var itemPageCount = 1 // TODO total size of current category / items.size
|
||||
protected set
|
||||
get() = navRemoCon.itemPage
|
||||
|
||||
var itemPageCount // TODO total size of current category / items.size
|
||||
protected set(value) {
|
||||
navRemoCon.itemPageCount = value
|
||||
}
|
||||
get() = navRemoCon.itemPageCount
|
||||
|
||||
var inventorySortList = ArrayList<InventoryPair>()
|
||||
protected var rebuildList = true
|
||||
@@ -140,35 +145,37 @@ open class UIItemInventoryItemGrid(
|
||||
|
||||
fun createInvCellGenericTouchDownFun(listRebuildFun: () -> Unit): (GameItem?, Long, Int, Any?, UIItemInventoryCellBase) -> Unit {
|
||||
return { item: GameItem?, amount: Long, button: Int, _, _ ->
|
||||
if (item != null && Terrarum.ingame != null) {
|
||||
// equip da shit
|
||||
val itemEquipSlot = item.equipPosition
|
||||
if (itemEquipSlot == GameItem.EquipPosition.NULL) {
|
||||
TODO("Equip position is NULL, does this mean it's single-consume items like a potion? (from item: \"$item\" with itemID: ${item.originalID}/${item.dynamicID})")
|
||||
}
|
||||
val player = (Terrarum.ingame!! as TerrarumIngame).actorNowPlaying
|
||||
if (player != null) {
|
||||
|
||||
if (item != ItemCodex[player.inventory.itemEquipped.get(itemEquipSlot)]) { // if this item is unequipped, equip it
|
||||
player.equipItem(item)
|
||||
|
||||
// also equip on the quickslot
|
||||
player.actorValue.getAsInt(AVKey.__PLAYER_QUICKSLOTSEL)?.let {
|
||||
player.inventory.setQuickslotItem(it, item.dynamicID)
|
||||
}
|
||||
if (button == App.getConfigInt("config_mouseprimary")) {
|
||||
if (item != null && Terrarum.ingame != null) {
|
||||
// equip da shit
|
||||
val itemEquipSlot = item.equipPosition
|
||||
if (itemEquipSlot == GameItem.EquipPosition.NULL) {
|
||||
TODO("Equip position is NULL, does this mean it's single-consume items like a potion? (from item: \"$item\" with itemID: ${item.originalID}/${item.dynamicID})")
|
||||
}
|
||||
else { // if not, unequip it
|
||||
player.unequipItem(item)
|
||||
val player = (Terrarum.ingame!! as TerrarumIngame).actorNowPlaying
|
||||
if (player != null) {
|
||||
|
||||
// also unequip on the quickslot
|
||||
player.actorValue.getAsInt(AVKey.__PLAYER_QUICKSLOTSEL)?.let {
|
||||
player.inventory.setQuickslotItem(it, null)
|
||||
if (item != ItemCodex[player.inventory.itemEquipped.get(itemEquipSlot)]) { // if this item is unequipped, equip it
|
||||
player.equipItem(item)
|
||||
|
||||
// also equip on the quickslot
|
||||
player.actorValue.getAsInt(AVKey.__PLAYER_QUICKSLOTSEL)?.let {
|
||||
player.inventory.setQuickslotItem(it, item.dynamicID)
|
||||
}
|
||||
}
|
||||
else { // if not, unequip it
|
||||
player.unequipItem(item)
|
||||
|
||||
// also unequip on the quickslot
|
||||
player.actorValue.getAsInt(AVKey.__PLAYER_QUICKSLOTSEL)?.let {
|
||||
player.inventory.setQuickslotItem(it, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listRebuildFun()
|
||||
}
|
||||
|
||||
listRebuildFun()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,50 +230,9 @@ open class UIItemInventoryItemGrid(
|
||||
}
|
||||
|
||||
private val iconPosX = if (drawScrollOnRightside)
|
||||
posX + width + LIST_TO_CONTROL_GAP
|
||||
posX + width
|
||||
else
|
||||
posX - LIST_TO_CONTROL_GAP - catBar.catIcons.tileW
|
||||
|
||||
|
||||
/** Long/compact mode buttons */
|
||||
val gridModeButtons = Array<UIItemImageButton>(2) { index ->
|
||||
UIItemImageButton(
|
||||
parentUI,
|
||||
catBar.catIcons.get(index + 14, 0),
|
||||
backgroundCol = Color(0),
|
||||
activeBackCol = Color(0),
|
||||
highlightBackCol = Color(0),
|
||||
activeBackBlendMode = BlendMode.NORMAL,
|
||||
activeCol = Toolkit.Theme.COL_MOUSE_UP,
|
||||
initialX = iconPosX,
|
||||
initialY = getIconPosY(index),
|
||||
highlightable = true
|
||||
)
|
||||
}
|
||||
|
||||
private val scrollUpButton = UIItemImageButton(
|
||||
parentUI,
|
||||
catBar.catIcons.get(18, 0),
|
||||
backgroundCol = Color(0),
|
||||
activeBackCol = Color(0),
|
||||
activeBackBlendMode = BlendMode.NORMAL,
|
||||
activeCol = Toolkit.Theme.COL_MOUSE_UP,
|
||||
initialX = iconPosX,
|
||||
initialY = getIconPosY(2),
|
||||
highlightable = false
|
||||
)
|
||||
|
||||
private val scrollDownButton = UIItemImageButton(
|
||||
parentUI,
|
||||
catBar.catIcons.get(19, 0),
|
||||
backgroundCol = Color(0),
|
||||
activeBackCol = Color(0),
|
||||
activeBackBlendMode = BlendMode.NORMAL,
|
||||
activeCol = Toolkit.Theme.COL_MOUSE_UP,
|
||||
initialX = iconPosX,
|
||||
initialY = getIconPosY(3),
|
||||
highlightable = false
|
||||
)
|
||||
posX - UIItemListNavBarVertical.LIST_TO_CONTROL_GAP - UIItemListNavBarVertical.WIDTH - 4
|
||||
|
||||
fun setCustomHighlightRuleMain(predicate: ((UIItemInventoryCellBase) -> Boolean)?) {
|
||||
itemGrid.forEach { it.customHighlightRuleMain = predicate }
|
||||
@@ -282,63 +248,53 @@ open class UIItemInventoryItemGrid(
|
||||
itemPage = if (itemPageCount == 0) 0 else (itemPage + relativeAmount).fmod(itemPageCount)
|
||||
}
|
||||
|
||||
val navRemoCon = UIItemListNavBarVertical(parentUI, iconPosX, posY + 8, height, true, if (isCompactMode) 1 else 0)
|
||||
|
||||
init {
|
||||
// initially highlight grid mode buttons
|
||||
if (!hideSidebar) {
|
||||
gridModeButtons[if (isCompactMode) 1 else 0].highlighted = true
|
||||
|
||||
|
||||
gridModeButtons[0].touchDownListener = { _, _, _, _ ->
|
||||
navRemoCon.listButtonListener = { _, _ ->
|
||||
isCompactMode = false
|
||||
gridModeButtons[0].highlighted = true
|
||||
gridModeButtons[1].highlighted = false
|
||||
itemPage = 0
|
||||
rebuild(currentFilter)
|
||||
}
|
||||
gridModeButtons[1].touchDownListener = { _, _, _, _ ->
|
||||
navRemoCon.gridButtonListener = { _, _ ->
|
||||
isCompactMode = true
|
||||
gridModeButtons[0].highlighted = false
|
||||
gridModeButtons[1].highlighted = true
|
||||
itemPage = 0
|
||||
rebuild(currentFilter)
|
||||
}
|
||||
|
||||
scrollUpButton.clickOnceListener = { _, _, _ ->
|
||||
scrollUpButton.highlighted = false
|
||||
navRemoCon.scrollUpListener = { _, it ->
|
||||
it.highlighted = false
|
||||
scrollItemPage(-1)
|
||||
}
|
||||
scrollDownButton.clickOnceListener = { _, _, _ ->
|
||||
scrollDownButton.highlighted = false
|
||||
navRemoCon.scrollDownListener = { _, it ->
|
||||
it.highlighted = false
|
||||
scrollItemPage(1)
|
||||
}
|
||||
|
||||
// if (is.mouseUp) handled by this.touchDown()
|
||||
// draw wallet text
|
||||
navRemoCon.extraDrawOpOnBottom = { ui, batch ->
|
||||
if (drawWallet) {
|
||||
batch.color = Color.WHITE
|
||||
walletText.forEachIndexed { index, it ->
|
||||
batch.draw(
|
||||
walletFont.get(0, it - '0'),
|
||||
ui.gridModeButtons[0].posX - 1f, // scroll button size: 20px, font width: 20 px
|
||||
ui.gridModeButtons[0].posY + height - index * walletFont.tileH - 18f
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private val upDownButtonGapToDots = 7 // apparent gap may vary depend on the texture itself
|
||||
// private val upDownButtonGapToDots = 7 // apparent gap may vary depend on the texture itself
|
||||
|
||||
private fun getIconPosY(index: Int) =
|
||||
posY + 8 + 26 * index
|
||||
// private fun getIconPosY(index: Int) =
|
||||
// posY + 8 + 26 * index
|
||||
|
||||
override fun render(batch: SpriteBatch, camera: Camera) {
|
||||
val posXDelta = posX - oldPosX
|
||||
itemGrid.forEach { it.posX += posXDelta }
|
||||
itemList.forEach { it.posX += posXDelta }
|
||||
if (!hideSidebar) {
|
||||
gridModeButtons.forEach { it.posX += posXDelta }
|
||||
scrollUpButton.posX += posXDelta
|
||||
scrollDownButton.posX += posXDelta
|
||||
}
|
||||
|
||||
|
||||
fun getScrollDotYHeight(i: Int) = scrollUpButton.posY + 14 + upDownButtonGapToDots + 10 * i
|
||||
|
||||
|
||||
scrollDownButton.posY = getScrollDotYHeight(itemPageCount) + upDownButtonGapToDots
|
||||
|
||||
|
||||
// define each button's highlighted status from the list of forceHighlighted, then render the button
|
||||
items.forEach {
|
||||
if (useHighlightingManager) it.forceHighlighted = forceHighlightList.contains(it.item?.dynamicID)
|
||||
@@ -346,41 +302,7 @@ open class UIItemInventoryItemGrid(
|
||||
}
|
||||
|
||||
if (!hideSidebar) {
|
||||
// draw the tray
|
||||
batch.color = Toolkit.Theme.COL_CELL_FILL
|
||||
Toolkit.fillArea(batch, iconPosX - 4, getIconPosY(0) - 8, 28, height)
|
||||
// cell border
|
||||
batch.color = colourTheme.cellHighlightNormalCol
|
||||
Toolkit.drawBoxBorder(batch, iconPosX - 4, getIconPosY(0) - 8, 28, height)
|
||||
|
||||
gridModeButtons.forEach { it.render(batch, camera) }
|
||||
scrollUpButton.render(batch, camera)
|
||||
scrollDownButton.render(batch, camera)
|
||||
|
||||
// draw scroll dots
|
||||
for (i in 0 until itemPageCount) {
|
||||
val colour = if (i == itemPage) Color.WHITE else Color(0xffffff7f.toInt())
|
||||
|
||||
batch.color = colour
|
||||
batch.draw(
|
||||
catBar.catIcons.get(if (i == itemPage) 20 else 21, 0),
|
||||
iconPosX.toFloat(),
|
||||
getScrollDotYHeight(i).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// draw wallet text
|
||||
if (drawWallet) {
|
||||
batch.color = Color.WHITE
|
||||
walletText.forEachIndexed { index, it ->
|
||||
batch.draw(
|
||||
walletFont.get(0, it - '0'),
|
||||
gridModeButtons[0].posX - 1f, // scroll button size: 20px, font width: 20 px
|
||||
gridModeButtons[0].posY + height - index * walletFont.tileH - 18f
|
||||
)
|
||||
}
|
||||
navRemoCon.render(batch, camera)
|
||||
}
|
||||
|
||||
super.render(batch, camera)
|
||||
@@ -424,9 +346,7 @@ open class UIItemInventoryItemGrid(
|
||||
|
||||
|
||||
if (!hideSidebar) {
|
||||
gridModeButtons.forEach { it.update(delta) }
|
||||
scrollUpButton.update(delta)
|
||||
scrollDownButton.update(delta)
|
||||
navRemoCon.update(delta)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,9 +469,7 @@ open class UIItemInventoryItemGrid(
|
||||
|
||||
items.forEach { if (it.mouseUp) it.touchDown(screenX, screenY, pointer, button) }
|
||||
if (!hideSidebar) {
|
||||
gridModeButtons.forEach { if (it.mouseUp) it.touchDown(screenX, screenY, pointer, button) }
|
||||
if (scrollUpButton.mouseUp) scrollUpButton.touchDown(screenX, screenY, pointer, button)
|
||||
if (scrollDownButton.mouseUp) scrollDownButton.touchDown(screenX, screenY, pointer, button)
|
||||
navRemoCon.touchDown(screenX, screenY, pointer, button)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
package net.torvald.terrarum.modulebasegame.ui
|
||||
|
||||
import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.terrarum.BlendMode
|
||||
import net.torvald.terrarum.CommonResourcePool
|
||||
import net.torvald.terrarum.toInt
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.terrarum.ui.UIItem
|
||||
import net.torvald.terrarum.ui.UIItemImageButton
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2023-06-17.
|
||||
*/
|
||||
class UIItemListNavBarVertical(
|
||||
parentUI: UICanvas, initialX: Int, initialY: Int,
|
||||
override val height: Int,
|
||||
val hasGridModeButtons: Boolean, initialModeSelection: Int = 0,
|
||||
private val colourTheme: InventoryCellColourTheme = UIItemInventoryCellCommonRes.defaultInventoryCellTheme,
|
||||
var extraDrawOpOnBottom: (UIItemListNavBarVertical, SpriteBatch) -> Unit = { _,_ -> }
|
||||
) : UIItem(parentUI, initialX, initialY) {
|
||||
|
||||
override val width = UIItemListNavBarVertical.WIDTH
|
||||
|
||||
companion object {
|
||||
const val WIDTH = 28
|
||||
const val LIST_TO_CONTROL_GAP = 12
|
||||
}
|
||||
|
||||
private fun getIconPosY(index: Int) =
|
||||
posY + 26 * index
|
||||
private val catIcons = CommonResourcePool.getAsTextureRegionPack("inventory_category")
|
||||
private val iconPosX = posX + LIST_TO_CONTROL_GAP
|
||||
|
||||
val gridModeButtons = Array<UIItemImageButton>(2) { index ->
|
||||
UIItemImageButton(
|
||||
parentUI,
|
||||
catIcons.get(index + 14, 0),
|
||||
backgroundCol = Color(0),
|
||||
activeBackCol = Color(0),
|
||||
highlightBackCol = Color(0),
|
||||
activeBackBlendMode = BlendMode.NORMAL,
|
||||
activeCol = Toolkit.Theme.COL_MOUSE_UP,
|
||||
initialX = iconPosX,
|
||||
initialY = getIconPosY(index),
|
||||
highlightable = true
|
||||
)
|
||||
}
|
||||
|
||||
val scrollUpButton = UIItemImageButton(
|
||||
parentUI,
|
||||
catIcons.get(18, 0),
|
||||
backgroundCol = Color(0),
|
||||
activeBackCol = Color(0),
|
||||
activeBackBlendMode = BlendMode.NORMAL,
|
||||
activeCol = Toolkit.Theme.COL_MOUSE_UP,
|
||||
initialX = iconPosX,
|
||||
initialY = getIconPosY(2 - (!hasGridModeButtons).toInt(1)),
|
||||
highlightable = false
|
||||
)
|
||||
|
||||
val scrollDownButton = UIItemImageButton(
|
||||
parentUI,
|
||||
catIcons.get(19, 0),
|
||||
backgroundCol = Color(0),
|
||||
activeBackCol = Color(0),
|
||||
activeBackBlendMode = BlendMode.NORMAL,
|
||||
activeCol = Toolkit.Theme.COL_MOUSE_UP,
|
||||
initialX = iconPosX,
|
||||
initialY = getIconPosY(3 - (!hasGridModeButtons).toInt(1)),
|
||||
highlightable = false
|
||||
)
|
||||
|
||||
private val upDownButtonGapToDots = 7 // apparent gap may vary depend on the texture itself
|
||||
|
||||
init {
|
||||
gridModeButtons[initialModeSelection].highlighted = true
|
||||
|
||||
gridModeButtons[0].touchDownListener = { _, _, _, _ ->
|
||||
gridModeButtons[0].highlighted = true
|
||||
gridModeButtons[1].highlighted = false
|
||||
itemPage = 0
|
||||
listButtonListener(this, gridModeButtons[0])
|
||||
}
|
||||
gridModeButtons[1].touchDownListener = { _, _, _, _ ->
|
||||
gridModeButtons[0].highlighted = false
|
||||
gridModeButtons[1].highlighted = true
|
||||
itemPage = 0
|
||||
gridButtonListener(this, gridModeButtons[1])
|
||||
}
|
||||
|
||||
scrollUpButton.clickOnceListener = { _, _ ->
|
||||
scrollUpButton.highlighted = false
|
||||
scrollUpListener(this, scrollUpButton)
|
||||
}
|
||||
scrollDownButton.clickOnceListener = { _, _ ->
|
||||
scrollDownButton.highlighted = false
|
||||
scrollDownListener(this, scrollDownButton)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var listButtonListener: (UIItemListNavBarVertical, UIItemImageButton) -> Unit = { _,_ -> }
|
||||
var gridButtonListener: (UIItemListNavBarVertical, UIItemImageButton) -> Unit = { _,_ -> }
|
||||
var scrollUpListener: (UIItemListNavBarVertical, UIItemImageButton) -> Unit = { _,_ -> }
|
||||
var scrollDownListener: (UIItemListNavBarVertical, UIItemImageButton) -> Unit = { _,_ -> }
|
||||
var itemPageCount = 0
|
||||
var itemPage = 0
|
||||
|
||||
|
||||
override fun render(batch: SpriteBatch, camera: Camera) {
|
||||
val posXDelta = posX - oldPosX
|
||||
|
||||
gridModeButtons.forEach { it.posX += posXDelta }
|
||||
scrollUpButton.posX += posXDelta
|
||||
scrollDownButton.posX += posXDelta
|
||||
|
||||
fun getScrollDotYHeight(i: Int) = scrollUpButton.posY + 14 + upDownButtonGapToDots + 10 * i
|
||||
|
||||
scrollDownButton.posY = getScrollDotYHeight(itemPageCount) + upDownButtonGapToDots
|
||||
|
||||
|
||||
|
||||
// draw the tray
|
||||
batch.color = Toolkit.Theme.COL_CELL_FILL
|
||||
Toolkit.fillArea(batch, iconPosX - 4, getIconPosY(0) - 8, width, height)
|
||||
// cell border
|
||||
batch.color = colourTheme.cellHighlightNormalCol
|
||||
Toolkit.drawBoxBorder(batch, iconPosX - 4, getIconPosY(0) - 8, width, height)
|
||||
|
||||
if (hasGridModeButtons) gridModeButtons.forEach { it.render(batch, camera) }
|
||||
scrollUpButton.render(batch, camera)
|
||||
scrollDownButton.render(batch, camera)
|
||||
|
||||
// draw scroll dots
|
||||
for (i in 0 until itemPageCount) {
|
||||
val colour = if (i == itemPage) Color.WHITE else Color(0xffffff7f.toInt())
|
||||
|
||||
batch.color = colour
|
||||
batch.draw(
|
||||
catIcons.get(if (i == itemPage) 20 else 21, 0),
|
||||
iconPosX.toFloat(),
|
||||
getScrollDotYHeight(i) - 2f
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
extraDrawOpOnBottom(this, batch)
|
||||
|
||||
super.render(batch, camera)
|
||||
|
||||
oldPosX = posX
|
||||
}
|
||||
|
||||
override fun update(delta: Float) {
|
||||
|
||||
if (hasGridModeButtons) gridModeButtons.forEach { it.update(delta) }
|
||||
scrollUpButton.update(delta)
|
||||
scrollDownButton.update(delta)
|
||||
|
||||
super.update(delta)
|
||||
}
|
||||
|
||||
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
if (hasGridModeButtons) gridModeButtons.forEach { if (it.mouseUp) it.touchDown(screenX, screenY, pointer, button) }
|
||||
if (scrollUpButton.mouseUp) scrollUpButton.touchDown(screenX, screenY, pointer, button)
|
||||
if (scrollDownButton.mouseUp) scrollDownButton.touchDown(screenX, screenY, pointer, button)
|
||||
return super.touchDown(screenX, screenY, pointer, button)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.CommonResourcePool
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.serialise.WriteSavegame
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.terrarum.ui.UIItem
|
||||
import kotlin.math.roundToInt
|
||||
@@ -30,9 +31,10 @@ class UIItemSaving(parentUI: UICanvas, initialX: Int, initialY: Int) : UIItem(pa
|
||||
}
|
||||
|
||||
override fun render(batch: SpriteBatch, camera: Camera) {
|
||||
// these things will not scroll along with the parent GUI!
|
||||
val t = Lang["MENU_IO_SAVING"]
|
||||
val tlen = App.fontGame.getWidth(t)
|
||||
App.fontGame.draw(batch, t, (posX + (width - tlen) / 2).toFloat(), posY - 32f)
|
||||
App.fontGame.draw(batch, t, (posX + (width - tlen) / 2).toFloat(), ((App.scr.height - circleSheet.tileH) / 2) - 40f)
|
||||
|
||||
// -1..63
|
||||
val index = ((WriteSavegame.saveProgress / WriteSavegame.saveProgressMax) * circles).roundToInt() - 1
|
||||
@@ -41,7 +43,11 @@ class UIItemSaving(parentUI: UICanvas, initialX: Int, initialY: Int) : UIItem(pa
|
||||
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), (posX + (width - circleSheet.tileW) / 2).toFloat(), posY.toFloat())
|
||||
batch.draw(
|
||||
circleSheet.get(sx, sy),
|
||||
((Toolkit.drawWidth - circleSheet.tileW) / 2).toFloat(),
|
||||
((App.scr.height - circleSheet.tileH) / 2).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
keycaps.values.forEach { addUIitem(it) }
|
||||
updateKeycaps()
|
||||
|
||||
buttonReset.clickOnceListener = { x, y, button ->
|
||||
buttonReset.clickOnceListener = { x, y ->
|
||||
resetKeyConfig()
|
||||
updateKeycaps()
|
||||
}
|
||||
@@ -201,7 +201,9 @@ class UIKeyboardControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
// todo show "Keyboard"/"Gamepad" accordingly
|
||||
batch.color = Color.WHITE
|
||||
val title = Lang["MENU_CONTROLS_KEYBOARD"]
|
||||
App.fontGame.draw(batch, title, drawX.toFloat() + (width - App.fontGame.getWidth(title)) / 2, drawY.toFloat())
|
||||
App.fontUITitle.draw(batch, title, drawX.toFloat() + (width - App.fontUITitle.getWidth(title)) / 2, drawY.toFloat())
|
||||
|
||||
|
||||
|
||||
val desc = Lang["MENU_LABEL_KEYCONFIG_HELP1"]
|
||||
App.fontGame.draw(batch, desc, drawX.toFloat() + (width - App.fontGame.getWidth(desc)) / 2, drawY + 360f)
|
||||
|
||||
@@ -36,6 +36,8 @@ import net.torvald.terrarum.ui.Movement
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.terrarum.ui.UIItem
|
||||
import net.torvald.terrarum.utils.JsonFetcher
|
||||
import net.torvald.terrarum.utils.forEachSiblings
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||
import java.time.Instant
|
||||
import java.time.format.DateTimeFormatter
|
||||
@@ -55,6 +57,11 @@ val SAVE_CELL_HEIGHT = 120
|
||||
* WARNING: the values are not guaranteed to reset when the selector UI is closed!
|
||||
*/
|
||||
object UILoadGovernor {
|
||||
// used by the default save loader
|
||||
var playerUUID: UUID? = null
|
||||
var worldUUID: UUID? = null
|
||||
var previousSaveWasLoaded = false
|
||||
// used by the debug save loader
|
||||
var playerDisk: DiskSkimmer? = null
|
||||
set(value) {
|
||||
printdbg(this, "Player selected: ${value?.diskFile?.name}")
|
||||
@@ -71,15 +78,23 @@ object UILoadGovernor {
|
||||
printdbg(this, "Resetting player and world selection")
|
||||
playerDisk = null
|
||||
worldDisk = null
|
||||
|
||||
playerUUID = null
|
||||
worldUUID = null
|
||||
previousSaveWasLoaded = false
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Advanceable : UICanvas() {
|
||||
abstract fun advanceMode(button: UIItem)
|
||||
}
|
||||
|
||||
/**
|
||||
* Only works if current screen set by the App is [TitleScreen]
|
||||
*
|
||||
* Created by minjaesong on 2021-09-09.
|
||||
*/
|
||||
class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
||||
class UILoadDemoSavefiles(val remoCon: UIRemoCon) : Advanceable() {
|
||||
|
||||
// private val hash = RandomWordsName(3)
|
||||
|
||||
@@ -155,7 +170,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
||||
this.mode = mode
|
||||
}
|
||||
|
||||
fun advanceMode() {
|
||||
override fun advanceMode(button: UIItem) {
|
||||
mode += 1
|
||||
uiScroll = 0f
|
||||
scrollFrom = 0
|
||||
@@ -175,7 +190,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
||||
// read savegames
|
||||
var savegamesCount = 0
|
||||
App.sortedSavegameWorlds.forEach { uuid ->
|
||||
val skimmer = App.savegameWorlds[uuid]!!
|
||||
val skimmer = App.savegameWorlds[uuid]!!.loadable()
|
||||
val x = uiX
|
||||
val y = titleTopGradEnd + cellInterval * savegamesCount
|
||||
try {
|
||||
@@ -190,7 +205,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
||||
|
||||
savegamesCount = 0
|
||||
App.sortedPlayers.forEach { uuid ->
|
||||
val skimmer = App.savegamePlayers[uuid]!!
|
||||
val skimmer = App.savegamePlayers[uuid]!!.loadable()
|
||||
val x = uiX
|
||||
val y = titleTopGradEnd + cellInterval * savegamesCount
|
||||
try {
|
||||
@@ -374,7 +389,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
||||
// draw texts
|
||||
val loadGameTitleStr = Lang[titles[mode]]// + "$EMDASH$hash"
|
||||
// "Game Load"
|
||||
App.fontUITitle.draw(batch, loadGameTitleStr, (width - App.fontGame.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||
App.fontUITitle.draw(batch, loadGameTitleStr, (width - App.fontUITitle.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||
// Control help
|
||||
App.fontGame.draw(batch, controlHelp, uiX.toFloat(), controlHelperY.toFloat())
|
||||
}
|
||||
@@ -470,7 +485,7 @@ class UILoadDemoSavefiles(val remoCon: UIRemoCon) : UICanvas() {
|
||||
|
||||
|
||||
class UIItemPlayerCells(
|
||||
parent: UILoadDemoSavefiles,
|
||||
parent: Advanceable,
|
||||
initialX: Int,
|
||||
initialY: Int,
|
||||
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
|
||||
@@ -478,9 +493,11 @@ class UIItemPlayerCells(
|
||||
override val width = SAVE_CELL_WIDTH
|
||||
override val height = SAVE_CELL_HEIGHT
|
||||
|
||||
override var clickOnceListener: ((Int, Int, Int) -> Unit)? = { _: Int, _: Int, _: Int ->
|
||||
override var clickOnceListener: ((Int, Int) -> Unit) = { _: Int, _: Int ->
|
||||
UILoadGovernor.playerDisk = skimmer
|
||||
parent.advanceMode()
|
||||
UILoadGovernor.playerUUID = playerUUID
|
||||
UILoadGovernor.worldUUID = worldUUID
|
||||
parent.advanceMode(this)
|
||||
}
|
||||
|
||||
private var playerName: String = "$EMDASH"
|
||||
@@ -488,28 +505,25 @@ class UIItemPlayerCells(
|
||||
private var lastPlayTime: String = "????-??-?? --:--:--"
|
||||
private var totalPlayTime: String = "--h--m--s"
|
||||
|
||||
private var playerUUID: UUID? = null
|
||||
lateinit var playerUUID: UUID; private set
|
||||
lateinit var worldUUID: UUID; private set
|
||||
|
||||
init {
|
||||
skimmer.getFile(SAVEGAMEINFO)?.bytes?.let {
|
||||
val json = JsonReader().parse(ByteArray64Reader(it, Common.CHARSET))
|
||||
var lastPlayTime0 = 0L
|
||||
|
||||
playerUUID = UUID.fromString(json["uuid"]?.asString())
|
||||
val worldUUID = UUID.fromString(json["worldCurrentlyPlaying"]?.asString())
|
||||
JsonFetcher.readFromJsonString(ByteArray64Reader(it, Common.CHARSET)).forEachSiblings { name, value ->
|
||||
if (name == "uuid") playerUUID = UUID.fromString(value.asString())
|
||||
if (name == "worldCurrentlyPlaying") worldUUID = UUID.fromString(value.asString())
|
||||
if (name == "totalPlayTime") totalPlayTime = parseDuration(value.asLong())
|
||||
if (name == "lastPlayTime") lastPlayTime0 = value.asLong()
|
||||
}
|
||||
|
||||
App.savegamePlayersName[playerUUID]?.let { if (it.isNotBlank()) playerName = it else "(name)" }
|
||||
App.savegameWorldsName[worldUUID]?.let { if (it.isNotBlank()) worldName = it }
|
||||
/*json["lastPlayTime"]?.asString()?.let {
|
||||
lastPlayTime = Instant.ofEpochSecond(it.toLong())
|
||||
.atZone(TimeZone.getDefault().toZoneId())
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
}*/
|
||||
lastPlayTime = Instant.ofEpochSecond(skimmer.getLastModifiedTime())
|
||||
lastPlayTime = Instant.ofEpochSecond(lastPlayTime0)
|
||||
.atZone(TimeZone.getDefault().toZoneId())
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
json["totalPlayTime"]?.asString()?.let {
|
||||
totalPlayTime = parseDuration(it.toLong())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -541,10 +555,12 @@ class UIItemPlayerCells(
|
||||
private var highlightCol: Color = defaultCol
|
||||
private var highlightTextCol: Color = defaultCol
|
||||
|
||||
var forceMouseDown = false
|
||||
|
||||
override fun update(delta: Float) {
|
||||
super.update(delta)
|
||||
highlightCol = if (mouseUp) litCol else defaultCol
|
||||
highlightTextCol = if (mouseUp) litCol else Toolkit.Theme.COL_LIST_DEFAULT
|
||||
highlightCol = if (mouseUp && !forceMouseDown) litCol else defaultCol
|
||||
highlightTextCol = if (mouseUp && !forceMouseDown) litCol else Toolkit.Theme.COL_LIST_DEFAULT
|
||||
}
|
||||
|
||||
override fun render(batch: SpriteBatch, camera: Camera) {
|
||||
@@ -670,7 +686,7 @@ class UIItemPlayerCells(
|
||||
|
||||
|
||||
class UIItemWorldCells(
|
||||
parent: UILoadDemoSavefiles,
|
||||
parent: Advanceable,
|
||||
initialX: Int,
|
||||
initialY: Int,
|
||||
val skimmer: DiskSkimmer) : UIItem(parent, initialX, initialY) {
|
||||
@@ -685,9 +701,6 @@ class UIItemWorldCells(
|
||||
private val lastPlayedTimestamp: String
|
||||
|
||||
init {
|
||||
printdbg(this, "Rebuilding skimmer for savefile ${skimmer.diskFile.absolutePath}")
|
||||
skimmer.rebuild()
|
||||
|
||||
metaFile = skimmer.getFile(-1)
|
||||
if (metaFile == null) saveDamaged = true
|
||||
|
||||
@@ -699,14 +712,18 @@ class UIItemWorldCells(
|
||||
saveDamaged = saveDamaged or checkForSavegameDamage(skimmer)
|
||||
|
||||
if (metaFile != null) {
|
||||
val worldJson = JsonReader().parse(ByteArray64Reader(metaFile.bytes, Common.CHARSET))
|
||||
val lastplay_t = skimmer.getLastModifiedTime()//worldJson["lastPlayTime"].asLong()
|
||||
val playtime_t = worldJson["totalPlayTime"].asLong()
|
||||
// val lastplay_t = skimmer.getLastModifiedTime()//worldJson["lastPlayTime"].asLong()
|
||||
var playtime_t = ""
|
||||
var lastplay_t = 0L
|
||||
JsonFetcher.readFromJsonString(ByteArray64Reader(metaFile.bytes, Common.CHARSET)).forEachSiblings { name, value ->
|
||||
if (name == "lastPlayTime") lastplay_t = value.asLong()
|
||||
if (name == "totalPlayTime") playtime_t = parseDuration(value.asLong())
|
||||
}
|
||||
lastPlayedTimestamp =
|
||||
Instant.ofEpochSecond(lastplay_t)
|
||||
.atZone(TimeZone.getDefault().toZoneId())
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) +
|
||||
"/${parseDuration(playtime_t)}"
|
||||
"/$playtime_t"
|
||||
}
|
||||
else {
|
||||
lastPlayedTimestamp = "--:--:--/--h--m--s"
|
||||
@@ -739,9 +756,9 @@ class UIItemWorldCells(
|
||||
|
||||
private var highlightCol: Color = Toolkit.Theme.COL_LIST_DEFAULT
|
||||
|
||||
override var clickOnceListener: ((Int, Int, Int) -> Unit)? = { _: Int, _: Int, _: Int ->
|
||||
override var clickOnceListener: ((Int, Int) -> Unit) = { _: Int, _: Int ->
|
||||
UILoadGovernor.worldDisk = skimmer
|
||||
parent.advanceMode()
|
||||
parent.advanceMode(this)
|
||||
}
|
||||
|
||||
internal var hasTexture = false
|
||||
|
||||
709
src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt
Normal file
709
src/net/torvald/terrarum/modulebasegame/ui/UILoadSavegame.kt
Normal file
@@ -0,0 +1,709 @@
|
||||
package net.torvald.terrarum.modulebasegame.ui
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.graphics.*
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import com.badlogic.gdx.graphics.glutils.FrameBuffer
|
||||
import com.badlogic.gdx.utils.Disposable
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import net.torvald.unicode.getKeycapConsole
|
||||
import net.torvald.unicode.getKeycapPC
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.savegame.ByteArray64InputStream
|
||||
import net.torvald.terrarum.savegame.EntryFile
|
||||
import net.torvald.terrarum.modulebasegame.serialise.LoadSavegame
|
||||
import net.torvald.terrarum.savegame.VDFileID.PLAYER_SCREENSHOT
|
||||
import net.torvald.terrarum.ui.*
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||
import java.time.Instant
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
import java.util.zip.GZIPInputStream
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
/**
|
||||
* Only works if current screen set by the App is [TitleScreen]
|
||||
*
|
||||
* Created by minjaesong on 2023-06-24.
|
||||
*/
|
||||
class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
||||
|
||||
// private val hash = RandomWordsName(3)
|
||||
|
||||
init {
|
||||
CommonResourcePool.addToLoadingList("terrarum-defaultsavegamethumb") {
|
||||
TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/savegame_thumb_placeholder.png")))
|
||||
}
|
||||
CommonResourcePool.addToLoadingList("savegame_status_icon") {
|
||||
TextureRegionPack("assets/graphics/gui/savegame_status_icon.tga", 24, 24)
|
||||
}
|
||||
CommonResourcePool.loadAll()
|
||||
}
|
||||
|
||||
override var width: Int
|
||||
get() = Toolkit.drawWidth
|
||||
set(value) {}
|
||||
override var height: Int
|
||||
get() = App.scr.height
|
||||
set(value) {}
|
||||
override var openCloseTime: Second = OPENCLOSE_GENERIC
|
||||
|
||||
|
||||
private val shapeRenderer = App.makeShapeRenderer()
|
||||
|
||||
|
||||
internal val uiWidth = SAVE_CELL_WIDTH
|
||||
internal val uiX: Int
|
||||
get() = (Toolkit.drawWidth - uiWidth) / 2
|
||||
internal val uiXdiffChatOverlay = App.scr.chatWidth / 2
|
||||
|
||||
internal val textH = App.fontGame.lineHeight.toInt()
|
||||
|
||||
internal val cellGap = 20
|
||||
internal val cellInterval = cellGap + SAVE_CELL_HEIGHT
|
||||
internal val gradAreaHeight = 32
|
||||
|
||||
// internal val titleTextPosY: Int = App.scr.tvSafeGraphicsHeight + 10
|
||||
internal val titleTopGradStart: Int = App.scr.tvSafeGraphicsHeight
|
||||
internal val titleTopGradEnd: Int = titleTopGradStart + gradAreaHeight
|
||||
internal val titleBottomGradStart: Int = height - App.scr.tvSafeGraphicsHeight - gradAreaHeight
|
||||
internal val titleBottomGradEnd: Int = titleBottomGradStart + gradAreaHeight
|
||||
internal val controlHelperY: Int = titleBottomGradStart + gradAreaHeight - textH
|
||||
|
||||
|
||||
private val controlHelp: String
|
||||
get() = if (App.environment == RunningEnvironment.PC)
|
||||
"${getKeycapPC(App.getConfigInt("control_key_up"))}${getKeycapPC(App.getConfigInt("control_key_down"))}" +
|
||||
" ${Lang["MENU_CONTROLS_SCROLL"]}"
|
||||
else
|
||||
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}"
|
||||
|
||||
|
||||
private var scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
|
||||
private var listScroll = 0 // only update when animation is finished
|
||||
private var savesVisible = (scrollAreaHeight + cellGap) / cellInterval
|
||||
|
||||
private var uiScroll = 0f
|
||||
private var scrollFrom = 0
|
||||
private var scrollTarget = 0
|
||||
private var scrollAnimCounter = 0f
|
||||
private val scrollAnimLen = 0.1f
|
||||
|
||||
private var sliderFBO = FrameBuffer(Pixmap.Format.RGBA8888, uiWidth + 10, height, false)
|
||||
|
||||
private var showSpinner = false
|
||||
|
||||
private val playerCells = ArrayList<UIItemPlayerCells>()
|
||||
|
||||
var mode = 0 // 0: show players, 1: show worlds
|
||||
private set(value) {
|
||||
touchLatched = true
|
||||
field = value
|
||||
}
|
||||
|
||||
private val MODE_SELECT = 0
|
||||
private val MODE_SELECT_AFTER = 1
|
||||
private val MODE_SAVE_MULTIPLE_CHOICES = 2
|
||||
private val MODE_LOAD_DA_SHIT_ALREADY = 255
|
||||
private val MODE_SAVE_DAMAGED = 256
|
||||
private val MODE_SAVE_DELETE = 512
|
||||
private val MODE_SAVE_DELETE_CONFIRM = 513
|
||||
|
||||
private var buttonSelectedForDeletion: UIItemPlayerCells? = null
|
||||
|
||||
private val goButtonWidth = 180
|
||||
private val drawX = (Toolkit.drawWidth - 480) / 2
|
||||
private val drawY = (App.scr.height - 480) / 2
|
||||
private val corruptedBackButton = UIItemTextButton(this, "MENU_LABEL_BACK", (Toolkit.drawWidth - goButtonWidth) / 2, drawY + 480 - 24, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
||||
private val confirmCancelButton = UIItemTextButton(this, "MENU_LABEL_CANCEL", drawX + (240 - goButtonWidth) / 2, drawY + 480 - 24, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
||||
private val confirmDeleteButton = UIItemTextButton(this, "MENU_LABEL_DELETE", drawX + 240 + (240 - goButtonWidth) / 2, drawY + 480- 24, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true, inactiveCol = Toolkit.Theme.COL_RED, activeCol = Toolkit.Theme.COL_REDD)
|
||||
|
||||
private lateinit var loadables: SavegameCollectionPair
|
||||
|
||||
private lateinit var loadManualThumbButton: UIItemImageButton
|
||||
private lateinit var loadAutoThumbButton: UIItemImageButton
|
||||
|
||||
private val disposablePool = ArrayList<Disposable>()
|
||||
|
||||
private fun DiskPair.getThumbnail(): TextureRegion {
|
||||
return this.player.requestFile(PLAYER_SCREENSHOT).let { file ->
|
||||
CommonResourcePool.getAsTextureRegion("terrarum-defaultsavegamethumb")
|
||||
|
||||
if (file != null) {
|
||||
val zippedTga = (file.contents as EntryFile).bytes
|
||||
val gzin = GZIPInputStream(ByteArray64InputStream(zippedTga))
|
||||
val tgaFileContents = gzin.readAllBytes(); gzin.close()
|
||||
val pixmap = Pixmap(tgaFileContents, 0, tgaFileContents.size)
|
||||
TextureRegion(Texture(pixmap)).also {
|
||||
disposablePool.add(it.texture)
|
||||
// do cropping and resizing
|
||||
it.setRegion(
|
||||
(pixmap.width - imageButtonW*2) / 2,
|
||||
(pixmap.height - imageButtonH*2) / 2,
|
||||
imageButtonW * 2,
|
||||
imageButtonH * 2
|
||||
)
|
||||
}
|
||||
}
|
||||
else {
|
||||
CommonResourcePool.getAsTextureRegion("terrarum-defaultsavegamethumb")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val altSelDrawW = 640
|
||||
private val altSelHdrawW = altSelDrawW / 2
|
||||
private val altSelDrawH = 480
|
||||
private val imageButtonW = 300
|
||||
private val imageButtonH = 240
|
||||
private val altSelDrawY = ((App.scr.height - altSelDrawH)/2)
|
||||
private val altSelQdrawW = altSelDrawW / 4
|
||||
private val altSelQQQdrawW = altSelDrawW * 3 / 4
|
||||
|
||||
private fun getDrawTextualInfoFun(disks: DiskPair): (UIItem, SpriteBatch) -> Unit {
|
||||
val lastPlayedStamp = Instant.ofEpochSecond(disks.player.getLastModifiedTime())
|
||||
.atZone(TimeZone.getDefault().toZoneId())
|
||||
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
|
||||
return { item: UIItem, batch: SpriteBatch ->
|
||||
App.fontSmallNumbers.draw(batch, lastPlayedStamp, item.posX + 5f, item.posY + 3f)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
corruptedBackButton.clickOnceListener = { _,_ -> remoCon.openUI(UILoadSavegame(remoCon)) }
|
||||
confirmCancelButton.clickOnceListener = { _, _ -> remoCon.openUI(UILoadSavegame(remoCon)) }
|
||||
confirmDeleteButton.clickOnceListener = { _,_ ->
|
||||
val pu = buttonSelectedForDeletion!!.playerUUID
|
||||
val wu = buttonSelectedForDeletion!!.worldUUID
|
||||
App.savegamePlayers[pu]?.moveToRecycle(App.recycledPlayersDir)?.let {
|
||||
App.sortedPlayers.remove(pu)
|
||||
App.savegamePlayers.remove(pu)
|
||||
App.savegamePlayersName.remove(pu)
|
||||
}
|
||||
// don't delete the world please
|
||||
remoCon.openUI(UILoadSavegame(remoCon))
|
||||
}
|
||||
}
|
||||
|
||||
override fun advanceMode(button: UIItem) {
|
||||
printdbg(this, "advanceMode ${button.javaClass.canonicalName}")
|
||||
|
||||
mode += 1
|
||||
uiScroll = 0f
|
||||
scrollFrom = 0
|
||||
scrollTarget = 0
|
||||
scrollAnimCounter = 0f
|
||||
loadFired = 0
|
||||
|
||||
printdbg(this, "savelist mode: $mode")
|
||||
|
||||
// look for recently played world
|
||||
if (mode == MODE_SELECT_AFTER) {
|
||||
|
||||
// select the most recent loadable save by comparing manual and autosaves, NOT JUST going with loadable()
|
||||
printdbg(this, "Load playerUUID: ${UILoadGovernor.playerUUID}, worldUUID: ${UILoadGovernor.worldUUID}")
|
||||
loadables = SavegameCollectionPair(App.savegamePlayers[UILoadGovernor.playerUUID], App.savegameWorlds[UILoadGovernor.worldUUID])
|
||||
|
||||
mode = if (loadables.moreRecentAutosaveAvailable()) {
|
||||
// make choice for load manual or auto, if available
|
||||
|
||||
val autoThumb = loadables.getAutoSave()!!.getThumbnail()
|
||||
val manualThumb = loadables.getManualSave()!!.getThumbnail()
|
||||
|
||||
loadManualThumbButton = UIItemImageButton(this, manualThumb,
|
||||
initialX = (Toolkit.drawWidth - altSelDrawW)/2 + altSelQdrawW - imageButtonW/2,
|
||||
initialY = altSelDrawY + 120,
|
||||
width = imageButtonW,
|
||||
height = imageButtonH,
|
||||
imageDrawWidth = imageButtonW,
|
||||
imageDrawHeight = imageButtonH,
|
||||
highlightable = false,
|
||||
useBorder = true,
|
||||
).also {
|
||||
it.extraDrawOp = getDrawTextualInfoFun(loadables.getManualSave()!!)
|
||||
it.clickOnceListener = { _,_ ->
|
||||
loadables.getManualSave()!!.let {
|
||||
UILoadGovernor.playerDisk = it.player
|
||||
UILoadGovernor.worldDisk = it.world
|
||||
}
|
||||
mode = MODE_LOAD_DA_SHIT_ALREADY
|
||||
}
|
||||
}
|
||||
loadAutoThumbButton = UIItemImageButton(this, autoThumb,
|
||||
initialX = (Toolkit.drawWidth - altSelDrawW)/2 + altSelQQQdrawW - imageButtonW/2,
|
||||
initialY = altSelDrawY + 120,
|
||||
width = imageButtonW,
|
||||
height = imageButtonH,
|
||||
imageDrawWidth = imageButtonW,
|
||||
imageDrawHeight = imageButtonH,
|
||||
highlightable = false,
|
||||
useBorder = true,
|
||||
).also {
|
||||
it.extraDrawOp = getDrawTextualInfoFun(loadables.getAutoSave()!!)
|
||||
it.clickOnceListener = { _,_ ->
|
||||
loadables.getAutoSave()!!.let {
|
||||
UILoadGovernor.playerDisk = it.player
|
||||
UILoadGovernor.worldDisk = it.world
|
||||
}
|
||||
mode = MODE_LOAD_DA_SHIT_ALREADY
|
||||
}
|
||||
}
|
||||
|
||||
MODE_SAVE_MULTIPLE_CHOICES
|
||||
}
|
||||
else if (!loadables.saveAvaliable()) {
|
||||
// show save is damaged and cannot be loaded
|
||||
MODE_SAVE_DAMAGED
|
||||
}
|
||||
else {
|
||||
val (p, w) = loadables.getLoadableSave()!!
|
||||
UILoadGovernor.playerDisk = p; UILoadGovernor.worldDisk = w
|
||||
|
||||
if (loadables.newerSaveIsDamaged) {
|
||||
UILoadGovernor.previousSaveWasLoaded = true
|
||||
}
|
||||
|
||||
MODE_LOAD_DA_SHIT_ALREADY
|
||||
|
||||
|
||||
// test codes //
|
||||
|
||||
/*val autoThumb = loadables.getManualSave()!!.getThumbnail()
|
||||
val manualThumb = loadables.getManualSave()!!.getThumbnail()
|
||||
|
||||
loadManualThumbButton = UIItemImageButton(this, manualThumb,
|
||||
initialX = (Toolkit.drawWidth - altSelDrawW)/2 + altSelQdrawW - imageButtonW/2,
|
||||
initialY = altSelDrawY + 120,
|
||||
width = imageButtonW,
|
||||
height = imageButtonH,
|
||||
imageDrawWidth = imageButtonW,
|
||||
imageDrawHeight = imageButtonH,
|
||||
highlightable = false,
|
||||
useBorder = true,
|
||||
).also {
|
||||
it.extraDrawOp = getDrawTextualInfoFun(loadables.getManualSave()!!)
|
||||
it.clickOnceListener = { _,_ ->
|
||||
loadables.getManualSave()!!.let {
|
||||
UILoadGovernor.playerDisk = it.player
|
||||
UILoadGovernor.worldDisk = it.world
|
||||
}
|
||||
mode = MODE_LOAD_DA_SHIT_ALREADY
|
||||
}
|
||||
}
|
||||
loadAutoThumbButton = UIItemImageButton(this, autoThumb,
|
||||
initialX = (Toolkit.drawWidth - altSelDrawW)/2 + altSelQQQdrawW - imageButtonW/2,
|
||||
initialY = altSelDrawY + 120,
|
||||
width = imageButtonW,
|
||||
height = imageButtonH,
|
||||
imageDrawWidth = imageButtonW,
|
||||
imageDrawHeight = imageButtonH,
|
||||
highlightable = false,
|
||||
useBorder = true,
|
||||
).also {
|
||||
it.extraDrawOp = getDrawTextualInfoFun(loadables.getManualSave()!!)
|
||||
it.clickOnceListener = { _,_ ->
|
||||
loadables.getManualSave()!!.let {
|
||||
UILoadGovernor.playerDisk = it.player
|
||||
UILoadGovernor.worldDisk = it.world
|
||||
}
|
||||
mode = MODE_LOAD_DA_SHIT_ALREADY
|
||||
}
|
||||
}
|
||||
|
||||
MODE_SAVE_MULTIPLE_CHOICES*/
|
||||
}
|
||||
|
||||
printdbg(this, "mode = $mode")
|
||||
}
|
||||
else if (mode == MODE_SAVE_DELETE_CONFIRM) {
|
||||
// confirm deletion of selected player
|
||||
buttonSelectedForDeletion = (button as UIItemPlayerCells).also {
|
||||
deleteCellPosYstart = it.posY.toFloat()
|
||||
it.forceMouseDown = true
|
||||
it.update(0.01f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
try {
|
||||
remoCon.handler.lockToggle()
|
||||
showSpinner = true
|
||||
|
||||
Thread {
|
||||
// read savegames
|
||||
var savegamesCount = 0
|
||||
App.sortedPlayers.forEach { uuid ->
|
||||
val skimmer = App.savegamePlayers[uuid]!!.loadable()
|
||||
val x = uiX
|
||||
val y = titleTopGradEnd + cellInterval * savegamesCount
|
||||
try {
|
||||
playerCells.add(UIItemPlayerCells(this, x, y, skimmer))
|
||||
savegamesCount += 1
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
System.err.println("[UILoadSavegame] Error while loading Player '${skimmer.diskFile.absolutePath}'")
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remoCon.handler.unlockToggle()
|
||||
showSpinner = false
|
||||
}.start()
|
||||
|
||||
}
|
||||
catch (e: UninitializedPropertyAccessException) {}
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
playerCells.forEach { it.dispose() }
|
||||
playerCells.clear()
|
||||
}
|
||||
|
||||
private var touchLatched = false
|
||||
|
||||
private fun getCells() = playerCells
|
||||
private var loadFired = 0
|
||||
private var oldMode = -1
|
||||
|
||||
private val mode1Node = Yaml(UITitleRemoConYaml.injectedMenuSingleCharSel).parse()
|
||||
// private val mode2Node = Yaml(UITitleRemoConYaml.injectedMenuSingleWorldSel).parse()
|
||||
|
||||
// private val menus = listOf(mode1Node, mode2Node)
|
||||
|
||||
private val deleteCharacterButton = UIItemTextButton(
|
||||
this, "CONTEXT_CHARACTER_DELETE",
|
||||
UIRemoCon.menubarOffX - UIRemoCon.UIRemoConElement.paddingLeft + 72,
|
||||
UIRemoCon.menubarOffY - UIRemoCon.UIRemoConElement.lineHeight * 3 + 16,
|
||||
remoCon.width + UIRemoCon.UIRemoConElement.paddingLeft,
|
||||
true,
|
||||
inactiveCol = Toolkit.Theme.COL_RED,
|
||||
activeCol = Toolkit.Theme.COL_REDD,
|
||||
hitboxSize = UIRemoCon.UIRemoConElement.lineHeight - 2,
|
||||
alignment = UIItemTextButton.Companion.Alignment.LEFT
|
||||
).also {
|
||||
it.clickOnceListener = { _,_ ->
|
||||
mode = MODE_SAVE_DELETE
|
||||
it.highlighted = true
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
// this UI will NOT persist; the parent of the mode1Node must be set using an absolute value (e.g. treeRoot, not remoCon.currentRemoConContents)
|
||||
|
||||
//printdbg(this, "UILoadSavegame called, from:")
|
||||
//printStackTrace(this)
|
||||
|
||||
mode1Node.parent = remoCon.treeRoot
|
||||
// mode2Node.parent = mode1Node
|
||||
|
||||
mode1Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame"
|
||||
// mode2Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame"
|
||||
|
||||
// printdbg(this, "mode1Node parent: ${mode1Node.parent?.data}") // will be 'null' because the parent is the root node
|
||||
// printdbg(this, "mode1Node data: ${mode1Node.data}")
|
||||
// printdbg(this, "mode2Node data: ${mode2Node.data}")
|
||||
|
||||
}
|
||||
|
||||
private fun modeChangedHandler(mode: Int) {
|
||||
printdbg(this, "Change mode: $oldMode -> $mode")
|
||||
// remoCon.setNewRemoConContents(menus[mode])
|
||||
remoCon.setNewRemoConContents(mode1Node)
|
||||
}
|
||||
|
||||
override fun updateUI(delta: Float) {
|
||||
if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||
|
||||
if (oldMode != mode) {
|
||||
modeChangedHandler(mode)
|
||||
oldMode = mode
|
||||
}
|
||||
|
||||
if (scrollTarget != listScroll) {
|
||||
if (scrollAnimCounter < scrollAnimLen) {
|
||||
scrollAnimCounter += delta
|
||||
uiScroll = Movement.fastPullOut(
|
||||
scrollAnimCounter / scrollAnimLen,
|
||||
listScroll * cellInterval.toFloat(),
|
||||
scrollTarget * cellInterval.toFloat()
|
||||
)
|
||||
}
|
||||
else {
|
||||
scrollAnimCounter = 0f
|
||||
listScroll = scrollTarget
|
||||
uiScroll = cellInterval.toFloat() * scrollTarget
|
||||
}
|
||||
}
|
||||
|
||||
val cells = getCells()
|
||||
|
||||
for (index in 0 until cells.size) {
|
||||
|
||||
|
||||
val it = cells[index]
|
||||
if (index in listScroll - 2 until listScroll + savesVisible + 2) {
|
||||
// re-position
|
||||
it.posY = (it.initialY - uiScroll).roundToInt()
|
||||
it.update(delta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == MODE_SAVE_DELETE_CONFIRM && deleteCellAnimCounter <= scrollAnimLen) {
|
||||
// do transitional moving stuff
|
||||
buttonSelectedForDeletion?.posY = Movement.fastPullOut(deleteCellAnimCounter / scrollAnimLen, deleteCellPosYstart, (titleTopGradEnd + cellInterval).toFloat()).roundToInt()
|
||||
|
||||
deleteCellAnimCounter += delta
|
||||
if (deleteCellAnimCounter > scrollAnimLen) deleteCellAnimCounter = scrollAnimLen
|
||||
}
|
||||
}
|
||||
|
||||
private var deleteCellAnimCounter = 0f
|
||||
private var deleteCellPosYstart = 0f
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
|
||||
if (mode == MODE_LOAD_DA_SHIT_ALREADY) {
|
||||
loadFired += 1
|
||||
// to hide the "flipped skybox" artefact
|
||||
batch.end()
|
||||
|
||||
gdxClearAndEnableBlend(.094f, .094f, .094f, 0f)
|
||||
|
||||
batch.begin()
|
||||
|
||||
batch.color = Color.WHITE
|
||||
val txt = Lang["MENU_IO_LOADING"]
|
||||
App.fontGame.draw(batch, txt, (App.scr.width - App.fontGame.getWidth(txt)) / 2f, (App.scr.height - App.fontGame.lineHeight) / 2f)
|
||||
|
||||
if (loadFired == 2) {
|
||||
LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk)
|
||||
}
|
||||
}
|
||||
else if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||
batch.end()
|
||||
|
||||
val cells = getCells()
|
||||
|
||||
lateinit var savePixmap: Pixmap
|
||||
sliderFBO.inAction(camera as OrthographicCamera, batch) {
|
||||
gdxClearAndEnableBlend(0f, 0f, 0f, 0f)
|
||||
|
||||
setCameraPosition(batch, camera, 0f, 0f)
|
||||
batch.color = Color.WHITE
|
||||
batch.inUse {
|
||||
for (index in 0 until cells.size) {
|
||||
val it = cells[index]
|
||||
|
||||
if (App.getConfigBoolean("fx_streamerslayout"))
|
||||
it.posX += uiXdiffChatOverlay
|
||||
|
||||
if (index in listScroll - 2 until listScroll + savesVisible + 2)
|
||||
it.render(batch, camera)
|
||||
|
||||
if (App.getConfigBoolean("fx_streamerslayout"))
|
||||
it.posX -= uiXdiffChatOverlay
|
||||
}
|
||||
}
|
||||
savePixmap = Pixmap.createFromFrameBuffer(0, 0, sliderFBO.width, sliderFBO.height)
|
||||
savePixmap.blending = Pixmap.Blending.None
|
||||
}
|
||||
|
||||
|
||||
// implement "wipe-out" by CPU-rendering (*deep exhale*)
|
||||
//savePixmap.setColor(1f,1f,1f,0f)
|
||||
savePixmap.setColor(0f, 0f, 0f, 0f)
|
||||
savePixmap.fillRectangle(0, savePixmap.height - titleTopGradStart, savePixmap.width, titleTopGradStart)
|
||||
// top grad
|
||||
for (y in titleTopGradStart until titleTopGradEnd) {
|
||||
val alpha = (y - titleTopGradStart).toFloat() / gradAreaHeight
|
||||
for (x in 0 until savePixmap.width) {
|
||||
val col = savePixmap.getPixel(x, savePixmap.height - y)
|
||||
val blendAlpha = (col.and(0xFF) * alpha).roundToInt()
|
||||
savePixmap.drawPixel(x, savePixmap.height - y, col.and(0xFFFFFF00.toInt()) or blendAlpha)
|
||||
}
|
||||
}
|
||||
// bottom grad
|
||||
for (y in titleBottomGradStart until titleBottomGradEnd) {
|
||||
val alpha = 1f - ((y - titleBottomGradStart).toFloat() / gradAreaHeight)
|
||||
for (x in 0 until savePixmap.width) {
|
||||
val col = savePixmap.getPixel(x, savePixmap.height - y)
|
||||
val blendAlpha = (col.and(0xFF) * alpha).roundToInt()
|
||||
savePixmap.drawPixel(x, savePixmap.height - y, col.and(0xFFFFFF00.toInt()) or blendAlpha)
|
||||
}
|
||||
}
|
||||
savePixmap.setColor(0f, 0f, 0f, 0f)
|
||||
savePixmap.fillRectangle(0, 0, savePixmap.width, height - titleBottomGradEnd + 1)
|
||||
|
||||
|
||||
|
||||
setCameraPosition(batch, camera, 0f, 0f)
|
||||
val saveTex = TextureRegion(Texture(savePixmap)); saveTex.flip(false, true)
|
||||
batch.inUse {
|
||||
batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f)
|
||||
// Control help
|
||||
App.fontGame.draw(batch, controlHelp, uiX.toFloat(), controlHelperY.toFloat())
|
||||
}
|
||||
|
||||
saveTex.texture.dispose()
|
||||
savePixmap.dispose()
|
||||
|
||||
batch.begin()
|
||||
}
|
||||
else if (mode == MODE_SAVE_MULTIPLE_CHOICES) {
|
||||
// "The Autosave is more recent than the manual save"
|
||||
Toolkit.drawTextCentered(batch, App.fontGame, Lang["GAME_MORE_RECENT_AUTOSAVE1"], Toolkit.drawWidth, 0, altSelDrawY)
|
||||
Toolkit.drawTextCentered(batch, App.fontGame, Lang["GAME_MORE_RECENT_AUTOSAVE2"], Toolkit.drawWidth, 0, altSelDrawY + 24)
|
||||
// Manual Save Autosave
|
||||
Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_IO_MANUAL_SAVE"], altSelHdrawW, (Toolkit.drawWidth - altSelDrawW)/2, altSelDrawY + 80)
|
||||
Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_IO_AUTOSAVE"], altSelHdrawW, Toolkit.drawWidth/2, altSelDrawY + 80)
|
||||
|
||||
|
||||
// draw thumbnail-buttons
|
||||
loadAutoThumbButton.render(batch, camera)
|
||||
loadManualThumbButton.render(batch, camera)
|
||||
}
|
||||
else if (mode == MODE_SAVE_DAMAGED) {
|
||||
Toolkit.drawTextCentered(batch, App.fontGame, Lang["ERROR_SAVE_CORRUPTED"], Toolkit.drawWidth, 0, App.scr.height / 2 - 42)
|
||||
|
||||
corruptedBackButton.render(batch, camera)
|
||||
}
|
||||
|
||||
|
||||
if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE || mode == MODE_SAVE_DELETE_CONFIRM) {
|
||||
deleteCharacterButton.render(batch, camera)
|
||||
}
|
||||
if (mode == MODE_SAVE_DELETE_CONFIRM) {
|
||||
buttonSelectedForDeletion?.render(batch, camera)
|
||||
confirmCancelButton.render(batch, camera)
|
||||
confirmDeleteButton.render(batch, camera)
|
||||
}
|
||||
|
||||
if (mode == MODE_SAVE_DELETE_CONFIRM) {
|
||||
batch.color = Color.WHITE
|
||||
Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_SAVE_WILL_BE_DELETED"], Toolkit.drawWidth, 0, titleTopGradEnd + cellInterval - 46)
|
||||
Toolkit.drawTextCentered(batch, App.fontGame, Lang["MENU_LABEL_ARE_YOU_SURE"], Toolkit.drawWidth, 0, titleTopGradEnd + cellInterval + SAVE_CELL_HEIGHT + 36)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
override fun keyDown(keycode: Int): Boolean {
|
||||
if (this.isVisible && (mode == MODE_SELECT || mode == MODE_SAVE_DELETE)) {
|
||||
val cells = getCells()
|
||||
|
||||
if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) {
|
||||
scrollFrom = listScroll
|
||||
scrollTarget -= 1
|
||||
scrollAnimCounter = 0f
|
||||
}
|
||||
else if ((keycode == Input.Keys.DOWN || keycode == App.getConfigInt("control_key_down")) && scrollTarget < cells.size - savesVisible) {
|
||||
scrollFrom = listScroll
|
||||
scrollTarget += 1
|
||||
scrollAnimCounter = 0f
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
printdbg(this, "touchDown mode=$mode")
|
||||
|
||||
if (mode == MODE_SAVE_MULTIPLE_CHOICES) {
|
||||
if (::loadAutoThumbButton.isInitialized) loadAutoThumbButton.touchDown(screenX, screenY, pointer, button)
|
||||
if (::loadManualThumbButton.isInitialized) loadManualThumbButton.touchDown(screenX, screenY, pointer, button)
|
||||
}
|
||||
else if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||
getCells().forEach { it.touchDown(screenX, screenY, pointer, button) }
|
||||
deleteCharacterButton.touchDown(screenX, screenY, pointer, button)
|
||||
}
|
||||
else if (mode == MODE_SAVE_DELETE_CONFIRM) {
|
||||
confirmCancelButton.touchDown(screenX, screenY, pointer, button)
|
||||
confirmDeleteButton.touchDown(screenX, screenY, pointer, button)
|
||||
}
|
||||
else if (mode == MODE_SAVE_DAMAGED) corruptedBackButton.touchDown(screenX, screenY, pointer, button)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
if (mode == MODE_SAVE_MULTIPLE_CHOICES) {
|
||||
if (::loadAutoThumbButton.isInitialized) loadAutoThumbButton.touchUp(screenX, screenY, pointer, button)
|
||||
if (::loadManualThumbButton.isInitialized) loadManualThumbButton.touchUp(screenX, screenY, pointer, button)
|
||||
}
|
||||
else if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||
getCells().forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||
deleteCharacterButton.touchUp(screenX, screenY, pointer, button)
|
||||
}
|
||||
else if (mode == MODE_SAVE_DELETE_CONFIRM) {
|
||||
confirmCancelButton.touchUp(screenX, screenY, pointer, button)
|
||||
confirmDeleteButton.touchUp(screenX, screenY, pointer, button)
|
||||
}
|
||||
else if (mode == MODE_SAVE_DAMAGED) corruptedBackButton.touchUp(screenX, screenY, pointer, button)
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||
if (this.isVisible && mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||
val cells = getCells()
|
||||
|
||||
if (amountY <= -1f && scrollTarget > 0) {
|
||||
scrollFrom = listScroll
|
||||
scrollTarget -= 1
|
||||
scrollAnimCounter = 0f
|
||||
}
|
||||
else if (amountY >= 1f && scrollTarget < cells.size - savesVisible) {
|
||||
scrollFrom = listScroll
|
||||
scrollTarget += 1
|
||||
scrollAnimCounter = 0f
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun endClosing(delta: Float) {
|
||||
super.endClosing(delta)
|
||||
listScroll = 0
|
||||
scrollTarget = 0
|
||||
uiScroll = 0f
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
try { shapeRenderer.dispose() } catch (e: IllegalArgumentException) {}
|
||||
try { sliderFBO.dispose() } catch (e: IllegalArgumentException) {}
|
||||
disposablePool.forEach {
|
||||
try { it.dispose() } catch (e: GdxRuntimeException) {}
|
||||
}
|
||||
}
|
||||
|
||||
override fun resize(width: Int, height: Int) {
|
||||
super.resize(width, height)
|
||||
scrollAreaHeight = height - 2 * App.scr.tvSafeGraphicsHeight - 64
|
||||
savesVisible = (scrollAreaHeight + cellInterval) / (cellInterval + SAVE_CELL_HEIGHT)
|
||||
|
||||
listScroll = 0
|
||||
scrollTarget = 0
|
||||
uiScroll = 0f
|
||||
|
||||
sliderFBO.dispose()
|
||||
sliderFBO = FrameBuffer(Pixmap.Format.RGBA8888, uiWidth + 10, height, false)
|
||||
}
|
||||
|
||||
private fun setCameraPosition(batch: SpriteBatch, camera: Camera, newX: Float, newY: Float) {
|
||||
camera.position.set((-newX + App.scr.halfw).round(), (-newY + App.scr.halfh).round(), 0f)
|
||||
camera.update()
|
||||
batch.projectionMatrix = camera.combined
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.Second
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.gameactors.AVKey
|
||||
@@ -47,7 +48,7 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() {
|
||||
private var uiLocked = false
|
||||
|
||||
init {
|
||||
goButton.touchDownListener = { _, _, _, _ ->
|
||||
goButton.clickOnceListener = { _,_ ->
|
||||
uiLocked = true
|
||||
|
||||
|
||||
@@ -64,6 +65,7 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() {
|
||||
|
||||
|
||||
val savingThread = Thread({
|
||||
printdbg(this, "Player saving thread fired")
|
||||
|
||||
disk.saveMode = 2 // auto, no quick
|
||||
disk.capacity = 0L
|
||||
@@ -79,12 +81,17 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() {
|
||||
UILoadGovernor.playerDisk = DiskSkimmer(outFile)
|
||||
// comment above if chargen must send gamers back to the charcters list
|
||||
|
||||
printdbg(this, "playerdisk: ${UILoadGovernor.playerDisk?.diskFile?.path}")
|
||||
|
||||
}, "TerrarumBasegameNewCharcterSaveThread")
|
||||
savingThread.start()
|
||||
|
||||
// savingThread.start()
|
||||
// savingThread.join()
|
||||
|
||||
remoCon.openUI(UINewWorld(remoCon, savingThread)) // let UINewWorld handle the character file generation
|
||||
}
|
||||
backButton.touchDownListener = { _, _, _, _ ->
|
||||
remoCon.openUI(UILoadDemoSavefiles(remoCon, 0))
|
||||
backButton.clickOnceListener = { _,_ ->
|
||||
remoCon.openUI(UILoadSavegame(remoCon))
|
||||
}
|
||||
|
||||
addUIitem(nameInput)
|
||||
@@ -99,7 +106,7 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() {
|
||||
|
||||
if (returnedFromChargen) {
|
||||
returnedFromChargen = false
|
||||
remoCon.openUI(UILoadDemoSavefiles(remoCon, 1)) // 0 to go back (Terraria's behav), set variables up and 1 to choose world
|
||||
remoCon.openUI(UILoadSavegame(remoCon))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +114,7 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() {
|
||||
batch.color = Color.WHITE
|
||||
// ui title
|
||||
// val titlestr = Lang["CONTEXT_WORLD_NEW"]
|
||||
// App.fontUITitle.draw(batch, titlestr, drawX + (width - App.fontGame.getWidth(titlestr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||
// App.fontUITitle.draw(batch, titlestr, drawX + (width - App.fontUITitle.getWidth(titlestr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||
|
||||
|
||||
// name/seed input labels
|
||||
|
||||
@@ -7,11 +7,8 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.random.XXHash64
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.ModMgr
|
||||
import net.torvald.terrarum.Second
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame.Companion.NEW_WORLD_SIZE
|
||||
@@ -30,6 +27,12 @@ import net.torvald.terrarum.utils.RandomWordsName
|
||||
*/
|
||||
class UINewWorld(val remoCon: UIRemoCon) : UICanvas() {
|
||||
|
||||
private var newPlayerCreationThread = Thread {}
|
||||
|
||||
constructor(remoCon: UIRemoCon, playerCreationThread: Thread) : this(remoCon) {
|
||||
newPlayerCreationThread = playerCreationThread
|
||||
}
|
||||
|
||||
private val hugeTex = TextureRegion(Texture(ModMgr.getGdxFile("basegame", "gui/huge.png")))
|
||||
private val largeTex = TextureRegion(Texture(ModMgr.getGdxFile("basegame", "gui/large.png")))
|
||||
private val normalTex = TextureRegion(Texture(ModMgr.getGdxFile("basegame", "gui/normal.png")))
|
||||
@@ -85,9 +88,16 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() {
|
||||
private val backButton = UIItemTextButton(this, "MENU_LABEL_BACK", drawX + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
||||
private val goButton = UIItemTextButton(this, "MENU_LABEL_CONFIRM_BUTTON", drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
||||
|
||||
|
||||
init {
|
||||
goButton.touchDownListener = { _, _, _, _ ->
|
||||
// printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}")
|
||||
goButton.clickOnceListener = { _, _ ->
|
||||
|
||||
// after the save is complete, proceed to new world generation
|
||||
newPlayerCreationThread.start()
|
||||
newPlayerCreationThread.join()
|
||||
|
||||
|
||||
printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}")
|
||||
|
||||
val ingame = TerrarumIngame(App.batch)
|
||||
val player = ReadActor.invoke(UILoadGovernor.playerDisk!!, ByteArray64Reader(UILoadGovernor.playerDisk!!.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)) as IngamePlayer
|
||||
@@ -111,8 +121,8 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() {
|
||||
App.setLoadScreen(loadScreen)
|
||||
|
||||
}
|
||||
backButton.touchDownListener = { _, _, _, _ ->
|
||||
remoCon.openUI(UILoadDemoSavefiles(remoCon, 1))
|
||||
backButton.clickOnceListener = { _, _ ->
|
||||
remoCon.openUI(UILoadSavegame(remoCon))
|
||||
}
|
||||
|
||||
addUIitem(sizeSelector)
|
||||
@@ -131,7 +141,7 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() {
|
||||
batch.color = Color.WHITE
|
||||
// ui title
|
||||
// val titlestr = Lang["CONTEXT_WORLD_NEW"]
|
||||
// App.fontUITitle.draw(batch, titlestr, drawX + (width - App.fontGame.getWidth(titlestr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||
// App.fontUITitle.draw(batch, titlestr, drawX + (width - App.fontUITitle.getWidth(titlestr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||
|
||||
// draw size previews
|
||||
val texture = tex[sizeSelector.selection.coerceAtMost(tex.lastIndex)]
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
package net.torvald.terrarum.modulebasegame.ui
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.Input
|
||||
import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.CommonResourcePool
|
||||
import net.torvald.terrarum.ceilInt
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.ui.*
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||
import net.torvald.unicode.TIMES
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2023-06-22.
|
||||
*/
|
||||
class UIPerformanceControlPanel(remoCon: UIRemoCon?) : UICanvas() {
|
||||
|
||||
|
||||
private val linegap = 14
|
||||
private val panelgap = 20
|
||||
|
||||
private val rowheight = 20 + linegap
|
||||
|
||||
private val h1MarginTop = 16
|
||||
private val h1MarginBottom = 4
|
||||
|
||||
private val options = arrayOf(
|
||||
arrayOf("", { Lang["MENU_OPTIONS_GAMEPLAY"] }, "h1"),
|
||||
arrayOf("autosaveinterval", { Lang["MENU_OPTIONS_AUTOSAVE"] + " (${Lang["CONTEXT_TIME_MINUTE_PLURAL"]})" }, "spinnerimul,1,120,1,60000"),
|
||||
arrayOf("notificationshowuptime", { Lang["MENU_OPTIONS_NOTIFICATION_DISPLAY_DURATION"] + " (${Lang["CONTEXT_TIME_SECOND_PLURAL"]})" }, "spinnerimul,2,10,1,1000"),
|
||||
arrayOf("", { Lang["MENU_LABEL_JVM_DNT"] }, "h1"),
|
||||
arrayOf("jvm_xmx", { Lang["MENU_OPTIONS_JVM_HEAP_MAX"] + " (GB)" }, "spinner,2,32,1"),
|
||||
arrayOf("jvm_extra_cmd", { Lang["MENU_LABEL_EXTRA_JVM_ARGUMENTS"] }, "typein"),
|
||||
arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"),
|
||||
)
|
||||
|
||||
private val optionsYpos = IntArray(options.size + 1)
|
||||
|
||||
init {
|
||||
CommonResourcePool.addToLoadingList("gui_hrule") {
|
||||
TextureRegionPack(Gdx.files.internal("assets/graphics/gui/hrule.tga"), 216, 20)
|
||||
}
|
||||
CommonResourcePool.loadAll()
|
||||
|
||||
|
||||
|
||||
var akku = 0
|
||||
options.forEachIndexed { index, row ->
|
||||
val option = row[2]
|
||||
|
||||
if (index > 0 && option == "h1") {
|
||||
akku += h1MarginTop
|
||||
}
|
||||
|
||||
optionsYpos[index] = akku
|
||||
|
||||
akku += when (option) {
|
||||
"h1" -> rowheight + h1MarginBottom
|
||||
else -> rowheight
|
||||
}
|
||||
}
|
||||
optionsYpos[optionsYpos.lastIndex] = akku
|
||||
}
|
||||
override var width = 560
|
||||
override var height = optionsYpos.last()
|
||||
|
||||
private val hrule = CommonResourcePool.getAsTextureRegionPack("gui_hrule")
|
||||
|
||||
private val spinnerWidth = 140
|
||||
private val typeinWidth = 240
|
||||
private val drawX = (Toolkit.drawWidth - width) / 2
|
||||
private val drawY = (App.scr.height - height) / 2
|
||||
|
||||
// @return Pair of <UIItem, Init job for the item>
|
||||
private fun makeButton(args: String, x: Int, y: Int, optionName: String): Pair<UIItem, (UIItem, String) -> Unit> {
|
||||
return if (args.startsWith("h1") || args.startsWith("p")) {
|
||||
(object : UIItem(this, x, y) {
|
||||
override val width = 1
|
||||
override val height = 1
|
||||
override fun dispose() {}
|
||||
}) to { _, _ -> }
|
||||
}
|
||||
else if (args.startsWith("toggle")) {
|
||||
UIItemToggleButton(this, x, y, spinnerWidth, App.getConfigBoolean(optionName)) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemToggleButton).clickOnceListener = { _, _ ->
|
||||
it.toggle()
|
||||
App.setConfig(optionStr, it.getStatus())
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.startsWith("spinner,")) {
|
||||
val arg = args.split(',')
|
||||
UIItemSpinner(this, x, y, App.getConfigInt(optionName), arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), spinnerWidth, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemSpinner).selectionChangeListener = {
|
||||
App.setConfig(optionStr, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.startsWith("spinnerd,")) {
|
||||
val arg = args.split(',')
|
||||
UIItemSpinner(this, x, y, App.getConfigDouble(optionName), arg[1].toDouble(), arg[2].toDouble(), arg[3].toDouble(), spinnerWidth, numberToTextFunction = { "${((it as Double)*100).toInt()}%" }) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemSpinner).selectionChangeListener = {
|
||||
App.setConfig(optionStr, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.startsWith("spinnerimul,")) {
|
||||
val arg = args.split(',')
|
||||
val mult = arg[4].toInt()
|
||||
UIItemSpinner(this, x, y, App.getConfigInt(optionName) / mult, arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), spinnerWidth, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemSpinner).selectionChangeListener = {
|
||||
App.setConfig(optionStr, it.toInt() * mult)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.startsWith("typeinint")) {
|
||||
// val arg = args.split(',') // args: none
|
||||
UIItemTextLineInput(this, x, y, spinnerWidth,
|
||||
defaultValue = { "${App.getConfigInt(optionName)}" },
|
||||
maxLen = InputLenCap(4, InputLenCap.CharLenUnit.CODEPOINTS),
|
||||
keyFilter = { it.headkey in Input.Keys.NUM_0..Input.Keys.NUM_9 || it.headkey == Input.Keys.BACKSPACE }
|
||||
) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemTextLineInput).textCommitListener = {
|
||||
App.setConfig(optionStr, it.toInt()) // HAXXX!!!
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.startsWith("typeinres")) {
|
||||
val keyWidth = optionName.substringBefore(',')
|
||||
val keyHeight = optionName.substringAfter(',')
|
||||
UIItemTextLineInput(this, x, y, spinnerWidth,
|
||||
defaultValue = { "${App.getConfigInt(keyWidth)}x${App.getConfigInt(keyHeight)}" },
|
||||
maxLen = InputLenCap(9, InputLenCap.CharLenUnit.CODEPOINTS),
|
||||
keyFilter = { it.headkey == Input.Keys.ENTER || it.headkey == Input.Keys.BACKSPACE || it.character?.matches(Regex("[0-9xX]")) == true },
|
||||
alignment = UIItemTextButton.Companion.Alignment.CENTRE
|
||||
) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemTextLineInput).textCommitListener = { text ->
|
||||
val text = text.lowercase()
|
||||
if (text.matches(Regex("""[0-9]+x[0-9]+"""))) {
|
||||
it.markAsNormal()
|
||||
val width = text.substringBefore('x').toInt()
|
||||
val height = text.substringAfter('x').toInt()
|
||||
App.setConfig(keyWidth, width)
|
||||
App.setConfig(keyHeight, height)
|
||||
}
|
||||
else it.markAsInvalid()
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (args.startsWith("typein")) {
|
||||
//args: none
|
||||
UIItemTextLineInput(this, x, y, typeinWidth, defaultValue = { App.getConfigString(optionName) }) to { it: UIItem, optionStr: String ->
|
||||
(it as UIItemTextLineInput).textCommitListener = {
|
||||
App.setConfig(optionStr, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
else throw IllegalArgumentException(args)
|
||||
}
|
||||
|
||||
private val optionControllers: List<Pair<UIItem, (UIItem, String) -> Unit>> = options.mapIndexed { index, strings ->
|
||||
makeButton(options[index][2] as String,
|
||||
drawX + width / 2 + panelgap,
|
||||
drawY - 2 + optionsYpos[index],
|
||||
options[index][0] as String
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
optionControllers.forEachIndexed { i, it ->
|
||||
it.second.invoke(it.first, options[i][0] as String)
|
||||
addUIitem(it.first)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateUI(delta: Float) {
|
||||
uiItems.forEach { it.update(delta) }
|
||||
}
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
/*batch.color = Toolkit.Theme.COL_INACTIVE
|
||||
Toolkit.drawBoxBorder(batch, drawX, drawY, width, height)
|
||||
|
||||
batch.color = CELL_COL
|
||||
Toolkit.fillArea(batch, drawX, drawY, width, height)*/
|
||||
|
||||
options.forEachIndexed { index, strings ->
|
||||
val mode = strings[2]
|
||||
|
||||
val font = if (mode == "h1") App.fontUITitle else App.fontGame
|
||||
|
||||
val label = (strings[1] as () -> String).invoke()
|
||||
val labelWidth = font.getWidth(label)
|
||||
batch.color = when (mode) {
|
||||
"h1" -> Toolkit.Theme.COL_MOUSE_UP
|
||||
"p" -> Color.LIGHT_GRAY
|
||||
else -> Color.WHITE
|
||||
}
|
||||
|
||||
val xpos = if (mode == "p" || mode == "h1")
|
||||
drawX + (width - labelWidth)/2 // centre-aligned
|
||||
else
|
||||
drawX + width/2 - panelgap - labelWidth // right aligned at the middle of the panel, offsetted by panelgap
|
||||
|
||||
font.draw(batch, label, xpos.toFloat(), drawY + optionsYpos[index] - 2f)
|
||||
|
||||
// draw hrule
|
||||
if (mode == "h1") {
|
||||
val ruleWidth = ((width - 24 - labelWidth) / 2).toFloat()
|
||||
batch.draw(hrule.get(0,0), xpos - 24f - ruleWidth, drawY + optionsYpos[index].toFloat(), ruleWidth, hrule.tileH.toFloat())
|
||||
batch.draw(hrule.get(0,1), xpos + 24f + labelWidth, drawY + optionsYpos[index].toFloat(), ruleWidth, hrule.tileH.toFloat())
|
||||
}
|
||||
}
|
||||
uiItems.forEach { it.render(batch, camera) }
|
||||
|
||||
if (App.getConfigBoolean("fx_streamerslayout")) {
|
||||
val xstart = App.scr.width - App.scr.chatWidth
|
||||
|
||||
batch.color = Color(0x00f8ff_40)
|
||||
Toolkit.fillArea(batch, xstart + 1, 1, App.scr.chatWidth - 2, App.scr.height - 2)
|
||||
|
||||
batch.color = Toolkit.Theme.COL_MOUSE_UP
|
||||
Toolkit.drawBoxBorder(batch, xstart + 1, 1, App.scr.chatWidth - 2, App.scr.height - 2)
|
||||
|
||||
val overlayResTxt = "${(App.scr.chatWidth * App.scr.magn).ceilInt()}$TIMES${App.scr.windowH}"
|
||||
|
||||
App.fontGame.draw(batch, overlayResTxt,
|
||||
(xstart + (App.scr.chatWidth - App.fontGame.getWidth(overlayResTxt)) / 2).toFloat(),
|
||||
((App.scr.height - App.fontGame.lineHeight) / 2).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class UIQuickslotPie : UICanvas() {
|
||||
// update controls
|
||||
if (handler.isOpened || handler.isOpening) {
|
||||
val cursorPos = Vector2(Terrarum.mouseScreenX.toDouble(), Terrarum.mouseScreenY.toDouble())
|
||||
val centre = Vector2(Toolkit.drawWidth / 2.0, App.scr.halfh.toDouble())
|
||||
val centre = Vector2(Toolkit.hdrawWidth.toDouble(), App.scr.halfh.toDouble())
|
||||
val deg = -(centre - cursorPos).direction.toFloat()
|
||||
|
||||
selection = Math.round(deg * slotCount / FastMath.TWO_PI)
|
||||
|
||||
@@ -8,6 +8,7 @@ import net.torvald.terrarum.gameactors.AVKey
|
||||
import net.torvald.terrarum.gameitems.GameItem
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.getWidthOfCells
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.unicode.getKeycapPC
|
||||
@@ -23,7 +24,7 @@ internal class UIStorageChest : UICanvas(
|
||||
lateinit var chestInventory: FixtureInventory
|
||||
lateinit var chestNameFun: () -> String
|
||||
|
||||
override var width = App.scr.width
|
||||
override var width = Toolkit.drawWidth
|
||||
override var height = App.scr.height
|
||||
|
||||
private val negotiator = object : InventoryTransactionNegotiator() {
|
||||
@@ -49,12 +50,12 @@ internal class UIStorageChest : UICanvas(
|
||||
private var encumbrancePerc = 0f
|
||||
private var isEncumbered = false
|
||||
|
||||
private var halfSlotOffset = (UIItemInventoryElemSimple.height + UIItemInventoryItemGrid.listGap) / 2
|
||||
private var halfSlotOffset = (UIItemInventoryElemSimple.height + UIItemInventoryItemGrid.listGap * 2) / 2
|
||||
|
||||
init {
|
||||
catBar = UIItemInventoryCatBar(
|
||||
this,
|
||||
(App.scr.width - UIInventoryFull.catBarWidth) / 2,
|
||||
(width - UIInventoryFull.catBarWidth) / 2,
|
||||
42 - UIInventoryFull.YPOS_CORRECTION + (App.scr.height - UIInventoryFull.internalHeight) / 2,
|
||||
UIInventoryFull.internalWidth,
|
||||
UIInventoryFull.catBarWidth,
|
||||
@@ -65,42 +66,46 @@ internal class UIStorageChest : UICanvas(
|
||||
this,
|
||||
catBar,
|
||||
{ getFixtureInventory() },
|
||||
UIInventoryFull.INVENTORY_CELLS_OFFSET_X() - halfSlotOffset,
|
||||
Toolkit.hdrawWidth - getWidthOfCells(6) - halfSlotOffset,
|
||||
UIInventoryFull.INVENTORY_CELLS_OFFSET_Y(),
|
||||
6, UIInventoryFull.CELLS_VRT,
|
||||
drawScrollOnRightside = false,
|
||||
drawWallet = false,
|
||||
keyDownFun = { _, _, _, _, _ -> Unit },
|
||||
touchDownFun = { gameItem, amount, _, _, _ ->
|
||||
if (gameItem != null) {
|
||||
negotiator.reject(getFixtureInventory(), getPlayerInventory(), gameItem, amount)
|
||||
touchDownFun = { gameItem, amount, button, _, _ ->
|
||||
if (button == App.getConfigInt("config_mouseprimary")) {
|
||||
if (gameItem != null) {
|
||||
negotiator.reject(getFixtureInventory(), getPlayerInventory(), gameItem, amount)
|
||||
}
|
||||
itemListUpdate()
|
||||
}
|
||||
itemListUpdate()
|
||||
}
|
||||
)
|
||||
// make grid mode buttons work together
|
||||
itemListChest.gridModeButtons[0].touchDownListener = { _,_,_,_ -> setCompact(false) }
|
||||
itemListChest.gridModeButtons[1].touchDownListener = { _,_,_,_ -> setCompact(true) }
|
||||
itemListChest.navRemoCon.listButtonListener = { _,_ -> setCompact(false) }
|
||||
itemListChest.navRemoCon.gridButtonListener = { _,_ -> setCompact(true) }
|
||||
|
||||
itemListPlayer = UIItemInventoryItemGrid(
|
||||
this,
|
||||
catBar,
|
||||
{ INGAME.actorNowPlaying!!.inventory }, // literally a player's inventory
|
||||
UIInventoryFull.INVENTORY_CELLS_OFFSET_X() - halfSlotOffset + (UIItemInventoryItemGrid.listGap + UIItemInventoryElemWide.height) * 7,
|
||||
Toolkit.hdrawWidth + halfSlotOffset,
|
||||
UIInventoryFull.INVENTORY_CELLS_OFFSET_Y(),
|
||||
6, UIInventoryFull.CELLS_VRT,
|
||||
drawScrollOnRightside = true,
|
||||
drawWallet = false,
|
||||
keyDownFun = { _, _, _, _, _ -> Unit },
|
||||
touchDownFun = { gameItem, amount, _, _, _ ->
|
||||
if (gameItem != null) {
|
||||
negotiator.accept(getPlayerInventory(), getFixtureInventory(), gameItem, amount)
|
||||
touchDownFun = { gameItem, amount, button, _, _ ->
|
||||
if (button == App.getConfigInt("config_mouseprimary")) {
|
||||
if (gameItem != null) {
|
||||
negotiator.accept(getPlayerInventory(), getFixtureInventory(), gameItem, amount)
|
||||
}
|
||||
itemListUpdate()
|
||||
}
|
||||
itemListUpdate()
|
||||
}
|
||||
)
|
||||
itemListPlayer.gridModeButtons[0].touchDownListener = { _,_,_,_ -> setCompact(false) }
|
||||
itemListPlayer.gridModeButtons[1].touchDownListener = { _,_,_,_ -> setCompact(true) }
|
||||
itemListPlayer.navRemoCon.listButtonListener = { _,_ -> setCompact(false) }
|
||||
itemListPlayer.navRemoCon.gridButtonListener = { _,_ -> setCompact(true) }
|
||||
|
||||
handler.allowESCtoClose = true
|
||||
|
||||
@@ -132,14 +137,14 @@ internal class UIStorageChest : UICanvas(
|
||||
|
||||
private fun setCompact(yes: Boolean) {
|
||||
itemListChest.isCompactMode = yes
|
||||
itemListChest.gridModeButtons[0].highlighted = !yes
|
||||
itemListChest.gridModeButtons[1].highlighted = yes
|
||||
itemListChest.navRemoCon.gridModeButtons[0].highlighted = !yes
|
||||
itemListChest.navRemoCon.gridModeButtons[1].highlighted = yes
|
||||
itemListChest.itemPage = 0
|
||||
itemListChest.rebuild(catBar.catIconsMeaning[catBar.selectedIcon])
|
||||
|
||||
itemListPlayer.isCompactMode = yes
|
||||
itemListPlayer.gridModeButtons[0].highlighted = !yes
|
||||
itemListPlayer.gridModeButtons[1].highlighted = yes
|
||||
itemListPlayer.navRemoCon.gridModeButtons[0].highlighted = !yes
|
||||
itemListPlayer.navRemoCon.gridModeButtons[1].highlighted = yes
|
||||
itemListPlayer.itemPage = 0
|
||||
itemListPlayer.rebuild(catBar.catIconsMeaning[catBar.selectedIcon])
|
||||
|
||||
@@ -161,7 +166,7 @@ internal class UIStorageChest : UICanvas(
|
||||
if (openingClickLatched && !Terrarum.mouseDown) openingClickLatched = false
|
||||
}
|
||||
|
||||
private val thisOffsetX = UIInventoryFull.INVENTORY_CELLS_OFFSET_X() - halfSlotOffset
|
||||
private val thisOffsetX = Toolkit.hdrawWidth - getWidthOfCells(6) - halfSlotOffset
|
||||
private val thisOffsetX2 = thisOffsetX + (UIItemInventoryItemGrid.listGap + UIItemInventoryElemWide.height) * 7
|
||||
private val thisOffsetY = UIInventoryFull.INVENTORY_CELLS_OFFSET_Y()
|
||||
private val cellsWidth = (UIItemInventoryItemGrid.listGap + UIItemInventoryElemWide.height) * 6 - UIItemInventoryItemGrid.listGap
|
||||
@@ -174,7 +179,7 @@ internal class UIStorageChest : UICanvas(
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
// background fill
|
||||
UIInventoryFull.drawBackground(batch, handler.opacity)
|
||||
UIInventoryFull.drawBackground(batch, 1f)
|
||||
|
||||
// UI items
|
||||
batch.color = Color.WHITE
|
||||
@@ -232,19 +237,23 @@ internal class UIStorageChest : UICanvas(
|
||||
}
|
||||
|
||||
override fun doOpening(delta: Float) {
|
||||
super.doOpening(delta)
|
||||
INGAME.pause()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
override fun doClosing(delta: Float) {
|
||||
super.doClosing(delta)
|
||||
INGAME.resume()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
override fun endOpening(delta: Float) {
|
||||
super.endOpening(delta)
|
||||
}
|
||||
|
||||
override fun endClosing(delta: Float) {
|
||||
super.endClosing(delta)
|
||||
UIItemInventoryItemGrid.tooltipShowing.clear()
|
||||
INGAME.setTooltipMessage(null) // required!
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ class UITitleModules(val remoCon: UIRemoCon) : UICanvas() {
|
||||
savegamesCount += 1
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
System.err.println("[UILoadDemoSavefiles] Error while loading module info for '$s'")
|
||||
System.err.println("[UITitleModules] Error while loading module info for '$s'")
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,7 @@ class UITitleModules(val remoCon: UIRemoCon) : UICanvas() {
|
||||
// draw texts
|
||||
val loadGameTitleStr = Lang["MENU_MODULES"]// + "$EMDASH$hash"
|
||||
// "Game Load"
|
||||
App.fontUITitle.draw(batch, loadGameTitleStr, (width - App.fontGame.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||
App.fontUITitle.draw(batch, loadGameTitleStr, (width - App.fontUITitle.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat())
|
||||
// Control help
|
||||
App.fontGame.draw(batch, controlHelp, uiX.toFloat(), controlHelperY.toFloat())
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@ object UITitleRemoConYaml {
|
||||
* The class must be the UICanvas
|
||||
*/
|
||||
val menuBase = """
|
||||
- MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadDemoSavefiles
|
||||
- MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame
|
||||
- MENU_OPTIONS
|
||||
- MENU_LABEL_GRAPHICS : net.torvald.terrarum.modulebasegame.ui.UIGraphicsControlPanel
|
||||
- MENU_OPTIONS_CONTROLS : net.torvald.terrarum.modulebasegame.ui.UIKeyboardControlPanel
|
||||
- MENU_LABEL_IME : net.torvald.terrarum.modulebasegame.ui.UIIMEConfig
|
||||
- MENU_LABEL_LANGUAGE : net.torvald.terrarum.modulebasegame.ui.UITitleLanguage
|
||||
- GAME_GENRE_MISC : net.torvald.terrarum.modulebasegame.ui.UIPerformanceControlPanel
|
||||
- MENU_MODULES : net.torvald.terrarum.ModOptionsHost
|
||||
- MENU_LABEL_RETURN+WRITETOCONFIG
|
||||
- MENU_MODULES : net.torvald.terrarum.modulebasegame.ui.UITitleModules
|
||||
@@ -25,6 +26,7 @@ object UITitleRemoConYaml {
|
||||
- MENU_LABEL_CREDITS
|
||||
- MENU_LABEL_COPYRIGHT : net.torvald.terrarum.modulebasegame.ui.UITitleCredits
|
||||
- MENU_CREDIT_GPL_DNT : net.torvald.terrarum.modulebasegame.ui.UITitleGPL3
|
||||
- MENU_LABEL_SYSTEM_INFO : net.torvald.terrarum.modulebasegame.ui.UISystemInfo
|
||||
- MENU_LABEL_RETURN
|
||||
- MENU_LABEL_QUIT
|
||||
"""
|
||||
|
||||
@@ -43,4 +43,5 @@ open class UITitleWallOfText(private val text: List<String>) : UICanvas() {
|
||||
}
|
||||
|
||||
class UITitleCredits(val remoCon: UIRemoCon) : UITitleWallOfText(CreditSingleton.credit)
|
||||
class UITitleGPL3(val remoCon: UIRemoCon) : UITitleWallOfText(CreditSingleton.gpl3)
|
||||
class UITitleGPL3(val remoCon: UIRemoCon) : UITitleWallOfText(CreditSingleton.gpl3)
|
||||
class UISystemInfo(val remoCon: UIRemoCon) : UITitleWallOfText(CreditSingleton.systeminfo)
|
||||
@@ -56,10 +56,11 @@ class UITooltip : UICanvas() {
|
||||
batch.color = tooltipBackCol
|
||||
|
||||
Toolkit.drawBaloon(batch,
|
||||
tooltipX - textMarginX,
|
||||
tooltipY,
|
||||
tooltipW,
|
||||
font.lineHeight * msgBuffer.size
|
||||
tooltipX - textMarginX,
|
||||
tooltipY,
|
||||
tooltipW,
|
||||
font.lineHeight * msgBuffer.size,
|
||||
Notification.OPACITY
|
||||
)
|
||||
|
||||
batch.color = tooltipForeCol
|
||||
|
||||
@@ -3,18 +3,22 @@ package net.torvald.terrarum.modulebasegame.ui
|
||||
import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.gameactors.AVKey
|
||||
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureWorldPortal
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_Y
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.YPOS_CORRECTION
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.drawBackground
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.internalHeight
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.internalWidth
|
||||
import net.torvald.terrarum.serialise.toAscii85
|
||||
import net.torvald.terrarum.ui.*
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||
import net.torvald.unicode.getKeycapConsole
|
||||
import net.torvald.unicode.getKeycapPC
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Structure:
|
||||
@@ -34,60 +38,45 @@ class UIWorldPortal : UICanvas(
|
||||
override var width: Int = Toolkit.drawWidth
|
||||
override var height: Int = App.scr.height
|
||||
|
||||
internal lateinit var host: FixtureWorldPortal
|
||||
|
||||
|
||||
val controlHelpHeight = App.fontGame.lineHeight
|
||||
|
||||
private var panelTransitionLocked = false
|
||||
|
||||
fun lockTransition() {
|
||||
panelTransitionLocked = true
|
||||
}
|
||||
fun unlockTransition() {
|
||||
panelTransitionLocked = false
|
||||
}
|
||||
fun requestTransition(target: Int) = transitionPanel.requestTransition(target)
|
||||
|
||||
|
||||
val catBar = UIItemWorldPortalTopBar(
|
||||
this,
|
||||
0,
|
||||
42 - YPOS_CORRECTION + (App.scr.height - internalHeight) / 2,
|
||||
) { i -> if (!panelTransitionLocked) requestTransition(i) }
|
||||
|
||||
|
||||
private val SP = "\u3000 "
|
||||
val portalListingControlHelp: String
|
||||
get() = if (App.environment == RunningEnvironment.PC)
|
||||
"${getKeycapPC(App.getConfigInt("control_key_up"))}${getKeycapPC(App.getConfigInt("control_key_down"))}" +
|
||||
" ${Lang["MENU_CONTROLS_SCROLL"]}" +
|
||||
"$SP${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
|
||||
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
|
||||
else
|
||||
"${getKeycapConsole('R')} ${Lang["MENU_CONTROLS_SCROLL"]}" +
|
||||
"$SP${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}" +
|
||||
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}" +
|
||||
"$SP${App.gamepadLabelLT} ${Lang["GAME_WORLD_SEARCH"]}" +
|
||||
"$SP${App.gamepadLabelRT} ${Lang["GAME_INVENTORY"]}"
|
||||
|
||||
|
||||
private val transitionalSearch = UIWorldPortalSearch(this)
|
||||
private val transitionalListing = UIWorldPortalListing(this)
|
||||
private val transitionalCargo = UIWorldPortalCargo(this)
|
||||
val transitionalSearch = UIWorldPortalSearch(this)
|
||||
val transitionalListing = UIWorldPortalListing(this)
|
||||
// val transitionalCargo = UIWorldPortalCargo(this)
|
||||
private val transitionPanel = UIItemHorizontalFadeSlide(
|
||||
this,
|
||||
(width - internalWidth) / 2,
|
||||
INVENTORY_CELLS_OFFSET_Y(),
|
||||
width,
|
||||
App.scr.height,
|
||||
1f,
|
||||
transitionalSearch, transitionalListing, transitionalCargo
|
||||
0f,
|
||||
transitionalListing, transitionalSearch
|
||||
)
|
||||
|
||||
/**
|
||||
* Called by:
|
||||
* - "Search" button on UIWorldPortalListing
|
||||
* - "Cancel" button on UIWorldPortalSearch
|
||||
*/
|
||||
fun requestTransition(target: Int) = transitionPanel.requestTransition(target)
|
||||
|
||||
init {
|
||||
addUIitem(catBar)
|
||||
addUIitem(transitionPanel)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal var xEnd = (width + internalWidth).div(2).toFloat()
|
||||
@@ -99,57 +88,133 @@ class UIWorldPortal : UICanvas(
|
||||
|
||||
|
||||
override fun updateUI(delta: Float) {
|
||||
|
||||
transitionPanel.update(delta)
|
||||
}
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
drawBackground(batch, handler.opacity)
|
||||
drawBackground(batch, 1f)
|
||||
|
||||
// UI items
|
||||
catBar.render(batch, camera)
|
||||
transitionPanel.render(batch, camera)
|
||||
}
|
||||
|
||||
private fun addWorldToPlayersDict(uuid: UUID) {
|
||||
val uuidstr = uuid.toAscii85()
|
||||
INGAME.actorNowPlaying?.let {
|
||||
val avList = (it.actorValue.getAsString(AVKey.WORLD_PORTAL_DICT) ?: "").split(',').filter { it.isNotBlank() }.toMutableList()
|
||||
if (!avList.contains(uuidstr)) {
|
||||
avList.add(uuidstr)
|
||||
it.actorValue[AVKey.WORLD_PORTAL_DICT] = avList.joinToString(",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanUpWorldDict() {
|
||||
// remove dupes, etc
|
||||
INGAME.actorNowPlaying?.let {
|
||||
val avList = (it.actorValue.getAsString(AVKey.WORLD_PORTAL_DICT) ?: "").split(',').filter { it.isNotBlank() }.toSet()
|
||||
it.actorValue[AVKey.WORLD_PORTAL_DICT] = avList.joinToString(",")
|
||||
}
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
super.show()
|
||||
transitionPanel.forcePosition(0)
|
||||
transitionPanel.show()
|
||||
INGAME.setTooltipMessage(null)
|
||||
|
||||
// add current world to the player's worldportaldict
|
||||
addWorldToPlayersDict(INGAME.world.worldIndex)
|
||||
cleanUpWorldDict()
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
transitionPanel.hide()
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
catBar.dispose()
|
||||
}
|
||||
|
||||
fun resetUI() {
|
||||
|
||||
transitionPanel.dispose()
|
||||
}
|
||||
|
||||
override fun doOpening(delta: Float) {
|
||||
super.doOpening(delta)
|
||||
resetUI()
|
||||
transitionPanel.uis.forEach { it.opacity = FastMath.pow(opacity, 0.5f) }
|
||||
INGAME.pause()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
override fun doClosing(delta: Float) {
|
||||
super.doClosing(delta)
|
||||
transitionPanel.uis.forEach { it.opacity = FastMath.pow(opacity, 0.5f) }
|
||||
INGAME.resume()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
override fun endOpening(delta: Float) {
|
||||
super.endOpening(delta)
|
||||
transitionPanel.uis.forEach { it.opacity = FastMath.pow(opacity, 0.5f) }
|
||||
UIItemInventoryItemGrid.tooltipShowing.clear()
|
||||
INGAME.setTooltipMessage(null) // required!
|
||||
}
|
||||
|
||||
override fun endClosing(delta: Float) {
|
||||
super.endClosing(delta)
|
||||
resetUI()
|
||||
transitionPanel.uis.forEach { it.opacity = FastMath.pow(opacity, 0.5f) }
|
||||
UIItemInventoryItemGrid.tooltipShowing.clear()
|
||||
INGAME.setTooltipMessage(null) // required!
|
||||
}
|
||||
|
||||
override fun inputStrobed(e: TerrarumKeyboardEvent) {
|
||||
super.inputStrobed(e)
|
||||
transitionPanel.uis.forEach { it.inputStrobed(e) }
|
||||
}
|
||||
|
||||
override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean {
|
||||
super.touchDragged(screenX, screenY, pointer)
|
||||
transitionPanel.uis.forEach { it.touchDragged(screenX, screenY, pointer) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
super.touchDown(screenX, screenY, pointer, button)
|
||||
transitionPanel.uis.forEach { it.touchDown(screenX, screenY, pointer, button) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
super.touchUp(screenX, screenY, pointer, button)
|
||||
transitionPanel.uis.forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||
super.scrolled(amountX, amountY)
|
||||
transitionPanel.uis.forEach { it.scrolled(amountX, amountY) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun keyDown(keycode: Int): Boolean {
|
||||
super.keyDown(keycode)
|
||||
transitionPanel.uis.forEach { it.keyDown(keycode) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun keyUp(keycode: Int): Boolean {
|
||||
super.keyUp(keycode)
|
||||
transitionPanel.uis.forEach { it.keyUp(keycode) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun keyTyped(character: Char): Boolean {
|
||||
super.keyTyped(character)
|
||||
transitionPanel.uis.forEach { it.keyTyped(character) }
|
||||
return true
|
||||
}
|
||||
|
||||
override fun resize(width: Int, height: Int) {
|
||||
super.resize(width, height)
|
||||
transitionPanel.uis.forEach { it.resize(width, height) }
|
||||
}
|
||||
}
|
||||
|
||||
class UIItemWorldPortalTopBar(
|
||||
@@ -171,7 +236,7 @@ class UIItemWorldPortalTopBar(
|
||||
|
||||
private val genericIcons: TextureRegionPack = CommonResourcePool.getAsTextureRegionPack("inventory_category")
|
||||
private val icons = CommonResourcePool.getAsTextureRegionPack("terrarum-basegame-worldportalicons")
|
||||
private val catIconImages = listOf(
|
||||
/*private val catIconImages = listOf(
|
||||
icons.get(0, 0),
|
||||
genericIcons.get(16,0),
|
||||
icons.get(1, 0),
|
||||
@@ -182,15 +247,21 @@ class UIItemWorldPortalTopBar(
|
||||
"CONTEXT_WORLD_SEARCH",
|
||||
"",
|
||||
"CONTEXT_WORLD_LIST",
|
||||
"GAME_INVENTORY",
|
||||
"",
|
||||
)
|
||||
"GAME_INVENTORY",
|
||||
)*/
|
||||
private val buttonGapSize = 120
|
||||
private val highlighterYPos = icons.tileH + 4
|
||||
|
||||
var selection = 2
|
||||
var selectedPanel = 2; private set
|
||||
|
||||
private val buttons = Array<UIItemImageButton>(5) {
|
||||
/** (oldIndex: Int?, newIndex: Int) -> Unit
|
||||
* Indices are raw index. That is, not re-arranged. */
|
||||
var selectionChangeListener: ((Int?, Int) -> Unit)? = null
|
||||
|
||||
private var transitionFired = false
|
||||
|
||||
/*private val buttons = Array<UIItemImageButton>(5) {
|
||||
val xoff = if (it == 1) -32 else if (it == 3) 32 else 0
|
||||
UIItemImageButton(
|
||||
parentUI,
|
||||
@@ -207,17 +278,41 @@ class UIItemWorldPortalTopBar(
|
||||
)
|
||||
}
|
||||
|
||||
private val workingButtons = arrayOf(0,2,4)*/
|
||||
|
||||
override fun update(delta: Float) {
|
||||
super.update(delta)
|
||||
|
||||
|
||||
/*workingButtons.forEach { buttons[it].update(delta) }
|
||||
|
||||
// transition stuffs
|
||||
workingButtons.filter { buttons[it].mousePushed }.firstOrNull()?.let { pushedButton ->
|
||||
if (selectedPanel != pushedButton) transitionFired = true
|
||||
selectedPanel = pushedButton
|
||||
|
||||
workingButtons.forEach { i ->
|
||||
buttons[i].highlighted = i == pushedButton
|
||||
}
|
||||
}*/
|
||||
|
||||
if (transitionFired) {
|
||||
transitionFired = false
|
||||
panelTransitionReqFun(selectedPanel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(batch: SpriteBatch, camera: Camera) {
|
||||
super.render(batch, camera)
|
||||
|
||||
// button
|
||||
buttons.forEach { it.render(batch, camera) }
|
||||
/*buttons.forEach { it.render(batch, camera) }
|
||||
|
||||
// label
|
||||
batch.color = Color.WHITE
|
||||
val text = Lang[catIconLabels[selection]]
|
||||
App.fontGame.draw(batch, text, buttons[selection].posX + 10 - (App.fontGame.getWidth(text) / 2), posY + highlighterYPos + 4)
|
||||
|
||||
val text = Lang[catIconLabels[selectedPanel]]
|
||||
App.fontGame.draw(batch, text, buttons[selectedPanel].posX + 10 - (App.fontGame.getWidth(text) / 2), posY + highlighterYPos + 4)
|
||||
*/
|
||||
|
||||
blendNormalStraightAlpha(batch)
|
||||
|
||||
|
||||
@@ -1,25 +1,252 @@
|
||||
package net.torvald.terrarum.modulebasegame.ui
|
||||
|
||||
import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.gameactors.AVKey
|
||||
import net.torvald.terrarum.gameitems.GameItem
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.unicode.getKeycapPC
|
||||
|
||||
class UIWorldPortalCargo(val full: UIWorldPortal) : UICanvas() {
|
||||
class UIWorldPortalCargo(val full: UIWorldPortal) : UICanvas(), HasInventory {
|
||||
|
||||
override var width: Int = Toolkit.drawWidth
|
||||
override var height: Int = App.scr.height
|
||||
|
||||
override fun updateUI(delta: Float) {
|
||||
TODO("Not yet implemented")
|
||||
lateinit var chestInventory: FixtureInventory
|
||||
lateinit var chestNameFun: () -> String
|
||||
|
||||
private val negotiator = object : InventoryTransactionNegotiator() {
|
||||
override fun accept(player: FixtureInventory, fixture: FixtureInventory, item: GameItem, amount: Long) {
|
||||
player.remove(item, amount)
|
||||
fixture.add(item, amount)
|
||||
}
|
||||
|
||||
override fun reject(fixture: FixtureInventory, player: FixtureInventory, item: GameItem, amount: Long) {
|
||||
fixture.remove(item, amount)
|
||||
player.add(item, amount)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getNegotiator() = negotiator
|
||||
override fun getFixtureInventory(): FixtureInventory = chestInventory
|
||||
override fun getPlayerInventory(): FixtureInventory = INGAME.actorNowPlaying!!.inventory
|
||||
|
||||
private val catBar: UIItemInventoryCatBar
|
||||
private val itemListChest: UIItemInventoryItemGrid
|
||||
private val itemListPlayer: UIItemInventoryItemGrid
|
||||
|
||||
private var encumbrancePerc = 0f
|
||||
private var isEncumbered = false
|
||||
|
||||
private var halfSlotOffset = (UIItemInventoryElemSimple.height + UIItemInventoryItemGrid.listGap * 2) / 2
|
||||
|
||||
init {
|
||||
catBar = UIItemInventoryCatBar(
|
||||
this,
|
||||
(width - UIInventoryFull.catBarWidth) / 2,
|
||||
42 - UIInventoryFull.YPOS_CORRECTION + (App.scr.height - UIInventoryFull.internalHeight) / 2,
|
||||
UIInventoryFull.internalWidth,
|
||||
UIInventoryFull.catBarWidth,
|
||||
false
|
||||
)
|
||||
catBar.selectionChangeListener = { old, new -> itemListUpdate() }
|
||||
itemListChest = UIItemInventoryItemGrid(
|
||||
this,
|
||||
catBar,
|
||||
{ getFixtureInventory() },
|
||||
Toolkit.hdrawWidth - UIInventoryFull.getWidthOfCells(6) - halfSlotOffset,
|
||||
UIInventoryFull.INVENTORY_CELLS_OFFSET_Y(),
|
||||
6, UIInventoryFull.CELLS_VRT,
|
||||
drawScrollOnRightside = false,
|
||||
drawWallet = false,
|
||||
keyDownFun = { _, _, _, _, _ -> Unit },
|
||||
touchDownFun = { gameItem, amount, button, _, _ ->
|
||||
if (button == App.getConfigInt("config_mouseprimary")) {
|
||||
if (gameItem != null) {
|
||||
negotiator.reject(getFixtureInventory(), getPlayerInventory(), gameItem, amount)
|
||||
}
|
||||
itemListUpdate()
|
||||
}
|
||||
}
|
||||
)
|
||||
// make grid mode buttons work together
|
||||
itemListChest.navRemoCon.listButtonListener = { _,_ -> setCompact(false) }
|
||||
itemListChest.navRemoCon.gridButtonListener = { _,_ -> setCompact(true) }
|
||||
|
||||
itemListPlayer = UIItemInventoryItemGrid(
|
||||
this,
|
||||
catBar,
|
||||
{ INGAME.actorNowPlaying!!.inventory }, // literally a player's inventory
|
||||
Toolkit.hdrawWidth + halfSlotOffset,
|
||||
UIInventoryFull.INVENTORY_CELLS_OFFSET_Y(),
|
||||
6, UIInventoryFull.CELLS_VRT,
|
||||
drawScrollOnRightside = true,
|
||||
drawWallet = false,
|
||||
keyDownFun = { _, _, _, _, _ -> Unit },
|
||||
touchDownFun = { gameItem, amount, button, _, _ ->
|
||||
if (button == App.getConfigInt("config_mouseprimary")) {
|
||||
if (gameItem != null) {
|
||||
negotiator.accept(getPlayerInventory(), getFixtureInventory(), gameItem, amount)
|
||||
}
|
||||
itemListUpdate()
|
||||
}
|
||||
}
|
||||
)
|
||||
itemListPlayer.navRemoCon.listButtonListener = { _,_ -> setCompact(false) }
|
||||
itemListPlayer.navRemoCon.gridButtonListener = { _,_ -> setCompact(true) }
|
||||
|
||||
handler.allowESCtoClose = true
|
||||
|
||||
addUIitem(itemListChest)
|
||||
addUIitem(itemListPlayer)
|
||||
}
|
||||
|
||||
private var openingClickLatched = false
|
||||
|
||||
override fun show() {
|
||||
itemListPlayer.getInventory = { INGAME.actorNowPlaying!!.inventory }
|
||||
|
||||
itemListUpdate()
|
||||
|
||||
openingClickLatched = Terrarum.mouseDown
|
||||
|
||||
UIItemInventoryItemGrid.tooltipShowing.clear()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
private fun itemListUpdate() {
|
||||
itemListChest.rebuild(catBar.catIconsMeaning[catBar.selectedIcon])
|
||||
itemListPlayer.rebuild(catBar.catIconsMeaning[catBar.selectedIcon])
|
||||
|
||||
encumbrancePerc = getPlayerInventory().capacity.toFloat() / getPlayerInventory().maxCapacity
|
||||
isEncumbered = getPlayerInventory().isEncumbered
|
||||
}
|
||||
|
||||
private fun setCompact(yes: Boolean) {
|
||||
itemListChest.isCompactMode = yes
|
||||
itemListChest.navRemoCon.gridModeButtons[0].highlighted = !yes
|
||||
itemListChest.navRemoCon.gridModeButtons[1].highlighted = yes
|
||||
itemListChest.itemPage = 0
|
||||
itemListChest.rebuild(catBar.catIconsMeaning[catBar.selectedIcon])
|
||||
|
||||
itemListPlayer.isCompactMode = yes
|
||||
itemListPlayer.navRemoCon.gridModeButtons[0].highlighted = !yes
|
||||
itemListPlayer.navRemoCon.gridModeButtons[1].highlighted = yes
|
||||
itemListPlayer.itemPage = 0
|
||||
itemListPlayer.rebuild(catBar.catIconsMeaning[catBar.selectedIcon])
|
||||
|
||||
itemListUpdate()
|
||||
}
|
||||
|
||||
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
if (!openingClickLatched) {
|
||||
return super.touchDown(screenX, screenY, pointer, button)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun updateUI(delta: Float) {
|
||||
catBar.update(delta)
|
||||
itemListChest.update(delta)
|
||||
itemListPlayer.update(delta)
|
||||
|
||||
if (openingClickLatched && !Terrarum.mouseDown) openingClickLatched = false
|
||||
}
|
||||
|
||||
private val thisOffsetX = Toolkit.hdrawWidth - UIInventoryFull.getWidthOfCells(6) - halfSlotOffset
|
||||
private val thisOffsetX2 = thisOffsetX + (UIItemInventoryItemGrid.listGap + UIItemInventoryElemWide.height) * 7
|
||||
private val thisOffsetY = UIInventoryFull.INVENTORY_CELLS_OFFSET_Y()
|
||||
private val cellsWidth = (UIItemInventoryItemGrid.listGap + UIItemInventoryElemWide.height) * 6 - UIItemInventoryItemGrid.listGap
|
||||
|
||||
private val controlHelp: String
|
||||
get() = if (App.environment == RunningEnvironment.PC)
|
||||
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
|
||||
else
|
||||
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]} "
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
TODO("Not yet implemented")
|
||||
// background fill
|
||||
UIInventoryFull.drawBackground(batch, 1f)
|
||||
|
||||
// UI items
|
||||
batch.color = Color.WHITE
|
||||
|
||||
catBar.render(batch, camera)
|
||||
itemListChest.render(batch, camera)
|
||||
itemListPlayer.render(batch, camera)
|
||||
|
||||
|
||||
blendNormalStraightAlpha(batch)
|
||||
|
||||
// encumbrance meter
|
||||
val encumbranceText = Lang["GAME_INVENTORY_ENCUMBRANCE"]
|
||||
val chestName = chestNameFun()
|
||||
val playerName = INGAME.actorNowPlaying!!.actorValue.getAsString(AVKey.NAME).orEmpty().let { it.ifBlank { Lang["GAME_INVENTORY"] } }
|
||||
val encumbBarXPos = itemListPlayer.posX + itemListPlayer.width - UIInventoryCells.weightBarWidth + 36
|
||||
val encumbBarTextXPos = encumbBarXPos - 6 - App.fontGame.getWidth(encumbranceText)
|
||||
val yEnd = -UIInventoryFull.YPOS_CORRECTION + (App.scr.height + UIInventoryFull.internalHeight).div(2).toFloat() // directly copied from UIInventoryFull.yEnd
|
||||
val encumbBarYPos = yEnd - 20 + 3 // dunno why but extra 3 px is needed
|
||||
val encumbCol = UIItemInventoryCellCommonRes.getHealthMeterColour(1f - encumbrancePerc, 0f, 1f)
|
||||
val encumbBack = encumbCol mul UIItemInventoryCellCommonRes.meterBackDarkening
|
||||
|
||||
// encumbrance bar background
|
||||
batch.color = encumbBack
|
||||
Toolkit.fillArea(
|
||||
batch,
|
||||
encumbBarXPos,
|
||||
encumbBarYPos,
|
||||
UIInventoryCells.weightBarWidth,
|
||||
UIInventoryFull.controlHelpHeight - 6f
|
||||
)
|
||||
// encumbrance bar
|
||||
batch.color = encumbCol
|
||||
Toolkit.fillArea(
|
||||
batch,
|
||||
encumbBarXPos, encumbBarYPos,
|
||||
if (getPlayerInventory().capacityMode == FixtureInventory.CAPACITY_MODE_NO_ENCUMBER)
|
||||
1f
|
||||
else // make sure 1px is always be seen
|
||||
minOf(UIInventoryCells.weightBarWidth, maxOf(1f, UIInventoryCells.weightBarWidth * encumbrancePerc)),
|
||||
UIInventoryFull.controlHelpHeight - 6f
|
||||
)
|
||||
|
||||
// chest name text
|
||||
batch.color = Color.WHITE
|
||||
App.fontGame.draw(batch, chestName, thisOffsetX + (cellsWidth - App.fontGame.getWidth(chestName)) / 2, thisOffsetY - 30)
|
||||
App.fontGame.draw(batch, playerName, thisOffsetX2 + (cellsWidth - App.fontGame.getWidth(playerName)) / 2, thisOffsetY - 30)
|
||||
|
||||
// control hint
|
||||
App.fontGame.draw(batch, controlHelp, thisOffsetX - 34f, encumbBarYPos - 3)
|
||||
|
||||
// encumb text
|
||||
batch.color = Color.WHITE
|
||||
App.fontGame.draw(batch, encumbranceText, encumbBarTextXPos, encumbBarYPos - 3f)
|
||||
}
|
||||
|
||||
override fun doOpening(delta: Float) {
|
||||
INGAME.pause()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
override fun doClosing(delta: Float) {
|
||||
INGAME.resume()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
override fun endOpening(delta: Float) {
|
||||
}
|
||||
|
||||
override fun endClosing(delta: Float) {
|
||||
UIItemInventoryItemGrid.tooltipShowing.clear()
|
||||
INGAME.setTooltipMessage(null) // required!
|
||||
}
|
||||
|
||||
|
||||
override fun dispose() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,11 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.gameactors.AVKey
|
||||
import net.torvald.terrarum.gameworld.fmod
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureWorldPortal
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_Y
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.getCellCountVertically
|
||||
import net.torvald.terrarum.realestate.LandUtil.CHUNK_H
|
||||
@@ -28,8 +31,16 @@ import java.time.Instant
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
import java.util.zip.GZIPInputStream
|
||||
import kotlin.math.ceil
|
||||
|
||||
/**
|
||||
*
|
||||
* "Teleport" button sets the destination and makes the portal surface to 'glow' to indicate the teleportation is ready.
|
||||
* Teleportation is initiated with a rising edge of a logic signal.
|
||||
*
|
||||
* "New World" sets the parameter of the new world, and make the new (not yet generated) world as the teleportation target.
|
||||
* THe world generation will be done when the "teleportation" is on going.
|
||||
*
|
||||
* Created by minjaesong on 2023-05-19.
|
||||
*/
|
||||
class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
@@ -47,31 +58,67 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
private val textAreaW = thumbw - 32
|
||||
private val thumbh = 252
|
||||
private val hx = Toolkit.drawWidth.div(2)
|
||||
private val y = INVENTORY_CELLS_OFFSET_Y() + 1
|
||||
private val y = INVENTORY_CELLS_OFFSET_Y() + 1 - 34
|
||||
|
||||
private val listCount = getCellCountVertically(UIItemWorldCellsSimple.height, gridGap)
|
||||
private val listHeight = UIItemWorldCellsSimple.height + (listCount - 1) * (UIItemWorldCellsSimple.height + gridGap)
|
||||
|
||||
private val memoryGaugeWidth = textAreaW
|
||||
private val deleteButtonWidth = (thumbw - gridGap) / 2
|
||||
private val buttonDeleteWorld = UIItemTextButton(this,
|
||||
"MENU_LABEL_DELETE",
|
||||
private val buttonsY = y + listHeight + gridGap
|
||||
|
||||
private val buttonSearch = UIItemTextButton(this,
|
||||
"CONTEXT_WORLD_NEW",
|
||||
hx - gridGap/2 - 2*deleteButtonWidth - gridGap,
|
||||
buttonsY,
|
||||
deleteButtonWidth,
|
||||
readFromLang = true,
|
||||
hasBorder = true,
|
||||
alignment = UIItemTextButton.Companion.Alignment.CENTRE
|
||||
).also {
|
||||
it.clickOnceListener = { _,_ ->
|
||||
full.requestTransition(1)
|
||||
}
|
||||
}
|
||||
private val buttonTeleport = UIItemTextButton(this,
|
||||
"GAME_ACTION_TELEPORT",
|
||||
hx - gridGap/2 - deleteButtonWidth,
|
||||
y + listHeight - buttonHeight,
|
||||
buttonsY,
|
||||
deleteButtonWidth,
|
||||
readFromLang = true,
|
||||
hasBorder = true,
|
||||
alignment = UIItemTextButton.Companion.Alignment.CENTRE
|
||||
)
|
||||
private val buttonRenameWorld = UIItemTextButton(this,
|
||||
).also {
|
||||
it.clickOnceListener = { _,_ ->
|
||||
if (selected?.worldInfo != null) {
|
||||
full.host.teleportRequest = FixtureWorldPortal.TeleportRequest(
|
||||
selected?.worldInfo?.diskSkimmer, null
|
||||
)
|
||||
full.setAsClose()
|
||||
printdbg(this, "Teleport target set: ${full.host.teleportRequest}")
|
||||
}
|
||||
}
|
||||
}
|
||||
private val buttonRename = UIItemTextButton(this,
|
||||
"MENU_LABEL_RENAME",
|
||||
buttonDeleteWorld.posX - gridGap - deleteButtonWidth,
|
||||
y + listHeight - buttonHeight,
|
||||
hx + gridGap/2,
|
||||
buttonsY,
|
||||
deleteButtonWidth,
|
||||
readFromLang = true,
|
||||
hasBorder = true,
|
||||
alignment = UIItemTextButton.Companion.Alignment.CENTRE
|
||||
)
|
||||
private val buttonDelete = UIItemTextButton(this,
|
||||
"MENU_LABEL_DELETE",
|
||||
hx + gridGap/2 + deleteButtonWidth + gridGap,
|
||||
buttonsY,
|
||||
deleteButtonWidth,
|
||||
readFromLang = true,
|
||||
hasBorder = true,
|
||||
alignment = UIItemTextButton.Companion.Alignment.CENTRE
|
||||
)
|
||||
|
||||
private val navRemoCon = UIItemListNavBarVertical(full, hx + 6 + UIItemWorldCellsSimple.width, y + 7, listHeight + 2, false)
|
||||
|
||||
private val worldList = ArrayList<WorldInfo>()
|
||||
data class WorldInfo(
|
||||
@@ -88,32 +135,54 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun disableListEditButtons() {
|
||||
buttonRename.isEnabled = false
|
||||
buttonDelete.isEnabled = false
|
||||
buttonTeleport.isEnabled = false
|
||||
currentWorldSelected = false
|
||||
}
|
||||
|
||||
private fun highlightListEditButtons(info: WorldInfo?) {
|
||||
// will disable the delete and teleport button if the world is equal to the currently playing world
|
||||
|
||||
if (info == null) disableListEditButtons()
|
||||
else {
|
||||
buttonRename.isEnabled = true
|
||||
buttonDelete.isEnabled = info.uuid != INGAME.world.worldIndex
|
||||
buttonTeleport.isEnabled = info.uuid != INGAME.world.worldIndex
|
||||
currentWorldSelected = info.uuid == INGAME.world.worldIndex
|
||||
}
|
||||
}
|
||||
|
||||
private var currentWorldSelected = false
|
||||
|
||||
init {
|
||||
CommonResourcePool.addToLoadingList("terrarum-basegame-worldportalicons") {
|
||||
TextureRegionPack(ModMgr.getGdxFile("basegame", "gui/worldportal_catbar.tga"), 30, 20)
|
||||
}
|
||||
CommonResourcePool.loadAll()
|
||||
|
||||
navRemoCon.scrollUpListener = { _,_ -> scrollItemPage(-1) }
|
||||
navRemoCon.scrollDownListener = { _,_ -> scrollItemPage(1) }
|
||||
|
||||
addUIitem(buttonRenameWorld)
|
||||
addUIitem(buttonDeleteWorld)
|
||||
|
||||
addUIitem(buttonDelete)
|
||||
addUIitem(buttonRename)
|
||||
addUIitem(buttonTeleport)
|
||||
addUIitem(buttonSearch)
|
||||
addUIitem(navRemoCon)
|
||||
}
|
||||
|
||||
private var chunksUsed = 0
|
||||
private val chunksMax = 100000
|
||||
fun scrollItemPage(relativeAmount: Int) {
|
||||
listPage = if (listPageCount == 0) 0 else (listPage + relativeAmount).fmod(listPageCount)
|
||||
}
|
||||
|
||||
private lateinit var worldCells: Array<UIItemWorldCellsSimple>
|
||||
|
||||
private var selected: UIItemWorldCellsSimple? = null
|
||||
private var selectedIndex: Int? = null
|
||||
|
||||
override fun show() {
|
||||
private fun readWorldList() {
|
||||
worldList.clear()
|
||||
(INGAME.actorGamer.actorValue.getAsString(AVKey.WORLD_PORTAL_DICT) ?: "").split(",").filter { it.isNotBlank() }.map {
|
||||
it.ascii85toUUID().let { it to App.savegameWorlds[it] }
|
||||
}.filter { it.second != null }.mapIndexed { index, (uuid, disk) ->
|
||||
}.filter { it.second != null }.mapIndexed { index, (uuid, disk0) ->
|
||||
|
||||
val disk = disk0!!.loadable()
|
||||
var chunksCount = 0
|
||||
var seed = 0L
|
||||
var lastPlayed = 0L
|
||||
@@ -122,6 +191,8 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
var h = 0
|
||||
var thumb: TextureRegion? = null
|
||||
|
||||
disk.rebuild()
|
||||
|
||||
JsonFetcher.readFromJsonString(ByteArray64Reader(disk!!.requestFile(-1)!!.contents.getContent() as ByteArray64, Common.CHARSET)).let {
|
||||
JsonFetcher.forEachSiblings(it) { name, value ->
|
||||
if (name == "width") w = value.asInt()
|
||||
@@ -148,31 +219,63 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
}.let {
|
||||
worldList.addAll(it)
|
||||
}
|
||||
|
||||
|
||||
|
||||
chunksUsed = worldList.sumOf { it.dimensionInChunks }
|
||||
listPageCount = ceil(worldList.size.toDouble() / listCount).toInt()
|
||||
}
|
||||
|
||||
worldCells = Array(maxOf(worldList.size, listCount)) {
|
||||
private var chunksUsed = 0
|
||||
private val chunksMax = 100000
|
||||
|
||||
private lateinit var worldCells: Array<UIItemWorldCellsSimple>
|
||||
|
||||
private var selected: UIItemWorldCellsSimple? = null
|
||||
private var selectedIndex: Int? = null
|
||||
|
||||
var listPage
|
||||
set(value) {
|
||||
navRemoCon.itemPage = if (listPageCount == 0) 0 else (value).fmod(listPageCount)
|
||||
rebuildList()
|
||||
}
|
||||
get() = navRemoCon.itemPage
|
||||
|
||||
var listPageCount // TODO total size of current category / items.size
|
||||
protected set(value) {
|
||||
navRemoCon.itemPageCount = value
|
||||
}
|
||||
get() = navRemoCon.itemPageCount
|
||||
|
||||
private fun rebuildList() {
|
||||
worldCells = Array(listCount) { it0 ->
|
||||
val it = it0 + listCount * listPage
|
||||
UIItemWorldCellsSimple(
|
||||
this,
|
||||
hx + gridGap / 2,
|
||||
y + (gridGap + UIItemWorldCellsSimple.height) * it,
|
||||
y + (gridGap + UIItemWorldCellsSimple.height) * it0,
|
||||
worldList.getOrNull(it),
|
||||
worldList.getOrNull(it)?.diskSkimmer?.getDiskName(Common.CHARSET)
|
||||
).also { button ->
|
||||
button.clickOnceListener = { _, _, _ ->
|
||||
button.clickOnceListener = { _, _ ->
|
||||
selected = button
|
||||
selectedIndex = it
|
||||
highlightListEditButtons(worldList.getOrNull(it))
|
||||
updateUIbyButtonSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
listPage = 0
|
||||
|
||||
readWorldList()
|
||||
|
||||
rebuildList()
|
||||
|
||||
uiItems.forEach { it.show() }
|
||||
worldCells.forEach { it.show() }
|
||||
selected = null
|
||||
|
||||
disableListEditButtons()
|
||||
updateUIbyButtonSelection()
|
||||
}
|
||||
|
||||
@@ -200,7 +303,11 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
|
||||
override fun updateUI(delta: Float) {
|
||||
uiItems.forEach { it.update(delta) }
|
||||
worldCells.forEach { it.update(delta) }
|
||||
if (::worldCells.isInitialized) worldCells.forEach { it.update(delta) }
|
||||
|
||||
if (currentWorldSelected) {
|
||||
INGAME.setTooltipMessage(if (buttonTeleport.mouseUp || buttonDelete.mouseUp) Lang["CONTEXT_THIS_IS_A_WORLD_CURRENTLY_PLAYING"] else null)
|
||||
}
|
||||
}
|
||||
|
||||
private val iconGap = 12f
|
||||
@@ -231,7 +338,7 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
val icons = CommonResourcePool.getAsTextureRegionPack("terrarum-basegame-worldportalicons")
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
val memoryGaugeXpos = hx - memoryGaugeWidth - gridGap/2
|
||||
val memoryGaugeYpos = y + listHeight - buttonHeight - gridGap - buttonHeight
|
||||
val memoryGaugeYpos = y + listHeight - buttonHeight
|
||||
val textXpos = memoryGaugeXpos + 3
|
||||
|
||||
// draw background //
|
||||
@@ -291,7 +398,7 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
|
||||
|
||||
uiItems.forEach { it.render(batch, camera) }
|
||||
worldCells.forEach { it.render(batch, camera) }
|
||||
if (::worldCells.isInitialized) worldCells.forEach { it.render(batch, camera) }
|
||||
|
||||
// control hints
|
||||
batch.color = Color.WHITE
|
||||
@@ -300,15 +407,15 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
|
||||
override fun hide() {
|
||||
uiItems.forEach { it.hide() }
|
||||
worldCells.forEach { it.hide() }
|
||||
if (::worldCells.isInitialized) worldCells.forEach { it.hide() }
|
||||
|
||||
worldCells.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
|
||||
if (::worldCells.isInitialized) worldCells.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
|
||||
worldList.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
uiItems.forEach { it.dispose() }
|
||||
worldCells.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
|
||||
if (::worldCells.isInitialized) worldCells.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
|
||||
worldList.forEach { try { it.dispose() } catch (_: GdxRuntimeException) {} }
|
||||
}
|
||||
|
||||
@@ -325,17 +432,50 @@ class UIWorldPortalListing(val full: UIWorldPortal) : UICanvas() {
|
||||
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
if (this.isVisible) {
|
||||
uiItems.forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||
worldCells.forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||
if (::worldCells.isInitialized) worldCells.forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||
handler.subUIs.forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||
return true
|
||||
}
|
||||
else return false
|
||||
}
|
||||
|
||||
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||
if (this.isVisible) {
|
||||
uiItems.forEach { it.scrolled(amountX, amountY) }
|
||||
if (::worldCells.isInitialized) worldCells.forEach { it.scrolled(amountX, amountY) }
|
||||
handler.subUIs.forEach { it.scrolled(amountX, amountY) }
|
||||
return true
|
||||
}
|
||||
else return false
|
||||
}
|
||||
|
||||
override fun doOpening(delta: Float) {
|
||||
super.doOpening(delta)
|
||||
INGAME.pause()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
override fun doClosing(delta: Float) {
|
||||
super.doClosing(delta)
|
||||
INGAME.resume()
|
||||
INGAME.setTooltipMessage(null)
|
||||
}
|
||||
|
||||
override fun endOpening(delta: Float) {
|
||||
super.endOpening(delta)
|
||||
}
|
||||
|
||||
override fun endClosing(delta: Float) {
|
||||
super.endClosing(delta)
|
||||
UIItemInventoryItemGrid.tooltipShowing.clear()
|
||||
INGAME.setTooltipMessage(null) // required!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class UIItemWorldCellsSimple(
|
||||
parent: UIWorldPortalListing,
|
||||
val parent: UIWorldPortalListing,
|
||||
initialX: Int,
|
||||
initialY: Int,
|
||||
internal val worldInfo: UIWorldPortalListing.WorldInfo? = null,
|
||||
@@ -396,6 +536,15 @@ class UIItemWorldCellsSimple(
|
||||
|
||||
}
|
||||
|
||||
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||
|
||||
if (mouseUp) {
|
||||
parent.scrollItemPage(amountY.toInt())
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,174 @@
|
||||
package net.torvald.terrarum.modulebasegame.ui
|
||||
|
||||
import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.Texture
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.random.XXHash64
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.ui.UICanvas
|
||||
import net.torvald.terrarum.ModMgr
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
|
||||
import net.torvald.terrarum.langpack.Lang
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.modulebasegame.WorldgenLoadScreen
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureWorldPortal
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
|
||||
import net.torvald.terrarum.modulebasegame.serialise.ReadActor
|
||||
import net.torvald.terrarum.modulebasegame.ui.UIInventoryFull.Companion.INVENTORY_CELLS_OFFSET_Y
|
||||
import net.torvald.terrarum.savegame.ByteArray64Reader
|
||||
import net.torvald.terrarum.savegame.VDFileID
|
||||
import net.torvald.terrarum.savegame.VirtualDisk
|
||||
import net.torvald.terrarum.serialise.Common
|
||||
import net.torvald.terrarum.ui.*
|
||||
import net.torvald.terrarum.utils.RandomWordsName
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2023-05-19.
|
||||
*/
|
||||
class UIWorldPortalSearch(val full: UIWorldPortal) : UICanvas() {
|
||||
|
||||
override var width: Int = Toolkit.drawWidth
|
||||
override var height: Int = App.scr.height
|
||||
// override var width: Int = Toolkit.drawWidth
|
||||
// override var height: Int = App.scr.height
|
||||
|
||||
|
||||
|
||||
private val hugeTex = TextureRegion(Texture(ModMgr.getGdxFile("basegame", "gui/huge.png")))
|
||||
private val largeTex = TextureRegion(Texture(ModMgr.getGdxFile("basegame", "gui/large.png")))
|
||||
private val normalTex = TextureRegion(Texture(ModMgr.getGdxFile("basegame", "gui/normal.png")))
|
||||
private val smallTex = TextureRegion(Texture(ModMgr.getGdxFile("basegame", "gui/small.png")))
|
||||
|
||||
private val tex = arrayOf(smallTex, normalTex, largeTex, hugeTex)
|
||||
|
||||
override var width = 480
|
||||
override var height = 480
|
||||
|
||||
private val drawX = (Toolkit.drawWidth - width) / 2
|
||||
private val drawY = (App.scr.height - height) / 2
|
||||
|
||||
private val radioCellWidth = 116
|
||||
private val inputWidth = 340
|
||||
private val radioX = (width - (radioCellWidth * tex.size + 9)) / 2
|
||||
private val inputX = width - inputWidth
|
||||
|
||||
private val sizeSelY = 186 + 40
|
||||
|
||||
private val sizeSelector = UIItemInlineRadioButtons(this,
|
||||
drawX + radioX, drawY + sizeSelY, radioCellWidth,
|
||||
listOf(
|
||||
{ Lang["CONTEXT_DESCRIPTION_TINY"] },
|
||||
{ Lang["CONTEXT_DESCRIPTION_SMALL"] },
|
||||
{ Lang["CONTEXT_DESCRIPTION_BIG"] },
|
||||
{ Lang["CONTEXT_DESCRIPTION_HUGE"] }
|
||||
)
|
||||
)
|
||||
|
||||
private val rng = HQRNG()
|
||||
|
||||
private val nameInput = UIItemTextLineInput(this,
|
||||
drawX + width - inputWidth, drawY + sizeSelY + 80, inputWidth,
|
||||
{ RandomWordsName(4) }, InputLenCap(VirtualDisk.NAME_LENGTH, InputLenCap.CharLenUnit.UTF8_BYTES)
|
||||
)
|
||||
|
||||
private val seedInput = UIItemTextLineInput(this,
|
||||
drawX + width - inputWidth, drawY + sizeSelY + 120, inputWidth,
|
||||
{ rng.nextLong().toString() }, InputLenCap(256, InputLenCap.CharLenUnit.CODEPOINTS)
|
||||
)
|
||||
|
||||
private val goButtonWidth = 180
|
||||
private val backButton = UIItemTextButton(this, "MENU_LABEL_BACK", drawX + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
||||
private val goButton = UIItemTextButton(this, "MENU_LABEL_CONFIRM_BUTTON", drawX + width/2 + (width/2 - goButtonWidth) / 2, drawY + height - 24, goButtonWidth, true, alignment = UIItemTextButton.Companion.Alignment.CENTRE, hasBorder = true)
|
||||
|
||||
init {
|
||||
goButton.clickOnceListener = { _, _ ->
|
||||
val seed = try {
|
||||
seedInput.getTextOrPlaceholder().toLong()
|
||||
}
|
||||
catch (e: NumberFormatException) {
|
||||
XXHash64.hash(seedInput.getTextOrPlaceholder().toByteArray(Charsets.UTF_8), 10000)
|
||||
}
|
||||
val (wx, wy) = TerrarumIngame.WORLDPORTAL_NEW_WORLD_SIZE[sizeSelector.selection]
|
||||
val worldParam = TerrarumIngame.NewWorldParameters(wx, wy, seed, nameInput.getTextOrPlaceholder())
|
||||
full.host.teleportRequest = FixtureWorldPortal.TeleportRequest(null, worldParam)
|
||||
full.setAsClose()
|
||||
}
|
||||
backButton.clickOnceListener = { _, _ ->
|
||||
full.requestTransition(0)
|
||||
}
|
||||
|
||||
addUIitem(sizeSelector)
|
||||
addUIitem(seedInput) // order is important
|
||||
addUIitem(nameInput) // because of the IME candidates overlay
|
||||
addUIitem(goButton)
|
||||
addUIitem(backButton)
|
||||
}
|
||||
|
||||
|
||||
override fun show() {
|
||||
uiItems.forEach { it.show() }
|
||||
seedInput.clearText()
|
||||
seedInput.refreshPlaceholder()
|
||||
nameInput.clearText()
|
||||
nameInput.refreshPlaceholder()
|
||||
}
|
||||
|
||||
private var oldPosX = full.posX
|
||||
|
||||
override fun updateUI(delta: Float) {
|
||||
TODO("Not yet implemented")
|
||||
uiItems.forEach { it.update(delta) }
|
||||
}
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
TODO("Not yet implemented")
|
||||
val posXDelta = posX - oldPosX
|
||||
|
||||
// ugh why won't you just scroll along??
|
||||
seedInput.posX += posXDelta
|
||||
nameInput.posX += posXDelta
|
||||
goButton.posX += posXDelta
|
||||
backButton.posX += posXDelta
|
||||
|
||||
|
||||
|
||||
batch.color = Color.WHITE
|
||||
// ui title
|
||||
val titlestr = Lang["CONTEXT_WORLD_NEW"]
|
||||
App.fontUITitle.draw(batch, titlestr, drawX + (width - App.fontUITitle.getWidth(titlestr)).div(2).toFloat(), INVENTORY_CELLS_OFFSET_Y() - 72f)
|
||||
|
||||
// draw size previews
|
||||
val texture = tex[sizeSelector.selection.coerceAtMost(tex.lastIndex)]
|
||||
val tx = drawX + (width - texture.regionWidth) / 2
|
||||
val ty = drawY + (160 - texture.regionHeight) / 2
|
||||
batch.draw(texture, tx.toFloat(), ty.toFloat())
|
||||
// border
|
||||
batch.color = Toolkit.Theme.COL_INACTIVE
|
||||
Toolkit.drawBoxBorder(batch, tx - 1, ty - 1, texture.regionWidth + 2, texture.regionHeight + 2)
|
||||
|
||||
batch.color = Color.WHITE
|
||||
// size selector title
|
||||
val sizestr = Lang["MENU_OPTIONS_SIZE"]
|
||||
App.fontGame.draw(batch, sizestr, drawX + (width - App.fontGame.getWidth(sizestr)).div(2).toFloat(), drawY + sizeSelY - 40f)
|
||||
|
||||
// name/seed input labels
|
||||
App.fontGame.draw(batch, Lang["MENU_NAME"], drawX, drawY + sizeSelY + 80)
|
||||
App.fontGame.draw(batch, Lang["CONTEXT_GENERATOR_SEED"], drawX, drawY + sizeSelY + 120)
|
||||
|
||||
uiItems.forEach { it.render(batch, camera) }
|
||||
|
||||
|
||||
oldPosX = posX
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
uiItems.forEach { it.hide() }
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
TODO("Not yet implemented")
|
||||
hugeTex.texture.dispose()
|
||||
largeTex.texture.dispose()
|
||||
normalTex.texture.dispose()
|
||||
smallTex.texture.dispose()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,13 +50,13 @@ removefile:
|
||||
|
||||
fun checkFileSanity() {
|
||||
if (!diskFile.exists()) throw NoSuchFileException(diskFile.absoluteFile)
|
||||
if (diskFile.length() < 310L) throw RuntimeException("Invalid Virtual Disk file!")
|
||||
if (diskFile.length() < 310L) throw RuntimeException("Invalid Virtual Disk file: ${diskFile.path}")
|
||||
|
||||
// check magic
|
||||
val fis = FileInputStream(diskFile)
|
||||
|
||||
val magic = ByteArray(4).let { fis.read(it); it }
|
||||
if (!magic.contentEquals(VirtualDisk.MAGIC)) throw RuntimeException("Invalid Virtual Disk file!")
|
||||
if (!magic.contentEquals(VirtualDisk.MAGIC)) throw RuntimeException("Invalid Virtual Disk file: ${diskFile.path}")
|
||||
|
||||
fis.close()
|
||||
}
|
||||
@@ -221,7 +221,7 @@ removefile:
|
||||
fa.read(4).toIntBig().toLong()
|
||||
}
|
||||
DiskEntry.SYMLINK -> 8L
|
||||
else -> throw UnsupportedOperationException("Unsupported entry type: $fileFlag") // FIXME no support for compressed file
|
||||
else -> throw UnsupportedOperationException("Unsupported entry type: $fileFlag for entryID $entryID at offset $offset") // FIXME no support for compressed file
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ removefile:
|
||||
|
||||
EntrySymlink(target)
|
||||
}
|
||||
else -> throw UnsupportedOperationException("Unsupported entry type: $fileFlag") // FIXME no support for compressed file
|
||||
else -> throw UnsupportedOperationException("Unsupported entry type: $fileFlag for entryID $entryID at offset $offset") // FIXME no support for compressed file
|
||||
}
|
||||
|
||||
return DiskEntry(entryID, parent, creationTime, modifyTime, entryContent)
|
||||
|
||||
@@ -503,8 +503,8 @@ object VDUtil {
|
||||
* Throws an exception if specified size cannot fit into the disk
|
||||
*/
|
||||
fun VirtualDisk.checkCapacity(newSize: Long) {
|
||||
if (this.usedBytes + newSize > this.capacity)
|
||||
throw IOException("Not enough space on the disk")
|
||||
// if (this.usedBytes + newSize > this.capacity)
|
||||
// throw IOException("Not enough space on the disk")
|
||||
}
|
||||
fun ByteArray64.toIntBig(): Int {
|
||||
if (this.size != 4L)
|
||||
|
||||
@@ -72,7 +72,7 @@ Version 254 is a customised version of TEVD tailored to be used as a savegame fo
|
||||
Int8 Disk properties flag 1
|
||||
0th bit: readonly
|
||||
Int8 Save type (0b 0000 00ab)
|
||||
b: unset - full save; set - quick save
|
||||
b: unset - full save; set - quicksave (only applicable to worlds -- quicksave just means the disk is in dirty state)
|
||||
a: set - generated by autosave
|
||||
Int8 Kind of the Save file
|
||||
0: Undefined (or very old version of the game)
|
||||
@@ -145,9 +145,7 @@ class VirtualDisk(
|
||||
|
||||
var extraInfoBytes = ByteArray(16)
|
||||
val entries = HashMap<EntryID, DiskEntry>()
|
||||
var isReadOnly: Boolean
|
||||
set(value) { extraInfoBytes[0] = (extraInfoBytes[0] and 0xFE.toByte()) or value.toBit() }
|
||||
get() = capacity == 0L || (extraInfoBytes.size > 0 && extraInfoBytes[0].and(1) == 1.toByte())
|
||||
val isReadOnly = false
|
||||
var saveMode: Int
|
||||
set(value) { extraInfoBytes[1] = value.toByte() }
|
||||
get() = extraInfoBytes[1].toUint()
|
||||
@@ -260,6 +258,7 @@ object VDFileID {
|
||||
const val SPRITEDEF = -2L
|
||||
const val SPRITEDEF_GLOW = -3L
|
||||
const val LOADORDER = -4L
|
||||
const val PLAYER_SCREENSHOT = -5L
|
||||
const val BODYPART_TO_ENTRY_MAP = -1025L
|
||||
const val BODYPARTGLOW_TO_ENTRY_MAP = -1026L
|
||||
}
|
||||
@@ -279,6 +278,11 @@ fun diskIDtoReadableFilename(id: EntryID, saveKind: Int?): String = when (id) {
|
||||
"spritedef-glow"
|
||||
else
|
||||
"file #$id"
|
||||
VDFileID.PLAYER_SCREENSHOT ->
|
||||
if (saveKind == PLAYER_DATA)
|
||||
"screenshot.tga.gz"
|
||||
else
|
||||
"file #$id"
|
||||
VDFileID.LOADORDER -> "loadOrder.txt"
|
||||
// -16L -> "blockcodex.json.gz"
|
||||
// -17L -> "itemcodex.json.gz"
|
||||
|
||||
@@ -415,41 +415,6 @@ class VirtualDiskCracker(val sysCharset: Charset = Charsets.UTF_8) : JFrame() {
|
||||
}
|
||||
}
|
||||
})
|
||||
menuEdit.add("Resize Disk…").addMouseListener(object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent?) {
|
||||
if (vdisk != null) {
|
||||
try {
|
||||
val dialog = OptionSize()
|
||||
val confirmed = dialog.showDialog("Input") == JOptionPane.OK_OPTION
|
||||
if (confirmed) {
|
||||
vdisk!!.capacity = (dialog.capacity.value as Long).toLong()
|
||||
updateDiskInfo()
|
||||
setStat("Disk resized")
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
popupError(e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
menuEdit.addSeparator()
|
||||
menuEdit.add("Set/Unset Write Protection").addMouseListener(object : MouseAdapter() {
|
||||
override fun mousePressed(e: MouseEvent?) {
|
||||
if (vdisk != null) {
|
||||
try {
|
||||
vdisk!!.isReadOnly = vdisk!!.isReadOnly.not()
|
||||
updateDiskInfo()
|
||||
setStat("Disk write protection ${if (vdisk!!.isReadOnly) "" else "dis"}engaged")
|
||||
}
|
||||
catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
popupError(e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
menuBar.add(menuEdit)
|
||||
|
||||
val menuManage = JMenu("Manage")
|
||||
@@ -638,8 +603,7 @@ class VirtualDiskCracker(val sysCharset: Charset = Charsets.UTF_8) : JFrame() {
|
||||
}
|
||||
private fun getDiskInfoText(disk: VirtualDisk): String {
|
||||
return """Name: ${String(disk.diskName, sysCharset)}
|
||||
Capacity: ${disk.capacity} bytes (${disk.usedBytes} bytes used, ${disk.capacity - disk.usedBytes} bytes free)
|
||||
Write protected: ${disk.isReadOnly.toEnglish()}"""
|
||||
Capacity: ${disk.capacity} bytes (${disk.usedBytes} bytes used, ${disk.capacity - disk.usedBytes} bytes free)"""
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ class DummyTogglePane : UICanvas() {
|
||||
private var timer = 0f
|
||||
|
||||
init {
|
||||
button1.clickOnceListener = { _,_,_ ->
|
||||
button1.clickOnceListener = { _,_ ->
|
||||
button1.toggle()
|
||||
}
|
||||
uiItems.add(button1)
|
||||
|
||||
@@ -377,7 +377,7 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
|
||||
blendNormalStraightAlpha(batch)
|
||||
|
||||
batch.end()
|
||||
/*batch.end()
|
||||
gdxBlendNormalStraightAlpha()
|
||||
Terrarum.inShapeRenderer {
|
||||
it.color = uiColour
|
||||
@@ -388,7 +388,7 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
it.line(uiX + halfW, App.scr.height - (uiY + halfH), uiX + halfW + pointDX, App.scr.height - (uiY + halfH + pointDY))
|
||||
it.color = Color.GRAY
|
||||
}
|
||||
batch.begin()
|
||||
batch.begin()*/
|
||||
|
||||
App.fontSmallNumbers.draw(batch, gamepad.getName(), Toolkit.drawWidth - (gamepad.getName().length + 2f) * TinyAlphNum.W, uiY.toFloat() + h + 2)
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class ConsoleWindow : UICanvas() {
|
||||
init {
|
||||
reset()
|
||||
addUIitem(textinput)
|
||||
textinput.isActive = false
|
||||
textinput.isEnabled = false
|
||||
}
|
||||
|
||||
private val lb = ArrayList<String>()
|
||||
@@ -99,7 +99,7 @@ class ConsoleWindow : UICanvas() {
|
||||
clickLatched = false
|
||||
}
|
||||
|
||||
textinput.isActive = (isOpened && !isClosing)
|
||||
textinput.isEnabled = (isOpened && !isClosing)
|
||||
}
|
||||
|
||||
override fun renderUI(batch: SpriteBatch, camera: Camera) {
|
||||
@@ -269,14 +269,14 @@ class ConsoleWindow : UICanvas() {
|
||||
drawOffY = MovementInterpolator.fastPullOut(openingTimeCounter.toFloat() / openCloseTime.toFloat(),
|
||||
0f, -height.toFloat()
|
||||
)*/
|
||||
textinput.isActive = false
|
||||
textinput.isEnabled = false
|
||||
textinput.mouseoverUpdateLatch = false
|
||||
}
|
||||
|
||||
override fun endOpening(delta: Float) {
|
||||
drawOffY = 0f
|
||||
openingTimeCounter = 0f
|
||||
textinput.isActive = true
|
||||
textinput.isEnabled = true
|
||||
textinput.mouseoverUpdateLatch = true
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.torvald.terrarum.ui
|
||||
|
||||
import com.badlogic.gdx.Gdx
|
||||
import com.badlogic.gdx.graphics.*
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import com.badlogic.gdx.graphics.glutils.FloatFrameBuffer
|
||||
@@ -9,6 +10,7 @@ import com.badlogic.gdx.utils.Disposable
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.random.HQRNG
|
||||
import net.torvald.terrarum.*
|
||||
import net.torvald.terrarumsansbitmap.gdx.TerrarumSansBitmap
|
||||
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@@ -28,6 +30,8 @@ object Toolkit : Disposable {
|
||||
val COL_SELECTED = Color(0x00f8ff_ff) // cyan, HIGHLY SATURATED
|
||||
val COL_MOUSE_UP = Color(0xfff066_ff.toInt()) // yellow (all yellows are of low saturation according to the colour science)
|
||||
val COL_DISABLED = Color(0xaaaaaaff.toInt())
|
||||
val COL_RED = Color(0xff8888ff.toInt())
|
||||
val COL_REDD = Color(0xff4448ff.toInt())
|
||||
|
||||
/*
|
||||
Try this for alt colour set:
|
||||
@@ -93,6 +97,10 @@ object Toolkit : Disposable {
|
||||
get() = App.scr.width - if (App.getConfigBoolean("fx_streamerslayout")) App.scr.chatWidth else 0
|
||||
val drawWidthf: Float
|
||||
get() = drawWidth.toFloat()
|
||||
val hdrawWidth: Int
|
||||
get() = drawWidth / 2
|
||||
val hdrawWidthf: Float
|
||||
get() = hdrawWidth.toFloat()
|
||||
|
||||
fun drawCentered(batch: SpriteBatch, image: Texture, screenPosY: Int, ui: UICanvas? = null) {
|
||||
val imageW = image.width
|
||||
@@ -114,6 +122,11 @@ object Toolkit : Disposable {
|
||||
batch.draw(image, targetW.minus(imageW).ushr(1).toFloat() + offsetX, screenPosY.toFloat() + offsetY)
|
||||
}
|
||||
|
||||
fun drawTextCentered(batch: SpriteBatch, font: TerrarumSansBitmap, text: String, tbw: Int, tbx: Int, tby: Int) {
|
||||
val tw = font.getWidth(text)
|
||||
font.draw(batch, text, tbx + (tbw - tw) / 2, tby)
|
||||
}
|
||||
|
||||
fun fillArea(batch: SpriteBatch, x: Int, y: Int, w: Int, h: Int) {
|
||||
batch.draw(textureWhiteSquare, x.toFloat(), y.toFloat(), w.toFloat(), h.toFloat())
|
||||
}
|
||||
@@ -250,7 +263,7 @@ object Toolkit : Disposable {
|
||||
(batch as FlippingSpriteBatch).drawFlipped(fboBlur.colorBufferTexture, x.toFloat(), y.toFloat())
|
||||
}
|
||||
|
||||
fun drawBaloon(batch: SpriteBatch, x: Float, y: Float, w: Float, h: Float) {
|
||||
fun drawBaloon(batch: SpriteBatch, x: Float, y: Float, w: Float, h: Float, opacity: Float = 1f) {
|
||||
// centre area
|
||||
/*batch.draw(baloonTile.get(1, 1), x, y, w, h)
|
||||
|
||||
@@ -267,9 +280,9 @@ object Toolkit : Disposable {
|
||||
batch.draw(baloonTile.get(0, 2), x - baloonTile.tileW, y + h)*/
|
||||
|
||||
|
||||
batch.color = Theme.COL_CELL_FILL_OPAQUE
|
||||
batch.color = Theme.COL_CELL_FILL_OPAQUE.cpy().mul(1f,1f,1f,opacity)
|
||||
fillArea(batch, x - 4, y - 4, w + 8, h + 8)
|
||||
batch.color = Theme.COL_INACTIVE
|
||||
batch.color = Theme.COL_INACTIVE.cpy().mul(1f,1f,1f,opacity)
|
||||
drawBoxBorder(batch, x - 4, y - 4, w + 8, h + 8)
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class UIAutosaveNotifier : UICanvas() {
|
||||
private var errored = false
|
||||
|
||||
private var normalCol = Color.WHITE
|
||||
private var errorCol = Color(0xFF8888FF.toInt())
|
||||
private var errorCol = Toolkit.Theme.COL_RED
|
||||
|
||||
override fun updateUI(delta: Float) {
|
||||
spinnerTimer += delta
|
||||
|
||||
@@ -128,9 +128,15 @@ abstract class UICanvas(
|
||||
|
||||
|
||||
/** A function that is run ONCE when the UI is requested to be opened; will work identical to [endOpening] if [openCloseTime] is zero */
|
||||
open fun show() {}
|
||||
open fun show() {
|
||||
uiItems.forEach { it.show() }
|
||||
handler.subUIs.forEach { it.show() }
|
||||
}
|
||||
/** A function that is run ONCE when the UI is requested to be closed; will work identical to [endClosing] if [openCloseTime] is zero */
|
||||
open fun hide() {}
|
||||
open fun hide() {
|
||||
uiItems.forEach { it.hide() }
|
||||
handler.subUIs.forEach { it.hide() }
|
||||
}
|
||||
|
||||
|
||||
/** **DO NOT CALL THIS FUNCTION FOR THE ACTUAL UPDATING OF THE UI — USE update() INSTEAD**
|
||||
@@ -184,7 +190,7 @@ abstract class UICanvas(
|
||||
if (!uiItems.contains(uiItem)) uiItems.add(uiItem)
|
||||
}
|
||||
|
||||
fun mouseInScreen(x: Int, y: Int) = x in 0 until App.scr.width && y in 0 until App.scr.height
|
||||
fun mouseInScreen(x: Int, y: Int) = x in 0 until App.scr.windowW && y in 0 until App.scr.windowH
|
||||
|
||||
/**
|
||||
* Called by the screen's InputProcessor
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.torvald.terrarum.ui
|
||||
import com.badlogic.gdx.graphics.Camera
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.utils.Disposable
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
|
||||
|
||||
@@ -95,31 +96,33 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
||||
protected var mouseLatched = Terrarum.mouseDown
|
||||
|
||||
/** UI to call (show up) while mouse is up */
|
||||
open val mouseOverCall: UICanvas? = null
|
||||
open var mouseOverCall: UICanvas? = null
|
||||
|
||||
|
||||
// kind of listener implementation
|
||||
/** Fired once for every update
|
||||
* Parameter: delta */
|
||||
open var updateListener: ((Float) -> Unit)? = null
|
||||
open var updateListener: ((Float) -> Unit) = {}
|
||||
/** Parameter: keycode */
|
||||
open var keyDownListener: ((Int) -> Unit)? = null
|
||||
open var keyDownListener: ((Int) -> Unit) = {}
|
||||
/** Parameter: keycode */
|
||||
open var keyUpListener: ((Int) -> Unit)? = null
|
||||
open var keyTypedListener: ((Char) -> Unit)? = null
|
||||
open var touchDraggedListener: ((Int, Int, Int) -> Unit)? = null
|
||||
open var keyUpListener: ((Int) -> Unit) = {}
|
||||
open var keyTypedListener: ((Char) -> Unit) = {}
|
||||
open var touchDraggedListener: ((Int, Int, Int) -> Unit) = { _,_,_ -> }
|
||||
/** Parameters: screenX, screenY, pointer, button */
|
||||
open var touchDownListener: ((Int, Int, Int, Int) -> Unit)? = null
|
||||
open var touchUpListener: ((Int, Int, Int, Int) -> Unit)? = null
|
||||
@Deprecated("Not really deprecated but you MOST DEFINITELY want to use clickOnceListener(mouseX, mouseY) instead.")
|
||||
open var touchDownListener: ((Int, Int, Int, Int) -> Unit) = { _,_,_,_ -> }
|
||||
open var touchUpListener: ((Int, Int, Int, Int) -> Unit) = { _,_,_,_ -> }
|
||||
/** Parameters: amountX, amountY */
|
||||
open var scrolledListener: ((Float, Float) -> Unit)? = null
|
||||
/** Parameters: relative mouseX, relative mouseY, button
|
||||
*
|
||||
open var scrolledListener: ((Float, Float) -> Unit) = { _,_ -> }
|
||||
/** Parameters: relative mouseX, relative mouseY
|
||||
* ClickOnce listeners are only fired when clicked with primary mouse button
|
||||
* PROTIP: if clickOnceListener does not seem to work, make sure your parent UI is handling touchDown() and touchUp() events!
|
||||
*/
|
||||
open var clickOnceListener: ((Int, Int, Int) -> Unit)? = null
|
||||
open var clickOnceListener: ((Int, Int) -> Unit) = { _,_ -> }
|
||||
open var clickOnceListenerFired = false
|
||||
|
||||
/** Parameters: relative mouseX, mouseY */
|
||||
open var mouseUpListener: ((Int, Int) -> Unit) = { _,_, -> }
|
||||
|
||||
/** Since gamepads can't just choose which UIItem to control, this variable is used to allow processing of
|
||||
* gamepad button events for one or more UIItems in one or more UICanvases. */
|
||||
@@ -128,6 +131,11 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
||||
/**
|
||||
* Whether the button is "available" or not to the player
|
||||
*/
|
||||
open var isEnabled = true
|
||||
|
||||
/**
|
||||
* Whether the button should receive updates
|
||||
*/
|
||||
open var isActive = true
|
||||
|
||||
|
||||
@@ -137,28 +145,27 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
||||
|
||||
open fun update(delta: Float) {
|
||||
if (parentUI.isVisible) {
|
||||
if (updateListener != null) {
|
||||
updateListener!!.invoke(delta)
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
updateListener.invoke(delta)
|
||||
|
||||
mouseOverCall?.update(delta)
|
||||
|
||||
if (mouseUp) {
|
||||
if (mouseOverCall?.isVisible ?: false) {
|
||||
if (mouseOverCall?.isVisible == true) {
|
||||
mouseOverCall?.setAsOpen()
|
||||
}
|
||||
|
||||
mouseOverCall?.updateUI(delta)
|
||||
mouseUpListener.invoke(itemRelativeMouseX, itemRelativeMouseY)
|
||||
}
|
||||
else {
|
||||
if (mouseOverCall?.isVisible ?: false) {
|
||||
if (mouseOverCall?.isVisible == true) {
|
||||
mouseOverCall?.setAsClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mouseUp) mouseLatched = false
|
||||
if (!mouseUp) mouseLatched = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,36 +174,36 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
||||
*/
|
||||
open fun render(batch: SpriteBatch, camera: Camera) {
|
||||
if (parentUI.isVisible) {
|
||||
if (isActive) {
|
||||
// if (isActive) {
|
||||
mouseOverCall?.render(batch, camera)
|
||||
|
||||
if (mouseUp) {
|
||||
mouseOverCall?.renderUI(batch, camera)
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// keyboard controlled
|
||||
open fun keyDown(keycode: Int): Boolean {
|
||||
if (parentUI.isVisible && keyDownListener != null && isActive) {
|
||||
keyDownListener!!.invoke(keycode)
|
||||
if (parentUI.isVisible && isEnabled) {
|
||||
keyDownListener.invoke(keycode)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
open fun keyUp(keycode: Int): Boolean {
|
||||
if (parentUI.isVisible && keyUpListener != null && isActive) {
|
||||
keyUpListener!!.invoke(keycode)
|
||||
if (parentUI.isVisible && isEnabled) {
|
||||
keyUpListener.invoke(keycode)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
open fun keyTyped(character: Char): Boolean {
|
||||
if (parentUI.isVisible && keyTypedListener != null && isActive) {
|
||||
keyTypedListener!!.invoke(character)
|
||||
if (parentUI.isVisible && isEnabled) {
|
||||
keyTypedListener.invoke(character)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -205,8 +212,8 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
||||
|
||||
// mouse controlled
|
||||
open fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean {
|
||||
if (parentUI.isVisible && touchDraggedListener != null && isActive) {
|
||||
touchDraggedListener!!.invoke(itemRelativeMouseX, itemRelativeMouseY, pointer)
|
||||
if (parentUI.isVisible && isEnabled) {
|
||||
touchDraggedListener.invoke(itemRelativeMouseX, itemRelativeMouseY, pointer)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -215,14 +222,14 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
||||
open fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
var actionDone = false
|
||||
|
||||
if (parentUI.isVisible && isActive) {
|
||||
if (touchDownListener != null && mouseUp) {
|
||||
touchDownListener!!.invoke(itemRelativeMouseX, itemRelativeMouseY, pointer, button)
|
||||
if (parentUI.isVisible && isEnabled) {
|
||||
if (mouseUp) {
|
||||
touchDownListener.invoke(itemRelativeMouseX, itemRelativeMouseY, pointer, button)
|
||||
actionDone = true
|
||||
}
|
||||
|
||||
if (clickOnceListener != null && !clickOnceListenerFired && mouseUp) {
|
||||
clickOnceListener!!.invoke(itemRelativeMouseX, itemRelativeMouseY, button)
|
||||
if (!clickOnceListenerFired && mouseUp && button == App.getConfigInt("config_mouseprimary")) {
|
||||
clickOnceListener.invoke(itemRelativeMouseX, itemRelativeMouseY)
|
||||
actionDone = true
|
||||
}
|
||||
}
|
||||
@@ -232,16 +239,16 @@ abstract class UIItem(var parentUI: UICanvas, val initialX: Int, val initialY: I
|
||||
open fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
||||
clickOnceListenerFired = false
|
||||
|
||||
if (parentUI.isVisible && touchUpListener != null && mouseUp) {
|
||||
touchUpListener!!.invoke(itemRelativeMouseX, itemRelativeMouseY, pointer, button)
|
||||
if (parentUI.isVisible && mouseUp) {
|
||||
touchUpListener.invoke(itemRelativeMouseX, itemRelativeMouseY, pointer, button)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
open fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||
if (parentUI.isVisible && scrolledListener != null && mouseUp && isActive) {
|
||||
scrolledListener!!.invoke(amountX, amountY)
|
||||
if (parentUI.isVisible && mouseUp && isEnabled) {
|
||||
scrolledListener.invoke(amountX, amountY)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class UIItemHorizontalFadeSlide(
|
||||
//transitionLength: Float,
|
||||
currentPosition: Float,
|
||||
vararg uis: UICanvas
|
||||
) : UIItemTransitionContainer(parent, initialX, initialY, width, height, 0.10f, currentPosition, uis) {
|
||||
) : UIItemTransitionContainer(parent, initialX, initialY, width, height, 0.12f, currentPosition, uis) {
|
||||
|
||||
fun getOffX(index: Int) = ((currentPosition - index) * width / 2f).roundToInt()
|
||||
fun getOpacity(index: Int) = 1f - (currentPosition - index).absoluteValue.coerceIn(0f, 1f)
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import net.torvald.terrarum.BlendMode
|
||||
import net.torvald.terrarum.abs
|
||||
import net.torvald.terrarum.blendNormalStraightAlpha
|
||||
|
||||
/**
|
||||
@@ -33,14 +34,24 @@ open class UIItemImageButton(
|
||||
|
||||
initialX: Int,
|
||||
initialY: Int,
|
||||
/** this does NOT resize the image; use imageDrawWidth to actually resize the image */
|
||||
override val width: Int = image.regionWidth,
|
||||
/** this does NOT resize the image; use imageDrawHeight to actually resize the image */
|
||||
override val height: Int = image.regionHeight,
|
||||
|
||||
/** When clicked, toggle its "lit" status */
|
||||
var highlightable: Boolean
|
||||
var highlightable: Boolean,
|
||||
/** Changes the appearance to use a border instead of colour-changing image for highlighter */
|
||||
val useBorder: Boolean = false,
|
||||
|
||||
/** Image won't be place at right position if `image.regionWidth != imageDrawWidth`; define the `width` argument to avoid the issue */
|
||||
val imageDrawWidth: Int = image.regionWidth,
|
||||
/** Image won't be place at right position if `image.regionHeight != imageDrawHeight`; define the `height` argument to avoid the issue */
|
||||
val imageDrawHeight: Int = image.regionHeight,
|
||||
) : UIItem(parent, initialX, initialY) {
|
||||
|
||||
var highlighted = false
|
||||
var extraDrawOp: (UIItem, SpriteBatch) -> Unit = { _,_ -> }
|
||||
|
||||
override fun render(batch: SpriteBatch, camera: Camera) {
|
||||
// draw background
|
||||
@@ -60,15 +71,22 @@ open class UIItemImageButton(
|
||||
Toolkit.fillArea(batch, posX.toFloat(), posY.toFloat(), width.toFloat(), height.toFloat())
|
||||
}
|
||||
|
||||
blendNormalStraightAlpha(batch)
|
||||
|
||||
// draw image
|
||||
blendNormalStraightAlpha(batch)
|
||||
batch.color = if (highlighted) highlightCol
|
||||
else if (mouseUp) activeCol
|
||||
else if (useBorder) Toolkit.Theme.COL_INACTIVE else inactiveCol
|
||||
if (useBorder) {
|
||||
Toolkit.drawBoxBorder(batch, posX - 1f, posY - 1f, width + 2f, height + 2f)
|
||||
batch.color = Color.WHITE
|
||||
}
|
||||
batch.draw(image, (posX + (width - imageDrawWidth) / 2).toFloat(), (posY + (height - imageDrawHeight) / 2).toFloat(), imageDrawWidth.toFloat(), imageDrawHeight.toFloat())
|
||||
|
||||
batch.color = if (highlighted) highlightCol
|
||||
else if (mouseUp) activeCol
|
||||
else inactiveCol
|
||||
|
||||
batch.draw(image, (posX + (width - image.regionWidth) / 2).toFloat(), (posY + (height - image.regionHeight) / 2).toFloat())
|
||||
extraDrawOp(this, batch)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
|
||||
@@ -85,45 +85,6 @@ class UIItemIntSlider(
|
||||
}
|
||||
|
||||
|
||||
// TODO unimplemented
|
||||
|
||||
|
||||
override val mouseUp: Boolean
|
||||
get() = super.mouseUp
|
||||
override val mousePushed: Boolean
|
||||
get() = super.mousePushed
|
||||
override val mouseOverCall: UICanvas?
|
||||
get() = super.mouseOverCall
|
||||
override var updateListener: ((Float) -> Unit)?
|
||||
get() = super.updateListener
|
||||
set(_) {}
|
||||
override var keyDownListener: ((Int) -> Unit)?
|
||||
get() = super.keyDownListener
|
||||
set(_) {}
|
||||
override var keyUpListener: ((Int) -> Unit)?
|
||||
get() = super.keyUpListener
|
||||
set(_) {}
|
||||
override var touchDraggedListener: ((Int, Int, Int) -> Unit)?
|
||||
get() = super.touchDraggedListener
|
||||
set(_) {}
|
||||
override var touchDownListener: ((Int, Int, Int, Int) -> Unit)?
|
||||
get() = super.touchDownListener
|
||||
set(_) {}
|
||||
override var touchUpListener: ((Int, Int, Int, Int) -> Unit)?
|
||||
get() = super.touchUpListener
|
||||
set(_) {}
|
||||
override var scrolledListener: ((Float, Float) -> Unit)?
|
||||
get() = super.scrolledListener
|
||||
set(_) {}
|
||||
override var clickOnceListener: ((Int, Int, Int) -> Unit)?
|
||||
get() = super.clickOnceListener
|
||||
set(_) {}
|
||||
override var clickOnceListenerFired: Boolean
|
||||
get() = super.clickOnceListenerFired
|
||||
set(_) {}
|
||||
override var controllerInFocus: Boolean
|
||||
get() = super.controllerInFocus
|
||||
set(_) {}
|
||||
|
||||
override fun dispose() {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user