space-game001/src/cutscene/CutsceneDatabase.cpp
2026-06-06 22:04:42 +03:00

173 lines
5.9 KiB
C++

#include "cutscene/CutsceneDatabase.h"
#include "utils/Utils.h"
#include <iostream>
namespace ZL
{
extern const char* CONST_ZIP_FILE;
}
namespace ZL::Cutscene {
EasingType CutsceneDatabase::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 CutsceneDatabase::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;
}
CutsceneLine CutsceneDatabase::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.backgroundWidth = j.value("backgroundWidth", 0);
line.backgroundHeight= j.value("backgroundHeight", 0);
line.luaCallback = j.value("luaCallback", "");
line.durationMs = j.value("durationMs", 0);
line.waitForConfirm = j.value("waitForConfirm", false);
line.questUnlock = j.value("questUnlock", "");
line.questComplete = j.value("questComplete", "");
line.questFail = j.value("questFail", "");
line.objectiveComplete = j.value("objectiveComplete", "");
line.objectiveVisible = j.value("objectiveVisible", "");
return line;
}
CutsceneCameraPose CutsceneDatabase::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 CutsceneDatabase::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;
}
CutsceneImageCue CutsceneDatabase::parseCutsceneImageCue(const json& j) {
CutsceneImageCue cue;
cue.path = j.value("path", "");
cue.startMs = j.value("startMs", 0);
cue.endMs = j.value("endMs", 0);
cue.fadeInMs = j.value("fadeInMs", 0);
cue.fadeOutMs = j.value("fadeOutMs", 0);
return cue;
}
StaticCutsceneDefinition CutsceneDatabase::parseCutscene(const json& j) {
StaticCutsceneDefinition cutscene;
cutscene.id = j.value("id", "");
cutscene.background = j.value("background", "");
cutscene.backgroundWidth = j.value("backgroundWidth", 1280);
cutscene.backgroundHeight= j.value("backgroundHeight", 720);
cutscene.music = j.value("music", "");
cutscene.skippable = j.value("skippable", true);
cutscene.durationMs = j.value("durationMs", 0);
cutscene.fadeOutMs = j.value("fadeOutMs", 0);
cutscene.fadeInMs = j.value("fadeInMs", 0);
cutscene.endFadeOutMs = j.value("endFadeOutMs", 0);
cutscene.endFadeInMs = j.value("endFadeInMs", 0);
cutscene.onFadeInCallback= j.value("onFadeInCallback", "");
if (j.contains("cameraTrack") && j["cameraTrack"].is_array()) {
for (const auto& item : j["cameraTrack"]) {
cutscene.cameraTrack.push_back(parseCutsceneCameraSegment(item));
}
}
if (j.contains("images") && j["images"].is_array()) {
for (const auto& item : j["images"]) {
cutscene.images.push_back(parseCutsceneImageCue(item));
}
}
if (j.contains("lines") && j["lines"].is_array()) {
for (const auto& item : j["lines"]) {
cutscene.lines.push_back(parseCutsceneLine(item));
}
}
return cutscene;
}
bool CutsceneDatabase::loadFromFile(const std::string& path) {
cutscenes.clear();
std::string raw;
try {
if (strlen(ZL::CONST_ZIP_FILE) == 0) {
raw = readTextFile(path);
}
else {
auto buf = readFileFromZIP(path, ZL::CONST_ZIP_FILE);
if (buf.empty()) {
std::cerr << "[cutscene] Failed to read " << path << " from zip\n";
throw std::runtime_error("Failed to load cutscene file: " + path);
}
raw.assign(buf.begin(), buf.end());
}
}
catch (const std::exception& e) {
std::cerr << "[cutscene] Failed to open " << path << ": " << e.what() << "\n";
throw std::runtime_error("Failed to load cutscene file: " + path);
}
json root;
try {
root = json::parse(raw);
}
catch (const std::exception& e) {
std::cerr << "[cutscene] JSON parse error in " << path << ": " << e.what() << "\n";
return false;
}
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 true;
}
const StaticCutsceneDefinition* CutsceneDatabase::findCutscene(const std::string& id) const {
auto it = cutscenes.find(id);
return (it != cutscenes.end()) ? &it->second : nullptr;
}
} // namespace ZL::Cutscene