#include "dialogue.h" #include #include #include #include #include #include #include // #include #include #include #include #include #include #include #include #include #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(); 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(); 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 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(); 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& masterdata = world->getComponent(); uf::Entity* = world->findByUid(uid); if ( ! ) return; uf::Serializer& metadata = ->getComponent(); 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& masterdata = world->getComponent(); std::string url = "./data/audio/ui/" + key + ".ogg"; uf::Asset& assetLoader = world->getComponent(); 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::Serializer& masterdata = world->getComponent(); 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 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(); 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::Audio& sfx = this->getComponent(); 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::Audio& voice = this->getComponent(); 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(); uf::Serializer& masterdata = world.getComponent(); uf::Serializer& bMetadata = this->getComponent(); static float alpha = 0.0f; static uf::Timer timer(false); static float timeout = 1.0f; if ( !timer.running() ) timer.start(); std::function 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(); dialogueMessage->load("./dialogue-text.json", true); pMetadata["text settings"]["string"] = result["message"].asString(); dialogueMessage->initialize(); } timer.reset(); }; std::function 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(); dialogueOptions->load("./dialogue-option.json", true); pMetadata["text settings"]["string"] = string; pod::Transform<>& pTransform = dialogueOptions->getComponent>(); 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(); 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(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(); parent.getComponent()["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>(); uf::Serializer& metadata = dialogueOptions->getComponent(); metadata["color"][3] = alpha; for ( uf::Entity* pointer : dialogueOptions->getChildren() ) { uf::Serializer& cMetadata = pointer->getComponent(); 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(); }