jak-project/common/serialization/subtitles2/subtitles2_ser.h
ManDude 18ddd1613c
Jak 2 pc subtitle support (#2672)
Adds support for adding custom subtitles to Jak 2 audio. Comes with a
new editor for the new system and format. Compared to the Jak 1 system,
this is much simpler to make an editor for.

Comes with a few subtitles already made as an example.
Cutscenes are not officially supported but you can technically subtitle
those with editor, so please don't right now.

This new system supports multiple subtitles playing at once (even from a
single source!) and will smartly push the subtitles up if there's a
message already playing:

![image](https://github.com/open-goal/jak-project/assets/7569514/033e6374-a05a-4c31-b029-51868153a932)

![image](https://github.com/open-goal/jak-project/assets/7569514/5298aa6d-a183-446e-bdb6-61c4682df917)

Unlike in Jak 1, it will not hide the bottom HUD when subtitles are
active:

![image](https://github.com/open-goal/jak-project/assets/7569514/d466bfc0-55d0-4689-a6e1-b7784b9fff59)

Sadly this leaves us with not much space for the subtitle region (and
the subtitles are shrunk when the minimap is enabled) but when you have
guards and citizens talking all the time, hiding the HUD every time
anyone spoke would get really frustrating.

The subtitle speaker is also color-coded now, because I thought that
would be fun to do.

TODO:
- [x] proper cutscene support.
- [x] merge mode for cutscenes so we don't have to rewrite the script?

---------

Co-authored-by: Hat Kid <6624576+Hat-Kid@users.noreply.github.com>
2023-06-08 01:04:16 +01:00

108 lines
3 KiB
C++

#pragma once
#include <string>
#include <vector>
#include "common/util/Assert.h"
#include "common/util/FileUtil.h"
#include "common/util/FontUtils.h"
#include "common/util/json_util.h"
#include "common/versions/versions.h"
const std::vector<std::string> get_speaker_names(GameVersion version);
struct Subtitle2Line {
Subtitle2Line() {}
Subtitle2Line(float start,
float end,
const std::string& text,
const std::string& speaker,
bool offscreen,
bool merge)
: start(start), end(end), text(text), speaker(speaker), offscreen(offscreen), merge(merge) {}
float start, end;
std::string text;
// name in enum. saved as int later.
std::string speaker;
bool offscreen, merge;
bool operator<(const Subtitle2Line& other) const {
return (start < other.start) || (start == other.start && end < other.end);
}
};
void to_json(json& j, const Subtitle2Line& obj);
void from_json(const json& j, Subtitle2Line& obj);
struct Subtitle2Scene {
bool scene = false;
std::vector<Subtitle2Line> lines;
};
void to_json(json& j, const Subtitle2Scene& obj);
void from_json(const json& j, Subtitle2Scene& obj);
struct GameSubtitle2Bank {
GameSubtitle2Bank(int lang) : lang(lang) {}
int lang;
GameTextVersion text_version = GameTextVersion::JAK2;
std::string file_path;
std::map<std::string, std::string> speakers;
std::map<std::string, Subtitle2Scene> scenes;
bool scene_exists(const std::string& name) const { return scenes.find(name) != scenes.end(); }
void add_scene(const std::string& name, Subtitle2Scene& scene) {
ASSERT(!scene_exists(name));
scenes.insert({name, scene});
}
};
void to_json(json& j, const GameSubtitle2Bank& obj);
void from_json(const json& j, GameSubtitle2Bank& obj);
class GameSubtitle2DB {
public:
GameSubtitle2DB(GameVersion version) : m_version(version) {}
const std::map<int, std::shared_ptr<GameSubtitle2Bank>>& banks() const { return m_banks; }
bool bank_exists(int id) const { return m_banks.find(id) != m_banks.end(); }
std::shared_ptr<GameSubtitle2Bank> add_bank(std::shared_ptr<GameSubtitle2Bank> bank) {
ASSERT(!bank_exists(bank->lang));
m_banks[bank->lang] = bank;
return bank;
}
std::shared_ptr<GameSubtitle2Bank> bank_by_id(int id) {
if (!bank_exists(id)) {
return nullptr;
}
return m_banks.at(id);
}
std::map<int, std::shared_ptr<GameSubtitle2Bank>> m_banks;
std::unique_ptr<GameSubtitle2Bank> m_subtitle_groups;
GameVersion version() const { return m_version; }
private:
GameVersion m_version;
};
struct GameSubtitle2DefinitionFile {
std::string file_path = "";
int language_id = -1;
GameTextVersion text_version = GameTextVersion::JAK2;
};
void parse_subtitle2_json(GameSubtitle2DB& db, const GameSubtitle2DefinitionFile& file_info);
void open_subtitle2_project(const std::string& kind,
const std::string& filename,
std::vector<GameSubtitle2DefinitionFile>& inputs);
GameSubtitle2DB load_subtitle2_project(GameVersion game_version);