#include "dialogue/DialogueDatabase.h" #include "utils/Utils.h" #include namespace ZL { extern const char* CONST_ZIP_FILE; } namespace ZL::Dialogue { NodeType DialogueDatabase::parseNodeType(const std::string& value) { if (value == "Choice") return NodeType::Choice; if (value == "Condition") return NodeType::Condition; if (value == "SetFlag") return NodeType::SetFlag; if (value == "Jump") return NodeType::Jump; if (value == "End") return NodeType::End; if (value == "CutsceneStart") return NodeType::CutsceneStart; return NodeType::Line; } ChoiceKind DialogueDatabase::parseChoiceKind(const std::string& value) { if (value == "Optional") return ChoiceKind::Optional; if (value == "Exit") return ChoiceKind::Exit; return ChoiceKind::Main; } ComparisonOp DialogueDatabase::parseComparisonOp(const std::string& value) { if (value == "==" || value == "Equals") return ComparisonOp::Equals; if (value == "!=" || value == "NotEquals") return ComparisonOp::NotEquals; if (value == ">=" || value == "GreaterOrEqual") return ComparisonOp::GreaterOrEqual; if (value == "<=" || value == "LessOrEqual") return ComparisonOp::LessOrEqual; return ComparisonOp::Exists; } Condition DialogueDatabase::parseCondition(const json& j) { Condition c; c.flag = j.value("flag", ""); c.op = parseComparisonOp(j.value("op", "Exists")); c.value = j.value("value", 1); return c; } Effect DialogueDatabase::parseEffect(const json& j) { Effect e; e.flag = j.value("flag", ""); e.value = j.value("value", 1); e.relative = j.value("relative", false); return e; } Choice DialogueDatabase::parseChoice(const json& j) { Choice c; c.id = j.value("id", ""); c.text = j.value("text", ""); c.next = j.value("next", ""); c.kind = parseChoiceKind(j.value("kind", "Main")); c.consumeOnce = j.value("consumeOnce", false); if (j.contains("conditions") && j["conditions"].is_array()) { for (const auto& item : j["conditions"]) { c.conditions.push_back(parseCondition(item)); } } if (j.contains("effects") && j["effects"].is_array()) { for (const auto& item : j["effects"]) { c.effects.push_back(parseEffect(item)); } } return c; } Node DialogueDatabase::parseNode(const json& j) { Node node; node.id = j.value("id", ""); node.type = parseNodeType(j.value("type", "Line")); node.speaker = j.value("speaker", ""); node.text = j.value("text", ""); node.portrait = j.value("portrait", ""); node.next = j.value("next", ""); node.trueNext = j.value("trueNext", ""); node.falseNext = j.value("falseNext", ""); node.cutsceneId = j.value("cutsceneId", ""); node.luaCallback= j.value("luaCallback", ""); node.chatBubble = j.value("chatBubble", ""); node.questUnlock = j.value("questUnlock", ""); node.questComplete = j.value("questComplete", ""); node.questFail = j.value("questFail", ""); node.objectiveComplete = j.value("objectiveComplete", ""); node.objectiveVisible = j.value("objectiveVisible", ""); if (j.contains("conditions") && j["conditions"].is_array()) { for (const auto& item : j["conditions"]) { node.conditions.push_back(parseCondition(item)); } } if (j.contains("effects") && j["effects"].is_array()) { for (const auto& item : j["effects"]) { node.effects.push_back(parseEffect(item)); } } if (j.contains("choices") && j["choices"].is_array()) { for (const auto& item : j["choices"]) { node.choices.push_back(parseChoice(item)); } } return node; } DialogueDefinition DialogueDatabase::parseDialogue(const json& j) { DialogueDefinition result; result.id = j.value("id", ""); result.displayName = j.value("displayName", result.id); result.startNode = j.value("start", ""); result.uninterruptible= j.value("uninterruptible", false); if (j.contains("nodes") && j["nodes"].is_array()) { for (const auto& item : j["nodes"]) { Node node = parseNode(item); if (!node.id.empty()) { result.nodes[node.id] = std::move(node); } } } return result; } bool DialogueDatabase::loadFromFile(const std::string& path) { dialogues.clear(); std::string raw; try { if (strlen(CONST_ZIP_FILE) == 0) { raw = readTextFile(path); } else { auto buf = readFileFromZIP(path, CONST_ZIP_FILE); if (buf.empty()) { std::cerr << "[dialogue] Failed to read " << path << " from zip\n"; throw std::runtime_error("Failed to load dialogue file: " + path); } raw.assign(buf.begin(), buf.end()); } } catch (const std::exception& e) { std::cerr << "[dialogue] Failed to open " << path << ": " << e.what() << "\n"; throw std::runtime_error("Failed to load dialogue file: " + path); } json root; try { root = json::parse(raw); } catch (const std::exception& e) { std::cerr << "[dialogue] JSON parse error in " << path << ": " << e.what() << "\n"; return false; } if (root.contains("dialogues") && root["dialogues"].is_array()) { for (const auto& item : root["dialogues"]) { DialogueDefinition dialogue = parseDialogue(item); if (!dialogue.id.empty()) { dialogues[dialogue.id] = std::move(dialogue); } } } return !dialogues.empty(); } const DialogueDefinition* DialogueDatabase::findDialogue(const std::string& id) const { auto it = dialogues.find(id); return (it != dialogues.end()) ? &it->second : nullptr; } } // namespace ZL::Dialogue