a (not so) mini update
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
build/
|
build/
|
||||||
build-web/
|
build-web/
|
||||||
|
concept/
|
||||||
resources/
|
resources/
|
||||||
release/
|
release/
|
||||||
out/
|
out/
|
||||||
|
|||||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -6,7 +6,7 @@
|
|||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"preLaunchTask": "cmake: build debug",
|
"preLaunchTask": "cmake: build debug",
|
||||||
"program": "${workspaceFolder}/out/build/linux-debug/bin/Debug/snivy",
|
"program": "${workspaceFolder}/out/build/linux-debug/bin/Debug/shweets-sim",
|
||||||
"args": [],
|
"args": [],
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
"cwd": "${workspaceFolder}/out/build/linux-debug/bin/Debug",
|
"cwd": "${workspaceFolder}/out/build/linux-debug/bin/Debug",
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
],
|
],
|
||||||
"windows": {
|
"windows": {
|
||||||
"type": "cppvsdbg",
|
"type": "cppvsdbg",
|
||||||
"program": "${workspaceFolder}/out/build/x64-Debug/bin/Debug/snivy.exe",
|
"program": "${workspaceFolder}/out/build/x64-Debug/bin/Debug/shweets-sim.exe",
|
||||||
"cwd": "${workspaceFolder}/out/build/x64-Debug/bin/Debug"
|
"cwd": "${workspaceFolder}/out/build/x64-Debug/bin/Debug"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"noDebug": true,
|
"noDebug": true,
|
||||||
"preLaunchTask": "cmake: build release",
|
"preLaunchTask": "cmake: build release",
|
||||||
"program": "${workspaceFolder}/out/build/linux-release/bin/Release/snivy",
|
"program": "${workspaceFolder}/out/build/linux-release/bin/Release/shweets-sim",
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}/out/build/linux-release/bin/Release",
|
"cwd": "${workspaceFolder}/out/build/linux-release/bin/Release",
|
||||||
"environment": [],
|
"environment": [],
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
"miDebuggerPath": "/usr/bin/gdb",
|
"miDebuggerPath": "/usr/bin/gdb",
|
||||||
"windows": {
|
"windows": {
|
||||||
"type": "cppvsdbg",
|
"type": "cppvsdbg",
|
||||||
"program": "${workspaceFolder}/out/build/x64-Release/bin/Release/snivy.exe",
|
"program": "${workspaceFolder}/out/build/x64-Release/bin/Release/shweets-sim.exe",
|
||||||
"cwd": "${workspaceFolder}/out/build/x64-Release/bin/Release"
|
"cwd": "${workspaceFolder}/out/build/x64-Release/bin/Release"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
10
.vscode/tasks.json
vendored
10
.vscode/tasks.json
vendored
@@ -59,7 +59,7 @@
|
|||||||
"label": "linux: run debug",
|
"label": "linux: run debug",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"linux": {
|
"linux": {
|
||||||
"command": "${workspaceFolder}/out/build/linux-debug/bin/Debug/snivy"
|
"command": "${workspaceFolder}/out/build/linux-debug/bin/Debug/shweets-sim"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/out/build/linux-debug/bin/Debug"
|
"cwd": "${workspaceFolder}/out/build/linux-debug/bin/Debug"
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
"label": "linux: run release",
|
"label": "linux: run release",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"linux": {
|
"linux": {
|
||||||
"command": "${workspaceFolder}/out/build/linux-release/bin/Release/snivy"
|
"command": "${workspaceFolder}/out/build/linux-release/bin/Release/shweets-sim"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/out/build/linux-release/bin/Release"
|
"cwd": "${workspaceFolder}/out/build/linux-release/bin/Release"
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
"label": "web: serve",
|
"label": "web: serve",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"linux": {
|
"linux": {
|
||||||
"command": "sh -lc 'PID_FILE=/tmp/snivy-web-http.pid; LOG_FILE=/tmp/snivy-web-http.log; WEB_ROOT=out/build/web; URL=http://127.0.0.1:8000/bin/Release/index.html; [ -f \"$WEB_ROOT/bin/Release/index.html\" ] || { echo \"Web output not found at $WEB_ROOT/bin/Release/index.html (run web: build)\"; exit 1; }; if [ -f \"$PID_FILE\" ] && kill -0 \"$(cat \"$PID_FILE\")\" 2>/dev/null; then kill \"$(cat \"$PID_FILE\")\" >/dev/null 2>&1 || true; rm -f \"$PID_FILE\"; fi; nohup python3 -m http.server 8000 --bind 127.0.0.1 --directory \"$WEB_ROOT\" >\"$LOG_FILE\" 2>&1 & echo $! >\"$PID_FILE\"; echo \"Started web server on http://127.0.0.1:8000 (root: $WEB_ROOT)\"; READY=0; for _ in $(seq 1 40); do if command -v curl >/dev/null 2>&1; then curl -fsS \"$URL\" >/dev/null 2>&1 && READY=1 && break; elif command -v wget >/dev/null 2>&1; then wget -qO- \"$URL\" >/dev/null 2>&1 && READY=1 && break; fi; sleep 0.1; done; [ \"$READY\" = \"1\" ] || { echo \"Web server did not become ready. See /tmp/snivy-web-http.log\"; exit 1; }'"
|
"command": "sh -lc 'PID_FILE=/tmp/shweets-sim-web-http.pid; LOG_FILE=/tmp/shweets-sim-web-http.log; WEB_ROOT=out/build/web; URL=http://127.0.0.1:8000/bin/Release/index.html; [ -f \"$WEB_ROOT/bin/Release/index.html\" ] || { echo \"Web output not found at $WEB_ROOT/bin/Release/index.html (run web: build)\"; exit 1; }; if [ -f \"$PID_FILE\" ] && kill -0 \"$(cat \"$PID_FILE\")\" 2>/dev/null; then kill \"$(cat \"$PID_FILE\")\" >/dev/null 2>&1 || true; rm -f \"$PID_FILE\"; fi; nohup python3 -m http.server 8000 --bind 127.0.0.1 --directory \"$WEB_ROOT\" >\"$LOG_FILE\" 2>&1 & echo $! >\"$PID_FILE\"; echo \"Started web server on http://127.0.0.1:8000 (root: $WEB_ROOT)\"; READY=0; for _ in $(seq 1 40); do if command -v curl >/dev/null 2>&1; then curl -fsS \"$URL\" >/dev/null 2>&1 && READY=1 && break; elif command -v wget >/dev/null 2>&1; then wget -qO- \"$URL\" >/dev/null 2>&1 && READY=1 && break; fi; sleep 0.1; done; [ \"$READY\" = \"1\" ] || { echo \"Web server did not become ready. See /tmp/shweets-sim-web-http.log\"; exit 1; }'"
|
||||||
},
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"command": "echo \"web tasks are configured for Linux (emsdk + python + chromium).\" && exit 1"
|
"command": "echo \"web tasks are configured for Linux (emsdk + python + chromium).\" && exit 1"
|
||||||
@@ -123,7 +123,7 @@
|
|||||||
"label": "web: stop server",
|
"label": "web: stop server",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"linux": {
|
"linux": {
|
||||||
"command": "sh -lc 'PID_FILE=/tmp/snivy-web-http.pid; if [ -f \"$PID_FILE\" ] && kill -0 \"$(cat \"$PID_FILE\")\" 2>/dev/null; then kill \"$(cat \"$PID_FILE\")\" && rm -f \"$PID_FILE\" && echo \"Stopped web server\"; else rm -f \"$PID_FILE\" && echo \"Web server not running\"; fi'"
|
"command": "sh -lc 'PID_FILE=/tmp/shweets-sim-web-http.pid; if [ -f \"$PID_FILE\" ] && kill -0 \"$(cat \"$PID_FILE\")\" 2>/dev/null; then kill \"$(cat \"$PID_FILE\")\" && rm -f \"$PID_FILE\" && echo \"Stopped web server\"; else rm -f \"$PID_FILE\" && echo \"Web server not running\"; fi'"
|
||||||
},
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"command": "echo \"web tasks are configured for Linux (emsdk + python + chromium).\" && exit 1"
|
"command": "echo \"web tasks are configured for Linux (emsdk + python + chromium).\" && exit 1"
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"label": "web: run",
|
"label": "web: run",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"linux": {
|
"linux": {
|
||||||
"command": "sh -lc 'set -e; if ! command -v emcmake >/dev/null 2>&1; then if [ -n \"$EMSDK\" ] && [ -f \"$EMSDK/emsdk_env.sh\" ]; then . \"$EMSDK/emsdk_env.sh\" >/dev/null 2>&1; elif [ -f \"$HOME/emsdk/emsdk_env.sh\" ]; then . \"$HOME/emsdk/emsdk_env.sh\" >/dev/null 2>&1; elif [ -f \"../emsdk/emsdk_env.sh\" ]; then . \"../emsdk/emsdk_env.sh\" >/dev/null 2>&1; else echo \"Emscripten not found. Install emsdk or set EMSDK env var.\"; exit 1; fi; fi; emcmake cmake -S . -B out/build/web -DCMAKE_BUILD_TYPE=Release; cmake --build out/build/web -j$(nproc); PID_FILE=/tmp/snivy-web-http.pid; LOG_FILE=/tmp/snivy-web-http.log; WEB_ROOT=out/build/web; URL=http://127.0.0.1:8000/bin/Release/index.html; [ -f \"$WEB_ROOT/bin/Release/index.html\" ] || { echo \"Web output not found at $WEB_ROOT/bin/Release/index.html\"; exit 1; }; if [ -f \"$PID_FILE\" ] && kill -0 \"$(cat \"$PID_FILE\")\" 2>/dev/null; then kill \"$(cat \"$PID_FILE\")\" >/dev/null 2>&1 || true; rm -f \"$PID_FILE\"; fi; nohup python3 -m http.server 8000 --bind 127.0.0.1 --directory \"$WEB_ROOT\" >\"$LOG_FILE\" 2>&1 & echo $! >\"$PID_FILE\"; echo \"Started web server on http://127.0.0.1:8000 (root: $WEB_ROOT)\"; READY=0; for _ in $(seq 1 40); do if command -v curl >/dev/null 2>&1; then curl -fsS \"$URL\" >/dev/null 2>&1 && READY=1 && break; elif command -v wget >/dev/null 2>&1; then wget -qO- \"$URL\" >/dev/null 2>&1 && READY=1 && break; fi; sleep 0.1; done; [ \"$READY\" = \"1\" ] || { echo \"Web server did not become ready. See /tmp/snivy-web-http.log\"; exit 1; }; (xdg-open \"$URL\" >/dev/null 2>&1 || chromium --new-tab \"$URL\" >/dev/null 2>&1 || chromium-browser --new-tab \"$URL\" >/dev/null 2>&1 || google-chrome --new-tab \"$URL\" >/dev/null 2>&1 || google-chrome-stable --new-tab \"$URL\" >/dev/null 2>&1 || python3 -m webbrowser \"$URL\" >/dev/null 2>&1 || { echo \"Could not launch browser automatically. Open: $URL\"; exit 0; }) & sleep 0.15; echo \"Requested browser open: $URL\"'"
|
"command": "sh -lc 'set -e; if ! command -v emcmake >/dev/null 2>&1; then if [ -n \"$EMSDK\" ] && [ -f \"$EMSDK/emsdk_env.sh\" ]; then . \"$EMSDK/emsdk_env.sh\" >/dev/null 2>&1; elif [ -f \"$HOME/emsdk/emsdk_env.sh\" ]; then . \"$HOME/emsdk/emsdk_env.sh\" >/dev/null 2>&1; elif [ -f \"../emsdk/emsdk_env.sh\" ]; then . \"../emsdk/emsdk_env.sh\" >/dev/null 2>&1; else echo \"Emscripten not found. Install emsdk or set EMSDK env var.\"; exit 1; fi; fi; emcmake cmake -S . -B out/build/web -DCMAKE_BUILD_TYPE=Release; cmake --build out/build/web -j$(nproc); PID_FILE=/tmp/shweets-sim-web-http.pid; LOG_FILE=/tmp/shweets-sim-web-http.log; WEB_ROOT=out/build/web; URL=http://127.0.0.1:8000/bin/Release/index.html; [ -f \"$WEB_ROOT/bin/Release/index.html\" ] || { echo \"Web output not found at $WEB_ROOT/bin/Release/index.html\"; exit 1; }; if [ -f \"$PID_FILE\" ] && kill -0 \"$(cat \"$PID_FILE\")\" 2>/dev/null; then kill \"$(cat \"$PID_FILE\")\" >/dev/null 2>&1 || true; rm -f \"$PID_FILE\"; fi; nohup python3 -m http.server 8000 --bind 127.0.0.1 --directory \"$WEB_ROOT\" >\"$LOG_FILE\" 2>&1 & echo $! >\"$PID_FILE\"; echo \"Started web server on http://127.0.0.1:8000 (root: $WEB_ROOT)\"; READY=0; for _ in $(seq 1 40); do if command -v curl >/dev/null 2>&1; then curl -fsS \"$URL\" >/dev/null 2>&1 && READY=1 && break; elif command -v wget >/dev/null 2>&1; then wget -qO- \"$URL\" >/dev/null 2>&1 && READY=1 && break; fi; sleep 0.1; done; [ \"$READY\" = \"1\" ] || { echo \"Web server did not become ready. See /tmp/shweets-sim-web-http.log\"; exit 1; }; (xdg-open \"$URL\" >/dev/null 2>&1 || chromium --new-tab \"$URL\" >/dev/null 2>&1 || chromium-browser --new-tab \"$URL\" >/dev/null 2>&1 || google-chrome --new-tab \"$URL\" >/dev/null 2>&1 || google-chrome-stable --new-tab \"$URL\" >/dev/null 2>&1 || python3 -m webbrowser \"$URL\" >/dev/null 2>&1 || { echo \"Could not launch browser automatically. Open: $URL\"; exit 0; }) & sleep 0.15; echo \"Requested browser open: $URL\"'"
|
||||||
},
|
},
|
||||||
"windows": {
|
"windows": {
|
||||||
"command": "echo \"web tasks are configured for Linux (emsdk + python + chromium).\" && exit 1"
|
"command": "echo \"web tasks are configured for Linux (emsdk + python + chromium).\" && exit 1"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.27)
|
cmake_minimum_required(VERSION 3.27)
|
||||||
project(snivy LANGUAGES C CXX)
|
project(shweets-sim LANGUAGES C CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
@@ -132,7 +132,7 @@ add_executable(${PROJECT_NAME}
|
|||||||
if(WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
if(WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
||||||
enable_language(RC)
|
enable_language(RC)
|
||||||
target_sources(${PROJECT_NAME} PRIVATE
|
target_sources(${PROJECT_NAME} PRIVATE
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/snivy.rc"
|
"${CMAKE_CURRENT_SOURCE_DIR}/shweets-sim.rc"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/Icon.ico"
|
"${CMAKE_CURRENT_SOURCE_DIR}/Icon.ico"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
@@ -205,16 +205,23 @@ set(PROJECT_RESOURCES_BINARY_DIR "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/resources")
|
|||||||
if(EXISTS "${PROJECT_RESOURCES_DIR}")
|
if(EXISTS "${PROJECT_RESOURCES_DIR}")
|
||||||
file(GLOB_RECURSE PROJECT_RESOURCE_FILES CONFIGURE_DEPENDS
|
file(GLOB_RECURSE PROJECT_RESOURCE_FILES CONFIGURE_DEPENDS
|
||||||
"${PROJECT_RESOURCES_DIR}/*")
|
"${PROJECT_RESOURCES_DIR}/*")
|
||||||
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
list(FILTER PROJECT_RESOURCE_FILES EXCLUDE REGEX ".*/characters/[^/]+\\.zip$")
|
||||||
add_custom_target(copy_resources ALL
|
set(COPY_RESOURCES_STAMP "${CMAKE_BINARY_DIR}/CMakeFiles/copy_resources.stamp")
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${COPY_RESOURCES_STAMP}"
|
||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
-DSRC_DIR="${PROJECT_RESOURCES_DIR}"
|
-DSRC_DIR=${PROJECT_RESOURCES_DIR}
|
||||||
-DDST_DIR="${PROJECT_RESOURCES_BINARY_DIR}"
|
-DDST_DIR=${PROJECT_RESOURCES_BINARY_DIR}
|
||||||
|
-DSTAMP_FILE=${COPY_RESOURCES_STAMP}
|
||||||
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/copy_resources.cmake"
|
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/copy_resources.cmake"
|
||||||
DEPENDS ${PROJECT_RESOURCE_FILES}
|
DEPENDS ${PROJECT_RESOURCE_FILES}
|
||||||
COMMENT "Copying resources directory")
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/copy_resources.cmake"
|
||||||
|
COMMENT "Copying resources directory"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
add_custom_target(copy_resources ALL
|
||||||
|
DEPENDS "${COPY_RESOURCES_STAMP}")
|
||||||
add_dependencies(${PROJECT_NAME} copy_resources)
|
add_dependencies(${PROJECT_NAME} copy_resources)
|
||||||
endif()
|
|
||||||
set(HAS_PROJECT_RESOURCES TRUE)
|
set(HAS_PROJECT_RESOURCES TRUE)
|
||||||
else()
|
else()
|
||||||
set(HAS_PROJECT_RESOURCES FALSE)
|
set(HAS_PROJECT_RESOURCES FALSE)
|
||||||
@@ -243,7 +250,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
|||||||
if(HAS_PROJECT_RESOURCES)
|
if(HAS_PROJECT_RESOURCES)
|
||||||
target_link_options(${PROJECT_NAME} PRIVATE
|
target_link_options(${PROJECT_NAME} PRIVATE
|
||||||
"--preload-file"
|
"--preload-file"
|
||||||
"${PROJECT_RESOURCES_DIR}@resources"
|
"${PROJECT_RESOURCES_BINARY_DIR}@resources"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
@@ -254,7 +261,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
|||||||
COMMAND ${CMAKE_COMMAND}
|
COMMAND ${CMAKE_COMMAND}
|
||||||
-DBIN_DIR="$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
-DBIN_DIR="$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/create_index_zip.cmake"
|
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/create_index_zip.cmake"
|
||||||
COMMENT "Creating snivy-web.zip from Emscripten output")
|
COMMENT "Creating shweets-sim-web.zip from Emscripten output")
|
||||||
else()
|
else()
|
||||||
find_package(OpenGL REQUIRED COMPONENTS OpenGL)
|
find_package(OpenGL REQUIRED COMPONENTS OpenGL)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL)
|
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL)
|
||||||
@@ -266,9 +273,9 @@ if(WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
|
|||||||
-DBIN_ROOT="${CMAKE_BINARY_DIR}/bin"
|
-DBIN_ROOT="${CMAKE_BINARY_DIR}/bin"
|
||||||
-DTARGET_DIR="$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
-DTARGET_DIR="$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
-DEXE_FILE="$<TARGET_FILE_NAME:${PROJECT_NAME}>"
|
-DEXE_FILE="$<TARGET_FILE_NAME:${PROJECT_NAME}>"
|
||||||
-DPACKAGE_NAME="snivy-win32"
|
-DPACKAGE_NAME="shweets-sim-win32"
|
||||||
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/create_windows_zip.cmake"
|
-P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/create_windows_zip.cmake"
|
||||||
COMMENT "Creating snivy-win32 package")
|
COMMENT "Creating shweets-sim-win32 package")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "System: ${CMAKE_SYSTEM_NAME}")
|
message(STATUS "System: ${CMAKE_SYSTEM_NAME}")
|
||||||
|
|||||||
BIN
Icon.ico
BIN
Icon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 6.9 KiB |
23
MODDING.md
23
MODDING.md
@@ -1,10 +1,10 @@
|
|||||||
# Modding Feed Snivy
|
# Modding Shweet's Sim
|
||||||
|
|
||||||
Want to add characters or modify existing ones or mod Feed Snivy in general or otherwise want to know how the game works? Here's how.
|
Want to add characters or modify existing ones or mod Shweet's Sim in general or otherwise want to know how the game works? Here's how.
|
||||||
|
|
||||||
# Animation Format
|
# Animation Format
|
||||||
|
|
||||||
Feed Snivy uses a semi-proprietary format called ".anm2" for tweened character animations, sourced from a spritesheet. This file format comes from the game _The Binding of Isaac: Rebirth_ (a game I mod on my own time) for animations.
|
Shweet's Sim uses a semi-proprietary format called ".anm2" for tweened character animations, sourced from a spritesheet. This file format comes from the game _The Binding of Isaac: Rebirth_ (a game I mod on my own time) for animations.
|
||||||
|
|
||||||
You can either use my own animation editor for that game and this one (recommended), [Anm2Ed](https://github.com/ShweetsStuff/anm2ed), or if you have that game on Steam, you can find that game's own proprietary animation editor in that game's Steam folder and then in tools/IsaacAnimationEditor. I can't guarantee the stability or perfect efficacy of either, but Anm2Ed has crash mitigations and autosave (but of course, save often, for whatever you do).
|
You can either use my own animation editor for that game and this one (recommended), [Anm2Ed](https://github.com/ShweetsStuff/anm2ed), or if you have that game on Steam, you can find that game's own proprietary animation editor in that game's Steam folder and then in tools/IsaacAnimationEditor. I can't guarantee the stability or perfect efficacy of either, but Anm2Ed has crash mitigations and autosave (but of course, save often, for whatever you do).
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ There's two folders inside resources; "characters" and "font".
|
|||||||
|
|
||||||
EVERYTHING ELSE is stored inside bespoke character archives (.zips) in the "characters" folder. This has all data associated with characters. Think of characters more as tailored game experiences rather than literally being just the characters. Not only is there the character graphics, but backgrounds, items, parameters, etc. These are intensely customizable to suit whatever experience you'd like (within the confines of the engine, of course).
|
EVERYTHING ELSE is stored inside bespoke character archives (.zips) in the "characters" folder. This has all data associated with characters. Think of characters more as tailored game experiences rather than literally being just the characters. Not only is there the character graphics, but backgrounds, items, parameters, etc. These are intensely customizable to suit whatever experience you'd like (within the confines of the engine, of course).
|
||||||
|
|
||||||
Feed Snivy uses a collection of [XML](https://en.wikipedia.org/wiki/XML) files to parse data; make sure to brush up on the format. The engine can expect six files in the archive's root:
|
Shweet's Sim uses a collection of [XML](https://en.wikipedia.org/wiki/XML) files to parse data; make sure to brush up on the format. The engine can expect six files in the archive's root:
|
||||||
|
|
||||||
- areas.xml
|
- areas.xml
|
||||||
- character.xml
|
- character.xml
|
||||||
@@ -34,7 +34,7 @@ Feed Snivy uses a collection of [XML](https://en.wikipedia.org/wiki/XML) files t
|
|||||||
- dialogue.xml
|
- dialogue.xml
|
||||||
- items.xml
|
- items.xml
|
||||||
- menu.xml
|
- menu.xml
|
||||||
- play.xml
|
- skill_check.xml
|
||||||
|
|
||||||
(dialogue.xml may or may not be optional; but in future updates I'll make sure of it, for dialogueless characters).
|
(dialogue.xml may or may not be optional; but in future updates I'll make sure of it, for dialogueless characters).
|
||||||
|
|
||||||
@@ -460,10 +460,10 @@ Sound that will play after entering a special code to activate cheats.
|
|||||||
#### Sound (path)
|
#### Sound (path)
|
||||||
The sound that will play, based on SoundRootPath.
|
The sound that will play, based on SoundRootPath.
|
||||||
|
|
||||||
## play.xml
|
## skill_check.xml
|
||||||
Determines behavior and appearance of the "Play" minigame.
|
Determines behavior and appearance of the "Skill Check" minigame.
|
||||||
|
|
||||||
### Play
|
### SkillCheck
|
||||||
#### SoundRootPath (path)
|
#### SoundRootPath (path)
|
||||||
Working folder/directory of where used sounds will be contained within.
|
Working folder/directory of where used sounds will be contained within.
|
||||||
#### RewardScore (int)
|
#### RewardScore (int)
|
||||||
@@ -529,7 +529,7 @@ This sound will play when the grade is hit.
|
|||||||
|
|
||||||
# Saves
|
# Saves
|
||||||
|
|
||||||
Outside of resources, Feed Snivy also has a few files it writes outside of the game. You will find these in %AppData%/snivy on Windows and ~/.local/share/snivy on Linux.
|
Outside of resources, Shweet's Sim also has a few files it writes outside of the game. You will find these in %AppData%/shweets-sim on Windows and ~/.local/share/shweets-sim on Linux.
|
||||||
|
|
||||||
## settings.xml
|
## settings.xml
|
||||||
Stores general game settings and configuration; beyond invididual characters.
|
Stores general game settings and configuration; beyond invididual characters.
|
||||||
@@ -574,8 +574,7 @@ When digestion bar is going down, this is the remaining time to 0 (in ticks)
|
|||||||
Total calories consumed by the character, per save file.
|
Total calories consumed by the character, per save file.
|
||||||
#### TotalFoodItemsEaten (int)
|
#### TotalFoodItemsEaten (int)
|
||||||
How many food items have been completely consumed by the character, per save file.
|
How many food items have been completely consumed by the character, per save file.
|
||||||
|
### SkillCheck
|
||||||
### Play
|
|
||||||
#### TotalPlays (int)
|
#### TotalPlays (int)
|
||||||
However many times the "play" game has been attempted (hitting the bar counts as one "play")
|
However many times the "play" game has been attempted (hitting the bar counts as one "play")
|
||||||
#### HighScore (int)
|
#### HighScore (int)
|
||||||
@@ -599,4 +598,4 @@ Count of the item.
|
|||||||
|
|
||||||
# Conclusion
|
# Conclusion
|
||||||
|
|
||||||
Hopefully this'll give you the resources you need to start making your own characters. If you need any help with this guide, or with clarification on anything, I'm available. Additionally, the game is [licensed as free software](https://github.com/ShweetsStuff/snivy), meaning if you're stuck the code should give you a clue (though I apologize for the lack of comments. Self-documenting code though, am I right? :^) )
|
Hopefully this'll give you the resources you need to start making your own characters. If you need any help with this guide, or with clarification on anything, I'm available. Additionally, the game is [licensed as free software](https://github.com/ShweetsStuff/shweets-sim), meaning if you're stuck the code should give you a clue (though I apologize for the lack of comments. Self-documenting code though, am I right? :^) )
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Feed Snivy
|
# Shweet's Sim
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
This Is A Video Game Where You Feed The Snivy.
|
Interactive character simulation game.
|
||||||
|
|
||||||
[Resources](https://shweetz.net/files/games/feed-snivy/resources.zip)
|
[Resources](https://shweetz.net/files/games/feed-snivy/resources.zip)
|
||||||
|
|
||||||
|
|||||||
@@ -1,55 +1,41 @@
|
|||||||
if(NOT DEFINED SRC_DIR OR NOT DEFINED DST_DIR)
|
if(NOT DEFINED SRC_DIR OR NOT DEFINED DST_DIR OR NOT DEFINED STAMP_FILE)
|
||||||
message(FATAL_ERROR "SRC_DIR and DST_DIR must be defined")
|
message(FATAL_ERROR "SRC_DIR, DST_DIR, and STAMP_FILE must be defined")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CHARACTERS_DIR "${SRC_DIR}/characters")
|
set(CHARACTERS_DIR "${SRC_DIR}/characters")
|
||||||
set(CHARACTERS_ZIP_SCRIPT "${CHARACTERS_DIR}/zip")
|
|
||||||
set(IS_HOST_WINDOWS FALSE)
|
|
||||||
if(CMAKE_HOST_WIN32)
|
|
||||||
set(IS_HOST_WINDOWS TRUE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(EXISTS "${CHARACTERS_ZIP_SCRIPT}" AND NOT IS_HOST_WINDOWS)
|
|
||||||
execute_process(
|
|
||||||
COMMAND "${CHARACTERS_ZIP_SCRIPT}"
|
|
||||||
WORKING_DIRECTORY "${CHARACTERS_DIR}"
|
|
||||||
RESULT_VARIABLE ZIP_SCRIPT_RESULT
|
|
||||||
)
|
|
||||||
if(NOT ZIP_SCRIPT_RESULT EQUAL 0)
|
|
||||||
message(WARNING "Failed running ${CHARACTERS_ZIP_SCRIPT} (exit code ${ZIP_SCRIPT_RESULT}); continuing with existing archives")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(REMOVE_RECURSE "${DST_DIR}")
|
file(REMOVE_RECURSE "${DST_DIR}")
|
||||||
file(MAKE_DIRECTORY "${DST_DIR}")
|
file(MAKE_DIRECTORY "${DST_DIR}")
|
||||||
|
|
||||||
# Copy all resources except characters/ contents.
|
# Copy all resources except character source folders and any stray character zips.
|
||||||
file(COPY "${SRC_DIR}/" DESTINATION "${DST_DIR}"
|
file(COPY "${SRC_DIR}/" DESTINATION "${DST_DIR}"
|
||||||
PATTERN "characters/*" EXCLUDE)
|
PATTERN "characters/*" EXCLUDE
|
||||||
|
PATTERN "characters/*.zip" EXCLUDE)
|
||||||
|
|
||||||
# Copy only .zip archives from resources/characters.
|
|
||||||
file(MAKE_DIRECTORY "${DST_DIR}/characters")
|
file(MAKE_DIRECTORY "${DST_DIR}/characters")
|
||||||
file(GLOB CHARACTER_ZIPS "${CHARACTERS_DIR}/*.zip")
|
if(EXISTS "${CHARACTERS_DIR}")
|
||||||
|
file(GLOB CHILDREN RELATIVE "${CHARACTERS_DIR}" "${CHARACTERS_DIR}/*")
|
||||||
|
foreach(CHILD IN LISTS CHILDREN)
|
||||||
|
set(CHARACTER_SOURCE_DIR "${CHARACTERS_DIR}/${CHILD}")
|
||||||
|
if(NOT IS_DIRECTORY "${CHARACTER_SOURCE_DIR}")
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT CHARACTER_ZIPS)
|
file(GLOB_RECURSE CHARACTER_CONTENTS RELATIVE "${CHARACTER_SOURCE_DIR}" "${CHARACTER_SOURCE_DIR}/*")
|
||||||
file(GLOB CHARACTER_FILES RELATIVE "${CHARACTERS_DIR}" "${CHARACTERS_DIR}/*")
|
if(NOT CHARACTER_CONTENTS)
|
||||||
list(FILTER CHARACTER_FILES EXCLUDE REGEX "^zip$")
|
message(STATUS "Skipping empty character directory: ${CHARACTER_SOURCE_DIR}")
|
||||||
list(FILTER CHARACTER_FILES EXCLUDE REGEX ".*\\.zip$")
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CHARACTER_FILES)
|
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND "${CMAKE_COMMAND}" -E tar cf "snivy.zip" --format=zip ${CHARACTER_FILES}
|
COMMAND "${CMAKE_COMMAND}" -E tar cf "${DST_DIR}/characters/${CHILD}.zip" --format=zip ${CHARACTER_CONTENTS}
|
||||||
WORKING_DIRECTORY "${CHARACTERS_DIR}"
|
WORKING_DIRECTORY "${CHARACTER_SOURCE_DIR}"
|
||||||
RESULT_VARIABLE ZIP_GENERATE_RESULT
|
RESULT_VARIABLE ZIP_GENERATE_RESULT
|
||||||
)
|
)
|
||||||
if(NOT ZIP_GENERATE_RESULT EQUAL 0)
|
if(NOT ZIP_GENERATE_RESULT EQUAL 0)
|
||||||
message(WARNING "Failed generating ${CHARACTERS_DIR}/snivy.zip (exit code ${ZIP_GENERATE_RESULT}); continuing without character zip archives")
|
message(FATAL_ERROR "Failed generating ${DST_DIR}/characters/${CHILD}.zip (exit code ${ZIP_GENERATE_RESULT})")
|
||||||
else()
|
|
||||||
file(GLOB CHARACTER_ZIPS "${CHARACTERS_DIR}/*.zip")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
endforeach()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CHARACTER_ZIPS)
|
file(WRITE "${STAMP_FILE}" "resources copied\n")
|
||||||
file(COPY ${CHARACTER_ZIPS} DESTINATION "${DST_DIR}/characters")
|
|
||||||
endif()
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ if(NOT DEFINED BIN_DIR OR BIN_DIR STREQUAL "")
|
|||||||
message(FATAL_ERROR "BIN_DIR is required")
|
message(FATAL_ERROR "BIN_DIR is required")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(ARCHIVE_PATH "${BIN_DIR}/snivy-web.zip")
|
set(ARCHIVE_PATH "${BIN_DIR}/shweets-sim-web.zip")
|
||||||
file(REMOVE "${ARCHIVE_PATH}")
|
file(REMOVE "${ARCHIVE_PATH}")
|
||||||
|
|
||||||
file(GLOB INDEX_OUTPUTS "${BIN_DIR}/index.*")
|
file(GLOB INDEX_OUTPUTS "${BIN_DIR}/index.*")
|
||||||
@@ -16,7 +16,7 @@ endforeach()
|
|||||||
|
|
||||||
if(FILES_TO_ZIP)
|
if(FILES_TO_ZIP)
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND "${CMAKE_COMMAND}" -E tar cf "snivy-web.zip" --format=zip ${FILES_TO_ZIP}
|
COMMAND "${CMAKE_COMMAND}" -E tar cf "shweets-sim-web.zip" --format=zip ${FILES_TO_ZIP}
|
||||||
WORKING_DIRECTORY "${BIN_DIR}"
|
WORKING_DIRECTORY "${BIN_DIR}"
|
||||||
RESULT_VARIABLE ZIP_RESULT
|
RESULT_VARIABLE ZIP_RESULT
|
||||||
)
|
)
|
||||||
@@ -24,5 +24,5 @@ if(FILES_TO_ZIP)
|
|||||||
message(FATAL_ERROR "Failed creating ${ARCHIVE_PATH}")
|
message(FATAL_ERROR "Failed creating ${ARCHIVE_PATH}")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
message(WARNING "No index.* files found in ${BIN_DIR}; skipping snivy-web.zip creation")
|
message(WARNING "No index.* files found in ${BIN_DIR}; skipping shweets-sim-web.zip creation")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -236,6 +236,13 @@ namespace game::entity
|
|||||||
if (auto animation = animation_get(); animation && animation->isLoop) currentQueuedPlay = QueuedPlay{};
|
if (auto animation = animation_get(); animation && animation->isLoop) currentQueuedPlay = QueuedPlay{};
|
||||||
|
|
||||||
if (!queuedPlay.empty())
|
if (!queuedPlay.empty())
|
||||||
|
{
|
||||||
|
if (!animationMap.contains(queuedPlay.animation))
|
||||||
|
{
|
||||||
|
logger.error(std::string("Animation \"" + queuedPlay.animation + "\" does not exist! Unable to play!"));
|
||||||
|
if (!defaultAnimation.empty()) queue_default_animation();
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
auto& index = animationMap.at(queuedPlay.animation);
|
auto& index = animationMap.at(queuedPlay.animation);
|
||||||
if (queuedPlay.isPlayAfterAnimation)
|
if (queuedPlay.isPlayAfterAnimation)
|
||||||
@@ -247,6 +254,7 @@ namespace game::entity
|
|||||||
}
|
}
|
||||||
queuedPlay = QueuedPlay{};
|
queuedPlay = QueuedPlay{};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto animation = animation_get();
|
auto animation = animation_get();
|
||||||
if (!animation || animation->frameNum == 1 || mode == SET || state == STOPPED) return;
|
if (!animation || animation->frameNum == 1 || mode == SET || state == STOPPED) return;
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ namespace game::entity
|
|||||||
auto& save = data.save;
|
auto& save = data.save;
|
||||||
auto saveIsValid = save.is_valid();
|
auto saveIsValid = save.is_valid();
|
||||||
|
|
||||||
capacity = saveIsValid ? save.capacity : data.capacity;
|
capacity = saveIsValid ? save.capacity : (double)data.capacity;
|
||||||
weight = saveIsValid ? save.weight : data.weight;
|
weight = saveIsValid ? save.weight : (double)data.weight;
|
||||||
digestionRate = saveIsValid ? save.digestionRate : data.digestionRate;
|
digestionRate = saveIsValid ? save.digestionRate : (double)data.digestionRate;
|
||||||
eatSpeed = saveIsValid ? save.eatSpeed : data.eatSpeed;
|
eatSpeed = saveIsValid ? save.eatSpeed : (double)data.eatSpeed;
|
||||||
|
|
||||||
calories = saveIsValid ? save.calories : 0;
|
calories = saveIsValid ? save.calories : 0;
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ namespace game::entity
|
|||||||
|
|
||||||
float Character::weight_get(measurement::System system)
|
float Character::weight_get(measurement::System system)
|
||||||
{
|
{
|
||||||
return system == measurement::IMPERIAL ? weight * (float)measurement::KG_TO_LB : weight;
|
return system == measurement::IMPERIAL ? weight * (double)measurement::KG_TO_LB : weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Character::stage_from_weight_get(float checkWeight) const
|
int Character::stage_from_weight_get(float checkWeight) const
|
||||||
@@ -99,7 +99,7 @@ namespace game::entity
|
|||||||
{
|
{
|
||||||
if (stageIndex == -1) stageIndex = this->stage;
|
if (stageIndex == -1) stageIndex = this->stage;
|
||||||
|
|
||||||
float threshold = data.weight;
|
double threshold = data.weight;
|
||||||
|
|
||||||
if (!data.stages.empty())
|
if (!data.stages.empty())
|
||||||
{
|
{
|
||||||
@@ -111,7 +111,7 @@ namespace game::entity
|
|||||||
threshold = data.stages[stageIndex - 1].threshold;
|
threshold = data.stages[stageIndex - 1].threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
return system == measurement::IMPERIAL ? threshold * (float)measurement::KG_TO_LB : threshold;
|
return system == measurement::IMPERIAL ? threshold * (double)measurement::KG_TO_LB : threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Character::stage_threshold_next_get(measurement::System system) const
|
float Character::stage_threshold_next_get(measurement::System system) const
|
||||||
@@ -122,23 +122,35 @@ namespace game::entity
|
|||||||
float Character::stage_progress_get()
|
float Character::stage_progress_get()
|
||||||
{
|
{
|
||||||
auto currentStage = stage_get();
|
auto currentStage = stage_get();
|
||||||
if (currentStage >= stage_max_get()) return 1.0f;
|
if (currentStage >= stage_max_get()) return 1.0;
|
||||||
|
|
||||||
auto currentThreshold = stage_threshold_get(currentStage);
|
auto currentThreshold = stage_threshold_get(currentStage);
|
||||||
auto nextThreshold = stage_threshold_get(currentStage + 1);
|
auto nextThreshold = stage_threshold_get(currentStage + 1);
|
||||||
if (nextThreshold <= currentThreshold) return 1.0f;
|
if (nextThreshold <= currentThreshold) return 1.0;
|
||||||
|
|
||||||
return (weight - currentThreshold) / (nextThreshold - currentThreshold);
|
return (weight - currentThreshold) / (nextThreshold - currentThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Character::digestion_rate_get() { return digestionRate * 60; }
|
float Character::digestion_rate_get() { return digestionRate * 60.0; }
|
||||||
|
|
||||||
float Character::max_capacity() const { return capacity * data.capacityMaxMultiplier; }
|
float Character::max_capacity() const { return capacity * data.capacityMaxMultiplier; }
|
||||||
bool Character::is_over_capacity() const { return calories > capacity; }
|
bool Character::is_over_capacity() const { return calories > capacity; }
|
||||||
bool Character::is_max_capacity() const { return calories >= max_capacity(); }
|
bool Character::is_max_capacity() const { return calories >= max_capacity(); }
|
||||||
float Character::capacity_percent_get() const { return calories / max_capacity(); }
|
float Character::capacity_percent_get() const { return calories / max_capacity(); }
|
||||||
|
|
||||||
std::string Character::animation_name_convert(const std::string& name) { return std::format("{}{}", name, stage); }
|
std::string Character::animation_append_id_get() const
|
||||||
|
{
|
||||||
|
if (stage <= 0) return {};
|
||||||
|
auto stageIndex = stage - 1;
|
||||||
|
if (stageIndex < 0 || stageIndex >= (int)data.stages.size()) return {};
|
||||||
|
return data.stages[stageIndex].animationAppendID;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Character::animation_name_convert(const std::string& name)
|
||||||
|
{
|
||||||
|
auto appendID = animation_append_id_get();
|
||||||
|
return appendID.empty() ? name : name + appendID;
|
||||||
|
}
|
||||||
void Character::play_convert(const std::string& animation, Mode playMode, float startAtTime,
|
void Character::play_convert(const std::string& animation, Mode playMode, float startAtTime,
|
||||||
float speedMultiplierValue)
|
float speedMultiplierValue)
|
||||||
{
|
{
|
||||||
@@ -148,8 +160,7 @@ namespace game::entity
|
|||||||
void Character::expand_areas_apply()
|
void Character::expand_areas_apply()
|
||||||
{
|
{
|
||||||
auto stageProgress = stage_progress_get();
|
auto stageProgress = stage_progress_get();
|
||||||
auto capacityProgress = isDigesting
|
auto capacityProgress = isDigesting ? (double)calories / max_capacity() * (double)digestionTimer / data.digestionTimerMax
|
||||||
? (float)calories / max_capacity() * (float)digestionTimer / data.digestionTimerMax
|
|
||||||
: calories / max_capacity();
|
: calories / max_capacity();
|
||||||
|
|
||||||
for (int i = 0; i < (int)data.expandAreas.size(); i++)
|
for (int i = 0; i < (int)data.expandAreas.size(); i++)
|
||||||
@@ -158,8 +169,8 @@ namespace game::entity
|
|||||||
auto& overrideLayer = overrides[expandAreaOverrideLayerIDs[i]];
|
auto& overrideLayer = overrides[expandAreaOverrideLayerIDs[i]];
|
||||||
auto& overrideNull = overrides[expandAreaOverrideNullIDs[i]];
|
auto& overrideNull = overrides[expandAreaOverrideNullIDs[i]];
|
||||||
|
|
||||||
auto stageScaleAdd = ((expandArea.scaleAdd * stageProgress) * 0.5f);
|
auto stageScaleAdd = ((double)expandArea.scaleAdd * stageProgress) * 0.5;
|
||||||
auto capacityScaleAdd = ((expandArea.scaleAdd * capacityProgress) * 0.5f);
|
auto capacityScaleAdd = ((double)expandArea.scaleAdd * capacityProgress) * 0.5;
|
||||||
|
|
||||||
auto scaleAdd =
|
auto scaleAdd =
|
||||||
glm::clamp(glm::vec2(), glm::vec2(stageScaleAdd + capacityScaleAdd), glm::vec2(expandArea.scaleAdd));
|
glm::clamp(glm::vec2(), glm::vec2(stageScaleAdd + capacityScaleAdd), glm::vec2(expandArea.scaleAdd));
|
||||||
@@ -201,20 +212,21 @@ namespace game::entity
|
|||||||
if (digestionTimer <= 0)
|
if (digestionTimer <= 0)
|
||||||
{
|
{
|
||||||
auto increment = calories * data.caloriesToKilogram;
|
auto increment = calories * data.caloriesToKilogram;
|
||||||
|
auto nextWeight = glm::clamp(weight + increment, data.weight, data.weightMax);
|
||||||
|
|
||||||
if (is_over_capacity())
|
if (is_over_capacity())
|
||||||
{
|
{
|
||||||
auto capacityMaxCalorieDifference = (calories - capacity);
|
auto capacityMaxCalorieDifference = (calories - capacity);
|
||||||
auto overCapacityPercent = capacityMaxCalorieDifference / (max_capacity() - capacity);
|
auto overCapacityPercent = capacityMaxCalorieDifference / (max_capacity() - capacity);
|
||||||
auto capacityIncrement =
|
auto capacityIncrement =
|
||||||
(overCapacityPercent * data.capacityIfOverStuffedOnDigestBonus) * capacityMaxCalorieDifference;
|
(double)((overCapacityPercent * data.capacityIfOverStuffedOnDigestBonus) * capacityMaxCalorieDifference);
|
||||||
capacity = glm::clamp(data.capacityMin, capacity + capacityIncrement, data.capacityMax);
|
capacity = glm::clamp(capacity + capacityIncrement, (double)data.capacityMin, (double)data.capacityMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
totalCaloriesConsumed += calories;
|
totalCaloriesConsumed += calories;
|
||||||
calories = 0;
|
calories = 0;
|
||||||
|
|
||||||
if (auto nextStage = stage_from_weight_get(weight + increment); nextStage > stage_from_weight_get(weight))
|
if (auto nextStage = stage_from_weight_get((double)nextWeight); nextStage > stage_from_weight_get(weight))
|
||||||
{
|
{
|
||||||
queuedPlay = QueuedPlay{};
|
queuedPlay = QueuedPlay{};
|
||||||
nextQueuedPlay = QueuedPlay{};
|
nextQueuedPlay = QueuedPlay{};
|
||||||
@@ -226,7 +238,7 @@ namespace game::entity
|
|||||||
else
|
else
|
||||||
isJustDigested = true;
|
isJustDigested = true;
|
||||||
|
|
||||||
weight += increment;
|
weight = (double)nextWeight;
|
||||||
|
|
||||||
isDigesting = false;
|
isDigesting = false;
|
||||||
digestionTimer = data.digestionTimerMax;
|
digestionTimer = data.digestionTimerMax;
|
||||||
@@ -258,7 +270,7 @@ namespace game::entity
|
|||||||
auto talk_reset = [&]()
|
auto talk_reset = [&]()
|
||||||
{
|
{
|
||||||
isTalking = false;
|
isTalking = false;
|
||||||
talkTimer = 0.0f;
|
talkTimer = 0.0;
|
||||||
talkOverride.frame = FrameOptional();
|
talkOverride.frame = FrameOptional();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -277,9 +289,9 @@ namespace game::entity
|
|||||||
talkOverride.frame.size = frame.size;
|
talkOverride.frame.size = frame.size;
|
||||||
talkOverride.frame.pivot = frame.pivot;
|
talkOverride.frame.pivot = frame.pivot;
|
||||||
|
|
||||||
talkTimer += 1.0f;
|
talkTimer += 1.0;
|
||||||
|
|
||||||
if (talkTimer > animationTalkDurations.at(animationIndex)) talkTimer = 0.0f;
|
if (talkTimer > animationTalkDurations.at(animationIndex)) talkTimer = 0.0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
talk_reset();
|
talk_reset();
|
||||||
@@ -301,7 +313,7 @@ namespace game::entity
|
|||||||
auto blink_reset = [&]()
|
auto blink_reset = [&]()
|
||||||
{
|
{
|
||||||
isBlinking = false;
|
isBlinking = false;
|
||||||
blinkTimer = 0.0f;
|
blinkTimer = 0.0;
|
||||||
blinkOverride.frame = FrameOptional();
|
blinkOverride.frame = FrameOptional();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -320,7 +332,7 @@ namespace game::entity
|
|||||||
blinkOverride.frame.size = frame.size;
|
blinkOverride.frame.size = frame.size;
|
||||||
blinkOverride.frame.pivot = frame.pivot;
|
blinkOverride.frame.pivot = frame.pivot;
|
||||||
|
|
||||||
blinkTimer += 1.0f;
|
blinkTimer += 1.0;
|
||||||
|
|
||||||
if (blinkTimer >= blinkDuration) blink_reset();
|
if (blinkTimer >= blinkDuration) blink_reset();
|
||||||
}
|
}
|
||||||
@@ -348,14 +360,6 @@ namespace game::entity
|
|||||||
{is_over_capacity() && !data.animations.idleFull.empty() ? data.animations.idleFull : data.animations.idle});
|
{is_over_capacity() && !data.animations.idleFull.empty() ? data.animations.idleFull : data.animations.idle});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Character::queue_interact_area_animation(resource::xml::Character::InteractArea& interactArea)
|
|
||||||
{
|
|
||||||
if (isStageUp) return;
|
|
||||||
if (interactArea.animation.empty()) return;
|
|
||||||
queue_play({is_over_capacity() && !interactArea.animationFull.empty() ? interactArea.animationFull
|
|
||||||
: interactArea.animation});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Character::spritesheet_set(SpritesheetType type)
|
void Character::spritesheet_set(SpritesheetType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ namespace game::entity
|
|||||||
void tick();
|
void tick();
|
||||||
void play_convert(const std::string&, Mode = PLAY, float time = 0.0f, float speedMultiplier = 1.0f);
|
void play_convert(const std::string&, Mode = PLAY, float time = 0.0f, float speedMultiplier = 1.0f);
|
||||||
void queue_idle_animation();
|
void queue_idle_animation();
|
||||||
void queue_interact_area_animation(resource::xml::Character::InteractArea&);
|
|
||||||
void queue_play(QueuedPlay);
|
void queue_play(QueuedPlay);
|
||||||
|
|
||||||
|
std::string animation_append_id_get() const;
|
||||||
std::string animation_name_convert(const std::string& name);
|
std::string animation_name_convert(const std::string& name);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ using namespace glm;
|
|||||||
|
|
||||||
namespace game::entity
|
namespace game::entity
|
||||||
{
|
{
|
||||||
Item::Item(Anm2 _anm2, glm::ivec2 _position, int _schemaID, int _chewCount, int _animationIndex, glm::vec2 _velocity,
|
Item::Item(Anm2 _anm2, glm::ivec2 _position, int _schemaID, int _durability, int _animationIndex, glm::vec2 _velocity,
|
||||||
float _rotation)
|
float _rotation)
|
||||||
: Actor(_anm2, _position, SET, 0.0f, _animationIndex), schemaID(_schemaID), chewCount(_chewCount),
|
: Actor(_anm2, _position, SET, 0.0f, _animationIndex), schemaID(_schemaID), durability(_durability),
|
||||||
velocity(_velocity)
|
velocity(_velocity)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ namespace game::entity
|
|||||||
|
|
||||||
int schemaID{};
|
int schemaID{};
|
||||||
int rotationOverrideID{};
|
int rotationOverrideID{};
|
||||||
int chewCount{};
|
int durability{};
|
||||||
|
|
||||||
glm::vec2 velocity{};
|
glm::vec2 velocity{};
|
||||||
float angularVelocity{};
|
float angularVelocity{};
|
||||||
|
|
||||||
Item(resource::xml::Anm2, glm::ivec2 position, int id, int chewCount = 0, int animationIndex = -1,
|
Item(resource::xml::Anm2, glm::ivec2 position, int id, int durability = 0, int animationIndex = -1,
|
||||||
glm::vec2 velocity = {}, float rotation = 0.0f);
|
glm::vec2 velocity = {}, float rotation = 0.0f);
|
||||||
void update();
|
void update();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace game
|
|||||||
|
|
||||||
logger.info("Initializing...");
|
logger.info("Initializing...");
|
||||||
|
|
||||||
if (!PHYSFS_init((argc > 0 && argv && argv[0]) ? argv[0] : "snivy"))
|
if (!PHYSFS_init((argc > 0 && argv && argv[0]) ? argv[0] : "shweets-sim"))
|
||||||
{
|
{
|
||||||
logger.fatal(std::format("Failed to initialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
|
logger.fatal(std::format("Failed to initialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())));
|
||||||
isError = true;
|
isError = true;
|
||||||
@@ -85,12 +85,12 @@ namespace game
|
|||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
static constexpr glm::vec2 SIZE = {1600, 900};
|
static constexpr glm::vec2 SIZE = {1600, 900};
|
||||||
window = SDL_CreateWindow("Feed Snivy", SIZE.x, SIZE.y, SDL_WINDOW_OPENGL);
|
window = SDL_CreateWindow("Shweet's Sim", SIZE.x, SIZE.y, SDL_WINDOW_OPENGL);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
SDL_PropertiesID windowProperties = SDL_CreateProperties();
|
SDL_PropertiesID windowProperties = SDL_CreateProperties();
|
||||||
|
|
||||||
SDL_SetStringProperty(windowProperties, SDL_PROP_WINDOW_CREATE_TITLE_STRING, "Feed Snivy");
|
SDL_SetStringProperty(windowProperties, SDL_PROP_WINDOW_CREATE_TITLE_STRING, "Shweet's Sim");
|
||||||
SDL_SetNumberProperty(windowProperties, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, (long)settings.windowSize.x);
|
SDL_SetNumberProperty(windowProperties, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, (long)settings.windowSize.x);
|
||||||
SDL_SetNumberProperty(windowProperties, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, (long)settings.windowSize.y);
|
SDL_SetNumberProperty(windowProperties, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, (long)settings.windowSize.y);
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,9 @@ namespace game::resource
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
static constexpr auto NORMAL = 20;
|
static constexpr auto NORMAL = 20;
|
||||||
static constexpr auto ABOVE_AVERAGE = 24;
|
static constexpr auto HEADER_1 = 24;
|
||||||
static constexpr auto BIG = 30;
|
static constexpr auto HEADER_2 = 30;
|
||||||
static constexpr auto HEADER_3 = 40;
|
static constexpr auto HEADER_3 = 40;
|
||||||
static constexpr auto HEADER_2 = 50;
|
|
||||||
static constexpr auto HEADER_1 = 60;
|
|
||||||
|
|
||||||
Font() = default;
|
Font() = default;
|
||||||
Font(const std::filesystem::path&, float = NORMAL);
|
Font(const std::filesystem::path&, float = NORMAL);
|
||||||
|
|||||||
@@ -43,28 +43,31 @@ namespace game::resource::xml
|
|||||||
|
|
||||||
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
|
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
|
||||||
|
|
||||||
root->QueryFloatAttribute("Weight", &weight);
|
root->QueryDoubleAttribute("Weight", &weight);
|
||||||
|
root->QueryDoubleAttribute("WeightMax", &weightMax);
|
||||||
|
|
||||||
root->QueryFloatAttribute("Capacity", &capacity);
|
root->QueryDoubleAttribute("Capacity", &capacity);
|
||||||
root->QueryFloatAttribute("CapacityMin", &capacityMin);
|
root->QueryDoubleAttribute("CapacityMin", &capacityMin);
|
||||||
root->QueryFloatAttribute("CapacityMax", &capacityMax);
|
root->QueryDoubleAttribute("CapacityMax", &capacityMax);
|
||||||
root->QueryFloatAttribute("CapacityMaxMultiplier", &capacityMaxMultiplier);
|
root->QueryDoubleAttribute("CapacityMaxMultiplier", &capacityMaxMultiplier);
|
||||||
root->QueryFloatAttribute("CapacityIfOverStuffedOnDigestBonus", &capacityIfOverStuffedOnDigestBonus);
|
root->QueryDoubleAttribute("CapacityIfOverStuffedOnDigestBonus", &capacityIfOverStuffedOnDigestBonus);
|
||||||
|
|
||||||
root->QueryFloatAttribute("CaloriesToKilogram", &caloriesToKilogram);
|
root->QueryDoubleAttribute("CaloriesToKilogram", &caloriesToKilogram);
|
||||||
|
|
||||||
root->QueryFloatAttribute("DigestionRate", &digestionRate);
|
root->QueryDoubleAttribute("DigestionRate", &digestionRate);
|
||||||
root->QueryFloatAttribute("DigestionRateMin", &digestionRateMin);
|
root->QueryDoubleAttribute("DigestionRateMin", &digestionRateMin);
|
||||||
root->QueryFloatAttribute("DigestionRateMax", &digestionRateMax);
|
root->QueryDoubleAttribute("DigestionRateMax", &digestionRateMax);
|
||||||
root->QueryIntAttribute("DigestionTimerMax", &digestionTimerMax);
|
root->QueryIntAttribute("DigestionTimerMax", &digestionTimerMax);
|
||||||
|
|
||||||
root->QueryFloatAttribute("EatSpeed", &eatSpeed);
|
root->QueryDoubleAttribute("EatSpeed", &eatSpeed);
|
||||||
root->QueryFloatAttribute("EatSpeedMin", &eatSpeedMin);
|
root->QueryDoubleAttribute("EatSpeedMin", &eatSpeedMin);
|
||||||
root->QueryFloatAttribute("EatSpeedMax", &eatSpeedMax);
|
root->QueryDoubleAttribute("EatSpeedMax", &eatSpeedMax);
|
||||||
|
|
||||||
root->QueryFloatAttribute("BlinkChance", &blinkChance);
|
root->QueryDoubleAttribute("BlinkChance", &blinkChance);
|
||||||
root->QueryFloatAttribute("GurgleChance", &gurgleChance);
|
root->QueryDoubleAttribute("GurgleChance", &gurgleChance);
|
||||||
root->QueryFloatAttribute("GurgleCapacityMultiplier", &gurgleCapacityMultiplier);
|
root->QueryDoubleAttribute("GurgleCapacityMultiplier", &gurgleCapacityMultiplier);
|
||||||
|
|
||||||
|
root->QueryIntAttribute("TextBlipPeriodBase", &textBlipPeriodBase);
|
||||||
|
|
||||||
auto dialoguePath = physfs::Path(archive + "/" + "dialogue.xml");
|
auto dialoguePath = physfs::Path(archive + "/" + "dialogue.xml");
|
||||||
|
|
||||||
@@ -100,6 +103,7 @@ namespace game::resource::xml
|
|||||||
|
|
||||||
if (auto element = root->FirstChildElement("Sounds"))
|
if (auto element = root->FirstChildElement("Sounds"))
|
||||||
{
|
{
|
||||||
|
query_sound_entry_collection(element, "Blip", archive, soundRootPath, sounds.blip);
|
||||||
query_sound_entry_collection(element, "Digest", archive, soundRootPath, sounds.digest);
|
query_sound_entry_collection(element, "Digest", archive, soundRootPath, sounds.digest);
|
||||||
query_sound_entry_collection(element, "Gurgle", archive, soundRootPath, sounds.gurgle);
|
query_sound_entry_collection(element, "Gurgle", archive, soundRootPath, sounds.gurgle);
|
||||||
}
|
}
|
||||||
@@ -127,6 +131,7 @@ namespace game::resource::xml
|
|||||||
child->QueryFloatAttribute("Threshold", &stage.threshold);
|
child->QueryFloatAttribute("Threshold", &stage.threshold);
|
||||||
child->QueryIntAttribute("AreaID", &stage.areaID);
|
child->QueryIntAttribute("AreaID", &stage.areaID);
|
||||||
dialogue.query_pool_id(child, "DialoguePoolID", stage.pool.id);
|
dialogue.query_pool_id(child, "DialoguePoolID", stage.pool.id);
|
||||||
|
query_string_attribute(child, "AnimationAppendID", &stage.animationAppendID);
|
||||||
stages.emplace_back(std::move(stage));
|
stages.emplace_back(std::move(stage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,12 +183,11 @@ namespace game::resource::xml
|
|||||||
if (child->FindAttribute("Layer")) query_layer_id(child, "Layer", anm2, interactArea.layerID);
|
if (child->FindAttribute("Layer")) query_layer_id(child, "Layer", anm2, interactArea.layerID);
|
||||||
|
|
||||||
query_null_id(child, "Null", anm2, interactArea.nullID);
|
query_null_id(child, "Null", anm2, interactArea.nullID);
|
||||||
query_string_attribute(child, "Animation", &interactArea.animation);
|
|
||||||
query_string_attribute(child, "AnimationFull", &interactArea.animationFull);
|
|
||||||
query_string_attribute(child, "AnimationCursorHover", &interactArea.animationCursorHover);
|
query_string_attribute(child, "AnimationCursorHover", &interactArea.animationCursorHover);
|
||||||
query_string_attribute(child, "AnimationCursorActive", &interactArea.animationCursorActive);
|
query_string_attribute(child, "AnimationCursorActive", &interactArea.animationCursorActive);
|
||||||
query_sound_entry_collection(child, "Sound", archive, soundRootPath, interactArea.sound, "Path");
|
query_sound_entry_collection(child, "Sound", archive, soundRootPath, interactArea.sound, "Path");
|
||||||
dialogue.query_pool_id(child, "DialoguePoolID", interactArea.pool.id);
|
dialogue.query_pool_id(child, "DialoguePoolID", interactArea.pool.id);
|
||||||
|
dialogue.query_pool_id(child, "DialoguePoolIDFull", interactArea.poolFull.id);
|
||||||
query_bool_attribute(child, "IsHold", &interactArea.isHold);
|
query_bool_attribute(child, "IsHold", &interactArea.isHold);
|
||||||
child->QueryFloatAttribute("DigestionBonusRub", &interactArea.digestionBonusRub);
|
child->QueryFloatAttribute("DigestionBonusRub", &interactArea.digestionBonusRub);
|
||||||
child->QueryFloatAttribute("DigestionBonusClick", &interactArea.digestionBonusClick);
|
child->QueryFloatAttribute("DigestionBonusClick", &interactArea.digestionBonusClick);
|
||||||
@@ -225,6 +229,9 @@ namespace game::resource::xml
|
|||||||
else
|
else
|
||||||
logger.warning(std::format("No character skill_check.xml file found: {}", path.string()));
|
logger.warning(std::format("No character skill_check.xml file found: {}", path.string()));
|
||||||
|
|
||||||
|
if (auto stringsPath = physfs::Path(archive + "/" + "strings.xml"); stringsPath.is_valid())
|
||||||
|
strings = Strings(stringsPath);
|
||||||
|
|
||||||
logger.info(std::format("Initialized character: {}", name));
|
logger.info(std::format("Initialized character: {}", name));
|
||||||
|
|
||||||
this->path = path;
|
this->path = path;
|
||||||
|
|||||||
@@ -11,8 +11,9 @@
|
|||||||
#include "dialogue.hpp"
|
#include "dialogue.hpp"
|
||||||
#include "item.hpp"
|
#include "item.hpp"
|
||||||
#include "menu.hpp"
|
#include "menu.hpp"
|
||||||
#include "skill_check.hpp"
|
|
||||||
#include "save.hpp"
|
#include "save.hpp"
|
||||||
|
#include "skill_check.hpp"
|
||||||
|
#include "strings.hpp"
|
||||||
|
|
||||||
namespace game::resource::xml
|
namespace game::resource::xml
|
||||||
{
|
{
|
||||||
@@ -24,6 +25,7 @@ namespace game::resource::xml
|
|||||||
float threshold{};
|
float threshold{};
|
||||||
int areaID{};
|
int areaID{};
|
||||||
Dialogue::PoolReference pool{-1};
|
Dialogue::PoolReference pool{-1};
|
||||||
|
std::string animationAppendID{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EatArea
|
struct EatArea
|
||||||
@@ -42,8 +44,6 @@ namespace game::resource::xml
|
|||||||
|
|
||||||
struct InteractArea
|
struct InteractArea
|
||||||
{
|
{
|
||||||
std::string animation{};
|
|
||||||
std::string animationFull{};
|
|
||||||
std::string animationCursorActive{};
|
std::string animationCursorActive{};
|
||||||
std::string animationCursorHover{};
|
std::string animationCursorHover{};
|
||||||
SoundEntryCollection sound{};
|
SoundEntryCollection sound{};
|
||||||
@@ -53,6 +53,7 @@ namespace game::resource::xml
|
|||||||
int typeID{-1};
|
int typeID{-1};
|
||||||
bool isHold{};
|
bool isHold{};
|
||||||
Dialogue::PoolReference pool{-1};
|
Dialogue::PoolReference pool{-1};
|
||||||
|
Dialogue::PoolReference poolFull{-1};
|
||||||
|
|
||||||
float digestionBonusRub{};
|
float digestionBonusRub{};
|
||||||
float digestionBonusClick{};
|
float digestionBonusClick{};
|
||||||
@@ -73,6 +74,7 @@ namespace game::resource::xml
|
|||||||
|
|
||||||
struct Sounds
|
struct Sounds
|
||||||
{
|
{
|
||||||
|
SoundEntryCollection blip{};
|
||||||
SoundEntryCollection gurgle{};
|
SoundEntryCollection gurgle{};
|
||||||
SoundEntryCollection digest{};
|
SoundEntryCollection digest{};
|
||||||
};
|
};
|
||||||
@@ -98,6 +100,7 @@ namespace game::resource::xml
|
|||||||
Menu menuSchema{};
|
Menu menuSchema{};
|
||||||
Cursor cursorSchema{};
|
Cursor cursorSchema{};
|
||||||
SkillCheck skillCheckSchema{};
|
SkillCheck skillCheckSchema{};
|
||||||
|
Strings strings{};
|
||||||
|
|
||||||
Save save{};
|
Save save{};
|
||||||
|
|
||||||
@@ -119,23 +122,25 @@ namespace game::resource::xml
|
|||||||
|
|
||||||
std::string name{};
|
std::string name{};
|
||||||
std::filesystem::path path{};
|
std::filesystem::path path{};
|
||||||
float weight{50};
|
double weight{50};
|
||||||
float capacity{2000.0f};
|
double weightMax{1000};
|
||||||
float capacityMin{2000.0f};
|
double capacity{2000.0};
|
||||||
float capacityMax{99999.0f};
|
double capacityMin{2000.0};
|
||||||
float capacityMaxMultiplier{1.5f};
|
double capacityMax{99999.0};
|
||||||
float capacityIfOverStuffedOnDigestBonus{0.25f};
|
double capacityMaxMultiplier{1.5};
|
||||||
float caloriesToKilogram{1000.0f};
|
double capacityIfOverStuffedOnDigestBonus{0.25};
|
||||||
float digestionRate{0.05f};
|
double caloriesToKilogram{1000.0};
|
||||||
float digestionRateMin{0.0f};
|
double digestionRate{0.05};
|
||||||
float digestionRateMax{0.25f};
|
double digestionRateMin{0.0};
|
||||||
|
double digestionRateMax{0.25};
|
||||||
int digestionTimerMax{60};
|
int digestionTimerMax{60};
|
||||||
float eatSpeed{1.0f};
|
int textBlipPeriodBase{3};
|
||||||
float eatSpeedMin{1.0f};
|
double eatSpeed{1.0};
|
||||||
float eatSpeedMax{3.0f};
|
double eatSpeedMin{1.0};
|
||||||
float blinkChance{1.0f};
|
double eatSpeedMax{3.0};
|
||||||
float gurgleChance{1.0f};
|
double blinkChance{1.0};
|
||||||
float gurgleCapacityMultiplier{1.0f};
|
double gurgleChance{1.0};
|
||||||
|
double gurgleCapacityMultiplier{1.0};
|
||||||
Dialogue::PoolReference pool{-1};
|
Dialogue::PoolReference pool{-1};
|
||||||
|
|
||||||
Character() = default;
|
Character() = default;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace game::resource::xml
|
|||||||
|
|
||||||
query_string_attribute(root, "Name", &name);
|
query_string_attribute(root, "Name", &name);
|
||||||
query_string_attribute(root, "Description", &description);
|
query_string_attribute(root, "Description", &description);
|
||||||
query_string_attribute(root, "Author", &author);
|
query_string_attribute(root, "Credits", &credits);
|
||||||
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
|
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
|
||||||
root->QueryFloatAttribute("Weight", &weight);
|
root->QueryFloatAttribute("Weight", &weight);
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace game::resource::xml
|
|||||||
int stages{1};
|
int stages{1};
|
||||||
|
|
||||||
std::string name{};
|
std::string name{};
|
||||||
std::string author{};
|
std::string credits{};
|
||||||
std::string description{};
|
std::string description{};
|
||||||
std::filesystem::path path{};
|
std::filesystem::path path{};
|
||||||
float weight{50};
|
float weight{50};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "../../log.hpp"
|
#include "../../log.hpp"
|
||||||
#include "../../util/math.hpp"
|
#include "../../util/math.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
|
||||||
using namespace tinyxml2;
|
using namespace tinyxml2;
|
||||||
@@ -98,6 +99,8 @@ namespace game::resource::xml
|
|||||||
|
|
||||||
id++;
|
id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entrySelectionOrder.assign(entries.size(), -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto element = root->FirstChildElement("Pools"))
|
if (auto element = root->FirstChildElement("Pools"))
|
||||||
@@ -158,15 +161,32 @@ namespace game::resource::xml
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Dialogue::Pool::get() const
|
int Dialogue::entry_pick(Pool& pool)
|
||||||
{
|
{
|
||||||
if (this->empty()) return -1;
|
if (pool.empty()) return -1;
|
||||||
auto index = rand() % this->size();
|
|
||||||
return this->at(index);
|
std::vector<int> unselected{};
|
||||||
|
for (auto id : pool)
|
||||||
|
if (id >= 0 && id < (int)entrySelectionOrder.size() && entrySelectionOrder[id] < 0) unselected.emplace_back(id);
|
||||||
|
|
||||||
|
std::vector<int> candidates = unselected.empty() ? std::vector<int>(pool.begin(), pool.end()) : unselected;
|
||||||
|
if (candidates.empty()) return -1;
|
||||||
|
|
||||||
|
auto oldestOrder = entrySelectionOrder[candidates.front()];
|
||||||
|
for (auto id : candidates)
|
||||||
|
oldestOrder = std::min(oldestOrder, entrySelectionOrder[id]);
|
||||||
|
|
||||||
|
std::vector<int> oldestCandidates{};
|
||||||
|
for (auto id : candidates)
|
||||||
|
if (entrySelectionOrder[id] == oldestOrder) oldestCandidates.emplace_back(id);
|
||||||
|
|
||||||
|
auto pickedID = oldestCandidates.at(rand() % oldestCandidates.size());
|
||||||
|
entrySelectionOrder[pickedID] = selectionCounter++;
|
||||||
|
return pickedID;
|
||||||
}
|
}
|
||||||
Dialogue::Entry* Dialogue::get(int id) { return &entries.at(id); }
|
Dialogue::Entry* Dialogue::get(int id) { return &entries.at(id); }
|
||||||
Dialogue::Entry* Dialogue::get(Dialogue::EntryReference& entry) { return &entries.at(entry.id); }
|
Dialogue::Entry* Dialogue::get(Dialogue::EntryReference& entry) { return &entries.at(entry.id); }
|
||||||
Dialogue::Entry* Dialogue::get(const std::string& string) { return &entries.at(entryIDMap.at(string)); }
|
Dialogue::Entry* Dialogue::get(const std::string& string) { return &entries.at(entryIDMap.at(string)); }
|
||||||
Dialogue::Entry* Dialogue::get(Dialogue::PoolReference& pool) { return &entries.at(pools.at(pool.id).get()); }
|
Dialogue::Entry* Dialogue::get(Dialogue::PoolReference& pool) { return &entries.at(entry_pick(pools.at(pool.id))); }
|
||||||
Dialogue::Entry* Dialogue::get(Dialogue::Pool& pool) { return &entries.at(pool.get()); }
|
Dialogue::Entry* Dialogue::get(Dialogue::Pool& pool) { return &entries.at(entry_pick(pool)); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "../../util/physfs.hpp"
|
#include "../../util/physfs.hpp"
|
||||||
|
|
||||||
@@ -45,11 +46,7 @@ namespace game::resource::xml
|
|||||||
inline bool is_valid() const { return id != -1; };
|
inline bool is_valid() const { return id != -1; };
|
||||||
};
|
};
|
||||||
|
|
||||||
class Pool : public std::vector<int>
|
class Pool : public std::vector<int> {};
|
||||||
{
|
|
||||||
public:
|
|
||||||
int get() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<std::string, int> entryIDMap;
|
std::map<std::string, int> entryIDMap;
|
||||||
std::map<int, std::string> entryIDMapReverse;
|
std::map<int, std::string> entryIDMapReverse;
|
||||||
@@ -57,6 +54,8 @@ namespace game::resource::xml
|
|||||||
|
|
||||||
std::vector<Pool> pools{};
|
std::vector<Pool> pools{};
|
||||||
std::map<std::string, int> poolMap{};
|
std::map<std::string, int> poolMap{};
|
||||||
|
std::vector<long long> entrySelectionOrder{};
|
||||||
|
long long selectionCounter{};
|
||||||
|
|
||||||
EntryReference start{-1};
|
EntryReference start{-1};
|
||||||
EntryReference end{-1};
|
EntryReference end{-1};
|
||||||
@@ -83,6 +82,7 @@ namespace game::resource::xml
|
|||||||
Entry* get(Dialogue::EntryReference&);
|
Entry* get(Dialogue::EntryReference&);
|
||||||
Entry* get(Dialogue::Pool&);
|
Entry* get(Dialogue::Pool&);
|
||||||
Entry* get(Dialogue::PoolReference&);
|
Entry* get(Dialogue::PoolReference&);
|
||||||
|
int entry_pick(Pool&);
|
||||||
void query_entry_id(tinyxml2::XMLElement* element, const char* name, int& id);
|
void query_entry_id(tinyxml2::XMLElement* element, const char* name, int& id);
|
||||||
void query_pool_id(tinyxml2::XMLElement* element, const char* name, int& id);
|
void query_pool_id(tinyxml2::XMLElement* element, const char* name, int& id);
|
||||||
inline bool is_valid() const { return isValid; };
|
inline bool is_valid() const { return isValid; };
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ namespace game::resource::xml
|
|||||||
std::string itemTextureRootPath{};
|
std::string itemTextureRootPath{};
|
||||||
query_string_attribute(element, "TextureRootPath", &itemTextureRootPath);
|
query_string_attribute(element, "TextureRootPath", &itemTextureRootPath);
|
||||||
|
|
||||||
element->QueryIntAttribute("ChewCount", &chewCount);
|
element->QueryIntAttribute("Durability", &durability);
|
||||||
|
if (element->FindAttribute("ChewCount")) element->QueryIntAttribute("ChewCount", &durability);
|
||||||
element->QueryIntAttribute("QuantityMax", &quantityMax);
|
element->QueryIntAttribute("QuantityMax", &quantityMax);
|
||||||
|
|
||||||
for (auto child = element->FirstChildElement("Item"); child; child = child->NextSiblingElement("Item"))
|
for (auto child = element->FirstChildElement("Item"); child; child = child->NextSiblingElement("Item"))
|
||||||
@@ -121,11 +122,14 @@ namespace game::resource::xml
|
|||||||
query_string_attribute(child, "Description", &item.description);
|
query_string_attribute(child, "Description", &item.description);
|
||||||
|
|
||||||
query_float_optional_attribute(child, "Calories", item.calories);
|
query_float_optional_attribute(child, "Calories", item.calories);
|
||||||
|
query_float_optional_attribute(child, "CapacityBonus", item.capacityBonus);
|
||||||
|
query_optional_vec3(child, "ColorR", "ColorG", "ColorB", item.color);
|
||||||
query_float_optional_attribute(child, "DigestionBonus", item.digestionBonus);
|
query_float_optional_attribute(child, "DigestionBonus", item.digestionBonus);
|
||||||
query_float_optional_attribute(child, "EatSpeedBonus", item.eatSpeedBonus);
|
query_float_optional_attribute(child, "EatSpeedBonus", item.eatSpeedBonus);
|
||||||
query_float_optional_attribute(child, "Gravity", item.gravity);
|
query_float_optional_attribute(child, "Gravity", item.gravity);
|
||||||
|
|
||||||
query_int_optional_attribute(child, "ChewCount", item.chewCount);
|
query_int_optional_attribute(child, "Durability", item.durability);
|
||||||
|
if (!item.durability.has_value()) query_int_optional_attribute(child, "ChewCount", item.durability);
|
||||||
|
|
||||||
if (child->FindAttribute("UpgradeID"))
|
if (child->FindAttribute("UpgradeID"))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include "../audio.hpp"
|
#include "../audio.hpp"
|
||||||
#include "anm2.hpp"
|
#include "anm2.hpp"
|
||||||
#include "sound_entry.hpp"
|
#include "sound_entry.hpp"
|
||||||
@@ -46,10 +48,12 @@ namespace game::resource::xml
|
|||||||
std::optional<int> upgradeID{};
|
std::optional<int> upgradeID{};
|
||||||
std::optional<int> flavorID;
|
std::optional<int> flavorID;
|
||||||
std::optional<float> calories{};
|
std::optional<float> calories{};
|
||||||
|
std::optional<float> capacityBonus{};
|
||||||
|
std::optional<glm::vec3> color{};
|
||||||
std::optional<float> eatSpeedBonus{};
|
std::optional<float> eatSpeedBonus{};
|
||||||
std::optional<float> digestionBonus{};
|
std::optional<float> digestionBonus{};
|
||||||
std::optional<float> gravity{};
|
std::optional<float> gravity{};
|
||||||
std::optional<int> chewCount{};
|
std::optional<int> durability{};
|
||||||
bool isSkillCheckReward{};
|
bool isSkillCheckReward{};
|
||||||
bool isToggleSpritesheet{};
|
bool isToggleSpritesheet{};
|
||||||
};
|
};
|
||||||
@@ -90,7 +94,7 @@ namespace game::resource::xml
|
|||||||
Animations animations{};
|
Animations animations{};
|
||||||
Sounds sounds{};
|
Sounds sounds{};
|
||||||
Anm2 baseAnm2{};
|
Anm2 baseAnm2{};
|
||||||
int chewCount{2};
|
int durability{2};
|
||||||
int quantityMax{99};
|
int quantityMax{99};
|
||||||
|
|
||||||
bool isValid{};
|
bool isValid{};
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ namespace game::resource::xml
|
|||||||
{
|
{
|
||||||
Item item{};
|
Item item{};
|
||||||
child->QueryIntAttribute("ID", &item.id);
|
child->QueryIntAttribute("ID", &item.id);
|
||||||
child->QueryIntAttribute("ChewCount", &item.chewCount);
|
child->QueryIntAttribute("Durability", &item.durability);
|
||||||
|
if (child->FindAttribute("ChewCount")) child->QueryIntAttribute("ChewCount", &item.durability);
|
||||||
child->QueryFloatAttribute("PositionX", &item.position.x);
|
child->QueryFloatAttribute("PositionX", &item.position.x);
|
||||||
child->QueryFloatAttribute("PositionY", &item.position.y);
|
child->QueryFloatAttribute("PositionY", &item.position.y);
|
||||||
child->QueryFloatAttribute("VelocityX", &item.velocity.x);
|
child->QueryFloatAttribute("VelocityX", &item.velocity.x);
|
||||||
@@ -162,7 +163,7 @@ namespace game::resource::xml
|
|||||||
auto itemElement = itemsElement->InsertNewChildElement("Item");
|
auto itemElement = itemsElement->InsertNewChildElement("Item");
|
||||||
|
|
||||||
itemElement->SetAttribute("ID", item.id);
|
itemElement->SetAttribute("ID", item.id);
|
||||||
itemElement->SetAttribute("ChewCount", item.chewCount);
|
itemElement->SetAttribute("Durability", item.durability);
|
||||||
itemElement->SetAttribute("PositionX", item.position.x);
|
itemElement->SetAttribute("PositionX", item.position.x);
|
||||||
itemElement->SetAttribute("PositionY", item.position.y);
|
itemElement->SetAttribute("PositionY", item.position.y);
|
||||||
itemElement->SetAttribute("VelocityX", item.velocity.x);
|
itemElement->SetAttribute("VelocityX", item.velocity.x);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace game::resource::xml
|
|||||||
struct Item
|
struct Item
|
||||||
{
|
{
|
||||||
int id{};
|
int id{};
|
||||||
int chewCount{};
|
int durability{};
|
||||||
glm::vec2 position{};
|
glm::vec2 position{};
|
||||||
glm::vec2 velocity{};
|
glm::vec2 velocity{};
|
||||||
float rotation{};
|
float rotation{};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace game::resource::xml
|
|||||||
int volume{50};
|
int volume{50};
|
||||||
bool isUseCharacterColor{true};
|
bool isUseCharacterColor{true};
|
||||||
|
|
||||||
glm::vec3 color{0.120f, 0.515f, 0.115f};
|
glm::vec3 color{0.120f, 0.120f, 0.120f};
|
||||||
glm::ivec2 windowSize{1600, 900};
|
glm::ivec2 windowSize{1600, 900};
|
||||||
glm::vec2 windowPosition{};
|
glm::vec2 windowPosition{};
|
||||||
|
|
||||||
|
|||||||
48
src/resource/xml/strings.cpp
Normal file
48
src/resource/xml/strings.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "strings.hpp"
|
||||||
|
|
||||||
|
#include "../../log.hpp"
|
||||||
|
#include "util.hpp"
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
using namespace tinyxml2;
|
||||||
|
|
||||||
|
namespace game::resource::xml
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string definition_element_name_get(const Strings::Definition& definition)
|
||||||
|
{
|
||||||
|
std::string name = definition.attribute;
|
||||||
|
if (name.rfind("Text", 0) == 0) name.replace(0, 4, "String");
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Strings::Strings()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
values[i] = definitions[i].fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
Strings::Strings(const util::physfs::Path& path)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
values[i] = definitions[i].fallback;
|
||||||
|
|
||||||
|
XMLDocument document;
|
||||||
|
if (document_load(path, document) != XML_SUCCESS) return;
|
||||||
|
|
||||||
|
auto root = document.RootElement();
|
||||||
|
if (!root) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < Count; i++)
|
||||||
|
if (auto element = root->FirstChildElement(definition_element_name_get(definitions[i]).c_str()))
|
||||||
|
query_string_attribute(element, "Text", &values[i]);
|
||||||
|
|
||||||
|
isValid = true;
|
||||||
|
logger.info(std::format("Initialized strings: {}", path.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& Strings::get(Type type) const { return values[type]; }
|
||||||
|
}
|
||||||
159
src/resource/xml/strings.hpp
Normal file
159
src/resource/xml/strings.hpp
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../util/physfs.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace game::resource::xml
|
||||||
|
{
|
||||||
|
#define GAME_XML_STRING_LIST(X) \
|
||||||
|
X(MenuTabInteract, "TextMenuTabInteract", "Interact") \
|
||||||
|
X(MenuTabArcade, "TextMenuTabArcade", "Arcade") \
|
||||||
|
X(MenuTabInventory, "TextMenuTabInventory", "Inventory") \
|
||||||
|
X(MenuTabSettings, "TextMenuTabSettings", "Settings") \
|
||||||
|
X(MenuTabCheats, "TextMenuTabCheats", "Cheats") \
|
||||||
|
X(MenuTabDebug, "TextMenuTabDebug", "Debug") \
|
||||||
|
X(MenuOpenTooltip, "TextMenuOpenTooltip", "Open Main Menu") \
|
||||||
|
X(MenuCloseTooltip, "TextMenuCloseTooltip", "Close Main Menu") \
|
||||||
|
X(InteractChatButton, "TextInteractChatButton", "Let's chat!") \
|
||||||
|
X(InteractHelpButton, "TextInteractHelpButton", "Help") \
|
||||||
|
X(InteractFeelingButton, "TextInteractFeelingButton", "How are you feeling?") \
|
||||||
|
X(InteractWeightFormat, "TextInteractWeightFormat", "Weight: %0.2f %s (Stage: %i)") \
|
||||||
|
X(InteractCapacityFormat, "TextInteractCapacityFormat", "Capacity: %0.0f kcal (Max: %0.0f kcal)") \
|
||||||
|
X(InteractDigestionRateFormat, "TextInteractDigestionRateFormat", "Digestion Rate: %0.2f%%/sec") \
|
||||||
|
X(InteractEatingSpeedFormat, "TextInteractEatingSpeedFormat", "Eating Speed: %0.2fx") \
|
||||||
|
X(InteractTotalCaloriesFormat, "TextInteractTotalCaloriesFormat", "Total Calories Consumed: %0.0f kcal") \
|
||||||
|
X(InteractTotalFoodItemsFormat, "TextInteractTotalFoodItemsFormat", "Total Food Items Eaten: %i") \
|
||||||
|
X(SettingsMeasurementSystem, "TextSettingsMeasurementSystem", "Measurement System") \
|
||||||
|
X(SettingsMetric, "TextSettingsMetric", "Metric") \
|
||||||
|
X(SettingsMetricTooltip, "TextSettingsMetricTooltip", "Use kilograms (kg).") \
|
||||||
|
X(SettingsImperial, "TextSettingsImperial", "Imperial") \
|
||||||
|
X(SettingsImperialTooltip, "TextSettingsImperialTooltip", "Use pounds (lbs).") \
|
||||||
|
X(SettingsSound, "TextSettingsSound", "Sound") \
|
||||||
|
X(SettingsVolume, "TextSettingsVolume", "Volume") \
|
||||||
|
X(SettingsVolumeTooltip, "TextSettingsVolumeTooltip", "Adjust master volume.") \
|
||||||
|
X(SettingsAppearance, "TextSettingsAppearance", "Appearance") \
|
||||||
|
X(SettingsUseCharacterColor, "TextSettingsUseCharacterColor", "Use Character Color") \
|
||||||
|
X(SettingsUseCharacterColorTooltip, "TextSettingsUseCharacterColorTooltip", \
|
||||||
|
"When playing, the UI will use the character's preset UI color.") \
|
||||||
|
X(SettingsColor, "TextSettingsColor", "Color") \
|
||||||
|
X(SettingsColorTooltip, "TextSettingsColorTooltip", "Change the UI color.") \
|
||||||
|
X(SettingsResetButton, "TextSettingsResetButton", "Reset to Default") \
|
||||||
|
X(SettingsSaveButton, "TextSettingsSaveButton", "Save") \
|
||||||
|
X(SettingsSaveTooltip, "TextSettingsSaveTooltip", "Save the game.\n(Note: the game autosaves frequently.)") \
|
||||||
|
X(SettingsReturnToCharactersButton, "TextSettingsReturnToCharactersButton", "Return to Characters") \
|
||||||
|
X(SettingsReturnToCharactersTooltip, "TextSettingsReturnToCharactersTooltip", \
|
||||||
|
"Go back to the character selection screen.\nProgress will be saved.") \
|
||||||
|
X(ToastCheatsUnlocked, "TextToastCheatsUnlocked", "Cheats unlocked!") \
|
||||||
|
X(ToastSaving, "TextToastSaving", "Saving...") \
|
||||||
|
X(ToolsHomeButton, "TextToolsHomeButton", "Home") \
|
||||||
|
X(ToolsHomeTooltip, "TextToolsHomeTooltip", "Reset camera view.\n(Shortcut: Home)") \
|
||||||
|
X(ToolsOpenTooltip, "TextToolsOpenTooltip", "Open Tools") \
|
||||||
|
X(ToolsCloseTooltip, "TextToolsCloseTooltip", "Close Tools") \
|
||||||
|
X(DebugCursorScreenFormat, "TextDebugCursorScreenFormat", "Cursor Pos (Screen): %0.0f, %0.0f") \
|
||||||
|
X(DebugCursorWorldFormat, "TextDebugCursorWorldFormat", "Cursor Pos (World): %0.0f, %0.0f") \
|
||||||
|
X(DebugAnimations, "TextDebugAnimations", "Animations") \
|
||||||
|
X(DebugNowPlayingFormat, "TextDebugNowPlayingFormat", "Now Playing: %s") \
|
||||||
|
X(DebugDialogue, "TextDebugDialogue", "Dialogue") \
|
||||||
|
X(DebugShowNulls, "TextDebugShowNulls", "Show Nulls (Hitboxes)") \
|
||||||
|
X(DebugShowWorldBounds, "TextDebugShowWorldBounds", "Show World Bounds") \
|
||||||
|
X(DebugItem, "TextDebugItem", "Item") \
|
||||||
|
X(DebugHeld, "TextDebugHeld", "Held") \
|
||||||
|
X(DebugItemTypeFormat, "TextDebugItemTypeFormat", "Type: %i") \
|
||||||
|
X(DebugItemPositionFormat, "TextDebugItemPositionFormat", "Position: %0.0f, %0.0f") \
|
||||||
|
X(DebugItemVelocityFormat, "TextDebugItemVelocityFormat", "Velocity: %0.0f, %0.0f") \
|
||||||
|
X(DebugItemDurabilityFormat, "TextDebugItemDurabilityFormat", "Durability: %i") \
|
||||||
|
X(InventoryEmptyHint, "TextInventoryEmptyHint", "Check the \"Arcade\" tab to earn rewards!") \
|
||||||
|
X(InventoryFlavorFormat, "TextInventoryFlavorFormat", "Flavor: %s") \
|
||||||
|
X(InventoryCaloriesFormat, "TextInventoryCaloriesFormat", "%0.0f kcal") \
|
||||||
|
X(InventoryDurabilityFormat, "TextInventoryDurabilityFormat", "Durability: %i") \
|
||||||
|
X(InventoryCapacityBonusFormat, "TextInventoryCapacityBonusFormat", "Capacity Bonus: +%0.0f kcal") \
|
||||||
|
X(InventoryDigestionRateBonusFormat, "TextInventoryDigestionRateBonusFormat", "Digestion Rate Bonus: +%0.2f%% / sec") \
|
||||||
|
X(InventoryDigestionRatePenaltyFormat, "TextInventoryDigestionRatePenaltyFormat", "Digestion Rate Penalty: %0.2f%% / sec") \
|
||||||
|
X(InventoryEatSpeedBonusFormat, "TextInventoryEatSpeedBonusFormat", "Eat Speed Bonus: +%0.2f%% / sec") \
|
||||||
|
X(InventoryEatSpeedPenaltyFormat, "TextInventoryEatSpeedPenaltyFormat", "Eat Speed Penalty: %0.2f%% / sec") \
|
||||||
|
X(InventoryUpgradePreviewFormat, "TextInventoryUpgradePreviewFormat", "Upgrade: %ix -> %s") \
|
||||||
|
X(InventoryUnknown, "TextInventoryUnknown", "???") \
|
||||||
|
X(InventorySpawnButton, "TextInventorySpawnButton", "Spawn") \
|
||||||
|
X(InventoryUpgradeButton, "TextInventoryUpgradeButton", "Upgrade") \
|
||||||
|
X(InventoryUpgradeAllButton, "TextInventoryUpgradeAllButton", "Upgrade All") \
|
||||||
|
X(InventoryUpgradeNoPath, "TextInventoryUpgradeNoPath", "This item cannot be upgraded.") \
|
||||||
|
X(InventoryUpgradeNeedsTemplate, "TextInventoryUpgradeNeedsTemplate", "Needs {}x to upgrade into {}!") \
|
||||||
|
X(InventoryUpgradeOneTemplate, "TextInventoryUpgradeOneTemplate", "Use {}x to upgrade into 1x {}.") \
|
||||||
|
X(InventoryUpgradeAllTemplate, "TextInventoryUpgradeAllTemplate", "Use {}x to upgrade into {}x {}.") \
|
||||||
|
X(ArcadeSkillCheckName, "TextArcadeSkillCheckName", "Skill Check") \
|
||||||
|
X(ArcadeSkillCheckDescription, "TextArcadeSkillCheckDescription", \
|
||||||
|
"Test your timing to build score, chain combos, and earn rewards based on your performance.") \
|
||||||
|
X(ArcadePlayButton, "TextArcadePlayButton", "Play") \
|
||||||
|
X(ArcadeStatsButton, "TextArcadeStatsButton", "Stats") \
|
||||||
|
X(ArcadeBackButton, "TextArcadeBackButton", "Back") \
|
||||||
|
X(ArcadeBestFormat, "TextArcadeBestFormat", "Best: %i pts (%ix)") \
|
||||||
|
X(ArcadeTotalSkillChecksFormat, "TextArcadeTotalSkillChecksFormat", "Total Skill Checks: %i") \
|
||||||
|
X(ArcadeAccuracyFormat, "TextArcadeAccuracyFormat", "Accuracy: %0.2f%%") \
|
||||||
|
X(InfoProgressMax, "TextInfoProgressMax", "MAX") \
|
||||||
|
X(InfoProgressToNextStage, "TextInfoProgressToNextStage", "To Next Stage") \
|
||||||
|
X(InfoStageProgressFormat, "TextInfoStageProgressFormat", "Stage: %i/%i (%0.1f%%)") \
|
||||||
|
X(InfoMaxedOut, "TextInfoMaxedOut", "Maxed out!") \
|
||||||
|
X(InfoStageStartFormat, "TextInfoStageStartFormat", "Start: %0.2f %s") \
|
||||||
|
X(InfoStageCurrentFormat, "TextInfoStageCurrentFormat", "Current: %0.2f %s") \
|
||||||
|
X(InfoStageNextFormat, "TextInfoStageNextFormat", "Next: %0.2f %s") \
|
||||||
|
X(InfoDigestion, "TextInfoDigestion", "Digestion") \
|
||||||
|
X(InfoDigesting, "TextInfoDigesting", "Digesting...") \
|
||||||
|
X(InfoDigestionInProgress, "TextInfoDigestionInProgress", "Digestion in progress...") \
|
||||||
|
X(InfoGiveFoodToStartDigesting, "TextInfoGiveFoodToStartDigesting", "Give food to start digesting!") \
|
||||||
|
X(InfoDigestionRateFormat, "TextInfoDigestionRateFormat", "Rate: %0.2f%% / sec") \
|
||||||
|
X(InfoEatingSpeedFormat, "TextInfoEatingSpeedFormat", "Eating Speed: %0.2fx") \
|
||||||
|
X(SkillCheckScoreFormat, "TextSkillCheckScoreFormat", "Score: %i pts (%ix)") \
|
||||||
|
X(SkillCheckBestFormat, "TextSkillCheckBestFormat", "Best: %i pts (%ix)") \
|
||||||
|
X(SkillCheckInstructions, "TextSkillCheckInstructions", "Match the line to the colored areas with Space/click! Better performance, better rewards!") \
|
||||||
|
X(SkillCheckScoreLoss, "TextSkillCheckScoreLoss", "-1") \
|
||||||
|
X(SkillCheckRewardToast, "TextSkillCheckRewardToast", "Fantastic score! Congratulations!") \
|
||||||
|
X(SkillCheckHighScoreToast, "TextSkillCheckHighScoreToast", "High Score!") \
|
||||||
|
X(SkillCheckGradeSuccessTemplate, "TextSkillCheckGradeSuccessTemplate", "{} (+{})") \
|
||||||
|
X(SkillCheckMenuButton, "TextSkillCheckMenuButton", "Menu") \
|
||||||
|
X(CheatsCalories, "TextCheatsCalories", "Calories") \
|
||||||
|
X(CheatsCapacity, "TextCheatsCapacity", "Capacity") \
|
||||||
|
X(CheatsWeight, "TextCheatsWeight", "Weight") \
|
||||||
|
X(CheatsWeightFormat, "TextCheatsWeightFormat", "%0.2f kg") \
|
||||||
|
X(CheatsStage, "TextCheatsStage", "Stage") \
|
||||||
|
X(CheatsDigestionRate, "TextCheatsDigestionRate", "Digestion Rate") \
|
||||||
|
X(CheatsDigestionRateFormat, "TextCheatsDigestionRateFormat", "%0.2f% / tick") \
|
||||||
|
X(CheatsEatSpeed, "TextCheatsEatSpeed", "Eat Speed") \
|
||||||
|
X(CheatsEatSpeedFormat, "TextCheatsEatSpeedFormat", "%0.2fx") \
|
||||||
|
X(CheatsDigestButton, "TextCheatsDigestButton", "Digest") \
|
||||||
|
X(CheatsInventory, "TextCheatsInventory", "Inventory")
|
||||||
|
|
||||||
|
class Strings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
#define X(type, attr, fallback) type,
|
||||||
|
GAME_XML_STRING_LIST(X)
|
||||||
|
#undef X
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Definition
|
||||||
|
{
|
||||||
|
const char* attribute;
|
||||||
|
const char* fallback;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static constexpr std::array<Definition, Count> definitions{{
|
||||||
|
#define X(type, attr, fallback) {attr, fallback},
|
||||||
|
GAME_XML_STRING_LIST(X)
|
||||||
|
#undef X
|
||||||
|
}};
|
||||||
|
|
||||||
|
std::array<std::string, Count> values{};
|
||||||
|
|
||||||
|
bool isValid{};
|
||||||
|
|
||||||
|
Strings();
|
||||||
|
Strings(const util::physfs::Path&);
|
||||||
|
|
||||||
|
const std::string& get(Type) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -117,6 +117,29 @@ namespace game::resource::xml
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XMLError query_optional_vec3(XMLElement* element, const char* attributeX, const char* attributeY,
|
||||||
|
const char* attributeZ, std::optional<glm::vec3>& value)
|
||||||
|
{
|
||||||
|
auto hasX = element->FindAttribute(attributeX);
|
||||||
|
auto hasY = element->FindAttribute(attributeY);
|
||||||
|
auto hasZ = element->FindAttribute(attributeZ);
|
||||||
|
|
||||||
|
if (!hasX && !hasY && !hasZ)
|
||||||
|
{
|
||||||
|
value.reset();
|
||||||
|
return XML_NO_ATTRIBUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = glm::vec3();
|
||||||
|
auto result = XML_SUCCESS;
|
||||||
|
|
||||||
|
if (hasX) result = query_result_merge(result, element->QueryFloatAttribute(attributeX, &value->x));
|
||||||
|
if (hasY) result = query_result_merge(result, element->QueryFloatAttribute(attributeY, &value->y));
|
||||||
|
if (hasZ) result = query_result_merge(result, element->QueryFloatAttribute(attributeZ, &value->z));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
XMLError document_load(const physfs::Path& path, XMLDocument& document)
|
XMLError document_load(const physfs::Path& path, XMLDocument& document)
|
||||||
{
|
{
|
||||||
if (!path.is_valid())
|
if (!path.is_valid())
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ namespace game::resource::xml
|
|||||||
std::optional<float>& value);
|
std::optional<float>& value);
|
||||||
tinyxml2::XMLError query_int_optional_attribute(tinyxml2::XMLElement* element, const char* attribute,
|
tinyxml2::XMLError query_int_optional_attribute(tinyxml2::XMLElement* element, const char* attribute,
|
||||||
std::optional<int>& value);
|
std::optional<int>& value);
|
||||||
|
tinyxml2::XMLError query_optional_vec3(tinyxml2::XMLElement* element, const char* attributeX, const char* attributeY,
|
||||||
|
const char* attributeZ, std::optional<glm::vec3>& value);
|
||||||
|
|
||||||
tinyxml2::XMLError query_event_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& eventID);
|
tinyxml2::XMLError query_event_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& eventID);
|
||||||
tinyxml2::XMLError query_layer_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& layerID);
|
tinyxml2::XMLError query_layer_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& layerID);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "play.hpp"
|
#include "play.hpp"
|
||||||
|
#include "play/style.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
@@ -11,12 +12,25 @@
|
|||||||
#include "../util/math.hpp"
|
#include "../util/math.hpp"
|
||||||
|
|
||||||
using namespace game::resource;
|
using namespace game::resource;
|
||||||
|
using namespace game::resource::xml;
|
||||||
using namespace game::util;
|
using namespace game::util;
|
||||||
using namespace game::state::play;
|
using namespace game::state::play;
|
||||||
using namespace glm;
|
using namespace glm;
|
||||||
|
|
||||||
namespace game::state
|
namespace game::state
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int durability_animation_index_get(const resource::xml::Item& schema, const resource::xml::Anm2& anm2, int durability,
|
||||||
|
int durabilityMax)
|
||||||
|
{
|
||||||
|
if (durability >= durabilityMax) return -1;
|
||||||
|
|
||||||
|
auto animationName = schema.animations.chew + std::to_string(std::max(0, durability));
|
||||||
|
return anm2.animationMap.contains(animationName) ? anm2.animationMap.at(animationName) : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
World::Focus Play::focus_get()
|
World::Focus Play::focus_get()
|
||||||
{
|
{
|
||||||
if (!isWindows) return World::CENTER;
|
if (!isWindows) return World::CENTER;
|
||||||
@@ -40,9 +54,10 @@ namespace game::state
|
|||||||
|
|
||||||
character =
|
character =
|
||||||
entity::Character(data, vec2(World::BOUNDS.x + World::BOUNDS.z * 0.5f, World::BOUNDS.w - World::BOUNDS.y));
|
entity::Character(data, vec2(World::BOUNDS.x + World::BOUNDS.z * 0.5f, World::BOUNDS.w - World::BOUNDS.y));
|
||||||
character.digestionRate = glm::clamp(data.digestionRateMin, character.digestionRate, data.digestionRateMax);
|
character.digestionRate =
|
||||||
character.eatSpeed = glm::clamp(data.eatSpeedMin, character.eatSpeed, data.eatSpeedMax);
|
glm::clamp(character.digestionRate, (float)data.digestionRateMin, (float)data.digestionRateMax);
|
||||||
character.capacity = glm::clamp(data.capacityMin, character.capacity, data.capacityMax);
|
character.eatSpeed = glm::clamp(character.eatSpeed, (float)data.eatSpeedMin, (float)data.eatSpeedMax);
|
||||||
|
character.capacity = glm::clamp(character.capacity, (float)data.capacityMin, (float)data.capacityMax);
|
||||||
|
|
||||||
auto isAlternateSpritesheet =
|
auto isAlternateSpritesheet =
|
||||||
(game == NEW_GAME && math::random_percent_roll(data.alternateSpritesheet.chanceOnNewGame));
|
(game == NEW_GAME && math::random_percent_roll(data.alternateSpritesheet.chanceOnNewGame));
|
||||||
@@ -71,29 +86,34 @@ namespace game::state
|
|||||||
for (auto& item : saveData.items)
|
for (auto& item : saveData.items)
|
||||||
{
|
{
|
||||||
auto& anm2 = itemSchema.anm2s.at(item.id);
|
auto& anm2 = itemSchema.anm2s.at(item.id);
|
||||||
auto chewAnimation = itemSchema.animations.chew + std::to_string(item.chewCount);
|
auto& schemaItem = itemSchema.items.at(item.id);
|
||||||
auto animationIndex = item.chewCount > 0 ? anm2.animationMap[chewAnimation] : -1;
|
auto durabilityMax = schemaItem.durability.value_or(itemSchema.durability);
|
||||||
|
auto animationIndex = durability_animation_index_get(itemSchema, anm2, item.durability, durabilityMax);
|
||||||
auto& saveItem = itemSchema.anm2s.at(item.id);
|
auto& saveItem = itemSchema.anm2s.at(item.id);
|
||||||
itemManager.items.emplace_back(saveItem, item.position, item.id, item.chewCount, animationIndex, item.velocity,
|
itemManager.items.emplace_back(saveItem, item.position, item.id, item.durability, animationIndex, item.velocity,
|
||||||
item.rotation);
|
item.rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
imgui::style::rounding_set(menuSchema.rounding);
|
imgui::style::rounding_set(menuSchema.rounding);
|
||||||
imgui::widget::sounds_set(&menuSchema.sounds.hover, &menuSchema.sounds.select);
|
imgui::widget::sounds_set(&menuSchema.sounds.hover, &menuSchema.sounds.select);
|
||||||
menu.color_set_check(resources, character);
|
play::style::color_set(resources, character);
|
||||||
|
|
||||||
menu.skillCheck = SkillCheck(character);
|
menu.arcade = Arcade(character);
|
||||||
menu.skillCheck.totalPlays = saveData.totalPlays;
|
menu.arcade.skillCheck.totalPlays = saveData.totalPlays;
|
||||||
menu.skillCheck.highScore = saveData.highScore;
|
menu.arcade.skillCheck.highScore = saveData.highScore;
|
||||||
menu.skillCheck.bestCombo = saveData.bestCombo;
|
menu.arcade.skillCheck.bestCombo = saveData.bestCombo;
|
||||||
menu.skillCheck.gradeCounts = saveData.gradeCounts;
|
menu.arcade.skillCheck.gradeCounts = saveData.gradeCounts;
|
||||||
menu.skillCheck.isHighScoreAchieved = saveData.highScore > 0 ? true : false;
|
menu.arcade.skillCheck.isHighScoreAchieved = saveData.highScore > 0 ? true : false;
|
||||||
menu.isChat = character.data.dialogue.help.is_valid() || character.data.dialogue.random.is_valid();
|
|
||||||
|
|
||||||
text.entry = nullptr;
|
text.entry = nullptr;
|
||||||
text.isEnabled = false;
|
text.isEnabled = false;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
menu.isCheats = true;
|
||||||
|
#else
|
||||||
menu.isCheats = false;
|
menu.isCheats = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
isPostgame = saveData.isPostgame;
|
isPostgame = saveData.isPostgame;
|
||||||
if (character.stage_get() >= character.stage_max_get()) isPostgame = true;
|
if (character.stage_get() >= character.stage_max_get()) isPostgame = true;
|
||||||
if (isPostgame) menu.isCheats = true;
|
if (isPostgame) menu.isCheats = true;
|
||||||
@@ -190,7 +210,7 @@ namespace game::state
|
|||||||
menu.isCheats = true;
|
menu.isCheats = true;
|
||||||
cheatCodeIndex = 0;
|
cheatCodeIndex = 0;
|
||||||
cheatCodeStartTime = 0.0;
|
cheatCodeStartTime = 0.0;
|
||||||
toasts.push("Cheats unlocked!");
|
toasts.push(character.data.strings.get(Strings::ToastCheatsUnlocked));
|
||||||
character.data.menuSchema.sounds.cheatsActivated.play();
|
character.data.menuSchema.sounds.cheatsActivated.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,10 +358,10 @@ namespace game::state
|
|||||||
save.digestionTimer = character.digestionTimer;
|
save.digestionTimer = character.digestionTimer;
|
||||||
save.totalCaloriesConsumed = character.totalCaloriesConsumed;
|
save.totalCaloriesConsumed = character.totalCaloriesConsumed;
|
||||||
save.totalFoodItemsEaten = character.totalFoodItemsEaten;
|
save.totalFoodItemsEaten = character.totalFoodItemsEaten;
|
||||||
save.totalPlays = menu.skillCheck.totalPlays;
|
save.totalPlays = menu.arcade.skillCheck.totalPlays;
|
||||||
save.highScore = menu.skillCheck.highScore;
|
save.highScore = menu.arcade.skillCheck.highScore;
|
||||||
save.bestCombo = menu.skillCheck.bestCombo;
|
save.bestCombo = menu.arcade.skillCheck.bestCombo;
|
||||||
save.gradeCounts = menu.skillCheck.gradeCounts;
|
save.gradeCounts = menu.arcade.skillCheck.gradeCounts;
|
||||||
save.isPostgame = isPostgame;
|
save.isPostgame = isPostgame;
|
||||||
save.isAlternateSpritesheet = character.spritesheetType == entity::Character::ALTERNATE;
|
save.isAlternateSpritesheet = character.spritesheetType == entity::Character::ALTERNATE;
|
||||||
|
|
||||||
@@ -352,7 +372,7 @@ namespace game::state
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto& item : itemManager.items)
|
for (auto& item : itemManager.items)
|
||||||
save.items.emplace_back(item.schemaID, item.chewCount, item.position, item.velocity,
|
save.items.emplace_back(item.schemaID, item.durability, item.position, item.velocity,
|
||||||
*item.overrides[item.rotationOverrideID].frame.rotation);
|
*item.overrides[item.rotationOverrideID].frame.rotation);
|
||||||
|
|
||||||
save.isValid = true;
|
save.isValid = true;
|
||||||
@@ -360,6 +380,6 @@ namespace game::state
|
|||||||
resources.character_save_set(characterIndex, save);
|
resources.character_save_set(characterIndex, save);
|
||||||
save.serialize(character.data.save_path_get());
|
save.serialize(character.data.save_path_get());
|
||||||
|
|
||||||
toasts.push("Saving...");
|
toasts.push(character.data.strings.get(Strings::ToastSaving));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
76
src/state/play/arcade.cpp
Normal file
76
src/state/play/arcade.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include "arcade.hpp"
|
||||||
|
|
||||||
|
#include "../../util/imgui/widget.hpp"
|
||||||
|
|
||||||
|
using namespace game::util::imgui;
|
||||||
|
using namespace game::resource::xml;
|
||||||
|
|
||||||
|
namespace game::state::play
|
||||||
|
{
|
||||||
|
Arcade::Arcade(entity::Character& character) : skillCheck(character) {}
|
||||||
|
|
||||||
|
void Arcade::tick() { skillCheck.tick(); }
|
||||||
|
|
||||||
|
void Arcade::update(Resources& resources, entity::Character& character, Inventory& inventory, Text& text)
|
||||||
|
{
|
||||||
|
auto available = ImGui::GetContentRegionAvail();
|
||||||
|
auto& strings = character.data.strings;
|
||||||
|
|
||||||
|
if (view == SKILL_CHECK)
|
||||||
|
{
|
||||||
|
if (skillCheck.update(resources, character, inventory, text)) view = MENU;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buttonHeight = ImGui::GetFrameHeightWithSpacing();
|
||||||
|
auto childSize = ImVec2(available.x, std::max(0.0f, available.y - buttonHeight));
|
||||||
|
|
||||||
|
if (ImGui::BeginChild("##Arcade Child", childSize))
|
||||||
|
{
|
||||||
|
if (view == MENU)
|
||||||
|
{
|
||||||
|
auto buttonWidth = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
|
||||||
|
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_2);
|
||||||
|
ImGui::TextUnformatted(strings.get(Strings::ArcadeSkillCheckName).c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::TextWrapped("%s", strings.get(Strings::ArcadeSkillCheckDescription).c_str());
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::ArcadePlayButton).c_str(), ImVec2(buttonWidth, 0))))
|
||||||
|
view = SKILL_CHECK;
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::ArcadeStatsButton).c_str(), ImVec2(buttonWidth, 0))))
|
||||||
|
view = SKILL_CHECK_STATS;
|
||||||
|
}
|
||||||
|
else if (view == SKILL_CHECK_STATS)
|
||||||
|
{
|
||||||
|
auto& schema = character.data.skillCheckSchema;
|
||||||
|
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_2);
|
||||||
|
ImGui::TextUnformatted(strings.get(Strings::ArcadeSkillCheckName).c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text(strings.get(Strings::ArcadeBestFormat).c_str(), skillCheck.highScore, skillCheck.bestCombo);
|
||||||
|
ImGui::Text(strings.get(Strings::ArcadeTotalSkillChecksFormat).c_str(), skillCheck.totalPlays);
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)schema.grades.size(); i++)
|
||||||
|
{
|
||||||
|
auto& grade = schema.grades[i];
|
||||||
|
ImGui::Text("%s: %i", grade.namePlural.c_str(), skillCheck.gradeCounts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text(strings.get(Strings::ArcadeAccuracyFormat).c_str(), skillCheck.accuracy_score_get(character));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
if (view == SKILL_CHECK_STATS)
|
||||||
|
{
|
||||||
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::ArcadeBackButton).c_str()))) view = MENU;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/state/play/arcade.hpp
Normal file
26
src/state/play/arcade.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "arcade/skill_check.hpp"
|
||||||
|
|
||||||
|
namespace game::state::play
|
||||||
|
{
|
||||||
|
class Arcade
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum View
|
||||||
|
{
|
||||||
|
MENU,
|
||||||
|
SKILL_CHECK,
|
||||||
|
SKILL_CHECK_STATS
|
||||||
|
};
|
||||||
|
|
||||||
|
SkillCheck skillCheck{};
|
||||||
|
View view{MENU};
|
||||||
|
|
||||||
|
Arcade() = default;
|
||||||
|
Arcade(entity::Character&);
|
||||||
|
|
||||||
|
void tick();
|
||||||
|
void update(Resources&, entity::Character&, Inventory&, Text&);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -6,13 +6,16 @@
|
|||||||
#include "../../../util/imgui/widget.hpp"
|
#include "../../../util/imgui/widget.hpp"
|
||||||
#include "../../../util/math.hpp"
|
#include "../../../util/math.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
using namespace game::util;
|
using namespace game::util;
|
||||||
using namespace game::entity;
|
using namespace game::entity;
|
||||||
using namespace game::resource;
|
using namespace game::resource;
|
||||||
|
using namespace game::resource::xml;
|
||||||
using namespace glm;
|
using namespace glm;
|
||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
@@ -69,7 +72,7 @@ namespace game::state::play
|
|||||||
actor.tick();
|
actor.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkillCheck::update(Resources& resources, entity::Character& character, Inventory& inventory, Text& text)
|
bool SkillCheck::update(Resources& resources, entity::Character& character, Inventory& inventory, Text& text)
|
||||||
{
|
{
|
||||||
static constexpr auto BG_COLOR_MULTIPLIER = 0.5f;
|
static constexpr auto BG_COLOR_MULTIPLIER = 0.5f;
|
||||||
static constexpr ImVec4 LINE_COLOR = ImVec4(1, 1, 1, 1);
|
static constexpr ImVec4 LINE_COLOR = ImVec4(1, 1, 1, 1);
|
||||||
@@ -82,25 +85,31 @@ namespace game::state::play
|
|||||||
auto& dialogue = character.data.dialogue;
|
auto& dialogue = character.data.dialogue;
|
||||||
auto& schema = character.data.skillCheckSchema;
|
auto& schema = character.data.skillCheckSchema;
|
||||||
auto& itemSchema = character.data.itemSchema;
|
auto& itemSchema = character.data.itemSchema;
|
||||||
|
auto& strings = character.data.strings;
|
||||||
auto& style = ImGui::GetStyle();
|
auto& style = ImGui::GetStyle();
|
||||||
auto drawList = ImGui::GetWindowDrawList();
|
auto drawList = ImGui::GetWindowDrawList();
|
||||||
auto position = ImGui::GetCursorScreenPos();
|
auto position = ImGui::GetCursorScreenPos();
|
||||||
auto size = ImGui::GetContentRegionAvail();
|
auto size = ImGui::GetContentRegionAvail();
|
||||||
auto spacing = ImGui::GetTextLineHeightWithSpacing();
|
auto spacing = ImGui::GetTextLineHeightWithSpacing();
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
|
auto menuButtonHeight = ImGui::GetFrameHeightWithSpacing();
|
||||||
|
size.y = std::max(0.0f, size.y - menuButtonHeight);
|
||||||
|
|
||||||
auto cursorPos = ImGui::GetCursorPos();
|
auto cursorPos = ImGui::GetCursorPos();
|
||||||
|
|
||||||
ImGui::Text("Score: %i pts (%ix)", score, combo);
|
ImGui::Text(strings.get(Strings::SkillCheckScoreFormat).c_str(), score, combo);
|
||||||
auto bestString = std::format("Best: {} pts({}x)", highScore, bestCombo);
|
std::array<char, 128> bestBuffer{};
|
||||||
|
std::snprintf(bestBuffer.data(), bestBuffer.size(), strings.get(Strings::SkillCheckBestFormat).c_str(), highScore,
|
||||||
|
bestCombo);
|
||||||
|
auto bestString = std::string(bestBuffer.data());
|
||||||
ImGui::SetCursorPos(ImVec2(size.x - ImGui::CalcTextSize(bestString.c_str()).x, cursorPos.y));
|
ImGui::SetCursorPos(ImVec2(size.x - ImGui::CalcTextSize(bestString.c_str()).x, cursorPos.y));
|
||||||
|
|
||||||
ImGui::Text("Best: %i pts (%ix)", highScore, bestCombo);
|
ImGui::Text(strings.get(Strings::SkillCheckBestFormat).c_str(), highScore, bestCombo);
|
||||||
|
|
||||||
if (score == 0 && isActive)
|
if (score == 0 && isActive)
|
||||||
{
|
{
|
||||||
ImGui::SetCursorPos(ImVec2(style.WindowPadding.x, size.y - style.WindowPadding.y));
|
ImGui::SetCursorPos(ImVec2(style.WindowPadding.x, size.y - style.WindowPadding.y));
|
||||||
ImGui::TextWrapped("Match the line to the colored areas with Space/click! Better performance, better rewards!");
|
ImGui::TextWrapped("%s", strings.get(Strings::SkillCheckInstructions).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto barMin = ImVec2(position.x + (size.x * 0.5f) - (spacing * 0.5f), position.y + (spacing * 2.0f));
|
auto barMin = ImVec2(position.x + (size.x * 0.5f) - (spacing * 0.5f), position.y + (spacing * 2.0f));
|
||||||
@@ -193,8 +202,11 @@ namespace game::state::play
|
|||||||
score--;
|
score--;
|
||||||
schema.sounds.scoreLoss.play();
|
schema.sounds.scoreLoss.play();
|
||||||
auto toastMessagePosition =
|
auto toastMessagePosition =
|
||||||
ImVec2(barMin.x - ImGui::CalcTextSize("-1").x - ImGui::GetTextLineHeightWithSpacing(), lineMin.y);
|
ImVec2(barMin.x - ImGui::CalcTextSize(strings.get(Strings::SkillCheckScoreLoss).c_str()).x -
|
||||||
toasts.emplace_back("-1", toastMessagePosition, schema.endTimerMax, schema.endTimerMax);
|
ImGui::GetTextLineHeightWithSpacing(),
|
||||||
|
lineMin.y);
|
||||||
|
toasts.emplace_back(strings.get(Strings::SkillCheckScoreLoss), toastMessagePosition, schema.endTimerMax,
|
||||||
|
schema.endTimerMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,10 +270,10 @@ namespace game::state::play
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto toastMessagePosition =
|
auto toastMessagePosition =
|
||||||
ImVec2(barMin.x - ImGui::CalcTextSize("Fantastic score!\nCongratulations!").x -
|
ImVec2(barMin.x - ImGui::CalcTextSize(strings.get(Strings::SkillCheckRewardToast).c_str()).x -
|
||||||
ImGui::GetTextLineHeightWithSpacing(),
|
ImGui::GetTextLineHeightWithSpacing(),
|
||||||
lineMin.y + (ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y));
|
lineMin.y + (ImGui::GetTextLineHeightWithSpacing() + ImGui::GetStyle().ItemSpacing.y));
|
||||||
toasts.emplace_back("Fantastic score! Congratulations!", toastMessagePosition, schema.endTimerMax,
|
toasts.emplace_back(strings.get(Strings::SkillCheckRewardToast), toastMessagePosition, schema.endTimerMax,
|
||||||
schema.endTimerMax);
|
schema.endTimerMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,9 +286,11 @@ namespace game::state::play
|
|||||||
isHighScoreAchievedThisRun = true;
|
isHighScoreAchievedThisRun = true;
|
||||||
schema.sounds.highScore.play();
|
schema.sounds.highScore.play();
|
||||||
auto toastMessagePosition =
|
auto toastMessagePosition =
|
||||||
ImVec2(barMin.x - ImGui::CalcTextSize("High Score!").x - ImGui::GetTextLineHeightWithSpacing(),
|
ImVec2(barMin.x - ImGui::CalcTextSize(strings.get(Strings::SkillCheckHighScoreToast).c_str()).x -
|
||||||
|
ImGui::GetTextLineHeightWithSpacing(),
|
||||||
lineMin.y + ImGui::GetTextLineHeightWithSpacing());
|
lineMin.y + ImGui::GetTextLineHeightWithSpacing());
|
||||||
toasts.emplace_back("High Score!", toastMessagePosition, schema.endTimerMax, schema.endTimerMax);
|
toasts.emplace_back(strings.get(Strings::SkillCheckHighScoreToast), toastMessagePosition,
|
||||||
|
schema.endTimerMax, schema.endTimerMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,10 +351,10 @@ namespace game::state::play
|
|||||||
{
|
{
|
||||||
score = 0;
|
score = 0;
|
||||||
combo = 0;
|
combo = 0;
|
||||||
if (isHighScoreAchieved) schema.sounds.highScoreLoss.play();
|
if (isHighScoreAchievedThisRun) schema.sounds.highScoreLoss.play();
|
||||||
if (highScore > 0) isHighScoreAchieved = true;
|
if (highScore > 0) isHighScoreAchieved = true;
|
||||||
isRewardScoreAchieved = false;
|
isRewardScoreAchieved = false;
|
||||||
isHighScoreAchievedThisRun = true;
|
isHighScoreAchievedThisRun = false;
|
||||||
highScoreStart = highScore;
|
highScoreStart = highScore;
|
||||||
isGameOver = true;
|
isGameOver = true;
|
||||||
}
|
}
|
||||||
@@ -351,7 +365,9 @@ namespace game::state::play
|
|||||||
|
|
||||||
queuedChallenge = challenge_generate(character);
|
queuedChallenge = challenge_generate(character);
|
||||||
|
|
||||||
auto string = grade.isFailure ? grade.name : std::format("{} (+{})", grade.name, grade.value);
|
auto string = grade.isFailure ? grade.name
|
||||||
|
: std::vformat(strings.get(Strings::SkillCheckGradeSuccessTemplate),
|
||||||
|
std::make_format_args(grade.name, grade.value));
|
||||||
auto toastMessagePosition =
|
auto toastMessagePosition =
|
||||||
ImVec2(barMin.x - ImGui::CalcTextSize(string.c_str()).x - ImGui::GetTextLineHeightWithSpacing(), lineMin.y);
|
ImVec2(barMin.x - ImGui::CalcTextSize(string.c_str()).x - ImGui::GetTextLineHeightWithSpacing(), lineMin.y);
|
||||||
toasts.emplace_back(string, toastMessagePosition, endTimerMax, endTimerMax);
|
toasts.emplace_back(string, toastMessagePosition, endTimerMax, endTimerMax);
|
||||||
@@ -429,5 +445,8 @@ namespace game::state::play
|
|||||||
if (fallingItem.position.y > position.y + size.y) items.erase(items.begin() + i--);
|
if (fallingItem.position.y > position.y + size.y) items.erase(items.begin() + i--);
|
||||||
}
|
}
|
||||||
ImGui::PopClipRect();
|
ImGui::PopClipRect();
|
||||||
|
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(position.x, position.y + size.y + ImGui::GetStyle().ItemSpacing.y));
|
||||||
|
return WIDGET_FX(ImGui::Button(strings.get(Strings::SkillCheckMenuButton).c_str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ namespace game::state::play
|
|||||||
SkillCheck(entity::Character&);
|
SkillCheck(entity::Character&);
|
||||||
Challenge challenge_generate(entity::Character&);
|
Challenge challenge_generate(entity::Character&);
|
||||||
void tick();
|
void tick();
|
||||||
void update(Resources&, entity::Character&, Inventory&, Text&);
|
bool update(Resources&, entity::Character&, Inventory&, Text&);
|
||||||
float accuracy_score_get(entity::Character&);
|
float accuracy_score_get(entity::Character&);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,14 +89,18 @@ namespace game::state::play
|
|||||||
interact_area_override_tick, interactArea.scaleEffectCycles));
|
interact_area_override_tick, interactArea.scaleEffectCycles));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interactArea.pool.is_valid() && text.is_interruptible())
|
if (text.is_interruptible())
|
||||||
text.set(dialogue.get(interactArea.pool), character);
|
{
|
||||||
|
auto& pool = character.is_over_capacity() && interactArea.poolFull.is_valid() ? interactArea.poolFull
|
||||||
|
: interactArea.pool;
|
||||||
|
if (pool.is_valid())
|
||||||
|
text.set(dialogue.get(pool), character);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInteracting)
|
if (isInteracting)
|
||||||
{
|
{
|
||||||
cursor.state = entity::Cursor::ACTION;
|
cursor.state = entity::Cursor::ACTION;
|
||||||
character.queue_interact_area_animation(interactArea);
|
|
||||||
cursor.queue_play({interactArea.animationCursorActive});
|
cursor.queue_play({interactArea.animationCursorActive});
|
||||||
|
|
||||||
if (interactArea.digestionBonusRub > 0 && character.calories > 0 && !character.isDigesting)
|
if (interactArea.digestionBonusRub > 0 && character.calories > 0 && !character.isDigesting)
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
#include "chat.hpp"
|
|
||||||
|
|
||||||
#include "../../util/imgui/widget.hpp"
|
|
||||||
|
|
||||||
using namespace game::resource;
|
|
||||||
using namespace game::util::imgui;
|
|
||||||
|
|
||||||
namespace game::state::play
|
|
||||||
{
|
|
||||||
void Chat::update(Resources&, Text& text, entity::Character& character)
|
|
||||||
{
|
|
||||||
auto& dialogue = character.data.dialogue;
|
|
||||||
auto size = ImGui::GetContentRegionAvail();
|
|
||||||
|
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
|
||||||
|
|
||||||
if (dialogue.random.is_valid())
|
|
||||||
if (WIDGET_FX(ImGui::Button("Let's chat!", ImVec2(size.x, 0))))
|
|
||||||
text.set(dialogue.get(dialogue.random), character);
|
|
||||||
|
|
||||||
ImGui::PopFont();
|
|
||||||
|
|
||||||
if (dialogue.help.is_valid())
|
|
||||||
if (WIDGET_FX(ImGui::Button("Help", ImVec2(size.x, 0)))) text.set(dialogue.get(dialogue.help), character);
|
|
||||||
|
|
||||||
auto stage = glm::clamp(0, character.stage_get(), character.stage_max_get());
|
|
||||||
auto& pool = stage > 0 ? character.data.stages.at(stage - 1).pool : character.data.pool;
|
|
||||||
|
|
||||||
if (pool.is_valid())
|
|
||||||
if (WIDGET_FX(ImGui::Button("How are you feeling?", ImVec2(size.x, 0)))) text.set(dialogue.get(pool), character);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,82 +8,50 @@
|
|||||||
|
|
||||||
using namespace game::util::imgui;
|
using namespace game::util::imgui;
|
||||||
using namespace game::util;
|
using namespace game::util;
|
||||||
|
using namespace game::resource::xml;
|
||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
{
|
{
|
||||||
void Cheats::update(Resources&, entity::Character& character, Inventory& inventory, Text& text)
|
void Cheats::update(Resources&, entity::Character& character, Inventory& inventory)
|
||||||
{
|
{
|
||||||
static constexpr auto FEED_INCREMENT = 100.0f;
|
auto& strings = character.data.strings;
|
||||||
|
|
||||||
if (ImGui::BeginChild("##Cheats"))
|
if (ImGui::BeginChild("##Cheats"))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::Button("Feed")))
|
|
||||||
{
|
|
||||||
character.calories = std::min(character.calories + FEED_INCREMENT, character.max_capacity());
|
|
||||||
character.queue_idle_animation();
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (WIDGET_FX(ImGui::Button("Starve")))
|
|
||||||
{
|
|
||||||
character.calories = std::max(0.0f, character.calories - FEED_INCREMENT);
|
|
||||||
character.queue_idle_animation();
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (WIDGET_FX(ImGui::Button("Digest"))) character.digestionProgress = entity::Character::DIGESTION_MAX;
|
|
||||||
|
|
||||||
auto stage = character.stage + 1;
|
auto stage = character.stage + 1;
|
||||||
if (WIDGET_FX(ImGui::SliderInt("Stage", &stage, 1, (int)character.data.stages.size() + 1)))
|
|
||||||
|
auto weight_update = [&]() { character.queue_idle_animation(); };
|
||||||
|
|
||||||
|
WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsCalories).c_str(), &character.calories, 0,
|
||||||
|
character.max_capacity(), "%0.0f kcal"));
|
||||||
|
WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsCapacity).c_str(), &character.capacity,
|
||||||
|
character.data.capacityMin, character.data.capacityMax, "%0.0f kcal"));
|
||||||
|
|
||||||
|
if (WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsWeight).c_str(), &character.weight,
|
||||||
|
character.data.weight, character.data.weightMax,
|
||||||
|
strings.get(Strings::CheatsWeightFormat).c_str())))
|
||||||
|
weight_update();
|
||||||
|
|
||||||
|
if (WIDGET_FX(ImGui::SliderInt(strings.get(Strings::CheatsStage).c_str(), &stage, 1,
|
||||||
|
(int)character.data.stages.size() + 1)))
|
||||||
{
|
{
|
||||||
character.stage = glm::clamp(0, stage - 1, (int)character.data.stages.size());
|
character.stage = glm::clamp(0, stage - 1, (int)character.data.stages.size());
|
||||||
character.weight =
|
character.weight =
|
||||||
character.stage == 0 ? character.data.weight : character.data.stages.at(character.stage - 1).threshold;
|
character.stage == 0 ? character.data.weight : character.data.stages.at(character.stage - 1).threshold;
|
||||||
character.queue_idle_animation();
|
weight_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
WIDGET_FX(ImGui::SliderFloat("Capacity", &character.capacity, character.data.capacityMin,
|
WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsDigestionRate).c_str(), &character.digestionRate,
|
||||||
character.data.capacityMax, "%0.0f kcal"));
|
character.data.digestionRateMin, character.data.digestionRateMax,
|
||||||
|
strings.get(Strings::CheatsDigestionRateFormat).c_str()));
|
||||||
|
WIDGET_FX(ImGui::SliderFloat(strings.get(Strings::CheatsEatSpeed).c_str(), &character.eatSpeed,
|
||||||
|
character.data.eatSpeedMin, character.data.eatSpeedMax,
|
||||||
|
strings.get(Strings::CheatsEatSpeedFormat).c_str()));
|
||||||
|
|
||||||
WIDGET_FX(ImGui::SliderFloat("Digestion Rate", &character.digestionRate, character.data.digestionRateMin,
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::CheatsDigestButton).c_str())))
|
||||||
character.data.digestionRateMax, "%0.2f% / tick"));
|
character.digestionProgress = entity::Character::DIGESTION_MAX;
|
||||||
WIDGET_FX(ImGui::SliderFloat("Eat Speed", &character.eatSpeed, character.data.eatSpeedMin,
|
|
||||||
character.data.eatSpeedMax, "%0.2fx"));
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Animations");
|
ImGui::SeparatorText(strings.get(Strings::CheatsInventory).c_str());
|
||||||
ImGui::Text("Now Playing: %s", character.animationMapReverse.at(character.animationIndex).c_str());
|
|
||||||
|
|
||||||
auto childSize = ImVec2(0, ImGui::GetContentRegionAvail().y / 3);
|
|
||||||
|
|
||||||
if (ImGui::BeginChild("##Animations", childSize, ImGuiChildFlags_Borders))
|
|
||||||
{
|
|
||||||
for (int i = 0; i < (int)character.animations.size(); i++)
|
|
||||||
{
|
|
||||||
auto& animation = character.animations[i];
|
|
||||||
ImGui::PushID(i);
|
|
||||||
if (WIDGET_FX(ImGui::Selectable(animation.name.c_str())))
|
|
||||||
character.play(animation.name.c_str(), entity::Actor::PLAY_FORCE);
|
|
||||||
ImGui::SetItemTooltip("%s", animation.name.c_str());
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndChild();
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Dialogue");
|
|
||||||
|
|
||||||
if (ImGui::BeginChild("##Dialogue", childSize, ImGuiChildFlags_Borders))
|
|
||||||
{
|
|
||||||
for (int i = 0; i < (int)character.data.dialogue.entries.size(); i++)
|
|
||||||
{
|
|
||||||
auto& entry = character.data.dialogue.entries[i];
|
|
||||||
ImGui::PushID(i);
|
|
||||||
if (WIDGET_FX(ImGui::Selectable(entry.name.c_str()))) text.set(&entry, character);
|
|
||||||
ImGui::SetItemTooltip("%s", entry.name.c_str());
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::EndChild();
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Inventory");
|
|
||||||
|
|
||||||
if (ImGui::BeginChild("##Inventory", ImGui::GetContentRegionAvail(), ImGuiChildFlags_Borders))
|
if (ImGui::BeginChild("##Inventory", ImGui::GetContentRegionAvail(), ImGuiChildFlags_Borders))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ namespace game::state::play
|
|||||||
class Cheats
|
class Cheats
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
void update(Resources&, entity::Character&, Inventory&);
|
||||||
void update(Resources&, entity::Character&, Inventory&, Text&);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,31 +5,68 @@
|
|||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
using namespace game::util::imgui;
|
using namespace game::util::imgui;
|
||||||
|
using namespace game::resource::xml;
|
||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
{
|
{
|
||||||
void Debug::update(entity::Character& character, entity::Cursor& cursor, ItemManager& itemManager, Canvas& canvas)
|
void Debug::update(entity::Character& character, entity::Cursor& cursor, ItemManager& itemManager, Canvas& canvas,
|
||||||
|
Text& text)
|
||||||
{
|
{
|
||||||
|
auto& strings = character.data.strings;
|
||||||
auto cursorPosition = canvas.screen_position_convert(cursor.position);
|
auto cursorPosition = canvas.screen_position_convert(cursor.position);
|
||||||
|
|
||||||
ImGui::Text("Cursor Pos (Screen): %0.0f, %0.0f", cursor.position.x, cursor.position.y);
|
ImGui::Text(strings.get(Strings::DebugCursorScreenFormat).c_str(), cursor.position.x, cursor.position.y);
|
||||||
ImGui::Text("Cursor Pos (World): %0.0f, %0.0f", cursorPosition.x, cursorPosition.y);
|
ImGui::Text(strings.get(Strings::DebugCursorWorldFormat).c_str(), cursorPosition.x, cursorPosition.y);
|
||||||
|
|
||||||
WIDGET_FX(ImGui::Checkbox("Show Nulls (Hitboxes)", &character.isShowNulls));
|
ImGui::SeparatorText(strings.get(Strings::DebugAnimations).c_str());
|
||||||
WIDGET_FX(ImGui::Checkbox("Show World Bounds", &isBoundsDisplay));
|
ImGui::Text(strings.get(Strings::DebugNowPlayingFormat).c_str(), character.animationMapReverse.at(character.animationIndex).c_str());
|
||||||
|
|
||||||
|
auto childSize = ImVec2(0, ImGui::GetContentRegionAvail().y / 3);
|
||||||
|
|
||||||
|
if (ImGui::BeginChild("##Animations", childSize, ImGuiChildFlags_Borders))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (int)character.animations.size(); i++)
|
||||||
|
{
|
||||||
|
auto& animation = character.animations[i];
|
||||||
|
ImGui::PushID(i);
|
||||||
|
if (WIDGET_FX(ImGui::Selectable(animation.name.c_str())))
|
||||||
|
character.play(animation.name.c_str(), entity::Actor::PLAY_FORCE);
|
||||||
|
ImGui::SetItemTooltip("%s", animation.name.c_str());
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
ImGui::SeparatorText(strings.get(Strings::DebugDialogue).c_str());
|
||||||
|
|
||||||
|
if (ImGui::BeginChild("##Dialogue", childSize, ImGuiChildFlags_Borders))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (int)character.data.dialogue.entries.size(); i++)
|
||||||
|
{
|
||||||
|
auto& entry = character.data.dialogue.entries[i];
|
||||||
|
ImGui::PushID(i);
|
||||||
|
if (WIDGET_FX(ImGui::Selectable(entry.name.c_str()))) text.set(&entry, character);
|
||||||
|
ImGui::SetItemTooltip("%s", entry.name.c_str());
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
WIDGET_FX(ImGui::Checkbox(strings.get(Strings::DebugShowNulls).c_str(), &character.isShowNulls));
|
||||||
|
WIDGET_FX(ImGui::Checkbox(strings.get(Strings::DebugShowWorldBounds).c_str(), &isBoundsDisplay));
|
||||||
|
|
||||||
if (!itemManager.items.empty())
|
if (!itemManager.items.empty())
|
||||||
{
|
{
|
||||||
ImGui::SeparatorText("Item");
|
ImGui::SeparatorText(strings.get(Strings::DebugItem).c_str());
|
||||||
|
|
||||||
for (int i = 0; i < (int)itemManager.items.size(); i++)
|
for (int i = 0; i < (int)itemManager.items.size(); i++)
|
||||||
{
|
{
|
||||||
auto& item = itemManager.items[i];
|
auto& item = itemManager.items[i];
|
||||||
if (itemManager.heldItemIndex == i) ImGui::Text("Held");
|
if (itemManager.heldItemIndex == i) ImGui::TextUnformatted(strings.get(Strings::DebugHeld).c_str());
|
||||||
ImGui::Text("Type: %i", item.schemaID);
|
ImGui::Text(strings.get(Strings::DebugItemTypeFormat).c_str(), item.schemaID);
|
||||||
ImGui::Text("Position: %0.0f, %0.0f", item.position.x, item.position.y);
|
ImGui::Text(strings.get(Strings::DebugItemPositionFormat).c_str(), item.position.x, item.position.y);
|
||||||
ImGui::Text("Velocity: %0.0f, %0.0f", item.velocity.x, item.velocity.y);
|
ImGui::Text(strings.get(Strings::DebugItemVelocityFormat).c_str(), item.velocity.x, item.velocity.y);
|
||||||
ImGui::Text("Chew Count: %i", item.chewCount);
|
ImGui::Text(strings.get(Strings::DebugItemDurabilityFormat).c_str(), item.durability);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "../../entity/cursor.hpp"
|
#include "../../entity/cursor.hpp"
|
||||||
|
|
||||||
#include "item_manager.hpp"
|
#include "item_manager.hpp"
|
||||||
|
#include "text.hpp"
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
@@ -14,6 +15,6 @@ namespace game::state::play
|
|||||||
public:
|
public:
|
||||||
bool isBoundsDisplay{};
|
bool isBoundsDisplay{};
|
||||||
|
|
||||||
void update(entity::Character&, entity::Cursor& cursor, ItemManager&, Canvas& canvas);
|
void update(entity::Character&, entity::Cursor&, ItemManager&, Canvas&, Text&);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include <format>
|
#include <format>
|
||||||
|
|
||||||
using namespace game::resource;
|
using namespace game::resource;
|
||||||
|
using namespace game::resource::xml;
|
||||||
using namespace game::util;
|
using namespace game::util;
|
||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
@@ -18,6 +19,7 @@ namespace game::state::play
|
|||||||
static constexpr auto WIDTH_MULTIPLIER = 0.30f;
|
static constexpr auto WIDTH_MULTIPLIER = 0.30f;
|
||||||
static constexpr auto HEIGHT_MULTIPLIER = 4.0f;
|
static constexpr auto HEIGHT_MULTIPLIER = 4.0f;
|
||||||
|
|
||||||
|
auto& strings = character.data.strings;
|
||||||
auto& style = ImGui::GetStyle();
|
auto& style = ImGui::GetStyle();
|
||||||
auto windowSize = imgui::to_ivec2(ImGui::GetMainViewport()->Size);
|
auto windowSize = imgui::to_ivec2(ImGui::GetMainViewport()->Size);
|
||||||
|
|
||||||
@@ -45,26 +47,29 @@ namespace game::state::play
|
|||||||
auto unitString = (system == measurement::IMPERIAL ? "lbs" : "kg");
|
auto unitString = (system == measurement::IMPERIAL ? "lbs" : "kg");
|
||||||
|
|
||||||
auto weightString = util::string::format_commas(weight, 2) + " " + unitString;
|
auto weightString = util::string::format_commas(weight, 2) + " " + unitString;
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::ABOVE_AVERAGE);
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_1);
|
||||||
ImGui::TextUnformatted(weightString.c_str());
|
ImGui::TextUnformatted(weightString.c_str());
|
||||||
ImGui::SetItemTooltip("%s", weightString.c_str());
|
ImGui::SetItemTooltip("%s", weightString.c_str());
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
auto stageProgress = character.stage_progress_get();
|
auto stageProgress = character.stage_progress_get();
|
||||||
ImGui::ProgressBar(stageProgress, ImVec2(ImGui::GetContentRegionAvail().x, 0),
|
ImGui::ProgressBar(stageProgress, ImVec2(ImGui::GetContentRegionAvail().x, 0),
|
||||||
stage >= stageMax ? "MAX" : "To Next Stage");
|
strings.get(stage >= stageMax ? Strings::InfoProgressMax
|
||||||
|
: Strings::InfoProgressToNextStage)
|
||||||
|
.c_str());
|
||||||
if (ImGui::BeginItemTooltip())
|
if (ImGui::BeginItemTooltip())
|
||||||
{
|
{
|
||||||
ImGui::Text("Stage: %i/%i (%0.1f%%)", stage + 1, stageMax + 1, math::to_percent(stageProgress));
|
ImGui::Text(strings.get(Strings::InfoStageProgressFormat).c_str(), stage + 1, stageMax + 1,
|
||||||
|
math::to_percent(stageProgress));
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, imgui::to_imvec4(color::GRAY));
|
ImGui::PushStyleColor(ImGuiCol_Text, imgui::to_imvec4(color::GRAY));
|
||||||
if (stage >= stageMax)
|
if (stage >= stageMax)
|
||||||
ImGui::Text("Maxed out!");
|
ImGui::TextUnformatted(strings.get(Strings::InfoMaxedOut).c_str());
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui::Text("Start: %0.2f %s", stageWeight, unitString);
|
ImGui::Text(strings.get(Strings::InfoStageStartFormat).c_str(), stageWeight, unitString);
|
||||||
ImGui::Text("Current: %0.2f %s", weight, unitString);
|
ImGui::Text(strings.get(Strings::InfoStageCurrentFormat).c_str(), weight, unitString);
|
||||||
ImGui::Text("Next: %0.2f %s", stageNextWeight, unitString);
|
ImGui::Text(strings.get(Strings::InfoStageNextFormat).c_str(), stageNextWeight, unitString);
|
||||||
}
|
}
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
ImGui::EndTooltip();
|
ImGui::EndTooltip();
|
||||||
@@ -82,7 +87,7 @@ namespace game::state::play
|
|||||||
auto overstuffedPercent = std::max(0.0f, (calories - capacity) / (character.max_capacity() - capacity));
|
auto overstuffedPercent = std::max(0.0f, (calories - capacity) / (character.max_capacity() - capacity));
|
||||||
auto caloriesColor = ImVec4(1.0f, 1.0f - overstuffedPercent, 1.0f - overstuffedPercent, 1.0f);
|
auto caloriesColor = ImVec4(1.0f, 1.0f - overstuffedPercent, 1.0f - overstuffedPercent, 1.0f);
|
||||||
|
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::ABOVE_AVERAGE);
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_1);
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, caloriesColor);
|
ImGui::PushStyleColor(ImGuiCol_Text, caloriesColor);
|
||||||
auto caloriesString = std::format("{:.0f} kcal / {:.0f} kcal", calories,
|
auto caloriesString = std::format("{:.0f} kcal / {:.0f} kcal", calories,
|
||||||
character.is_over_capacity() ? character.max_capacity() : character.capacity);
|
character.is_over_capacity() ? character.max_capacity() : character.capacity);
|
||||||
@@ -95,14 +100,16 @@ namespace game::state::play
|
|||||||
? (float)character.digestionTimer / character.data.digestionTimerMax
|
? (float)character.digestionTimer / character.data.digestionTimerMax
|
||||||
: character.digestionProgress / entity::Character::DIGESTION_MAX;
|
: character.digestionProgress / entity::Character::DIGESTION_MAX;
|
||||||
ImGui::ProgressBar(digestionProgress, ImVec2(ImGui::GetContentRegionAvail().x, 0),
|
ImGui::ProgressBar(digestionProgress, ImVec2(ImGui::GetContentRegionAvail().x, 0),
|
||||||
character.isDigesting ? "Digesting..." : "Digestion");
|
strings.get(character.isDigesting ? Strings::InfoDigesting
|
||||||
|
: Strings::InfoDigestion)
|
||||||
|
.c_str());
|
||||||
|
|
||||||
if (ImGui::BeginItemTooltip())
|
if (ImGui::BeginItemTooltip())
|
||||||
{
|
{
|
||||||
if (character.isDigesting)
|
if (character.isDigesting)
|
||||||
ImGui::TextUnformatted("Digestion in progress...");
|
ImGui::TextUnformatted(strings.get(Strings::InfoDigestionInProgress).c_str());
|
||||||
else if (digestionProgress <= 0.0f)
|
else if (digestionProgress <= 0.0f)
|
||||||
ImGui::TextUnformatted("Give food to start digesting!");
|
ImGui::TextUnformatted(strings.get(Strings::InfoGiveFoodToStartDigesting).c_str());
|
||||||
else
|
else
|
||||||
ImGui::Text("%0.2f%%", math::to_percent(digestionProgress));
|
ImGui::Text("%0.2f%%", math::to_percent(digestionProgress));
|
||||||
|
|
||||||
@@ -110,8 +117,8 @@ namespace game::state::play
|
|||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||||
|
|
||||||
ImGui::Text("Rate: %0.2f%% / sec", character.digestion_rate_get());
|
ImGui::Text(strings.get(Strings::InfoDigestionRateFormat).c_str(), character.digestion_rate_get());
|
||||||
ImGui::Text("Eating Speed: %0.2fx", character.eatSpeed);
|
ImGui::Text(strings.get(Strings::InfoEatingSpeedFormat).c_str(), character.eatSpeed);
|
||||||
|
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
|||||||
57
src/state/play/interact.cpp
Normal file
57
src/state/play/interact.cpp
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#include "interact.hpp"
|
||||||
|
|
||||||
|
#include "../../util/imgui/widget.hpp"
|
||||||
|
#include "../../util/measurement.hpp"
|
||||||
|
|
||||||
|
using namespace game::resource;
|
||||||
|
using namespace game::resource::xml;
|
||||||
|
using namespace game::util;
|
||||||
|
using namespace game::util::imgui;
|
||||||
|
|
||||||
|
namespace game::state::play
|
||||||
|
{
|
||||||
|
void Interact::update(Resources& resources, Text& text, entity::Character& character)
|
||||||
|
{
|
||||||
|
auto& dialogue = character.data.dialogue;
|
||||||
|
auto& strings = character.data.strings;
|
||||||
|
auto size = ImGui::GetContentRegionAvail();
|
||||||
|
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_2);
|
||||||
|
|
||||||
|
if (dialogue.random.is_valid())
|
||||||
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::InteractChatButton).c_str(), ImVec2(size.x, 0))))
|
||||||
|
text.set(dialogue.get(dialogue.random), character);
|
||||||
|
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
if (dialogue.help.is_valid())
|
||||||
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::InteractHelpButton).c_str(), ImVec2(size.x, 0))))
|
||||||
|
text.set(dialogue.get(dialogue.help), character);
|
||||||
|
|
||||||
|
auto stage = glm::clamp(0, character.stage_get(), character.stage_max_get());
|
||||||
|
auto& pool = stage > 0 ? character.data.stages.at(stage - 1).pool : character.data.pool;
|
||||||
|
|
||||||
|
if (pool.is_valid())
|
||||||
|
if (WIDGET_FX(
|
||||||
|
ImGui::Button(strings.get(Strings::InteractFeelingButton).c_str(), ImVec2(size.x, 0))))
|
||||||
|
text.set(dialogue.get(pool), character);
|
||||||
|
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), resource::Font::HEADER_1);
|
||||||
|
ImGui::SeparatorText(character.data.name.c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
auto& system = resources.settings.measurementSystem;
|
||||||
|
auto weight = character.weight_get(system);
|
||||||
|
auto weightUnit = system == measurement::IMPERIAL ? "lbs" : "kg";
|
||||||
|
|
||||||
|
ImGui::Text(strings.get(Strings::InteractWeightFormat).c_str(), weight, weightUnit,
|
||||||
|
character.stage_get() + 1);
|
||||||
|
ImGui::Text(strings.get(Strings::InteractCapacityFormat).c_str(), character.capacity,
|
||||||
|
character.max_capacity());
|
||||||
|
ImGui::Text(strings.get(Strings::InteractDigestionRateFormat).c_str(), character.digestion_rate_get());
|
||||||
|
ImGui::Text(strings.get(Strings::InteractEatingSpeedFormat).c_str(), character.eatSpeed);
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text(strings.get(Strings::InteractTotalCaloriesFormat).c_str(), character.totalCaloriesConsumed);
|
||||||
|
ImGui::Text(strings.get(Strings::InteractTotalFoodItemsFormat).c_str(), character.totalFoodItemsEaten);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
{
|
{
|
||||||
class Chat
|
class Interact
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void update(Resources&, Text&, entity::Character&);
|
void update(Resources&, Text&, entity::Character&);
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "inventory.hpp"
|
#include "inventory.hpp"
|
||||||
|
#include "style.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <format>
|
#include <format>
|
||||||
@@ -7,6 +8,7 @@
|
|||||||
|
|
||||||
#include "../../util/color.hpp"
|
#include "../../util/color.hpp"
|
||||||
#include "../../util/imgui.hpp"
|
#include "../../util/imgui.hpp"
|
||||||
|
#include "../../util/imgui/style.hpp"
|
||||||
#include "../../util/imgui/widget.hpp"
|
#include "../../util/imgui/widget.hpp"
|
||||||
#include "../../util/math.hpp"
|
#include "../../util/math.hpp"
|
||||||
|
|
||||||
@@ -18,6 +20,8 @@ using namespace glm;
|
|||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
{
|
{
|
||||||
|
using Strings = resource::xml::Strings;
|
||||||
|
|
||||||
void Inventory::tick()
|
void Inventory::tick()
|
||||||
{
|
{
|
||||||
for (auto& [i, actor] : actors)
|
for (auto& [i, actor] : actors)
|
||||||
@@ -29,6 +33,7 @@ namespace game::state::play
|
|||||||
static constexpr auto INFO_CHILD_HEIGHT_MULTIPLIER = 1.0f / 3.0f;
|
static constexpr auto INFO_CHILD_HEIGHT_MULTIPLIER = 1.0f / 3.0f;
|
||||||
|
|
||||||
auto& schema = character.data.itemSchema;
|
auto& schema = character.data.itemSchema;
|
||||||
|
auto& strings = character.data.strings;
|
||||||
|
|
||||||
auto quantity_get = [&](int itemID) -> int&
|
auto quantity_get = [&](int itemID) -> int&
|
||||||
{
|
{
|
||||||
@@ -46,6 +51,77 @@ namespace game::state::play
|
|||||||
auto is_able_to_upgrade_get = [&](const resource::xml::Item::Entry& item, int quantity)
|
auto is_able_to_upgrade_get = [&](const resource::xml::Item::Entry& item, int quantity)
|
||||||
{ return is_possible_to_upgrade_get(item) && quantity >= *item.upgradeCount; };
|
{ return is_possible_to_upgrade_get(item) && quantity >= *item.upgradeCount; };
|
||||||
|
|
||||||
|
auto item_summary_draw = [&](const resource::xml::Item::Entry& item, int quantity)
|
||||||
|
{
|
||||||
|
auto& category = schema.categories[item.categoryID];
|
||||||
|
auto& rarity = schema.rarities[item.rarityID];
|
||||||
|
auto durability = item.durability.value_or(schema.durability);
|
||||||
|
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||||
|
ImGui::TextWrapped("%s (x%i)", item.name.c_str(), quantity);
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||||
|
ImGui::TextWrapped("-- %s (%s) --", category.name.c_str(), rarity.name.c_str());
|
||||||
|
if (item.flavorID.has_value())
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryFlavorFormat).c_str(),
|
||||||
|
schema.flavors[*item.flavorID].name.c_str());
|
||||||
|
if (item.calories.has_value())
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryCaloriesFormat).c_str(), *item.calories);
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryDurabilityFormat).c_str(), durability);
|
||||||
|
if (item.capacityBonus.has_value())
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryCapacityBonusFormat).c_str(), *item.capacityBonus);
|
||||||
|
if (item.digestionBonus.has_value())
|
||||||
|
{
|
||||||
|
if (*item.digestionBonus > 0)
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryDigestionRateBonusFormat).c_str(),
|
||||||
|
*item.digestionBonus * 60.0f);
|
||||||
|
else if (*item.digestionBonus < 0)
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryDigestionRatePenaltyFormat).c_str(),
|
||||||
|
*item.digestionBonus * 60.0f);
|
||||||
|
}
|
||||||
|
if (item.eatSpeedBonus.has_value())
|
||||||
|
{
|
||||||
|
if (*item.eatSpeedBonus > 0)
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryEatSpeedBonusFormat).c_str(), *item.eatSpeedBonus);
|
||||||
|
else if (*item.eatSpeedBonus < 0)
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryEatSpeedPenaltyFormat).c_str(), *item.eatSpeedBonus);
|
||||||
|
}
|
||||||
|
if (is_possible_to_upgrade_get(item))
|
||||||
|
ImGui::TextWrapped(strings.get(Strings::InventoryUpgradePreviewFormat).c_str(), *item.upgradeCount,
|
||||||
|
schema.idToStringMap.at(*item.upgradeID).c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto item_details_draw = [&](const resource::xml::Item::Entry& item, int quantity)
|
||||||
|
{
|
||||||
|
item_summary_draw(item, quantity);
|
||||||
|
|
||||||
|
if (ImGui::BeginChild("##Info Description Child", ImGui::GetContentRegionAvail()))
|
||||||
|
ImGui::TextWrapped("%s", item.description.c_str());
|
||||||
|
ImGui::EndChild();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto item_tooltip_draw = [&](const resource::xml::Item::Entry& item, int quantity)
|
||||||
|
{
|
||||||
|
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 24.0f);
|
||||||
|
item_summary_draw(item, quantity);
|
||||||
|
ImGui::TextWrapped("%s", item.description.c_str());
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto item_unknown_draw = [&]()
|
||||||
|
{
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||||
|
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 24.0f);
|
||||||
|
ImGui::TextWrapped("%s", strings.get(Strings::InventoryUnknown).c_str());
|
||||||
|
ImGui::PopTextWrapPos();
|
||||||
|
ImGui::PopFont();
|
||||||
|
};
|
||||||
|
|
||||||
auto item_use = [&](int itemID)
|
auto item_use = [&](int itemID)
|
||||||
{
|
{
|
||||||
auto& item = schema.items[itemID];
|
auto& item = schema.items[itemID];
|
||||||
@@ -176,6 +252,7 @@ namespace game::state::play
|
|||||||
auto& item = schema.items[i];
|
auto& item = schema.items[i];
|
||||||
auto& quantity = quantity_get(i);
|
auto& quantity = quantity_get(i);
|
||||||
auto& rarity = schema.rarities[item.rarityID];
|
auto& rarity = schema.rarities[item.rarityID];
|
||||||
|
auto hasItemColor = item.color.has_value();
|
||||||
|
|
||||||
if (rarity.isHidden && quantity <= 0) continue;
|
if (rarity.isHidden && quantity <= 0) continue;
|
||||||
|
|
||||||
@@ -185,6 +262,7 @@ namespace game::state::play
|
|||||||
auto cursorScreenPos = ImGui::GetCursorScreenPos();
|
auto cursorScreenPos = ImGui::GetCursorScreenPos();
|
||||||
auto [canvas, rect] = item_canvas_get(i, size);
|
auto [canvas, rect] = item_canvas_get(i, size);
|
||||||
auto isSelected = selectedItemID == i;
|
auto isSelected = selectedItemID == i;
|
||||||
|
if (hasItemColor) imgui::style::color_set(*item.color);
|
||||||
|
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
{
|
{
|
||||||
@@ -201,8 +279,16 @@ namespace game::state::play
|
|||||||
isAnyInventoryItemHovered = isAnyInventoryItemHovered || ImGui::IsItemHovered();
|
isAnyInventoryItemHovered = isAnyInventoryItemHovered || ImGui::IsItemHovered();
|
||||||
if (isPressed) selectedItemID = i;
|
if (isPressed) selectedItemID = i;
|
||||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && quantity > 0) item_use(i);
|
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && quantity > 0) item_use(i);
|
||||||
|
if (ImGui::BeginItemTooltip())
|
||||||
|
{
|
||||||
|
if (quantity > 0)
|
||||||
|
item_tooltip_draw(item, quantity);
|
||||||
|
else
|
||||||
|
item_unknown_draw();
|
||||||
|
ImGui::EndTooltip();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||||
|
|
||||||
auto text = std::format("x{}", quantity);
|
auto text = std::format("x{}", quantity);
|
||||||
auto textPos = ImVec2(cursorScreenPos.x + size.x - ImGui::CalcTextSize(text.c_str()).x,
|
auto textPos = ImVec2(cursorScreenPos.x + size.x - ImGui::CalcTextSize(text.c_str()).x,
|
||||||
@@ -210,6 +296,7 @@ namespace game::state::play
|
|||||||
ImGui::GetWindowDrawList()->AddText(textPos, ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Text)),
|
ImGui::GetWindowDrawList()->AddText(textPos, ImGui::GetColorU32(ImGui::GetStyleColorVec4(ImGuiCol_Text)),
|
||||||
text.c_str());
|
text.c_str());
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
if (hasItemColor) style::color_set(resources, character);
|
||||||
|
|
||||||
auto increment = ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x;
|
auto increment = ImGui::GetItemRectSize().x + ImGui::GetStyle().ItemSpacing.x;
|
||||||
cursorPos.x += increment;
|
cursorPos.x += increment;
|
||||||
@@ -231,13 +318,15 @@ namespace game::state::play
|
|||||||
isItemSelected = selectedItemID >= 0 && selectedItemID < (int)schema.items.size();
|
isItemSelected = selectedItemID >= 0 && selectedItemID < (int)schema.items.size();
|
||||||
auto selectedQuantity = isItemSelected ? quantity_get(selectedItemID) : 0;
|
auto selectedQuantity = isItemSelected ? quantity_get(selectedItemID) : 0;
|
||||||
auto isSelectedItemKnown = isItemSelected && selectedQuantity > 0;
|
auto isSelectedItemKnown = isItemSelected && selectedQuantity > 0;
|
||||||
|
auto selectedItemHasColor = isItemSelected && schema.items[selectedItemID].color.has_value();
|
||||||
|
|
||||||
if (isInfoVisible &&
|
if (isInfoVisible &&
|
||||||
ImGui::BeginChild("##Info Child", infoChildSize, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar))
|
ImGui::BeginChild("##Info Child", infoChildSize, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar))
|
||||||
{
|
{
|
||||||
|
if (selectedItemHasColor) imgui::style::color_set(*schema.items[selectedItemID].color);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
auto isButtonChildVisible = selectedQuantity > 0;
|
auto isButtonChildVisible = selectedQuantity > 0;
|
||||||
ImGui::PushFont(resources.font.get(), Font::BIG);
|
ImGui::PushFont(resources.font.get(), Font::HEADER_2);
|
||||||
auto buttonRowHeight = ImGui::GetFrameHeight();
|
auto buttonRowHeight = ImGui::GetFrameHeight();
|
||||||
auto buttonChildHeight =
|
auto buttonChildHeight =
|
||||||
isButtonChildVisible ? buttonRowHeight * 2.0f + ImGui::GetStyle().ItemSpacing.y * 5.0f : 0.0f;
|
isButtonChildVisible ? buttonRowHeight * 2.0f + ImGui::GetStyle().ItemSpacing.y * 5.0f : 0.0f;
|
||||||
@@ -252,56 +341,18 @@ namespace game::state::play
|
|||||||
{
|
{
|
||||||
if (!isItemSelected)
|
if (!isItemSelected)
|
||||||
{
|
{
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||||
ImGui::TextWrapped("%s", "Check the \"Arcade\" tab to earn rewards!");
|
ImGui::TextWrapped("%s", strings.get(Strings::InventoryEmptyHint).c_str());
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto& item = schema.items[selectedItemID];
|
auto& item = schema.items[selectedItemID];
|
||||||
auto& category = schema.categories[item.categoryID];
|
|
||||||
auto& rarity = schema.rarities[item.rarityID];
|
|
||||||
|
|
||||||
if (isSelectedItemKnown)
|
if (isSelectedItemKnown)
|
||||||
{
|
item_details_draw(item, selectedQuantity);
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
|
||||||
ImGui::TextWrapped("%s (x%i)", item.name.c_str(), selectedQuantity);
|
|
||||||
ImGui::PopFont();
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
|
||||||
ImGui::TextWrapped("-- %s (%s) --", category.name.c_str(), rarity.name.c_str());
|
|
||||||
if (item.flavorID.has_value())
|
|
||||||
ImGui::TextWrapped("Flavor: %s", schema.flavors[*item.flavorID].name.c_str());
|
|
||||||
if (item.calories.has_value()) ImGui::TextWrapped("%0.0f kcal", *item.calories);
|
|
||||||
if (item.digestionBonus.has_value())
|
|
||||||
{
|
|
||||||
if (*item.digestionBonus > 0)
|
|
||||||
ImGui::TextWrapped("Digestion Rate Bonus: +%0.2f%% / sec", *item.digestionBonus * 60.0f);
|
|
||||||
else if (*item.digestionBonus < 0)
|
|
||||||
ImGui::TextWrapped("Digestion Rate Penalty: %0.2f%% / sec", *item.digestionBonus * 60.0f);
|
|
||||||
}
|
|
||||||
if (item.eatSpeedBonus.has_value())
|
|
||||||
{
|
|
||||||
if (*item.eatSpeedBonus > 0)
|
|
||||||
ImGui::TextWrapped("Eat Speed Bonus: +%0.2f%% / sec", *item.eatSpeedBonus);
|
|
||||||
else if (*item.eatSpeedBonus < 0)
|
|
||||||
ImGui::TextWrapped("Eat Speed Penalty: %0.2f%% / sec", *item.eatSpeedBonus);
|
|
||||||
}
|
|
||||||
if (is_possible_to_upgrade_get(item))
|
|
||||||
ImGui::TextWrapped("Upgrade: %ix -> %s", *item.upgradeCount,
|
|
||||||
schema.idToStringMap.at(*item.upgradeID).c_str());
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::TextWrapped("%s", item.description.c_str());
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
item_unknown_draw();
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
|
||||||
ImGui::TextWrapped("%s", "???");
|
|
||||||
ImGui::PopFont();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
@@ -310,28 +361,75 @@ namespace game::state::play
|
|||||||
ImGui::BeginChild("##Info Actions Child", buttonChildSize, ImGuiChildFlags_None,
|
ImGui::BeginChild("##Info Actions Child", buttonChildSize, ImGuiChildFlags_None,
|
||||||
ImGuiWindowFlags_NoScrollbar))
|
ImGuiWindowFlags_NoScrollbar))
|
||||||
{
|
{
|
||||||
|
auto& selectedItem = schema.items[selectedItemID];
|
||||||
auto canUseSelectedItem = true;
|
auto canUseSelectedItem = true;
|
||||||
auto canUpgradeSelectedItem = is_able_to_upgrade_get(schema.items[selectedItemID], selectedQuantity);
|
auto canUpgradeSelectedItem = is_able_to_upgrade_get(selectedItem, selectedQuantity);
|
||||||
auto rowTwoButtonSize = row_widget_size_get(2);
|
auto rowTwoButtonSize = row_widget_size_get(2);
|
||||||
|
|
||||||
|
auto upgrade_item_name_get = [&]() -> std::string
|
||||||
|
{
|
||||||
|
if (!selectedItem.upgradeID.has_value()) return {};
|
||||||
|
return schema.items.at(*selectedItem.upgradeID).name;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto upgrade_tooltip_get = [&](bool isAll)
|
||||||
|
{
|
||||||
|
if (!is_possible_to_upgrade_get(selectedItem))
|
||||||
|
return strings.get(Strings::InventoryUpgradeNoPath);
|
||||||
|
|
||||||
|
auto upgradeItemName = upgrade_item_name_get();
|
||||||
|
auto upgradeCount = *selectedItem.upgradeCount;
|
||||||
|
|
||||||
|
if (!canUpgradeSelectedItem)
|
||||||
|
return std::vformat(strings.get(Strings::InventoryUpgradeNeedsTemplate),
|
||||||
|
std::make_format_args(upgradeCount, upgradeItemName));
|
||||||
|
|
||||||
|
if (!isAll)
|
||||||
|
return std::vformat(strings.get(Strings::InventoryUpgradeOneTemplate),
|
||||||
|
std::make_format_args(upgradeCount, upgradeItemName));
|
||||||
|
|
||||||
|
auto upgradedCount = selectedQuantity / upgradeCount;
|
||||||
|
return std::vformat(strings.get(Strings::InventoryUpgradeAllTemplate),
|
||||||
|
std::make_format_args(upgradeCount, upgradedCount, upgradeItemName));
|
||||||
|
};
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Dummy(ImVec2(0, ImGui::GetStyle().ItemSpacing.y));
|
ImGui::Dummy(ImVec2(0, ImGui::GetStyle().ItemSpacing.y));
|
||||||
|
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||||
|
|
||||||
ImGui::BeginDisabled(!canUseSelectedItem);
|
ImGui::BeginDisabled(!canUseSelectedItem);
|
||||||
if (WIDGET_FX(ImGui::Button("Spawn", {ImGui::GetContentRegionAvail().x, 0}))) item_use(selectedItemID);
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::InventorySpawnButton).c_str(),
|
||||||
|
{ImGui::GetContentRegionAvail().x, 0})))
|
||||||
|
item_use(selectedItemID);
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::BeginDisabled(!canUpgradeSelectedItem);
|
ImGui::BeginDisabled(!canUpgradeSelectedItem);
|
||||||
if (WIDGET_FX(ImGui::Button("Upgrade", rowTwoButtonSize))) item_upgrade(selectedItemID, false);
|
if (WIDGET_FX(
|
||||||
|
ImGui::Button(strings.get(Strings::InventoryUpgradeButton).c_str(), rowTwoButtonSize)))
|
||||||
|
item_upgrade(selectedItemID, false);
|
||||||
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||||
|
{
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), Font::NORMAL);
|
||||||
|
ImGui::SetItemTooltip("%s", upgrade_tooltip_get(false).c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (WIDGET_FX(ImGui::Button("Upgrade All", rowTwoButtonSize))) item_upgrade(selectedItemID, true);
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::InventoryUpgradeAllButton).c_str(),
|
||||||
|
rowTwoButtonSize)))
|
||||||
|
item_upgrade(selectedItemID, true);
|
||||||
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
|
||||||
|
{
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), Font::NORMAL);
|
||||||
|
ImGui::SetItemTooltip("%s", upgrade_tooltip_get(true).c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
}
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
}
|
}
|
||||||
if (isButtonChildVisible) ImGui::EndChild();
|
if (isButtonChildVisible) ImGui::EndChild();
|
||||||
|
if (selectedItemHasColor) style::color_set(resources, character);
|
||||||
}
|
}
|
||||||
if (isInfoVisible) ImGui::EndChild();
|
if (isInfoVisible) ImGui::EndChild();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,18 @@ using namespace glm;
|
|||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int durability_animation_index_get(const resource::xml::Item& schema, const resource::xml::Anm2& anm2, int durability,
|
||||||
|
int durabilityMax)
|
||||||
|
{
|
||||||
|
if (durability >= durabilityMax) return -1;
|
||||||
|
|
||||||
|
auto animationName = schema.animations.chew + std::to_string(std::max(0, durability));
|
||||||
|
return anm2.animationMap.contains(animationName) ? anm2.animationMap.at(animationName) : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ItemManager::update(entity::Character& character, entity::Cursor& cursor, AreaManager& areaManager, Text& text,
|
void ItemManager::update(entity::Character& character, entity::Cursor& cursor, AreaManager& areaManager, Text& text,
|
||||||
const glm::vec4& bounds, Canvas& canvas)
|
const glm::vec4& bounds, Canvas& canvas)
|
||||||
{
|
{
|
||||||
@@ -70,7 +82,11 @@ namespace game::state::play
|
|||||||
math::random_in_range(spawnBounds.y, spawnBounds.y + spawnBounds.w));
|
math::random_in_range(spawnBounds.y, spawnBounds.y + spawnBounds.w));
|
||||||
|
|
||||||
auto& itemSchema = character.data.itemSchema;
|
auto& itemSchema = character.data.itemSchema;
|
||||||
items.emplace_back(itemSchema.anm2s.at(id), position, id);
|
auto& item = itemSchema.items.at(id);
|
||||||
|
auto& anm2 = itemSchema.anm2s.at(id);
|
||||||
|
auto durabilityMax = item.durability.value_or(itemSchema.durability);
|
||||||
|
auto animationIndex = durability_animation_index_get(itemSchema, anm2, 0, durabilityMax);
|
||||||
|
items.emplace_back(anm2, position, id, 0, animationIndex);
|
||||||
}
|
}
|
||||||
queuedItemIDs.clear();
|
queuedItemIDs.clear();
|
||||||
|
|
||||||
@@ -97,15 +113,15 @@ namespace game::state::play
|
|||||||
|
|
||||||
if (schema.categories[item.categoryID].isEdible)
|
if (schema.categories[item.categoryID].isEdible)
|
||||||
{
|
{
|
||||||
auto& chewCountMax = item.chewCount.has_value() ? *item.chewCount : schema.chewCount;
|
auto& durabilityMax = item.durability.has_value() ? *item.durability : schema.durability;
|
||||||
auto caloriesChew = item.calories.has_value() ? *item.calories / (chewCountMax + 1) : 0;
|
auto caloriesPerBite = item.calories.has_value() && durabilityMax > 0 ? *item.calories / durabilityMax : 0;
|
||||||
auto isCanEat = character.calories + caloriesChew <= character.max_capacity();
|
auto isCanEat = character.calories + caloriesPerBite <= character.max_capacity();
|
||||||
|
|
||||||
if (isJustItemHeld)
|
if (isJustItemHeld)
|
||||||
{
|
{
|
||||||
if (isCanEat)
|
if (isCanEat)
|
||||||
text.set(dialogue.get(isOverCapacity ? dialogue.feedFull : dialogue.feed), character);
|
text.set(dialogue.get(isOverCapacity ? dialogue.feedFull : dialogue.feed), character);
|
||||||
else if (caloriesChew > character.capacity)
|
else if (caloriesPerBite > character.capacity)
|
||||||
text.set(dialogue.get(dialogue.lowCapacity), character);
|
text.set(dialogue.get(dialogue.lowCapacity), character);
|
||||||
else
|
else
|
||||||
text.set(dialogue.get(dialogue.full), character);
|
text.set(dialogue.get(dialogue.full), character);
|
||||||
@@ -125,37 +141,44 @@ namespace game::state::play
|
|||||||
|
|
||||||
if (character.playedEventID == eatArea.eventID)
|
if (character.playedEventID == eatArea.eventID)
|
||||||
{
|
{
|
||||||
heldItem->chewCount++;
|
heldItem->durability++;
|
||||||
character.consume_played_event();
|
character.consume_played_event();
|
||||||
|
|
||||||
auto chewAnimation = schema.animations.chew + std::to_string(heldItem->chewCount);
|
character.calories += caloriesPerBite;
|
||||||
auto animationIndex = heldItem->chewCount > 0 ? heldItem->animationMap[chewAnimation] : -1;
|
character.totalCaloriesConsumed += caloriesPerBite;
|
||||||
|
|
||||||
heldItem->play(animationIndex, entity::Actor::SET);
|
|
||||||
|
|
||||||
character.calories += caloriesChew;
|
|
||||||
character.totalCaloriesConsumed += caloriesChew;
|
|
||||||
|
|
||||||
|
if (item.capacityBonus.has_value())
|
||||||
|
{
|
||||||
|
character.capacity += *item.capacityBonus / durabilityMax;
|
||||||
|
character.capacity =
|
||||||
|
glm::clamp(character.capacity, (float)character.data.capacityMin, (float)character.data.capacityMax);
|
||||||
|
}
|
||||||
if (item.eatSpeedBonus.has_value())
|
if (item.eatSpeedBonus.has_value())
|
||||||
{
|
{
|
||||||
character.eatSpeed += *item.eatSpeedBonus / (chewCountMax + 1);
|
character.eatSpeed += *item.eatSpeedBonus / durabilityMax;
|
||||||
character.eatSpeed =
|
character.eatSpeed = glm::clamp(character.eatSpeed, (float)character.data.eatSpeedMin,
|
||||||
glm::clamp(character.data.eatSpeedMin, character.eatSpeed, character.data.eatSpeedMax);
|
(float)character.data.eatSpeedMax);
|
||||||
}
|
}
|
||||||
if (item.digestionBonus.has_value())
|
if (item.digestionBonus.has_value())
|
||||||
{
|
{
|
||||||
character.digestionRate += *item.digestionBonus / (chewCountMax + 1);
|
character.digestionRate += *item.digestionBonus / durabilityMax;
|
||||||
character.digestionRate = glm::clamp(character.data.digestionRateMin, character.digestionRate,
|
character.digestionRate = glm::clamp(character.digestionRate, (float)character.data.digestionRateMin,
|
||||||
character.data.digestionRateMax);
|
(float)character.data.digestionRateMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (heldItem->chewCount > chewCountMax)
|
if (heldItem->durability >= durabilityMax)
|
||||||
{
|
{
|
||||||
isQueueFinishFood = true;
|
isQueueFinishFood = true;
|
||||||
character.totalFoodItemsEaten++;
|
character.totalFoodItemsEaten++;
|
||||||
queuedRemoveItemIndex = heldItemIndex;
|
queuedRemoveItemIndex = heldItemIndex;
|
||||||
heldItemIndex = -1;
|
heldItemIndex = -1;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto animationIndex =
|
||||||
|
durability_animation_index_get(schema, *heldItem, heldItem->durability, durabilityMax);
|
||||||
|
heldItem->play(animationIndex, entity::Actor::SET);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +200,7 @@ namespace game::state::play
|
|||||||
|
|
||||||
// Food stolen
|
// Food stolen
|
||||||
if (auto animation = character.animation_get(character.animation_name_convert(eatArea.animation));
|
if (auto animation = character.animation_get(character.animation_name_convert(eatArea.animation));
|
||||||
character.is_playing(animation->name))
|
animation && character.is_playing(animation->name))
|
||||||
{
|
{
|
||||||
if (!math::is_point_in_rectf(rect, heldItem->position))
|
if (!math::is_point_in_rectf(rect, heldItem->position))
|
||||||
text.set(dialogue.get(isOverCapacity ? dialogue.foodTakenFull : dialogue.foodTaken), character);
|
text.set(dialogue.get(isOverCapacity ? dialogue.foodTakenFull : dialogue.foodTaken), character);
|
||||||
@@ -232,7 +255,7 @@ namespace game::state::play
|
|||||||
|
|
||||||
if (isMouseRightClicked)
|
if (isMouseRightClicked)
|
||||||
{
|
{
|
||||||
if (item.chewCount > 0)
|
if (item.durability > 0)
|
||||||
schema.sounds.dispose.play();
|
schema.sounds.dispose.play();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "menu.hpp"
|
#include "menu.hpp"
|
||||||
|
|
||||||
|
#include "style.hpp"
|
||||||
|
|
||||||
#include "../../util/imgui.hpp"
|
#include "../../util/imgui.hpp"
|
||||||
#include "../../util/imgui/style.hpp"
|
#include "../../util/imgui/style.hpp"
|
||||||
#include "../../util/imgui/widget.hpp"
|
#include "../../util/imgui/widget.hpp"
|
||||||
@@ -8,18 +10,14 @@
|
|||||||
|
|
||||||
using namespace game::util;
|
using namespace game::util;
|
||||||
using namespace game::util::imgui;
|
using namespace game::util::imgui;
|
||||||
|
using namespace game::resource::xml;
|
||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
{
|
{
|
||||||
void Menu::tick()
|
void Menu::tick()
|
||||||
{
|
{
|
||||||
inventory.tick();
|
inventory.tick();
|
||||||
skillCheck.tick();
|
arcade.tick();
|
||||||
}
|
|
||||||
|
|
||||||
void Menu::color_set_check(Resources& resources, entity::Character& character)
|
|
||||||
{
|
|
||||||
imgui::style::color_set(resources.settings.isUseCharacterColor ? character.data.color : resources.settings.color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::update(Resources& resources, ItemManager& itemManager, entity::Character& character,
|
void Menu::update(Resources& resources, ItemManager& itemManager, entity::Character& character,
|
||||||
@@ -28,6 +26,7 @@ namespace game::state::play
|
|||||||
static constexpr auto WIDTH_MULTIPLIER = 0.30f;
|
static constexpr auto WIDTH_MULTIPLIER = 0.30f;
|
||||||
|
|
||||||
auto& schema = character.data.menuSchema;
|
auto& schema = character.data.menuSchema;
|
||||||
|
auto& strings = character.data.strings;
|
||||||
|
|
||||||
auto style = ImGui::GetStyle();
|
auto style = ImGui::GetStyle();
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
@@ -61,47 +60,41 @@ namespace game::state::play
|
|||||||
if (ImGui::BeginTabBar("##Options"))
|
if (ImGui::BeginTabBar("##Options"))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (isChat && WIDGET_FX(ImGui::BeginTabItem("Chat")))
|
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabInteract).c_str())))
|
||||||
{
|
{
|
||||||
chat.update(resources, text, character);
|
interact.update(resources, text, character);
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::BeginTabItem("Arcade")))
|
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabArcade).c_str())))
|
||||||
{
|
{
|
||||||
skillCheck.update(resources, character, inventory, text);
|
arcade.update(resources, character, inventory, text);
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::BeginTabItem("Inventory")))
|
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabInventory).c_str())))
|
||||||
{
|
{
|
||||||
inventory.update(resources, itemManager, character);
|
inventory.update(resources, itemManager, character);
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::BeginTabItem("Stats")))
|
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabSettings).c_str())))
|
||||||
{
|
{
|
||||||
stats.update(resources, skillCheck, character);
|
settingsMenu.update(resources, SettingsMenu::PLAY, &strings);
|
||||||
|
if (settingsMenu.isJustColorSet) style::color_set(resources, character);
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::BeginTabItem("Settings")))
|
if (isCheats && WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabCheats).c_str())))
|
||||||
{
|
{
|
||||||
settingsMenu.update(resources, SettingsMenu::PLAY);
|
cheats.update(resources, character, inventory);
|
||||||
if (settingsMenu.isJustColorSet) color_set_check(resources, character);
|
|
||||||
ImGui::EndTabItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCheats && WIDGET_FX(ImGui::BeginTabItem("Cheats")))
|
|
||||||
{
|
|
||||||
cheats.update(resources, character, inventory, text);
|
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (WIDGET_FX(ImGui::BeginTabItem("Debug")))
|
if (WIDGET_FX(ImGui::BeginTabItem(strings.get(Strings::MenuTabDebug).c_str())))
|
||||||
{
|
{
|
||||||
debug.update(character, cursor, itemManager, canvas);
|
debug.update(character, cursor, itemManager, canvas, text);
|
||||||
ImGui::EndTabItem();
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -128,7 +121,9 @@ namespace game::state::play
|
|||||||
|
|
||||||
if (t <= 0.0f || t >= 1.0f)
|
if (t <= 0.0f || t >= 1.0f)
|
||||||
{
|
{
|
||||||
ImGui::SetItemTooltip(isOpen ? "Close Main Menu" : "Open Main Menu");
|
ImGui::SetItemTooltip("%s", strings.get(isOpen ? Strings::MenuCloseTooltip
|
||||||
|
: Strings::MenuOpenTooltip)
|
||||||
|
.c_str());
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
isOpen = !isOpen;
|
isOpen = !isOpen;
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
#include "../settings_menu.hpp"
|
#include "../settings_menu.hpp"
|
||||||
|
|
||||||
#include "arcade/skill_check.hpp"
|
#include "arcade.hpp"
|
||||||
#include "chat.hpp"
|
|
||||||
#include "cheats.hpp"
|
#include "cheats.hpp"
|
||||||
#include "debug.hpp"
|
#include "debug.hpp"
|
||||||
#include "stats.hpp"
|
#include "interact.hpp"
|
||||||
|
#include "inventory.hpp"
|
||||||
#include "text.hpp"
|
#include "text.hpp"
|
||||||
|
|
||||||
#include "../../util/imgui/window_slide.hpp"
|
#include "../../util/imgui/window_slide.hpp"
|
||||||
@@ -18,27 +18,24 @@ namespace game::state::play
|
|||||||
class Menu
|
class Menu
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SkillCheck skillCheck;
|
Arcade arcade;
|
||||||
Chat chat;
|
Interact interact;
|
||||||
Cheats cheats;
|
Cheats cheats;
|
||||||
Debug debug;
|
Debug debug;
|
||||||
Stats stats;
|
|
||||||
Inventory inventory;
|
Inventory inventory;
|
||||||
|
|
||||||
state::SettingsMenu settingsMenu;
|
state::SettingsMenu settingsMenu;
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
bool isCheats{true};
|
bool isCheats{true};
|
||||||
#elif
|
#else
|
||||||
bool isCheats{};
|
bool isCheats{};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool isOpen{true};
|
bool isOpen{true};
|
||||||
bool isChat{true};
|
|
||||||
util::imgui::WindowSlide slide{};
|
util::imgui::WindowSlide slide{};
|
||||||
|
|
||||||
void tick();
|
void tick();
|
||||||
void update(Resources&, ItemManager&, entity::Character&, entity::Cursor&, Text&, Canvas&);
|
void update(Resources&, ItemManager&, entity::Character&, entity::Cursor&, Text&, Canvas&);
|
||||||
void color_set_check(Resources&, entity::Character&);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
#include "stats.hpp"
|
|
||||||
|
|
||||||
#include <ranges>
|
|
||||||
|
|
||||||
#include "../../util/measurement.hpp"
|
|
||||||
|
|
||||||
using namespace game::resource;
|
|
||||||
using namespace game::util;
|
|
||||||
|
|
||||||
namespace game::state::play
|
|
||||||
{
|
|
||||||
void Stats::update(Resources& resources, SkillCheck& skillCheck, entity::Character& character)
|
|
||||||
{
|
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
|
||||||
ImGui::TextUnformatted(character.data.name.c_str());
|
|
||||||
ImGui::PopFont();
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
auto& skillCheckSchema = character.data.skillCheckSchema;
|
|
||||||
auto& system = resources.settings.measurementSystem;
|
|
||||||
auto weight = character.weight_get(system);
|
|
||||||
auto weightUnit = system == measurement::IMPERIAL ? "lbs" : "kg";
|
|
||||||
|
|
||||||
ImGui::Text("Weight: %0.2f %s (Stage: %i)", weight, weightUnit, character.stage_get() + 1);
|
|
||||||
ImGui::Text("Capacity: %0.0f kcal (Max: %0.0f kcal)", character.capacity, character.max_capacity());
|
|
||||||
ImGui::Text("Digestion Rate: %0.2f%%/sec", character.digestion_rate_get());
|
|
||||||
ImGui::Text("Eating Speed: %0.2fx", character.eatSpeed);
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Totals");
|
|
||||||
|
|
||||||
ImGui::Text("Total Calories Consumed: %0.0f kcal", character.totalCaloriesConsumed);
|
|
||||||
ImGui::Text("Total Food Items Eaten: %i", character.totalFoodItemsEaten);
|
|
||||||
|
|
||||||
ImGui::SeparatorText("Skill Check");
|
|
||||||
|
|
||||||
ImGui::Text("Best: %i pts (%ix)", skillCheck.highScore, skillCheck.bestCombo);
|
|
||||||
ImGui::Text("Total Skill Checks: %i", skillCheck.totalPlays);
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)skillCheckSchema.grades.size(); i++)
|
|
||||||
{
|
|
||||||
auto& grade = skillCheckSchema.grades[i];
|
|
||||||
ImGui::Text("%s: %i", grade.namePlural.c_str(), skillCheck.gradeCounts[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Text("Accuracy: %0.2f%%", skillCheck.accuracy_score_get(character));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../../entity/character.hpp"
|
|
||||||
#include "../../resources.hpp"
|
|
||||||
|
|
||||||
#include "arcade/skill_check.hpp"
|
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
namespace game::state::play
|
|
||||||
{
|
|
||||||
class Stats
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void update(Resources&, SkillCheck&, entity::Character&);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
14
src/state/play/style.hpp
Normal file
14
src/state/play/style.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../entity/character.hpp"
|
||||||
|
#include "../../resources.hpp"
|
||||||
|
#include "../../util/imgui/style.hpp"
|
||||||
|
|
||||||
|
namespace game::state::play::style
|
||||||
|
{
|
||||||
|
inline void color_set(Resources& resources, const entity::Character& character)
|
||||||
|
{
|
||||||
|
game::util::imgui::style::color_set(resources.settings.isUseCharacterColor ? character.data.color
|
||||||
|
: resources.settings.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,10 +37,12 @@ namespace game::state::play
|
|||||||
index = 0;
|
index = 0;
|
||||||
time = 0.0f;
|
time = 0.0f;
|
||||||
isEnabled = true;
|
isEnabled = true;
|
||||||
character.isTalking = true;
|
|
||||||
if (!dialogueEntry->animation.empty())
|
if (!dialogueEntry->animation.empty())
|
||||||
character.queue_play({.animation = dialogueEntry->animation, .isInterruptible = isInterruptible});
|
character.queue_play({.animation = dialogueEntry->animation, .isInterruptible = isInterruptible});
|
||||||
if (dialogueEntry->text.empty()) isEnabled = false;
|
if (dialogueEntry->text.empty())
|
||||||
|
isEnabled = false;
|
||||||
|
else
|
||||||
|
character.isTalking = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Text::tick(entity::Character& character)
|
void Text::tick(entity::Character& character)
|
||||||
@@ -48,6 +50,8 @@ namespace game::state::play
|
|||||||
if (!entry || isFinished) return;
|
if (!entry || isFinished) return;
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
|
auto blipPeriod = character.data.textBlipPeriodBase;
|
||||||
|
if (blipPeriod > 0 && index % blipPeriod == 0) character.data.sounds.blip.play();
|
||||||
|
|
||||||
if (index >= ImTextCountCharsFromUtf8(entry->text.c_str(), entry->text.c_str() + entry->text.size()))
|
if (index >= ImTextCountCharsFromUtf8(entry->text.c_str(), entry->text.c_str() + entry->text.size()))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
using namespace game::util;
|
using namespace game::util;
|
||||||
using namespace game::util::imgui;
|
using namespace game::util::imgui;
|
||||||
|
using namespace game::resource::xml;
|
||||||
|
|
||||||
namespace game::state::play
|
namespace game::state::play
|
||||||
{
|
{
|
||||||
@@ -18,6 +19,7 @@ namespace game::state::play
|
|||||||
auto style = ImGui::GetStyle();
|
auto style = ImGui::GetStyle();
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
auto& schema = character.data.menuSchema;
|
auto& schema = character.data.menuSchema;
|
||||||
|
auto& strings = character.data.strings;
|
||||||
|
|
||||||
slide.update(isOpen, io.DeltaTime);
|
slide.update(isOpen, io.DeltaTime);
|
||||||
|
|
||||||
@@ -56,8 +58,9 @@ namespace game::state::play
|
|||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::Button("Home", buttonSize))) world.character_focus(character, canvas, focus);
|
if (WIDGET_FX(ImGui::Button(strings.get(Strings::ToolsHomeButton).c_str(), buttonSize)))
|
||||||
ImGui::SetItemTooltip("%s", "Reset camera view.\n(Shortcut: Home)");
|
world.character_focus(character, canvas, focus);
|
||||||
|
ImGui::SetItemTooltip("%s", strings.get(Strings::ToolsHomeTooltip).c_str());
|
||||||
|
|
||||||
for (int i = 0; i < (int)character.data.interactTypeNames.size(); i++)
|
for (int i = 0; i < (int)character.data.interactTypeNames.size(); i++)
|
||||||
cursor_mode_button(character.data.interactTypeNames[i], i);
|
cursor_mode_button(character.data.interactTypeNames[i], i);
|
||||||
@@ -82,7 +85,9 @@ namespace game::state::play
|
|||||||
|
|
||||||
if (t <= 0.0f || t >= 1.0f)
|
if (t <= 0.0f || t >= 1.0f)
|
||||||
{
|
{
|
||||||
ImGui::SetItemTooltip(isOpen ? "Close Tools" : "Open Tools");
|
ImGui::SetItemTooltip("%s", strings.get(isOpen ? Strings::ToolsCloseTooltip
|
||||||
|
: Strings::ToolsOpenTooltip)
|
||||||
|
.c_str());
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
isOpen = !isOpen;
|
isOpen = !isOpen;
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ namespace game::state::play
|
|||||||
{
|
{
|
||||||
static constexpr float MENU_WIDTH_MULTIPLIER = 0.30f;
|
static constexpr float MENU_WIDTH_MULTIPLIER = 0.30f;
|
||||||
static constexpr float TOOLS_WIDTH_MULTIPLIER = 0.10f;
|
static constexpr float TOOLS_WIDTH_MULTIPLIER = 0.10f;
|
||||||
|
static constexpr float INFO_HEIGHT_MULTIPLIER = 4.0f;
|
||||||
static constexpr float PADDING = 100.0f;
|
static constexpr float PADDING = 100.0f;
|
||||||
|
|
||||||
auto rect = character.rect();
|
auto rect = character.rect();
|
||||||
@@ -72,13 +73,18 @@ namespace game::state::play
|
|||||||
|
|
||||||
rect = {rect.x - PADDING * 0.5f, rect.y - PADDING * 0.5f, rect.z + PADDING, rect.w + PADDING};
|
rect = {rect.x - PADDING * 0.5f, rect.y - PADDING * 0.5f, rect.z + PADDING, rect.w + PADDING};
|
||||||
|
|
||||||
auto zoomFactor = std::min((float)canvas.size.x / rect.z, (float)canvas.size.y / rect.w);
|
auto infoHeightPixels =
|
||||||
|
ImGui::GetTextLineHeightWithSpacing() * INFO_HEIGHT_MULTIPLIER + ImGui::GetStyle().WindowPadding.y * 2.0f;
|
||||||
|
auto usableHeightPixels = std::max(1.0f, (float)canvas.size.y - infoHeightPixels);
|
||||||
|
|
||||||
|
auto zoomFactor = std::min((float)canvas.size.x / rect.z, usableHeightPixels / rect.w);
|
||||||
canvas.zoom = glm::clamp(ZOOM_MIN, math::to_percent(zoomFactor), ZOOM_MAX);
|
canvas.zoom = glm::clamp(ZOOM_MIN, math::to_percent(zoomFactor), ZOOM_MAX);
|
||||||
zoomFactor = math::to_unit(canvas.zoom);
|
zoomFactor = math::to_unit(canvas.zoom);
|
||||||
|
|
||||||
auto rectCenter = glm::vec2(rect.x + rect.z * 0.5f, rect.y + rect.w * 0.5f);
|
auto rectCenter = glm::vec2(rect.x + rect.z * 0.5f, rect.y + rect.w * 0.5f);
|
||||||
auto viewSizeWorld = glm::vec2(canvas.size) / zoomFactor;
|
auto viewSizeWorld = glm::vec2(canvas.size) / zoomFactor;
|
||||||
canvas.pan = rectCenter - (vec2(viewSizeWorld.x, viewSizeWorld.y) * 0.5f);
|
auto infoHeightWorld = infoHeightPixels / zoomFactor;
|
||||||
|
canvas.pan = rectCenter - vec2(viewSizeWorld.x * 0.5f, (viewSizeWorld.y + infoHeightWorld) * 0.5f);
|
||||||
auto menuWidthWorld = (canvas.size.x * MENU_WIDTH_MULTIPLIER) / zoomFactor;
|
auto menuWidthWorld = (canvas.size.x * MENU_WIDTH_MULTIPLIER) / zoomFactor;
|
||||||
auto toolsWidthWorld = (canvas.size.x * TOOLS_WIDTH_MULTIPLIER) / zoomFactor;
|
auto toolsWidthWorld = (canvas.size.x * TOOLS_WIDTH_MULTIPLIER) / zoomFactor;
|
||||||
|
|
||||||
|
|||||||
@@ -47,12 +47,17 @@ namespace game::state::select
|
|||||||
ImGui::TextUnformatted(character.name.c_str());
|
ImGui::TextUnformatted(character.name.c_str());
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), Font::NORMAL);
|
||||||
|
if (ImGui::BeginTabBar("##Preview Tabs"))
|
||||||
|
{
|
||||||
|
if (ImGui::BeginTabItem("Overview"))
|
||||||
|
{
|
||||||
if (!character.description.empty())
|
if (!character.description.empty())
|
||||||
{
|
{
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||||
ImGui::TextWrapped("%s", character.description.c_str());
|
ImGui::TextWrapped("%s", character.description.c_str());
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
|
||||||
@@ -61,24 +66,32 @@ namespace game::state::select
|
|||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::BIG);
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||||
|
|
||||||
ImGui::Text("Weight: %0.2f %s", system == IMPERIAL ? weight * KG_TO_LB : weight,
|
ImGui::Text("Weight: %0.2f %s", system == IMPERIAL ? weight * KG_TO_LB : weight,
|
||||||
system == IMPERIAL ? "lbs" : "kg");
|
system == IMPERIAL ? "lbs" : "kg");
|
||||||
ImGui::Text("Stages: %i", character.stages);
|
ImGui::Text("Stages: %i", character.stages);
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::PushFont(ImGui::GetFont(), Font::NORMAL);
|
if (ImGui::BeginTabItem("Credits"))
|
||||||
|
{
|
||||||
|
ImGui::Separator();
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(imgui::to_imvec4(color::GRAY)));
|
||||||
|
ImGui::PushFont(ImGui::GetFont(), Font::HEADER_2);
|
||||||
if (!character.author.empty()) ImGui::TextWrapped("Author: %s", character.author.c_str());
|
if (!character.credits.empty())
|
||||||
|
ImGui::TextWrapped("%s", character.credits.c_str());
|
||||||
|
else
|
||||||
|
ImGui::TextUnformatted("No credits listed.");
|
||||||
|
ImGui::PopFont();
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
ImGui::PopFont();
|
ImGui::PopFont();
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ namespace game::state::select
|
|||||||
|
|
||||||
auto renderSize = ImVec2(textureSize.x * scale, textureSize.y * scale);
|
auto renderSize = ImVec2(textureSize.x * scale, textureSize.y * scale);
|
||||||
|
|
||||||
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + (availableSize.x * 0.5f) - (renderSize.y * 0.5f),
|
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + (availableSize.x * 0.5f) - (renderSize.x * 0.5f),
|
||||||
ImGui::GetCursorPosY() + (availableSize.y * 0.5f) - (renderSize.y * 0.5f)));
|
ImGui::GetCursorPosY() + (availableSize.y * 0.5f) - (renderSize.y * 0.5f)));
|
||||||
|
|
||||||
ImGui::Image(character.render.id, renderSize);
|
ImGui::Image(character.render.id, renderSize);
|
||||||
|
|||||||
@@ -10,47 +10,61 @@
|
|||||||
|
|
||||||
using namespace game::util;
|
using namespace game::util;
|
||||||
using namespace game::util::imgui;
|
using namespace game::util::imgui;
|
||||||
|
using namespace game::resource::xml;
|
||||||
|
|
||||||
namespace game::state
|
namespace game::state
|
||||||
{
|
{
|
||||||
void SettingsMenu::update(Resources& resources, Mode mode)
|
void SettingsMenu::update(Resources& resources, Mode mode, const Strings* strings)
|
||||||
{
|
{
|
||||||
auto& settings = resources.settings;
|
auto& settings = resources.settings;
|
||||||
auto& measurementSystem = settings.measurementSystem;
|
auto& measurementSystem = settings.measurementSystem;
|
||||||
auto& volume = settings.volume;
|
auto& volume = settings.volume;
|
||||||
auto& color = settings.color;
|
auto& color = settings.color;
|
||||||
|
auto string_get = [&](Strings::Type type, const char* fallback) -> const char*
|
||||||
|
{
|
||||||
|
return strings ? strings->get(type).c_str() : fallback;
|
||||||
|
};
|
||||||
|
|
||||||
isJustColorSet = false;
|
isJustColorSet = false;
|
||||||
|
|
||||||
ImGui::SeparatorText("Measurement System");
|
ImGui::SeparatorText(string_get(Strings::SettingsMeasurementSystem, "Measurement System"));
|
||||||
WIDGET_FX(ImGui::RadioButton("Metric", (int*)&measurementSystem, measurement::METRIC));
|
WIDGET_FX(ImGui::RadioButton(string_get(Strings::SettingsMetric, "Metric"),
|
||||||
ImGui::SetItemTooltip("%s", "Use kilograms (kg).");
|
(int*)&measurementSystem, measurement::METRIC));
|
||||||
|
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsMetricTooltip, "Use kilograms (kg)."));
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
WIDGET_FX(ImGui::RadioButton("Imperial", (int*)&measurementSystem, measurement::IMPERIAL));
|
WIDGET_FX(ImGui::RadioButton(string_get(Strings::SettingsImperial, "Imperial"),
|
||||||
ImGui::SetItemTooltip("%s", "Use pounds (lbs).");
|
(int*)&measurementSystem, measurement::IMPERIAL));
|
||||||
|
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsImperialTooltip, "Use pounds (lbs)."));
|
||||||
|
|
||||||
ImGui::SeparatorText("Sound");
|
ImGui::SeparatorText(string_get(Strings::SettingsSound, "Sound"));
|
||||||
if (WIDGET_FX(ImGui::SliderInt("Volume", &volume, 0, 100, "%d%%")))
|
if (WIDGET_FX(
|
||||||
|
ImGui::SliderInt(string_get(Strings::SettingsVolume, "Volume"), &volume, 0, 100, "%d%%")))
|
||||||
resources.volume_set(math::to_unit((float)volume));
|
resources.volume_set(math::to_unit((float)volume));
|
||||||
ImGui::SetItemTooltip("%s", "Adjust master volume.");
|
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsVolumeTooltip, "Adjust master volume."));
|
||||||
|
|
||||||
ImGui::SeparatorText("Appearance");
|
ImGui::SeparatorText(string_get(Strings::SettingsAppearance, "Appearance"));
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::Checkbox("Use Character Color", &settings.isUseCharacterColor))) isJustColorSet = true;
|
if (WIDGET_FX(ImGui::Checkbox(string_get(Strings::SettingsUseCharacterColor,
|
||||||
ImGui::SetItemTooltip("When playing, the UI will use the character's preset UI color.");
|
"Use Character Color"),
|
||||||
|
&settings.isUseCharacterColor)))
|
||||||
|
isJustColorSet = true;
|
||||||
|
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsUseCharacterColorTooltip,
|
||||||
|
"When playing, the UI will use the character's preset UI color."));
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::BeginDisabled(settings.isUseCharacterColor);
|
ImGui::BeginDisabled(settings.isUseCharacterColor);
|
||||||
if (WIDGET_FX(
|
if (WIDGET_FX(
|
||||||
ImGui::ColorEdit3("Color", value_ptr(color), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip)))
|
ImGui::ColorEdit3(string_get(Strings::SettingsColor, "Color"), value_ptr(color),
|
||||||
|
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip)))
|
||||||
{
|
{
|
||||||
style::color_set(color);
|
style::color_set(color);
|
||||||
isJustColorSet = true;
|
isJustColorSet = true;
|
||||||
}
|
}
|
||||||
ImGui::SetItemTooltip("%s", "Change the UI color.");
|
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsColorTooltip, "Change the UI color."));
|
||||||
ImGui::EndDisabled();
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
if (WIDGET_FX(ImGui::Button("Reset to Default", ImVec2(-FLT_MIN, 0))))
|
if (WIDGET_FX(ImGui::Button(string_get(Strings::SettingsResetButton, "Reset to Default"),
|
||||||
|
ImVec2(-FLT_MIN, 0))))
|
||||||
{
|
{
|
||||||
settings = resource::xml::Settings();
|
settings = resource::xml::Settings();
|
||||||
style::color_set(settings.color);
|
style::color_set(settings.color);
|
||||||
@@ -60,11 +74,19 @@ namespace game::state
|
|||||||
{
|
{
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::Button("Save", ImVec2(-FLT_MIN, 0)))) isSave = true;
|
if (WIDGET_FX(
|
||||||
ImGui::SetItemTooltip("%s", "Save the game.\n(Note: the game autosaves frequently.)");
|
ImGui::Button(string_get(Strings::SettingsSaveButton, "Save"), ImVec2(-FLT_MIN, 0))))
|
||||||
|
isSave = true;
|
||||||
|
ImGui::SetItemTooltip(
|
||||||
|
"%s", string_get(Strings::SettingsSaveTooltip,
|
||||||
|
"Save the game.\n(Note: the game autosaves frequently.)"));
|
||||||
|
|
||||||
if (WIDGET_FX(ImGui::Button("Return to Characters", ImVec2(-FLT_MIN, 0)))) isGoToSelect = true;
|
if (WIDGET_FX(ImGui::Button(
|
||||||
ImGui::SetItemTooltip("%s", "Go back to the character selection screen.\nProgress will be saved.");
|
string_get(Strings::SettingsReturnToCharactersButton, "Return to Characters"),
|
||||||
|
ImVec2(-FLT_MIN, 0))))
|
||||||
|
isGoToSelect = true;
|
||||||
|
ImGui::SetItemTooltip("%s", string_get(Strings::SettingsReturnToCharactersTooltip,
|
||||||
|
"Go back to the character selection screen.\nProgress will be saved."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "../resource/xml/strings.hpp"
|
||||||
#include "../resources.hpp"
|
#include "../resources.hpp"
|
||||||
|
|
||||||
namespace game::state
|
namespace game::state
|
||||||
@@ -17,6 +18,6 @@ namespace game::state
|
|||||||
bool isSave{};
|
bool isSave{};
|
||||||
bool isJustColorSet{};
|
bool isJustColorSet{};
|
||||||
|
|
||||||
void update(Resources&, Mode = SELECT);
|
void update(Resources&, Mode = SELECT, const resource::xml::Strings* = nullptr);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,20 @@ namespace game::util::imgui::style
|
|||||||
static constexpr auto COLOR_BG_ALPHA = 0.90f;
|
static constexpr auto COLOR_BG_ALPHA = 0.90f;
|
||||||
static constexpr auto COLOR_ACTIVE_MULTIPLIER = 1.30f;
|
static constexpr auto COLOR_ACTIVE_MULTIPLIER = 1.30f;
|
||||||
static constexpr auto COLOR_HOVERED_MULTIPLIER = 1.60f;
|
static constexpr auto COLOR_HOVERED_MULTIPLIER = 1.60f;
|
||||||
|
static constexpr auto COLOR_ACTIVE_HOVERED_MIN = 0.1f;
|
||||||
|
static constexpr auto COLOR_ACTIVE_HOVERED_MAX = 0.9f;
|
||||||
static constexpr auto COLOR_ACCENT_MULTIPLIER = 2.0f;
|
static constexpr auto COLOR_ACCENT_MULTIPLIER = 2.0f;
|
||||||
static constexpr auto COLOR_ACCENT_ACTIVE_MULTIPLIER = 2.25f;
|
static constexpr auto COLOR_ACCENT_ACTIVE_MULTIPLIER = 2.25f;
|
||||||
|
|
||||||
auto& colors = ImGui::GetStyle().Colors;
|
auto& colors = ImGui::GetStyle().Colors;
|
||||||
|
auto active_hovered_clamp = [](glm::vec3 color)
|
||||||
|
{ return glm::clamp(color, glm::vec3(COLOR_ACTIVE_HOVERED_MIN), glm::vec3(COLOR_ACTIVE_HOVERED_MAX)); };
|
||||||
|
|
||||||
auto colorNew = to_imvec4(glm::vec4(color, 1.0f));
|
auto colorNew = to_imvec4(glm::vec4(color, 1.0f));
|
||||||
auto colorBg = to_imvec4(glm::vec4(color * COLOR_BG_MULTIPLIER, COLOR_BG_ALPHA));
|
auto colorBg = to_imvec4(glm::vec4(color * COLOR_BG_MULTIPLIER, COLOR_BG_ALPHA));
|
||||||
auto colorChildBg = to_imvec4(glm::vec4(color * COLOR_BG_MULTIPLIER, 0.0f));
|
auto colorChildBg = to_imvec4(glm::vec4(color * COLOR_BG_MULTIPLIER, 0.0f));
|
||||||
auto colorActive = to_imvec4(glm::vec4(color * COLOR_ACTIVE_MULTIPLIER, 1.0f));
|
auto colorActive = to_imvec4(glm::vec4(active_hovered_clamp(color * COLOR_ACTIVE_MULTIPLIER), 1.0f));
|
||||||
auto colorHovered = to_imvec4(glm::vec4(color * COLOR_HOVERED_MULTIPLIER, 1.0f));
|
auto colorHovered = to_imvec4(glm::vec4(active_hovered_clamp(color * COLOR_HOVERED_MULTIPLIER), 1.0f));
|
||||||
auto colorAccent = to_imvec4(glm::vec4(color * COLOR_ACCENT_MULTIPLIER, 1.0f));
|
auto colorAccent = to_imvec4(glm::vec4(color * COLOR_ACCENT_MULTIPLIER, 1.0f));
|
||||||
auto colorAccentActive = to_imvec4(glm::vec4(color * COLOR_ACCENT_ACTIVE_MULTIPLIER, 1.0f));
|
auto colorAccentActive = to_imvec4(glm::vec4(color * COLOR_ACCENT_ACTIVE_MULTIPLIER, 1.0f));
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ namespace game::util::preferences
|
|||||||
std::filesystem::path path()
|
std::filesystem::path path()
|
||||||
{
|
{
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
static constexpr auto filePath = "/snivy";
|
static constexpr auto filePath = "/shweets-sim";
|
||||||
std::filesystem::create_directories(filePath);
|
std::filesystem::create_directories(filePath);
|
||||||
return filePath;
|
return filePath;
|
||||||
#else
|
#else
|
||||||
auto sdlPath = SDL_GetPrefPath(nullptr, "snivy");
|
auto sdlPath = SDL_GetPrefPath(nullptr, "shweets-sim");
|
||||||
if (!sdlPath) return {};
|
if (!sdlPath) return {};
|
||||||
auto filePath = std::filesystem::path(sdlPath);
|
auto filePath = std::filesystem::path(sdlPath);
|
||||||
std::filesystem::create_directories(filePath);
|
std::filesystem::create_directories(filePath);
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ namespace game::util::web_filesystem
|
|||||||
Module.filesystemReady = 0;
|
Module.filesystemReady = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FS.mkdir('/snivy');
|
FS.mkdir('/shweets-sim');
|
||||||
}
|
}
|
||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
FS.mount(IDBFS, {}, '/snivy');
|
FS.mount(IDBFS, {}, '/shweets-sim');
|
||||||
FS.syncfs(
|
FS.syncfs(
|
||||||
true, function(err) {
|
true, function(err) {
|
||||||
if (err) console.error('IDBFS init sync failed', err);
|
if (err) console.error('IDBFS init sync failed', err);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>snivy</title>
|
<title>shweets-sim</title>
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user