252 lines
7.7 KiB
C++
252 lines
7.7 KiB
C++
#include "dialogue/DialogueDatabase.h"
|
|
|
|
#include "utils/Utils.h"
|
|
#include <iostream>
|
|
|
|
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;
|
|
}
|
|
|
|
EasingType DialogueDatabase::parseEasingType(const std::string& value) {
|
|
if (value == "EaseInSine") return EasingType::EaseInSine;
|
|
if (value == "EaseOutSine") return EasingType::EaseOutSine;
|
|
if (value == "EaseInOutSine") return EasingType::EaseInOutSine;
|
|
if (value == "EaseInQuad") return EasingType::EaseInQuad;
|
|
if (value == "EaseOutQuad") return EasingType::EaseOutQuad;
|
|
if (value == "EaseInOutQuad") return EasingType::EaseInOutQuad;
|
|
if (value == "EaseInCubic") return EasingType::EaseInCubic;
|
|
if (value == "EaseOutCubic") return EasingType::EaseOutCubic;
|
|
if (value == "EaseInOutCubic") return EasingType::EaseInOutCubic;
|
|
return EasingType::Linear;
|
|
}
|
|
|
|
CutsceneAnchor DialogueDatabase::parseCutsceneAnchor(const std::string& value) {
|
|
if (value == "TopLeft") return CutsceneAnchor::TopLeft;
|
|
if (value == "TopRight") return CutsceneAnchor::TopRight;
|
|
if (value == "BottomRight") return CutsceneAnchor::BottomRight;
|
|
if (value == "BottomLeft") return CutsceneAnchor::BottomLeft;
|
|
if (value == "Custom") return CutsceneAnchor::Custom;
|
|
return CutsceneAnchor::Center;
|
|
}
|
|
|
|
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", "");
|
|
|
|
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", "");
|
|
|
|
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;
|
|
}
|
|
|
|
CutsceneLine DialogueDatabase::parseCutsceneLine(const json& j) {
|
|
CutsceneLine line;
|
|
line.speaker = j.value("speaker", "");
|
|
line.text = j.value("text", "");
|
|
line.portrait = j.value("portrait", "");
|
|
line.sfx = j.value("sfx", "");
|
|
line.background = j.value("background", "");
|
|
line.durationMs = j.value("durationMs", 0);
|
|
line.waitForConfirm = j.value("waitForConfirm", false);
|
|
return line;
|
|
}
|
|
|
|
CutsceneCameraPose DialogueDatabase::parseCutsceneCameraPose(const json& j) {
|
|
CutsceneCameraPose pose;
|
|
pose.anchor = parseCutsceneAnchor(j.value("anchor", "Center"));
|
|
pose.centerX = j.value("centerX", 0.5f);
|
|
pose.centerY = j.value("centerY", 0.5f);
|
|
pose.zoom = j.value("zoom", 1.0f);
|
|
pose.rotationDeg = j.value("rotationDeg", 0.0f);
|
|
return pose;
|
|
}
|
|
|
|
CutsceneCameraSegment DialogueDatabase::parseCutsceneCameraSegment(const json& j) {
|
|
CutsceneCameraSegment segment;
|
|
segment.durationMs = j.value("durationMs", 0);
|
|
segment.easing = parseEasingType(j.value("easing", "EaseInOutSine"));
|
|
if (j.contains("from") && j["from"].is_object()) {
|
|
segment.from = parseCutsceneCameraPose(j["from"]);
|
|
}
|
|
if (j.contains("to") && j["to"].is_object()) {
|
|
segment.to = parseCutsceneCameraPose(j["to"]);
|
|
}
|
|
else {
|
|
segment.to = segment.from;
|
|
}
|
|
return segment;
|
|
}
|
|
|
|
StaticCutsceneDefinition DialogueDatabase::parseCutscene(const json& j) {
|
|
StaticCutsceneDefinition cutscene;
|
|
cutscene.id = j.value("id", "");
|
|
cutscene.background = j.value("background", "");
|
|
cutscene.music = j.value("music", "");
|
|
cutscene.skippable = j.value("skippable", true);
|
|
cutscene.durationMs = j.value("durationMs", 0);
|
|
|
|
if (j.contains("cameraTrack") && j["cameraTrack"].is_array()) {
|
|
for (const auto& item : j["cameraTrack"]) {
|
|
cutscene.cameraTrack.push_back(parseCutsceneCameraSegment(item));
|
|
}
|
|
}
|
|
|
|
if (j.contains("lines") && j["lines"].is_array()) {
|
|
for (const auto& item : j["lines"]) {
|
|
cutscene.lines.push_back(parseCutsceneLine(item));
|
|
}
|
|
}
|
|
|
|
return cutscene;
|
|
}
|
|
|
|
bool DialogueDatabase::loadFromFile(const std::string& path) {
|
|
dialogues.clear();
|
|
cutscenes.clear();
|
|
|
|
const std::string raw = ZL::readTextFile(path);
|
|
if (raw.empty()) {
|
|
std::cerr << "[dialogue] Failed to read file: " << path << "\n";
|
|
return false;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (root.contains("cutscenes") && root["cutscenes"].is_array()) {
|
|
for (const auto& item : root["cutscenes"]) {
|
|
StaticCutsceneDefinition cutscene = parseCutscene(item);
|
|
if (!cutscene.id.empty()) {
|
|
cutscenes[cutscene.id] = std::move(cutscene);
|
|
}
|
|
}
|
|
}
|
|
|
|
return !dialogues.empty();
|
|
}
|
|
|
|
const DialogueDefinition* DialogueDatabase::findDialogue(const std::string& id) const {
|
|
auto it = dialogues.find(id);
|
|
return (it != dialogues.end()) ? &it->second : nullptr;
|
|
}
|
|
|
|
const StaticCutsceneDefinition* DialogueDatabase::findCutscene(const std::string& id) const {
|
|
auto it = cutscenes.find(id);
|
|
return (it != cutscenes.end()) ? &it->second : nullptr;
|
|
}
|
|
|
|
} // namespace ZL::Dialogue
|