Can now export the time code/actions from Reaper as json data and the storyboard will play them.

This commit is contained in:
Zed A. Shaw 2025-11-12 11:42:22 -05:00
parent b5280b4a4d
commit c486db5a57
6 changed files with 51 additions and 16 deletions

View file

@ -1,5 +1,6 @@
{ {
"sounds": { "sounds": {
"epic_theme": "assets/sounds/epic_theme.mp3",
"Sword_Hit_1": "assets/sounds/Creature_Sounds-Sword_Hit_1.ogg", "Sword_Hit_1": "assets/sounds/Creature_Sounds-Sword_Hit_1.ogg",
"Evil_Eye_Sound_1": "assets/sounds/Creature_Sounds-Evil_Eye_Sound_1.ogg", "Evil_Eye_Sound_1": "assets/sounds/Creature_Sounds-Evil_Eye_Sound_1.ogg",
"Evil_Eye_Sound_2": "assets/sounds/Creature_Sounds-Evil_Eye_Sound_2.ogg", "Evil_Eye_Sound_2": "assets/sounds/Creature_Sounds-Evil_Eye_Sound_2.ogg",

View file

@ -18,6 +18,7 @@ namespace components {
void init() { void init() {
if(!MAP_configured) { if(!MAP_configured) {
components::enroll<AnimatedScene>(MAP); components::enroll<AnimatedScene>(MAP);
components::enroll<Storyboard>(MAP);
components::enroll<Combat>(MAP); components::enroll<Combat>(MAP);
components::enroll<Position>(MAP); components::enroll<Position>(MAP);
components::enroll<Curative>(MAP); components::enroll<Curative>(MAP);

View file

@ -61,6 +61,7 @@ namespace components {
settings::Config devices; settings::Config devices;
settings::Config bosses; settings::Config bosses;
settings::Config rituals; settings::Config rituals;
settings::Config stories;
}; };
struct Personality { struct Personality {
@ -90,6 +91,13 @@ namespace components {
json fixtures; json fixtures;
}; };
struct Storyboard {
std::string image;
std::string audio;
std::vector<std::string> layout;
json beats;
};
struct Combat { struct Combat {
int hp; int hp;
int max_hp; int max_hp;
@ -170,6 +178,7 @@ namespace components {
ENROLL_COMPONENT(Motion, dx, dy, random); ENROLL_COMPONENT(Motion, dx, dy, random);
ENROLL_COMPONENT(Combat, hp, max_hp, damage, dead); ENROLL_COMPONENT(Combat, hp, max_hp, damage, dead);
ENROLL_COMPONENT(Device, config, events); ENROLL_COMPONENT(Device, config, events);
ENROLL_COMPONENT(Storyboard, image, audio, layout, beats);
ENROLL_COMPONENT(Animation, min_x, min_y, ENROLL_COMPONENT(Animation, min_x, min_y,
max_x, max_y, simple, frames, max_x, max_y, simple, frames,
speed, easing, motion, ease_rate, speed, easing, motion, ease_rate,

View file

@ -123,7 +123,8 @@ namespace GameDB {
settings::get("tiles"), settings::get("tiles"),
settings::get("devices"), settings::get("devices"),
settings::get("bosses"), settings::get("bosses"),
settings::get("rituals") settings::get("rituals"),
settings::get("stories")
}); });
} }
} }

View file

@ -2,6 +2,11 @@
#include "components.hpp" #include "components.hpp"
#include "animation.hpp" #include "animation.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "config.hpp"
#include <chrono>
#include <iostream>
#include <locale>
#include <sstream>
namespace storyboard { namespace storyboard {
UI::UI() : UI::UI() :
@ -11,25 +16,26 @@ namespace storyboard {
{ {
$view_sprite.setPosition({0, 0}); $view_sprite.setPosition({0, 0});
$camera.style("pan"); $camera.style("pan");
sound::play("ambient_1", true);
} }
void UI::init() { void UI::init() {
auto config = settings::get("stories");
$story = components::convert<components::Storyboard>(config["rat_king"]);
$ui.position(0,0, SCREEN_WIDTH, SCREEN_HEIGHT); $ui.position(0,0, SCREEN_WIDTH, SCREEN_HEIGHT);
$ui.set<guecs::Background>($ui.MAIN, {$ui.$parser, guecs::THEME.TRANSPARENT}); $ui.set<guecs::Background>($ui.MAIN, {$ui.$parser, guecs::THEME.TRANSPARENT});
auto& background = $ui.get<guecs::Background>($ui.MAIN); auto& background = $ui.get<guecs::Background>($ui.MAIN);
background.set_sprite("test_story", true); background.set_sprite($story.image, true);
$ui.layout( for(auto& line : $story.layout) {
"[a|b|c]" $layout.append(line);
"[*%(200,100)d|_|f]"
"[g|h|i]");
for(auto& [key, value] : $ui.$parser.cells) {
$cell_names.push_back(key);
} }
$ui.layout($layout);
sound::play($story.audio, true);
} }
void UI::render(sf::RenderWindow &window) { void UI::render(sf::RenderWindow &window) {
@ -44,10 +50,23 @@ namespace storyboard {
window.draw($view_sprite); window.draw($view_sprite);
} }
void UI::track_audio() { sf::Time parse_time_code(const std::string& time) {
int track_head = int($audio->getPlayingOffset().asSeconds()); std::chrono::seconds out{};
if(track_head % 3 == 0) { std::istringstream is{time};
is >> std::chrono::parse("%M:%S", out);
dbc::check(!is.fail(), fmt::format("Time parse failed: {}", time));
return sf::Time(out);
}
void UI::track_audio() {
auto& beat = $story.beats[cur_beat % $story.beats.size()];
auto track_head = $audio->getPlayingOffset();
auto next_beat = parse_time_code(beat[0]);
if(track_head >= next_beat) {
if($moving) return; if($moving) return;
$moving = true; // prevent motion until next tick $moving = true; // prevent motion until next tick
@ -55,10 +74,12 @@ namespace storyboard {
auto& cell = $ui.cell_for($zoom_target); auto& cell = $ui.cell_for($zoom_target);
$camera.position(cell.mid_x, cell.mid_y); $camera.position(cell.mid_x, cell.mid_y);
$zoom_target = beat[1];
$camera.style(beat[2]);
// get the new target from the cell names // get the new target from the cell names
$zoom_target = $cell_names.at(track_head % $cell_names.size());
zoom($zoom_target); zoom($zoom_target);
$camera.play(); $camera.play();
cur_beat++;
} else { } else {
$moving = false; $moving = false;
} }

View file

@ -13,8 +13,10 @@ namespace storyboard {
cinematic::Camera $camera; cinematic::Camera $camera;
std::shared_ptr<sf::Sound> $audio; std::shared_ptr<sf::Sound> $audio;
std::string $zoom_target = "a"; std::string $zoom_target = "a";
int $moving = false; bool $moving = false;
std::vector<std::string> $cell_names; int cur_beat = 0;
components::Storyboard $story;
std::string $layout;
UI(); UI();