#if 0 #include "battle.h" #include ".h" #include #include #include #include #include #include #include #include #include #include #include "../gui/battle.h" #include namespace { ext::Gui* gui; std::string previousMusic; uf::Serializer masterTableGet( const std::string& table ) { uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& mastertable = scene.getComponent(); return mastertable["system"]["mastertable"][table]; } uf::Serializer masterDataGet( const std::string& table, const std::string& key ) { uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& mastertable = scene.getComponent(); return mastertable["system"]["mastertable"][table][key]; } inline int64_t parseInt( const std::string& str ) { return atoi(str.c_str()); } inline std::string colorString( const std::string& hex ) { return "%#" + hex + "%"; } } namespace { void playSound( ext::HousamoBattle& entity, const std::string& id, const std::string& key ) { uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& masterdata = scene.getComponent(); uf::Serializer cardData = masterDataGet("Card", id); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); std::string name = charaData["filename"].as(); if ( name == "none" ) return; std::string url = "https://cdn..xyz//unity/Android/voice/voice_" + name + "_"+key+".ogg"; if ( charaData["internal"].as() ) { url = uf::io::root+"/smtsamo/voice/voice_" + name + "_" + key + ".ogg"; } uf::Asset& assetLoader = scene.getComponent(); assetLoader.cache(url, "asset:Cache.Voice." + std::to_string(entity.getUid())); } void playSound( ext::HousamoBattle& entity, std::size_t uid, const std::string& key ) { uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& pMetadata = entity.getComponent(); uf::Serializer& masterdata = scene.getComponent(); uf::Entity* = scene.findByUid(uid); if ( ! ) return; uf::Serializer& metadata = ->getComponent(); std::string id = metadata[""]["id"].as(); playSound( entity, id, key ); } void playSound( ext::HousamoBattle& entity, const std::string& key ) { uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& metadata = entity.getComponent(); uf::Serializer& masterdata = scene.getComponent(); std::string url = uf::io::root+"/audio/ui/" + key + ".ogg"; uf::Asset& assetLoader = scene.getComponent(); assetLoader.cache(url, "asset:Cache.SFX." + std::to_string(entity.getUid())); } void playMusic( ext::HousamoBattle& entity, const std::string& filename ) { uf::Scene& scene = uf::scene::getCurrentScene(); uf::Asset& assetLoader = scene.getComponent(); uf::Serializer& masterdata = scene.getComponent(); uf::Audio& audio = scene.getComponent(); if ( audio.playing() ) { uf::Serializer& metadata = scene.getComponent(); metadata["previous bgm"]["filename"] = audio.getFilename(); metadata["previous bgm"]["timestamp"] = audio.getTime(); } assetLoader.load(filename, "asset:Load." + std::to_string(scene.getUid())); } uf::Audio sfx; uf::Audio voice; std::vector transients; } void ext::HousamoBattle::initialize() { uf::Object::initialize(); ::gui = NULL; transients.clear(); uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& metadata = this->getComponent(); // std::vector& transients = this->getComponent>(); this->m_name = "Battle Manager"; // asset loading this->addHook( "asset:Cache.Voice.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& metadata = this->getComponent(); uf::Serializer& masterdata = scene.getComponent(); std::string filename = json["filename"].as(); if ( uf::io::extension(filename) != "ogg" ) return "false"; if ( filename == "" ) return "false"; if ( voice.playing() ) voice.stop(); voice = uf::Audio(); voice.load(filename); voice.setVolume(masterdata["volumes"]["voice"].as()); voice.play(); return "true"; }); this->addHook( "asset:Cache.SFX.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; uf::Scene& scene = uf::scene::getCurrentScene(); uf::Serializer& masterdata = scene.getComponent(); std::string filename = json["filename"].as(); if ( uf::io::extension(filename) != "ogg" ) return "false"; if ( filename == "" ) return "false"; if ( sfx.playing() ) sfx.stop(); sfx = uf::Audio(); sfx.load(filename); sfx.setVolume(masterdata["volumes"]["sfx"].as()); sfx.play(); return "true"; }); this->addHook( "asset:Music.Load.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; std::string filename = ""; if ( ext::json::isArray( json["music"] ) ) { int ri = floor(rand() % json["music"].size()); filename = json["music"][ri].as(); } else if ( json["music"].is() ) { filename = json["music"].as(); } if ( filename != "" ) playMusic(*this, filename); return "true"; }); // bind events this->addHook( "world:Battle.Start.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; metadata["battle"] = json["battle"]; { uf::Serializer payload; payload["music"] = metadata["battle"]["music"]; this->queueHook( "asset:Music.Load.%UID%", payload ); } // generate battle data uf::Serializer actions; actions.readFromFile(uf::io::root+"/entities/gui/battle/actions.json"); metadata["actions"] = actions; if ( ext::json::isNull( metadata["actions"] ) ) { metadata["actions"]["00.member-attack"] = "攻撃"; metadata["actions"]["01.member-skill"] = "スキル"; metadata["actions"]["02.inventory"] = "アイテム"; metadata["actions"]["03.talk"] = "話し掛ける"; metadata["actions"]["04.transients"] = "転光生"; metadata["actions"]["05.escape"] = "脱走"; metadata["actions"]["06.analyze"] = "Analyze"; metadata["actions"]["07.pass"] = "Pass"; } // spawn transients accordingly // for ( auto& id : metadata["battle"]["player"]["party"] ) { ext::json::forEach(metadata["battle"]["player"]["party"], [&](ext::json::Value& id){ uint64_t uid = transients.size(); auto& member = metadata["battle"]["player"]["transients"][id.as()]; member["uid"] = uid; ext::Housamo* transient = new ext::Housamo; transients.push_back(transient); this->addChild(*transient); uf::Serializer& pMetadata = transient->getComponent(); pMetadata[""] = member; pMetadata[""]["uid"] = uid; pMetadata[""]["type"] = "player"; pMetadata[""]["initialized"] = false; transient->initialize(); metadata["battle"]["transients"][std::to_string(uid)] = pMetadata[""]; }); // for ( auto& id : metadata["battle"]["enemy"]["party"] ) { ext::json::forEach(metadata["battle"]["enemy"]["party"], [&](ext::json::Value& id){ uint64_t uid = transients.size(); auto& member = metadata["battle"]["enemy"]["transients"][id.as()]; member["uid"] = uid; ext::Housamo* transient = new ext::Housamo; transients.push_back(transient); this->addChild(*transient); uf::Serializer& pMetadata = transient->getComponent(); pMetadata[""] = member; pMetadata[""]["uid"] = uid; pMetadata[""]["type"] = "enemy"; pMetadata[""]["initialized"] = false; transient->initialize(); metadata["battle"]["transients"][std::to_string(uid)] = pMetadata[""]; }); this->queueHook("world:Battle.Gui.%UID%"); uf::Serializer payload; // for ( auto& member : metadata["battle"]["transients"] ) { ext::json::forEach(metadata["skills"], [&](ext::json::Value& member){ // for ( auto& skillId : member["skills"] ) { ext::json::forEach(member["skills"], [&](ext::json::Value& skillId){ uf::Serializer skillData = masterDataGet("Skill", skillId.as()); if ( skillData["type"].as() != 16 ) return; // for ( auto& status : skillData["statuses"] ) { ext::json::forEach(skillData["status"], [&](ext::json::Value& status){ std::string target = status["target"].as(); std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); float r = (rand() % 100) / 100.0; if ( statusData["proc"].is() ) { // failed if ( r > statusData["proc"].as() / 100.0f ) { target = ""; } } // apply status uf::Serializer targets; if ( target == "self" ) { targets.emplace_back(member["uid"].as()); } else if ( target == "enemy" ) { // for ( auto& tmember : metadata["battle"]["transients"] ) { ext::json::forEach(metadata["battle"]["transients"], [&](ext::json::Value& tmember){ if ( tmember["type"] == member["type"] ) return; targets.emplace_back(tmember["uid"].as()); }); } else if ( target == "ally" ) { // for ( auto& tmember : metadata["battle"]["transients"] ) { ext::json::forEach(metadata["battle"]["transients"], [&](ext::json::Value& tmember){ if ( tmember["type"] != member["type"] ) return; targets.emplace_back(tmember["uid"].as()); }); } // for ( auto& target : targets ) { ext::json::forEach(targets, [&](ext::json::Value& target){ uf::Serializer statusPayload; statusPayload["id"] = statusId; statusPayload["modifier"] = status["modifier"]; statusPayload["turns"] = status["turns"].is() ? status["turns"] : statusData["turns"]; metadata["battle"]["transients"][target.as()]["statuses"].emplace_back(statusPayload); uf::Serializer cardData = masterDataGet( "Card", metadata["battle"]["transients"][target.as()]["id"].as() ); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); payload["message"] = payload["message"].as() + "\nApplied "+colorString("FF0000") + "" + statusData["name"].as() + ""+colorString("FFFFFF") + " to " + charaData["name"].as(); }); }); }); }); payload["battle"] = metadata["battle"]; return payload; }); this->addHook( "world:Battle.Gui.%UID%", [&](const std::string& event)->std::string{ ext::Gui* guiManager = (ext::Gui*) this->globalFindByName("Gui Manager"); ext::Gui* guiMenu = new ext::GuiBattle; guiManager->addChild(*guiMenu); guiMenu->as().load("./scenes/world/gui/battle/menu.json"); guiMenu->initialize(); uf::Serializer payload; payload["battle"] = metadata["battle"]; guiMenu->queueHook("world:Battle.Start.%UID%", payload); ::gui = guiMenu; return "true"; }); // json.action and json.uid this->addHook( "world:Battle.Action.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; std::string hookName = ""; uf::Serializer payload; std::string action = json["action"].as(); // dead check struct { struct { int dead = 0; int total = 0; } player, enemy; } counts; // for ( auto& member : metadata["battle"]["transients"] ) { ext::json::forEach(metadata["battle"]["transients"], [&](ext::json::Value& member){ bool dead = member["hp"].as() <= 0; if ( member["type"] == "player" ) { ++counts.player.total; if ( dead ) ++counts.player.dead; } else { ++counts.enemy.total; if ( dead ) ++counts.enemy.dead; } }); if ( counts.player.total > 0 && counts.player.total == counts.player.dead ) { hookName = "world:Battle.End.%UID%"; payload["won"] = false; } else if ( counts.enemy.total > 0 && counts.enemy.total == counts.enemy.dead ) { hookName = "world:Battle.End.%UID%"; payload["won"] = true; } else if ( action == "turn-start" ) { payload = json; hookName = "world:Battle.Turn.%UID%"; } else if ( action == "member-turn" ) { payload = json; hookName = "world:Battle.Get.%UID%"; } else if ( action == "member-skill" ) { payload = json; hookName = "world:Battle.Get.%UID%"; // test } else if ( action == "member-attack" ) { payload = json; hookName = "world:Battle.Attack.%UID%"; metadata["battle"]["turn"]["decrement"] = 1.0f; } else if ( action == "member-targets" ) { payload = json; hookName = "world:Battle.GetTargets.%UID%"; } else if ( action == "enemy-attack" ) { payload = this->callHook("world:Battle.AI.%UID%", json)[0].as(); hookName = "world:Battle.Attack.%UID%"; metadata["battle"]["turn"]["decrement"] = 1.0f; } else if ( action == "escape" ) { payload = json; hookName = "world:Battle.Escape.%UID%"; metadata["battle"]["turn"]["decrement"] = 1.0f; } else if ( action == "talk" ) { payload = json; hookName = "world:Battle.Talk.%UID%"; metadata["battle"]["turn"]["decrement"] = 1.0f; } else if ( action == "pass" ) { payload["uid"] = json["uid"]; payload["message"] = "Waiting..."; payload["timeout"] = 2.0f; // take half metadata["battle"]["turn"]["decrement"] = 0.5f; } else if ( action == "force-pass" ) { payload["uid"] = json["uid"]; payload["message"] = "Immobilized..."; payload["timeout"] = 2.0f; } else if ( action == "analyze" ) { payload = json; hookName = "world:Battle.Get.%UID%"; } else { payload["message"] = "Invalid command."; payload["timeout"] = 2.0f; payload["invalid"] = true; } // this->callHook("world:Battle.AI.%UID%", json)[0]; { auto& turnState = metadata["battle"]["turn"]; float decrement = turnState["decrement"].as(); turnState["counter"] = turnState["counter"].as() - decrement; turnState["decrement"] = 0; } { // for ( auto it = metadata["battle"]["transients"].begin(); it != metadata["battle"]["transients"].end(); ++it ) { // std::string key = it.key(); ext::json::forEach(metadata["battle"]["transients"], [&](const std::string& key, ext::json::Value& member){ if ( key == "" || ext::json::isNull( member["type"] ) ) metadata["battle"].erase(key); }); } /* remove dead */ if ( false ) { // for ( auto it = metadata["battle"]["transients"].begin(); it != metadata["battle"]["transients"].end(); ++it ) { // std::string key = it.key(); ext::json::forEach(metadata["battle"]["transients"], [&](const std::string& key, ext::json::Value& member){ if ( metadata["battle"]["transients"][key]["hp"].as() > 0 ) return; // metadata["battle"]["transients"].erase(key); }); // std::cout << metadata["battle"]["transients"] << std::endl; } if ( ::gui ) { uf::Serializer payload; payload["battle"] = metadata["battle"]; ::gui->queueHook("world:Battle.Update.%UID%", payload); } if ( hookName == "" ) return payload; uf::Serializer result; result = this->callHook(hookName, payload)[0].as(); return result; }); this->addHook( "world:Battle.AI.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; // default to attack std::string queuedSkillId = "0"; if ( json["uid"] == "" ) { json["skill"] = "0"; return json; } auto& member = metadata["battle"]["transients"][json["uid"].as()]; // deduce member state struct { std::vector ailments; int ndas = 0; float factor = 0.0f; } stats; // for ( auto& status : member["statuses"] ) { ext::json::forEach(metadata["statuses"], [&](ext::json::Value& status){ std::string id = status["id"].as(); if ( id == "0" ) return; stats.ailments.push_back(id); if ( id == "6" ) ++stats.ndas; }); uf::Serializer possibilities; possibilities = ext::json::array(); // for ( auto& skillId : member["skills"] ) { ext::json::forEach(metadata["skills"], [&](ext::json::Value& skillId, bool breaks){ uf::Serializer skillData = masterDataGet("Skill", skillId.as()); // not recarms std::cout << skillData["name"] << ": "; if ( skillId == "81" || skillId == "82" ) { std::cout << "\tBad skill ID" << std::endl; return; } // invalid skill type if ( skillData["type"].as() < 1 || skillData["type"].as() > 15 ) { std::cout << "\tBad skill type" << std::endl; return; } // no MP if ( skillData["mp"].as() > member["mp"].as() ) { std::cout << "\tNot enough MP" << std::endl; return; } // no HP if ( skillData["hp%"].as() / 100.0f * member["max hp"].as() >= member["hp"].as() ) { std::cout << "\tNot enough HP" << std::endl; return; } // do not use same skills if ( std::find(member["used skills"].begin(), member["used skills"].end(), skillId.as()) != member["used skills"].end() ) { std::cout << "\tRecently Used" << std::endl; return; } // grants additional turns if ( skillData["turns+"].is() ) { queuedSkillId = skillId.as(); std::cout << "\tCan gain turns" << std::endl; breaks = true; return; // try and heal if under 30% } else if ( member["hp"].as() / member["max hp"].as() < 0.3f ) { if ( skillData["type"] != "14" ) return; if ( stats.factor < skillData["power"].as() ) { queuedSkillId = skillId.as(); stats.factor = skillData["power"].as(); std::cout << "\tCan heal" << std::endl; } // remove debuffs } else if ( skillId == "200" ) { if ( stats.ndas * 75.0f > stats.factor ) { queuedSkillId = skillId.as(); stats.factor = stats.ndas * 75.0f; std::cout << "\tCan Dekunda" << std::endl; } return; } else { // remove negative ailment (fear, etc) // for ( auto& status : skillData["statuses"] ) { ext::json::forEach(skillData["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } // remove statuses if ( statusData["type"] == "remove" && std::find( stats.ailments.begin(), stats.ailments.end(), statusId ) != stats.ailments.end() ) { queuedSkillId = skillId.as(); stats.factor = 150.0f; std::cout << "\tCan remove status " << statusData["name"] << std::endl; } }); std::cout << "\tRandom" << std::endl; // no heal spells if ( skillData["type"] == "14" ) return; // let RNG pick possibilities.emplace_back(skillId); } }); // let RNG pick std::cout << queuedSkillId << ": " << possibilities << std::endl; if ( queuedSkillId == "0" && possibilities.size() > 0 ) { possibilities.emplace_back("0"); int ri = floor(rand() % possibilities.size()); queuedSkillId = possibilities[ri].as(); auto queuedSkillData = masterDataGet( "Skill", queuedSkillId ); std::cout << "Picked " << queuedSkillData["name"].as() << std::endl; } json["skill"] = queuedSkillId; return json; }); this->addHook( "world:Battle.GetTargets.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; uf::Serializer payload; payload["targets"] = ext::json::array(); auto& member = metadata["battle"]["transients"][json["uid"].as()]; if ( json["skill"].is() ) { std::string skillId = json["skill"].as(); if ( json["skill"] == "0" ) { // for ( auto& status : member["statuses"] ) { ext::json::forEach(member["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( statusData["skill"] != skillId ) return; if ( statusData["range"].is() ) json["range"] = statusData["range"]; }); } else if ( !json["type"].is() ) { uf::Serializer skillData = masterDataGet( "Skill", skillId ); json["type"] = skillData["target"].as(); if ( json["type"] == "" ) json["type"] = "enemy"; } } else { json["type"] = "all"; } if ( json["type"].is() ) { if ( json["type"] == "self" ) { payload["targets"].emplace_back( metadata["battle"]["transients"][json["uid"].as()] ); } else { for ( auto& m : metadata["battle"]["transients"] ) { if ( json["type"] == "ally" && m["type"] != member["type"] ) continue; if ( json["type"] == "enemy" && m["type"] == member["type"] ) continue; payload["targets"].emplace_back( m ); } } } return payload; }); this->addHook( "world:Battle.Escape.%UID%", [&](const std::string& event)->std::string{ uf::Serializer payload; float r = (rand() % 100) / 100.0; if ( r > 0.9 ) { payload["message"] = "Cannot escape!"; payload["timeout"] = 2.0f; payload["invalid"] = true; } else { payload["message"] = "Escaped!"; payload["timeout"] = 2.0f; this->queueHook("world:Battle.End.%UID%", payload, 2.0f); } return payload; }); this->addHook( "world:Battle.Talk.%UID%", [&](const std::string& event)->std::string{ uf::Serializer payload; float r = (rand() % 100) / 100.0; payload["message"] = ""; payload["timeout"] = 2.0f; payload["invalid"] = true; uf::Serializer target; std::string name; // for ( auto& member : metadata["battle"]["transients"] ) { ext::json::forEach(metadata["battle"]["transients"], [&](ext::json::Value& member, bool breaks){ if ( member["type"] != "enemy" ) return; target = (ext::json::Value&) member; size_t uid = member["uid"].as(); uf::Serializer cardData = masterDataGet("Card", member["id"].as()); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); name = charaData["name"].as(); breaks = true; }); if ( target ) { playSound(*this, target["id"].as(), r > 0.5 ? "turn" : "battlestart"); payload["message"] = ""+colorString("FF0000") + "" + name + ""+colorString("FFFFFF") + " speaks."; } return payload; }); this->addHook( "world:Battle.OnHit.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; uint64_t uid = json["uid"].as(); ext::Housamo* transient = transients.at(uid); uf::Serializer& member = transient->getComponent(); uint64_t entid = metadata["battle"]["enemy"]["uid"].as(); uf::Entity* = scene.findByUid(entid); if ( ! ) return "false"; uf::Serializer& hMetadata = ->getComponent(); uint64_t phase = json["phase"].as(); // start color pod::Vector4f color = { 1, 1, 1, 0 }; if ( phase == 0 ) { color = (member[""]["type"] == "enemy") ? pod::Vector4f{ 1.0f, 0, 0, 0.6f } : pod::Vector4f{ 1.0f, 1.0f, 1.0f, 0.6f }; } hMetadata["color"][0] = color[0]; hMetadata["color"][1] = color[1]; hMetadata["color"][2] = color[2]; hMetadata["color"][3] = color[3]; return "true"; }); this->addHook( "world:Battle.Turn.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; bool found = false; uint64_t turnPlayer = json["uid"].as(); std::string name = ""; auto& turnState = metadata["battle"]["turn"]; { if ( turnState["counter"].as() <= 0 ) { if ( turnState["phase"] == "player" ) turnState["phase"] = "enemy"; else turnState["phase"] = "player"; int counter = 0; // for ( auto& member : metadata["battle"]["transients"] ) { ext::json::forEach(metadata["battle"]["transients"], [&](ext::json::Value& member){ if ( member["type"] != turnState["phase"] ) return; bool locked = false; // for ( auto& status : member["statuses"] ) { ext::json::forEach(member["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( statusData["lock"].as() ) locked = true; }); if ( locked ) return; ++counter; }); turnState["counter"] = counter; turnState["start of turn"] = true; if ( ::gui ) { uf::Serializer payload; payload["battle"] = metadata["battle"]; ::gui->queueHook("world:Battle.TurnStart.%UID%", payload); } } else { turnState["start of turn"] = false; } } std::function getTurnPlayer = [&]()->bool{ // for ( auto& member : metadata["battle"]["transients"] ) { bool found = false; ext::json::forEach(metadata["battle"]["transients"], [&](ext::json::Value& member, bool breaks){ if ( member["hp"].as() <= 0 ) return; bool locked = false; // for ( auto& status : member["statuses"] ) { ext::json::forEach(member["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( statusData["lock"].as() ) locked = true; }); if ( locked ) return; if ( turnPlayer > member["uid"].as() ) return; if ( member["type"] != turnState["phase"] ) return; turnPlayer = member["uid"].as(); breaks = true; found = true; }); return found; }; uf::Serializer payload; payload["message"] = "An error occurred!"; if ( !getTurnPlayer() ) { turnPlayer = 0; if ( !getTurnPlayer() ) { std::cout << metadata["battle"]["transients"] << std::endl; payload["end"] = true; return payload; } } payload["uid"] = turnPlayer; auto& member = metadata["battle"]["transients"][payload["uid"].as()]; uf::Serializer cardData = masterDataGet("Card", member["id"].as()); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); name = charaData["name"].as(); payload["message"] = "What will "+colorString("FF0000") + "" + name + ""+colorString("FFFFFF") + " do?"; // if ( member["type"] == "enemy" ) { payload["message"] = ""+colorString("FF0000") + "" + name + ""+colorString("FFFFFF") + "'s turn..."; payload["timeout"] = 2.0f; } bool skipped = false; // tick down statuses if ( turnState["start of turn"].as() ) { /* remove dead */ if ( false ) { for ( auto it = metadata["battle"]["transients"].begin(); it != metadata["battle"]["transients"].end(); ++it ) { std::string key = it.key(); if ( metadata["battle"]["transients"][key]["hp"].as() > 0 ) continue; // metadata["battle"]["transients"].erase(key); } // std::cout << metadata["battle"]["transients"] << std::endl; } // for ( auto& member : metadata["battle"]["transients"] ) { ext::json::forEach(metadata["battle"]["transients"], [&](ext::json::Value& member){ // clear OPT used skills member["used skills"] = ext::json::array(); if ( member["type"] != turnState["phase"] ) return; auto& statuses = member["statuses"]; // std::cout << "Before: " << member["uid"].as() << ": " << statuses << std::endl; // for ( int i = 0; i < statuses.size(); ++i ) { ext::json::forEach(statuses, [&](size_t i, ext::json::Value& status){ uf::Serializer statusData = masterDataGet("Status", status["id"].as()); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } uf::Serializer cardData = masterDataGet( "Card", member["id"].as() ); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); if ( statusData["hp"].is() ) { int64_t hp = statusData["hp"].as(); member["hp"] = member["hp"].as() + hp; payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "" + charaData["name"].as() + ""+colorString("FFFFFF") + " gained "+colorString("00FF00") + ""+ std::to_string(hp) +" HP"+colorString("FFFFFF") + " from "+colorString("FF0000") + "" + statusData["name"].as() + ""+colorString("FFFFFF") + ""; } if ( statusData["hp%"].is() ) { int64_t hp = member["max hp"].as() * (statusData["hp%"].as() / 100.0f); if ( member["max hp"].as() < member["hp"].as() + hp ) hp = member["max hp"].as() - member["hp"].as(); member["hp"] = member["hp"].as() + hp; if ( hp > 0 ) payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "" + charaData["name"].as() + ""+colorString("FFFFFF") + " gained "+colorString("00FF00") + ""+ std::to_string(hp) +" HP"+colorString("FFFFFF") + " from "+colorString("FF0000") + "" + statusData["name"].as() + ""+colorString("FFFFFF") + ""; } if ( statusData["mp"].is() ) { int64_t mp = statusData["mp"].as(); member["mp"] = member["mp"].as() + mp; if ( member["max mp"].as() < member["mp"].as() + mp ) mp = member["max mp"].as() - member["mp"].as(); payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "" + charaData["name"].as() + ""+colorString("FFFFFF") + " gained "+colorString("0000FF") + ""+ std::to_string(mp) +" MP"+colorString("FFFFFF") + " from "+colorString("FF0000") + "" + statusData["name"].as() + ""+colorString("FFFFFF") + ""; } if ( statusData["lock"].is() ) { skipped = true; } }); uf::Serializer newStatuses; for ( int i = statuses.size() - 1; i >= 0; --i ) { auto& status = statuses[i]; uf::Serializer statusData = masterDataGet("Status", status["id"].as()); if ( !ext::json::isNull( status["turns"] ) ) { status["turns"] = status["turns"].as() - 1; if ( status["turns"].as() < 0 ) { if ( statusData["id"] != "0" ) payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "" + statusData["name"].as() + ""+colorString("FFFFFF") + " wore off."; } else { newStatuses.emplace_back(status); } } else { newStatuses.emplace_back(status); } } statuses = newStatuses; // std::cout << "After: " << member["uid"].as() << ": " << statuses << std::endl; }); } { for ( auto it = metadata["battle"]["transients"].begin(); it != metadata["battle"]["transients"].end(); ++it ) { std::string key = it.key(); if ( key == "" || ext::json::isNull( (*it)["type"] ) ) metadata["battle"].erase(key); } } if ( ::gui ) { uf::Serializer payload; payload["battle"] = metadata["battle"]; ::gui->queueHook("world:Battle.Update.%UID%", payload); } if ( skipped ) payload["skipped"] = true; /* std::vector list = { "member-attack", "member-skill", "inventory", "talk", "transients", "escape", "pass", "analyze", }; payload["actions"]["list"] = ext::json::array(); payload["actions"]["names"]["member-attack"] = "攻撃"; payload["actions"]["names"]["member-skill"] = "スキル"; payload["actions"]["names"]["inventory"] = "アイテム"; payload["actions"]["names"]["talk"] = "話し掛ける"; payload["actions"]["names"]["transients"] = "転光生"; payload["actions"]["names"]["escape"] = "脱走"; payload["actions"]["names"]["analyze"] = "Analyze"; payload["actions"]["names"]["pass"] = "Pass"; for ( auto& x : list ) payload["actions"]["list"].emplace_back(x); */ payload["actions"] = metadata["actions"]; payload[""] = member; return payload; }); this->addHook( "world:Battle.Attack.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; std::string skillId = json["skill"].as(); uf::Serializer skillData = masterDataGet("Skill", skillId); // ext::Housamo* transient = transients.at(uid); auto& member = metadata["battle"]["transients"][json["uid"].as()]; uf::Serializer payload; payload["message"] = ""; // modify skill data based on statuses // for ( auto& status : member["statuses"] ) { ext::json::forEach(member["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( statusData["skill"] != skillId ) return; if ( statusData["range"].is() ) skillData["range"] = statusData["range"]; }); // select random target if ( ext::json::isNull( json["target"] ) ) { if ( ext::json::isNull( skillData["target"] ) ) skillData["target"] = "enemy"; if ( skillData["target"] == "self" ) { json["target"].emplace_back( member["uid"].as() ); } else if ( skillData["range"].is() && skillData["range"].as() > 1 ) { json["target"] = ext::json::array(); // for ( auto& m : metadata["battle"]["transients"] ) { ext::json::forEach(member["battle"]["transients"], [&](ext::json::Value& m){ if ( skillData["target"] == "ally" && m["type"] != member["type"] ) return; if ( skillData["target"] != "ally" && m["type"] == member["type"] ) return; json["target"].emplace_back( m["uid"].as() ); }); } else { std::vector targets; // for ( auto& m : metadata["battle"]["transients"] ) { ext::json::forEach(member["battle"]["transients"], [&](ext::json::Value& m){ if ( skillData["target"] == "ally" && m["type"] != member["type"] ) return; if ( skillData["target"] != "ally" && m["type"] == member["type"] ) return; targets.push_back( m["uid"].as() ); }); int ri = floor(rand() % targets.size()); json["target"] = ext::json::array(); json["target"].emplace_back(targets.at(ri)); } } uf::Serializer elementData; uf::Serializer cardData = masterDataGet("Card", member["id"].as()); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); std::string elementId; float basePower = member["type"] == "enemy" ? 1.2f : 1.0f; if ( skillId != "0" ) { payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "" + charaData["name"].as() + ""+colorString("FFFFFF") + ": "+colorString("7777FF") + "" + skillData["name"].as() + colorString("FFFFFF"); elementId = skillData["type"].as(); elementData = masterDataGet("Element", elementId); } else { payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "" + charaData["name"].as() + ""+colorString("FFFFFF") + ": "+colorString("7777FF") + "攻撃" + colorString("FFFFFF"); elementId = "0"; if ( cardData["weapon_type"] == "1" ) elementId = "1"; // slash if ( cardData["weapon_type"] == "2" ) elementId = "3"; // strike if ( cardData["weapon_type"] == "3" ) elementId = "2"; // pierce elementData = masterDataGet("Element", elementId); } struct { float damage = 0.0f; } delays; struct { bool endTurn = false; int loseTurn = 0; } turnEffects; std::function showMessage = [&]( uf::Serializer& json ) { uf::Serializer payload; payload["color"] = json["color"]; payload["target"]["uid"] = json["uid"]; payload["target"]["damage"] = json["message"]; ::gui->queueHook("world:Battle.Damage.%UID%", payload, delays.damage); delays.damage += 0.25f; }; { int64_t mp = member["mp"].as(); int64_t hp = member["hp"].as(); int64_t mpCost = skillData["mp"].as(); int64_t hpCost = skillData["hp%"].as(); // for ( auto& status : member["statuses"] ) { ext::json::forEach(member["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( !ext::json::isNull( statusData["type"] ) && elementData["type"] != statusData["type"] ) return; if ( !ext::json::isNull( statusData["attributes"] ) ) { bool found = false; for ( auto& value : statusData["attributes"] ) { if ( value == elementData["id"] ) found = true; } if ( !found ) return; } if ( hpCost > 0 && statusData["cost"].is() ) { hpCost -= statusData["cost"].as(); } }); hpCost = member["max hp"].as() * ( hpCost / 100.0f ); if ( mp < mpCost ) { payload["message"] = "Not enough MP to use `"+colorString("FF0000") + ""+skillData["name"].as()+""+colorString("FFFFFF") + "`."; payload["timeout"] = 2.0f; if ( member["type"] != "enemy" ) { payload["invalid"] = true; } return payload; } else if ( mpCost > 0 ) { metadata["battle"]["transients"][member["uid"].as()]["mp"] = mp - mpCost; } if ( hp < hpCost ) { payload["message"] = "Not enough HP to use `"+colorString("FF0000") + ""+skillData["name"].as()+""+colorString("FFFFFF") + "`."; payload["timeout"] = 2.0f; if ( member["type"] != "enemy" ) { payload["invalid"] = true; return payload; } else { skillId = "0"; skillData = masterDataGet("Skill", skillId); } } else if ( hpCost > 0 ) { metadata["battle"]["transients"][member["uid"].as()]["hp"] = hp - hpCost; { uf::Serializer payload; payload["uid"] = member["uid"]; payload["message"] = hpCost; payload["color"] = "FF0000"; showMessage( payload ); } } } if ( skillData["turns+"].is() ) { member["used skills"].emplace_back(skillId); payload["message"] = payload["message"].as() + "\nGained "+colorString("FF0000") + ""+skillData["turns+"].as()+""+colorString("FFFFFF") + " additional turns!"; metadata["battle"]["turn"]["counter"] = metadata["battle"]["turn"]["counter"].as() + skillData["turns+"].as(); } // for ( auto& targetId : json["target"] ) { ext::json::forEach(json["target"], [&](ext::json::Value& targetId){ uf::Serializer target = metadata["battle"]["transients"][targetId.as()]; float power = basePower; float morePower = 0; float probability = skillData["proc"].is() ? skillData["proc"].as() / 100.0f : 1.0f; // modify probability from statuses // for ( auto& status : metadata["battle"]["transients"][json["uid"].as()]["statuses"] ) { ext::json::forEach(metadata["battle"]["transients"][json["uid"].as()]["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( !ext::json::isNull( statusData["type"] ) && elementData["type"] != statusData["type"] ) return; if ( !ext::json::isNull( statusData["attributes"] ) ) { bool found = false; for ( auto& value : statusData["attributes"] ) { if ( value == elementData["id"] ) found = true; } if ( !found ) return; } if ( statusData["proc+"].is() ) { float moreProb = statusData["proc+"].as() / 100.0f; probability += moreProb; payload["message"] = payload["message"].as() + "\n"+colorString("FF00FF") + "" + std::to_string((int)(moreProb*100)) + "%"+colorString("FF0000") + " more probability"+colorString("FFFFFF") + ""; } }); // find attack modifying statuses on attacker // for ( auto& status : metadata["battle"]["transients"][json["uid"].as()]["statuses"] ) { ext::json::forEach(metadata["battle"]["transients"][json["uid"].as()]["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( !ext::json::isNull( statusData["type"] ) && elementData["type"] != statusData["type"] ) return; if ( !ext::json::isNull( statusData["attributes"] ) ) { bool found = false; for ( auto& value : statusData["attributes"] ) { if ( value == elementData["id"] ) found = true; } if ( !found ) return; } if ( !ext::json::isNull( statusData["ailments"] ) ) return; if ( statusData["power"].is() ) { float adjust = statusData["power"].as() / 100.0f; morePower += adjust; // payload["message"] = payload["message"].as() + "\n"+colorString("FF00FF") + "" + std::to_string((int)(adjust*100)) + "%"+colorString("FF0000") + " more power"+colorString("FFFFFF") + ""; } }); // find defense modifying statuses on target // for ( auto& status : metadata["battle"]["transients"][targetId.as()]["statuses"] ) { ext::json::forEach(metadata["battle"]["transients"][targetId.as()]["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( !ext::json::isNull( statusData["type"] ) && elementData["type"] != statusData["type"] ) return; if ( !ext::json::isNull( statusData["attributes"] ) ) { bool found = false; for ( auto& value : statusData["attributes"] ) { if ( value == elementData["id"] ) found = true; } if ( !found ) return; } if ( statusData["defense"].is() ) { float adjust = statusData["defense"].as() / 100.0f; morePower -= adjust; // payload["message"] = payload["message"].as() + "\n"+colorString("FF00FF") + "" + std::to_string((int)(adjust*100)) + "%"+colorString("FF0000") + " more defense"+colorString("FFFFFF") + ""; } }); // if ( morePower != 0 ) payload["message"] = payload["message"].as() + "\n"+colorString("FF00FF") + "" + std::to_string((int)(morePower*100)) + "%"+colorString("FF0000") + " power modifier"+colorString("FFFFFF") + ""; float r = (rand() % 100) / 100.0; // missed if ( r > probability ) { uf::Serializer payload; payload["target"]["uid"] = target["uid"]; payload["target"]["damage"] = "Miss"; ::gui->queueHook("world:Battle.Damage.%UID%", payload); return; } bool repel = false; std::function affinityCheck = [&]( const std::string& targetId ) { auto& target = metadata["battle"]["transients"][targetId]; uf::Serializer cardData = masterDataGet("Card", target["id"].as()); uf::Serializer affinity = cardData["affinity"][elementId]; // affinity change check // for ( auto& status : target["statuses"] ) { ext::json::forEach(target["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( !ext::json::isNull( statusData["type"] ) && elementData["type"] != statusData["type"] ) return; if ( !ext::json::isNull( statusData["attributes"] ) ) { bool found = false; for ( auto& value : statusData["attributes"] ) { if ( value == elementData["id"] ) found = true; } if ( !found ) return; } if ( !ext::json::isNull( statusData["affinity"] ) ) { if ( !ext::json::isNull( statusData["affinity"][elementId] ) ) affinity = statusData["affinity"][elementId]; // payload["message"] = payload["message"].as() + "\naffinity modifier."+colorString("FFFFFF") + ""; } }); if ( !ext::json::isNull( affinity ) ) { std::string affinityStr = affinity.as(); if ( repel && affinityStr == "repel" ) affinityStr = "null"; if ( affinityStr == "weak" ) { payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "Weak!\n"; morePower *= 2.0f; } else if ( affinityStr == "strong" ) { payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "Strong!\n"; morePower *= 0.5f; } else if ( affinityStr == "drain" ) { turnEffects.endTurn = true; payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "Drain!\n"; morePower *= -1.0f; } else if ( affinityStr == "null" ) { turnEffects.loseTurn = 2; payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "Nulled!\n"; morePower *= 0; } else if ( affinityStr == "repel" ) { turnEffects.endTurn = true; payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + "Repelled!\n"; repel = true; // check again affinityCheck(json["uid"].as()); } else { affinityStr = ""; } if ( affinityStr != "" ) { uf::Serializer payload; payload["uid"] = targetId; payload["message"] = affinityStr; payload["color"] = "FF0000"; showMessage( payload ); } } }; affinityCheck(target["uid"].as()); power += morePower; // damage calc float damage = 0.0f; { float calcDamage = 0.0f; // normal attack if ( elementData["type"] == "Physical" ) { if ( skillData["hp%"].is() ) { // max hp * power calcDamage = (member["max hp"].as() * skillData["power"].as() * 0.00114f ); } else { // ( lv + str ) * power / 15 calcDamage = (member["lv"].as() + member["str"].as()) * skillData["power"].as() / 15.0f; } } else if ( elementData["type"] == "Magic" ) { if ( member["lv"].as() < 30 ) { calcDamage *= skillData["power"].as() * member["lv"].as() * ( 2 * member["mag"].as() + 70.0f ) / 1000.0f; } else if ( member["lv"].as() < 160 ) { calcDamage = 3.0f * skillData["power"].as() * ( 2 * member["mag"].as() + 70.0f - ( 0.4f * member["lv"].as() ) ) / 100.0f; } else { calcDamage = 3.0f * skillData["power"].as() * ( 2 * member["mag"].as() + 5 ) / 100.0f; } } else { std::cout << elementData << ": " << skillData << std::endl; } damage = (int) (calcDamage * power); } if ( ext::json::isArray( skillData["hits"] ) ) { float low = skillData["hits"][0].as(); float high = skillData["hits"][1].as(); int hits = low + r * (high - low); damage *= hits; } // hama/mudo instakill bool instakill = false; if ( elementId == "12" || elementId == "13" ) { instakill = true; } bool critted = false; std::function applyDamage = [&]( const std::string& targetId ) { auto& target = metadata["battle"]["transients"][targetId]; uf::Serializer cardData = masterDataGet("Card", target["id"].as()); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); int curHp = target["hp"].as(); bool isCrit = ((rand() % 100) / 100.0) < 0.1; if ( damage == 0 ) isCrit = false; float oldDamage = damage; if ( isCrit ) { damage *= 1.5; critted = true; } // is a heal if ( elementId == "14" ) { float power = skillData["power"].as() / 100.0f * (morePower == 0 ? 1 : morePower); std::cout << target["max hp"].as() << ", " << power << std::endl; damage = -1 * std::abs(target["max hp"].as() * power); } if ( instakill ) damage = curHp; target["hp"] = (int) (curHp -= damage); if ( target["hp"].as() > target["max hp"].as() ) target["hp"] = target["max hp"].as(); std::cout << power << ", " << damage << ", " << morePower << (isCrit ? " / crit" : "") << std::endl; { uf::Serializer payload; payload["uid"] = target["uid"]; payload["crit"] = isCrit; payload["message"] = ""; if ( damage > 0 ) { payload["color"] = "FF0000"; payload["message"] = (int) damage; if ( skillData["drains"].as() ) { float ratio = damage / target["max hp"].as(); int mpDrain = target["max mp"].as() * ratio; if ( target["mp"].as() > mpDrain ) mpDrain = target["mp"].as(); target["mp"] = target["mp"].as() - mpDrain; payload["message"] = payload["message"].as() + "\n " + colorString("0000FF") + std::to_string(mpDrain); showMessage( payload ); payload["uid"] = member["uid"]; payload["color"] = "00FF00"; } // heal } else if ( damage < 0 ) { payload["message"] = (int) -damage; payload["color"] = "00FF00"; } showMessage( payload ); } damage = oldDamage; if ( curHp <= 0 ) { payload["message"] = payload["message"].as() + "\n"+colorString("FF0000") + ""+ charaData["name"].as() +" "+colorString("FFFFFF") + "died!"; target["hp"] = 0; uf::Serializer statusPayload; statusPayload["id"] = "21"; target["statuses"] = ext::json::array(); target["statuses"].emplace_back(statusPayload); } }; applyDamage(repel ? json["uid"].as() : target["uid"].as()); // counter statuses if ( !repel ) { // for ( auto& status : metadata["battle"]["transients"][target["uid"].as()]["statuses"] ) { ext::json::forEach(metadata["battle"]["transients"][target["uid"].as()]["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); if ( statusId != "19" ) return; uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( !ext::json::isNull( statusData["type"] ) && elementData["type"] != statusData["type"] ) return; if ( !ext::json::isNull( statusData["attributes"] ) ) { bool found = false; for ( auto& value : statusData["attributes"] ) { if ( value == elementData["id"] ) found = true; } if ( !found ) return; } if ( statusData["proc"].is() ) { float r = (rand() % 100) / 100.0; if ( r <= statusData["proc"].as() / 100.0f ) { applyDamage(json["uid"].as()); payload["message"] = payload["message"].as() + "\nCountered!"; } } }); } // apply status { // for ( auto& status : skillData["statuses"] ) { ext::json::forEach(skillData["statuses"], [&](ext::json::Value& status){ std::string target = status["target"].as(); std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); if ( skillData["type"].as() == 16 ) return; float r = (rand() % 100) / 100.0; if ( repel ) { if ( target == "enemy" ) target = "self"; else target = ""; } if ( statusData["proc"].is() ) { // failed if ( r > statusData["proc"].as() / 100.0f ) { target = ""; } } uf::Serializer targets; if ( target == "self" ) { targets.emplace_back(json["uid"].as()); // } else if ( target == "enemy" || target == "ally" ) { } else { targets.emplace_back(targetId.as()); } // for ( auto& target : targets ) { ext::json::forEach(targets, [&](ext::json::Value& target){ // remove status(es) if ( status["type"] == "remove" ) { for ( auto& statusC : metadata["battle"]["transients"][target.as()]["statuses"] ) { if ( statusC["id"] == statusId ) { statusC = ext::json::null(); statusC["id"] = 0; statusC["turns"] = -1; } } return; } uf::Serializer statusPayload; statusPayload["id"] = statusId; statusPayload["modifier"] = status["modifier"]; statusPayload["turns"] = status["turns"].is() ? status["turns"] : statusData["turns"]; metadata["battle"]["transients"][target.as()]["statuses"].emplace_back(statusPayload); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } uf::Serializer cardData = masterDataGet( "Card", metadata["battle"]["transients"][target.as()]["id"].as() ); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); // payload["message"] = payload["message"].as() + "\nApplied "+colorString("FF0000") + "" + statusData["name"].as() + ""+colorString("FFFFFF") + " to " + charaData["name"].as(); { uf::Serializer payload; payload["uid"] = targetId; payload["message"] = skillData["name"].as(); payload["color"] = "9900FF"; showMessage( payload ); } }); }); } if ( damage > 0 ) { float r = (rand() % 100) / 100.0; // if ( r < 0.5 ) { if ( r < 1.0 ) { playSound(*this, member["id"].as(), r > 0.25 ? "attack" : "skill"); } else { playSound(*this, target["id"].as(), r > 0.75 ? "damagedsmall" : "damagedlarge"); } for ( int i = 0; i < 16; ++i ) { uf::Serializer payload; payload["uid"] = target["uid"]; payload["phase"] = i % 2 == 0 ? 0 : 1; this->queueHook("world:Battle.OnHit.%UID%", payload, 0.05f * i); } } if ( critted ) { uf::Serializer payload; payload["uid"] = json["uid"]; payload["id"] = member["id"]; ::gui->queueHook("world:Battle.OnCrit.%UID%", payload, 0.1f); } }); /* heal self */ if ( skillData["add hp"].is() ) { int heal = skillData["add hp"].as() / 100.0f * member["max hp"].as(); if ( heal + member["hp"].as() >= member["max hp"].as() ) heal = member["max hp"].as() - member["hp"].as(); member["hp"] = member["hp"].as() + heal; uf::Serializer payload; payload["uid"] = member["uid"]; payload["message"] = ""; payload["color"] = "00FF00"; payload["message"] = (int) heal; showMessage( payload ); } if ( turnEffects.endTurn ) { turnEffects.loseTurn = 99; } if ( turnEffects.loseTurn > 0 ) { auto& turnState = metadata["battle"]["turn"]; float decrement = turnState["decrement"].as(); turnState["counter"] = turnState["counter"].as() - turnEffects.loseTurn; turnState["decrement"] = 0; } payload["uid"] = json["uid"]; payload["target"] = json["target"]; payload[""]["id"] = member["id"]; payload[""]["skill"] = skillId; payload["timeout"] = 2.0f; return payload; }); this->addHook( "world:Battle.Get.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; auto& member = metadata["battle"]["transients"][json["uid"].as()]; /* ext::Housamo* transient = transients.at(uid); uf::Serializer& member = transient->getComponent(); */ std::string message; uf::Serializer cardData = masterDataGet("Card", member["id"].as()); uf::Serializer charaData = masterDataGet("Chara", cardData["character_id"].as()); uf::Serializer affinity = cardData["affinity"]; std::string name = charaData["name"].as(); if ( member["type"] == "player" ) message += colorString("00FF00"); else if ( member["type"] == "enemy" ) message += colorString("FF0000"); else message += colorString("AAAAAA"); message += name + colorString("FFFFFF") + ": " + member["type"].as(); message += "\n" + colorString("FF0000") + "HP: " + member["hp"].as(); message += "\n" + colorString("0000FF") + "MP: " + member["mp"].as(); message += "\n" + colorString("9900FF") + "Ailments" + colorString("FFFFFF") + ":"; // for ( auto& status : member["statuses"] ) { ext::json::forEach(member["statuses"], [&](ext::json::Value& status){ std::string statusId = status["id"].as(); uf::Serializer statusData = masterDataGet("Status", statusId); for ( auto it = status["modifier"].begin(); it != status["modifier"].end(); ++it ) { statusData[it.key()] = *it; } if ( statusId == "0" ) return; // affinity change check for ( auto it = statusData["affinity"].begin(); it != statusData["affinity"].end(); ++it ) { affinity[it.key()] = *it; } message += "\n\t" + statusData["name"].as() + (ext::json::isNull( status["turns"] ) ? "" : " (Turns: " + status["turns"].as() + ")" ); }); message += "\n" + colorString("9900FF") + "Affinities" + colorString("FFFFFF") + ":"; // for ( auto it = affinity.begin(); it != affinity.end(); ++it ) { // std::string elementId = it.key(); // std::string value = it->as(); ext::json::forEach(affinity, [&](const std::string& elementId, ext::json::Value& value){ uf::Serializer elementData = masterDataGet("Element", elementId); message += "\n\t" + elementData["name"].as() + ": " + value.as(); }); uf::Serializer payload; payload[""] = member; // payload["timeout"] = 0.1f; payload["message"] = message; return payload; }); this->addHook( "world:Battle.End.%UID%", [&](const std::string& event)->std::string{ uf::Serializer json = event; // play music /* std::cout << "Previous BGM: " << previousMusic << std::endl; playMusic(*this, previousMusic); */ uf::Scene& scene = uf::scene::getCurrentScene(); scene.callHook("world:Music.LoadPrevious.%UID%"); ::gui->callHook("menu:Close.%UID%"); // cleanup for ( ext::Housamo* pointer : transients ) { pointer->destroy(); this->removeChild(*pointer); delete pointer; } transients.clear(); if ( json["won"].as() ) { // play death sound // playSound(*this, metadata["battle"]["enemy"]["uid"].as(), "killed"); // kill { uf::Scene& scene = uf::scene::getCurrentScene(); std::size_t uid = metadata["battle"]["enemy"]["uid"].as(); uf::Entity* = scene.findByUid(uid); if ( ) { uf::Object& parent = ->getParent(); ->destroy(); parent.removeChild(*); } } } { uf::Serializer payload; payload["battle"] = metadata["battle"]; this->callHook("world:Battle.End", payload); } uf::Serializer payload; payload["end"] = true; return payload; /* if ( json["action"].as() == "killed" ) { // play death sound playSound(*this, metadata["battle"]["enemy"]["uid"].as(), "killed"); // kill { uf::Scene& scene = uf::scene::getCurrentScene(); std::size_t uid = metadata["battle"]["enemy"]["uid"].as(); uf::Entity* = scene.findByUid(uid); if ( ) { uf::Object& parent = ->getParent(); ->destroy(); parent.removeChild(*); } } } // clear battle data on player { uf::Scene& scene = uf::scene::getCurrentScene(); std::size_t uid = metadata["battle"]["player"]["uid"].as(); uf::Entity* = scene.findByUid(uid); if ( ) { uf::Serializer& pMetadata = ->getComponent(); pMetadata["system"].erase("battle"); } } uf::Serializer payload; payload["end"] = true; return payload; return "true"; */ }); } void ext::HousamoBattle::tick() { uf::Object::tick(); } void ext::HousamoBattle::destroy() { uf::Object::destroy(); } void ext::HousamoBattle::render() { uf::Object::render(); } #endif