258 lines
5.7 KiB
C++
258 lines
5.7 KiB
C++
#define FSM_DEBUG 1
|
|
#include "sound.hpp"
|
|
#include "ai.hpp"
|
|
#include "animation.hpp"
|
|
#include <iostream>
|
|
#include "shaders.hpp"
|
|
#include "backend.hpp"
|
|
#include "constants.hpp"
|
|
#include "animate2.hpp"
|
|
#include "tools/animator.hpp"
|
|
#include <unistd.h>
|
|
|
|
/*
|
|
* TODO:
|
|
*
|
|
* - Easing functions from strings in json.
|
|
* - Map of animation forms.
|
|
* - Bring back shaders.
|
|
*/
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
bool YES_SYNC=true;
|
|
|
|
namespace animator {
|
|
|
|
void FSM::init(const std::string &sprite_name, const std::string &anim_name, const std::string &background) {
|
|
$timer.start();
|
|
$sprite_name = sprite_name;
|
|
$anim_name = anim_name;
|
|
$background = background;
|
|
|
|
// this loads the animation
|
|
reload();
|
|
|
|
sf::Vector2u new_size{(unsigned int)$anim.sheet.frame_width, (unsigned int)$anim.sheet.frame_height};
|
|
$window = sf::RenderWindow(sf::VideoMode(new_size), "Animation Crafting Tool");
|
|
$window.setPosition({0,0});
|
|
|
|
$ui.init($sprite_name, $background, new_size.x, new_size.y);
|
|
$sprite = $ui.get_sprite();
|
|
|
|
// need to keep these aroung
|
|
$pos = $sprite->getPosition();
|
|
$scale = $sprite->getScale();
|
|
|
|
if(YES_SYNC) {
|
|
$window.setVerticalSyncEnabled(VSYNC);
|
|
if(FRAME_LIMIT) $window.setFramerateLimit(FRAME_LIMIT);
|
|
}
|
|
}
|
|
|
|
void FSM::event(Event ev, std::any data) {
|
|
switch($state) {
|
|
FSM_STATE(State, START, ev);
|
|
FSM_STATE(State, ANIMATE, ev);
|
|
FSM_STATE(State, END, ev);
|
|
}
|
|
}
|
|
|
|
void FSM::START(Event ev) {
|
|
switch(ev) {
|
|
case Event::TICK:
|
|
state(State::ANIMATE);
|
|
break;
|
|
default:
|
|
state(State::START);
|
|
}
|
|
}
|
|
|
|
void FSM::ANIMATE(Event ev) {
|
|
switch(ev) {
|
|
case Event::TICK:
|
|
run_animation();
|
|
break;
|
|
case Event::PLAY_STOP:
|
|
if($anim.playing) {
|
|
$anim.stop();
|
|
} else {
|
|
$anim.play();
|
|
}
|
|
break;
|
|
case Event::RELOAD:
|
|
reload();
|
|
state(State::START);
|
|
break;
|
|
default:
|
|
state(State::START);
|
|
}
|
|
}
|
|
|
|
void FSM::END(Event ev) {
|
|
}
|
|
|
|
void FSM::run_animation() {
|
|
if($anim.playing) {
|
|
$anim.update();
|
|
if(!$anim.transform.simple) $anim.apply(*$sprite);
|
|
$anim.motion(*$sprite, $pos, $scale);
|
|
}
|
|
}
|
|
|
|
void FSM::tick() {
|
|
auto [ticks, alpha] = $anim.commit();
|
|
|
|
for(int i = 0; i < ticks; i++) {
|
|
event(animator::Event::TICK, {});
|
|
}
|
|
}
|
|
|
|
void FSM::check_update() {
|
|
if($timer.getElapsedTime().toDuration() > 500ms) {
|
|
auto mod_time = std::filesystem::last_write_time("assets/animate2.json");
|
|
|
|
if($last_mod_time < mod_time) {
|
|
event(Event::RELOAD);
|
|
}
|
|
|
|
$timer.restart();
|
|
}
|
|
}
|
|
|
|
void FSM::reload() {
|
|
$anim = animate2::load("assets/animate2.json", $anim_name);
|
|
$anim.play();
|
|
$last_mod_time = std::filesystem::last_write_time("assets/animate2.json");
|
|
}
|
|
|
|
void FSM::handle_keyboard_mouse() {
|
|
while(const auto ev = $window.pollEvent()) {
|
|
using enum game::Event;
|
|
using KEY = sf::Keyboard::Scan;
|
|
auto gui_ev = $router.process_event(ev);
|
|
auto mouse_pos = $window.mapPixelToCoords($router.position);
|
|
|
|
switch(gui_ev) {
|
|
case KEY_PRESS:
|
|
if($router.scancode == KEY::Space) {
|
|
event(Event::PLAY_STOP);
|
|
}
|
|
break;
|
|
case MOUSE_CLICK:
|
|
$ui.mouse(mouse_pos.x, mouse_pos.y, guecs::NO_MODS);
|
|
break;
|
|
case MOUSE_MOVE:
|
|
$ui.mouse(mouse_pos.x, mouse_pos.y, {1 << guecs::ModBit::hover});
|
|
break;
|
|
case QUIT:
|
|
state(State::END);
|
|
default:
|
|
break; // ignored
|
|
}
|
|
}
|
|
}
|
|
|
|
void FSM::render() {
|
|
$window.clear();
|
|
$ui.render($window, true);
|
|
$window.display();
|
|
}
|
|
|
|
bool FSM::active() {
|
|
return !in_state(State::END);
|
|
}
|
|
|
|
void UI::init(const std::string& sprite_name, const std::string& background, int width, int height) {
|
|
$ui.position(0,0, width, height);
|
|
$ui.layout("[=viewer]");
|
|
|
|
if(background != "") {
|
|
$ui.set<guecs::Background>($ui.MAIN, {$ui.$parser, guecs::THEME.TRANSPARENT});
|
|
auto& bg = $ui.get<guecs::Background>($ui.MAIN);
|
|
bg.set_sprite(background, true);
|
|
}
|
|
|
|
auto viewer = $ui.entity("viewer");
|
|
$ui.set<guecs::Sprite>(viewer, { sprite_name, 0, false});
|
|
|
|
$ui.init();
|
|
}
|
|
|
|
void UI::render(sf::RenderWindow& window, bool debug) {
|
|
$ui.render(window);
|
|
|
|
if(debug) {
|
|
$ui.debug_layout(window);
|
|
}
|
|
}
|
|
|
|
bool UI::mouse(float x, float y, guecs::Modifiers mods) {
|
|
return $ui.mouse(x, y, mods);
|
|
}
|
|
|
|
std::shared_ptr<sf::Sprite> UI::get_sprite() {
|
|
auto viewer = $ui.entity("viewer");
|
|
return $ui.get<guecs::Sprite>(viewer).sprite;
|
|
}
|
|
}
|
|
|
|
int error() {
|
|
fmt::println("USAGE: animator -h -b <background> -s <sprite> -a <animation>");
|
|
return 1;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
shaders::init();
|
|
components::init();
|
|
sfml::Backend backend;
|
|
guecs::init(&backend);
|
|
ai::init("ai");
|
|
animation::init();
|
|
|
|
std::string sprite_name;
|
|
std::string background;
|
|
std::string anim_name;
|
|
int opt = 0;
|
|
|
|
while((opt = getopt(argc, argv, "hb:s:a:")) != -1) {
|
|
switch(opt) {
|
|
case 'b':
|
|
background = optarg;
|
|
break;
|
|
case 's':
|
|
sprite_name = optarg;
|
|
break;
|
|
case 'a':
|
|
anim_name = optarg;
|
|
break;
|
|
case 'h': // fallthrough
|
|
error();
|
|
break;
|
|
default:
|
|
return error();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(sprite_name == "") {
|
|
return error();
|
|
} else if(anim_name == "") {
|
|
anim_name = sprite_name; // default to the same
|
|
}
|
|
|
|
sound::mute(true);
|
|
sound::play("ambient_1", true);
|
|
|
|
animator::FSM main;
|
|
main.init(sprite_name, anim_name, background);
|
|
|
|
while(main.active()) {
|
|
main.tick();
|
|
main.check_update();
|
|
main.render();
|
|
main.handle_keyboard_mouse();
|
|
}
|
|
|
|
return 0;
|
|
}
|