raycaster/animation.hpp

150 lines
4.3 KiB
C++

#pragma once
#include <memory>
#include <chrono>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Shader.hpp>
#include <SFML/Graphics/View.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/System/Time.hpp>
#include <functional>
#include "ease2.hpp"
#include <fmt/core.h>
#include "json_mods.hpp"
#include <source_location>
#include "dinkyecs.hpp"
namespace animation {
template <typename T> struct NameOf;
struct Sheet {
int frames{0};
int frame_width{0};
int frame_height{0};
};
struct Timer {
double DELTA = 1.0/60.0;
double accumulator = 0.0;
double prev_time = 0.0;
double current_time = 0.0;
double frame_duration = 0.0;
double alpha = 0.0;
int elapsed_ticks = 0;
sf::Clock clock{};
std::pair<int, double> commit();
void start();
void reset();
void restart();
sf::Time getElapsedTime();
};
struct Sequence {
std::vector<int> frames{};
std::vector<int> durations{}; // in ticks
size_t current{0};
int loop_count{0};
size_t frame_count{frames.size()};
Timer timer{};
int subframe{0};
float easing_duration{0.0f};
float easing_position{0.0f};
void INVARIANT(const std::source_location location = std::source_location::current());
};
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 flipped{false};
bool scaled{false};
bool relative{false};
// handled by onLoop
bool toggled{false};
bool looped{false};
std::string easing{"in_out_back"};
std::string motion{"move_rush"};
// change to using a callback function for these
ease2::EaseFunc easing_func{ease2::get_easing(easing)};
ease2::MotionFunc motion_func{ease2::get_motion(motion)};
std::shared_ptr<sf::Shader> shader{nullptr};
void apply(Sequence& seq, sf::Vector2f& pos_out, sf::Vector2f& scale_out);
};
/* Gets the number of times it looped, and returns if it should stop. */
using OnLoopHandler = std::function<bool(Sequence& seq, Transform& tr)>;
using OnFrameHandler = std::function<void()>;
inline bool DefaultOnLoop(Sequence& seq, Transform& tr) {
if(tr.toggled) {
seq.current = seq.frame_count - 1;
} else {
seq.current = 0;
}
return tr.looped;
}
using Form = std::pair<std::string, std::string>;
using Sound = std::pair<size_t, std::string>;
class Animation {
public:
Sheet sheet;
std::unordered_map<std::string, Sequence> sequences;
std::unordered_map<std::string, Transform> transforms;
std::unordered_map<std::string, Form> forms;
std::unordered_map<std::string, std::vector<Sound>> sounds;
OnFrameHandler onFrame = nullptr;
Sequence sequence{};
Transform transform{};
std::vector<sf::IntRect> $frame_rects{calc_frames()};
OnLoopHandler onLoop = DefaultOnLoop;
bool playing = false;
// mostly for debugging purposes
std::string form_name="idle";
std::string sequence_name="";
std::string transform_name="";
std::string name="";
std::vector<sf::IntRect> calc_frames();
void play();
void play_sound();
void stop();
bool has_form(const std::string& as_form);
void set_form(const std::string& form);
void apply(sf::Sprite& sprite);
void apply(sf::Sprite& sprite, sf::IntRect& rect_io);
void apply_effect(std::shared_ptr<sf::Shader> effect);
void update();
void motion(sf::Transformable& sprite, sf::Vector2f pos, sf::Vector2f scale);
void motion(sf::View& view_out, sf::Vector2f pos, sf::Vector2f scale);
};
Animation load(const std::string &file, const std::string &anim_name);
// BUG: brought over from animation to finish the refactor, but these may not be needed or maybe they go in system.cpp?
bool has(const std::string& name);
void configure(DinkyECS::World& world, DinkyECS::Entity entity);
void animate_entity(DinkyECS::World &world, DinkyECS::Entity entity);
ENROLL_COMPONENT(Sheet, frames, frame_width, frame_height);
ENROLL_COMPONENT(Sequence, frames, durations);
ENROLL_COMPONENT(Transform, min_x, min_y, max_x, max_y,
flipped, scaled, relative, toggled, looped, easing, motion);
ENROLL_COMPONENT(Animation, sheet, sequences, transforms, forms, sounds);
}