diff --git a/.clangd b/.clangd index 3542d83..6b1088e 100644 --- a/.clangd +++ b/.clangd @@ -1,3 +1,3 @@ CompileFlags: - CompilationDatabase: build + CompilationDatabase: out/build/linux-debug Add: [-std=gnu++23] diff --git a/.vscode/launch.json b/.vscode/launch.json index 6ea77e7..acc7572 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "program": "${workspaceFolder}/out/build/linux-debug/bin/Debug/snivy", "args": [], "stopAtEntry": false, - "cwd": "${workspaceFolder}/out/build/linux-debug", + "cwd": "${workspaceFolder}/out/build/linux-debug/bin/Debug", "environment": [], "externalConsole": false, "MIMode": "gdb", @@ -27,7 +27,7 @@ "windows": { "type": "cppvsdbg", "program": "${workspaceFolder}/out/build/x64-Debug/bin/Debug/snivy.exe", - "cwd": "${workspaceFolder}/out/build/x64-Debug" + "cwd": "${workspaceFolder}/out/build/x64-Debug/bin/Debug" } }, { @@ -38,7 +38,7 @@ "preLaunchTask": "cmake: build release", "program": "${workspaceFolder}/out/build/linux-release/bin/Release/snivy", "args": [], - "cwd": "${workspaceFolder}/out/build/linux-release", + "cwd": "${workspaceFolder}/out/build/linux-release/bin/Release", "environment": [], "externalConsole": false, "MIMode": "gdb", @@ -46,7 +46,7 @@ "windows": { "type": "cppvsdbg", "program": "${workspaceFolder}/out/build/x64-Release/bin/Release/snivy.exe", - "cwd": "${workspaceFolder}/out/build/x64-Release" + "cwd": "${workspaceFolder}/out/build/x64-Release/bin/Release" } }, { diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b756d93..5f8507e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -61,6 +61,9 @@ "linux": { "command": "${workspaceFolder}/out/build/linux-debug/bin/Debug/snivy" }, + "options": { + "cwd": "${workspaceFolder}/out/build/linux-debug/bin/Debug" + }, "windows": { "command": "echo \"linux: run debug is Linux-only\" && exit 1" }, @@ -73,6 +76,9 @@ "linux": { "command": "${workspaceFolder}/out/build/linux-release/bin/Release/snivy" }, + "options": { + "cwd": "${workspaceFolder}/out/build/linux-release/bin/Release" + }, "windows": { "command": "echo \"linux: run release is Linux-only\" && exit 1" }, diff --git a/CMakeLists.txt b/CMakeLists.txt index 29bb69c..21530c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,7 @@ if(EXISTS "${PROJECT_RESOURCES_DIR}") -DSRC_DIR="${PROJECT_RESOURCES_DIR}" -DDST_DIR="${PROJECT_RESOURCES_BINARY_DIR}" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/copy_resources.cmake" + WORKING_DIRECTORY "$" DEPENDS ${PROJECT_RESOURCE_FILES} COMMENT "Copying resources directory") add_dependencies(${PROJECT_NAME} copy_resources) diff --git a/cmake/copy_resources.cmake b/cmake/copy_resources.cmake index 500e3b7..1f85d80 100644 --- a/cmake/copy_resources.cmake +++ b/cmake/copy_resources.cmake @@ -2,6 +2,20 @@ if(NOT DEFINED SRC_DIR OR NOT DEFINED DST_DIR) message(FATAL_ERROR "SRC_DIR and DST_DIR must be defined") endif() +set(CHARACTERS_DIR "${SRC_DIR}/characters") +set(CHARACTERS_ZIP_SCRIPT "${CHARACTERS_DIR}/zip") + +if(EXISTS "${CHARACTERS_ZIP_SCRIPT}") + execute_process( + COMMAND "${CHARACTERS_ZIP_SCRIPT}" + WORKING_DIRECTORY "${CHARACTERS_DIR}" + RESULT_VARIABLE ZIP_SCRIPT_RESULT + ) + if(NOT ZIP_SCRIPT_RESULT EQUAL 0) + message(FATAL_ERROR "Failed running ${CHARACTERS_ZIP_SCRIPT} (exit code ${ZIP_SCRIPT_RESULT})") + endif() +endif() + file(REMOVE_RECURSE "${DST_DIR}") file(MAKE_DIRECTORY "${DST_DIR}") @@ -11,7 +25,7 @@ file(COPY "${SRC_DIR}/" DESTINATION "${DST_DIR}" # Copy only .zip archives from resources/characters. file(MAKE_DIRECTORY "${DST_DIR}/characters") -file(GLOB CHARACTER_ZIPS "${SRC_DIR}/characters/*.zip") +file(GLOB CHARACTER_ZIPS "${CHARACTERS_DIR}/*.zip") if(CHARACTER_ZIPS) file(COPY ${CHARACTER_ZIPS} DESTINATION "${DST_DIR}/characters") endif() diff --git a/src/entity/character.cpp b/src/entity/character.cpp index 5bc7af6..c59ed4c 100644 --- a/src/entity/character.cpp +++ b/src/entity/character.cpp @@ -139,7 +139,8 @@ namespace game::entity 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); } - void Character::play_convert(const std::string& animation, Mode playMode, float startAtTime, float speedMultiplierValue) + void Character::play_convert(const std::string& animation, Mode playMode, float startAtTime, + float speedMultiplierValue) { play(animation_name_convert(animation), playMode, startAtTime, speedMultiplierValue); } @@ -225,7 +226,7 @@ namespace game::entity else isJustDigested = true; - weight = glm::clamp(data.weightMin, weight + increment, data.weightMax); + weight += increment; isDigesting = false; digestionTimer = data.digestionTimerMax; diff --git a/src/resource/font.cpp b/src/resource/font.cpp index 0938c19..ac63e27 100644 --- a/src/resource/font.cpp +++ b/src/resource/font.cpp @@ -4,11 +4,32 @@ using namespace game::util; +static ImFont* default_font_fallback_get() +{ + auto& io = ImGui::GetIO(); + + if (io.FontDefault) return io.FontDefault; + if (!io.Fonts->Fonts.empty()) return io.Fonts->Fonts.front(); + return io.Fonts->AddFontDefault(); +} + namespace game::resource { Font::Font(const std::filesystem::path& path, float size) { auto pathString = path.string(); + std::error_code ec; + + if (!std::filesystem::is_regular_file(path, ec)) + { + logger.error(std::format("Failed to initialize font: {} (file not found)", pathString)); + internal = default_font_fallback_get(); + if (internal) + logger.info(std::format("Falling back to default ImGui font: {}", pathString)); + else + logger.error("Failed to initialize fallback ImGui default font"); + return; + } ImFontConfig config; config.FontDataOwnedByAtlas = false; @@ -18,7 +39,14 @@ namespace game::resource if (internal) logger.info(std::format("Initialized font: {}", pathString)); else + { logger.error(std::format("Failed to initialize font: {}", pathString)); + internal = default_font_fallback_get(); + if (internal) + logger.info(std::format("Falling back to default ImGui font: {}", pathString)); + else + logger.error("Failed to initialize fallback ImGui default font"); + } } Font::Font(const physfs::Path& path, float size) @@ -27,6 +55,11 @@ namespace game::resource { logger.error( std::format("Failed to initialize font from PhysicsFS path: {} ({})", path.c_str(), physfs::error_get())); + internal = default_font_fallback_get(); + if (internal) + logger.info(std::format("Falling back to default ImGui font: {}", path.c_str())); + else + logger.error("Failed to initialize fallback ImGui default font"); return; } @@ -36,6 +69,22 @@ namespace game::resource { logger.error( std::format("Failed to initialize font from PhysicsFS path: {} ({})", path.c_str(), physfs::error_get())); + internal = default_font_fallback_get(); + if (internal) + logger.info(std::format("Falling back to default ImGui font: {}", path.c_str())); + else + logger.error("Failed to initialize fallback ImGui default font"); + return; + } + + if (buffer.size() <= 100) + { + logger.error(std::format("Failed to initialize font from PhysicsFS path: {} (buffer too small)", path.c_str())); + internal = default_font_fallback_get(); + if (internal) + logger.info(std::format("Falling back to default ImGui font: {}", path.c_str())); + else + logger.error("Failed to initialize fallback ImGui default font"); return; } @@ -47,7 +96,14 @@ namespace game::resource if (internal) logger.info(std::format("Initialized font: {}", path.c_str())); else + { logger.error(std::format("Failed to initialize font: {}", path.c_str())); + internal = default_font_fallback_get(); + if (internal) + logger.info(std::format("Falling back to default ImGui font: {}", path.c_str())); + else + logger.error("Failed to initialize fallback ImGui default font"); + } } ImFont* Font::get() { return internal; }; diff --git a/src/resource/xml/character.cpp b/src/resource/xml/character.cpp index 6ab7eaa..e8cc1ff 100644 --- a/src/resource/xml/character.cpp +++ b/src/resource/xml/character.cpp @@ -40,8 +40,6 @@ namespace game::resource::xml query_string_attribute(root, "Name", &name); root->QueryFloatAttribute("Weight", &weight); - root->QueryFloatAttribute("WeightMin", &weightMin); - root->QueryFloatAttribute("WeightMax", &weightMax); root->QueryFloatAttribute("Capacity", &capacity); root->QueryFloatAttribute("CapacityMin", &capacityMin); diff --git a/src/resource/xml/character.hpp b/src/resource/xml/character.hpp index 1714aef..286e5bf 100644 --- a/src/resource/xml/character.hpp +++ b/src/resource/xml/character.hpp @@ -117,8 +117,6 @@ namespace game::resource::xml std::string name{}; std::filesystem::path path{}; float weight{50}; - float weightMin{}; - float weightMax{999}; float capacity{2000.0f}; float capacityMin{2000.0f}; float capacityMax{99999.0f}; diff --git a/src/resources.cpp b/src/resources.cpp index d877023..991ffda 100644 --- a/src/resources.cpp +++ b/src/resources.cpp @@ -1,6 +1,7 @@ #include "resources.hpp" #include "util/preferences.hpp" +#include "log.hpp" using namespace game::resource; using namespace game::util; @@ -12,7 +13,19 @@ namespace game for (int i = 0; i < shader::COUNT; i++) shaders[i] = Shader(shader::INFO[i].vertex, shader::INFO[i].fragment); - for (auto& entry : std::filesystem::recursive_directory_iterator("resources/characters")) + std::string CHARACTERS_DIRECTORY{"resources/characters"}; + std::error_code ec{}; + + if (!std::filesystem::is_directory(CHARACTERS_DIRECTORY, ec)) + { + if (ec) + logger.warning("Failed to read characters directory: " + CHARACTERS_DIRECTORY + " (" + ec.message() + ")"); + else + logger.warning("Characters directory not found: " + CHARACTERS_DIRECTORY); + return; + } + + for (auto& entry : std::filesystem::recursive_directory_iterator(CHARACTERS_DIRECTORY)) if (entry.is_regular_file() && entry.path().extension() == ".zip") characterPreviews.emplace_back(entry.path()); characters.resize(characterPreviews.size()); } diff --git a/src/state/main.cpp b/src/state/main.cpp index 390031c..daa5dc2 100644 --- a/src/state/main.cpp +++ b/src/state/main.cpp @@ -37,6 +37,9 @@ namespace game::state character = 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.eatSpeed = glm::clamp(data.eatSpeedMin, character.eatSpeed, data.eatSpeedMax); + character.capacity = glm::clamp(data.capacityMin, character.capacity, data.capacityMax); auto isAlternateSpritesheet = (game == NEW_GAME && math::random_percent_roll(data.alternateSpritesheet.chanceOnNewGame)); @@ -88,16 +91,13 @@ namespace game::state isPostgame = saveData.isPostgame; - if (isPostgame) - menu.isCheats = true; - else - menu.isCheats = false; + if (isPostgame) menu.isCheats = true; if (game == NEW_GAME) isWindows = false; if (auto font = character.data.menuSchema.font.get()) ImGui::GetIO().FontDefault = font; - character.play_default_animation(); + character.queue_idle_animation(); character.tick(); worldCanvas.size_set(imgui::to_vec2(ImGui::GetMainViewport()->Size)); world.set(character, worldCanvas, focus_get()); @@ -170,7 +170,8 @@ namespace game::state } } - if (character.isJustStageFinal && !isEnd && !isPostgame) isEnd = true; + if (character.isJustStageFinal && !isEnd && !isPostgame) + isEnd = true; if (isEnd) { @@ -199,6 +200,7 @@ namespace game::state isEndEnd = true; isEnd = false; isPostgame = true; + world.character_focus(character, worldCanvas, focus_get()); } } } diff --git a/src/state/main/cheats.cpp b/src/state/main/cheats.cpp index 0b02372..597b81a 100644 --- a/src/state/main/cheats.cpp +++ b/src/state/main/cheats.cpp @@ -32,13 +32,6 @@ namespace game::state::main ImGui::SameLine(); if (WIDGET_FX(ImGui::Button("Digest"))) character.digestionProgress = entity::Character::DIGESTION_MAX; - if (WIDGET_FX(ImGui::SliderFloat("Weight", &character.weight, character.data.weightMin, character.data.weightMax, - "%0.2f kg"))) - { - character.stage = character.stage_get(); - character.queue_idle_animation(); - } - auto stage = character.stage + 1; if (WIDGET_FX(ImGui::SliderInt("Stage", &stage, 1, (int)character.data.stages.size() + 1))) { diff --git a/src/state/main/cheats.hpp b/src/state/main/cheats.hpp index 5def3cf..74c988b 100644 --- a/src/state/main/cheats.hpp +++ b/src/state/main/cheats.hpp @@ -10,6 +10,7 @@ namespace game::state::main class Cheats { public: + void update(Resources&, entity::Character&, Inventory&, Text&); }; } diff --git a/src/state/main/item_manager.cpp b/src/state/main/item_manager.cpp index 4021be6..9c2c548 100644 --- a/src/state/main/item_manager.cpp +++ b/src/state/main/item_manager.cpp @@ -37,6 +37,13 @@ namespace game::state::main auto isImguiCaptureMouse = ImGui::GetIO().WantCaptureMouse; + auto isItemsLocked = character.isStageUp; + + if (isItemsLocked) + { + heldItemIndex = -1; + } + auto isMouseLeftClicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left); auto isMouseLeftDown = ImGui::IsMouseDown(ImGuiMouseButton_Left); auto isMouseLeftReleased = ImGui::IsMouseReleased(ImGuiMouseButton_Left); @@ -54,7 +61,7 @@ namespace game::state::main if (isJustItemHeldStopped || isJustItemThrown) { cursor.queue_default_animation(); - if (!isJustItemThrown) character.queue_idle_animation(); + if (!isJustItemThrown && !isItemsLocked) character.queue_idle_animation(); isJustItemHeldStopped = false; isJustItemThrown = false; } @@ -115,10 +122,10 @@ namespace game::state::main auto rect = character.null_frame_rect(eatArea.nullID); - if (isCanEat && math::is_point_in_rectf(rect, heldItem->position)) + if (isCanEat && math::is_point_in_rectf(rect, heldItem->position) && !isItemsLocked) { character.queue_play( - {.animation = eatArea.animation, .speedMultiplier = character.eatSpeed, .isInterruptible = false}); + {.animation = eatArea.animation, .speedMultiplier = character.eatSpeed}); if (character.playedEventID == eatArea.eventID) { @@ -206,7 +213,7 @@ namespace game::state::main item.update(); - if (math::is_point_in_rectf(item.rect(), cursorPosition) && !isImguiCaptureMouse) + if (math::is_point_in_rectf(item.rect(), cursorPosition) && !isImguiCaptureMouse && !isItemsLocked) { isItemHovered = true; cursor.queue_play({cursorSchema.animations.hover.get()}); diff --git a/src/state/main/menu.cpp b/src/state/main/menu.cpp index d8041b9..267c671 100644 --- a/src/state/main/menu.cpp +++ b/src/state/main/menu.cpp @@ -91,13 +91,11 @@ namespace game::state::main ImGui::EndTabItem(); } -#if defined(DEBUG) && DEBUG - if (WIDGET_FX(ImGui::BeginTabItem("Debug"))) + if (isDebug && WIDGET_FX(ImGui::BeginTabItem("Debug"))) { debug.update(character, cursor, itemManager, canvas); ImGui::EndTabItem(); } -#endif } ImGui::EndTabBar(); } diff --git a/src/state/main/menu.hpp b/src/state/main/menu.hpp index 8cba07a..2766b80 100644 --- a/src/state/main/menu.hpp +++ b/src/state/main/menu.hpp @@ -27,7 +27,14 @@ namespace game::state::main state::Configuration configuration; +#if DEBUG bool isCheats{true}; + bool isDebug{true}; +#else + bool isCheats{}; + bool isDebug{}; +#endif + bool isOpen{true}; bool isChat{true}; util::imgui::WindowSlide slide{};