#include #include "animation.hpp" #include "textures.hpp" #include "dinkyecs.hpp" #include "config.hpp" #include #include using namespace components; using namespace textures; struct Sheet { std::string texture_name; int width; int height; int frame_width; int frame_height; }; struct Sequence { size_t current_frame; float subframe; std::vector frames; std::vector durations; }; struct Transform { // how to know when a transform ends? float min_x = 1.0f; float min_y = 1.0f; float max_x = 1.0f; float max_y = 1.0f; bool simple = true; bool flipped = false; float ease_rate = 0.5f; bool scaled = false; bool stationary = false; // these can handled by the onLoop, same as ganim8 does it bool toggled = false; bool looped = false; std::shared_ptr shader = nullptr; // change to using a callback function for these ease::Style easing = ease::IN_OUT_BACK; ease::Motion motion = ease::RUSH; }; /* Gets the number of times it looped, and returns if it should stop. */ using OnLoopHandler = std::function; class Animate2 { public: Sheet& $sheet; Sequence $sequence; Transform $transform; std::shared_ptr $sprite = nullptr; std::shared_ptr $texture = nullptr; Animate2(Sheet sheet, Sequence seq, Transform trans) : $sheet(sheet), $sequence(seq), $transform(trans) { auto st = textures::get_sprite($sheet.texture_name); $sprite = st.sprite; $texture = st.texture; } OnLoopHandler onLoop = nullptr; }; /* * Animation is a Sheet + Sequence + Transform. * * A Sheet is just a grid of images with a predefined size for each cell. Arbitrary sized cells not supported. * * A Sequence is a list of Sheet cells _in any order_. See https://github.com/yottahmd/ganim8-lib. Sequences have a timing element for the cells, possibly a list of durations or a single duration. * * A Transform is combinations of scale and/or position easing/motion, shader effects, and things like if it's looped or toggled. * * I like the ganim8 onLoop concept, just a callback that says what to do when the animation has looped. */ TEST_CASE("new animation system", "[animation-new]") { textures::init(); Sheet sheet{ .texture_name{"rat_king_boss"}, .width{720*2}, .height{720}, .frame_width{720}, .frame_height{720}, }; Sequence sequence{ .current_frame{0}, .subframe{0.0f}, .frames{0,1}, .durations{0.1f,0.1f}, }; Transform transform{ .min_x{1.0f}, .min_y{1.0f}, .max_x{1.0f}, .max_y{1.0f}, .simple{true}, .flipped{false}, .ease_rate{0.5f}, .scaled{false}, .stationary{false}, .toggled{false}, .looped{false}, }; Animate2 anim{sheet, sequence, transform}; anim.onLoop = [](int loop_count) -> bool { return loop_count < 2; }; } TEST_CASE("animation easing tests", "[animation]") { Animation anim; anim.easing = ease::NONE; float res = anim.twitching(); REQUIRE(res == 0.0); anim.easing = ease::SINE; anim.subframe = 1.0f; res = anim.twitching(); REQUIRE(!std::isnan(res)); anim.easing = ease::OUT_CIRC; res = anim.twitching(); REQUIRE(!std::isnan(res)); anim.easing = ease::OUT_BOUNCE; res = anim.twitching(); REQUIRE(!std::isnan(res)); anim.easing = ease::IN_OUT_BACK; res = anim.twitching(); REQUIRE(!std::isnan(res)); } TEST_CASE("animation utility API", "[animation]") { textures::init(); animation::init(); auto blanket = textures::get_sprite("ritual_crafting_area"); auto anim = animation::load("ritual_crafting_area"); anim.play(); while(anim.apply(*blanket.sprite, {0,0})) { fmt::println("animation: {}", anim.subframe); } }