From e7b6b4269866c7a39828fee7722b24554aad5c74 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 26 May 2026 12:48:34 -0400 Subject: [PATCH] The new scene system brought over and updated animation to be optional in the scene engine. --- src/boss/ui.cpp | 4 +- src/constants.hpp | 5 +++ src/graphics/animation.cpp | 14 +++++-- src/graphics/animation.hpp | 1 + src/graphics/scene.cpp | 71 +++++++++++++++++++++++----------- src/graphics/scene.hpp | 9 +++-- src/gui/combat_ui.cpp | 2 +- src/gui/guecstra.cpp | 12 ++++-- src/gui/guecstra.hpp | 6 ++- src/gui/overlay_ui.cpp | 2 +- src/gui/scene_ui.cpp | 78 ++++++++++++++++++++++++++++++++++++++ src/gui/scene_ui.hpp | 27 +++++++++++++ src/meson.build | 1 + 13 files changed, 194 insertions(+), 38 deletions(-) create mode 100644 src/gui/scene_ui.cpp create mode 100644 src/gui/scene_ui.hpp diff --git a/src/boss/ui.cpp b/src/boss/ui.cpp index 9926608..aa9b8b6 100644 --- a/src/boss/ui.cpp +++ b/src/boss/ui.cpp @@ -46,7 +46,7 @@ namespace boss { $actions.set(commit, {L"COMMIT"}); $actions.set(commit, {}); $actions.set(commit, - guecs::make_action(commit, game::Event::COMBAT_START, {})); + guecs::make_action(commit, game::Event::COMBAT_START)); auto stats = $actions.entity("stats"); $actions.set(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); } diff --git a/src/constants.hpp b/src/constants.hpp index 6881b41..aaf480e 100644 --- a/src/constants.hpp +++ b/src/constants.hpp @@ -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; diff --git a/src/graphics/animation.cpp b/src/graphics/animation.cpp index 95e5c10..9d2bc25 100644 --- a/src/graphics/animation.cpp +++ b/src/graphics/animation.cpp @@ -249,13 +249,12 @@ namespace animation { sequence.INVARIANT(); } - Animation load(const std::string &file, const std::string &anim_name) { + std::optional 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={}", diff --git a/src/graphics/animation.hpp b/src/graphics/animation.hpp index d188eb0..8861d7d 100644 --- a/src/graphics/animation.hpp +++ b/src/graphics/animation.hpp @@ -133,6 +133,7 @@ namespace animation { void motion(sf::View& view_out, sf::Vector2f pos, sf::Vector2f scale); }; + std::optional 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? diff --git a/src/graphics/scene.cpp b/src/graphics/scene.cpp index d0f4bb8..881c21b 100644 --- a/src/graphics/scene.cpp +++ b/src/graphics/scene.cpp @@ -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($ui.MAIN, {$ui.$parser, guecs::THEME.TRANSPARENT}); - auto& background = $ui.get($ui.MAIN); + $gui.position(SCENE_VIEW_X, SCENE_VIEW_Y, SCENE_VIEW_WIDTH, SCENE_VIEW_HEIGHT); + $gui.set($gui.MAIN, {$gui.$parser, guecs::THEME.TRANSPARENT}); + auto& background = $gui.get($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(text_id, {0, bg_color}); + $gui.set(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& 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 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($gui.entity("text")); + el->text->setString(guecs::to_wstring(content)); + } + } + void Engine::reset(sf::RenderTexture& view) { $camera.reset(view); } diff --git a/src/graphics/scene.hpp b/src/graphics/scene.hpp index 2db737a..849f52b 100644 --- a/src/graphics/scene.hpp +++ b/src/graphics/scene.hpp @@ -17,7 +17,7 @@ namespace scene { struct Element { std::string name; textures::SpriteTexture st; - animation::Animation anim; + std::optional 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 $actor_name_ids; std::vector $fixtures; std::vector $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 cb); + void set_text(const std::string& content); }; } diff --git a/src/gui/combat_ui.cpp b/src/gui/combat_ui.cpp index dcf4ece..7d8dbbf 100644 --- a/src/gui/combat_ui.cpp +++ b/src/gui/combat_ui.cpp @@ -81,7 +81,7 @@ namespace gui { auto hp_gauge = $gui.entity("hp_gauge"); $gui.set(hp_gauge, {"stone_doll_cursed"}); $gui.set(hp_gauge, - guecs::make_action(hp_gauge, game::Event::HP_STATUS, {})); + guecs::make_action(hp_gauge, game::Event::HP_STATUS)); } $gui.init(); diff --git a/src/gui/guecstra.cpp b/src/gui/guecstra.cpp index b4a58a1..ab3953f 100644 --- a/src/gui/guecstra.cpp +++ b/src/gui/guecstra.cpp @@ -1,18 +1,22 @@ #include "gui/guecstra.hpp" #include "game/level.hpp" +#include +#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(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(event, gui_id, data); }}; } diff --git a/src/gui/guecstra.hpp b/src/gui/guecstra.hpp index 63f7d6d..935b95e 100644 --- a/src/gui/guecstra.hpp +++ b/src/gui/guecstra.hpp @@ -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; diff --git a/src/gui/overlay_ui.cpp b/src/gui/overlay_ui.cpp index 8d797b0..6bd2edb 100644 --- a/src/gui/overlay_ui.cpp +++ b/src/gui/overlay_ui.cpp @@ -22,7 +22,7 @@ namespace gui { auto area = gui.entity(name); gui.set(area, { - [&](auto) { + [=](auto) { auto world = GameDB::current_world(); world->send(game::Event::AIM_CLICK, area, {}); } diff --git a/src/gui/scene_ui.cpp b/src/gui/scene_ui.cpp new file mode 100644 index 0000000..4477844 --- /dev/null +++ b/src/gui/scene_ui.cpp @@ -0,0 +1,78 @@ +#include "gui/scene_ui.hpp" +#include "game/config.hpp" +#include "events.hpp" +#include "gui/guecstra.hpp" +#include + +#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(ui_id, {guecs::to_wstring(name), 40}); + $gui.set(ui_id, {5, guecs::THEME.DARK_MID}); + $gui.set(ui_id, {}); + + if(actions.contains(name)) { + auto event_id = actions[name]; + $gui.set(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(scene_config); + } +} diff --git a/src/gui/scene_ui.hpp b/src/gui/scene_ui.hpp new file mode 100644 index 0000000..2ae6aaf --- /dev/null +++ b/src/gui/scene_ui.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#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); + }; +} diff --git a/src/meson.build b/src/meson.build index 9609930..d59ec59 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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',