engine/ext/scenes/world/gui/dialogue.cpp
2020-08-13 00:00:00 -05:00

402 lines
14 KiB
C++

#include "dialogue.h"
#include <uf/utils/hook/hook.h>
#include <uf/utils/time/time.h>
#include <uf/utils/serialize/serializer.h>
#include <uf/utils/userdata/userdata.h>
#include <uf/utils/window/window.h>
#include <uf/utils/camera/camera.h>
#include <uf/utils/mesh/mesh.h>
#include <uf/utils/string/ext.h>
// #include <uf/gl/glyph/glyph.h>
#include <uf/engine/asset/asset.h>
#include <unordered_map>
#include <uf/ext/vulkan/vulkan.h>
#include <uf/ext/vulkan/graphics/base.h>
#include <uf/utils/http/http.h>
#include <uf/utils/audio/audio.h>
#include <sys/stat.h>
#include <fstream>
#include "../world.h"
#include "..//dialogue.h"
namespace {
ext::Gui* circleIn;
ext::Gui* circleOut;
ext::Gui* dialogueMessage;
ext::Gui* dialogueOptions;
ext::DialogueManager* dialogueManager;
ext::World* world;
uf::Serializer masterTableGet( const std::string& table ) {
if ( !world ) world = (ext::World*) uf::Entity::globalFindByName("World");
if ( !world ) return std::string();
uf::Serializer& mastertable = world->getComponent<uf::Serializer>();
return mastertable["system"]["mastertable"][table];
}
uf::Serializer masterDataGet( const std::string& table, const std::string& key ) {
if ( !world ) world = (ext::World*) uf::Entity::globalFindByName("World");
if ( !world ) return std::string();
uf::Serializer& mastertable = world->getComponent<uf::Serializer>();
return mastertable["system"]["mastertable"][table][key];
}
inline int64_t parseInt( const std::string& str ) {
return atoi(str.c_str());
}
void playSound( ext::GuiDialogue& entity, const std::string& id, const std::string& key ) {
if ( !world ) world = (ext::World*) uf::Entity::globalFindByName("World");
if ( !world ) return;
uf::Serializer& masterdata = world->getComponent<uf::Serializer>();
uf::Serializer cardData = masterDataGet("Card", id);
uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].asString());
std::string name = charaData["filename"].asString();
std::string url = "https://cdn..xyz//unity/Android/voice/voice_" + name + "_"+key+".ogg";
if ( charaData["internal"].asBool() ) {
url = "/smtsamo/voice/voice_" + name + "_" + key + ".ogg";
}
uf::Asset& assetLoader = world->getComponent<uf::Asset>();
assetLoader.cache(url, "asset:Cache.Voice." + std::to_string(entity.getUid()));
}
void playSound( ext::GuiDialogue& entity, std::size_t uid, const std::string& key ) {
if ( !world ) world = (ext::World*) uf::Entity::globalFindByName("World");
if ( !world ) return;
uf::Serializer& pMetadata = entity.getComponent<uf::Serializer>();
uf::Serializer& masterdata = world->getComponent<uf::Serializer>();
uf::Entity* = world->findByUid(uid);
if ( ! ) return;
uf::Serializer& metadata = ->getComponent<uf::Serializer>();
std::string id = metadata[""]["id"].asString();
playSound( entity, id, key );
}
void playSound( ext::GuiDialogue& entity, const std::string& key ) {
if ( !world ) world = (ext::World*) uf::Entity::globalFindByName("World");
if ( !world ) return;
uf::Serializer& metadata = entity.getComponent<uf::Serializer>();
uf::Serializer& masterdata = world->getComponent<uf::Serializer>();
std::string url = "./data/audio/ui/" + key + ".ogg";
uf::Asset& assetLoader = world->getComponent<uf::Asset>();
assetLoader.cache(url, "asset:Cache.SFX." + std::to_string(entity.getUid()));
}
void playMusic( ext::GuiDialogue& entity, const std::string& filename ) {
if ( !world ) world = (ext::World*) uf::Entity::globalFindByName("World");
if ( !world ) return;
uf::Asset& assetLoader = world->getComponent<uf::Asset>();
uf::Serializer& masterdata = world->getComponent<uf::Serializer>();
assetLoader.load(filename, "asset:Load." + std::to_string(world->getUid()));
}
std::string getKeyFromIndex( uf::Serializer& object, uint64_t index ) {
uint64_t i = 0;
std::string key = "";
for ( auto it = object.begin(); it != object.end(); ++it ) {
if ( i++ == index ) key = it.key().asString();
}
return key;
}
uf::Audio sfx;
uf::Audio voice;
struct DialogueStats {
struct {
int selection = 0;
int selectionsMax = 3;
std::vector<std::string> invalids;
uf::Serializer current;
std::string index = "0";
} actions;
std::string state = "open";
bool initialized = false;
};
static DialogueStats stats;
}
void ext::GuiDialogue::initialize() {
ext::Gui::initialize();
uf::Serializer& metadata = this->getComponent<uf::Serializer>();
dialogueManager = (ext::DialogueManager*) this->getRootParent().findByName( "Dialogue Manager" );
this->addHook( "asset:Cache.SFX." + std::to_string(this->getUid()), [&](const std::string& event)->std::string{
uf::Serializer json = event;
std::string filename = json["filename"].asString();
if ( uf::string::extension(filename) != "ogg" ) return "false";
if ( filename == "" ) return "false";
uf::Serializer& masterdata = world->getComponent<uf::Serializer>();
uf::Audio& sfx = this->getComponent<uf::Audio>();
if ( sfx.playing() ) sfx.stop();
sfx = uf::Audio();
sfx.load(filename);
sfx.setVolume(masterdata["volumes"]["sfx"].asFloat());
sfx.play();
return "true";
});
this->addHook( "asset:Cache.Voice." + std::to_string(this->getUid()), [&](const std::string& event)->std::string{
uf::Serializer json = event;
std::string filename = json["filename"].asString();
if ( uf::string::extension(filename) != "ogg" ) return "false";
if ( filename == "" ) return "false";
uf::Serializer& masterdata = world->getComponent<uf::Serializer>();
uf::Audio& voice = this->getComponent<uf::Audio>();
if ( voice.playing() ) voice.stop();
voice = uf::Audio();
voice.load(filename);
voice.setVolume(masterdata["volumes"]["voice"].asFloat());
voice.play();
return "true";
});
// lock control
{
uf::Serializer payload;
payload["state"] = true;
uf::hooks.call("window:Mouse.CursorVisibility", payload);
uf::hooks.call("window:Mouse.Lock");
}
this->addHook("menu:Close.%UID%", [&]( const std::string& event ) -> std::string {
// kill
this->getParent().removeChild(*this);
this->destroy();
delete this;
return "true";
});
stats = DialogueStats();
}
void ext::GuiDialogue::tick() {
if ( !dialogueManager ) return;
ext::World& world = this->getRootParent<ext::World>();
uf::Serializer& masterdata = world.getComponent<uf::Serializer>();
uf::Serializer& bMetadata = this->getComponent<uf::Serializer>();
static float alpha = 0.0f;
static uf::Timer<long long> timer(false);
static float timeout = 1.0f;
if ( !timer.running() ) timer.start();
std::function<void(uf::Serializer&)> postParseResult = [&]( uf::Serializer& result ) {
if ( result["end"].asBool() ) bMetadata["system"]["closing"] = true;
if ( !result["timeout"].isNull() ) timeout = result["timeout"].asFloat();
else timeout = 1;
if ( result["message"].asString() != "" ) {
if ( dialogueMessage ) {
dialogueMessage->destroy();
this->removeChild(*dialogueMessage);
delete dialogueMessage;
}
dialogueMessage = new ext::Gui;
this->addChild(*dialogueMessage);
uf::Serializer& pMetadata = dialogueMessage->getComponent<uf::Serializer>();
dialogueMessage->load("./dialogue-text.json", true);
pMetadata["text settings"]["string"] = result["message"].asString();
dialogueMessage->initialize();
}
timer.reset();
};
std::function<void(uf::Serializer)> renderDialogueOptions = [&]( uf::Serializer actions ) {
if ( dialogueOptions ) {
this->removeChild(*dialogueOptions);
dialogueOptions->destroy();
delete dialogueOptions;
dialogueOptions = NULL;
}
if ( actions.isNull() ) {
stats.actions.selection = 0;
return;
}
stats.actions.current = actions;
std::string string = "";
std::size_t counter = 0;
std::size_t start = stats.actions.selection;
auto keys = actions.getMemberNames();
for ( int i = start; i < keys.size(); ++i ) {
std::string key = getKeyFromIndex( actions, i );
std::string text = actions[key].asString();
if ( std::find( stats.actions.invalids.begin(), stats.actions.invalids.end(), key ) != stats.actions.invalids.end() ) continue;
if ( ++counter > stats.actions.selectionsMax ) break;
if ( i != stats.actions.selection ) text = "%#FFFFFF%" + text;
else text = "%#FF0000%" + text;
string += "\n" + text;
}
if ( keys.size() > stats.actions.selectionsMax ) {
for ( int i = 0; i < keys.size(); ++i ) {
std::string key = getKeyFromIndex( actions, i );
std::string text = actions[key].asString();
if ( std::find( stats.actions.invalids.begin(), stats.actions.invalids.end(), key ) != stats.actions.invalids.end() ) continue;
if ( ++counter > stats.actions.selectionsMax ) break;
if ( i != stats.actions.selection ) text = "%#FFFFFF%" + text;
else text = "%#FF0000%" + text;
string += "\n" + text;
}
}
if ( string != "" ) {
string = string.substr(1);
dialogueOptions = new ext::Gui;
this->addChild(*dialogueOptions);
uf::Serializer& pMetadata = dialogueOptions->getComponent<uf::Serializer>();
dialogueOptions->load("./dialogue-option.json", true);
pMetadata["text settings"]["string"] = string;
pod::Transform<>& pTransform = dialogueOptions->getComponent<pod::Transform<>>();
dialogueOptions->initialize();
}
};
if ( !stats.initialized ) {
stats.initialized = true;
this->addHook("menu:Dialogue.Turn.%UID%", [&](const std::string& event) -> std::string {
uf::Serializer json = event;
uf::Serializer payload;
payload["action"] = "dialogue-select";
payload["index"] = stats.actions.index;
uf::Serializer result = uf::hooks.call("menu:Dialogue.Action." + std::to_string(dialogueManager->getUid()), payload)[0];
postParseResult(result);
// renderDialogueOptions(std::string(""));
renderDialogueOptions(result["actions"]);
return "true";
});
this->queueHook("menu:Dialogue.Turn.%UID%");
}
{
uf::Serializer& metadata = this->getComponent<uf::Serializer>();
if ( metadata["system"]["closing"].asBool() ) {
if ( alpha >= 1.0f ) {
uf::Asset assetLoader;
std::string canonical = assetLoader.load("./data/audio/ui/menu close.ogg");
uf::Audio& sfx = assetLoader.get<uf::Audio>(canonical);
sfx.setVolume(masterdata["volumes"]["sfx"].asFloat());
sfx.play();
}
if ( alpha <= 0.0f ) {
alpha = 0.0f;
metadata["system"]["closing"] = false;
metadata["system"]["closed"] = true;
} else alpha -= uf::physics::time::delta;
metadata["color"][3] = alpha;
} else if ( metadata["system"]["closed"].asBool() ) {
uf::Object& parent = this->getParent<uf::Object>();
parent.getComponent<uf::Serializer>()["system"]["closed"] = true;
this->destroy();
parent.removeChild(*this);
} else {
if ( !metadata["initialized"].asBool() ) alpha = 0.0f;
metadata["initialized"] = true;
if ( alpha >= 1.0f ) alpha = 1.0f;
else alpha += uf::physics::time::delta * 1.5f;
metadata["color"][3] = alpha;
}
}
ext::Gui::tick();
if ( stats.state != "open" ) {
if ( timer.elapsed().asDouble() >= timeout ) stats.state = "open";
return;
}
struct {
bool up = uf::Window::isKeyPressed("W");
bool down = uf::Window::isKeyPressed("S");
bool back = uf::Window::isKeyPressed("Escape");
bool select = uf::Window::isKeyPressed("E");
} keys;
static bool lastPress = false;
if ( dialogueOptions ) {
bool delta = false;
bool invalid = false;
pod::Transform<>& transform = dialogueOptions->getComponent<pod::Transform<>>();
uf::Serializer& metadata = dialogueOptions->getComponent<uf::Serializer>();
metadata["color"][3] = alpha;
for ( uf::Entity* pointer : dialogueOptions->getChildren() ) {
uf::Serializer& cMetadata = pointer->getComponent<uf::Serializer>();
cMetadata["text settings"]["color"][3] = alpha;
}
static bool released = true;
if ( keys.up ) {
if ( released ) {
delta = true;
released = false;
do {
--stats.actions.selection;
if ( stats.actions.selection < 0 ) { stats.actions.selection = stats.actions.current.size() - 1; }
} while ( std::find( stats.actions.invalids.begin(), stats.actions.invalids.end(), getKeyFromIndex(stats.actions.current, stats.actions.selection) ) != stats.actions.invalids.end() );
}
}
else if ( keys.down ) {
if ( released ) {
delta = true;
released = false;
do {
++stats.actions.selection;
if ( stats.actions.selection >= stats.actions.current.size() ) { stats.actions.selection = 0; }
} while ( std::find( stats.actions.invalids.begin(), stats.actions.invalids.end(), getKeyFromIndex(stats.actions.current, stats.actions.selection) ) != stats.actions.invalids.end() );
}
}
else released = true;
if ( stats.actions.selection < 0 ) { stats.actions.selection = stats.actions.current.size() - 1; }
else if ( stats.actions.selection >= stats.actions.current.size() ) { stats.actions.selection = 0; }
if ( stats.actions.current.size() == 0 ) released = true;
if ( !released ) {
if ( invalid ) {
playSound(*this, "invalid select");
} else if ( delta ) {
playSound(*this, "menu flip");
renderDialogueOptions(stats.actions.current);
}
} else if ( keys.back && timer.elapsed().asDouble() >= timeout ) {
playSound(*this, "invalid select");
timer.reset();
} else if ( keys.select && timer.elapsed().asDouble() >= timeout ) {
playSound(*this, "menu close");
uf::Serializer payload;
std::string action = getKeyFromIndex(stats.actions.current, stats.actions.selection);
payload["action"] = "dialogue-select";
payload["index"] = action;
stats.actions.index = action;
renderDialogueOptions(std::string(""));
timer.reset();
stats.state = "waiting";
stats.actions.invalids.clear();
this->queueHook("menu:Dialogue.Turn.%UID%", "", timeout);
}
}
}
void ext::GuiDialogue::render() {
ext::Gui::render();
}
void ext::GuiDialogue::destroy() {
ext::Gui::destroy();
}