diff --git a/CHANGELOG.md b/CHANGELOG.md index 358ccbd4..2b676e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,17 @@ All notable changes to this project will be documented in this file. -## [3.3.1] - 2025-11-05 +## [3.4.0] - 2025-11-22 + +### Added +- Add running animation + - animation speed-up when full CPU + +### Fixed +- working (CPU) animation stuck + + +## [3.3.1] - 2025-11-15 ### Fixed - randomize at start-up diff --git a/CMakeLists.txt b/CMakeLists.txt index ba0b04cb..040c7a5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ endif() -project(bongocat LANGUAGES C CXX VERSION 3.3.1) +project(bongocat LANGUAGES C CXX VERSION 3.4.0) # Feature Flags include(CMakeDependentOption) @@ -371,7 +371,7 @@ endif() # Package set(CPACK_PACKAGE_NAME "bongocat") -set(CPACK_PACKAGE_VERSION "3.3.1") +set(CPACK_PACKAGE_VERSION "3.4.0") set(CPACK_PACKAGE_CONTACT "hircreacc@gmail.com") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A delightful Wayland overlay that displays an animated V-Pet reacting to your keyboard input! ") set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") diff --git a/README.md b/README.md index 94aa9b61..eaa00cf2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Bongo Cat + V-Pets Wayland Overlay [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) -[![Version](https://img.shields.io/badge/version-3.3.1-blue.svg)](https://github.com/furudbat/wayland-vpets/releases) +[![Version](https://img.shields.io/badge/version-3.4.0-blue.svg)](https://github.com/furudbat/wayland-vpets/releases) [![Release Build](https://github.com/furudbat/wayland-vpets/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/furudbat/wayland-vpets/actions/workflows/release.yml) A delightful Wayland overlay that displays an animated V-Pet reacting to your keyboard input! @@ -785,5 +785,5 @@ See [COPYRIGHT](assets/COPYRIGHT.md) for more details. --- -**₍^. .^₎ Wayland Bongo Cat Overlay v3.3.1** - Making desktops more delightful, one keystroke at a time! +**₍^. .^₎ Wayland Bongo Cat Overlay v3.4.0** - Making desktops more delightful, one keystroke at a time! Now with Digimon V-Pets, Clippy and Pokémon. diff --git a/examples/custom-sprite-sheets/README.md b/examples/custom-sprite-sheets/README.md index 792ef2ca..388954ec 100644 --- a/examples/custom-sprite-sheets/README.md +++ b/examples/custom-sprite-sheets/README.md @@ -154,6 +154,24 @@ custom_moving_frames=8 custom_mirror_x_moving=1 ``` +## RunCat + +[gnome-runcat](https://github.com/win0err/gnome-runcat) by win0err + +![runcat sprite sheet](runcat.png) + +- Running animation (5 frames) + +```ini +# Sprite Sheet Settings +animation_name=custom +custom_sprite_sheet_filename=runcat.png +custom_idle_row=1 +custom_idle_frames=1 +custom_running_row=1 +custom_running_frames=5 +custom_rows=1 +``` ## Know issues diff --git a/examples/custom-sprite-sheets/run-neko.bongocat.conf b/examples/custom-sprite-sheets/run-neko.bongocat.conf new file mode 100644 index 00000000..1ea3c911 --- /dev/null +++ b/examples/custom-sprite-sheets/run-neko.bongocat.conf @@ -0,0 +1,41 @@ +# Base config for neko sprite sheet + +# Custom Sprite Sheet Settings +animation_name=custom +custom_sprite_sheet_filename=neko.png +custom_idle_frames=2 +custom_boring_frames=2 +custom_writing_frames=2 +custom_happy_frames=2 +custom_asleep_frames=2 +custom_sleep_frames=2 +custom_wake_up_frames=1 +custom_working_frames=2 +custom_moving_frames=2 + +invert_color=0 +idle_frame=0 +mirror_x=1 + + +# Anti-aliasing settings +enable_antialiasing=0 + +# Size settings +cat_height=64 + +# Animation timing (in milliseconds) +keypress_duration=500 +idle_animation=0 +animation_speed=500 +# CPU settings +update_rate=2000 +cpu_threshold=30 +cpu_running_factor=5.0 + +# Frame rate settings, higher FPS for faster running animation +fps=60 +input_fps=60 + +# Moving +#movement_speed=0 \ No newline at end of file diff --git a/examples/custom-sprite-sheets/runcat.bongocat.conf b/examples/custom-sprite-sheets/runcat.bongocat.conf new file mode 100644 index 00000000..eec0f0dd --- /dev/null +++ b/examples/custom-sprite-sheets/runcat.bongocat.conf @@ -0,0 +1,37 @@ +# Base config for neko sprite sheet + +# Custom Sprite Sheet Settings +animation_name=custom +custom_sprite_sheet_filename=runcat.png +custom_idle_row=1 +custom_idle_frames=1 +custom_running_row=1 +custom_running_frames=5 +custom_rows=1 +idle_frame=0 + +invert_color=0 +mirror_x=0 + + +# Anti-aliasing settings +enable_antialiasing=0 + +# Size settings +cat_height=18 + +# Animation timing (in milliseconds) +keypress_duration=500 +idle_animation=0 +animation_speed=250 +# CPU settings +update_rate=2000 +cpu_threshold=30 +cpu_running_factor=5.0 + +# Frame rate settings, higher FPS for faster running animation +fps=60 +input_fps=60 + +# Moving +#movement_speed=0 \ No newline at end of file diff --git a/examples/custom-sprite-sheets/runcat.png b/examples/custom-sprite-sheets/runcat.png new file mode 100644 index 00000000..3cda00a3 Binary files /dev/null and b/examples/custom-sprite-sheets/runcat.png differ diff --git a/include/config/config.h b/include/config/config.h index ffc89ede..56f51106 100644 --- a/include/config/config.h +++ b/include/config/config.h @@ -119,9 +119,11 @@ namespace bongocat::config { int32_t idle_animation{0}; int32_t input_fps{0}; int32_t randomize_index{0}; + int32_t randomize_on_reload{0}; int32_t update_rate_ms{0}; double cpu_threshold{0}; + double cpu_running_factor{0}; int32_t movement_radius{0}; int32_t enable_movement_debug{0}; @@ -183,8 +185,10 @@ namespace bongocat::config { idle_animation(other.idle_animation), input_fps(other.input_fps), randomize_index(other.randomize_index), + randomize_on_reload(other.randomize_on_reload), update_rate_ms(other.update_rate_ms), cpu_threshold(other.cpu_threshold), + cpu_running_factor(other.cpu_running_factor), movement_radius(other.movement_radius), enable_movement_debug(other.enable_movement_debug), movement_speed(other.movement_speed), @@ -237,11 +241,13 @@ namespace bongocat::config { idle_animation = other.idle_animation; input_fps = other.input_fps; randomize_index = other.randomize_index; + randomize_on_reload = other.randomize_on_reload; update_rate_ms = other.update_rate_ms; movement_radius = other.movement_radius; enable_movement_debug = other.enable_movement_debug; movement_speed = other.movement_speed; cpu_threshold = other.cpu_threshold; + cpu_running_factor = other.cpu_running_factor; screen_width = other.screen_width; custom_sprite_sheet_settings = other.custom_sprite_sheet_settings; _keep_old_animation_index = other._keep_old_animation_index; @@ -292,8 +298,10 @@ namespace bongocat::config { idle_animation(other.idle_animation), input_fps(other.input_fps), randomize_index(other.randomize_index), + randomize_on_reload(other.randomize_on_reload), update_rate_ms(other.update_rate_ms), cpu_threshold(other.cpu_threshold), + cpu_running_factor(other.cpu_running_factor), movement_radius(other.movement_radius), enable_movement_debug(other.enable_movement_debug), movement_speed(other.movement_speed), @@ -354,8 +362,10 @@ namespace bongocat::config { idle_animation = other.idle_animation; input_fps = other.input_fps; randomize_index = other.randomize_index; + randomize_on_reload = other.randomize_on_reload; update_rate_ms = other.update_rate_ms; cpu_threshold = other.cpu_threshold; + cpu_running_factor = other.cpu_running_factor; movement_radius = other.movement_radius; enable_movement_debug = other.enable_movement_debug; movement_speed = other.movement_speed; diff --git a/include/core/bongocat.h b/include/core/bongocat.h index aaa68b65..d19c554e 100644 --- a/include/core/bongocat.h +++ b/include/core/bongocat.h @@ -7,7 +7,7 @@ #include "utils/memory.h" // Version -inline static constexpr const char* BONGOCAT_VERSION = "3.3.1"; +inline static constexpr const char* BONGOCAT_VERSION = "3.4.0"; // Common constants inline static constexpr int32_t DEFAULT_SCREEN_WIDTH = 1920; diff --git a/include/embedded_assets/custom/custom_sprite.h b/include/embedded_assets/custom/custom_sprite.h index bd00e644..8a3fa7e9 100644 --- a/include/embedded_assets/custom/custom_sprite.h +++ b/include/embedded_assets/custom/custom_sprite.h @@ -20,6 +20,9 @@ namespace bongocat::assets { inline static constexpr size_t CUSTOM_SPRITE_SHEET_ROW_START_MOVING = 12; inline static constexpr size_t CUSTOM_SPRITE_SHEET_ROW_MOVING = 13; inline static constexpr size_t CUSTOM_SPRITE_SHEET_ROW_END_MOVING = 14; + inline static constexpr size_t CUSTOM_SPRITE_SHEET_ROW_START_RUNNING = 15; + inline static constexpr size_t CUSTOM_SPRITE_SHEET_ROW_RUNNING = 16; + inline static constexpr size_t CUSTOM_SPRITE_SHEET_ROW_END_RUNNING = 17; enum class CustomAnimations : uint8_t { Idle = CUSTOM_SPRITE_SHEET_ROW_IDLE, Boring = CUSTOM_SPRITE_SHEET_ROW_BORING, @@ -36,8 +39,11 @@ namespace bongocat::assets { StartMoving = CUSTOM_SPRITE_SHEET_ROW_START_MOVING, Moving = CUSTOM_SPRITE_SHEET_ROW_MOVING, EndMoving = CUSTOM_SPRITE_SHEET_ROW_END_MOVING, + StartRunning = CUSTOM_SPRITE_SHEET_ROW_START_RUNNING, + Running = CUSTOM_SPRITE_SHEET_ROW_RUNNING, + EndRunning = CUSTOM_SPRITE_SHEET_ROW_END_RUNNING, }; - inline static constexpr size_t CUSTOM_SPRITE_SHEET_MAX_ROWS = 15; + inline static constexpr size_t CUSTOM_SPRITE_SHEET_MAX_ROWS = 18; // custom (sprite sheet) inline static constexpr char CUSTOM_ID_ARR[] = "custom"; @@ -73,6 +79,10 @@ namespace bongocat::assets { int32_t moving_frames{0}; int32_t end_moving_frames{0}; + int32_t start_running_frames{0}; + int32_t running_frames{0}; + int32_t end_running_frames{0}; + int32_t feature_toggle_writing_frames{-1}; int32_t feature_toggle_writing_frames_random{-1}; int32_t feature_mirror_x_moving{-1}; @@ -98,9 +108,19 @@ namespace bongocat::assets { int32_t start_moving_row_index{-1}; int32_t moving_row_index{-1}; int32_t end_moving_row_index{-1}; + + int32_t start_running_row_index{-1}; + int32_t running_row_index{-1}; + int32_t end_running_row_index{-1}; + + int32_t rows{-1}; }; inline int get_custom_animation_settings_rows_count(const custom_animation_settings_t& sprite_sheet_settings) { + if (sprite_sheet_settings.rows >= 1) { + return sprite_sheet_settings.rows; + } + int sprite_sheet_rows{0}; // detect sprite sheet rows @@ -124,6 +144,10 @@ namespace bongocat::assets { if (sprite_sheet_settings.moving_frames > 0) sprite_sheet_rows++; if (sprite_sheet_settings.end_moving_frames > 0) sprite_sheet_rows++; + if (sprite_sheet_settings.start_running_frames > 0) sprite_sheet_rows++; + if (sprite_sheet_settings.running_frames > 0) sprite_sheet_rows++; + if (sprite_sheet_settings.end_running_frames > 0) sprite_sheet_rows++; + return sprite_sheet_rows; } inline int get_custom_animation_settings_max_cols(const custom_animation_settings_t& sprite_sheet_settings) { @@ -148,6 +172,10 @@ namespace bongocat::assets { if (sprite_sheet_settings.moving_frames >= sprite_sheet_cols) sprite_sheet_cols = sprite_sheet_settings.moving_frames; if (sprite_sheet_settings.end_moving_frames >= sprite_sheet_cols) sprite_sheet_cols = sprite_sheet_settings.end_moving_frames; + if (sprite_sheet_settings.start_running_frames >= sprite_sheet_cols) sprite_sheet_cols = sprite_sheet_settings.start_running_frames; + if (sprite_sheet_settings.running_frames >= sprite_sheet_cols) sprite_sheet_cols = sprite_sheet_settings.running_frames; + if (sprite_sheet_settings.end_running_frames >= sprite_sheet_cols) sprite_sheet_cols = sprite_sheet_settings.end_running_frames; + return sprite_sheet_cols; } } diff --git a/include/graphics/sprite_sheet.h b/include/graphics/sprite_sheet.h index 1bab053c..51a34a1f 100644 --- a/include/graphics/sprite_sheet.h +++ b/include/graphics/sprite_sheet.h @@ -63,6 +63,7 @@ namespace bongocat::animation { int32_t working[MAX_ANIMATION_FRAMES]{}; // attack int32_t moving[MAX_ANIMATION_FRAMES]{}; int32_t happy[MAX_ANIMATION_FRAMES]{}; + int32_t running[MAX_ANIMATION_FRAMES]{}; }; struct dm_sprite_sheet_t { @@ -136,6 +137,10 @@ namespace bongocat::animation { ms_agent_sprite_sheet_animation_section_t end_moving; ms_agent_sprite_sheet_animation_section_t happy; + + ms_agent_sprite_sheet_animation_section_t start_running; + ms_agent_sprite_sheet_animation_section_t running; + ms_agent_sprite_sheet_animation_section_t end_running; }; struct custom_sprite_sheet_animation_section_t { @@ -170,6 +175,10 @@ namespace bongocat::animation { custom_sprite_sheet_animation_section_t moving; custom_sprite_sheet_animation_section_t end_moving; + custom_sprite_sheet_animation_section_t start_running; + custom_sprite_sheet_animation_section_t running; + custom_sprite_sheet_animation_section_t end_running; + // features bool feature_idle{false}; bool feature_boring{false}; @@ -179,6 +188,7 @@ namespace bongocat::animation { bool feature_sleep_wake_up{false}; bool feature_working{false}; bool feature_moving{false}; + bool feature_running{false}; bool feature_writing_toggle_frames{false}; bool feature_writing_toggle_frames_random{false}; }; diff --git a/nix/default.nix b/nix/default.nix index 91d773ea..a94268f8 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -11,7 +11,7 @@ }: stdenv.mkDerivation (finalAttrs: { pname = "wayland-vpets"; - version = "3.3.1"; + version = "3.4.0"; src = ../.; # Build toolchain and dependencies diff --git a/src/config/config.cpp b/src/config/config.cpp index 6fa42ff0..a8ad819f 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -60,7 +60,8 @@ namespace bongocat::config { static inline constexpr int MAX_INTERVAL_SEC = 3600; static inline constexpr int MIN_KPM = 0; static inline constexpr int MAX_KPM = 10000; - static inline constexpr int MAX_CPU_THRESHOLD = 100; + static inline constexpr double MAX_CPU_THRESHOLD = 100.0; + static inline constexpr double MAX_CPU_RUNNING_FACTOR = 10.0; static inline constexpr int MAX_UPDATE_RATE_MS = 60 * 60 * 1000; static inline constexpr int MAX_SLEEP_TIMEOUT_SEC = 30 * 24 * 60 * 60; static inline constexpr int MIN_OFFSET = -16000; @@ -138,9 +139,11 @@ namespace bongocat::config { static inline constexpr auto MIRROR_X_KEY = "mirror_x"; static inline constexpr auto MIRROR_Y_KEY = "mirror_y"; static inline constexpr auto RANDOM_KEY = "random"; + static inline constexpr auto RANDOM_ON_RELOAD_KEY = "random_on_reload"; static inline constexpr auto ENABLE_ANTIALIASING_KEY = "enable_antialiasing"; static inline constexpr auto UPDATE_RATE_KEY = "update_rate"; static inline constexpr auto CPU_THRESHOLD_KEY = "cpu_threshold"; + static inline constexpr auto CPU_RUNNING_FACTOR_KEY = "cpu_running_factor"; static inline constexpr auto MOVEMENT_RADIUS_KEY = "movement_radius"; static inline constexpr auto ENABLE_MOVEMENT_DEBUG_KEY = "enable_movement_debug"; static inline constexpr auto MOVEMENT_SPEED_KEY = "movement_speed"; @@ -164,6 +167,9 @@ namespace bongocat::config { static inline constexpr auto CUSTOM_START_MOVING_FRAMES_KEY = "custom_start_moving_frames"; static inline constexpr auto CUSTOM_MOVING_FRAMES_KEY = "custom_moving_frames"; static inline constexpr auto CUSTOM_END_MOVING_FRAMES_KEY = "custom_end_moving_frames"; + static inline constexpr auto CUSTOM_START_RUNNING_FRAMES_KEY = "custom_start_running_frames"; + static inline constexpr auto CUSTOM_RUNNING_FRAMES_KEY = "custom_running_frames"; + static inline constexpr auto CUSTOM_END_RUNNING_FRAMES_KEY = "custom_end_running_frames"; static inline constexpr auto CUSTOM_TOGGLE_WRITING_FRAMES_KEY = "custom_toggle_writing_frames"; static inline constexpr auto CUSTOM_TOGGLE_WRITING_FRAMES_RANDOM_KEY = "custom_toggle_writing_frames_random"; @@ -184,6 +190,10 @@ namespace bongocat::config { static inline constexpr auto CUSTOM_START_MOVING_ROW_KEY = "custom_start_moving_row"; static inline constexpr auto CUSTOM_MOVING_ROW_KEY = "custom_moving_row"; static inline constexpr auto CUSTOM_END_MOVING_ROW_KEY = "custom_end_moving_row"; + static inline constexpr auto CUSTOM_START_RUNNING_ROW_KEY = "custom_start_running_row"; + static inline constexpr auto CUSTOM_RUNNING_ROW_KEY = "custom_running_row"; + static inline constexpr auto CUSTOM_END_RUNNING_ROW_KEY = "custom_end_running_row"; + static inline constexpr auto CUSTOM_ROWS_KEY = "custom_rows"; static inline constexpr size_t KEY_BUF = 256; static inline constexpr size_t VALUE_BUF = PATH_MAX + 256; // max value + comment @@ -266,6 +276,7 @@ namespace bongocat::config { ret |= config_clamp_int(config.update_rate_ms, 0, MAX_UPDATE_RATE_MS, UPDATE_RATE_KEY); ret |= config_clamp_double(config.cpu_threshold, 0, MAX_CPU_THRESHOLD, CPU_THRESHOLD_KEY); + ret |= config_clamp_double(config.cpu_running_factor, 0, MAX_CPU_RUNNING_FACTOR, CPU_RUNNING_FACTOR_KEY); return ret; } @@ -307,6 +318,10 @@ namespace bongocat::config { ret |= config_clamp_int(config.custom_sprite_sheet_settings.moving_frames, MIN_CUSTOM_FRAMES, MAX_CUSTOM_FRAMES, CUSTOM_MOVING_FRAMES_KEY); ret |= config_clamp_int(config.custom_sprite_sheet_settings.end_moving_frames, MIN_CUSTOM_FRAMES, MAX_CUSTOM_FRAMES, CUSTOM_END_MOVING_FRAMES_KEY); + ret |= config_clamp_int(config.custom_sprite_sheet_settings.start_running_frames, MIN_CUSTOM_FRAMES, MAX_CUSTOM_FRAMES, CUSTOM_START_RUNNING_FRAMES_KEY); + ret |= config_clamp_int(config.custom_sprite_sheet_settings.running_frames, MIN_CUSTOM_FRAMES, MAX_CUSTOM_FRAMES, CUSTOM_RUNNING_FRAMES_KEY); + ret |= config_clamp_int(config.custom_sprite_sheet_settings.end_running_frames, MIN_CUSTOM_FRAMES, MAX_CUSTOM_FRAMES, CUSTOM_END_RUNNING_FRAMES_KEY); + // clamp rows if (config.custom_sprite_sheet_settings.idle_row_index >= 0) { ret |= config_clamp_int(config.custom_sprite_sheet_settings.idle_row_index, MIN_CUSTOM_ROWS, MAX_CUSTOM_ROWS, CUSTOM_IDLE_ROW_KEY); @@ -353,6 +368,18 @@ namespace bongocat::config { if (config.custom_sprite_sheet_settings.end_moving_row_index >= 0) { ret |= config_clamp_int(config.custom_sprite_sheet_settings.end_moving_row_index, MIN_CUSTOM_ROWS, MAX_CUSTOM_ROWS, CUSTOM_END_MOVING_ROW_KEY); } + if (config.custom_sprite_sheet_settings.start_running_row_index >= 0) { + ret |= config_clamp_int(config.custom_sprite_sheet_settings.start_running_row_index, MIN_CUSTOM_ROWS, MAX_CUSTOM_ROWS, CUSTOM_START_RUNNING_ROW_KEY); + } + if (config.custom_sprite_sheet_settings.running_row_index >= 0) { + ret |= config_clamp_int(config.custom_sprite_sheet_settings.running_row_index, MIN_CUSTOM_ROWS, MAX_CUSTOM_ROWS, CUSTOM_RUNNING_ROW_KEY); + } + if (config.custom_sprite_sheet_settings.end_running_row_index >= 0) { + ret |= config_clamp_int(config.custom_sprite_sheet_settings.end_running_row_index, MIN_CUSTOM_ROWS, MAX_CUSTOM_ROWS, CUSTOM_END_RUNNING_ROW_KEY); + } + if (config.custom_sprite_sheet_settings.rows >= 0) { + ret |= config_clamp_int(config.custom_sprite_sheet_settings.rows, 1, MAX_CUSTOM_ROWS, CUSTOM_ROWS_KEY); + } const int sprite_sheet_cols = get_custom_animation_settings_max_cols(config.custom_sprite_sheet_settings); @@ -385,6 +412,10 @@ namespace bongocat::config { ret |= config_validate_max_int(config.custom_sprite_sheet_settings.moving_frames, sprite_sheet_cols, CUSTOM_MOVING_FRAMES_KEY); ret |= config_validate_max_int(config.custom_sprite_sheet_settings.end_moving_frames, sprite_sheet_cols, CUSTOM_END_MOVING_FRAMES_KEY); + ret |= config_validate_max_int(config.custom_sprite_sheet_settings.start_running_frames, sprite_sheet_cols, CUSTOM_START_RUNNING_FRAMES_KEY); + ret |= config_validate_max_int(config.custom_sprite_sheet_settings.running_frames, sprite_sheet_cols, CUSTOM_RUNNING_FRAMES_KEY); + ret |= config_validate_max_int(config.custom_sprite_sheet_settings.end_running_frames, sprite_sheet_cols, CUSTOM_END_RUNNING_FRAMES_KEY); + // validate rows if (sprite_sheet_rows > 0) { if (config.custom_sprite_sheet_settings.idle_row_index >= 0) { @@ -432,9 +463,20 @@ namespace bongocat::config { if (config.custom_sprite_sheet_settings.end_moving_row_index >= 0) { ret |= config_validate_max_int(config.custom_sprite_sheet_settings.end_moving_row_index, sprite_sheet_rows-1, CUSTOM_END_MOVING_ROW_KEY); } + if (config.custom_sprite_sheet_settings.start_running_row_index >= 0) { + ret |= config_validate_max_int(config.custom_sprite_sheet_settings.start_running_row_index, sprite_sheet_rows-1, CUSTOM_START_RUNNING_ROW_KEY); + } + if (config.custom_sprite_sheet_settings.running_row_index >= 0) { + ret |= config_validate_max_int(config.custom_sprite_sheet_settings.running_row_index, sprite_sheet_rows-1, CUSTOM_RUNNING_ROW_KEY); + } + if (config.custom_sprite_sheet_settings.end_running_row_index >= 0) { + ret |= config_validate_max_int(config.custom_sprite_sheet_settings.end_running_row_index, sprite_sheet_rows-1, CUSTOM_END_RUNNING_ROW_KEY); + } } else { - BONGOCAT_LOG_WARNING("custom sprite sheet has no rows"); - ret |= (1u << 4); + if (config.custom_sprite_sheet_settings.rows <= 0) { + BONGOCAT_LOG_WARNING("custom sprite sheet has no rows"); + ret |= (1u << 4); + } } @@ -715,6 +757,7 @@ namespace bongocat::config { config.mirror_x = config.mirror_x ? 1 : 0; config.mirror_y = config.mirror_y ? 1 : 0; config.randomize_index = config.randomize_index ? 1 : 0; + config.randomize_on_reload = config.randomize_on_reload ? 1 : 0; config.enable_antialiasing = config.enable_antialiasing ? 1 : 0; config.enable_movement_debug = config.enable_movement_debug ? 1 : 0; @@ -804,7 +847,12 @@ namespace bongocat::config { } static bongocat_error_t config_parse_integer_key(config_t& config, const char *key, const char *value) { - const int int_value = static_cast(strtol(value, nullptr, 10)); + errno = 0; + char* endptr_int = nullptr; + const int int_value = static_cast(strtol(value, &endptr_int, 10)); + if (errno != 0 || endptr_int == value || *endptr_int != '\0') { + return bongocat_error_t::BONGOCAT_ERROR_INVALID_PARAM; + } if (strcmp(key, CAT_X_OFFSET_KEY) == 0) { config.cat_x_offset = int_value; @@ -856,10 +904,14 @@ namespace bongocat::config { config.input_fps = int_value; } else if (strcmp(key, RANDOM_KEY) == 0) { config.randomize_index = int_value; + } else if (strcmp(key, RANDOM_ON_RELOAD_KEY) == 0) { + config.randomize_on_reload = int_value; } else if (strcmp(key, UPDATE_RATE_KEY) == 0) { config.update_rate_ms = int_value; } else if (strcmp(key, CPU_THRESHOLD_KEY) == 0) { config.cpu_threshold = int_value; + } else if (strcmp(key, CPU_RUNNING_FACTOR_KEY) == 0) { + config.cpu_running_factor = int_value; } else if (strcmp(key, MOVEMENT_RADIUS_KEY) == 0) { config.movement_radius = int_value; } else if (strcmp(key, ENABLE_MOVEMENT_DEBUG_KEY) == 0) { @@ -898,6 +950,12 @@ namespace bongocat::config { config.custom_sprite_sheet_settings.moving_frames = int_value; } else if (strcmp(key, CUSTOM_END_MOVING_FRAMES_KEY) == 0) { config.custom_sprite_sheet_settings.end_moving_frames = int_value; + } else if (strcmp(key, CUSTOM_START_RUNNING_FRAMES_KEY) == 0) { + config.custom_sprite_sheet_settings.start_running_frames = int_value; + } else if (strcmp(key, CUSTOM_RUNNING_FRAMES_KEY) == 0) { + config.custom_sprite_sheet_settings.running_frames = int_value; + } else if (strcmp(key, CUSTOM_END_RUNNING_FRAMES_KEY) == 0) { + config.custom_sprite_sheet_settings.end_running_frames = int_value; } else if (strcmp(key, CUSTOM_TOGGLE_WRITING_FRAMES_KEY) == 0) { config.custom_sprite_sheet_settings.feature_toggle_writing_frames = int_value; } else if (strcmp(key, CUSTOM_TOGGLE_WRITING_FRAMES_RANDOM_KEY) == 0) { @@ -934,6 +992,33 @@ namespace bongocat::config { config.custom_sprite_sheet_settings.moving_row_index = int_value - 1; } else if (strcmp(key, CUSTOM_END_MOVING_ROW_KEY) == 0) { config.custom_sprite_sheet_settings.end_moving_row_index = int_value - 1; + } else if (strcmp(key, CUSTOM_START_RUNNING_ROW_KEY) == 0) { + config.custom_sprite_sheet_settings.start_running_row_index = int_value - 1; + } else if (strcmp(key, CUSTOM_RUNNING_ROW_KEY) == 0) { + config.custom_sprite_sheet_settings.running_row_index = int_value - 1; + } else if (strcmp(key, CUSTOM_END_RUNNING_ROW_KEY) == 0) { + config.custom_sprite_sheet_settings.end_running_row_index = int_value - 1; + } else if (strcmp(key, CUSTOM_ROWS_KEY) == 0) { + config.custom_sprite_sheet_settings.rows = int_value; + } else { + return bongocat_error_t::BONGOCAT_ERROR_INVALID_PARAM; // Unknown key + } + + return bongocat_error_t::BONGOCAT_SUCCESS; + } + + static bongocat_error_t config_parse_double_key(config_t& config, const char *key, const char *value) { + errno = 0; + char* endptr_double = nullptr; + const double double_value = strtod(value, &endptr_double); + if (errno != 0 || endptr_double == value || *endptr_double != '\0') { + return bongocat_error_t::BONGOCAT_ERROR_INVALID_PARAM; + } + + if (strcmp(key, CPU_THRESHOLD_KEY) == 0) { + config.cpu_threshold = double_value; + } else if (strcmp(key, CPU_RUNNING_FACTOR_KEY) == 0) { + config.cpu_running_factor = double_value; } else { return bongocat_error_t::BONGOCAT_ERROR_INVALID_PARAM; // Unknown key } @@ -1306,6 +1391,10 @@ namespace bongocat::config { if (config_parse_integer_key(config, key, value) == bongocat_error_t::BONGOCAT_SUCCESS) { return bongocat_error_t::BONGOCAT_SUCCESS; } + // Try double keys first + if (config_parse_double_key(config, key, value) == bongocat_error_t::BONGOCAT_SUCCESS) { + return bongocat_error_t::BONGOCAT_SUCCESS; + } // Try enum keys if (config_parse_enum_key(config, key, value) == bongocat_error_t::BONGOCAT_SUCCESS) { @@ -1461,6 +1550,7 @@ namespace bongocat::config { cfg.idle_animation = 0; cfg.input_fps = 0; // when 0 fallback to fps cfg.randomize_index = 0; + cfg.randomize_on_reload = 0; cfg.screen_width = 0; cfg.custom_sprite_sheet_settings = {}; cfg._keep_old_animation_index = false; diff --git a/src/core/main.cpp b/src/core/main.cpp index b0bdd852..7dd7df90 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -369,7 +369,7 @@ namespace bongocat { if (old_config.randomize_index && new_config.randomize_index && old_config.animation_sprite_sheet_layout == new_config.animation_sprite_sheet_layout && old_config.animation_dm_set == new_config.animation_dm_set) { - new_config._keep_old_animation_index = true; + new_config._keep_old_animation_index = !new_config.randomize_on_reload; } // If successful, check if input devices changed before updating config devices_changed = config_devices_changed(old_config, new_config); diff --git a/src/graphics/animation.cpp b/src/graphics/animation.cpp index c82837c5..702db324 100644 --- a/src/graphics/animation.cpp +++ b/src/graphics/animation.cpp @@ -97,6 +97,9 @@ namespace bongocat::animation { StartMoving, Moving, EndMoving, + StartRunning, + Running, + EndRunning, }; struct animation_state_t { @@ -165,22 +168,27 @@ namespace bongocat::animation { bool process_idle_animation{false}; bool process_movement_animation{false}; bool process_working_animation{false}; + bool process_running_animation{false}; bool go_next_frame{false}; + bool go_next_frame_running{false}; bool release_frame_for_non_idle{false}; // current animation bool is_writing{false}; bool is_moving{false}; bool is_working{false}; + bool is_running{false}; bool continue_writing{false}; }; static anim_conditions_t get_anim_conditions([[maybe_unused]] const animation_context_t& ctx, const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, const animation_state_t& current_state, const animation_trigger_t& trigger, const config::config_t& current_config) { assert(input.shm != nullptr); const auto& input_shm = *input.shm; + const auto& update_shm = *upd.shm; const platform::timestamp_ms_t last_key_pressed_timestamp = input_shm.last_key_pressed_timestamp; const platform::timestamp_ms_t fps_ms = 1000/current_config.fps; @@ -199,15 +207,28 @@ namespace bongocat::animation { const bool process_movement_animation_by_fps = current_config.movement_speed > 0 && current_config.movement_radius > 0 && current_config.animation_speed_ms <= 0 && current_state.frame_delta_ms_counter > fps_ms; const bool process_movement_animation = process_movement_animation_by_animation_speed || process_movement_animation_by_fps; - const bool process_working_animation_by_animation_speed = current_config.cpu_threshold > 0 && current_config.update_rate_ms > 0 && current_config.animation_speed_ms > 0 && current_state.frame_delta_ms_counter > current_config.animation_speed_ms; - const bool process_working_animation_by_fps = current_config.cpu_threshold > 0 && current_config.update_rate_ms > 0 && current_config.animation_speed_ms <= 0 && current_state.frame_delta_ms_counter > fps_ms; + const bool process_working_animation_by_animation_speed = current_config.cpu_running_factor < 1.0 && current_config.cpu_threshold > 0 && current_config.update_rate_ms > 0 && current_config.animation_speed_ms > 0 && current_state.frame_delta_ms_counter > current_config.animation_speed_ms; + const bool process_working_animation_by_fps = current_config.cpu_running_factor < 1.0 && current_config.cpu_threshold > 0 && current_config.update_rate_ms > 0 && current_config.animation_speed_ms <= 0 && current_state.frame_delta_ms_counter > fps_ms; const bool process_working_animation = process_working_animation_by_animation_speed || process_working_animation_by_fps; + const bool process_running_animation_by_animation_speed = current_config.cpu_running_factor >= 1.0 && current_config.cpu_threshold > 0 && current_config.update_rate_ms > 0 && current_config.animation_speed_ms > 0 && current_state.frame_delta_ms_counter > current_config.animation_speed_ms; + const bool process_running_animation_by_fps = current_config.cpu_running_factor >= 1.0 && current_config.cpu_threshold > 0 && current_config.update_rate_ms > 0 && current_config.animation_speed_ms <= 0 && current_state.frame_delta_ms_counter > fps_ms; + const bool process_running_animation = process_running_animation_by_animation_speed || process_running_animation_by_fps; + const bool process_movement = current_config.movement_speed > 0 && current_config.movement_radius > 0 && current_state.update_delta_ms_counter > current_config.animation_speed_ms; const bool is_writing = current_state.row_state == animation_state_row_t::StartWriting || current_state.row_state == animation_state_row_t::Writing || current_state.row_state == animation_state_row_t::EndWriting; const bool release_frame_after_press = !any_key_pressed && current_state.hold_frame_ms > current_config.keypress_duration_ms; + const bool is_running = current_state.row_state == animation_state_row_t::StartRunning || current_state.row_state == animation_state_row_t::Running; + double running_animation_speed_factor = 1.0; + if (current_config.cpu_running_factor >= 1.0 && update_shm.cpu_active) { + running_animation_speed_factor = update_shm.avg_cpu_usage > 0 ? 1.0 / ((update_shm.avg_cpu_usage/100.0) * (current_config.cpu_running_factor)) : 0; + } + const double running_animation_speed_ms = running_animation_speed_factor * current_config.animation_speed_ms; + const bool go_next_frame_running = current_config.cpu_running_factor >= 1.0 && ((running_animation_speed_ms > 0 && static_cast(current_state.frame_delta_ms_counter) > running_animation_speed_ms) || (running_animation_speed_ms <= 0 && (running_animation_speed_factor*static_cast(current_state.frame_delta_ms_counter)) > static_cast(fps_ms))); + + assert(SMALL_MAX_DISTANCE_PER_MOVEMENT_PART > 0); assert(MAX_DISTANCE_PER_MOVEMENT_PART > 0); @@ -228,12 +249,15 @@ namespace bongocat::animation { .process_idle_animation = process_idle_animation, .process_movement_animation = process_movement_animation, .process_working_animation = process_working_animation, + .process_running_animation = process_running_animation, .go_next_frame = go_next_frame, - .release_frame_for_non_idle = release_frame_for_non_idle || go_next_frame, + .go_next_frame_running = go_next_frame_running, + .release_frame_for_non_idle = release_frame_for_non_idle || go_next_frame || (is_running && go_next_frame_running), .is_writing = is_writing, .is_moving = current_state.row_state == animation_state_row_t::StartMoving || current_state.row_state == animation_state_row_t::Moving || current_state.row_state == animation_state_row_t::EndMoving, .is_working = current_state.row_state == animation_state_row_t::StartWorking || current_state.row_state == animation_state_row_t::Working, + .is_running = is_running, .continue_writing = ((!any_key_pressed && current_state.hold_frame_ms < current_config.keypress_duration_ms) || !release_frame_after_press) && is_writing, }; } @@ -330,6 +354,11 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: new_animation_result.sprite_sheet_col = current_frames.animations.moving[new_state.animations_index]; break; + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: + new_animation_result.sprite_sheet_col = current_frames.animations.running[new_state.animations_index]; + break; } return ret; } @@ -392,6 +421,11 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: new_animation_result.sprite_sheet_col = current_frames.animations.moving[new_state.animations_index]; break; + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: + new_animation_result.sprite_sheet_col = current_frames.animations.running[new_state.animations_index]; + break; } return ret; } @@ -502,7 +536,9 @@ namespace bongocat::animation { current_state, current_frames, current_config); } - static anim_next_frame_result_t anim_bongocat_idle_next_frame(animation_context_t& ctx, const platform::input::input_context_t& input, + static anim_next_frame_result_t anim_bongocat_idle_next_frame(animation_context_t& ctx, + const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, animation_state_t& state, const anim_handle_key_press_result_t& trigger_result) { using namespace assets; // read-only config @@ -524,7 +560,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger_result.trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger_result.trigger, current_config); // Idle Animation const bool stop_writing = conditions.is_writing && conditions.release_frame_after_press; @@ -699,6 +735,7 @@ namespace bongocat::animation { static anim_next_frame_result_t anim_bongocat_key_pressed_next_frame(animation_context_t& ctx, animation_state_t& state, const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, const animation_trigger_t& trigger) { using namespace assets; @@ -720,7 +757,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger, current_config); /// @TODO: use state machine for animation (states) @@ -841,6 +878,11 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: new_animation_result.sprite_sheet_col = current_frames.animations.moving[new_state.animations_index]; break; + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: + new_animation_result.sprite_sheet_col = current_frames.animations.running[new_state.animations_index]; + break; } return ret; } @@ -901,6 +943,11 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: new_animation_result.sprite_sheet_col = current_frames.animations.moving[new_state.animations_index]; break; + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: + new_animation_result.sprite_sheet_col = current_frames.animations.running[new_state.animations_index]; + break; } return ret; } @@ -1054,6 +1101,35 @@ namespace bongocat::animation { } } break; + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + if (current_frames.frames.movement_1.valid) { + new_animation_result.sprite_sheet_col = current_frames.frames.movement_1.col; + } else { + // toggle frame + if (new_animation_result.sprite_sheet_col == DM_FRAME_IDLE1) { + new_animation_result.sprite_sheet_col = DM_FRAME_IDLE2; + } else if (new_animation_result.sprite_sheet_col == DM_FRAME_IDLE2) { + new_animation_result.sprite_sheet_col = DM_FRAME_IDLE1; + } else { + new_animation_result.sprite_sheet_col = ctx._rng.range(0, 100) <= 50 ? DM_FRAME_IDLE1 : DM_FRAME_IDLE2; + } + } + break; + case animation_state_row_t::EndRunning: + if (current_frames.frames.movement_2.valid) { + new_animation_result.sprite_sheet_col = current_frames.frames.movement_2.col; + } else { + // toggle frame + if (new_animation_result.sprite_sheet_col == DM_FRAME_IDLE1) { + new_animation_result.sprite_sheet_col = DM_FRAME_IDLE2; + } else if (new_animation_result.sprite_sheet_col == DM_FRAME_IDLE2) { + new_animation_result.sprite_sheet_col = DM_FRAME_IDLE1; + } else { + new_animation_result.sprite_sheet_col = ctx._rng.range(0, 100) <= 50 ? DM_FRAME_IDLE1 : DM_FRAME_IDLE2; + } + } + break; } return ret; } @@ -1083,6 +1159,7 @@ namespace bongocat::animation { static anim_dm_process_animation_result_t anim_dm_handle_movement(animation_context_t& ctx, const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, animation_player_result_t& new_animation_result, animation_state_t& new_state, const anim_handle_key_press_result_t& trigger_result, @@ -1094,7 +1171,7 @@ namespace bongocat::animation { assert(ctx.shm != nullptr); animation_shared_memory_t& anim_shm = *ctx.shm; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger_result.trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger_result.trigger, current_config); anim_dm_process_animation_result_t ret {.row_state = new_state.row_state, .status = anim_dm_process_animation_result_status_t::None}; if (!conditions.is_writing && current_config.movement_speed > 0 && current_config.movement_radius > 0 && current_config.fps > 0) { @@ -1303,7 +1380,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger_result.trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger_result.trigger, current_config); /// @TODO: make animation state machine ? @@ -1380,16 +1457,16 @@ namespace bongocat::animation { current_state, current_frames, current_config); } } - } else if (conditions.is_working && current_state.row_state != animation_state_row_t::EndMoving && !update_shm.cpu_active) { + } else if (conditions.is_working && current_state.row_state != animation_state_row_t::EndWorking && !update_shm.cpu_active) { // Cancel Working if (conditions.process_idle_animation) { // back to idle - anim_dm_start_or_process_animation(ctx, animation_state_row_t::EndMoving, + anim_dm_start_or_process_animation(ctx, animation_state_row_t::EndWorking, new_animation_result, new_state, current_state, current_frames, current_config); } else { if (conditions.release_frame_for_non_idle) { - anim_dm_show_single_frame(ctx, animation_state_row_t::EndMoving, + anim_dm_show_single_frame(ctx, animation_state_row_t::EndWorking, new_animation_result, new_state, current_state, current_frames, current_config); } @@ -1414,7 +1491,7 @@ namespace bongocat::animation { /// @TODO: move moving handle into own anim_handle_moving_animation // Move Animation - anim_dm_handle_movement(ctx, input, new_animation_result, new_state, trigger_result, current_state, current_frames, current_config); + anim_dm_handle_movement(ctx, input, upd, new_animation_result, new_state, trigger_result, current_state, current_frames, current_config); const bool is_sleeping_time = current_config.enable_scheduled_sleep && is_sleep_time(current_config); @@ -1578,7 +1655,9 @@ namespace bongocat::animation { current_config); } - static anim_next_frame_result_t anim_dm_key_pressed_next_frame(animation_context_t& ctx, const platform::input::input_context_t& input, + static anim_next_frame_result_t anim_dm_key_pressed_next_frame(animation_context_t& ctx, + const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, animation_state_t& state, const animation_trigger_t& trigger) { using namespace assets; @@ -1600,7 +1679,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger, current_config); bool show_happy = false; if (input_shm.kpm > 0) { @@ -1725,7 +1804,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger, current_config); /// @TODO: use state machine for animation (states) @@ -1890,6 +1969,11 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: new_animation_result.sprite_sheet_col = current_frames.animations.moving[new_state.animations_index]; break; + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: + new_animation_result.sprite_sheet_col = current_frames.animations.moving[new_state.animations_index]; + break; } return ret; } @@ -1948,7 +2032,12 @@ namespace bongocat::animation { case animation_state_row_t::StartMoving: case animation_state_row_t::Moving: case animation_state_row_t::EndMoving: - new_animation_result.sprite_sheet_col = current_frames.animations.moving[new_state.animations_index]; + new_animation_result.sprite_sheet_col = current_frames.animations.running[new_state.animations_index]; + break; + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: + new_animation_result.sprite_sheet_col = current_frames.animations.running[new_state.animations_index]; break; } return ret; @@ -2067,7 +2156,9 @@ namespace bongocat::animation { current_state, current_frames, current_config); } - static anim_next_frame_result_t anim_pkmn_idle_next_frame(animation_context_t& ctx, [[maybe_unused]] const platform::input::input_context_t& input, + static anim_next_frame_result_t anim_pkmn_idle_next_frame(animation_context_t& ctx, + [[maybe_unused]] const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, animation_state_t& state, const anim_handle_key_press_result_t& trigger_result) { using namespace assets; // read-only config @@ -2087,7 +2178,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger_result.trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger_result.trigger, current_config); // Idle Animation const bool stop_writing = conditions.is_writing && conditions.release_frame_after_press; @@ -2144,7 +2235,9 @@ namespace bongocat::animation { current_config); } - static anim_next_frame_result_t anim_pkmn_key_pressed_next_frame(animation_context_t& ctx, [[maybe_unused]] const platform::input::input_context_t& input, + static anim_next_frame_result_t anim_pkmn_key_pressed_next_frame(animation_context_t& ctx, + [[maybe_unused]] const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, animation_state_t& state, const animation_trigger_t& trigger) { using namespace assets; @@ -2167,7 +2260,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger, current_config); // in Writing mode/start writing if (!conditions.is_writing) { @@ -2272,6 +2365,15 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: section = current_frames.end_moving.valid ? ¤t_frames.end_moving : nullptr; break; + case animation_state_row_t::StartRunning: + section = current_frames.start_running.valid ? ¤t_frames.start_running : nullptr; + break; + case animation_state_row_t::Running: + section = current_frames.running.valid ? ¤t_frames.running : nullptr; + break; + case animation_state_row_t::EndRunning: + section = current_frames.end_running.valid ? ¤t_frames.end_running : nullptr; + break; } anim_ms_agent_process_animation_result_t ret {.row_state = new_state.row_state, .status = anim_ms_agent_process_animation_result_status_t::None}; @@ -2366,6 +2468,15 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: section = current_frames.end_moving.valid ? ¤t_frames.end_moving : nullptr; break; + case animation_state_row_t::StartRunning: + section = current_frames.start_running.valid ? ¤t_frames.start_running : nullptr; + break; + case animation_state_row_t::Running: + section = current_frames.running.valid ? ¤t_frames.running : nullptr; + break; + case animation_state_row_t::EndRunning: + section = current_frames.end_running.valid ? ¤t_frames.end_running : nullptr; + break; } anim_ms_agent_process_animation_result_t ret {.row_state = new_state.row_state, .status = anim_ms_agent_process_animation_result_status_t::None}; @@ -2432,7 +2543,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger_result.trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger_result.trigger, current_config); /// @TODO: make animation fsm @@ -2449,6 +2560,9 @@ namespace bongocat::animation { case animation_state_row_t::StartMoving: case animation_state_row_t::Moving: case animation_state_row_t::EndMoving: + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: // not supported, same as idle break; case animation_state_row_t::StartWorking: @@ -2633,6 +2747,7 @@ namespace bongocat::animation { static anim_next_frame_result_t anim_ms_agent_key_pressed_next_frame(animation_context_t& ctx, animation_state_t& state, [[maybe_unused]] const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, [[maybe_unused]] const animation_trigger_t& trigger) { using namespace assets; @@ -2660,6 +2775,9 @@ namespace bongocat::animation { case animation_state_row_t::StartMoving: case animation_state_row_t::Moving: case animation_state_row_t::EndMoving: + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: // moving not supported case animation_state_row_t::Test: case animation_state_row_t::Happy: @@ -2699,7 +2817,7 @@ namespace bongocat::animation { case animation_state_row_t::StartWorking: case animation_state_row_t::Working: case animation_state_row_t::EndWorking: - // start end writing and process animation in anim_ms_pet_idle_next_frame + // start end writing and process animation in anim_ms_agent_idle_next_frame break; } @@ -2809,6 +2927,15 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: section = current_frames.end_moving.valid ? ¤t_frames.end_moving : nullptr; break; + case animation_state_row_t::StartRunning: + section = current_frames.start_running.valid ? ¤t_frames.start_running : nullptr; + break; + case animation_state_row_t::Running: + section = current_frames.running.valid ? ¤t_frames.running : nullptr; + break; + case animation_state_row_t::EndRunning: + section = current_frames.end_running.valid ? ¤t_frames.end_running : nullptr; + break; } anim_custom_process_animation_result_t ret {.row_state = new_state.row_state, .status = anim_custom_process_animation_result_status_t::None}; @@ -2893,6 +3020,15 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: section = current_frames.end_moving.valid ? ¤t_frames.end_moving : nullptr; break; + case animation_state_row_t::StartRunning: + section = current_frames.start_running.valid ? ¤t_frames.start_running : nullptr; + break; + case animation_state_row_t::Running: + section = current_frames.running.valid ? ¤t_frames.running : nullptr; + break; + case animation_state_row_t::EndRunning: + section = current_frames.end_running.valid ? ¤t_frames.end_running : nullptr; + break; } anim_custom_process_animation_result_t ret {.row_state = new_state.row_state, .status = anim_custom_process_animation_result_status_t::None}; @@ -2989,6 +3125,15 @@ namespace bongocat::animation { case animation_state_row_t::EndMoving: section = current_frames.end_moving.valid ? ¤t_frames.end_moving : nullptr; break; + case animation_state_row_t::StartRunning: + section = current_frames.start_running.valid ? ¤t_frames.start_running : nullptr; + break; + case animation_state_row_t::Running: + section = current_frames.running.valid ? ¤t_frames.running : nullptr; + break; + case animation_state_row_t::EndRunning: + section = current_frames.end_running.valid ? ¤t_frames.end_running : nullptr; + break; } } @@ -3073,6 +3218,7 @@ namespace bongocat::animation { static anim_custom_process_animation_result_t anim_custom_handle_movement(animation_context_t& ctx, const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, animation_player_result_t& new_animation_result, animation_state_t& new_state, const anim_handle_key_press_result_t& trigger_result, @@ -3084,7 +3230,7 @@ namespace bongocat::animation { assert(ctx.shm != nullptr); animation_shared_memory_t& anim_shm = *ctx.shm; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger_result.trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger_result.trigger, current_config); anim_custom_process_animation_result_t ret {.row_state = new_state.row_state, .status = anim_custom_process_animation_result_status_t::None}; if (!conditions.is_writing && current_config.movement_speed > 0 && current_config.movement_radius > 0 && current_config.fps > 0) { @@ -3293,7 +3439,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger_result.trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger_result.trigger, current_config); /// @TODO: make animation fsm @@ -3340,7 +3486,7 @@ namespace bongocat::animation { case animation_state_row_t::Moving: case animation_state_row_t::EndMoving: if (current_frames.feature_moving) { - anim_custom_handle_movement(ctx, input, new_animation_result, new_state, trigger_result, current_state, current_frames, current_config); + anim_custom_handle_movement(ctx, input, upd, new_animation_result, new_state, trigger_result, current_state, current_frames, current_config); } break; case animation_state_row_t::StartWorking: @@ -3408,7 +3554,7 @@ namespace bongocat::animation { if (current_frames.feature_moving) { // Move Animation - anim_custom_handle_movement(ctx, input, new_animation_result, new_state, trigger_result, current_state, current_frames, current_config); + anim_custom_handle_movement(ctx, input, upd, new_animation_result, new_state, trigger_result, current_state, current_frames, current_config); } if (current_frames.feature_sleep || current_frames.feature_boring) { @@ -3660,6 +3806,49 @@ namespace bongocat::animation { } } break; + case animation_state_row_t::StartRunning: + if (current_frames.feature_running) { + if (conditions.go_next_frame_running) { + anim_custom_start_or_process_animation(ctx, animation_state_row_t::Running, animation_state_row_t::EndRunning, + new_animation_result, new_state, + current_state, current_frames, current_config); + } + } else { + anim_custom_restart_animation(ctx, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + } + break; + case animation_state_row_t::Running: + if (current_frames.feature_running) { + if (conditions.go_next_frame_running) { + if (update_shm.cpu_active) { + anim_custom_process_animation(new_animation_result, new_state, current_state, current_frames); + } else { + anim_custom_start_or_process_animation(ctx, animation_state_row_t::EndRunning, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + } + } + } else { + anim_custom_restart_animation(ctx, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + } + break; + case animation_state_row_t::EndRunning: + if (current_frames.feature_running) { + if (conditions.go_next_frame_running) { + anim_custom_start_or_process_animation(ctx, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + } + } else { + anim_custom_restart_animation(ctx, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + } + break; } return anim_update_animation_state(anim_shm, state, @@ -3670,6 +3859,7 @@ namespace bongocat::animation { static anim_next_frame_result_t anim_custom_key_pressed_next_frame(animation_context_t& ctx, animation_state_t& state, [[maybe_unused]] const platform::input::input_context_t& input, + [[maybe_unused]] const platform::update::update_context_t& upd, [[maybe_unused]] const animation_trigger_t& trigger) { using namespace assets; @@ -3690,7 +3880,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger, current_config); bool show_happy = false; if (current_frames.feature_writing_happy) { @@ -3785,7 +3975,12 @@ namespace bongocat::animation { case animation_state_row_t::StartWorking: case animation_state_row_t::Working: case animation_state_row_t::EndWorking: - // start end writing and process animation in anim_custom_idle_next_frame + // start end working and process animation in anim_custom_idle_next_frame + break; + case animation_state_row_t::StartRunning: + case animation_state_row_t::Running: + case animation_state_row_t::EndRunning: + // start end running and process animation in anim_custom_idle_next_frame break; } @@ -3822,7 +4017,7 @@ namespace bongocat::animation { auto new_animation_result = anim_shm.animation_player_result; auto new_state = state; - const auto conditions = get_anim_conditions(ctx, input, current_state, trigger, current_config); + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger, current_config); /// @TODO: use state machine for animation (states) @@ -3892,6 +4087,104 @@ namespace bongocat::animation { current_animation_result, current_state, current_config); } + + static anim_next_frame_result_t anim_custom_running_next_frame(animation_context_t& ctx, + const platform::input::input_context_t& input, + const platform::update::update_context_t& upd, + animation_state_t& state, + const animation_trigger_t& trigger) { + using namespace assets; + + // read-only config + assert(ctx._local_copy_config != nullptr); + const config::config_t& current_config = *ctx._local_copy_config; + + assert(ctx.shm != nullptr); + //assert(input.shm != nullptr); + assert(upd.shm != nullptr); + animation_shared_memory_t& anim_shm = *ctx.shm; + //const auto& input_shm = *input.shm; + const auto& update_shm = *upd.shm; + const auto current_state = state; + const auto& current_animation_result = anim_shm.animation_player_result; + [[maybe_unused]] const int anim_index = anim_shm.anim_index; + //const platform::timestamp_ms_t last_key_pressed_timestamp = input_shm.last_key_pressed_timestamp; + assert(get_current_animation(ctx).type == animation_t::Type::Custom); + const auto& current_frames = get_current_animation(ctx).custom; + + auto new_animation_result = anim_shm.animation_player_result; + auto new_state = state; + + const auto conditions = get_anim_conditions(ctx, input, upd, current_state, trigger, current_config); + + /// @TODO: use state machine for animation (states) + + if (current_frames.feature_running) { + if (conditions.process_running_animation) { + // toggle frame, show running animation + const bool above_threshold = current_config.cpu_threshold >= platform::ENABLED_MIN_CPU_PERCENT && (update_shm.avg_cpu_usage >= current_config.cpu_threshold || update_shm.max_cpu_usage >= current_config.cpu_threshold); + const bool lower_threshold = current_config.cpu_threshold >= platform::ENABLED_MIN_CPU_PERCENT && (update_shm.avg_cpu_usage < current_config.cpu_threshold && update_shm.max_cpu_usage < current_config.cpu_threshold); + + if (above_threshold) { + if (current_state.row_state == animation_state_row_t::Idle || conditions.is_moving) { + anim_custom_restart_animation(ctx, animation_state_row_t::StartRunning, animation_state_row_t::Running, animation_state_row_t::EndRunning, + new_animation_result, new_state, + current_state, current_frames, current_config); + BONGOCAT_LOG_VERBOSE("Start Running: %d %d; %d%%", above_threshold, lower_threshold, update_shm.avg_cpu_usage); + } else if (conditions.is_running) { + if (update_shm.cpu_active && current_state.row_state == animation_state_row_t::StartRunning) { + anim_custom_start_or_process_animation(ctx, animation_state_row_t::Running, animation_state_row_t::EndRunning, + new_animation_result, new_state, + current_state, current_frames, current_config); + } else if (!update_shm.cpu_active && (current_state.row_state == animation_state_row_t::StartRunning || state.row_state == animation_state_row_t::Running)) { + // end running, cool down + anim_custom_start_or_process_animation(ctx, animation_state_row_t::EndRunning, animation_state_row_t::Running, + new_animation_result, new_state, + current_state, current_frames, current_config); + } else if (!update_shm.cpu_active && current_state.row_state == animation_state_row_t::EndRunning) { + // back to idle + anim_custom_start_or_process_animation(ctx, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + } + } + } else if (lower_threshold) { + // End Running + if (conditions.is_running && current_state.row_state != animation_state_row_t::EndRunning) { + if (conditions.go_next_frame_running) { + // end running, cool down + anim_custom_start_or_process_animation(ctx, animation_state_row_t::EndRunning, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + BONGOCAT_LOG_VERBOSE("Stop Running: %d %d; %d%%", above_threshold, lower_threshold, update_shm.avg_cpu_usage); + } + } + } else { + // Cancel Running + if (conditions.is_running && current_state.row_state != animation_state_row_t::EndRunning && !update_shm.cpu_active) { + // back to idle + anim_custom_start_or_process_animation(ctx, animation_state_row_t::EndRunning, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + BONGOCAT_LOG_VERBOSE("Stop Running: %d %d; %d%%", above_threshold, lower_threshold, update_shm.avg_cpu_usage); + } + } + } + } else { + // Cancel Running + if (conditions.is_running && !update_shm.cpu_active) { + // back to idle + anim_custom_start_or_process_animation(ctx, animation_state_row_t::Idle, + new_animation_result, new_state, + current_state, current_frames, current_config); + } + } + + return anim_update_animation_state(anim_shm, state, + new_animation_result, new_state, + current_animation_result, current_state, + current_config); + } #endif static anim_next_frame_result_t anim_handle_idle_animation(animation_context_t& ctx, @@ -3919,7 +4212,7 @@ namespace bongocat::animation { break; case config::config_animation_sprite_sheet_layout_t::Bongocat: { #ifdef FEATURE_BONGOCAT_EMBEDDED_ASSETS - return anim_bongocat_idle_next_frame(ctx, input, state, trigger_result); + return anim_bongocat_idle_next_frame(ctx, input, upd, state, trigger_result); #endif }break; case config::config_animation_sprite_sheet_layout_t::Dm: { @@ -3929,7 +4222,7 @@ namespace bongocat::animation { }break; case config::config_animation_sprite_sheet_layout_t::Pkmn: { #ifdef FEATURE_PKMN_EMBEDDED_ASSETS - return anim_pkmn_idle_next_frame(ctx, input, state, trigger_result); + return anim_pkmn_idle_next_frame(ctx, input, upd, state, trigger_result); #endif }break; case config::config_animation_sprite_sheet_layout_t::MsAgent: { @@ -3974,8 +4267,8 @@ namespace bongocat::animation { anim_next_frame_result_t update_frame_result{}; - // handle working animation if (has_flag(trigger.anim_cause, trigger_animation_cause_mask_t::CpuUpdate)) { + // handle working animation switch (anim_shm.anim_type) { case config::config_animation_sprite_sheet_layout_t::None: break; @@ -3997,6 +4290,24 @@ namespace bongocat::animation { case config::config_animation_sprite_sheet_layout_t::Custom: { #ifdef FEATURE_CUSTOM_SPRITE_SHEETS_ANIMATION update_frame_result = anim_custom_working_next_frame(ctx, input, upd, state, trigger); +#endif + }break; + } + // handle running animation + switch (anim_shm.anim_type) { + case config::config_animation_sprite_sheet_layout_t::None: + break; + case config::config_animation_sprite_sheet_layout_t::Bongocat: + break; + case config::config_animation_sprite_sheet_layout_t::Dm: + break; + case config::config_animation_sprite_sheet_layout_t::Pkmn: + break; + case config::config_animation_sprite_sheet_layout_t::MsAgent: + break; + case config::config_animation_sprite_sheet_layout_t::Custom: { +#ifdef FEATURE_CUSTOM_SPRITE_SHEETS_ANIMATION + update_frame_result = anim_custom_running_next_frame(ctx, input, upd, state, trigger); #endif }break; } @@ -4010,27 +4321,27 @@ namespace bongocat::animation { break; case config::config_animation_sprite_sheet_layout_t::Bongocat: { #ifdef FEATURE_BONGOCAT_EMBEDDED_ASSETS - update_frame_result = anim_bongocat_key_pressed_next_frame(ctx, state, input, trigger); + update_frame_result = anim_bongocat_key_pressed_next_frame(ctx, state, input, upd, trigger); #endif }break; case config::config_animation_sprite_sheet_layout_t::Dm: { #ifdef FEATURE_ENABLE_DM_EMBEDDED_ASSETS - update_frame_result = anim_dm_key_pressed_next_frame(ctx, input, state, trigger); + update_frame_result = anim_dm_key_pressed_next_frame(ctx, input, upd, state, trigger); #endif }break; case config::config_animation_sprite_sheet_layout_t::Pkmn: { #ifdef FEATURE_PKMN_EMBEDDED_ASSETS - update_frame_result = anim_pkmn_key_pressed_next_frame(ctx, input, state, trigger); + update_frame_result = anim_pkmn_key_pressed_next_frame(ctx, input, upd, state, trigger); #endif }break; case config::config_animation_sprite_sheet_layout_t::MsAgent: { #ifdef FEATURE_MS_AGENT_EMBEDDED_ASSETS - update_frame_result = anim_ms_agent_key_pressed_next_frame(ctx, state, input, trigger); + update_frame_result = anim_ms_agent_key_pressed_next_frame(ctx, state, input, upd, trigger); #endif }break; case config::config_animation_sprite_sheet_layout_t::Custom: { #ifdef FEATURE_CUSTOM_SPRITE_SHEETS_ANIMATION - update_frame_result = anim_custom_key_pressed_next_frame(ctx, state, input, trigger); + update_frame_result = anim_custom_key_pressed_next_frame(ctx, state, input, upd, trigger); #endif }break; } diff --git a/src/image_loader/base_dm/load_dm.cpp b/src/image_loader/base_dm/load_dm.cpp index 5faae477..62d5af56 100644 --- a/src/image_loader/base_dm/load_dm.cpp +++ b/src/image_loader/base_dm/load_dm.cpp @@ -114,6 +114,19 @@ namespace bongocat::animation { ret.animations.moving[2] = ret.frames.idle_2.col; ret.animations.moving[3] = ret.frames.idle_2.col; } + // running animation (same as moving) + if (ret.frames.movement_1.valid || ret.frames.movement_2.valid) { + ret.animations.running[0] = ret.frames.movement_1.valid ? ret.frames.movement_1.col : ret.frames.idle_1.col; + ret.animations.running[1] = ret.frames.movement_1.valid ? ret.frames.movement_1.col : ret.frames.idle_1.col; + ret.animations.running[2] = ret.frames.movement_2.valid ? ret.frames.movement_2.col : ret.frames.idle_2.col; + ret.animations.running[3] = ret.frames.movement_2.valid ? ret.frames.movement_2.col : ret.frames.idle_2.col; + } else { + // fallback + ret.animations.running[0] = ret.frames.idle_1.col; + ret.animations.running[1] = ret.frames.idle_1.col; + ret.animations.running[2] = ret.frames.idle_2.col; + ret.animations.running[3] = ret.frames.idle_2.col; + } // happy animation if (ret.frames.happy.valid) { diff --git a/src/image_loader/bongocat/load_images_bongocat.cpp b/src/image_loader/bongocat/load_images_bongocat.cpp index 947e8cd8..bf5c764f 100644 --- a/src/image_loader/bongocat/load_images_bongocat.cpp +++ b/src/image_loader/bongocat/load_images_bongocat.cpp @@ -75,6 +75,11 @@ namespace bongocat::animation { ret.animations.happy[2] = BONGOCAT_FRAME_LEFT_DOWN; ret.animations.happy[3] = BONGOCAT_FRAME_RIGHT_DOWN; + ret.animations.running[0] = BONGOCAT_FRAME_BOTH_UP; + ret.animations.running[1] = BONGOCAT_FRAME_BOTH_DOWN; + ret.animations.running[2] = BONGOCAT_FRAME_BOTH_UP; + ret.animations.running[3] = BONGOCAT_FRAME_BOTH_DOWN; + return ret; } diff --git a/src/image_loader/custom/load_custom.cpp b/src/image_loader/custom/load_custom.cpp index 4c5b0713..9629bc13 100644 --- a/src/image_loader/custom/load_custom.cpp +++ b/src/image_loader/custom/load_custom.cpp @@ -151,6 +151,43 @@ namespace bongocat::animation { ret.end_moving.row = ret.end_moving.row < sprite_sheet_rows ? ret.end_moving.row : sprite_sheet_rows-1; row++; } + + if (sprite_sheet_settings.start_running_frames > 0) { + ret.start_running = { .valid = true, .start_col = 0, .end_col = sprite_sheet_settings.start_running_frames-1, .row = sprite_sheet_settings.start_running_row_index >= 0 ? sprite_sheet_settings.start_running_row_index : row }; + if (ret.start_moving.valid) { + ret.start_running.row = ret.start_moving.row; + } else { + ret.start_running.row = ret.start_running.row >= 0 ? ret.start_running.row : 0; + ret.start_running.row = ret.start_running.row < sprite_sheet_rows ? ret.start_running.row : sprite_sheet_rows-1; + } + row++; + } else if (ret.start_moving.valid) { + ret.start_running = { .valid = true, .start_col = ret.start_moving.start_col, .end_col = ret.start_moving.end_col, .row = sprite_sheet_settings.start_running_row_index >= 0 ? sprite_sheet_settings.start_running_row_index : ret.start_moving.row }; + } + if (sprite_sheet_settings.running_frames > 0) { + ret.running = { .valid = true, .start_col = 0, .end_col = sprite_sheet_settings.running_frames-1, .row = sprite_sheet_settings.running_row_index >= 0 ? sprite_sheet_settings.running_row_index : row }; + if (ret.moving.valid) { + ret.running.row = ret.moving.row; + } else { + ret.running.row = ret.running.row >= 0 ? ret.running.row : 0; + ret.running.row = ret.running.row < sprite_sheet_rows ? ret.running.row : sprite_sheet_rows-1; + } + row++; + } else if (ret.moving.valid) { + ret.running = { .valid = true, .start_col = ret.moving.start_col, .end_col = ret.moving.end_col, .row = sprite_sheet_settings.running_row_index >= 0 ? sprite_sheet_settings.running_row_index : ret.moving.row }; + } + if (sprite_sheet_settings.end_running_frames > 0) { + ret.end_running = { .valid = true, .start_col = 0, .end_col = sprite_sheet_settings.end_running_frames-1, .row = sprite_sheet_settings.end_running_row_index >= 0 ? sprite_sheet_settings.end_running_row_index : row }; + if (ret.end_moving.valid) { + ret.end_running.row = ret.end_moving.row; + } else { + ret.end_running.row = ret.end_running.row >= 0 ? ret.end_running.row : 0; + ret.end_running.row = ret.end_running.row < sprite_sheet_rows ? ret.end_running.row : sprite_sheet_rows-1; + } + row++; + } else if (ret.end_moving.valid) { + ret.end_running = { .valid = true, .start_col = ret.end_moving.start_col, .end_col = ret.end_moving.end_col, .row = sprite_sheet_settings.end_running_row_index >= 0 ? sprite_sheet_settings.end_running_row_index : ret.end_moving.row }; + } } // features @@ -162,12 +199,17 @@ namespace bongocat::animation { ret.feature_sleep_wake_up = ret.feature_sleep && ret.wake_up.valid; ret.feature_working = ret.working.valid || ret.start_working.valid || ret.end_working.valid; ret.feature_moving = ret.moving.valid || ret.start_moving.valid || ret.end_moving.valid; + ret.feature_running = ret.running.valid || ret.start_running.valid || ret.end_running.valid; // is feature_toggle_writing_frames enabled or writing has only 2 frames (default) ret.feature_writing_toggle_frames = ret.working.valid && (sprite_sheet_settings.feature_toggle_writing_frames >= 1 || (sprite_sheet_settings.feature_toggle_writing_frames < 0 && !ret.start_moving.valid && !ret.end_working.valid && ret.working.valid && sprite_sheet_settings.working_frames == 2)); ret.feature_writing_toggle_frames_random = ret.working.valid && (sprite_sheet_settings.feature_toggle_writing_frames_random >= 1 || (sprite_sheet_settings.feature_toggle_writing_frames_random < 0 && !ret.start_moving.valid && !ret.end_working.valid && ret.working.valid && sprite_sheet_settings.working_frames == 2)); if (!ret.feature_idle) [[unlikely]] { BONGOCAT_LOG_WARNING("Custom Animation without idle animation: %s", sprite_sheet_image.name); + // default to first frame + ret.idle = { .valid = true, .start_col = 0, .end_col = 0, .row = sprite_sheet_settings.idle_row_index >= 0 ? sprite_sheet_settings.idle_row_index : 0 }; + ret.idle.row = ret.idle.row >= 0 ? ret.idle.row : 0; + ret.idle.row = ret.idle.row < sprite_sheet_rows ? ret.idle.row : sprite_sheet_rows-1; } return ret; diff --git a/src/image_loader/pkmn/load_images_pkmn.cpp b/src/image_loader/pkmn/load_images_pkmn.cpp index d5880ff3..15f01fe6 100644 --- a/src/image_loader/pkmn/load_images_pkmn.cpp +++ b/src/image_loader/pkmn/load_images_pkmn.cpp @@ -81,6 +81,11 @@ namespace bongocat::animation { ret.animations.happy[2] = ret.idle_1.col; ret.animations.happy[3] = ret.idle_2.col; + ret.animations.running[0] = ret.idle_1.col; + ret.animations.running[1] = ret.idle_2.col; + ret.animations.running[2] = ret.idle_1.col; + ret.animations.running[3] = ret.idle_2.col; + return ret; }