The new scene system brought over and updated animation to be optional in the scene engine.

This commit is contained in:
Zed A. Shaw 2026-05-26 12:48:34 -04:00
parent d86617aa3a
commit e7b6b42698
13 changed files with 194 additions and 38 deletions

View file

@ -46,7 +46,7 @@ namespace boss {
$actions.set<Text>(commit, {L"COMMIT"});
$actions.set<Effect>(commit, {});
$actions.set<Clickable>(commit,
guecs::make_action(commit, game::Event::COMBAT_START, {}));
guecs::make_action(commit, game::Event::COMBAT_START));
auto stats = $actions.entity("stats");
$actions.set<Rectangle>(stats, {});
@ -98,7 +98,7 @@ namespace boss {
}
void UI::status(const std::wstring& msg, const std::wstring &button_msg) {
$arena.$ui.show_text("status", msg);
$arena.$gui.show_text("status", msg);
$actions.show_text("commit", button_msg);
}

View file

@ -71,6 +71,11 @@ constexpr int COMBAT_UI_Y = RAY_VIEW_HEIGHT;
constexpr int COMBAT_UI_WIDTH = RAY_VIEW_WIDTH ;
constexpr int COMBAT_UI_HEIGHT = SCREEN_HEIGHT - RAY_VIEW_HEIGHT;
constexpr int SCENE_VIEW_WIDTH=SCREEN_WIDTH;
constexpr int SCENE_VIEW_HEIGHT=SCREEN_HEIGHT;
constexpr int SCENE_VIEW_X=0;
constexpr int SCENE_VIEW_Y=0;
constexpr int INITIAL_MAP_W = 21;
constexpr int INITIAL_MAP_H = 21;

View file

@ -249,13 +249,12 @@ namespace animation {
sequence.INVARIANT();
}
Animation load(const std::string &file, const std::string &anim_name) {
std::optional<Animation> maybe_load(const std::string &file, const std::string &anim_name) {
using nlohmann::json;
std::ifstream infile(file);
auto data = json::parse(infile);
dbc::check(data.contains(anim_name),
$F("{} animation config does not have animation {}", file, anim_name));
if(!data.contains(anim_name)) return std::nullopt;
Animation anim;
animation::from_json(data[anim_name], anim);
@ -269,6 +268,15 @@ namespace animation {
return anim;
}
Animation load(const std::string &file, const std::string &anim_name) {
auto anim = maybe_load(file, anim_name);
dbc::check(anim != std::nullopt,
$F("FAILED to load animation {} from file {}", anim_name, file));
return *anim;
}
void Sequence::INVARIANT(const std::source_location location) {
dbc::check(frames.size() == durations.size(),
$F("frames.size={} doesn't match durations.size={}",

View file

@ -133,6 +133,7 @@ namespace animation {
void motion(sf::View& view_out, sf::Vector2f pos, sf::Vector2f scale);
};
std::optional<Animation> maybe_load(const std::string &file, const std::string &anim_name);
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?

View file

@ -18,17 +18,20 @@ namespace scene {
bool flipped = config["flipped"];
// BUG: put the .json file to load as a default/optional arg
auto anim = animation::load("./assets/animation.json", sprite_name);
anim.play();
auto anim = animation::maybe_load("./assets/animation.json", sprite_name);
anim.transform.flipped = flipped;
if(anim) {
// only start it if there's an animation set
anim->play();
anim->transform.flipped = flipped;
}
std::string cell = config["cell"];
std::string name = config["name"];
bool at_mid = config["at_mid"];
sf::Text text(*$ui.$font, "", 60);
sf::Text text(*$gui.$font, "", 60);
return {name, st, anim, cell, {scale_x, scale_y}, {x, y}, at_mid, flipped, nullptr, text};
}
@ -55,12 +58,12 @@ namespace scene {
}
void Engine::init() {
$ui.position(0,0, BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT);
$ui.set<guecs::Background>($ui.MAIN, {$ui.$parser, guecs::THEME.TRANSPARENT});
auto& background = $ui.get<guecs::Background>($ui.MAIN);
$gui.position(SCENE_VIEW_X, SCENE_VIEW_Y, SCENE_VIEW_WIDTH, SCENE_VIEW_HEIGHT);
$gui.set<guecs::Background>($gui.MAIN, {$gui.$parser, guecs::THEME.TRANSPARENT});
auto& background = $gui.get<guecs::Background>($gui.MAIN);
background.set_sprite($scene.background, true);
$ui.layout($layout);
$gui.layout($layout);
for(auto& actor : $actors) {
actor.pos = position_sprite(actor.st, actor.cell,
@ -71,6 +74,17 @@ namespace scene {
fixture.pos = position_sprite(fixture.st, fixture.cell,
fixture.scale, fixture.at_mid, fixture.pos.x, fixture.pos.y);
}
if($gui.contains("text")) {
auto text_id = $gui.entity("text");
auto bg_color = guecs::THEME.DARK_DARK;
bg_color.a = 100;
// BUG: guecs should initialize anything added if it needs it
$gui.set<guecs::Rectangle>(text_id, {0, bg_color});
$gui.set<guecs::Text>(text_id, {L"", 55});
}
$gui.init();
}
void Engine::apply_effect(const std::string& actor, const std::string& shader) {
@ -89,11 +103,11 @@ namespace scene {
}
bool Engine::mouse(float x, float y, guecs::Modifiers mods) {
return $ui.mouse(x, y, mods);
return $gui.mouse(x, y, mods);
}
void Engine::render(sf::RenderTexture& view) {
$ui.render(view);
$gui.render(view);
for(auto& fixture : $fixtures) {
view.draw(*fixture.st.sprite, fixture.effect.get());
@ -101,11 +115,11 @@ namespace scene {
for(auto& actor : $actors) {
view.draw(*actor.st.sprite, actor.effect.get());
if(actor.anim.playing) view.draw(actor.text);
if(actor.anim && actor.anim->playing) view.draw(actor.text);
}
$camera.render(view);
if(DEBUG) $ui.debug_layout(view);
if(DEBUG) $gui.debug_layout(view);
}
Element& Engine::actor_config(const std::string& actor) {
@ -121,20 +135,23 @@ namespace scene {
void Engine::animate_actor(const std::string& actor, const std::string& form) {
auto& config = actor_config(actor);
config.anim.set_form(form);
if(!config.anim.playing) {
config.anim.play();
if(!config.anim) return;
config.anim->set_form(form);
if(!config.anim->playing) {
config.anim->play();
}
}
inline void this_is_stupid_refactor(std::vector<Element>& elements) {
for(auto& element : elements) {
if(element.anim.playing) {
element.anim.update();
element.anim.motion(*element.st.sprite, element.pos, element.scale);
element.anim.apply(*element.st.sprite);
if(element.effect != nullptr) element.anim.apply_effect(element.effect);
if(element.anim && element.anim->playing) {
element.anim->update();
element.anim->motion(*element.st.sprite, element.pos, element.scale);
element.anim->apply(*element.st.sprite);
if(element.effect != nullptr) element.anim->apply_effect(element.effect);
}
}
}
@ -145,7 +162,7 @@ namespace scene {
}
sf::Vector2f Engine::position_sprite(textures::SpriteTexture& st, const std::string& cell_name, sf::Vector2f scale, bool at_mid, float x_diff, float y_diff) {
auto& cell = $ui.cell_for(cell_name);
auto& cell = $gui.cell_for(cell_name);
float x = float(at_mid ? cell.mid_x : cell.x);
float y = float(at_mid ? cell.mid_y : cell.y);
@ -174,7 +191,10 @@ namespace scene {
void Engine::set_end_cb(std::function<void()> cb) {
for(auto& actor : $actors) {
actor.anim.onLoop = [&,cb](auto& seq, auto& tr) -> bool {
// no animation set so no point doing the end
if(!actor.anim) continue;
actor.anim->onLoop = [&,cb](auto& seq, auto& tr) -> bool {
seq.current = tr.toggled ? seq.frame_count - 1 : 0;
cb();
actor.effect = nullptr;
@ -183,6 +203,13 @@ namespace scene {
}
}
void Engine::set_text(const std::string& content) {
if($gui.contains("text")) {
auto el = $gui.get_if<guecs::Text>($gui.entity("text"));
el->text->setString(guecs::to_wstring(content));
}
}
void Engine::reset(sf::RenderTexture& view) {
$camera.reset(view);
}

View file

@ -17,7 +17,7 @@ namespace scene {
struct Element {
std::string name;
textures::SpriteTexture st;
animation::Animation anim;
std::optional<animation::Animation> anim;
std::string cell;
sf::Vector2f scale{1.0f, 1.0f};
sf::Vector2f pos{0.0f, 0.0f};
@ -29,13 +29,13 @@ namespace scene {
struct Engine {
sf::Clock $clock;
guecs::UI $ui;
guecs::UI $gui;
components::AnimatedScene& $scene;
std::string $layout;
std::unordered_map<std::string, int> $actor_name_ids;
std::vector<Element> $fixtures;
std::vector<Element> $actors;
cinematic::Camera $camera{{BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT}, "scene"};
cinematic::Camera $camera{{SCENE_VIEW_WIDTH, SCENE_VIEW_HEIGHT}, "scene"};
Engine(components::AnimatedScene& scene);
@ -44,6 +44,8 @@ namespace scene {
void update();
bool mouse(float x, float y, guecs::Modifiers mods);
void attach_text(const std::string& actor, const std::string& text);
nlohmann::json& buttons() { return $scene.buttons; };
lel::Cell& button_cell() { return $gui.cell_for("buttons"); }
Element config_scene_element(nlohmann::json& config, bool duped);
sf::Vector2f position_sprite(textures::SpriteTexture& st, const std::string& cell_name, sf::Vector2f scale, bool at_mid, float x_diff=0.0f, float y_diff=0.0f);
@ -56,5 +58,6 @@ namespace scene {
void zoom(float mid_x, float mid_y, const std::string& style, float scale);
void reset(sf::RenderTexture& view);
void set_end_cb(std::function<void()> cb);
void set_text(const std::string& content);
};
}

View file

@ -81,7 +81,7 @@ namespace gui {
auto hp_gauge = $gui.entity("hp_gauge");
$gui.set<Sprite>(hp_gauge, {"stone_doll_cursed"});
$gui.set<Clickable>(hp_gauge,
guecs::make_action(hp_gauge, game::Event::HP_STATUS, {}));
guecs::make_action(hp_gauge, game::Event::HP_STATUS));
}
$gui.init();

View file

@ -1,18 +1,22 @@
#include "gui/guecstra.hpp"
#include "game/level.hpp"
#include <magic_enum/magic_enum.hpp>
#include "dbc.hpp"
namespace guecs {
Clickable make_action(guecs::Entity gui_id, game::Event event) {
return {[&, gui_id, event](auto){
Clickable make_action(guecs::Entity gui_id, game::Event event, const std::source_location location) {
return {[&, gui_id, event, location](guecs::Modifiers mods){
auto world = GameDB::current_world();
dbc::log($F("SENDING EVENT: {}: {}", magic_enum::enum_name(event), mods.to_string()), location);
world->send<game::Event>(event, gui_id, {});
}};
}
Clickable make_action(guecs::Entity gui_id, game::Event event, std::any data) {
return {[&, event, data](auto){
Clickable make_action(guecs::Entity gui_id, game::Event event, std::any data, const std::source_location location) {
return {[&, event, data, location](auto){
auto world = GameDB::current_world();
dbc::log($F("SENDING EVENT: {}", magic_enum::enum_name(event)), location);
world->send<game::Event>(event, gui_id, data);
}};
}

View file

@ -5,8 +5,10 @@
#include "graphics/textures.hpp"
namespace guecs {
Clickable make_action(guecs::Entity gui_id, game::Event event);
Clickable make_action(guecs::Entity gui_id, game::Event event, std::any data);
Clickable make_action(guecs::Entity gui_id, game::Event event,
const std::source_location location = std::source_location::current());
Clickable make_action(guecs::Entity gui_id, game::Event event, std::any data,
const std::source_location location = std::source_location::current());
struct GrabSource {
DinkyECS::Entity world_entity;

View file

@ -22,7 +22,7 @@ namespace gui {
auto area = gui.entity(name);
gui.set<Clickable>(area, {
[&](auto) {
[=](auto) {
auto world = GameDB::current_world();
world->send<game::Event>(game::Event::AIM_CLICK, area, {});
}

78
src/gui/scene_ui.cpp Normal file
View file

@ -0,0 +1,78 @@
#include "gui/scene_ui.hpp"
#include "game/config.hpp"
#include "events.hpp"
#include "gui/guecstra.hpp"
#include <iostream>
#define DEBUG 0
namespace gui {
void SceneUI::init() {
$view_sprite.setPosition({0,0});
$scene.init();
create_buttons($scene.buttons());
}
void SceneUI::create_buttons(nlohmann::json& buttons) {
// buttons are optional
if(buttons.size() == 0) return;
// alright have buttons
has_buttons = true;
std::string layout{};
auto& actions = buttons["actions"];
for(auto& line : buttons["layout"]) {
layout.append(line);
}
auto& button_cell = $scene.button_cell();
$gui.position(button_cell.x, button_cell.y, button_cell.w, button_cell.h);
$gui.layout(layout);
for(auto& [name, cell] : $gui.cells()) {
auto ui_id = $gui.entity(name);
$gui.set<guecs::Text>(ui_id, {guecs::to_wstring(name), 40});
$gui.set<guecs::Rectangle>(ui_id, {5, guecs::THEME.DARK_MID});
$gui.set<guecs::Effect>(ui_id, {});
if(actions.contains(name)) {
auto event_id = actions[name];
$gui.set<guecs::Clickable>(ui_id, guecs::make_action(ui_id, event_id));
}
}
$gui.init();
}
void SceneUI::set_text(const std::string& text) {
$scene.set_text(text);
}
void SceneUI::render(sf::RenderTarget &target) {
$scene.render($view_texture);
target.draw($view_sprite);
if(has_buttons) {
$gui.render(target);
if(DEBUG) $gui.debug_layout(target);
}
}
void SceneUI::update() {
$scene.update();
}
bool SceneUI::mouse(float x, float y, guecs::Modifiers mods) {
$scene.mouse(x, y, mods);
$gui.mouse(x, y, mods);
return false;
}
AnimatedScene SceneUI::load_scene(const std::string& name) {
auto scene_config = settings::get("scenes")[name];
return components::convert<AnimatedScene>(scene_config);
}
}

27
src/gui/scene_ui.hpp Normal file
View file

@ -0,0 +1,27 @@
#pragma once
#include <guecs/ui.hpp>
#include "graphics/scene.hpp"
namespace gui {
using namespace components;
class SceneUI {
public:
std::string name;
sf::RenderTexture $view_texture{{SCENE_VIEW_WIDTH, SCENE_VIEW_HEIGHT}};
sf::Sprite $view_sprite{$view_texture.getTexture()};
AnimatedScene $scene_data{load_scene(name)};
scene::Engine $scene{$scene_data};
bool has_buttons = false;
guecs::UI $gui;
AnimatedScene load_scene(const std::string& name);
void create_buttons(nlohmann::json& buttons);
void init();
void render(sf::RenderTarget &target);
void update();
bool mouse(float x, float y, guecs::Modifiers mods);
void set_text(const std::string& text);
};
}

View file

@ -27,6 +27,7 @@ sources = files(
'gui/overlay_ui.cpp',
'gui/ritual_ui.cpp',
'gui/status_ui.cpp',
'gui/scene_ui.cpp',
# storyboard
'storyboard/ui.cpp',