Animator won't crash anymore when there's an error, and instead displays an error message.

This commit is contained in:
Zed A. Shaw 2026-02-07 13:05:24 -05:00
parent 46f34828e4
commit 0d481a5ab7
6 changed files with 76 additions and 18 deletions

View file

@ -77,5 +77,8 @@ arena:
story: story:
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args ./builddir/storyboard gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args ./builddir/storyboard
animator: debug_animator:
gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args ./builddir/animator.exe -s "rat_king_boss" -a "rat_king_boss" -b "test_background" gdb --nx -x .gdbinit --batch --ex run --ex bt --ex q --args ./builddir/animator.exe -s "rat_king_boss" -a "rat_king_boss" -b "test_background"
animator:
./builddir/animator.exe -s "rat_king_boss" -a "rat_king_boss" -b "test_background"

View file

@ -51,6 +51,7 @@ namespace animate2 {
} }
void Animate2::play_sound() { void Animate2::play_sound() {
// BUG: this can be optimized way better
if(sounds.contains(form_name)) { if(sounds.contains(form_name)) {
fmt::println("Playing sound for {}", form_name); fmt::println("Playing sound for {}", form_name);
for(auto& [at_frame, sound_name] : sounds.at(form_name)) { for(auto& [at_frame, sound_name] : sounds.at(form_name)) {
@ -58,6 +59,8 @@ namespace animate2 {
sound::play(sound_name); sound::play(sound_name);
} }
} }
} else {
fmt::println("Animation has not sound {}", form_name);
} }
} }
@ -169,6 +172,10 @@ namespace animate2 {
// scale_out.x, scale_out.y); // scale_out.x, scale_out.y);
} }
bool Animate2::has_form(const std::string& as_form) {
return forms.contains(as_form);
}
void Animate2::set_form(const std::string& as_form) { void Animate2::set_form(const std::string& as_form) {
dbc::check(forms.contains(as_form), dbc::check(forms.contains(as_form),
fmt::format("form {} does not exist in animation", as_form)); fmt::format("form {} does not exist in animation", as_form));

View file

@ -113,6 +113,7 @@ namespace animate2 {
void play(); void play();
void play_sound(); void play_sound();
void stop(); void stop();
bool has_form(const std::string& as_form);
void set_form(const std::string& form); void set_form(const std::string& form);
void apply(sf::Sprite& sprite); void apply(sf::Sprite& sprite);
void update_frame(); void update_frame();

View file

@ -9,6 +9,8 @@
#include <thread> #include <thread>
#include "rand.hpp" #include "rand.hpp"
#include "animate2.hpp" #include "animate2.hpp"
#include "sound.hpp"
using namespace components; using namespace components;
using namespace textures; using namespace textures;
@ -53,6 +55,8 @@ void PLAY_TEST(Animate2 &anim) {
*/ */
TEST_CASE("new animation system", "[animation-new]") { TEST_CASE("new animation system", "[animation-new]") {
textures::init(); textures::init();
sound::init();
sound::mute(true);
auto anim = load_animation("rat_king_boss"); auto anim = load_animation("rat_king_boss");
PLAY_TEST(anim); PLAY_TEST(anim);
@ -96,6 +100,8 @@ TEST_CASE("new animation system", "[animation-new]") {
TEST_CASE("confirm frame sequencing works", "[animation-new]") { TEST_CASE("confirm frame sequencing works", "[animation-new]") {
textures::init(); textures::init();
animation::init(); animation::init();
sound::init();
sound::mute(true);
auto anim = load_animation("rat_king_boss"); auto anim = load_animation("rat_king_boss");
@ -109,8 +115,6 @@ TEST_CASE("confirm frame sequencing works", "[animation-new]") {
anim.onLoop = [&](auto& seq, auto& tr) -> bool { anim.onLoop = [&](auto& seq, auto& tr) -> bool {
seq.current = 0; seq.current = 0;
loop_ran = true; loop_ran = true;
REQUIRE(boss.sprite->getTextureRect() != init_rect);
return false; return false;
}; };
@ -125,14 +129,13 @@ TEST_CASE("confirm frame sequencing works", "[animation-new]") {
REQUIRE(loop_ran == true); REQUIRE(loop_ran == true);
REQUIRE(anim.playing == false); REQUIRE(anim.playing == false);
// this confirms it went back to the first frame
REQUIRE(boss.sprite->getTextureRect() == init_rect);
} }
TEST_CASE("confirm transition changes work", "[animation-new]") { TEST_CASE("confirm transition changes work", "[animation-new]") {
textures::init(); textures::init();
animation::init(); animation::init();
sound::init();
sound::mute(true);
auto sprite = *textures::get_sprite("rat_king_boss").sprite; auto sprite = *textures::get_sprite("rat_king_boss").sprite;
sf::Vector2f pos{100,100}; sf::Vector2f pos{100,100};

View file

@ -104,10 +104,13 @@ namespace animator {
} }
void FSM::change_form(int direction) { void FSM::change_form(int direction) {
dbc::check($anim.forms.size() > 0, "you can't use an empty animation.forms idiot."); if($anim.forms.size() == 0) {
$ui.show_error("NO FORMS!");
return;
}
$cur_form_i = std::clamp($cur_form_i + direction, 0, int($anim.forms.size()) - 1); $cur_form_i = std::clamp($cur_form_i + direction, 0, int($anim.forms.size()) - 1);
dbc::check($cur_form_i >= 0, "you fucked up, Zed"); dbc::check($cur_form_i >= 0, "CATASTROPHE! cur_form_i went below 0. How?");
// this is the dumbest shit ever // this is the dumbest shit ever
auto key_view = std::views::keys($anim.forms); auto key_view = std::views::keys($anim.forms);
@ -157,12 +160,34 @@ namespace animator {
} }
void FSM::reload() { void FSM::reload() {
$anim = animate2::load("assets/animate2.json", $anim_name); animate2::Animate2 new_anim;
// BUG: this will throw the index off after reloads, oh well try {
$anim.set_form($cur_form); new_anim = animate2::load("assets/animate2.json", $anim_name);
$last_mod_time = std::filesystem::last_write_time("assets/animate2.json"); } catch(...) {
$ui.show_error("Failed to load JSON");
return;
}
if(!new_anim.has_form($cur_form)) {
$ui.show_error(fmt::format("No form {}", $cur_form));
$cur_form = "idle";
$cur_form_i = 0;
}
new_anim.set_form($cur_form);
try {
$last_mod_time = std::filesystem::last_write_time("assets/animate2.json");
} catch(...) {
$ui.show_error("Filesystem error");
}
if($anim.form_name == new_anim.form_name) {
$ui.clear_error();
}
$anim = new_anim;
$anim.play(); $anim.play();
} }
@ -233,6 +258,8 @@ namespace animator {
"[error]"); "[error]");
$overlay.init(); $overlay.init();
$initialized_this_sucks_ass = true;
} }
void UI::render(sf::RenderWindow& window, bool debug) { void UI::render(sf::RenderWindow& window, bool debug) {
@ -255,13 +282,27 @@ namespace animator {
$overlay.show_text("transform", guecs::to_wstring(anim.transform_name)); $overlay.show_text("transform", guecs::to_wstring(anim.transform_name));
} }
void UI::show_error(const std::string& message) {
if($initialized_this_sucks_ass) {
$overlay.show_text("error", guecs::to_wstring(message));
} else {
dbc::log(message);
}
}
void UI::clear_error() {
if($initialized_this_sucks_ass) {
$overlay.show_text("error", L"");
}
}
std::shared_ptr<sf::Sprite> UI::get_sprite() { std::shared_ptr<sf::Sprite> UI::get_sprite() {
auto viewer = $ui.entity("viewer"); auto viewer = $ui.entity("viewer");
return $ui.get<guecs::Sprite>(viewer).sprite; return $ui.get<guecs::Sprite>(viewer).sprite;
} }
} }
int error() { int error_usage() {
fmt::println("USAGE: animator -h -b <background> -s <sprite> -a <animation>"); fmt::println("USAGE: animator -h -b <background> -s <sprite> -a <animation>");
return 1; return 1;
} }
@ -291,21 +332,21 @@ int main(int argc, char* argv[]) {
anim_name = optarg; anim_name = optarg;
break; break;
case 'h': // fallthrough case 'h': // fallthrough
error(); error_usage();
break; break;
default: default:
return error(); return error_usage();
break; break;
} }
} }
if(sprite_name == "") { if(sprite_name == "") {
return error(); return error_usage();
} else if(anim_name == "") { } else if(anim_name == "") {
anim_name = sprite_name; // default to the same anim_name = sprite_name; // default to the same
} }
sound::mute(false); sound::mute(true);
animator::FSM main; animator::FSM main;
main.init(sprite_name, anim_name, background); main.init(sprite_name, anim_name, background);

View file

@ -26,6 +26,7 @@ namespace animator {
struct UI { struct UI {
guecs::UI $ui; guecs::UI $ui;
guecs::UI $overlay; guecs::UI $overlay;
bool $initialized_this_sucks_ass = false;
void button(const std::string& name, std::function<void(guecs::Modifiers mods)> cb); void button(const std::string& name, std::function<void(guecs::Modifiers mods)> cb);
void init(const std::string& sprite_name, const std::string& background, int width, int height); void init(const std::string& sprite_name, const std::string& background, int width, int height);
@ -33,6 +34,8 @@ namespace animator {
bool mouse(float x, float y, guecs::Modifiers mods); bool mouse(float x, float y, guecs::Modifiers mods);
void update_status(animate2::Animate2& anim); void update_status(animate2::Animate2& anim);
std::shared_ptr<sf::Sprite> get_sprite(); std::shared_ptr<sf::Sprite> get_sprite();
void show_error(const std::string& message);
void clear_error();
}; };
struct FSM : public DeadSimpleFSM<State, Event> { struct FSM : public DeadSimpleFSM<State, Event> {
@ -50,7 +53,7 @@ namespace animator {
sf::Clock $timer; sf::Clock $timer;
std::string $cur_form = "idle"; std::string $cur_form = "idle";
int $cur_form_i = 0; int $cur_form_i = 0;
bool $mute = false; bool $mute = true;
void init(const std::string &sprite_name, const std::string& background, const std::string &anim_name); void init(const std::string &sprite_name, const std::string& background, const std::string &anim_name);
void event(Event ev, std::any data={}); void event(Event ev, std::any data={});