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>
This commit is contained in:
ManDude 2023-06-08 01:04:16 +01:00 committed by GitHub
parent 3dbaee1ecc
commit 18ddd1613c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
138 changed files with 30019 additions and 2336 deletions

1
.gitignore vendored
View File

@ -74,3 +74,4 @@ __pycache__/
/jak1-*.json
/jak2-*.json
/TODO.md
unifont-15.0.03.ttf

View File

@ -114,7 +114,7 @@
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 2 - Runtime",
"name": "Game - Jak 2 - Runtime (no boot)",
"args": ["-v", "--game", "jak2", "--", "-fakeiso", "-debug"]
},
{
@ -124,6 +124,13 @@
"name": "Game - Jak 2 - Runtime (boot)",
"args": ["-v", "--game", "jak2", "--", "-boot", "-fakeiso", "-debug"]
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Game - Jak 2 - Runtime (release)",
"args": ["-v", "--game", "jak2", "--", "-boot", "-fakeiso"]
},
{
"type": "default",
"project": "CMakeLists.txt",

View File

@ -53,6 +53,8 @@ add_library(common
repl/util.cpp
serialization/subtitles/subtitles_deser.cpp
serialization/subtitles/subtitles_ser.cpp
serialization/subtitles2/subtitles2_deser.cpp
serialization/subtitles2/subtitles2_ser.cpp
type_system/defenum.cpp
type_system/deftype.cpp
type_system/state.cpp

View File

@ -0,0 +1,27 @@
#include "subtitles2_deser.h"
#include "common/log/log.h"
#include "common/util/FileUtil.h"
#include "third-party/json.hpp"
const std::vector<std::string> locale_lookup = {"en-US", "fr-FR", "de-DE", "es-ES",
"it-IT", "jp-JP", "ko-KR", "en-GB"};
bool write_subtitle_db_to_files(const GameSubtitle2DB& db, const GameVersion game_version) {
try {
for (const auto& [language_id, bank] : db.m_banks) {
json data;
to_json(data, *bank);
std::string dump_path = (file_util::get_jak_project_dir() / "game" / "assets" /
version_to_game_name(game_version) / "subtitle" /
fmt::format("subtitle_{}.json", locale_lookup.at(language_id)))
.string();
file_util::write_text_file(dump_path, data.dump(2));
}
} catch (std::exception& ex) {
lg::error(ex.what());
return false;
}
return true;
}

View File

@ -0,0 +1,5 @@
#pragma once
#include "common/serialization/subtitles2/subtitles2_ser.h"
bool write_subtitle_db_to_files(const GameSubtitle2DB& db, const GameVersion game_version);

View File

@ -0,0 +1,192 @@
#include "subtitles2_ser.h"
#include "common/goos/ParseHelpers.h"
#include "common/goos/Reader.h"
#include "common/log/log.h"
#include "common/util/FileUtil.h"
#include "common/util/json_util.h"
// matches enum in `subtitle2.gc` with "none" (first) and "max" (last) removed
const std::vector<std::string> s_speakers_jak2 = {
"computer",
"jak",
"darkjak",
"daxter",
"samos",
"keira",
"keira-before-class-3",
"kid",
"kor",
"metalkor",
"baron",
"errol",
"torn",
"tess",
"guard",
"guard-a",
"guard-b",
"krew",
"sig",
"brutter",
"vin",
"youngsamos",
"youngsamos-before-rescue",
"pecker",
"onin",
"ashelin",
"jinx",
"mog",
"grim",
"agent",
"citizen-male",
"citizen-female",
"oracle",
"precursor",
};
const std::vector<std::string> get_speaker_names(GameVersion version) {
switch (version) {
case GameVersion::Jak2:
return s_speakers_jak2;
break;
}
throw std::runtime_error(
fmt::format("no speakers for game version {} project", version_to_game_name(version)));
}
void parse_subtitle2_json(GameSubtitle2DB& db, const GameSubtitle2DefinitionFile& file_info) {
// TODO - some validation
// Init Settings
std::shared_ptr<GameSubtitle2Bank> bank;
try {
if (!db.bank_exists(file_info.language_id)) {
// database has no lang yet
bank = db.add_bank(std::make_shared<GameSubtitle2Bank>(file_info.language_id));
} else {
bank = db.bank_by_id(file_info.language_id);
}
bank->text_version = file_info.text_version;
bank->file_path = file_info.file_path;
// Parse the file
auto file = parse_commented_json(
file_util::read_text_file(file_util::get_jak_project_dir() / file_info.file_path),
"subtitle2_json");
from_json(file, *bank);
} catch (std::exception& e) {
lg::error("Unable to parse subtitle json entry, couldn't successfully load files - {}",
e.what());
throw;
}
}
void to_json(json& j, const Subtitle2Line& obj) {
j = json{{"start", obj.start}, {"end", obj.end}, {"offscreen", obj.offscreen},
{"merge", obj.merge}, {"speaker", obj.speaker}, {"text", obj.text}};
}
void from_json(const json& j, Subtitle2Line& obj) {
json_deserialize_if_exists(start);
json_deserialize_if_exists(end);
json_deserialize_if_exists(offscreen);
json_deserialize_if_exists(merge);
json_deserialize_if_exists(speaker);
json_deserialize_if_exists(text);
}
void to_json(json& j, const Subtitle2Scene& obj) {
j = json{{"scene", obj.scene}};
json lines;
for (const auto& line : obj.lines) {
json l;
to_json(l, line);
lines.push_back(l);
}
j["lines"] = obj.lines.size() == 0 ? json::array({}) : lines;
}
void from_json(const json& j, Subtitle2Scene& obj) {
json_deserialize_if_exists(scene);
for (auto& kv : j.at("lines").items()) {
auto& line = obj.lines.emplace_back();
from_json(kv.value(), line);
}
}
void to_json(json& j, const GameSubtitle2Bank& obj) {
j = json{{"speakers", obj.speakers}, {"lang", obj.lang}};
json scenes = json::object({});
for (const auto& [name, scene] : obj.scenes) {
json s;
to_json(s, scene);
scenes[name] = s;
}
j["scenes"] = scenes;
}
void from_json(const json& j, GameSubtitle2Bank& obj) {
json_deserialize_if_exists(speakers);
for (auto& kv : j.at("scenes").items()) {
Subtitle2Scene scene;
from_json(kv.value(), scene);
obj.scenes[kv.key()] = scene;
}
json_deserialize_if_exists(lang);
}
void open_subtitle2_project(const std::string& kind,
const std::string& filename,
std::vector<GameSubtitle2DefinitionFile>& subtitle_files) {
goos::Reader reader;
auto& proj = reader.read_from_file({filename}).as_pair()->cdr.as_pair()->car;
if (!proj.is_pair() || !proj.as_pair()->car.is_symbol() ||
proj.as_pair()->car.as_symbol()->name != kind) {
throw std::runtime_error(fmt::format("invalid {} project", kind));
}
goos::for_each_in_list(proj.as_pair()->cdr, [&](const goos::Object& o) {
if (o.is_pair() && o.as_pair()->cdr.is_pair()) {
auto args = o.as_pair();
auto& action = args->car.as_symbol()->name;
args = args->cdr.as_pair();
if (action == "file-json") {
GameSubtitle2DefinitionFile new_file;
while (true) {
const auto& kwarg = args->car.as_symbol()->name;
args = args->cdr.as_pair();
if (kwarg == ":language-id") {
new_file.language_id = args->car.as_int();
} else if (kwarg == ":text-version") {
new_file.text_version = get_text_version_from_name(args->car.as_string()->data);
} else if (kwarg == ":data") {
new_file.file_path = args->car.as_string()->data;
}
if (args->cdr.is_empty_list()) {
break;
}
args = args->cdr.as_pair();
}
subtitle_files.push_back(new_file);
} else {
throw std::runtime_error(fmt::format("unknown action {} in {} project", action, kind));
}
} else {
throw std::runtime_error(fmt::format("invalid entry in {} project", kind));
}
});
}
GameSubtitle2DB load_subtitle2_project(GameVersion game_version) {
// Load the subtitle files
GameSubtitle2DB db(game_version);
try {
goos::Reader reader;
std::vector<GameSubtitle2DefinitionFile> files;
std::string subtitle_project = (file_util::get_jak_project_dir() / "game" / "assets" /
version_to_game_name(game_version) / "game_subtitle.gp")
.string();
open_subtitle2_project("subtitle2", subtitle_project, files);
for (auto& file : files) {
parse_subtitle2_json(db, file);
}
} catch (std::runtime_error& e) {
lg::error("error loading subtitle project: {}", e.what());
}
return db;
}

View File

@ -0,0 +1,107 @@
#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);

View File

@ -42,6 +42,10 @@ const std::string& get_text_version_name(GameTextVersion version) {
throw std::runtime_error(fmt::format("invalid text version {}", fmt::underlying(version)));
}
GameTextVersion get_text_version_from_name(const std::string& name) {
return sTextVerEnumMap.at(name);
}
GameTextFontBank::GameTextFontBank(GameTextVersion version,
std::vector<EncodeInfo>* encode_info,
std::vector<ReplaceInfo>* replace_info,
@ -105,7 +109,7 @@ const EncodeInfo* GameTextFontBank::find_encode_to_game(const std::string& in, i
}
/*!
* Finds a remap info that best matches the byte sequence (is the longest match).
* Finds a remap info that best matches the character sequence (is the longest match).
*/
const ReplaceInfo* GameTextFontBank::find_replace_to_utf8(const std::string& in, int off) const {
const ReplaceInfo* best_info = nullptr;
@ -113,14 +117,8 @@ const ReplaceInfo* GameTextFontBank::find_replace_to_utf8(const std::string& in,
if (info.from.empty() || in.size() - off < info.from.size())
continue;
bool found = true;
for (int i = 0; found && i < (int)info.from.size(); ++i) {
if (in.at(i + off) != info.from.at(i)) {
found = false;
}
}
if (found && (!best_info || info.to.length() > best_info->to.length())) {
bool found = memcmp(in.data() + off, info.from.data(), info.from.size()) == 0;
if (found && (!best_info || info.from.length() > best_info->from.length())) {
best_info = &info;
}
}
@ -133,16 +131,10 @@ const ReplaceInfo* GameTextFontBank::find_replace_to_utf8(const std::string& in,
const ReplaceInfo* GameTextFontBank::find_replace_to_game(const std::string& in, int off) const {
const ReplaceInfo* best_info = nullptr;
for (auto& info : *m_replace_info) {
if (info.to.length() == 0 || in.size() - off < info.to.size())
if (info.to.empty() || in.size() - off < info.to.size())
continue;
bool found = true;
for (int i = 0; found && i < (int)info.to.length(); ++i) {
if (in.at(i + off) != info.to.at(i)) {
found = false;
}
}
bool found = memcmp(in.data() + off, info.to.data(), info.to.size()) == 0;
if (found && (!best_info || info.to.length() > best_info->to.length())) {
best_info = &info;
}

View File

@ -30,6 +30,7 @@ enum class GameTextVersion {
extern const std::unordered_map<std::string, GameTextVersion> sTextVerEnumMap;
const std::string& get_text_version_name(GameTextVersion version);
GameTextVersion get_text_version_from_name(const std::string& name);
/*!
* What bytes a set of characters (UTF-8) correspond to. You can convert to and fro.

View File

@ -107,6 +107,14 @@ bool replace(std::string& str, const std::string& from, const std::string& to) {
return true;
}
std::string lower(const std::string& str) {
std::string res;
for (auto c : str) {
res.push_back(tolower(c));
}
return res;
}
std::string uuid() {
static std::random_device dev;
static std::mt19937 rng(dev());

View File

@ -21,6 +21,7 @@ std::vector<std::string> split(const ::std::string& str, char delimiter = '\n');
std::string join(const std::vector<std::string>& strs, const std::string& join_with);
std::vector<std::string> regex_get_capture_groups(const std::string& str, const std::string& regex);
bool replace(std::string& str, const std::string& from, const std::string& to);
std::string lower(const std::string& str);
std::string uuid();
std::string repeat(size_t n, const std::string& str);
std::string current_local_timestamp();

View File

@ -13063,37 +13063,29 @@
(defenum continue-flags
:type uint32
:bitfield #t
(cf0 0)
(cf1 1)
(cf2 2)
(cf3 3)
(cf4 4)
(cf5 5)
(cf6 6)
(cf7 7)
(cf8 8)
(cf9 9)
(cf10 10)
(cf11 11)
(cf12 12)
(cf13 13)
(cf14 14)
(cf15 15)
(cf16 16)
(cf17 17)
(cf18 18)
(cf19 19)
(cf20 20)
(cf21 21)
(cf22 22)
(cf23 23)
(cf24 24)
(cf25 25)
(cf26 26)
(cf27 27)
(cf28 28)
(cf29 29)
(cf30 30)
;(continue-flag-0 0)
(scene-wait 1)
(change-continue 2)
(no-auto 3)
(no-blackout 4)
(game-start 5)
(demo-end 6)
(warp-gate 7)
(demo 8)
(intro 9)
(hero-mode 10)
(demo-movie 11)
(title 12)
(title-movie 13)
(continue-flag-14 14)
(continue-flag-15 15)
(continue-flag-16 16)
(test 17)
(record-path 18)
(pilot 19)
(pilot-dax 20)
(record-sig 21)
(indax 22)
)
(deftype continue-point (basic)
@ -13367,6 +13359,7 @@
(subtitle 69)
(supertitle 70)
(notice-low 71)
(subtitle-pc 78) ;; custom
(screen 79)
(hud-upper-right 80)
(hud-upper-left 81)
@ -13441,7 +13434,7 @@
(stop-str (_type_ gui-connection) int 11)
(gui-control-method-12 (_type_ process gui-channel gui-action string int float sound-id) sound-id 12)
(update (_type_ symbol) int 13)
(lookup-gui-connection-id (_type_ string gui-channel gui-action) int 14)
(lookup-gui-connection-id (_type_ string gui-channel gui-action) sound-id 14)
(lookup-gui-connection (_type_ process gui-channel string sound-id) gui-connection 15)
(set-action! (_type_ gui-action sound-id gui-channel gui-action string (function gui-connection symbol) process) int 16)
(get-status (_type_ sound-id) gui-status 17)
@ -30209,7 +30202,7 @@
(define-extern entity-by-name (function string entity))
(define-extern entity-by-type (function type entity-actor))
(define-extern entity-by-aid (function uint entity))
(define-extern entity-actor-from-level-name (function level entity-actor))
(define-extern entity-actor-from-level-name (function symbol entity-actor))
(define-extern entity-nav-mesh-by-aid (function actor-id entity-nav-mesh))
(define-extern nav-mesh-from-res-tag (function entity symbol int nav-mesh))
(define-extern entity-by-meters (function float float float entity-actor))
@ -30227,7 +30220,7 @@
(define-extern *pid-string* string)
(define-extern debug-actor (function string none))
(define-extern draw-actor-marks (function process none))
(define-extern init-entity (function process entity-actor process none))
(define-extern init-entity (function process entity-actor type none))
;; (define-extern entity-deactivate-handler function) ;; (function process entity-actor none)
(define-extern check-for-rougue-process (function process int int level none))
(define-extern process-drawable-scale-from-entity! (function process-drawable entity none))
@ -32779,7 +32772,7 @@
(enemy-flag36 36)
(enemy-flag37 37)
(enemy-flag38 38)
(enemy-flag39 39)
(not-frustrated 39)
(enemy-flag40 40)
(enemy-flag41 41)
(enemy-flag42 42)
@ -36133,7 +36126,7 @@
(kick-attack () _type_ :state 158)
(attack () _type_ :state 159)
(die-now () _type_ :state 160)
(shoot (_type_ vector projectile-init-by-other-params int float float) none 161)
(shoot (_type_ vector projectile-init-by-other-params int int float) none 161)
(crimson-guard-hover-method-162 (_type_ process-focusable) symbol 162)
)
)
@ -41516,7 +41509,7 @@
(deftype sew-scare-grunt (grunt)
((anim spool-anim :offset-assert 692)
(manipy (pointer manipy) :offset-assert 696)
(spooled-sound-id uint32 :offset-assert 700)
(spooled-sound-id sound-id :offset-assert 700)
(grill-actor entity-actor :offset-assert 704)
)
:method-count-assert 188
@ -46196,7 +46189,7 @@
(deftype tomb-vibe (process-drawable)
((spawn-pos vector :inline :offset-assert 208)
(pat-tbl (array handle) :offset-assert 224) ;; pattern-table
(pat-tbl (pointer int32) :offset-assert 224) ;; pattern-table
(pat-count int32 :offset-assert 228)
(pat-index int32 :offset-assert 232)
(pat-entry-index int32 :offset-assert 236)
@ -47743,13 +47736,23 @@
)
)
(defenum under-locking-mode
:type int64
(want-mech)
(want-fill)
(airlock-wait)
(want-drain)
(drain)
(want-exit-mech)
)
(deftype under-locking (process-drawable)
((id int8 :offset-assert 200)
(up-y float :offset-assert 204)
(down-y float :offset-assert 208)
(mode uint64 :offset-assert 216)
(mode under-locking-mode :offset-assert 216)
(which-reminder? symbol :offset-assert 224)
(spooled-sound-id uint32 :offset-assert 228)
(spooled-sound-id sound-id :offset-assert 228)
(draining-part sparticle-launch-control :offset-assert 232)
(actor-group (pointer actor-group) :offset-assert 236)
(spooled-sound-delay int32 :offset-assert 240)

View File

@ -553,8 +553,7 @@
],
"print-game-text": [
[225, "v1", "float"],
[241, "v1", "float"],
[[324, 327], "v1", "dma-packet"]
[241, "v1", "float"]
],
"fx-copy-buf": [
[[2, 8], "a2", "dma-packet"],

View File

@ -446,44 +446,95 @@ std::string write_spool_subtitles(
file_util::create_dir_if_needed(image_out);
}
for (auto& [spool_name, subs] : data) {
int image_count = 0;
result += "(\"" + spool_name + "\"\n";
for (auto& sub : subs) {
std::string temp_for_indent = fmt::format(" (({} {}) (", float_to_string(sub.start_frame),
float_to_string(sub.end_frame));
auto indent = temp_for_indent.length();
result += temp_for_indent;
for (int i = 0; i < 8; ++i) {
const auto& msg = sub.message[i];
if (i > 0) {
result += "\n" + std::string(indent, ' ');
constexpr bool as_json = true;
if constexpr (as_json) {
constexpr bool dump_text = false;
constexpr int lang = 0;
// no line data
bool has_spools = false;
for (auto& [spool_name, subs] : data) {
result += " \"" + spool_name + "\": {\n";
result += " \"scene\": true,\n";
result += " \"lines\": [\n";
bool has_subs = false;
for (auto& sub : subs) {
const auto& msg = sub.message[lang];
if (msg.kind != SpoolSubtitleMessage::Kind::STRING) {
continue;
}
if (msg.kind == SpoolSubtitleMessage::Kind::NIL) {
result += "#f";
result += " {\n";
result += " \"end\": " + float_to_string(sub.end_frame) + ",\n";
if (dump_text) {
result += " \"merge\": false,\n";
} else {
result += "(";
if (msg.kind == SpoolSubtitleMessage::Kind::IMAGE) {
auto img_name = fmt::format("{}-{}-{}.png", spool_name, i, image_count++);
result += "image " + img_name;
if (dump_images) {
std::vector<u32> rgba_out;
rgba_out.resize(msg.w * msg.h);
for (int px = 0; px < (int)rgba_out.size(); ++px) {
int idx = px & 1 ? msg.data[px / 2] >> 4 : msg.data[px / 2] & 0xf;
rgba_out.at(px) = msg.palette[idx];
}
file_util::write_rgba_png(image_out / img_name, rgba_out.data(), msg.w, msg.h);
}
} else if (msg.kind == SpoolSubtitleMessage::Kind::STRING) {
result += "\"" + msg.text + "\"";
}
result += ")";
result += " \"merge\": true,\n";
}
result += " \"offscreen\": false,\n";
result += " \"speaker\": \"none\",\n";
result += " \"start\": " + float_to_string(sub.start_frame) + ",\n";
if (dump_text) {
result += " \"text\": \"" + msg.text + "\"\n";
} else {
result += " \"text\": \"\"\n";
}
result += " },\n";
has_subs = true;
}
result += ")\n )\n";
if (has_subs) {
result.pop_back();
result.pop_back();
result.push_back('\n');
}
result += " ]\n";
result += " },\n";
has_spools = true;
}
if (has_spools) {
result.pop_back();
result.pop_back();
result.push_back('\n');
}
} else {
for (auto& [spool_name, subs] : data) {
int image_count = 0;
result += "(\"" + spool_name + "\"\n";
for (auto& sub : subs) {
std::string temp_for_indent = fmt::format(" (({} {}) (", float_to_string(sub.start_frame),
float_to_string(sub.end_frame));
auto indent = temp_for_indent.length();
result += temp_for_indent;
for (int i = 0; i < 8; ++i) {
const auto& msg = sub.message[i];
if (i > 0) {
result += "\n" + std::string(indent, ' ');
}
if (msg.kind == SpoolSubtitleMessage::Kind::NIL) {
result += "#f";
} else {
result += "(";
if (msg.kind == SpoolSubtitleMessage::Kind::IMAGE) {
auto img_name = fmt::format("{}-{}-{}.png", spool_name, i, image_count++);
result += fmt::format("image \"{}\"", img_name);
if (dump_images) {
std::vector<u32> rgba_out;
rgba_out.resize(msg.w * msg.h);
for (int px = 0; px < (int)rgba_out.size(); ++px) {
int idx = px & 1 ? msg.data[px / 2] >> 4 : msg.data[px / 2] & 0xf;
rgba_out.at(px) = msg.palette[idx];
}
file_util::write_rgba_png(image_out / img_name, rgba_out.data(), msg.w, msg.h);
}
} else if (msg.kind == SpoolSubtitleMessage::Kind::STRING) {
result += "\"" + msg.text + "\"";
}
result += ")";
}
}
result += ")\n )\n";
}
result += " )\n\n";
}
result += " )\n\n";
}
return result;

View File

@ -213,6 +213,7 @@ set(RUNTIME_SOURCE
system/vm/vm.cpp
tools/filter_menu/filter_menu.cpp
tools/subtitles/subtitle_editor.cpp
tools/subtitles2/subtitle2_editor.cpp
)
add_subdirectory(sound)

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Binary file not shown.

93
game/assets/fonts/OFL.txt Normal file
View File

@ -0,0 +1,93 @@
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

View File

@ -0,0 +1,10 @@
;; "project file" for subtitles make tool.
(subtitle2
(file-json :text-version "jak2" :language-id 0
:data "game/assets/jak2/subtitle/subtitle_en-US.json")
(file-json :text-version "jak2" :language-id 5
:data "game/assets/jak2/subtitle/subtitle_jp-JP.json")
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
{
"lang": 5,
"scenes": {
"vin013": {
"lines": [
{
"end": 74.0,
"merge": false,
"offscreen": true,
"speaker": "vin",
"start": 0.0,
"text": "ジャック...コールが..."
},
{
"end": 154.0,
"merge": false,
"offscreen": true,
"speaker": "vin",
"start": 75.0,
"text": "爆弾...建造...エリア..."
},
{
"end": 189.0,
"merge": false,
"offscreen": true,
"speaker": "vin",
"start": 155.0,
"text": "フゥー..."
}
],
"scene": false
}
},
"speakers": {
"agent": "エイジェント",
"ashelin": "アシュリン",
"baron": "バロンプラクシス",
"brutter": "ブラッター",
"citizen-female": "女性しみん",
"citizen-male": "だん性しみん",
"computer": "コンピュータ",
"darkjak": "ダークジャック",
"daxter": "ダックスター",
"errol": "エロル",
"grim": "グリム",
"guard": "ガード",
"guard-a": "ガードA",
"guard-b": "ガードB",
"jak": "ジャック",
"jinx": "ジンクス",
"keira": "ケイラ",
"keira-before-class-3": "レーサー",
"kid": "キッド",
"kor": "コール",
"krew": "クルー",
"metalkor": "メタルコール",
"mog": "モッグ",
"onin": "オニン",
"oracle": "オラクル",
"pecker": "ペッカー",
"precursor": "プリカーソル",
"samos": "セイジィ",
"sig": "ジーグ",
"tess": "テス",
"torn": "トーン",
"vin": "ビン",
"youngsamos": "セイモス",
"youngsamos-before-rescue": "セイジィ"
}
}

View File

@ -703,7 +703,16 @@ void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) {
}
if (settings.draw_subtitle_editor_window) {
m_subtitle_editor.draw_window();
if (m_subtitle_editor == nullptr) {
m_subtitle_editor = new SubtitleEditor();
}
m_subtitle_editor->draw_window();
}
if (settings.draw_subtitle2_editor_window) {
if (m_subtitle2_editor == nullptr) {
m_subtitle2_editor = new Subtitle2Editor(m_version);
}
m_subtitle2_editor->draw_window();
}
if (settings.draw_filters_window) {

View File

@ -12,6 +12,7 @@
#include "game/graphics/opengl_renderer/opengl_utils.h"
#include "game/tools/filter_menu/filter_menu.h"
#include "game/tools/subtitles/subtitle_editor.h"
#include "game/tools/subtitles2/subtitle2_editor.h"
struct RenderOptions {
bool draw_render_debug_window = false;
@ -19,6 +20,7 @@ struct RenderOptions {
bool draw_loader_window = false;
bool draw_small_profiler_window = false;
bool draw_subtitle_editor_window = false;
bool draw_subtitle2_editor_window = false;
bool draw_filters_window = false;
// internal rendering settings - The OpenGLRenderer will internally use this resolution/format.
@ -140,7 +142,8 @@ class OpenGLRenderer {
SharedRenderState m_render_state;
Profiler m_profiler;
SmallProfiler m_small_profiler;
SubtitleEditor m_subtitle_editor;
SubtitleEditor* m_subtitle_editor = nullptr;
Subtitle2Editor* m_subtitle2_editor = nullptr;
FiltersMenu m_filters_menu;
std::vector<std::unique_ptr<BucketRenderer>> m_bucket_renderers;

View File

@ -106,6 +106,12 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
}
if (ImGui::BeginMenu("Tools")) {
if (m_version == GameVersion::Jak1) {
ImGui::MenuItem("Subtitle Editor", nullptr, &m_subtitle_editor);
} else {
ImGui::MenuItem("Subtitle2 Editor", nullptr, &m_subtitle2_editor);
}
if (ImGui::BeginMenu("Screenshot")) {
ImGui::MenuItem("Screenshot Next Frame!", nullptr, &m_want_screenshot);
ImGui::InputText("File", m_screenshot_save_name, 50);
@ -127,7 +133,7 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
if (Gfx::g_debug_settings.alternate_style) {
ImGui::applyAlternateStyle();
} else {
ImGui::StyleColorsClassic();
ImGui::applyClassicStyle();
}
}
ImGui::TreePop();

View File

@ -7,6 +7,7 @@
#include "common/dma/dma.h"
#include "common/util/Timer.h"
#include "common/versions/versions.h"
class FrameTimeRecorder {
public:
@ -39,12 +40,15 @@ class FrameTimeRecorder {
class OpenGlDebugGui {
public:
OpenGlDebugGui(GameVersion version) : m_version(version) {}
void start_frame();
void finish_frame();
void draw(const DmaStats& dma_stats);
bool should_draw_render_debug() const { return master_enable && m_draw_debug; }
bool should_draw_profiler() const { return master_enable && m_draw_profiler; }
bool should_draw_subtitle_editor() const { return master_enable && m_subtitle_editor; }
bool should_draw_subtitle2_editor() const { return master_enable && m_subtitle2_editor; }
bool should_draw_filters_menu() const { return master_enable && m_filters_menu; }
bool should_draw_loader_menu() const { return master_enable && m_draw_loader; }
const char* screenshot_name() const { return m_screenshot_save_name; }
@ -79,8 +83,11 @@ class OpenGlDebugGui {
bool m_draw_debug = false;
bool m_draw_loader = false;
bool m_subtitle_editor = false;
bool m_subtitle2_editor = false;
bool m_filters_menu = false;
bool m_want_screenshot = false;
char m_screenshot_save_name[256] = "screenshot.png";
float target_fps_input = 60.f;
GameVersion m_version;
};

View File

@ -82,6 +82,7 @@ struct GraphicsData {
file_util::get_jak_project_dir() / "out" / game_version_names[version] / "fr3",
fr3_level_count[version])),
ogl_renderer(texture_pool, loader, version),
debug_gui(version),
version(version) {}
};
@ -152,7 +153,7 @@ static void init_imgui(SDL_Window* window,
if (!Gfx::g_debug_settings.monospaced_font) {
// TODO - add or switch to Noto since it supports the entire unicode range
std::string font_path =
(file_util::get_jak_project_dir() / "game" / "assets" / "fonts" / "Roboto-Medium.ttf")
(file_util::get_jak_project_dir() / "game" / "assets" / "fonts" / "NotoSansJP-Medium.ttf")
.string();
if (file_util::file_exists(font_path)) {
static const ImWchar ranges[] = {
@ -163,11 +164,11 @@ static void init_imgui(SDL_Window* window,
0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
0x3131, 0x3163, // Korean alphabets
0x31F0, 0x31FF, // Katakana Phonetic Extensions
0x4E00, 0x9FAF, // CJK Ideograms
0xA640, 0xA69F, // Cyrillic Extended-B
0xAC00, 0xD7A3, // Korean characters
0xFF00, 0xFFEF, // Half-width characters
0xFFFD, 0xFFFD, // Invalid
0x4e00, 0x9FAF, // CJK Ideograms
0,
};
io.Fonts->AddFontFromFileTTF(font_path.c_str(), Gfx::g_debug_settings.imgui_font_size,
@ -356,6 +357,7 @@ void render_game_frame(int game_width,
options.draw_profiler_window = g_gfx_data->debug_gui.should_draw_profiler();
options.draw_loader_window = g_gfx_data->debug_gui.should_draw_loader_menu();
options.draw_subtitle_editor_window = g_gfx_data->debug_gui.should_draw_subtitle_editor();
options.draw_subtitle2_editor_window = g_gfx_data->debug_gui.should_draw_subtitle2_editor();
options.draw_filters_window = g_gfx_data->debug_gui.should_draw_filters_menu();
options.save_screenshot = false;
options.gpu_sync = g_gfx_data->debug_gui.should_gl_finish();

View File

@ -10,10 +10,10 @@ namespace game_settings {
struct DebugSettings {
DebugSettings();
std::string version = "1.1";
std::string version = "1.2";
bool show_imgui = false;
int imgui_font_size = 14;
int imgui_font_size = 16;
bool monospaced_font = true;
bool alternate_style = false;
bool ignore_hide_imgui = false;

View File

@ -0,0 +1,436 @@
#include "subtitle2_editor.h"
#include <algorithm>
#include "common/serialization/subtitles2/subtitles2_deser.h"
#include "common/util/FileUtil.h"
#include "common/util/json_util.h"
#include "common/util/string_util.h"
#include "game/runtime.h"
#include "third-party/fmt/core.h"
#include "third-party/imgui/imgui.h"
#include "third-party/imgui/imgui_stdlib.h"
static constexpr size_t LINE_DISPLAY_MAX_LEN = 38;
Subtitle2Editor::Subtitle2Editor(GameVersion version)
: db_loaded(true),
m_subtitle_db(load_subtitle2_project(version)),
m_repl(8182),
m_speaker_names(get_speaker_names(version)) {
m_filter = m_filter_placeholder;
m_filter_hints = m_filter_placeholder;
}
bool Subtitle2Editor::is_scene_in_current_lang(const std::string& scene_name) {
return m_subtitle_db.m_banks.at(m_current_language)->scenes.count(scene_name) > 0;
}
void Subtitle2Editor::repl_rebuild_text() {
// reload subtitles immediately
m_repl.eval("(reload-subtitles)");
}
void Subtitle2Editor::repl_play_vag(const std::string& name, bool is_scene) {
if (is_scene) {
m_repl.eval(fmt::format("(scene-find-and-play \"{}\")", name));
} else {
m_repl.eval(fmt::format("(vag-player-play-from-name \"{}\")", name));
}
}
void Subtitle2Editor::draw_window() {
ImGui::Begin("Subtitle2 Editor");
if (!db_loaded) {
if (ImGui::Button("Load Subtitles")) {
m_subtitle_db = load_subtitle2_project(g_game_version);
db_loaded = true;
}
ImGui::End();
return;
}
if (ImGui::Button("Save Changes")) {
m_files_saved_successfully =
std::make_optional(write_subtitle_db_to_files(m_subtitle_db, g_game_version));
repl_rebuild_text();
}
if (m_files_saved_successfully.has_value()) {
ImGui::SameLine();
if (m_files_saved_successfully.value()) {
ImGui::PushStyleColor(ImGuiCol_Text, m_success_text_color);
ImGui::Text("Saved!");
ImGui::PopStyleColor();
} else {
ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color);
ImGui::Text("Error!");
ImGui::PopStyleColor();
}
}
draw_edit_options();
draw_repl_options();
draw_speaker_options();
if (!m_current_scene) {
ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color);
} else {
ImGui::PushStyleColor(ImGuiCol_Text, m_selected_text_color);
}
if (ImGui::TreeNode("Currently Selected Cutscene")) {
ImGui::Text("%s", m_current_scene_name.c_str());
ImGui::SameLine();
ImGui::Checkbox("Cutscene?", &m_current_scene->scene);
ImGui::PopStyleColor();
if (m_current_scene) {
draw_subtitle_options(*m_current_scene, m_current_scene_name, true);
} else {
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 0, 0, 255));
ImGui::Text("Select a Scene from Below!");
ImGui::PopStyleColor();
}
ImGui::TreePop();
} else {
ImGui::PopStyleColor();
}
if (ImGui::TreeNode("All Cutscenes")) {
ImGui::InputText("New Scene Name", &m_new_scene_name);
ImGui::InputText("Filter", &m_filter, ImGuiInputTextFlags_::ImGuiInputTextFlags_AutoSelectAll);
if (is_scene_in_current_lang(m_new_scene_name)) {
ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color);
ImGui::Text("Scene already exists with that name, no!");
ImGui::PopStyleColor();
}
if (!is_scene_in_current_lang(m_new_scene_name) && !m_new_scene_name.empty()) {
if (ImGui::Button("Add Scene")) {
Subtitle2Scene new_scene;
m_subtitle_db.m_banks.at(m_current_language)->add_scene(m_new_scene_name, new_scene);
if (m_add_new_scene_as_current) {
auto& scenes = m_subtitle_db.m_banks.at(m_current_language)->scenes;
auto& scene_info = scenes.at(m_new_scene_name);
m_current_scene = &scene_info;
m_current_scene_name = m_new_scene_name;
}
m_new_scene_name = "";
}
ImGui::SameLine();
ImGui::Checkbox("Add as Current Scene", &m_add_new_scene_as_current);
}
draw_all_scenes();
ImGui::TreePop();
}
ImGui::End();
}
void Subtitle2Editor::draw_edit_options() {
if (ImGui::TreeNode("Editing Options")) {
if (ImGui::BeginCombo("Editor Language ID",
fmt::format("[{}] {}", m_subtitle_db.m_banks[m_current_language]->lang,
m_subtitle_db.m_banks[m_current_language]->file_path)
.c_str())) {
for (const auto& [key, value] : m_subtitle_db.m_banks) {
const bool isSelected = m_current_language == key;
if (ImGui::Selectable(fmt::format("[{}] {}", value->lang, value->file_path).c_str(),
isSelected)) {
m_current_language = key;
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
if (ImGui::BeginCombo("Base Language ID",
fmt::format("[{}] {}", m_subtitle_db.m_banks[m_base_language]->lang,
m_subtitle_db.m_banks[m_base_language]->file_path)
.c_str())) {
for (const auto& [key, value] : m_subtitle_db.m_banks) {
const bool isSelected = m_base_language == key;
if (ImGui::Selectable(fmt::format("[{}] {}", value->lang, value->file_path).c_str(),
isSelected)) {
m_base_language = key;
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
ImGui::Checkbox("Show missing cutscenes from base", &m_base_show_missing_cutscenes);
ImGui::TreePop();
}
}
void Subtitle2Editor::draw_repl_options() {
if (ImGui::TreeNode("REPL Options")) {
// TODO - the ReplServer should eventually be able to return statuses to make this easier:
// - Has the game been built before?
// - Is the repl connected?
ImGui::TextWrapped(
"This tool requires a REPL connected to the game, with the game built. Run the following "
"to do so:");
ImGui::Text(" - `task repl`");
ImGui::Text(" - `(lt)`");
ImGui::Text(" - `(mi)`");
ImGui::Text(" - Click Connect Below!");
if (m_repl.is_connected()) {
ImGui::PushStyleColor(ImGuiCol_Text, m_success_text_color);
ImGui::Text("REPL Connected, should be good to go!");
ImGui::PopStyleColor();
} else {
if (ImGui::Button("Connect to REPL")) {
m_repl.connect();
if (!m_repl.is_connected()) {
ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color);
ImGui::Text("Could not connect.");
ImGui::PopStyleColor();
}
}
}
ImGui::TreePop();
}
}
void Subtitle2Editor::draw_speaker_options() {
if (ImGui::TreeNode("Speakers")) {
const auto bank = m_subtitle_db.m_banks[m_current_language];
for (auto& speaker_name : m_speaker_names) {
// ImGui::SameLine();
if (bank->speakers.count(speaker_name) == 0) {
// no speaker yet.
std::string input = "";
ImGui::InputText(speaker_name.c_str(), &input);
if (!input.empty()) {
// speaker got filled
bank->speakers.insert({speaker_name, input});
}
} else {
// existing speaker
std::string input = bank->speakers.at(speaker_name);
if (ImGui::InputText(speaker_name.c_str(), &input)) {
if (input.empty()) {
// speaker got deleted
bank->speakers.erase(speaker_name);
} else {
// speaker got changed
bank->speakers.at(speaker_name) = input;
}
}
}
}
ImGui::TreePop();
}
}
void Subtitle2Editor::draw_all_scenes(bool base_cutscenes) {
auto& scenes =
m_subtitle_db.m_banks.at(base_cutscenes ? m_base_language : m_current_language)->scenes;
std::unordered_set<std::string> to_delete;
for (auto& [name, scene] : scenes) {
// Don't duplicate entries
if (base_cutscenes && is_scene_in_current_lang(name)) {
continue;
}
bool is_current_scene = m_current_scene && m_current_scene_name == name;
if ((!m_filter.empty() && m_filter != m_filter_placeholder) &&
name.find(m_filter) == std::string::npos) {
continue;
}
bool color_pushed = false;
if (!base_cutscenes && is_current_scene) {
ImGui::PushStyleColor(ImGuiCol_Text, m_selected_text_color);
color_pushed = true;
} else if (base_cutscenes) {
ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color);
color_pushed = true;
}
if (ImGui::TreeNode(
fmt::format("{}-{}", name, base_cutscenes ? m_base_language : m_current_language)
.c_str(),
"%s", name.c_str())) {
if (color_pushed) {
ImGui::PopStyleColor();
}
if (!base_cutscenes && !is_current_scene) {
if (ImGui::Button("Select as Current")) {
m_current_scene = &scene;
m_current_scene_name = name;
}
}
if (base_cutscenes) {
if (ImGui::Button("Copy from Base Language")) {
m_subtitle_db.m_banks.at(m_current_language)->add_scene(name, scene);
}
}
draw_subtitle_options(scene, name);
ImGui::PushStyleColor(ImGuiCol_Button, m_warning_color);
if (ImGui::Button("Delete")) {
if (&scene == m_current_scene || name == m_current_scene_name) {
m_current_scene = nullptr;
m_current_scene_name = "";
}
to_delete.insert(name);
}
ImGui::PopStyleColor();
ImGui::TreePop();
} else if (color_pushed) {
ImGui::PopStyleColor();
}
}
for (auto& name : to_delete) {
scenes.erase(name);
}
}
void Subtitle2Editor::draw_subtitle_options(Subtitle2Scene& scene,
const std::string& name,
bool current_scene) {
if (!m_repl.is_connected()) {
ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color);
ImGui::Text("REPL not connected, can't play!");
ImGui::PopStyleColor();
} else {
// Cutscenes
if (ImGui::Button("Play Scene")) {
repl_play_vag(name, scene.scene);
}
}
if (current_scene) {
draw_new_cutscene_line_form();
}
const auto bank = m_subtitle_db.m_banks[m_current_language];
int i = 0;
for (auto line = scene.lines.begin(); line != scene.lines.end();) {
float times[2] = {line->start, line->end};
bool speaker_exists = bank->speakers.count(line->speaker) != 0;
auto speaker_text = !speaker_exists ? "N/A" : bank->speakers.at(line->speaker);
std::string full_line = line->text;
if (speaker_exists) {
full_line = speaker_text + ": " + full_line;
}
auto summary = fmt::format("[{} - {}] {}", line->start, line->end, full_line);
if (line->text.empty()) {
ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color);
} else if (line->offscreen) {
ImGui::PushStyleColor(ImGuiCol_Text, m_offscreen_text_color);
}
if (ImGui::TreeNode(fmt::format("{}", i).c_str(), "%s", summary.c_str())) {
if (line->text.empty() || line->offscreen) {
ImGui::PopStyleColor();
}
ImGui::InputFloat2("Start and End Frame", times, "%.0f",
ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal);
if (ImGui::BeginCombo("Speaker",
fmt::format("{} ({})", speaker_text.c_str(), line->speaker).c_str())) {
const bool isSelected = line->speaker == "none";
if (ImGui::Selectable("none", isSelected)) {
line->speaker = "none";
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
for (auto& speaker_name : m_speaker_names) {
if (bank->speakers.count(speaker_name) == 0) {
continue;
}
const bool isSelected = line->speaker == speaker_name;
if (ImGui::Selectable(
fmt::format("{} ({})", bank->speakers.at(speaker_name), speaker_name).c_str(),
isSelected)) {
line->speaker = speaker_name;
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
ImGui::InputText("Text", &line->text);
ImGui::Checkbox("Offscreen?", &line->offscreen);
ImGui::SameLine();
ImGui::Checkbox("Merge text?", &line->merge);
if (scene.lines.size() > 1) { // prevent creating an empty scene
ImGui::PushStyleColor(ImGuiCol_Button, m_warning_color);
if (ImGui::Button("Delete")) {
line = scene.lines.erase(line);
ImGui::PopStyleColor();
ImGui::TreePop();
continue;
}
ImGui::PopStyleColor();
}
ImGui::TreePop();
} else if (line->text.empty() || line->offscreen) {
ImGui::PopStyleColor();
}
line->start = times[0];
line->end = times[1];
i++;
line++;
}
}
void Subtitle2Editor::draw_new_cutscene_line_form() {
auto bank = m_subtitle_db.m_banks[m_current_language];
ImGui::InputFloat2("Start and End Frame", m_current_scene_frame, "%.0f",
ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal);
const auto& speakers = bank->speakers;
if (speakers.count(m_current_scene_speaker) == 0) {
// pick whatever the first one it finds is
m_current_scene_speaker = "none";
}
if (ImGui::BeginCombo("Speaker",
m_current_scene_speaker == "none"
? "none"
: fmt::format("{} ({})", speakers.at(m_current_scene_speaker),
m_current_scene_speaker)
.c_str())) {
const bool isSelected = m_current_scene_speaker == "none";
if (ImGui::Selectable("none", isSelected)) {
m_current_scene_speaker = "none";
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
for (auto& speaker_name : m_speaker_names) {
if (speakers.count(speaker_name) == 0) {
continue;
}
const bool isSelected = m_current_scene_speaker == speaker_name;
if (ImGui::Selectable(fmt::format("{} ({})", speakers.at(speaker_name), speaker_name).c_str(),
isSelected)) {
m_current_scene_speaker = speaker_name;
}
if (isSelected) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
ImGui::InputText("Text", &m_current_scene_text);
ImGui::Checkbox("Offscreen?", &m_current_scene_offscreen);
ImGui::SameLine();
ImGui::Checkbox("Merge text?", &m_current_scene_merge);
if (m_current_scene_frame[0] < 0 || m_current_scene_frame[1] < 0 ||
(m_current_scene_text.empty() && !m_current_scene_merge)) {
ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color);
ImGui::Text("Can't add a new text entry with the current fields!");
ImGui::PopStyleColor();
} else {
if (ImGui::Button("Add Text Entry")) {
m_current_scene->lines.emplace_back(m_current_scene_frame[0], m_current_scene_frame[1],
m_current_scene_text, m_current_scene_speaker,
m_current_scene_offscreen, m_current_scene_merge);
// TODO - sorting after every insertion is slow, sort on the add scene instead
std::sort(m_current_scene->lines.begin(), m_current_scene->lines.end());
}
}
}

View File

@ -0,0 +1,75 @@
#pragma once
#include <optional>
#include <string_view>
#include "common/repl/nrepl/ReplClient.h"
#include "common/serialization/subtitles2/subtitles2_ser.h"
#include "third-party/imgui/imgui.h"
// TODO Later:
// - Hints, these seem less annoying but there are a lot of them
class Subtitle2Editor {
public:
Subtitle2Editor(GameVersion version);
void draw_window();
private:
void draw_edit_options();
void draw_repl_options();
void draw_speaker_options();
void draw_all_scenes(bool base_cutscenes = false);
void draw_subtitle_options(Subtitle2Scene& scene,
const std::string& name,
bool current_scene = false);
void draw_new_cutscene_line_form();
bool db_loaded = false;
GameSubtitle2DB m_subtitle_db;
Subtitle2Scene* m_current_scene = nullptr;
std::string m_current_scene_name = "";
std::string m_filter;
std::string m_filter_hints;
ReplClient m_repl;
float m_current_scene_frame[2] = {0, 0};
std::string m_current_scene_text = "";
std::string m_current_scene_speaker = "";
bool m_current_scene_offscreen = false;
bool m_current_scene_merge = false;
bool m_add_new_scene_as_current = false;
std::string m_new_scene_name = "";
std::string m_new_scene_id = "0";
std::string m_filter_placeholder = "Filter List...";
std::optional<bool> m_files_saved_successfully = {};
int m_base_language = 0;
int m_current_language = 0;
// bool m_base_show_lines = false;
bool m_base_show_missing_cutscenes = true;
// TODO - let the user customize these colors
ImVec4 m_normal_text_color = ImVec4(1.0f, 0.0f, 1.0f, 1.0f);
int m_selected_text_color = IM_COL32(89, 227, 225, 255);
ImVec4 m_success_text_color = ImVec4(0.0f, 1.0f, 0.0f, 1.0f);
ImVec4 m_error_text_color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
ImVec4 m_disabled_text_color = ImVec4(1.0f, 1.0f, 1.0f, 0.7f);
ImVec4 m_warning_color = ImVec4(0.619f, 0.443f, 0.0f, 1.0f);
int m_offscreen_text_color = IM_COL32(240, 242, 102, 255);
// TODO - cycle speaker colors
const std::vector<std::string> m_speaker_names;
void repl_rebuild_text();
void repl_play_vag(const std::string& name, bool is_scene);
bool is_scene_in_current_lang(const std::string& scene_name);
};

View File

@ -1037,6 +1037,14 @@
`(defconstant ,name (-> self draw art-group data ,idx))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; built-in type stuff
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmacro string? (val)
`(type? ,val string))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Load Project
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -424,21 +424,19 @@
;;;;;;;;;;;;;;;;;;;;;;;;
(defun debug-menu-item-get-max-width ((arg0 debug-menu-item) (arg1 debug-menu))
"Determine the width, in pixels"
(local-vars (v0-1 int))
"Determine the width, in screen units"
0
(cond
((= (-> arg0 type) debug-menu-item-submenu)
(set! v0-1 (+ (the int (get-string-length (-> arg0 name) (-> arg1 context font))) 16))
((= (-> arg0 type) debug-menu-item-submenu)
(+ (the int (get-string-length (-> arg0 name) (-> arg1 context font))) 16)
)
((= (-> arg0 type) debug-menu-item-var)
(the int (get-string-length (-> (the-as debug-menu-item-var arg0) display-str) (-> arg1 context font)))
)
(else
(+ (the int (get-string-length (-> arg0 name) (-> arg1 context font))) 6)
)
)
((= (-> arg0 type) debug-menu-item-var)
(set! v0-1 (the int (get-string-length (-> (the-as debug-menu-item-var arg0) display-str) (-> arg1 context font))))
)
(else
(set! v0-1 (+ (the int (get-string-length (-> arg0 name) (-> arg1 context font))) 6))
)
)
v0-1
)
(defun debug-menu-context-default-selection ((ctxt debug-menu-context) (keep-current symbol))
@ -761,8 +759,6 @@
))
(with-dma-buffer-add-bucket ((s3-0 (-> (current-frame) debug-buf))
(bucket-id debug-no-zbuf))
;; NOTE: the draw-string-adv advances too far on widescreen.
;; NOTE 2: should be fixed?
(draw-string-adv (-> item name) s3-0 s5-0)
(draw-string-adv "..." s3-0 s5-0)
)

View File

@ -1635,13 +1635,7 @@
)
(defun get-string-length ((arg0 string) (arg1 font-context))
(local-vars
(v0-0 float) (a2-1 (inline-array vector)) (a2-4 (inline-array vector)) (a3-2 int) (a3-9 uint)
(a3-10 int) (t0-0 uint) (t0-1 uint) (t0-3 int) (t0-4 int) (t0-5 int) (t1-1 uint) (t1-2 uint)
(t1-3 uint) (t1-4 uint) (t1-5 uint) (t2-0 uint) (t2-2 uint) (t2-3 uint) (t2-4 uint) (t2-5 uint)
(t2-6 uint) (t2-7 uint) (t2-8 uint) (t2-9 uint) (t2-10 uint) (t2-11 uint) (t2-12 uint)
(t2-13 uint) (t2-14 uint) (t2-15 uint) (t2-16 uint) (t2-17 uint) (t3-0 uint) (t3-1 int)
)
(local-vars (v0-0 float))
(rlet ((vf0 :class vf)
(vf1 :class vf)
(vf13 :class vf)
@ -1652,171 +1646,132 @@
(vf25 :class vf)
(vf5 :class vf)
)
(init-vf0-vector)
(.lvf vf25 (&-> arg1 context-vec quad))
(.lvf vf23 (&-> arg1 origin quad))
(.lvf vf24 (&-> arg1 origin quad))
(let ((v1-0 (-> arg1 flags-signed)))
(let ((a1-2 *video-parms*))
(.lvf vf1 (+ (the int a1-2) 64))
)
(.mul.vf vf25 vf25 vf1 :mask #b11)
(.mul.vf vf23 vf23 vf1 :mask #b11)
;; hack! fixes small font widescreen
(unless (logtest? (-> arg1 flags) (font-flags pc-hack))
(.mul.vf vf24 vf24 vf1 :mask #b11)
)
(let ((a1-4 *font-work*))
(set! (-> a1-4 str-ptr) (the-as uint arg0))
(set! (-> a1-4 flags) (the-as font-flags v1-0))
(.mov.vf vf1 vf0)
(let ((a2-0 (logand v1-0 32)))
(nop!)
(b! (nonzero? a2-0) cfg-2 :delay (set! a2-1 *font12-table*))
)
(let ((a2-2 a2-1))
(nop!)
(.lvf vf13 (&-> a1-4 size1-small quad))
(b! #t cfg-3 :delay (.lvf vf14 (&-> a1-4 size2-small quad)))
(label cfg-2)
(nop!)
(set! a2-2 *font24-table*)
(nop!)
(.lvf vf13 (&-> a1-4 size1-large quad))
(nop!)
(.lvf vf14 (&-> a1-4 size2-large quad))
(label cfg-3)
(let ((a3-0 (-> (the-as (pointer uint8) arg0) 4)))
(set! arg0 (the-as string (&-> (the-as (pointer uint8) arg0) 1)))
(b! (zero? a3-0) cfg-51 :delay (set! t0-0 (+ a3-0 -1)))
(b! (zero? t0-0) cfg-44 :delay (set! t0-1 (+ a3-0 -126)))
(b! (nonzero? t0-1) cfg-45 :delay (nop!))
(set! a3-0 (-> (the-as (pointer uint8) arg0) 4))
(set! arg0 (the-as string (&-> (the-as (pointer uint8) arg0) 1)))
(let ((t0-2 0)
(t1-0 0)
)
(b! (zero? a3-0) cfg-51 :delay (set! t2-0 (+ a3-0 -43)))
(.movz t0-3 a3-0 t2-0 t0-2)
(let ((t2-1 (+ a3-0 -45)))
(.movz t0-4 a3-0 t2-1 t0-3)
)
(nop!)
(b! (nonzero? t0-4) cfg-14 :delay (set! t2-2 (+ a3-0 -121)))
(b! (zero? t2-2) cfg-42 :delay (set! t1-1 (+ a3-0 -89)))
(b! (zero? t1-1) cfg-42 :delay (set! t1-2 (+ a3-0 -122)))
(b! (zero? t1-2) cfg-43 :delay (set! t1-3 (+ a3-0 -90)))
(b! (zero? t1-3) cfg-43 :delay (set! t1-4 (+ a3-0 -48)))
(b! (< (the-as int t1-4) 0) cfg-45 :delay (set! t1-5 (+ a3-0 -57)))
(b!
(> (the-as int t1-5) 0)
cfg-45
:delay
(set! t1-0 (the-as int (+ a3-0 -48)))
)
(label cfg-14)
(set! a3-0 (-> (the-as (pointer uint8) arg0) 4))
(set! arg0 (the-as string (&-> (the-as (pointer uint8) arg0) 1)))
(b! (zero? a3-0) cfg-51 :delay (set! t2-3 (+ a3-0 -110)))
(b! (zero? t2-3) cfg-32 :delay (set! t2-4 (+ a3-0 -78)))
(b! (zero? t2-4) cfg-32 :delay (set! t2-5 (+ a3-0 -108)))
(b! (zero? t2-5) cfg-3 :delay (set! t2-6 (+ a3-0 -76)))
(b! (zero? t2-6) cfg-3 :delay (set! t2-7 (+ a3-0 -119)))
(b! (zero? t2-7) cfg-3 :delay (set! t2-8 (+ a3-0 -87)))
(b! (zero? t2-8) cfg-3 :delay (set! t2-9 (+ a3-0 -107)))
(b! (zero? t2-9) cfg-35 :delay (set! t2-10 (+ a3-0 -75)))
(b! (zero? t2-10) cfg-35 :delay (set! t2-11 (+ a3-0 -106)))
(b! (zero? t2-11) cfg-3 :delay (set! t2-12 (+ a3-0 -74)))
(b! (zero? t2-12) cfg-3 :delay (set! t2-13 (+ a3-0 -104)))
(b! (zero? t2-13) cfg-37 :delay (set! t2-14 (+ a3-0 -72)))
(b! (zero? t2-14) cfg-37 :delay (set! t2-15 (+ a3-0 -118)))
(b! (zero? t2-15) cfg-3 :delay (set! t2-16 (+ a3-0 -86)))
(b! (zero? t2-16) cfg-3 :delay (set! t2-17 (+ a3-0 -48)))
(b! (< (the-as int t2-17) 0) cfg-45 :delay (set! t3-0 (+ a3-0 -57)))
(b! (> (the-as int t3-0) 0) cfg-45 :delay (.sll t3-1 t1-0 2))
(let ((a3-1 (+ t1-0 t3-1)))
(nop!)
(.sll a3-2 a3-1 1)
)
(nop!)
(b! #t cfg-14 :delay (set! t1-0 (+ a3-2 t2-17)))
(label cfg-32)
(b! (nonzero? t1-0) cfg-34 :delay (set! a2-4 *font12-table*))
(set! a2-2 a2-4)
(let ((a3-3 -33))
(.lvf vf13 (&-> a1-4 size1-small quad))
(nop!)
(.lvf vf14 (&-> a1-4 size2-small quad))
(b! #t cfg-3 :delay (set! v1-0 (logand v1-0 a3-3)))
)
(label cfg-34)
(nop!)
(set! a2-2 *font24-table*)
(nop!)
(.lvf vf13 (&-> a1-4 size1-large quad))
(nop!)
(.lvf vf14 (&-> a1-4 size2-large quad))
(b! #t cfg-3 :delay (set! v1-0 (logior v1-0 32)))
(label cfg-35)
(let ((a3-4 -3))
(nop!)
(b! (zero? t1-0) cfg-3 :delay (set! v1-0 (logand v1-0 a3-4)))
)
(b! #t cfg-3 :delay (set! v1-0 (logior v1-0 2)))
(label cfg-37)
(.mov vf1 t1-0)
(init-vf0-vector)
(.lvf vf25 (&-> arg1 context-vec quad))
(.lvf vf23 (&-> arg1 origin quad))
(.lvf vf24 (&-> arg1 origin quad))
(let ((flags (-> arg1 flags))
(work *font-work*))
(.lvf vf1 (&-> *video-parms* relative-x-scale-reciprical))
(.mul.vf vf25 vf25 vf1 :mask #b11)
(.mul.vf vf23 vf23 vf1 :mask #b11)
;; hack! fixes small font widescreen
(unless (logtest? (-> arg1 flags) (font-flags pc-hack))
(.mul.vf vf24 vf24 vf1 :mask #b11)
)
(let ((a3-5 (+ t0-4 -45)))
(b! (zero? t0-4) cfg-41 :delay (.itof.vf vf1 vf1))
(b! (zero? a3-5) cfg-40 :delay (nop!))
)
(b! #t cfg-3 :delay (.add.x.vf vf23 vf23 vf1 :mask #b1))
(label cfg-40)
(b! #t cfg-3 :delay (.sub.x.vf vf23 vf23 vf1 :mask #b1))
(label cfg-41)
(b! #t cfg-3 :delay (.add.x.vf vf23 vf0 vf1 :mask #b1))
(label cfg-42)
(b! #t cfg-3 :delay (.svf (&-> a1-4 save quad) vf23))
(label cfg-43)
(b! #t cfg-3 :delay (.lvf vf23 (&-> a1-4 save quad)))
(label cfg-44)
(let ((a3-6 (-> (the-as (pointer uint8) arg0) 4)))
(set! arg0 (the-as string (&-> (the-as (pointer uint8) arg0) 1)))
(nop!)
(let ((a3-7 (logand a3-6 127)))
(nop!)
(let ((a3-8 (+ a3-7 255)))
(b! #t cfg-48 :delay (.sll t0-5 a3-8 4))
(set! (-> work str-ptr) (the-as uint arg0))
(set! (-> work flags) flags)
(.mov.vf vf1 vf0)
(let ((kerning-table (cond
((logtest? flags (font-flags large))
(.lvf vf13 (&-> work size1-large quad))
(.lvf vf14 (&-> work size2-large quad))
*font24-table*)
(else
(.lvf vf13 (&-> work size1-small quad))
(.lvf vf14 (&-> work size2-small quad))
*font12-table*)
)))
(label cfg-3)
(let ((cur-char (-> arg0 data 0)))
(set! arg0 (the-as string (&-> (the-as (pointer uint8) arg0) 1)))
(if (zero? cur-char) (goto cfg-51))
(when (= cur-char 1)
(let ((a3-6 (-> arg0 data 0)))
(set! arg0 (the-as string (&-> (the-as (pointer uint8) arg0) 1)))
(set! cur-char (+ (logand a3-6 127) 255))
(goto cfg-48)
)
)
(when (= cur-char #\~)
(set! cur-char (-> arg0 data 0))
(set! arg0 (the-as string (&-> (the-as (pointer uint8) arg0) 1)))
(if (zero? cur-char) (goto cfg-51))
(when (or (= cur-char #\Y) (= cur-char #\y))
(.svf (&-> work save quad) vf23)
(goto cfg-3))
(when (or (= cur-char #\Z) (= cur-char #\z))
(.lvf vf23 (&-> work save quad))
(goto cfg-3))
(let ((sign-char (if (or (= cur-char #\-) (= cur-char #\+)) cur-char 0))
(arg-val 0))
(when (or (nonzero? sign-char)
(and (>= cur-char #\0) (<= cur-char #\9)))
(label cfg-14)
(set! cur-char (-> arg0 data 0))
(set! arg0 (the-as string (&-> (the-as (pointer uint8) arg0) 1)))
(if (zero? cur-char) (goto cfg-51))
(case cur-char
((#\n #\N)
(cond
((nonzero? arg-val)
(.lvf vf13 (&-> work size1-large quad))
(.lvf vf14 (&-> work size2-large quad))
(set! kerning-table *font24-table*)
(logior! flags (font-flags large)))
(else
(.lvf vf13 (&-> work size1-small quad))
(.lvf vf14 (&-> work size2-small quad))
(set! kerning-table *font12-table*)
(logclear! flags (font-flags large)))
)
(goto cfg-3)
)
((#\l #\L #\w #\W #\j #\J #\v #\V)
(goto cfg-3)
)
((#\k #\K)
(if (zero? arg-val)
(logclear! flags (font-flags kerning))
(logior! flags (font-flags kerning)))
(goto cfg-3)
)
((#\h #\H)
(.mov vf1 arg-val)
(.itof.vf vf1 vf1)
(cond
((zero? sign-char)
(.add.x.vf vf23 vf0 vf1 :mask #b1)
)
((= sign-char #\-)
(.sub.x.vf vf23 vf23 vf1 :mask #b1)
)
(else
(.add.x.vf vf23 vf23 vf1 :mask #b1)
)
)
(goto cfg-3)
)
)
(when (and (>= cur-char #\0) (<= cur-char #\9))
(set! arg-val (+ (* arg-val 10) (- cur-char #\0)))
(goto cfg-14)
)
)
)
)
(cond
((and (!= cur-char 10) (!= cur-char 13))
(label cfg-48)
(.lvf vf5 (&-> kerning-table (- cur-char 16) quad))
(.mul.vf vf19 vf5 vf13)
(if (logtest? flags (font-flags kerning))
(.add.w.vf vf23 vf23 vf19 :mask #b1)
(.add.w.vf vf23 vf23 vf14 :mask #b1))
)
(else
(.add.x.vf vf23 vf0 vf24 :mask #b1)
)
)
(goto cfg-3)
)
)
)
(label cfg-45)
(nop!)
(.sll t0-5 a3-0 4)
(nop!)
(b! (= a3-0 10) cfg-47 :delay (set! a3-9 (+ a3-0 -13)))
)
(b! (nonzero? a3-9) cfg-48 :delay (nop!))
(label cfg-47)
(b! #t cfg-3 :delay (.add.x.vf vf23 vf0 vf24 :mask #b1))
(label cfg-48)
(.addu a3-10 t0-5 a2-2)
)
)
(nop!)
(.lvf vf5 (+ a3-10 -256))
(nop!)
(.mul.vf vf19 vf5 vf13)
(b! (zero? (logand v1-0 2)) cfg-50 :delay (nop!))
(label cfg-51)
(.sub.vf vf23 vf23 vf24)
(.mov v0-0 vf23)
v0-0
)
(b! #t cfg-3 :delay (.add.w.vf vf23 vf23 vf19 :mask #b1))
(label cfg-50)
(b! #t cfg-3 :delay (.add.w.vf vf23 vf23 vf14 :mask #b1))
(label cfg-51)
(.sub.vf vf23 vf23 vf24)
(.mov v0-0 vf23)
v0-0
)
)
(defun draw-string-xy ((str string) (buf dma-buffer) (x int) (y int) (color font-color) (flags font-flags))

View File

@ -117,7 +117,7 @@
(disable-level-text-file-loading)
(protect ((-> *pc-settings* text-language))
;; swap language and load new text files
(set! (-> *pc-settings* text-language) (the pc-subtitle-lang (-> *setting-control* current language)))
(set! (-> *pc-settings* text-language) (the pc-language (-> *setting-control* current language)))
(load-level-text-files 0)))
(let ((s4-0 (+ (- arg0) (the int (* 1.5 (the float (-> *video-parms* screen-sy))))))
(gp-0 2815)

View File

@ -465,7 +465,7 @@
(none)
)
(defun print-game-text ((str string) (font-ctxt font-context) (opaque symbol) (alpha int) (line-height int))
(defun print-game-text ((str string) (font-ctxt font-context) (no-draw symbol) (alpha int) (line-height int))
"Print text. Not worth commenting until we get stack variables in lets, I think"
(local-vars
(sv-112 float)
@ -619,7 +619,7 @@
(if (nonzero? (-> *game-text-line* data 0))
(set! sv-168 (+ sv-168 1))
)
(when (not opaque)
(when (not no-draw)
(let* ((s1-1 (-> *display* frames (-> *display* on-screen) frame global-buf))
(s2-1 (-> s1-1 base))
)

View File

@ -403,7 +403,11 @@
(defstep :in "game/assets/jak1/game_subtitle.gp"
:tool 'subtitle
:out '("$OUT/iso/0SUBTIT.TXT"
"$OUT/iso/1SUBTIT.TXT"
"$OUT/iso/2SUBTIT.TXT"
"$OUT/iso/3SUBTIT.TXT"
"$OUT/iso/4SUBTIT.TXT"
"$OUT/iso/5SUBTIT.TXT"
"$OUT/iso/6SUBTIT.TXT")
)

View File

@ -429,7 +429,7 @@
)
(defun dm-subtitle-language ((blang int) (msg debug-menu-msg))
(let ((lang (the pc-subtitle-lang (/ blang 8))))
(let ((lang (the pc-language (/ blang 8))))
(when (= msg (debug-menu-msg press))
(set! (-> *pc-settings* subtitle-language) lang))
(= (-> *pc-settings* subtitle-language) lang)
@ -437,7 +437,7 @@
)
(defun dm-text-language ((blang int) (msg debug-menu-msg))
(let ((lang (the pc-subtitle-lang (/ blang 8))))
(let ((lang (the pc-language (/ blang 8))))
(when (= msg (debug-menu-msg press))
(set! (-> *pc-settings* text-language) lang))
(= (-> *pc-settings* text-language) lang)

View File

@ -692,7 +692,6 @@
(("lod-force-ocean") (set! (-> obj lod-force-ocean) (file-stream-read-int file)))
(("lod-force-actor") (set! (-> obj lod-force-actor) (file-stream-read-int file)))
(("game-language") (set-game-language! obj (the-as language-enum (file-stream-read-int file))))
(("text-language") (set! (-> obj text-language) (the-as pc-subtitle-lang (file-stream-read-int file))))
(("subtitle-speaker") (set! (-> obj subtitle-speaker?) (file-stream-read-symbol file)))
(("ignore-controller-win-unfocused?") (set-ignore-controller-in-bg! obj (file-stream-read-symbol file)))
@ -834,7 +833,6 @@
(format file " (music-fadeout? ~A)~%" (-> obj music-fadeout?))
(format file " (hinttitles? ~A)~%" (-> obj hinttitles?))
(format file " (game-language ~D)~%" (get-game-language obj))
(format file " (text-language ~D)~%" (-> obj text-language))
(format file " (subtitle-speaker ~A)~%" (-> obj subtitle-speaker?))
#|

View File

@ -81,39 +81,6 @@
)
;; subtitle languages.
(defenum pc-subtitle-lang
:type uint16
(english 0)
(french 1)
(german 2)
(spanish 3)
(italian 4)
(japanese 5)
(uk-english 6)
;; additional languages.
;; these don't neccessarily have to work but I'm future-proofing a bit here, just in case.
;; languages that use the existing glyphs
(portuguese 7)
(finnish 8)
(swedish 9)
(danish 10)
(norwegian 11)
(dutch 12)
(br-portuguese 13)
(hungarian 14)
(catalan 15)
(icelandic 16)
;; jak 1 has no glyphs for korean or cyrillic.
(korean 98) ;; future-proofing here
(russian 97) ;; same thing
(custom 100) ;; temp
)
;; concept arts
(defenum pc-jak1-concept-art
@ -275,7 +242,6 @@
(force-actors? symbol) ;; skips vis check for actor entity
(use-vis? symbol) ;; if off, don't use vis trees. this MUST be off for custom (non-cropping) aspect ratios.
(hinttitles? symbol) ;; if on, non-cutscene subtitles will show up
(text-language pc-subtitle-lang) ;; language for game text
(subtitle-speaker? symbol) ;; #f (force off), #t (force on), auto (on for offscreen)
(first-camera-h-inverted? symbol) ;; first-person horizontal camera inverted
(first-camera-v-inverted? symbol) ;; first-person vertical camera inverted
@ -535,7 +501,6 @@
"Set the default misc settings"
(set! (-> obj force-actors?) #f)
(set! (-> obj text-language) (the pc-subtitle-lang (scf-get-language)))
(set! (-> obj hinttitles?) #t)
(set! (-> obj subtitle-speaker?) 'auto)
(reset-original-camera obj)

View File

@ -19,12 +19,40 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pc enum for languages. this is the game's languages + custom ones.
(defenum pc-language
:type uint16
(english 0)
(french 1)
(german 2)
(spanish 3)
(italian 4)
(japanese 5)
(uk-english 6)
;; custom
(portuguese 7)
(finnish 8)
(swedish 9)
(danish 10)
(norwegian 11)
(dutch 12)
(br-portuguese 13)
(hungarian 14)
(catalan 15)
(icelandic 16)
(korean 17)
(russian 18)
(custom 999) ;; temp
)
;; The Jak 1 version of the pc-settings object.
(deftype pc-settings-jak1 (pc-settings)
(
(skip-movies? symbol) ;; if on, enable cutscene skipping
(subtitles? symbol) ;; if on, cutscene subtitles will show up
(subtitle-language pc-subtitle-lang) ;; language for subtitles
(text-language pc-language) ;; language for game text
(subtitle-language pc-language) ;; language for subtitles
(money-starburst? symbol) ;; add a starburst to the money
(extra-hud? symbol) ;; extra hud elements.
)
@ -72,18 +100,18 @@
"Set the default misc settings"
((method-of-type pc-settings reset-misc) obj)
(set! (-> obj text-language) (the pc-subtitle-lang (scf-get-language)))
(set! (-> obj subtitle-language) (the pc-subtitle-lang (scf-get-language)))
(set! (-> obj text-language) (the pc-language (scf-get-language)))
(set! (-> obj subtitle-language) (the pc-language (scf-get-language)))
(set! (-> obj skip-movies?) #t)
(set! (-> obj subtitles?) *debug-segment*)
(cond
((and (= *jak1-territory* GAME_TERRITORY_SCEE) (= (-> obj text-language) (pc-subtitle-lang english)))
(set! (-> obj text-language) (pc-subtitle-lang uk-english))
;(set! (-> obj subtitle-language) (pc-subtitle-lang uk-english))
((and (= *jak1-territory* GAME_TERRITORY_SCEE) (= (-> obj text-language) (pc-language english)))
(set! (-> obj text-language) (pc-language uk-english))
;(set! (-> obj subtitle-language) (pc-language uk-english))
)
((= *jak1-territory* GAME_TERRITORY_SCEI)
(set! (-> obj text-language) (pc-subtitle-lang japanese))
;(set! (-> obj subtitle-language) (pc-subtitle-lang japanese))
(set! (-> obj text-language) (pc-language japanese))
;(set! (-> obj subtitle-language) (pc-language japanese))
)
(else
))

View File

@ -337,7 +337,8 @@
(("extra-hud?") (set! (-> obj extra-hud?) (file-stream-read-symbol file)))
(("skip-movies?") (set! (-> obj skip-movies?) (file-stream-read-symbol file)))
(("subtitles?") (set! (-> obj subtitles?) (file-stream-read-symbol file)))
(("subtitle-language") (set! (-> obj subtitle-language) (the-as pc-subtitle-lang (file-stream-read-int file))))
(("subtitle-language") (set! (-> obj subtitle-language) (the-as pc-language (file-stream-read-int file))))
(("text-language") (set! (-> obj text-language) (the-as pc-language (file-stream-read-int file))))
)
0)
@ -350,6 +351,7 @@
(format file " (skip-movies? ~A)~%" (-> obj skip-movies?))
(format file " (subtitles? ~A)~%" (-> obj subtitles?))
(format file " (subtitle-language ~D)~%" (-> obj subtitle-language))
(format file " (text-language ~D)~%" (-> obj text-language))
0)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -69,7 +69,7 @@
(deftype progress-carousell-state (structure)
((int-backup int)
(symbol-backup symbol)
(subtitle-backup pc-subtitle-lang)
(subtitle-backup pc-language)
(aspect-native-choice symbol)
(current-carousell (array text-id))
@ -506,7 +506,7 @@
(defmacro def-language-remap-info (name langs)
`(define ,name (quote ,(apply (lambda (x) `((the binteger (text-id ,x)) (the binteger (pc-subtitle-lang ,x)))) langs)))
`(define ,name (quote ,(apply (lambda (x) `((the binteger (text-id ,x)) (the binteger (pc-language ,x)))) langs)))
)
(def-language-remap-info *language-remap-info-pc*
@ -756,10 +756,10 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun get-language-name ((lang pc-subtitle-lang))
(defun get-language-name ((lang pc-language))
"get the text-id from a lang"
(dolist (item *language-remap-info-pc*)
(when (= lang (the pc-subtitle-lang (/ (the int (cadr (car item))) 8)))
(when (= lang (the pc-language (/ (the int (cadr (car item))) 8)))
(return (the text-id (/ (the int (car (car item))) 8))))
)
(text-id zero)
@ -1862,12 +1862,12 @@
)
(((game-option-type language-subtitles))
(let ((lang (assoc (the binteger (-> *subtitle-languages* (-> *progress-carousell* int-backup))) *language-remap-info-pc*)))
(set! (-> *pc-settings* subtitle-language) (the pc-subtitle-lang (/ (the int (cadr lang)) 8)))
(set! (-> *pc-settings* subtitle-language) (the pc-language (/ (the int (cadr lang)) 8)))
)
)
(((game-option-type language-text))
(let ((lang (assoc (the binteger (-> *text-languages* (-> *progress-carousell* int-backup))) *language-remap-info-pc*)))
(set! (-> *pc-settings* text-language) (the pc-subtitle-lang (/ (the int (cadr lang)) 8)))
(set! (-> *pc-settings* text-language) (the pc-language (/ (the int (cadr lang)) 8)))
)
(if (not (-> *progress-carousell* transition))
(load-level-text-files (-> obj display-level-index)))

View File

@ -89,7 +89,7 @@
;; the global subtitle text info bank
(deftype subtitle-text-info (basic)
((length int32)
(lang pc-subtitle-lang)
(lang pc-language)
(dummy int32)
(data subtitle-text :inline :dynamic)
)
@ -296,7 +296,7 @@
`(begin
(asm-text-file subtitle :files ("game/assets/game_subtitle.gp"))
(if *subtitle-text*
(+! (-> *subtitle-text* lang) (the-as pc-subtitle-lang 1)))
(+! (-> *subtitle-text* lang) 1))
(load-level-subtitle-files 0)))
(defmacro reload-text ()
@ -316,7 +316,7 @@
(defun-recursive print-game-subtitle float ((str string) (font-ctxt font-context) (opaque symbol) (alpha int) (line-height int))
(defun-recursive print-game-subtitle float ((str string) (font-ctxt font-context) (no-draw symbol) (alpha int) (line-height int))
"Print text. Not worth commenting until we get stack variables in lets, I think"
(local-vars
(sv-112 float)
@ -469,7 +469,7 @@
(if (nonzero? (-> *game-text-line* data 0))
(set! sv-168 (+ sv-168 1))
)
(when (not opaque)
(when (not no-draw)
(let* ((s1-1 (-> *display* frames (-> *display* on-screen) frame global-buf))
(s2-1 (-> s1-1 base))
)
@ -693,19 +693,20 @@
(((pc-subtitle-channel hint) (pc-subtitle-channel hint-named))
;; hint! find it. or else.
(set! (-> self hint-subtitle?) #t)
(awhen (if (= (-> self cur-channel) (pc-subtitle-channel hint-named))
(get-scene-by-name *subtitle-text* (-> self cur-channel) (-> self spool-name))
(get-scene-by-text-id *subtitle-text* (-> self cur-channel) (-> self text-id))
)
(let ((pos (subtitle-str-adjust (current-str-pos (-> *hint-semaphore* 0 sound-id)))))
;; find closest keyframe
(dotimes (i (-> it length))
(when (>= pos (-> it keyframes i frame))
(set! keyframe (-> it keyframes i)))
)
)
)
(let ((scene (get-scene-by-text-id *subtitle-text* (pc-subtitle-channel hint) (-> self text-id))))
(if (not scene)
(set! scene (get-scene-by-name *subtitle-text* (pc-subtitle-channel hint-named) (-> self spool-name))))
(when scene
(let ((pos (subtitle-str-adjust (current-str-pos (-> *hint-semaphore* 0 sound-id)))))
;; find closest keyframe
(dotimes (i (-> scene length))
(when (>= pos (-> scene keyframes i frame))
(set! keyframe (-> scene keyframes i)))
)
)
))
)
))

View File

@ -1,384 +0,0 @@
("ENGINE.CGO"
("types-h.o"
"vu1-macros.o"
"math.o"
"vector-h.o"
"gravity-h.o"
"bounding-box-h.o"
"matrix-h.o"
"quaternion-h.o"
"euler-h.o"
"transform-h.o"
"geometry-h.o"
"trigonometry-h.o"
"transformq-h.o"
"bounding-box.o"
"matrix.o"
"transform.o"
"quaternion.o"
"euler.o"
"trigonometry.o"
"gsound-h.o"
"timer-h.o"
"vif-h.o"
"dma-h.o"
"video-h.o"
"vu1-user-h.o"
"profile-h.o"
"dma.o"
"dma-buffer.o"
"dma-bucket.o"
"dma-disasm.o"
"pckernel-h.o" ;; added
"pckernel-impl.o" ;; added
"pc-debug-common.o" ;; added
"pad.o"
"gs.o"
"display-h.o"
"geometry.o"
"timer.o"
"vector.o"
"file-io.o"
"loader-h.o"
"texture-h.o"
"texture-anim-h.o"
"lights-h.o"
"mood-h.o"
"level-h.o"
"capture-h.o"
"math-camera-h.o"
"math-camera.o"
"font-h.o"
"decomp-h.o"
"profile.o"
"display.o"
"connect.o"
"text-id-h.o"
"text-h.o"
"camera-defs-h.o"
"trail-h.o"
"minimap-h.o"
"bigmap-h.o"
"settings-h.o"
"capture.o"
"memory-usage-h.o"
"blit-displays-h.o"
"texture.o"
"main-h.o"
"mspace-h.o"
"drawable-h.o"
"drawable-group-h.o"
"drawable-inline-array-h.o"
"draw-node-h.o"
"drawable-tree-h.o"
"drawable-actor-h.o"
"region-h.o"
"traffic-h.o"
"game-task-h.o"
"task-control-h.o"
"generic-h.o"
"sky-h.o"
"ocean-h.o"
"ocean-trans-tables.o"
"ocean-tables.o"
"ocean-frames.o"
"time-of-day-h.o"
"art-h.o"
"generic-vu1-h.o"
"merc-h.o"
"generic-merc-h.o"
"generic-tie-h.o"
"generic-work-h.o"
"shadow-cpu-h.o"
"shadow-vu1-h.o"
"memcard-h.o"
"game-info-h.o"
"gui-h.o"
"ambient-h.o"
"speech-h.o"
"wind-h.o"
"prototype-h.o"
"joint-h.o"
"bones-h.o"
"foreground-h.o"
"engines.o"
"lightning-h.o"
"res-h.o"
"res.o"
"lights.o"
"dynamics-h.o"
"surface-h.o"
"pat-h.o"
"fact-h.o"
"aligner-h.o"
"penetrate-h.o"
"game-h.o"
"script-h.o"
"scene-h.o"
"sync-info-h.o"
"pov-camera-h.o"
"smush-control-h.o"
"debug-h.o"
"joint-mod-h.o"
"collide-func-h.o"
"collide-mesh-h.o"
"collide-shape-h.o"
"generic-obs-h.o"
"trajectory-h.o"
"collide-target-h.o"
"collide-touch-h.o"
"collide-edge-grab-h.o"
"process-drawable-h.o"
"process-focusable.o"
"process-taskable-h.o"
"focus.o"
"effect-control-h.o"
"collide-frag-h.o"
"collide-hash-h.o"
"chain-physics-h.o"
"projectile-h.o"
"find-nearest-h.o"
"target-h.o"
"stats-h.o"
"bsp-h.o"
"collide-cache-h.o"
"collide-h.o"
"shrubbery-h.o"
"tie-h.o"
"tfrag-h.o"
"background-h.o"
"subdivide-h.o"
"entity-h.o"
"sprite-h.o"
"simple-sprite-h.o"
"eye-h.o"
"sparticle-launcher-h.o"
"sparticle-h.o"
"actor-link-h.o"
"camera-h.o"
"cam-debug-h.o"
"cam-interface-h.o"
"cam-update-h.o"
"hud-h.o"
"progress-h.o"
"rpc-h.o"
"path-h.o"
"nav-mesh-h.o"
"nav-control-h.o"
"spatial-hash-h.o"
"actor-hash-h.o"
"load-dgo.o"
"ramdisk.o"
"gsound.o"
"transformq.o"
"collide-func.o"
"joint.o"
"joint-mod.o"
"chain-physics.o"
"cylinder.o"
"wind-work.o"
"wind.o"
"bsp.o"
"subdivide.o"
"sprite.o"
"sprite-distort.o"
"sprite-glow.o"
"debug-sphere.o"
"debug.o"
"history.o"
"merc-vu1.o"
"emerc-vu1.o"
"merc-blend-shape.o"
"merc.o"
"emerc.o"
"ripple.o"
"bones.o"
"debug-foreground.o"
"foreground.o"
"generic-vu0.o"
"generic-vu1.o"
"generic-effect.o"
"generic-merc.o"
"generic-tie.o"
"shadow-cpu.o"
"shadow-vu1.o"
"warp.o"
"texture-anim.o"
"texture-anim-funcs.o"
"texture-anim-tables.o"
"blit-displays.o"
"font-data.o"
"font.o"
"decomp.o"
"background.o"
"draw-node.o"
"shrubbery.o"
"shrub-work.o"
"tfrag-near.o"
"tfrag.o"
"tfrag-methods.o"
"tfrag-work.o"
"tie.o"
"etie-vu1.o"
"etie-near-vu1.o"
"tie-near.o"
"tie-work.o"
"tie-methods.o"
"sync-info.o"
"trajectory.o"
"sparticle-launcher.o"
"sparticle.o"
"entity-table.o"
"loader.o"
"game-info.o"
"game-task.o"
"game-save.o"
"settings.o"
"autosplit-h.o" ;; added
"autosplit.o" ;; added
"speedruns-h.o" ;; added
"speedruns.o" ;; added
"mood-tables.o"
"mood-tables2.o"
"mood.o"
"mood-funcs.o"
"mood-funcs2.o"
"weather-part.o"
"time-of-day.o"
"sky-data.o"
"sky-tng.o"
"load-state.o"
"pc-debug-methods.o" ;; added
"level-info.o"
"level.o"
"text.o"
"collide-hash.o"
"collide-probe.o"
"collide-frag.o"
"collide-mesh.o"
"collide-touch.o"
"collide-edge-grab.o"
"collide-shape.o"
"collide-shape-rider.o"
"collide.o"
;; "collide-planes.o"
"spatial-hash.o"
"actor-hash.o"
"merc-death.o"
"water-flow.o"
"water-h.o"
"camera.o"
"cam-interface.o"
"cam-master.o"
"cam-states.o"
"cam-states-dbg.o"
"cam-combiner.o"
"cam-update.o"
"vol-h.o"
"cam-layout.o"
"cam-debug.o"
"cam-start.o"
"process-drawable.o"
"ambient.o"
"speech.o"
"region.o"
"fma-sphere.o"
"script.o"
"generic-obs.o"
"lightning.o"
"carry-h.o"
"pilot-h.o"
"gun-h.o"
"board-h.o"
"darkjak-h.o"
"target-util.o"
"target-part.o"
"gun-part.o"
"collide-reaction-target.o"
"logic-target.o"
"sidekick.o"
"effect-control.o"
"voicebox.o"
"collectables-part.o"
"debug-part.o"
"find-nearest.o"
"task-arrow.o"
"projectile.o"
"target-handler.o"
"target-anim.o"
"target.o"
"target2.o"
"target-swim.o"
"target-carry.o"
"target-darkjak.o"
"target-death.o"
"target-gun.o"
"gun-util.o"
"gun-blue-shot.o"
"gun-yellow-shot.o"
"gun-red-shot.o"
"gun-dark-shot.o"
"gun-states.o"
"board-util.o"
"target-board.o"
"board-part.o"
"board-states.o"
"mech-h.o"
"menu.o"
"drawable.o"
"drawable-group.o"
"drawable-inline-array.o"
"drawable-tree.o"
"prototype.o"
"main-collide.o"
"video.o"
"pckernel-common.o" ;; added
"pckernel.o" ;; added
"main.o"
"collide-cache.o"
"collide-debug.o"
"relocate.o"
"memory-usage.o"
"entity.o"
"path.o"
"vol.o"
"nav-mesh.o"
"nav-control.o"
"aligner.o"
"water.o"
"collectables.o"
"task-control.o"
"scene.o"
"pov-camera.o"
"powerups.o"
"crates.o"
"hud.o"
"hud-classes.o"
"progress-static.o"
"progress.o"
"progress-draw.o"
"ocean.o"
"ocean-vu0.o"
"ocean-texture.o"
"ocean-mid.o"
"ocean-transition.o"
"ocean-near.o"
"minimap.o"
"bigmap-data.o"
"bigmap.o"
"eye.o"
"glist-h.o"
"glist.o"
"anim-tester.o"
"viewer.o"
"part-tester.o"
"editable-h.o"
"editable.o"
"editable-player.o"
"mysql-nav-graph.o"
"nav-graph-editor.o"
"sampler.o"
"default-menu.o"
"anim-tester-x.o" ;; added
"default-menu-pc.o" ;; added
))

View File

@ -334,6 +334,8 @@
"video.o"
"pckernel-common.o" ;; added
"pckernel.o" ;; added
"subtitle2-h.o" ;; added
"subtitle2.o" ;; added
"main.o"
"collide-cache.o"
"collide-debug.o"
@ -380,6 +382,7 @@
"sampler.o"
"default-menu.o"
"anim-tester-x.o" ;; added
"vag-player.o" ;; added
"default-menu-pc.o" ;; added
"dir-tpages.go"
"tpage-11.go"

View File

@ -54,7 +54,7 @@
(enemy-flag36 36)
(enemy-flag37 37)
(enemy-flag38 38)
(enemy-flag39 39)
(not-frustrated 39)
(enemy-flag40 40)
(enemy-flag41 41)
(enemy-flag42 42)

View File

@ -175,9 +175,11 @@
)
(play-communicator-speech! arg0)
)
(set! s2-0 (lookup-gui-connection-id *gui-control* (-> arg0 name) (-> arg0 channel) (gui-action none)))
(set! s2-0
(the-as int (lookup-gui-connection-id *gui-control* (-> arg0 name) (-> arg0 channel) (gui-action none)))
)
(set! s2-0 (cond
((zero? s2-0)
((zero? (the-as sound-id s2-0))
(let ((v1-17 (process-spawn talker :init talker-init arg0 arg2 arg3 :to arg1)))
(cond
(v1-17

View File

@ -734,7 +734,7 @@
(quaternion-copy! (-> self root quat) (-> (the-as process-drawable (-> self parent 0)) root quat))
(set! (-> self root scale quad) (-> (the-as process-drawable (-> self parent 0)) root scale quad))
(when (-> arg3 art-level)
(let ((a1-6 (entity-actor-from-level-name (the-as level (-> arg3 art-level)))))
(let ((a1-6 (entity-actor-from-level-name (-> arg3 art-level))))
(if a1-6
(process-entity-set! self a1-6)
)

View File

@ -643,6 +643,6 @@
(defmacro target-look-at-me! (&key trans &key (message 'nothing-special))
"make target look at a trans in self. PC PORT NOTE : added check to see if lods have been set"
`(if (and (logtest? (-> self draw status) (draw-control-status lod-set)) *target*)
`(if (and (not (logtest? (-> self draw status) (draw-control-status uninited no-draw-temp))) *target*)
(look-at! (-> *target* neck) ,trans ,message self)))

View File

@ -2695,7 +2695,7 @@
(a0-5 arg1)
)
(when (if (= a0-5 'test)
(logtest? (continue-flags cf17) (-> (the-as continue-point v1-2) flags))
(logtest? (continue-flags test) (-> (the-as continue-point v1-2) flags))
#t
)
(let ((s2-0 (method-of-type pair new))

View File

@ -471,7 +471,7 @@
(debug-menu-context-send-msg gp-0 (debug-menu-msg deactivate) (debug-menu-dest activation))
)
(set! (-> arg1 parent) arg0)
(set! (-> arg0 items) (the-as pair (append! (-> arg0 items) (cons arg1 '()))))
(set! (-> arg0 items) (the-as pair (append! (-> arg0 items) (dcons arg1 '())))) ;; changed to dcons
(debug-menu-rebuild arg0)
(if s4-0
(debug-menu-context-send-msg gp-0 (debug-menu-msg activate) (debug-menu-dest activation))

View File

@ -270,6 +270,6 @@ This commonly includes things such as:
;; WARN: Return type mismatch symbol vs object.
(defun birth-viewer ((arg0 process) (arg1 entity-actor))
(set! (-> arg0 type) viewer)
(init-entity arg0 arg1 (the-as process viewer))
(init-entity arg0 arg1 viewer)
(the-as object #t)
)

View File

@ -1348,7 +1348,7 @@
(none)
)
(defun main-debug-hook ()
(defun-debug main-debug-hook ()
"Execute the debug engine, collision renderer, and draw-instance-info."
(when (not (or (= *master-mode* 'menu) (= *master-mode* 'progress)))
(let ((a0-3 *col-rend*))

View File

@ -314,6 +314,7 @@ A "connection" is really just a function that gets called when the engine runs,
(defmethod apply-to-connections-reverse engine ((obj engine) (arg0 (function connectable none)))
"Apply f to all connections, reverse order.
Do not use f to remove yourself from the list."
(declare (inline))
(let ((s4-0 (-> obj alive-list-end prev0)))
(while (!= s4-0 (-> obj alive-list))
(arg0 s4-0)

View File

@ -216,7 +216,7 @@
)
;; WARN: Return type mismatch entity vs entity-actor.
(defun entity-actor-from-level-name ((arg0 level))
(defun entity-actor-from-level-name ((arg0 symbol))
(let ((v0-0 (the-as entity #f)))
(dotimes (s5-0 (-> *level* length))
(let ((s4-0 (-> *level* level s5-0)))
@ -1579,7 +1579,7 @@
)
;; WARN: Return type mismatch process vs none.
(defun init-entity ((arg0 process) (arg1 entity-actor) (arg2 process))
(defun init-entity ((arg0 process) (arg1 entity-actor) (arg2 type))
(activate arg0 *entity-pool* (res-lump-struct arg1 'name basic) (the-as pointer #x70004000))
(set! (-> arg0 entity) arg1)
(set! (-> arg0 level) (-> arg1 extra level))
@ -1605,7 +1605,7 @@
(set! (-> s4-0 type) s5-0)
(and s5-0 (valid? s5-0 type (the-as string #f) #f 0) (valid? (method-of-object s4-0 init-from-entity!) function (the-as string #f) #f 0))
)
(init-entity s4-0 obj (the-as process s5-0))
(init-entity s4-0 obj s5-0)
)
(else
(when (not (birth-viewer s4-0 obj))
@ -2229,9 +2229,15 @@
(dotimes (s1-1 s2-3)
(set! sv-48 (-> s3-4 data s1-1))
(cond
((and (#if PC_PORT (or (with-pc (and (-> *pc-settings* force-actors?) (not (let ((name (res-lump-struct (-> sv-48 entity) 'name string)))
(or (string= name "fort-entry-gate-11")
(string= name "com-airlock-outer-13"))))))
((and (#if PC_PORT (or (with-pc (and (-> *pc-settings* force-actors?)
;; ban specific entities
(not (let ((name (res-lump-struct (-> sv-48 entity) 'name string)))
(or (string= name "fort-entry-gate-11")
(string= name "com-airlock-outer-13")
(string= name "com-airlock-inner-41")
(string= name "under-lift-4")
(string= name "under-locking-1")
(string= name "under-locking-2"))))))
(is-object-visible? s4-1 (-> sv-48 vis-id)))
(is-object-visible? s4-1 (-> sv-48 vis-id)))
(not (logtest? (-> sv-48 perm status) (entity-perm-status bit-9 bit-10)))

View File

@ -12,37 +12,29 @@
(defenum continue-flags
:type uint32
:bitfield #t
(cf0 0)
(cf1 1)
(cf2 2)
(cf3 3)
(cf4 4)
(cf5 5)
(cf6 6)
(cf7 7)
(cf8 8)
(cf9 9)
(cf10 10)
(cf11 11)
(cf12 12)
(cf13 13)
(cf14 14)
(cf15 15)
(cf16 16)
(cf17 17)
(cf18 18)
(cf19 19)
(cf20 20)
(cf21 21)
(cf22 22)
(cf23 23)
(cf24 24)
(cf25 25)
(cf26 26)
(cf27 27)
(cf28 28)
(cf29 29)
(cf30 30)
;(continue-flag-0 0)
(scene-wait 1)
(change-continue 2)
(no-auto 3)
(no-blackout 4)
(game-start 5)
(demo-end 6)
(warp-gate 7)
(demo 8)
(intro 9)
(hero-mode 10)
(demo-movie 11)
(title 12)
(title-movie 13)
(continue-flag-14 14)
(continue-flag-15 15)
(continue-flag-16 16)
(test 17)
(record-path 18)
(pilot 19)
(pilot-dax 20)
(record-sig 21)
(indax 22)
)
;; +++game-secrets

View File

@ -79,7 +79,7 @@
(new 'static 'continue-point
:name "default"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :w 1.0)
:quat (new 'static 'vector :w 1.0)
:camera-trans (new 'static 'vector :w 1.0)
@ -227,7 +227,7 @@
)
)
)
(if (and (logtest? (-> obj current-continue flags) (continue-flags cf2))
(if (and (logtest? (-> obj current-continue flags) (continue-flags change-continue))
(and (!= (-> obj current-continue) *default-continue*) (not arg1))
)
(set! (-> obj current-continue) s5-0)
@ -1373,7 +1373,7 @@
(let ((conts (-> (the-as level-load-info (-> (the-as symbol (car levels)) value)) continues)))
(while (not (null? conts))
(let ((cont (the-as continue-point (car conts))))
(if (not (logtest? (-> cont flags) (continue-flags cf2)))
(if (not (logtest? (-> cont flags) (continue-flags change-continue)))
(format #t "~S~%" (-> cont name))
)
)

View File

@ -722,8 +722,8 @@
(set! (-> *font-work* color-table color color (* vert 2) r) (-> rgba-red r))
(set! (-> *font-work* color-table color color (* vert 2) g) (-> rgba-green r))
(set! (-> *font-work* color-table color color (* vert 2) b) (-> rgba-blue r))
;; missing?
;; (set! (-> *font-work* color-table color (1+ (* vert 2)) r) (-> rgba-red r))
;; NOTE : this line was missing in the original code
(set! (-> *font-work* color-table color color (1+ (* vert 2)) r) (-> rgba-red r))
(set! (-> *font-work* color-table color color (1+ (* vert 2)) g) (-> rgba-green r))
(set! (-> *font-work* color-table color color (1+ (* vert 2)) b) (-> rgba-blue r))
0

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,7 @@ into 7 sections, which might explain the weird sizes in the center.
(defglobalconstant NUM_LEVEL_PAGES 146)
(defglobalconstant LEVEL_PAGE_SIZE_KB 126) ;; original value
(defglobalconstant LEVEL_PAGE_SIZE (* LEVEL_PAGE_SIZE_KB 1024)) ;; original value
(defglobalconstant LEVEL_HEAP_SIZE (* NUM_LEVEL_PAGES LEVEL_PAGE_SIZE))
;(defglobalconstant DEBUG_LEVEL_HEAP_MULT 1.5) ;; level heap in debug mode is 1.5x larger
(defglobalconstant DEBUG_LEVEL_HEAP_MULT 1.1) ;; we're gonna use debug mode-style heaps but we don't actually need them at 1.5x size right now
@ -955,7 +956,7 @@ into 7 sections, which might explain the weird sizes in the center.
(cond
;; are we using debug sized large level?
((= (&- (-> *level* heap top) (the-as uint (-> *level* heap base))) DEBUG_LEVEL_HEAP_SIZE)
;; if so, everything is 1.5x bigger!
;; if so, everything is bigger!
(let ((v1-44 (-> obj heap)))
(set! (-> v1-44 base) (&+ (-> *level* heap base) (* DEBUG_LEVEL_PAGE_SIZE offset-in-level-heap)))
(set! (-> v1-44 current) (-> v1-44 base))
@ -1891,7 +1892,7 @@ into 7 sections, which might explain the weird sizes in the center.
(load-package "common" global)
)
(let ((s5-1 (if (and arg0 (not *debug-segment*))
LEVEL_HEAP_SIZE
(#if PC_PORT DEBUG_LEVEL_HEAP_SIZE LEVEL_HEAP_SIZE)
DEBUG_LEVEL_HEAP_SIZE
)
)
@ -2747,9 +2748,9 @@ into 7 sections, which might explain the weird sizes in the center.
(set! (-> *setting-control* user-default sound-reverb) (-> s4-1 info sound-reverb))
#t
)
(or (-> *level* border?) (logtest? (-> *game-info* current-continue flags) (continue-flags cf2)))
(or (-> *level* border?) (logtest? (-> *game-info* current-continue flags) (continue-flags change-continue)))
(or (!= (-> s4-1 name) (-> *game-info* current-continue level))
(logtest? (-> *game-info* current-continue flags) (continue-flags cf2))
(logtest? (-> *game-info* current-continue flags) (continue-flags change-continue))
)
(not (null? (-> s4-1 info continues)))
(-> *setting-control* user-current allow-continue)
@ -2765,7 +2766,7 @@ into 7 sections, which might explain the weird sizes in the center.
)
(string= (-> *game-info* current-continue name) (-> (the-as continue-point s1-0) name))
)
(not (logtest? (-> (the-as continue-point s1-0) flags) (continue-flags cf2 cf3)))
(not (logtest? (-> (the-as continue-point s1-0) flags) (continue-flags change-continue no-auto)))
)
(set! s3-0 (the-as continue-point s1-0))
(if (string= (-> *game-info* current-continue name) (-> (the-as continue-point s1-0) name))
@ -2778,7 +2779,7 @@ into 7 sections, which might explain the weird sizes in the center.
)
(label cfg-59)
(if (and (the-as continue-point s3-0)
(not (logtest? (-> (the-as continue-point s3-0) flags) (continue-flags cf2 cf3)))
(not (logtest? (-> (the-as continue-point s3-0) flags) (continue-flags change-continue no-auto)))
)
(set-continue! *game-info* (the-as basic s3-0) #f)
)
@ -2974,9 +2975,10 @@ into 7 sections, which might explain the weird sizes in the center.
)
)
)
(let ((v1-186 (- #x2000000 (the-as int (-> global current)))))
(if (and (not *debug-segment*) (< v1-186 #x10000))
(format *stdcon* "~3Lglobal heap fatally low at ~DK free~%~0L" (sar v1-186 10))
;; pc port note : this was hardcoded to the top of EE memory (#x2000000)
(let ((v1-186 (&- (-> global top-base) (-> global current))))
(if (and (not *debug-segment*) (< v1-186 (* 64 1024)))
(format *stdcon* "~3Lglobal heap fatally low at ~DK free~%~0L" (/ v1-186 (* 1024 1024)))
)
)

View File

@ -1140,228 +1140,13 @@
(defmethod print gui-connection ((obj gui-connection))
(let* ((t9-0 format)
(a0-1 #t)
(a1-0 "#<gc ~12S ~2D ~6D ~6,,0f ~7S ~8S ")
(a2-0 (-> obj name))
(a3-0 (-> obj anim-part))
(t0-0 (-> obj id))
(t1-0 (-> obj priority))
(v1-0 (-> obj channel))
(t2-1 (cond
((= v1-0 (gui-channel hud-lower-left))
"hud-lower-left"
)
((= v1-0 (gui-channel citizen))
"citizen"
)
((= v1-0 (gui-channel ashelin))
"ashelin"
)
((= v1-0 (gui-channel hud-lower-left-1))
"hud-lower-left-1"
)
((= v1-0 (gui-channel message))
"message"
)
((= v1-0 (gui-channel hud-middle-left))
"hud-middle-left"
)
((= v1-0 (gui-channel hud-upper-center-2))
"hud-upper-center-2"
)
((= v1-0 (gui-channel hud-middle-right))
"hud-middle-right"
)
((= v1-0 (gui-channel subtitle))
"subtitle"
)
((= v1-0 (gui-channel notice))
"notice"
)
((= v1-0 (gui-channel voicebox))
"voicebox"
)
((= v1-0 (gui-channel art-load-next))
"art-load-next"
)
((= v1-0 (gui-channel mog))
"mog"
)
((= v1-0 (gui-channel sig))
"sig"
)
((= v1-0 (gui-channel hud-center-left))
"hud-center-left"
)
((= v1-0 (gui-channel hud-upper-right))
"hud-upper-right"
)
((= v1-0 (gui-channel hud-center-right))
"hud-center-right"
)
((= v1-0 (gui-channel alert))
"alert"
)
((= v1-0 (gui-channel jinx))
"jinx"
)
((= v1-0 (gui-channel hud-upper-left))
"hud-upper-left"
)
((= v1-0 (gui-channel query))
"query"
)
((= v1-0 (gui-channel grim))
"grim"
)
((= v1-0 (gui-channel kor))
"kor"
)
((= v1-0 (gui-channel hud-lower-right))
"hud-lower-right"
)
((= v1-0 (gui-channel screen))
"screen"
)
((= v1-0 (gui-channel guard))
"guard"
)
((= v1-0 (gui-channel supertitle))
"supertitle"
)
((= v1-0 (gui-channel hal))
"hal"
)
((= v1-0 (gui-channel hud-upper-center))
"hud-upper-center"
)
((= v1-0 (gui-channel blackout))
"blackout"
)
((= v1-0 (gui-channel bbush))
"bbush"
)
((= v1-0 (gui-channel hud))
"hud"
)
((= v1-0 (gui-channel voice))
"voice"
)
((= v1-0 (gui-channel max))
"max"
)
((= v1-0 (gui-channel none))
"none"
)
((= v1-0 (gui-channel notice-low))
"notice-low"
)
((= v1-0 (gui-channel art-load))
"art-load"
)
((= v1-0 (gui-channel kid))
"kid"
)
((= v1-0 (gui-channel jak))
"jak"
)
((= v1-0 (gui-channel daxter))
"daxter"
)
((= v1-0 (gui-channel krew))
"krew"
)
((= v1-0 (gui-channel hud-lower-left-2))
"hud-lower-left-2"
)
((= v1-0 (gui-channel background))
"background"
)
((= v1-0 (gui-channel crocadog))
"crocadog"
)
((= v1-0 (gui-channel movie))
"movie"
)
(else
"*unknown*"
)
)
)
(v1-1 (-> obj action))
)
(t9-0 a0-1 a1-0 a2-0 a3-0 t0-0 t1-0 t2-1 (cond
((= v1-1 (gui-action queue))
"queue"
)
((= v1-1 (gui-action stop))
"stop"
)
((= v1-1 (gui-action play))
"play"
)
((= v1-1 (gui-action hide))
"hide"
)
((= v1-1 (gui-action fade))
"fade"
)
((= v1-1 (gui-action none))
"none"
)
((= v1-1 (gui-action abort))
"abort"
)
((= v1-1 (gui-action stopping))
"stopping"
)
((= v1-1 (gui-action hidden))
"hidden"
)
((= v1-1 (gui-action playing))
"playing"
)
(else
"*unknown*"
)
)
)
)
(let ((s5-0 format)
(s4-0 #t)
(s3-0 " ~6S <@ #x~A>")
(v1-3 (get-status *gui-control* (-> obj id)))
)
(s5-0
s4-0
s3-0
(cond
((= v1-3 (gui-status ready))
"ready"
)
((= v1-3 (gui-status active))
"active"
)
((= v1-3 (gui-status stop))
"stop"
)
((= v1-3 (gui-status unknown))
"unknown"
)
((= v1-3 (gui-status hide))
"hide"
)
((= v1-3 (gui-status pending))
"pending"
)
(else
"*unknown*"
)
)
obj
)
)
(format #t "#<gc ~12S ~2D ~6D ~6,,0f ~7S ~8S " (-> obj name)
(-> obj anim-part)
(-> obj id)
(-> obj priority)
(enum->string gui-channel (-> obj channel))
(enum->string gui-action (-> obj action)))
(format #t " ~6S <@ #x~A>" (enum->string gui-status (get-status *gui-control* (-> obj id))) obj)
obj
)
@ -1404,6 +1189,7 @@
(the-as gui-connection #f)
)
;; WARN: Return type mismatch int vs sound-id.
(defmethod lookup-gui-connection-id gui-control ((obj gui-control) (arg0 string) (arg1 gui-channel) (arg2 gui-action))
(let ((s2-0 (the-as gui-connection (-> obj engine alive-list next0))))
(-> obj engine)
@ -1413,7 +1199,7 @@
(or (= arg2 (gui-action none)) (= arg2 (-> s2-0 action)))
(or (not arg0) (string= arg0 (-> s2-0 name)))
)
(return (the-as int (-> s2-0 id)))
(return (the-as sound-id (-> s2-0 id)))
)
(set! s2-0 (the-as gui-connection s1-0))
(-> obj engine)
@ -1428,11 +1214,11 @@
(or (= arg2 (gui-action none)) (= arg2 (-> s1-1 action)))
(or (not arg0) (string= arg0 (-> s1-1 name)))
)
(return (the-as int (-> s1-1 id)))
(return (the-as sound-id (-> s1-1 id)))
)
)
)
0
(the-as sound-id 0)
)
(defmethod set-falloff! gui-control ((obj gui-control) (arg0 sound-id) (arg1 symbol) (arg2 int) (arg3 int) (arg4 int))
@ -2516,6 +2302,9 @@
)
(set! (-> gp-0 cmd 69) '((65 . wait) (71 . wait) (67 . wait) (66 . wait)))
(set! (-> gp-0 cmd 70) '((65 . wait) (90 . hide) (91 . hide) (81 . hide) (80 . hide)))
;; added this one
(set! (-> gp-0 cmd (gui-channel subtitle-pc)) '(((the binteger (gui-channel blackout)) . wait)
))
(set! (-> gp-0 cmd 80) '((64 . wait) (65 . wait) (80 . wait) (70 . wait)))
(set! (-> gp-0 cmd 81) '((64 . wait) (65 . wait) (81 . wait) (70 . wait)))
(set! (-> gp-0 cmd 82) '((64 . wait) (65 . wait) (67 . wait) (71 . wait) (69 . wait) (82 . wait)))

View File

@ -551,7 +551,7 @@
)
(set! (-> s5-0 y) 0.0)
(vector-normalize! s5-0 1.0)
;; modified for PC, see comment near definition in collide-shape-h.gc
(normalized-heading-to-quaternion! (-> obj root-override2 quat) s5-0)
)
@ -956,7 +956,7 @@
)
(defmethod nav-enemy-method-161 nav-enemy ((obj nav-enemy))
(set! (-> obj enemy-flags) (the-as enemy-flag (logclear (-> obj enemy-flags) (enemy-flag enemy-flag39))))
(set! (-> obj enemy-flags) (the-as enemy-flag (logclear (-> obj enemy-flags) (enemy-flag not-frustrated))))
(set! (-> obj frustration-time) (current-time))
(let ((v1-7 (handle->process (-> obj focus handle))))
(if v1-7
@ -982,7 +982,7 @@
(when (>= (- (current-time) (-> obj frustration-time))
(+ (-> obj reaction-time) (-> obj enemy-info-override frustration-time))
)
(set! (-> obj enemy-flags) (the-as enemy-flag (logior (enemy-flag enemy-flag39) (-> obj enemy-flags))))
(set! (-> obj enemy-flags) (the-as enemy-flag (logior (enemy-flag not-frustrated) (-> obj enemy-flags))))
0
)
)
@ -1522,7 +1522,7 @@ This commonly includes things such as:
(defmethod go-stare nav-enemy ((obj nav-enemy))
(let ((s5-0 (-> obj focus aware)))
(cond
((or (and (-> obj enemy-info-override use-frustration) (logtest? (enemy-flag enemy-flag39) (-> obj enemy-flags)))
((or (and (-> obj enemy-info-override use-frustration) (logtest? (enemy-flag not-frustrated) (-> obj enemy-flags)))
(nav-enemy-method-163 obj)
)
(go-stare2 obj)
@ -1542,7 +1542,7 @@ This commonly includes things such as:
)
(defmethod go-hostile nav-enemy ((obj nav-enemy))
(if (or (and (-> obj enemy-info-override use-frustration) (logtest? (enemy-flag enemy-flag39) (-> obj enemy-flags)))
(if (or (and (-> obj enemy-info-override use-frustration) (logtest? (enemy-flag not-frustrated) (-> obj enemy-flags)))
(nav-enemy-method-163 obj)
)
(go-stare2 obj)
@ -1905,7 +1905,9 @@ This commonly includes things such as:
)
)
)
(when (and (-> self enemy-info-override use-frustration) (logtest? (enemy-flag enemy-flag39) (-> self enemy-flags)))
(when (and (-> self enemy-info-override use-frustration)
(logtest? (enemy-flag not-frustrated) (-> self enemy-flags))
)
(if (-> self enemy-info-override use-stop-chase)
(go-virtual stop-chase)
(go-stare self)
@ -2094,7 +2096,9 @@ This commonly includes things such as:
)
((= gp-0 (enemy-aware enemy-aware-3))
(if (and (get-enemy-target self)
(not (and (-> self enemy-info-override use-frustration) (logtest? (enemy-flag enemy-flag39) (-> self enemy-flags)))
(not (and (-> self enemy-info-override use-frustration)
(logtest? (enemy-flag not-frustrated) (-> self enemy-flags))
)
)
(>= (- (current-time) (-> self starting-time)) (-> self reaction-time))
)

View File

@ -76,8 +76,8 @@
(-> (the-as collide-shape-moving gp-0) gspot-pos)
)
((and (or (= arg0 2) (= arg0 3)) (type? gp-0 collide-shape))
;; PC PORT NOTE : added lod-set check so we don't use invalid bones
(if (not (logtest? (-> obj draw status) (draw-control-status lod-set)))
;; PC PORT NOTE : added check here so we don't use invalid bones
(if (logtest? (-> obj draw status) (draw-control-status uninited))
(-> gp-0 trans)
(-> gp-0 root-prim prim-core))
)

View File

@ -217,7 +217,7 @@ Seen take in - `true-func` which takes no args TODO - seems fishy
)
)
;; PC PORT NOTE : added check so we don't use the wrong position for the distance check
(and (logtest? (-> self draw status) (draw-control-status lod-set))
(and (not (logtest? (-> self draw status) (draw-control-status uninited)))
(< (vector-vector-distance (target-pos 0) s5-0) f30-0))
)
)

View File

@ -673,6 +673,10 @@
)
(let ((v1-9 (-> self skel root-channel 0 frame-group)))
(when v1-9
;; send a movie-no-subtitle message so the pc subtitle system at least knows there's a movie playing
(#when PC_PORT
(if (= (-> self type) scene-player)
(send-event (ppointer->process *subtitle2*) 'movie-no-subtitle (-> (the scene-player self) anim name) #f (ja-aframe-num 0))))
(let ((gp-0 (res-lump-struct (-> v1-9 extra) 'subtitle-range (array subtitle-range))))
(when gp-0
(let ((f30-0 (ja-aframe-num 0))
@ -716,7 +720,12 @@
(set! (-> s2-0 origin y) (+ 1.0 (-> s2-0 origin y)))
(set! (-> s2-0 color) (font-color default))
(set! (-> s2-0 flags) (font-flags shadow kerning middle left large))
(print-game-text (the-as string s3-0) s2-0 #f 44 (bucket-id subtitle))
(#if PC_PORT
(if (or (!= (-> self type) scene-player)
(not (send-event (ppointer->process *subtitle2*) 'movie (-> (the scene-player self) anim name) s3-0 f30-0)))
(print-game-text (the-as string s3-0) s2-0 #f 44 (bucket-id subtitle)))
(print-game-text (the-as string s3-0) s2-0 #f 44 (bucket-id subtitle)))
(gui-control-method-12
*gui-control*
self
@ -952,7 +961,7 @@
(gui-action none)
)
)
(v1-167 (get-status *gui-control* (the-as sound-id a1-26)))
(v1-167 (get-status *gui-control* a1-26))
)
(not (or (= v1-167 (gui-status ready)) (= v1-167 (gui-status active))))
)
@ -1178,7 +1187,7 @@
(let ((v1-41 (scene-player-method-24 self (-> self scene-list (-> self scene-index)) #t)))
(when v1-41
(let ((a0-21 (scene-decode-continue (the-as basic (-> v1-41 load-point-obj)))))
(set! a0-24 (when (and a0-21 (logtest? (-> a0-21 flags) (continue-flags cf1)))
(set! a0-24 (when (and a0-21 (logtest? (-> a0-21 flags) (continue-flags scene-wait)))
(go-virtual wait a0-24)
a0-24
)
@ -1487,7 +1496,7 @@
(defbehavior scene-player-init scene-player ((arg0 object) (arg1 symbol) (arg2 string))
"`object` arg can be an `(array scene)`, `pair of scene` or a `scene`"
(process-entity-set! self (the-as entity #f))
(stack-size-set! (-> self main-thread) 1024) ;; changed from 512
(stack-size-set! (-> self main-thread) 768) ;; changed from 512
(set! (-> self root) (new 'process 'trsqv))
(case (rtype-of arg0)
((array)

View File

@ -24,7 +24,7 @@
(a3-0 (car a2-2))
)
(while (not (null? a2-2))
(if (and v1-0 (logtest? (continue-flags cf17) (-> (the-as continue-point a3-0) flags)))
(if (and v1-0 (logtest? (continue-flags test) (-> (the-as continue-point a3-0) flags)))
(return (the-as continue-point a3-0))
)
(if (= a3-0 arg0)
@ -122,12 +122,12 @@
(if (-> *art-control* reserve-buffer)
(reserve-free *art-control* (-> *art-control* reserve-buffer heap))
)
(when (logtest? (-> arg0 flags) (continue-flags cf8 cf11))
(when (logtest? (-> arg0 flags) (continue-flags demo demo-movie))
(set! (-> ctywide memory-mode) (load-buffer-mode small-edge))
0
)
(kill-persister *setting-control* (the-as engine-pers 'fail) 'bg-a)
(when (not (logtest? (-> arg0 flags) (continue-flags cf4)))
(when (not (logtest? (-> arg0 flags) (continue-flags no-blackout)))
(add-setting! 'bg-a 'abs 1.0 0)
(set! (-> *setting-control* user-current bg-a) 1.0)
)
@ -163,7 +163,7 @@
(if (not (string= (-> arg0 name) "default"))
(set! *external-cam-mode* #f)
)
(if (not (logtest? (-> arg0 flags) (continue-flags cf4)))
(if (not (logtest? (-> arg0 flags) (continue-flags no-blackout)))
(cam-stop)
)
(suspend)
@ -229,7 +229,7 @@
(set! (-> *level* border?) s5-2)
)
(set! (-> *ACTOR-bank* birth-max) 1000)
(when (not (logtest? (-> arg0 flags) (continue-flags cf4)))
(when (not (logtest? (-> arg0 flags) (continue-flags no-blackout)))
(new 'stack 'transformq)
(cam-start #t)
(suspend)
@ -270,7 +270,7 @@
(set! *spawn-actors* #t)
(set! *teleport* #t)
(set! (-> *ACTOR-bank* birth-max) 1000)
(if (not (logtest? (-> arg0 flags) (continue-flags cf4)))
(if (not (logtest? (-> arg0 flags) (continue-flags no-blackout)))
(set-blackout-frames (seconds 0.1))
)
(let* ((a0-94 *game-info*)
@ -290,7 +290,7 @@
(t9-53 a0-94 a1-51 #f)
)
(cond
((logtest? (-> arg0 flags) (continue-flags cf5))
((logtest? (-> arg0 flags) (continue-flags game-start))
(case *kernel-boot-message*
(('kiosk)
(let ((s5-5 (ppointer->handle (auto-save-command 'restore 0 0 *default-pool* #f))))
@ -306,27 +306,27 @@
)
)
)
((logtest? (-> arg0 flags) (continue-flags cf12))
((logtest? (-> arg0 flags) (continue-flags title))
(go target-title #t)
)
((logtest? (-> arg0 flags) (continue-flags cf13))
((logtest? (-> arg0 flags) (continue-flags title-movie))
(go target-title #f)
)
((logtest? (-> arg0 flags) (continue-flags cf8))
((logtest? (-> arg0 flags) (continue-flags demo))
(go target-demo #t)
)
((logtest? (-> arg0 flags) (continue-flags cf11))
((logtest? (-> arg0 flags) (continue-flags demo-movie))
(go target-demo #f)
)
((logtest? (-> arg0 flags) (continue-flags cf9))
((logtest? (-> arg0 flags) (continue-flags intro))
(intro-play)
)
((logtest? (-> arg0 flags) (continue-flags cf10))
((logtest? (-> arg0 flags) (continue-flags hero-mode))
(logior! (-> self game secrets) (game-secrets hero-mode))
(logior! (-> self game purchase-secrets) (game-secrets hero-mode))
(intro-play)
)
((logtest? (-> arg0 flags) (continue-flags cf7))
((logtest? (-> arg0 flags) (continue-flags warp-gate))
(let ((s5-7 (current-time)))
(until (>= (- (current-time) s5-7) (seconds 0.05))
(suspend)
@ -342,22 +342,22 @@
)
)
)
((logtest? (continue-flags cf22) (-> arg0 flags))
((logtest? (continue-flags indax) (-> arg0 flags))
enter-state
(go target-indax-start)
)
((logtest? (continue-flags cf18) (-> arg0 flags))
((logtest? (continue-flags record-path) (-> arg0 flags))
)
((logtest? (continue-flags cf21) (-> arg0 flags))
((logtest? (continue-flags record-sig) (-> arg0 flags))
(start-sig-recorder)
)
((logtest? (continue-flags cf19 cf20) (-> arg0 flags))
((logtest? (continue-flags pilot pilot-dax) (-> arg0 flags))
(set! (-> self focus-status) (logior (focus-status pilot) (-> self focus-status)))
(while (not (-> self mode-cache))
(suspend)
)
)
((logtest? (-> arg0 flags) (continue-flags cf6))
((logtest? (-> arg0 flags) (continue-flags demo-end))
(go target-grab 'stance)
)
(else

View File

@ -53,6 +53,7 @@
(subtitle 69)
(supertitle 70)
(notice-low 71)
(subtitle-pc 78) ;; custom
(screen 79)
(hud-upper-right 80)
(hud-upper-left 81)
@ -138,7 +139,7 @@
(stop-str (_type_ gui-connection) int 11)
(gui-control-method-12 (_type_ process gui-channel gui-action string int float sound-id) sound-id 12)
(update (_type_ symbol) int 13)
(lookup-gui-connection-id (_type_ string gui-channel gui-action) int 14)
(lookup-gui-connection-id (_type_ string gui-channel gui-action) sound-id 14)
(lookup-gui-connection (_type_ process gui-channel string sound-id) gui-connection 15)
(set-action! (_type_ gui-action sound-id gui-channel gui-action string (function gui-connection symbol) process) int 16)
(get-status (_type_ sound-id) gui-status 17)

View File

@ -529,25 +529,11 @@ in a single text group and file."
(set! sv-64 (+ sv-64 1))
)
(when (not arg2)
(let* ((s2-1 (-> *display* frames (-> *display* on-screen) global-buf))
(s3-1 (-> s2-1 base))
)
(with-dma-buffer-add-bucket ((s2-1 (-> *display* frames (-> *display* on-screen) global-buf))
arg4
)
(draw-string *game-text-line* s2-1 arg1)
(set! (-> arg1 color) (-> *font-work* last-color))
(let ((a3-2 (-> s2-1 base)))
(let ((v1-121 (the-as object (-> s2-1 base))))
(set! (-> (the-as dma-packet v1-121) dma) (new 'static 'dma-tag :id (dma-tag-id next)))
(set! (-> (the-as dma-packet v1-121) vif0) (new 'static 'vif-tag))
(set! (-> (the-as dma-packet v1-121) vif1) (new 'static 'vif-tag))
(set! (-> s2-1 base) (&+ (the-as pointer v1-121) 16))
)
(dma-bucket-insert-tag
(-> *display* frames (-> *display* on-screen) bucket-group)
arg4
s3-1
(the-as (pointer dma-tag) a3-2)
)
)
)
)
(set! (-> arg1 origin y) f30-2)
@ -575,6 +561,9 @@ in a single text group and file."
(set! (-> arg1 origin y) sv-20)
(set! (-> arg1 flags) sv-24)
(set! (-> arg1 color) sv-28)
(#when PC_PORT
(if (and *debug-segment* *display-text-box*)
(draw-debug-text-box arg1)))
(if (> sv-64 0)
(* sv-56 (the float sv-64))
0.0

View File

@ -90,14 +90,13 @@
)
(hash-table-set! *file-entry-map* "dir-tpages.go" #f)
(cgo-file "engine.gd" '("$OUT/obj/gcommon.o" "$OUT/obj/gstate.o" "$OUT/obj/gstring.o" "$OUT/obj/gkernel.o"))
(cgo-file "game.gd" '("$OUT/obj/gcommon.o" "$OUT/obj/gstate.o" "$OUT/obj/gstring.o" "$OUT/obj/gkernel.o"))
;; note: some of these dependencies are slightly wrong because cgo-file doesn't really handle
;; the case of a .o appearing in multiple dgos. But, if we depend on the last item in both lists, it
;; works out.
(define common-dep '("$OUT/obj/cty-guard-turret-button.o" "$OUT/obj/default-menu.o"))
(define common-dep '("$OUT/obj/cty-guard-turret-button.o" "$OUT/obj/default-menu-pc.o"))
(cgo-file "cwi.gd" common-dep)
(cgo-file "lmeetbrt.gd" common-dep)
(cgo-file "cta.gd" common-dep)
@ -339,6 +338,11 @@
"$OUT/iso/7COMMON.TXT")
)
(defstep :in "game/assets/jak2/game_subtitle.gp"
:tool 'subtitle2
:out '("$OUT/iso/0SUBTI2.TXT")
)
;;;;;;;;;;;;;;;;;;;;;
;; ISO Group
;;;;;;;;;;;;;;;;;;;;;
@ -346,6 +350,14 @@
(group-list "iso"
`("$OUT/iso/0COMMON.TXT"
"$OUT/iso/1COMMON.TXT"
"$OUT/iso/2COMMON.TXT"
"$OUT/iso/3COMMON.TXT"
"$OUT/iso/4COMMON.TXT"
"$OUT/iso/5COMMON.TXT"
"$OUT/iso/6COMMON.TXT"
"$OUT/iso/7COMMON.TXT"
"$OUT/iso/0SUBTI2.TXT"
"$OUT/iso/TWEAKVAL.MUS"
"$OUT/iso/VAGDIR.AYB"
,@(reverse *all-vis*)
@ -357,7 +369,16 @@
)
(group-list "text"
`("$OUT/iso/0COMMON.TXT")
`("$OUT/iso/0COMMON.TXT"
"$OUT/iso/1COMMON.TXT"
"$OUT/iso/2COMMON.TXT"
"$OUT/iso/3COMMON.TXT"
"$OUT/iso/4COMMON.TXT"
"$OUT/iso/5COMMON.TXT"
"$OUT/iso/6COMMON.TXT"
"$OUT/iso/7COMMON.TXT"
"$OUT/iso/0SUBTI2.TXT"
)
)
;; used for the type consistency test.
@ -366,6 +387,8 @@
)
(group "engine"
"$OUT/iso/0COMMON.TXT"
"$OUT/iso/0SUBTI2.TXT"
"$OUT/iso/KERNEL.CGO"
"$OUT/iso/GAME.CGO"
)

View File

@ -675,12 +675,12 @@
(defmacro process-mask-set! (mask &rest enum-value)
"Set the given bits in the process mask"
`(set! ,mask (logior ,mask (process-mask ,@enum-value)))
`(logior! ,mask (process-mask ,@enum-value))
)
(defmacro process-mask-clear! (mask &rest enum-value)
"Clear the given bits in the process mask."
`(set! ,mask (logand ,mask (lognot (process-mask ,@enum-value))))
`(logclear! ,mask (process-mask ,@enum-value))
)
(defmacro suspend ()
@ -730,6 +730,23 @@
`(method-set! ,method-type ,method-id (__pc-get-mips2c ,name))
)
(defmacro kheap-alloc (heap size)
"allocate space for a kheap"
`(let ((heap ,heap) (size ,size))
(set! (-> heap base) (malloc 'global size))
(set! (-> heap current) (-> heap base))
(set! (-> heap top-base) (&+ (-> heap base) size))
(set! (-> heap top) (-> heap top-base))
)
)
(defmacro kheap-reset (heap)
"reset the kheap, so you can use its memory again"
`(let ((heap ,heap))
(set! (-> heap current) (-> heap base))
)
)
(defmacro scratchpad-object (type &key (offset 0))
"Access an object on the scratchpad."
`(the-as ,type (&+ *fake-scratchpad-data* ,offset))

View File

@ -2479,6 +2479,8 @@
;; categories within the active pool.
(change-parent (define *display-pool* (new 'global 'process-tree "display-pool")) *active-pool*)
(#when PC_PORT
(change-parent (define *pc-pool* (new 'global 'process-tree "pc-pool")) *active-pool*))
(change-parent (define *camera-pool* (new 'global 'process-tree "camera-pool")) *active-pool*)
(set! (-> *camera-pool* mask) (process-mask freeze pause menu progress process-tree camera))

View File

@ -1245,7 +1245,7 @@
:end-point-obj (new 'static 'continue-point
:name "atoll-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x 2277804.5 :y 217672.9 :z -4571725.5 :w 1.0)
:quat (new 'static 'vector :y -0.9671 :w 0.2541)
:camera-trans (new 'static 'vector :x 2310242.8 :y 251249.05 :z -4513428.0 :w 1.0)
@ -1651,7 +1651,7 @@
:end-point-obj (new 'static 'continue-point
:name "atoll-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x 2277804.5 :y 217672.9 :z -4571725.5 :w 1.0)
:quat (new 'static 'vector :y -0.9671 :w 0.2541)
:camera-trans (new 'static 'vector :x 2310242.8 :y 251249.05 :z -4513428.0 :w 1.0)

View File

@ -624,7 +624,7 @@
:end-point-obj (new 'static 'continue-point
:name "ctyslumb-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x 2620227.2 :y 31333.99 :z -400654.34 :w 1.0)
:quat (new 'static 'vector :y 0.9997 :w -0.0234)
:camera-trans (new 'static 'vector :x 2606159.8 :y 52533.656 :z -360011.78 :w 1.0)
@ -797,7 +797,7 @@
:end-point-obj (new 'static 'continue-point
:name "ctyslumb-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x 2588147.8 :y 32754.482 :z -150693.89 :w 1.0)
:quat (new 'static 'vector :x -0.0012 :y -0.9819 :z -0.0008 :w 0.1889)
:camera-trans (new 'static 'vector :x 2593185.8 :y 51852.492 :z -107130.88 :w 1.0)

View File

@ -2010,7 +2010,7 @@ This commonly includes things such as:
:end-point-obj (new 'static 'continue-point
:name "ctymarkb-tanker"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x 1928400.1 :y 34444.902 :z 1788676.5 :w 1.0)
:quat (new 'static 'vector :y 0.3641 :w -0.9313)
:camera-trans (new 'static 'vector :x 1960564.4 :y 55543.81 :z 1760002.0 :w 1.0)

View File

@ -1292,10 +1292,7 @@ If the player is too far, play a warning speech."
(let ((speech (-> obj course speeches idx)))
(= (get-status
*gui-control*
(the-as
sound-id
(lookup-gui-connection-id *gui-control* (-> speech name) (gui-channel none) (gui-action none))
)
(lookup-gui-connection-id *gui-control* (-> speech name) (gui-channel none) (gui-action none))
)
(gui-status unknown)
)
@ -1316,10 +1313,7 @@ If the player is too far, play a warning speech."
)
(nonzero? (get-status
*gui-control*
(the-as
sound-id
(lookup-gui-connection-id *gui-control* (the-as string #f) (the-as gui-channel channel) (gui-action none))
)
(lookup-gui-connection-id *gui-control* (the-as string #f) (the-as gui-channel channel) (gui-action none))
)
)
)

View File

@ -827,7 +827,7 @@
(defmethod go-stare amphibian ((obj amphibian))
(let ((s5-0 (-> obj focus aware)))
(cond
((or (and (-> obj enemy-info-override use-frustration) (logtest? (enemy-flag enemy-flag39) (-> obj enemy-flags)))
((or (and (-> obj enemy-info-override use-frustration) (logtest? (enemy-flag not-frustrated) (-> obj enemy-flags)))
(nav-enemy-method-163 obj)
)
(go-stare2 obj)

View File

@ -296,10 +296,7 @@
)
(if (nonzero? (get-status
*gui-control*
(the-as
sound-id
(lookup-gui-connection-id *gui-control* (the-as string #f) (gui-channel guard) (gui-action none))
)
(lookup-gui-connection-id *gui-control* (the-as string #f) (gui-channel guard) (gui-action none))
)
)
(set! (-> self last-playing-time) (current-time))
@ -348,10 +345,7 @@
)
(until (= (get-status
*gui-control*
(the-as
sound-id
(lookup-gui-connection-id *gui-control* (the-as string #f) (gui-channel guard) (gui-action none))
)
(lookup-gui-connection-id *gui-control* (the-as string #f) (gui-channel guard) (gui-action none))
)
(gui-status unknown)
)

View File

@ -474,7 +474,7 @@
(kick-attack () _type_ :state 158)
(attack () _type_ :state 159)
(die-now () _type_ :state 160)
(shoot (_type_ vector projectile-init-by-other-params int float float) none 161)
(shoot (_type_ vector projectile-init-by-other-params int int float) none 161)
(crimson-guard-hover-method-162 (_type_ process-focusable) symbol 162)
)
)
@ -671,8 +671,8 @@
(set! (-> s5-0 attack-id) a0-6)
)
(set! (-> s5-0 timeout) (seconds 4))
(shoot self gp-0 s5-0 9 0.000000000000000000000000000000000000000000017 1.0)
(shoot self gp-0 s5-0 11 0.000000000000000000000000000000000000000000017 -1.0)
(shoot self gp-0 s5-0 9 12 1.0)
(shoot self gp-0 s5-0 11 12 -1.0)
)
(sound-play "hover-fire")
(let ((v0-0 (the-as object (+ (-> self shots-fired) 1))))
@ -866,8 +866,8 @@
(set! (-> s5-0 attack-id) a0-6)
)
(set! (-> s5-0 timeout) (seconds 4))
(shoot self gp-0 s5-0 9 0.000000000000000000000000000000000000000000017 1.0)
(shoot self gp-0 s5-0 11 0.000000000000000000000000000000000000000000017 -1.0)
(shoot self gp-0 s5-0 9 12 1.0)
(shoot self gp-0 s5-0 11 12 -1.0)
)
(sound-play "hover-fire")
(let ((v0-0 (the-as object (+ (-> self shots-fired) 1))))
@ -1604,7 +1604,7 @@
(arg0 vector)
(arg1 projectile-init-by-other-params)
(arg2 int)
(arg3 float)
(arg3 int)
(arg4 float)
)
(vector<-cspace! (-> arg1 pos) (-> obj node-list data arg2))

View File

@ -1010,7 +1010,7 @@
(let* ((gp-0 (-> obj race-state))
(s3-0 (-> gp-0 info))
)
(if (or (logtest? (-> s3-0 flags) 2) (logtest? (continue-flags cf19) (-> *game-info* last-continue flags)))
(if (or (logtest? (-> s3-0 flags) 2) (logtest? (continue-flags pilot) (-> *game-info* last-continue flags)))
(logior! (-> gp-0 flags) 2)
)
(let ((s4-0 (new 'stack-no-clear 'mystery-traffic-object-spawn-params)))
@ -1249,17 +1249,9 @@
(defmethod race-manager-method-24 race-manager ((obj race-manager))
(when (= (get-status *gui-control* (-> obj message-id)) (gui-status active))
(let ((gp-1 (new
'stack
'font-context
*font-default-matrix*
70
20
0.0
(font-color orange)
(font-flags shadow kerning)
)
)
(let ((gp-1
(new 'stack 'font-context *font-default-matrix* 70 20 0.0 (font-color orange) (font-flags shadow kerning))
)
)
(let ((v1-4 gp-1))
(set! (-> v1-4 scale) 0.7)
@ -1285,17 +1277,9 @@
(defmethod race-manager-method-25 race-manager ((obj race-manager))
(when (= (get-status *gui-control* (-> obj message-id)) (gui-status active))
(let ((gp-1 (new
'stack
'font-context
*font-default-matrix*
70
20
0.0
(font-color orange)
(font-flags shadow kerning)
)
)
(let ((gp-1
(new 'stack 'font-context *font-default-matrix* 70 20 0.0 (font-color orange) (font-flags shadow kerning))
)
)
(let ((v1-4 gp-1))
(set! (-> v1-4 scale) 0.7)

View File

@ -690,7 +690,7 @@
(ja :num! (loop!))
(ja-post)
)
(if (not (logtest? (-> arg0 flags) (continue-flags cf4)))
(if (not (logtest? (-> arg0 flags) (continue-flags no-blackout)))
(set-blackout-frames (seconds 0.05))
)
(start 'play arg0)

View File

@ -920,7 +920,7 @@
(defmethod go-hostile ginsu ((obj ginsu))
(cond
((or (and (-> obj enemy-info-override use-frustration) (logtest? (enemy-flag enemy-flag39) (-> obj enemy-flags)))
((or (and (-> obj enemy-info-override use-frustration) (logtest? (enemy-flag not-frustrated) (-> obj enemy-flags)))
(nav-enemy-method-163 obj)
)
(go-stare2 obj)

View File

@ -559,7 +559,9 @@
)
)
)
(when (and (-> self enemy-info-override use-frustration) (logtest? (enemy-flag enemy-flag39) (-> self enemy-flags)))
(when (and (-> self enemy-info-override use-frustration)
(logtest? (enemy-flag not-frustrated) (-> self enemy-flags))
)
(if (-> self enemy-info-override use-stop-chase)
(go-virtual stop-chase)
(go-stare self)

View File

@ -588,7 +588,9 @@ This commonly includes things such as:
)
)
(when (>= (- (current-time) (-> self state-time)) (seconds 4))
(if (and *target* (focus-test? *target* grabbed))
(if (and *target* (focus-test? *target* grabbed)
;; pc port note : added this check to make the beetle button not break everything!!
(< (vector-vector-xz-distance (-> *target* control trans) (-> self root-override trans)) (meters 10)))
(process-release? *target*)
)
(if (and *target*
@ -1839,7 +1841,7 @@ This commonly includes things such as:
(deftype tomb-vibe (process-drawable)
((spawn-pos vector :inline :offset-assert 208)
(pat-tbl (array handle) :offset-assert 224)
(pat-tbl (pointer int32) :offset-assert 224)
(pat-count int32 :offset-assert 228)
(pat-index int32 :offset-assert 232)
(pat-entry-index int32 :offset-assert 236)
@ -1932,16 +1934,14 @@ This commonly includes things such as:
(case (-> (the-as attack-info (-> event param 1)) mode)
(('flop 'spin 'punch)
(cond
((and (= (-> v1-0 0) (-> (the-as (pointer int32) (+ (the-as uint (-> self pat-tbl)) (* (-> self pat-index) 4)))))
(!= (-> gp-0 0) (process->handle self))
)
((and (= (-> v1-0 0) (-> self pat-tbl (-> self pat-index))) (!= (-> gp-0 0) (process->handle self)))
(send-event (handle->process (-> gp-0 0)) 'interrupt)
(send-event (handle->process (-> gp-0 0)) 'die)
(logior! (-> self flags) 1)
(go-virtual vibrate)
)
(else
(set! (-> v1-0 0) (-> (the-as (pointer int32) (+ (the-as uint (-> self pat-tbl)) (* (-> self pat-index) 4)))))
(set! (-> v1-0 0) (-> self pat-tbl (-> self pat-index)))
(set! (-> gp-0 0) (process->handle self))
(go-virtual vibrate)
)
@ -2071,7 +2071,7 @@ This commonly includes things such as:
'sound-tune
#f
0.0
(+ (-> (the-as (pointer int32) (+ (the-as uint (-> self pat-tbl)) (* (-> self pat-index) 4)))) 18)
(+ (-> self pat-tbl (-> self pat-index)) 18)
)
(let ((gp-1 (current-time)))
(until (>= (- (current-time) gp-1) (seconds 0.2))
@ -2227,11 +2227,11 @@ This commonly includes things such as:
(let ((v1-26 (res-lump-data (-> obj entity) 'vibe-pattern pointer :tag-ptr (& sv-16))))
(cond
((and v1-26 (nonzero? (-> sv-16 elt-count)))
(set! (-> obj pat-tbl) (the-as (array handle) v1-26))
(set! (-> obj pat-tbl) (the-as (pointer int32) v1-26))
(set! (-> obj pat-count) (the-as int (-> sv-16 elt-count)))
)
(else
(set! (-> obj pat-tbl) #f)
(set! (-> obj pat-tbl) (the-as (pointer int32) #f))
(set! (-> obj pat-count) 0)
0
)

View File

@ -512,7 +512,7 @@
:load-point-obj (new 'static 'continue-point
:name "palroof-boss"
:level #f
:flags (continue-flags cf3 cf17)
:flags (continue-flags no-auto test)
:trans (new 'static 'vector :x 870794.06 :y 1671154.9 :z 2027482.4 :w 1.0)
:quat (new 'static 'vector :y 0.4793 :w 0.8776)
:camera-trans (new 'static 'vector :x 831816.06 :y 1693132.0 :z 2045632.9 :w 1.0)

View File

@ -1428,7 +1428,7 @@ This commonly includes things such as:
(deftype sew-scare-grunt (grunt)
((anim spool-anim :offset-assert 692)
(manipy (pointer manipy) :offset-assert 696)
(spooled-sound-id uint32 :offset-assert 700)
(spooled-sound-id sound-id :offset-assert 700)
(grill-actor entity-actor :offset-assert 704)
)
:heap-base #x250
@ -1649,10 +1649,7 @@ This commonly includes things such as:
)
)
(set! (-> self spooled-sound-id)
(the-as
uint
(lookup-gui-connection-id *gui-control* (-> self anim name) (gui-channel art-load) (gui-action none))
)
(lookup-gui-connection-id *gui-control* (-> self anim name) (gui-channel art-load) (gui-action none))
)
(none)
)
@ -1711,7 +1708,7 @@ This commonly includes things such as:
(when *sound-player-enable*
(let ((set-sound-param (the-as sound-rpc-set-param (get-sound-buffer-entry))))
(set! (-> set-sound-param command) (sound-command set-param))
(set! (-> set-sound-param id) (the-as sound-id (-> self spooled-sound-id)))
(set! (-> set-sound-param id) (-> self spooled-sound-id))
(set! (-> set-sound-param params volume) (the int (* 1024.0 scale)))
(let ((position (-> self root-override2 trans)))
(let ((_self self))

View File

@ -906,7 +906,7 @@
:end-point-obj (new 'static 'continue-point
:name "sewesc-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x 5541148.0 :y -363968.9 :z 2435194.5 :w 1.0)
:quat (new 'static 'vector :y 0.0482 :w 0.9988)
:camera-trans (new 'static 'vector :x 5518817.0 :y -344878.7 :z 2399633.5 :w 1.0)
@ -1175,7 +1175,7 @@
:end-point-obj (new 'static 'continue-point
:name "sewesc-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x 5608474.5 :y -363960.3 :z 1988017.4 :w 1.0)
:quat (new 'static 'vector :y -0.6775 :w -0.7354)
:camera-trans (new 'static 'vector :x 5562796.5 :y -344327.78 :z 1985962.4 :w 1.0)

View File

@ -765,7 +765,7 @@
:end-point-obj (new 'static 'continue-point
:name "under-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x -281437.8 :y -266239.6 :z 7897175.0 :w 1.0)
:quat (new 'static 'vector :y -0.0077 :w 0.9999)
:camera-trans (new 'static 'vector :x -251373.16 :y -249839.2 :z 7882176.0 :w 1.0)
@ -1501,7 +1501,7 @@
:end-point-obj (new 'static 'continue-point
:name "under-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x -277934.5 :y -273739.38 :z 8274381.0 :w 1.0)
:quat (new 'static 'vector :y 0.1135 :w -0.9935)
:camera-trans (new 'static 'vector :x -251817.98 :y -252642.1 :z 8230336.0 :w 1.0)
@ -2324,7 +2324,7 @@
:end-point-obj (new 'static 'continue-point
:name "under-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x -912500.75 :y -274436.5 :z 8332461.5 :w 1.0)
:quat (new 'static 'vector :y -0.9997 :w 0.0214)
:camera-trans (new 'static 'vector :x -910896.3 :y -257269.77 :z 8368969.0 :w 1.0)
@ -2949,7 +2949,7 @@
:end-point-obj (new 'static 'continue-point
:name "under-start"
:level #f
:flags (continue-flags cf2)
:flags (continue-flags change-continue)
:trans (new 'static 'vector :x -934223.06 :y -290824.2 :z 7948726.0 :w 1.0)
:quat (new 'static 'vector :y 0.4849 :w 0.8745)
:camera-trans (new 'static 'vector :x -978344.75 :y -270091.06 :z 7925256.5 :w 1.0)

View File

@ -5,6 +5,16 @@
;; name in dgo: underb-master
;; dgos: UNB
(defenum under-locking-mode
:type int64
(want-mech)
(want-fill)
(airlock-wait)
(want-drain)
(drain)
(want-exit-mech)
)
;; DECOMP BEGINS
(deftype under-warp (process-drawable)
@ -84,7 +94,6 @@
(s5-0 (new 'stack-no-clear 'vector))
(gp-0 (new 'stack-no-clear 'vector))
(f30-0 4096.0)
(ratio (- 1.0 (/ 0.5 (-> *pc-settings* aspect-ratio-scale))))
)
;; changed for PC port: resize warp effect based on aspect ratio
(let ((f0-4 (* 0.00013563369 (tan (* 0.5 (-> *math-camera* fov))) f30-0)))
@ -636,9 +645,9 @@
((id int8 :offset-assert 200)
(up-y float :offset-assert 204)
(down-y float :offset-assert 208)
(mode uint64 :offset-assert 216)
(mode under-locking-mode :offset-assert 216)
(which-reminder? symbol :offset-assert 224)
(spooled-sound-id uint32 :offset-assert 228)
(spooled-sound-id sound-id :offset-assert 228)
(draining-part sparticle-launch-control :offset-assert 232)
(actor-group (pointer actor-group) :offset-assert 236)
(spooled-sound-delay int32 :offset-assert 240)
@ -721,7 +730,7 @@
(let ((a1-6 *target*))
(cond
((and a1-6 (focus-test? a1-6 mech))
(set! (-> self mode) (the-as uint 3))
(set! (-> self mode) (under-locking-mode want-drain))
(let ((a1-8 (new 'stack-no-clear 'event-message-block)))
(set! (-> a1-8 from) (process->ppointer self))
(set! (-> a1-8 num-params) 1)
@ -740,7 +749,7 @@
)
)
(else
(set! (-> self mode) (the-as uint 0))
(set! (-> self mode) (under-locking-mode want-mech))
(let ((a1-9 (new 'stack-no-clear 'event-message-block)))
(set! (-> a1-9 from) (process->ppointer self))
(set! (-> a1-9 num-params) 1)
@ -781,14 +790,14 @@
(let ((v1-1 (-> self mode)))
(cond
((-> event param 0)
(when (or (zero? v1-1) (= v1-1 5))
(when (or (= v1-1 (under-locking-mode want-mech)) (= v1-1 (under-locking-mode want-exit-mech)))
(let ((a0-4 *target*))
(and a0-4 (not (logtest? (focus-status mech) (-> a0-4 focus-status))))
)
)
)
(else
(or (= v1-1 2) (= v1-1 3))
(or (= v1-1 (under-locking-mode airlock-wait)) (= v1-1 (under-locking-mode want-drain)))
)
)
)
@ -796,80 +805,78 @@
)
)
:trans (behavior ()
(let ((v1-0 (-> self mode)))
(cond
((zero? v1-0)
(let ((v1-1 *target*)
(a0-1 (-> self actor-group))
(case (-> self mode)
(((under-locking-mode want-mech))
(let ((v1-1 *target*)
(a0-1 (-> self actor-group))
)
(when (and v1-1 a0-1)
(if (and (-> a0-1 0 data 0 actor)
(focus-test? v1-1 mech)
(send-event (ppointer->process *underb-master*) 'request 'mech #t)
)
(set! (-> self mode) (under-locking-mode want-fill))
)
(when (and v1-1 a0-1)
(if (and (-> a0-1 0 data 0 actor)
(focus-test? v1-1 mech)
(send-event (ppointer->process *underb-master*) 'request 'mech #t)
)
)
)
(((under-locking-mode want-fill))
(if (not (logtest? (-> self actor-group 0 data 0 actor extra perm status) (entity-perm-status subtask-complete)))
(go-virtual filling)
)
)
(((under-locking-mode airlock-wait))
(let ((a1-2 (new 'stack-no-clear 'event-message-block)))
(set! (-> a1-2 from) (process->ppointer self))
(set! (-> a1-2 num-params) 0)
(set! (-> a1-2 message) 'front)
(let ((t9-2 send-event-function)
(v1-24 (-> self actor-group 0 data 1 actor))
)
(if (not (t9-2
(if v1-24
(-> v1-24 extra process)
)
a1-2
)
(set! (-> self mode) (the-as uint 1))
)
)
)
(set! (-> self mode) (under-locking-mode want-drain))
)
)
)
((= v1-0 1)
(if (not (logtest? (-> self actor-group 0 data 0 actor extra perm status) (entity-perm-status subtask-complete)))
(go-virtual filling)
)
)
((= v1-0 2)
(let ((a1-2 (new 'stack-no-clear 'event-message-block)))
(set! (-> a1-2 from) (process->ppointer self))
(set! (-> a1-2 num-params) 0)
(set! (-> a1-2 message) 'front)
(let ((t9-2 send-event-function)
(v1-24 (-> self actor-group 0 data 1 actor))
)
(if (not (t9-2
(if v1-24
(-> v1-24 extra process)
)
a1-2
)
)
(set! (-> self mode) (the-as uint 3))
)
)
)
(((under-locking-mode want-drain))
(if (>= 20480.0 (vector-vector-xz-distance (target-pos 0) (-> self root trans)))
(set! (-> self mode) (under-locking-mode drain))
)
)
(((under-locking-mode drain))
(go-virtual draining)
)
(((under-locking-mode want-exit-mech))
(let ((a1-4 *target*))
(when (or (not a1-4) (not (logtest? (focus-status mech) (-> a1-4 focus-status))))
(set! (-> self mode) (under-locking-mode want-mech))
0
)
)
((= v1-0 3)
(if (>= 20480.0 (vector-vector-xz-distance (target-pos 0) (-> self root trans)))
(set! (-> self mode) (the-as uint 4))
)
)
((= v1-0 4)
(go-virtual draining)
)
((= v1-0 5)
(let ((a1-4 *target*))
(when (or (not a1-4) (not (logtest? (focus-status mech) (-> a1-4 focus-status))))
(set! (-> self mode) (the-as uint 0))
0
)
)
(when (>= (- (current-time) (-> self last-reminder-time)) (seconds 9))
(set! (-> self last-reminder-time) (current-time))
(add-process
*gui-control*
self
(gui-channel ashelin)
(gui-action play)
(if (-> self which-reminder?)
"cityv193"
"cityv192"
)
-99.0
0
)
(set! (-> self which-reminder?) (not (-> self which-reminder?)))
(when (>= (- (current-time) (-> self last-reminder-time)) (seconds 9))
(set! (-> self last-reminder-time) (current-time))
(add-process
*gui-control*
self
(gui-channel ashelin)
(gui-action play)
(if (-> self which-reminder?)
"cityv193"
"cityv192"
)
-99.0
0
)
(set! (-> self which-reminder?) (not (-> self which-reminder?)))
)
)
)
)
(none)
)
@ -881,7 +888,7 @@
:event (-> (method-of-type under-locking active) event)
:enter (behavior ()
(set! (-> self spooled-sound-id)
(the-as uint (add-process *gui-control* self (gui-channel ashelin) (gui-action queue) "wtrfill" -99.0 0))
(add-process *gui-control* self (gui-channel ashelin) (gui-action queue) "wtrfill" -99.0 0)
)
(set! (-> self state-time) 0)
(set! (-> self spooled-sound-delay) (if (= (-> self id) 1)
@ -893,7 +900,7 @@
)
:trans (behavior ()
(when (and (zero? (-> self state-time))
(= (get-status *gui-control* (the-as sound-id (-> self spooled-sound-id))) (gui-status ready))
(= (get-status *gui-control* (-> self spooled-sound-id)) (gui-status ready))
)
(set! (-> self state-time) (current-time))
(set! (-> self last-reminder-time) (current-time))
@ -905,7 +912,7 @@
(set-action!
*gui-control*
(gui-action play)
(the-as sound-id (-> self spooled-sound-id))
(-> self spooled-sound-id)
(gui-channel none)
(gui-action none)
(the-as string #f)
@ -974,7 +981,7 @@
(persist-with-delay *setting-control* 'interp-time (seconds 0.05) 'interp-time 'abs 0.0 0)
(remove-setting! 'entity-name)
(send-event (ppointer->process *underb-master*) 'request 'under-warp #t)
(set! (-> self mode) (the-as uint 2))
(set! (-> self mode) (under-locking-mode airlock-wait))
(go-virtual active)
)
)
@ -991,7 +998,7 @@
:event (-> (method-of-type under-locking active) event)
:enter (behavior ()
(set! (-> self spooled-sound-id)
(the-as uint (add-process *gui-control* self (gui-channel ashelin) (gui-action queue) "wtrdrain" -99.0 0))
(add-process *gui-control* self (gui-channel ashelin) (gui-action queue) "wtrdrain" -99.0 0)
)
(set! (-> self state-time) 0)
0
@ -999,14 +1006,14 @@
)
:trans (behavior ()
(when (and (zero? (-> self state-time))
(= (get-status *gui-control* (the-as sound-id (-> self spooled-sound-id))) (gui-status ready))
(= (get-status *gui-control* (-> self spooled-sound-id)) (gui-status ready))
)
(set! (-> self state-time) (current-time))
(set! (-> self last-reminder-time) (current-time))
(set-action!
*gui-control*
(gui-action play)
(the-as sound-id (-> self spooled-sound-id))
(-> self spooled-sound-id)
(gui-channel none)
(gui-action none)
(the-as string #f)
@ -1074,7 +1081,7 @@
)
(when v1-26
(when (= (-> (the-as water-anim v1-26) root trans y) (-> self down-y))
(set! (-> self mode) (the-as uint 5))
(set! (-> self mode) (under-locking-mode want-exit-mech))
(send-event (ppointer->process *underb-master*) 'request 'mech #f)
(set! (-> self which-reminder?) #f)
(go-virtual active)
@ -1098,7 +1105,7 @@ This commonly includes things such as:
- sounds"
(local-vars (sv-16 res-tag))
(set! (-> obj which-reminder?) #f)
(set! (-> obj spooled-sound-id) (the-as uint 0))
(set! (-> obj spooled-sound-id) (new 'static 'sound-id))
(set! (-> obj root) (new 'process 'trsqv))
(process-drawable-from-entity! obj arg0)
(set! (-> obj id) (res-lump-value arg0 'extra-id int :time -1000000000.0))

View File

@ -463,16 +463,8 @@
)
)
;; (defun dm-subtitle-language ((blang int) (msg debug-menu-msg))
;; (let ((lang (the pc-subtitle-lang (/ blang 8))))
;; (when (= msg (debug-menu-msg press))
;; (set! (-> *pc-settings* subtitle-language) lang))
;; (= (-> *pc-settings* subtitle-language) lang)
;; )
;; )
;;
;; (defun dm-text-language ((blang int) (msg debug-menu-msg))
;; (let ((lang (the pc-subtitle-lang (/ blang 8))))
;; (let ((lang (the pc-language (/ blang 8))))
;; (when (= msg (debug-menu-msg press))
;; (set! (-> *pc-settings* text-language) lang))
;; (= (-> *pc-settings* text-language) lang)
@ -712,208 +704,34 @@
)
(defun sort-string-array ((arr (array string)) (compare-func (function string string object)))
"Sort an array, using compare-func to compare elements.
The comparison function can return either an integer or a true/false.
For integers, use a positive number to represent first > second
Ex: (sort lst -) will sort in ascending order
For booleans, you must explicitly use #t and not a truthy value.
Ex: (sort my-list (lambda ((x int) (y int)) (< x y))) will sort ascending.
NOTE: if you use an integer, don't accidentally return #t"
(let ((sorted -1))
(while (nonzero? sorted)
(set! sorted 0)
(dotimes (i (1- (-> arr allocated-length)))
(let* ((cur (-> arr i))
(next (-> arr (1+ i)))
(result (compare-func cur next))
)
(when (and (or (not result) (> (the-as int result) 0)) (!= result #t))
(+! sorted 1)
(set! (-> arr i) next)
(set! (-> arr (1+ i)) cur)
)
)
)
)
)
arr
)
(define *vag-play-menu* (the debug-menu #f))
(define *vag-player* (the (pointer process) #f))
(define *vagdir-names-list* (alloc-vagdir-names 'debug))
(defun alloc-vag-list ()
"allocates and returns a boxed array with all of the vag names as strings, sorted"
(let ((list (new 'debug 'boxed-array string (the int (-> *vagdir-names-list* -1)))))
;; for each vag...
(dotimes (i (-> list allocated-length))
;; write the vag name (64 bits) into the string directly and add a null character
(set! (-> (the (pointer uint64) (-> *temp-string* data))) (-> *vagdir-names-list* i))
(set! (-> *temp-string* data 8) 0)
;; copy into a new string
(set! (-> list i) (new 'debug 'string 0 *temp-string*)))
;; return the allocated, filled and sorted array
(sort-string-array list string<=?))
)
(define *vag-list* (alloc-vag-list))
(define *vag-max-pos-list* (the (pointer int32) (malloc 'debug (* 4 (-> *vag-list* allocated-length)))))
(defun dm-vag-play-pick-func ((idx int))
(if *vag-player*
(return #f))
(set! *vag-player* (process-spawn-function process :to *display-pool*
(lambda :behavior process ((idx int))
;; restore these settings when we're done
(protect (*display-art-control* (-> *debug-menu-context* is-hidden) *gui-kick-str*)
(true! *display-art-control*) ;; force this on
(true! (-> *debug-menu-context* is-hidden)) ;; hide debug menu
(true! *gui-kick-str*) ;; force gui control to play streams
(sound-group-continue (sound-group dialog dialog2)) ;; unpause dialog
(set-setting! 'music-volume 'abs 0.0 0) ;; mute music
(set-setting! 'sfx-volume 'abs 0.0 0) ;; mute sfx
(set-setting! 'ambient-volume 'abs 0.0 0) ;; mute ambient
(set-setting! 'dialog-volume 'abs 1.0 0) ;; max dialog
(apply-settings *setting-control*) ;; apply settings now
(let* ((exit? #f)
(old-speed 0.0)
(speed 0.0)
(id (new 'static 'sound-id))
(stop! (lambda :behavior process ((id sound-id))
(set-action! *gui-control* (gui-action stop) id (gui-channel none) (gui-action none) (the-as string #f) (the-as (function gui-connection symbol) #f) (the-as process #f))))
(play! (lambda :behavior process ((idx int))
(add-process *gui-control* self (gui-channel alert) (gui-action play) (-> *vag-list* idx) -10.0 0)))
(playing? (lambda :behavior process ((id sound-id))
(let ((status (get-status *gui-control* id))) (or (= status (gui-status ready)) (= status (gui-status active))))))
(set-speed (lambda :behavior process ((id sound-id) (speed float))
(when *sound-player-enable*
(let ((cmd (the-as sound-rpc-set-param (get-sound-buffer-entry))))
(set! (-> cmd command) (sound-command set-param))
(set! (-> cmd id) id)
(set! (-> cmd params pitch-mod) (the int (* 1524.0 speed)))
(set! (-> cmd params mask) (the-as uint 2))
(-> cmd id)
)
))))
(set! id (play! idx))
(while (= (gui-status pending) (get-status *gui-control* id))
(suspend))
(until (or exit? (!= *master-mode* 'menu))
(format *stdcon* "Vag Player -- Press Triangle To Exit~%")
(cond
((zero? id)
(format *stdcon* "No vag queued~%"))
((not (playing? id))
(format *stdcon* "Vag not playing~%"))
(else
(format *stdcon* "Vag playing: ~3L~D~0L~%" (the int (/ (the float (current-str-pos id)) 34.133335))))
)
(format *stdcon* "Vag: ~S <- ~S(max:~3L~D~0L) -> ~S~%" (if (> idx 0) (-> *vag-list* (1- idx)))
(-> *vag-list* idx) (-> *vag-max-pos-list* idx)
(if (< (1+ idx) (-> *vag-list* allocated-length)) (-> *vag-list* (1+ idx))))
(format *stdcon* "X to Pause and Play~%R1 and L1 for Speed, Circle Resets~%Left and Right for Prev / Next~%")
(format *stdcon* "Speed: ~f~%" speed)
(cond
((cpad-pressed? 0 triangle)
(cpad-clear! 0 triangle)
(stop! id)
(true! exit?))
((cpad-pressed? 0 x)
(cpad-clear! 0 x)
(cond
((not (playing? id))
(set! id (play! idx))
(set! old-speed 0.0))
((= speed -1000.0)
(set! speed 0.0)
(sound-continue id)
(when *sound-player-enable*
(let ((cmd (the-as sound-rpc-set-param (get-sound-buffer-entry))))
(set! (-> cmd command) (sound-command set-param))
(set! (-> cmd id) id)
(set! (-> cmd params pitch-mod) 0)
(set! (-> cmd params mask) (the-as uint 2))
(-> cmd id)
)
)
)
(else
(set! speed -1000.0)
(sound-pause id)
)
)
)
((and (cpad-pressed? 0 left) (> idx 0))
(cpad-clear! 0 left)
(stop! id)
(1-! idx)
(set! id (play! idx))
(set! old-speed 0.0))
((and (cpad-pressed? 0 right) (< (1+ idx) (-> *vag-list* allocated-length)))
(cpad-clear! 0 right)
(stop! id)
(1+! idx)
(set! id (play! idx))
(set! old-speed 0.0))
((and (cpad-hold? 0 r1 l1) (!= speed -1000.0))
(seek! speed (if (cpad-hold? 0 l1) -4.0 4.0) (* 0.5 (-> self clock seconds-per-frame)))
)
((cpad-pressed? 0 circle)
(cpad-clear! 0 circle)
(set! speed 0.0))
((cpad-pressed? 0 square)
(cpad-clear! 0 square))
)
(when (playing? id)
(max! (-> *vag-max-pos-list* idx) (the int (/ (the float (current-str-pos id)) 34.133335)))
(when (and (!= speed old-speed) (!= speed -1000.0))
(set-speed id speed)
(set! old-speed speed)))
(suspend))
)
(sound-group-pause (sound-group dialog dialog2)) ;; re-pause dialog
(remove-setting! 'music-volume)
(remove-setting! 'sfx-volume)
(remove-setting! 'ambient-volume)
(remove-setting! 'dialog-volume)
(apply-settings *setting-control*)
(set! *vag-player* #f)
)) idx))
#t)
(define *made-vag-list* #f)
(defun build-vag-list ((menu debug-menu))
"Fill the vag play menu"
(if *made-vag-list*
(return #f))
(true! *made-vag-list*)
;; clear old list
(debug-menu-remove-all-items menu)
;; make button for each vag, we use an index
(dotimes (i (-> *vag-list* allocated-length))
(debug-menu-append-item menu (new-dm-func (-> *vag-list* i) i dm-vag-play-pick-func))
(debug-menu-append-item menu (new-dm-func (-> *vag-list* i) i vag-player-play-from-index))
)
;; sort by vag name - note: already sorted from before
;(set! (-> menu items) (sort (-> menu items) debug-menu-node<?))
)
#t)
(define *vag-play-menu* (the debug-menu #f))
(defun debug-menu-make-vag-menu ((ctx debug-menu-context))
(let ((vag-menu (new 'debug 'debug-menu ctx "Vag menu")))
(let ((play-menu (new 'debug 'debug-menu ctx "Play Vag menu")))
(set! *vag-play-menu* play-menu)
(debug-menu-append-item vag-menu (new-dm-submenu "Play" play-menu))
)
(debug-menu-append-item vag-menu (new-dm-func "Make List" *vag-play-menu* build-vag-list))
(debug-menu-append-item vag-menu (new-dm-bool "subtitle" #f
(lambda (arg (msg debug-menu-msg))
(if (= msg (debug-menu-msg press))
@ -921,8 +739,6 @@
(-> *setting-control* user-default subtitle))))
;; pick channel
(build-vag-list *vag-play-menu*)
(new-dm-submenu "Vag" vag-menu)
)
)
@ -1012,7 +828,7 @@
;; (flag "hungarian" 14 dm-text-language)
;; )
(flag "Discord RPC" #t ,(dm-lambda-boolean-flag (-> *pc-settings* discord-rpc?)))
(flag "SpeedRunner Mode" #t ,(dm-lambda-boolean-flag (-> *pc-settings* speedrunner-mode?)))
(flag "Speedrunner Mode" #t ,(dm-lambda-boolean-flag (-> *pc-settings* speedrunner-mode?)))
(flag "Jetboard Trick String" #t ,(dm-lambda-boolean-flag (-> *pc-settings* jetboard-trick-text?)))
;; (flag "Speedrunner Mode" #t ,(dm-lambda-boolean-flag (-> *pc-settings* speedrunner-mode?)))
(menu "PS2 settings"
@ -1075,12 +891,12 @@
(debug-menu-make-from-template *debug-menu-context*
'(menu "Other"
(flag "DECI Count" *display-deci-count* dm-boolean-toggle-pick-func)
;(flag "Actor graph" *display-actor-graph* dm-boolean-toggle-pick-func)
(flag "Actor graph" *display-actor-graph* dm-boolean-toggle-pick-func)
(flag "Update vis outside bsp" *update-leaf-when-outside-bsp* dm-boolean-toggle-pick-func)
;; (flag "Pad display" *display-pad-debug* dm-boolean-toggle-pick-func)
(flag "Display actor bank" *display-actor-bank* dm-boolean-toggle-pick-func)
(flag "Heap status" *display-heap-status* dm-boolean-toggle-pick-func)
;; (flag "Text boxes" *display-text-box* dm-boolean-toggle-pick-func)
(flag "Text boxes" *display-text-box* dm-boolean-toggle-pick-func)
(flag "Display actor bank" *display-actor-bank* dm-boolean-toggle-pick-func)
(float-var "Actor birth dist" #f ,(dm-lambda-meters-var (-> *ACTOR-bank* birth-dist)) 20 1 #t 0 10000 1)
(float-var "Actor pause dist" #f ,(dm-lambda-meters-var (-> *ACTOR-bank* pause-dist)) 20 1 #t 0 10000 1)
(flag "Display city info" *display-city-info* dm-boolean-toggle-pick-func)

View File

@ -23,6 +23,7 @@
(defun entity-inspect-draw ((inspect-info entity-debug-inspect))
"draw text about an entity on screen"
(mlet ((LINE_HEIGHT 15))
(update-pad inspect-info 0)
(let* ((e (-> inspect-info entity)) (name (res-lump-struct e 'name string)))
(set! *debug-actor* (the string (and (type-type? (-> e type) entity-actor) (-> inspect-info show-actor-info) name)))
@ -42,14 +43,14 @@
)
;; start writing text
(let* ((begin-y (- 16 (* (-> inspect-info scroll-y) 16))) (cur-y begin-y) (y-adv 16))
(let* ((begin-y (- LINE_HEIGHT (* (-> inspect-info scroll-y) LINE_HEIGHT))) (cur-y begin-y) (y-adv 16))
(with-dma-buffer-add-bucket ((debug-buf (-> (current-frame) debug-buf))
(bucket-id debug-no-zbuf1))
;; basic info, actor id, etc
(draw-string-xy
(string-format "~3L~A~0L ~A~%tags: ~D size: ~D aid: #x~x~%R1/L1 scroll L3 toggle display-actor-info~%--------------------" (-> e type) name (length e) (asize-of e) (-> e aid))
debug-buf 352 cur-y (font-color default) (font-flags shadow kerning middle))
(+! cur-y (* 14 4))
(+! cur-y (* LINE_HEIGHT 4))
(cond
((type-type? (-> e type) entity-actor)
(let ((actor (the entity-actor e)))
@ -57,11 +58,11 @@
(draw-string-xy
(string-format "etype: ~A~%vis: ~D task: ~S" (-> actor etype) (-> actor vis-id) (game-task->string (-> actor task)))
debug-buf 352 cur-y (font-color default) (font-flags shadow kerning middle))
(+! cur-y (* 14 2))
(format *debug-temp-string* "(~S)" (begin (bit-enum->string task-mask (-> actor kill-mask) (clear *temp-string*)) *temp-string*))
(+! cur-y (* LINE_HEIGHT 2))
(format (clear *debug-temp-string*) "(~S)" (begin (bit-enum->string task-mask (-> actor kill-mask) (clear *temp-string*)) *temp-string*))
(draw-string-xy (string-format "kill-mask: ~S" *debug-temp-string*)
debug-buf 352 cur-y (font-color default) (font-flags shadow kerning middle))
(+! cur-y (* 14 1))
(+! cur-y (* LINE_HEIGHT 1))
)
)
)
@ -77,7 +78,7 @@
(cond
;; some water-height info
((and (= (-> e tag i name) 'water-height) (= (-> e tag i elt-count) 4) (= (-> e tag i elt-type) float))
(+! y-adv (* 14 1))
(+! y-adv (* LINE_HEIGHT 1))
(format *debug-temp-string* "~mm ~mm ~mm~%(~S)"
(-> (the (pointer float) data) 0)
(-> (the (pointer float) data) 1)
@ -87,7 +88,7 @@
)
;; some water-height info but with 5 elts
((and (= (-> e tag i name) 'water-height) (= (-> e tag i elt-count) 4) (= (-> e tag i elt-type) float))
(+! y-adv (* 14 3))
(+! y-adv (* LINE_HEIGHT 3))
(format *debug-temp-string* "~mm ~mm ~mm~%(~S)~%~mm"
(-> (the (pointer float) data) 0)
(-> (the (pointer float) data) 1)
@ -218,24 +219,24 @@
'spawner-blocker-actor 'spawner-trigger-actor 'kill-actor 'fade-actor 'water-actor 'target-actor 'formation-actor)
(format *debug-temp-string* "~%#x~x (~S)" (-> (the (pointer uint32) data) ii)
(res-lump-struct (entity-by-aid (-> (the (pointer uint32) data) ii)) 'name string))
(+! y-adv 14)
(+! y-adv LINE_HEIGHT)
)
(('nav-mesh-actor)
(format *debug-temp-string* "~%#x~x (~S)" (-> (the (pointer uint32) data) ii)
(res-lump-struct (entity-nav-mesh-by-aid (-> (the (pointer actor-id) data) ii)) 'name string))
(+! y-adv 14)
(+! y-adv LINE_HEIGHT)
)
(('enemy-options)
(format *debug-temp-string* "~%(~S)" (begin (bit-enum->string enemy-option (-> (the (pointer uint32) data) ii) (clear *temp-string*)) *temp-string*))
(+! y-adv 14)
(+! y-adv LINE_HEIGHT)
)
(('kill-mask)
(format *debug-temp-string* "~%(~S)" (begin (bit-enum->string task-mask (-> (the (pointer uint32) data) ii) (clear *temp-string*)) *temp-string*))
(+! y-adv 14)
(+! y-adv LINE_HEIGHT)
)
(('elevator-flags)
(format *debug-temp-string* "~%(~S)" (begin (bit-enum->string elevator-flags (-> (the (pointer uint32) data) ii) (clear *temp-string*)) *temp-string*))
(+! y-adv 14)
(+! y-adv LINE_HEIGHT)
)
(else
(format *debug-temp-string* "#x~x" (-> (the (pointer uint32) data) ii))
@ -281,10 +282,10 @@
)
)
)
(+! y-adv 14))
(+! y-adv LINE_HEIGHT))
((pair)
(format *debug-temp-string* "~%~A" (-> (the (pointer pair) data) 0))
(+! y-adv 14)
(+! y-adv LINE_HEIGHT)
)
((actor-group)
(let* ((group (-> (the (pointer actor-group) data) ii))
@ -292,9 +293,9 @@
(format *debug-temp-string* "~%--- GROUP ~D: ---" ii len)
(dotimes (iii len)
(format *debug-temp-string* "~% #x~x (~S)" (-> group data iii id) (res-lump-struct (entity-by-aid (-> group data iii id)) 'name string))
(+! y-adv 14))
(+! y-adv LINE_HEIGHT))
)
(+! y-adv 14)
(+! y-adv LINE_HEIGHT)
)
;; no clue! please report this.
(else
@ -310,13 +311,13 @@
;; draw a string for each tag instead of all at once. allows using smaller strings.
(draw-string-xy *debug-temp-string* debug-buf 352 cur-y (font-color default) (font-flags shadow kerning middle))
(+! cur-y y-adv)
(set! y-adv 14)
(set! y-adv LINE_HEIGHT)
))
;; set max scroll based on how large the whole text was, ignore first 20 lines.
(set! (-> inspect-info scroll-y-max) (max 0 (+ -20 (/ (- cur-y begin-y) 14))))
(set! (-> inspect-info scroll-y-max) (max 0 (+ -20 (/ (- cur-y begin-y) LINE_HEIGHT))))
)
)))
))))

View File

@ -0,0 +1,464 @@
;;-*-Lisp-*-
(in-package goal)
#|
vag player process for debugging vag streams and for easier subtitling.
|#
(declare-file (debug))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; types and enums
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;----------------------------------
;; process type
;;;----------------------------------
;; the vag-player process. it lives on the PC actor pool
(deftype vag-player (process)
(
(vag-index int32)
(id sound-id)
(speed float)
(old-speed float)
;; temp settings
(master-mode symbol)
(display-art-control symbol)
(debug-menu-hidden symbol)
(gui-kick-str symbol)
)
(:methods
(vag-stop (_type_) int)
(vag-play (_type_) sound-id)
(vag-playing? (_type_) symbol)
(vag-set-speed (_type_ float) sound-id)
)
(:states
(vag-player-playing int)
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; vag list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; an array of 64-bit values which can be turned into a string
;; each representing the name of a vag stream. convert using alloc-vag-list
(define *vagdir-names-list* (alloc-vagdir-names 'debug))
(defun sort-string-array ((arr (array string)) (compare-func (function string string object)))
"Sort an array, using compare-func to compare elements.
The comparison function can return either an integer or a true/false.
For integers, use a positive number to represent first > second
Ex: (sort lst -) will sort in ascending order
For booleans, you must explicitly use #t and not a truthy value.
Ex: (sort my-list (lambda ((x int) (y int)) (< x y))) will sort ascending.
NOTE: if you use an integer, don't accidentally return #t"
(let ((sorted -1))
(while (nonzero? sorted)
(set! sorted 0)
(dotimes (i (1- (-> arr allocated-length)))
(let* ((cur (-> arr i))
(next (-> arr (1+ i)))
(result (compare-func cur next))
)
(when (and (or (not result) (> (the-as int result) 0)) (!= result #t))
(+! sorted 1)
(set! (-> arr i) next)
(set! (-> arr (1+ i)) cur)
)
)
)
)
)
arr
)
(defun alloc-vag-list ()
"allocates and returns a boxed array with all of the vag names as strings, sorted"
(let ((list (new 'debug 'boxed-array string (the int (-> *vagdir-names-list* -1)))))
;; for each vag...
(dotimes (i (-> list allocated-length))
;; write the vag name (64 bits) into the string directly and add a null character
(set! (-> (the (pointer uint64) (-> *temp-string* data))) (-> *vagdir-names-list* i))
(set! (-> *temp-string* data 8) 0)
(countdown (ii 8)
(if (!= #x20 (-> *temp-string* data ii))
(set! ii 0)
(set! (-> *temp-string* data ii) 0))
)
;; copy into a new string
(set! (-> list i) (new 'debug 'string 0 *temp-string*)))
;; return the allocated, filled and sorted array
(sort-string-array list string<=?))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; globals
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; the process pointer.
(define *vag-player* (the (pointer vag-player) #f))
;; list of vag names (as a string)
(define *vag-list* (alloc-vag-list))
;; the highest recorded position for each vag
(define *vag-max-pos-list* (the (pointer int32) (malloc 'debug (* 4 (-> *vag-list* allocated-length)))))
(defun get-vag-index-from-name ((name string))
"return the index of the vag with that name in the *vag-list* or -1 if not found"
;; uppercase the string so we have a consistent name format
(string-upcase name *vag-temp-string*)
(dotimes (i (-> *vag-list* allocated-length))
(string-upcase (-> *vag-list* i) *vag-temp-string-2*)
(when (string= *vag-temp-string* *vag-temp-string-2*)
(return i))
)
-1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; states
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod deactivate vag-player ((obj vag-player))
(set! *vag-player* (the (pointer vag-player) #f))
((method-of-type process deactivate) obj)
(none)
)
(defstate vag-player-idle (vag-player)
:event (behavior ((from process) (argc int) (msg symbol) (block event-message-block))
(case msg
(('play)
(let ((vag-idx (get-vag-index-from-name (the-as string (-> block param 0)))))
(when (!= vag-idx -1)
(go vag-player-playing vag-idx)
(return #t)))
#f)
(('play-index)
(go vag-player-playing (the-as int (-> block param 0))))
)
)
:code (behavior ()
(sleep-code)
(none))
)
(defstate vag-player-playing (vag-player)
:event (behavior ((from process) (argc int) (msg symbol) (block event-message-block))
(case msg
(('play)
(let ((vag-idx (get-vag-index-from-name (the-as string (-> block param 0)))))
(when (!= vag-idx -1)
(set! (-> self vag-index) vag-idx)
(vag-play self)
(return #t)))
#f)
(('play-index)
(set! (-> self vag-index) (the-as int (-> block param 0)))
(vag-play self)
#t)
)
)
:enter (behavior ((index int))
(set! (-> self master-mode) *master-mode*)
(set! (-> self debug-menu-hidden) (-> *debug-menu-context* is-hidden))
(set! (-> self display-art-control) *display-art-control*)
(set! (-> self gui-kick-str) *gui-kick-str*)
(set-master-mode 'menu) ;; put us in menu mode first
(true! *display-art-control*) ;; force this on
(true! (-> *debug-menu-context* is-hidden)) ;; hide debug menu
(true! *gui-kick-str*) ;; force gui control to play streams
(sound-group-continue (sound-group dialog dialog2)) ;; unpause dialog
(set-setting! 'music-volume 'abs 0.0 0) ;; mute music
(set-setting! 'sfx-volume 'abs 0.0 0) ;; mute sfx
(set-setting! 'ambient-volume 'abs 0.0 0) ;; mute ambient
(set-setting! 'dialog-volume 'abs 1.0 0) ;; max dialog
(apply-settings *setting-control*) ;; apply settings now
(set! (-> self speed) 0.0)
(set! (-> self old-speed) 0.0)
)
:exit (behavior ()
(vag-stop self)
(remove-setting! 'music-volume)
(remove-setting! 'sfx-volume)
(remove-setting! 'ambient-volume)
(remove-setting! 'dialog-volume)
(apply-settings *setting-control*)
(sound-group-pause (sound-group dialog dialog2))
(set! *display-art-control* (-> self display-art-control))
(set! (-> *debug-menu-context* is-hidden) (-> self debug-menu-hidden))
(set! *gui-kick-str* (-> self gui-kick-str))
(if (!= (-> self master-mode) 'menu)
(set-master-mode (-> self master-mode)))
(none))
:code (behavior ((index int))
(set! (-> self vag-index) index)
(let ((exit? #f))
(vag-play self)
(while (= (gui-status pending) (get-status *gui-control* (-> self id)))
(suspend))
(until (or exit? (!= *master-mode* 'menu))
(format *stdcon* "Vag Player -- Press Triangle To Exit~%")
(cond
((zero? (-> self id))
(format *stdcon* "No vag queued~%"))
((not (vag-playing? self))
(format *stdcon* "Vag not playing~%"))
(else
(format *stdcon* "Vag playing: ~3L~D~0L~%" (the int (/ (the float (current-str-pos (-> self id))) (/ 1024.0 30)))))
)
(format *stdcon* "Vag: ~S <- ~S(max:~3L~D~0L) -> ~S~%" (if (> (-> self vag-index) 0) (-> *vag-list* (1- (-> self vag-index))))
(-> *vag-list* (-> self vag-index)) (-> *vag-max-pos-list* (-> self vag-index))
(if (< (1+ (-> self vag-index)) (-> *vag-list* allocated-length)) (-> *vag-list* (1+ (-> self vag-index)))))
(format *stdcon* "X to Pause and Play~%R1 and L1 for Speed, Circle Resets~%Left and Right for Prev / Next~%Square for Subtitles (~A)~%" (-> *setting-control* user-default subtitle))
(format *stdcon* "Speed: ~f~%" (-> self speed))
(cond
((cpad-pressed? 0 triangle)
(cpad-clear! 0 triangle)
(true! exit?))
((cpad-pressed? 0 x)
(cpad-clear! 0 x)
(cond
((not (vag-playing? self))
(vag-play self))
((= (-> self speed) -1000.0)
(set! (-> self speed) 0.0)
(sound-continue (-> self id))
(when *sound-player-enable*
(let ((cmd (the-as sound-rpc-set-param (get-sound-buffer-entry))))
(set! (-> cmd command) (sound-command set-param))
(set! (-> cmd id) (-> self id))
(set! (-> cmd params pitch-mod) 0)
(set! (-> cmd params mask) (the-as uint 2))
(-> cmd id)
)
)
)
(else
(set! (-> self speed) -1000.0)
(sound-pause (-> self id))
)
)
)
((and (cpad-pressed? 0 left) (> (-> self vag-index) 0))
(cpad-clear! 0 left)
(1-! (-> self vag-index))
(vag-play self))
((and (cpad-pressed? 0 right) (< (1+ (-> self vag-index)) (-> *vag-list* allocated-length)))
(cpad-clear! 0 right)
(1+! (-> self vag-index))
(vag-play self))
((and (cpad-hold? 0 r1 l1) (!= (-> self speed) -1000.0))
(seek! (-> self speed) (if (cpad-hold? 0 l1) -4.0 4.0) (* 0.5 (-> self clock seconds-per-frame)))
)
((cpad-pressed? 0 circle)
(cpad-clear! 0 circle)
(set! (-> self speed) 0.0))
((cpad-pressed? 0 square)
(cpad-clear! 0 square)
(not! (-> *setting-control* user-default subtitle)))
)
(when (vag-playing? self)
(max! (-> *vag-max-pos-list* (-> self vag-index)) (the int (/ (the float (current-str-pos (-> self id))) (/ 1024.0 30))))
(when (and (!= (-> self speed) (-> self old-speed)) (!= (-> self speed) -1000.0))
(vag-set-speed self (-> self speed))
(set! (-> self old-speed) (-> self speed))))
(suspend))
)
(go vag-player-idle)
(none))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; methods
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod vag-play vag-player ((self vag-player))
"play the current vag stream"
(set! (-> self speed) 0.0)
(set! (-> self old-speed) 0.0)
(vag-stop self)
(set! (-> self id) (add-process *gui-control* self (gui-channel alert) (gui-action play) (-> *vag-list* (-> self vag-index)) -10.0 0)))
(defmethod vag-stop vag-player ((self vag-player))
"stop the current vag stream"
(set-action! *gui-control* (gui-action stop) (-> self id) (gui-channel none) (gui-action none) (the-as string #f) (the-as (function gui-connection symbol) #f) (the-as process #f)))
(defmethod vag-playing? vag-player ((self vag-player))
"is the current vag stream playing?"
(let ((status (get-status *gui-control* (-> self id)))) (or (= status (gui-status ready)) (= status (gui-status active)))))
(defmethod vag-set-speed vag-player ((self vag-player) (speed float))
"set the speed of the current vag stream"
(when *sound-player-enable*
(let ((cmd (the-as sound-rpc-set-param (get-sound-buffer-entry))))
(set! (-> cmd command) (sound-command set-param))
(set! (-> cmd id) (-> self id))
(set! (-> cmd params pitch-mod) (the int (* 1524.0 speed)))
(set! (-> cmd params mask) (the-as uint 2))
(-> cmd id)
)
))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; helper functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defbehavior vag-player-init-by-other vag-player ()
"external initializer for vag-player process"
(set! (-> self id) (new 'static 'sound-id))
(go vag-player-idle)
)
(defun vag-player-stop ()
"kill the subtitle2 process"
(kill-by-type vag-player *pc-pool*))
(defun vag-player-start ()
"start the subtitle2 process"
(when *vag-player*
(vag-player-stop)
)
(set! *vag-player* (process-spawn vag-player :from *pc-dead-pool* :to *pc-pool*))
)
(defun vag-player-play-from-index ((index int))
"play a vag from its index in the vag list"
(if (not *vag-player*)
(vag-player-start))
(send-event (ppointer->process *vag-player*) 'play-index index))
(defun vag-player-play-from-name ((name string))
"play a vag from its name"
(if (not *vag-player*)
(vag-player-start))
(send-event (ppointer->process *vag-player*) 'play name))
(defun vag-list-to-file ((file-name string))
(if *vag-list*
(let ((file (new 'stack 'file-stream file-name 'write)))
(dotimes (i (-> *vag-list* allocated-length))
(format file "~S~%" (-> *vag-list* i))
)
(file-stream-close file)
)
#f
)
)
;; start the vag-player process when this file loads.
(vag-player-start)
(defun scene-find-and-play ((name string))
"go through the scene player list to find and play the requested scene"
(vag-player-stop)
(cond
;; hardcoded cases for the continue points
((string= "intro-city-square" name)
(process-spawn scene-player :init scene-player-init name #t "ctyindb-intro-start"))
((string= "intro-prison" name)
(process-spawn scene-player :init scene-player-init name #t "prison-intro-start"))
((string= "outro-nest" name)
(set! (-> palout memory-mode) (load-buffer-mode borrow))
(set! (-> hiphog memory-mode) (load-buffer-mode small-center))
(process-spawn scene-player :init scene-player-init name #t "nestb-outro"))
((string= "outro-palace" name)
(set! (-> palout memory-mode) (load-buffer-mode borrow))
(set! (-> hiphog memory-mode) (load-buffer-mode small-center))
(process-spawn scene-player :init scene-player-init name #t "throne-outro"))
((string= "outro-hiphog" name)
(set! (-> hiphog memory-mode) (load-buffer-mode small-center))
(process-spawn scene-player :init scene-player-init name #t "hiphog-outro"))
((string= "outro-port" name)
(set! (-> hiphog memory-mode) (load-buffer-mode small-center))
(process-spawn scene-player :init scene-player-init name #t "ctyport-outro"))
(else
(let* ((find-scene-in-act
(lambda ((scene-list (array hud-scene-info)) (name string))
;; for each scene in list
(doarray (scene-info scene-list)
;; scene name matches - return that immediately
(if (and (string? (-> scene-info info)) (string= (the string (-> scene-info info)) name))
(return scene-info))
;; scene name didn't match, see if there is a scene playlist
;; if there is, then find our scene there
(when (pair? (-> scene-info info))
(let ((iter (the pair (-> scene-info info))))
(while (not (null? iter))
(if (and (string? (car iter)) (string= (the string (car iter)) name))
(return scene-info))
(set! iter (cdr iter))
)
)
)
)
(the hud-scene-info #f))))
(awhen (or (find-scene-in-act *hud-select-scene-act1* name)
(find-scene-in-act *hud-select-scene-act2* name)
(find-scene-in-act *hud-select-scene-act3* name))
(process-spawn scene-player :init scene-player-init name #t (-> (the hud-scene-info it) continue))
)
)
))
0)

View File

@ -19,11 +19,39 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pc enum for languages. this is the game's languages + custom ones.
(defenum pc-language
:type uint16
(english 0)
(french 1)
(german 2)
(spanish 3)
(italian 4)
(japanese 5)
(korean 6)
(uk-english 7)
;; custom
(portuguese 8)
(finnish 9)
(swedish 10)
(danish 11)
(norwegian 12)
(dutch 13)
(br-portuguese 14)
(hungarian 15)
(catalan 16)
(icelandic 17)
(russian 18)
(custom 999) ;; temp
)
;; The Jak 2 version of the pc-settings object.
(deftype pc-settings-jak2 (pc-settings)
(
(fast-airlock? symbol)
(fast-elevator? symbol)
(text-language pc-language) ;; language for game text
;; debug
(jetboard-trick-text? symbol) ;; enable rendering jetboard trick combo during minigame
@ -51,6 +79,7 @@
)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; resets
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -119,6 +119,7 @@
(("jetboard-trick-text?") (set! (-> obj jetboard-trick-text?) (file-stream-read-symbol file)))
(("fast-airlock?") (set! (-> obj fast-airlock?) (file-stream-read-symbol file)))
(("fast-elevator?") (set! (-> obj fast-elevator?) (file-stream-read-symbol file)))
(("text-language") (set! (-> obj text-language) (the-as pc-language (file-stream-read-int file))))
)
0)
@ -129,6 +130,7 @@
(format file " (jetboard-trick-text? ~A)~%" (-> obj jetboard-trick-text?))
(format file " (fast-airlock? ~A)~%" (-> obj fast-airlock?))
(format file " (fast-elevator? ~A)~%" (-> obj fast-elevator?))
(format file " (text-language ~D)~%" (-> obj text-language))
0)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -0,0 +1,355 @@
;;-*-Lisp-*-
(in-package goal)
#|
Code for subtitles for the PC port. A PC actor pool is provided, and the subtitle2 process lives there.
Jak 2 has subtitles, but only for cutscenes and only for the actual spoken text.
The subtitle process automatically looks for currently-playing audio in the gui control.
It looks for specific channels there, NOT including the movie or subtitle channel.
This updated subtitle system has a few different features than the Jak 1 subtitle system:
- you can have multiple playing subtitles at once. Additional subtitles are rendered above the older ones,
just like real subtitles. This goes for both multiple subtitles within the same scene, and also multiple scenes
playing at once.
- it can "merge" with the pre-existing subtitle system. Some code in scene.gc is changed to redirect subtitles
to here to do that.
- you supply the start AND end times as opposed to just the start time.
- the speaker names are color-coded.
Note that subtitle images are NOT supported with this! Merge mode will also NOT work with subtitle images.
Similarly to the generic text file, only one subtitles text file is loaded at once, stored in a specific
heap.
|#
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconstant PC_SUBTITLE_FILE_SIZE (* 192 1024)) ;; 192K heap for subtitles. adjust later if necessary.
(defconstant PC_SUBTITLE_FILE_NAME "subti2")
(defconstant PC_SUBTITLE_QUEUE_SIZE 5) ;; up to 8 things that display subtitles can be detected at once
(defconstant PC_SUBTITLE_QUEUE_MAX_LINES 2) ;; up to 2 lines can be queued per queueable thing
(defconstant PC_SUBTITLE_MAX_LINES 10) ;; max subtitles that can be displayed at once: queue-size * queue-lines
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; types and enums
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;------------------------
;; data
;;;------------------------
(defenum pc-subtitle2-flags
:bitfield #t
:type uint16
(offscreen) ;; speaker is offscreen.
(merge) ;; line of text comes from movie subtitles
)
;; the list of available speakers for subtitles
(defenum pc-subtitle2-speaker
:type uint16
(none) ;; won't display a speaker - use this for tutorial messages etc.
(computer)
(jak)
(darkjak)
(daxter)
(samos)
(keira)
(keira-before-class-3)
(kid)
(kor)
(metalkor)
(baron)
(errol)
(torn)
(tess)
(guard)
(guard-a)
(guard-b)
(krew)
(sig)
(brutter)
(vin)
(youngsamos)
(youngsamos-before-rescue)
(pecker)
(onin)
(ashelin)
(jinx)
(mog)
(grim)
(agent)
(citizen-male)
(citizen-female)
(oracle)
(precursor)
(max))
;; information about a single line of subtitles
(deftype subtitle2-line (structure)
(
(start-frame float) ;; the first frame to show the line on
(end-frame float) ;; the last frame to show the line on
(text string) ;; the text for the subtitle2 line
(speaker pc-subtitle2-speaker) ;; who the line speaker is
(flags pc-subtitle2-flags) ;; flags
)
:pack-me
)
;; an individual entry to a subtitle2 text making up a "scene" (audio file, spool), comprised of a series of lines
(deftype subtitle2-scene (structure)
(
;; the name of the spool-anim or audio file
(name string)
;; the amount of lines
(length int32)
;; line data
(lines (inline-array subtitle2-line))
)
:pack-me
:size-assert #xc ;; compact!
(:methods
(get-line-at-pos (_type_ float int) subtitle2-line)
)
)
;; the global subtitle2 text info bank
(deftype subtitle2-text-info (basic)
((length int16)
(version int16)
(lang pc-language)
(speaker-length int16)
(speaker-names (pointer string))
(data subtitle2-scene :inline :dynamic)
)
(:methods
(get-speaker (_type_ pc-subtitle2-speaker) string)
(get-scene-by-name (_type_ string) subtitle2-scene)
)
)
(defmacro subtitle2-flags? (sub &rest flags)
`(logtest? (-> ,sub flags) (pc-subtitle2-flags ,@flags)))
(defmethod inspect subtitle2-text-info ((obj subtitle2-text-info))
(if (not obj)
(return (the subtitle2-text-info #f)))
(format #t "[~8x] ~A~%" obj (-> obj type))
(format #t "~1Tlength: ~D~%" (-> obj length))
(format #t "~1Tversion: ~D~%" (-> obj version))
(format #t "~1Tlang: ~D~%" (-> obj lang))
(format #t "~1Tspeaker-names[~D] @ #x~x~%" (-> obj speaker-length) (-> obj speaker-names))
(dotimes (i (-> obj speaker-length))
(format #t "~2T[~D]: ~A~%" i (-> obj speaker-names i)))
(format #t "~1Tdata[0] @ #x~x~%" (-> obj data))
(dotimes (i (-> obj length))
(format #t "~2T--------~%")
(format #t "~2Tname: ~A~%" (-> obj data i name))
(format #t "~2Tlines[~D] @ #x~x~%" (-> obj data i length) (-> obj data i lines))
(dotimes (ii (-> obj data i length))
(format #t "~3T[~f to ~f] (#x~x)(~S) ~A~%" (-> obj data i lines ii start-frame) (-> obj data i lines ii end-frame)
(-> obj data i lines ii flags)
(enum->string pc-subtitle2-speaker (-> obj data i lines ii speaker))
(-> obj data i lines ii text)))
)
obj)
;;;----------------------------------
;; process type
;;;----------------------------------
;; graphic parameters for subtitles
(deftype subtitle2-bank (structure)
((scale float)
(width float)
(lines float)
)
)
(define *SUBTITLE2-bank*
(new 'static 'subtitle2-bank
:scale 0.9
:width 0.65
:lines 2.0
))
(deftype subtitle2-queue-element (structure)
((id sound-id)
(gui gui-connection)
)
:pack-me
(:methods
(clear-line (_type_) int))
)
(deftype subtitle2-line-queue-element (structure)
((line subtitle2-line)
(y float)
)
:pack-me
)
;; the subtitle2 process! it lives on the PC actor pool
(deftype subtitle2 (process)
(
(font font-context) ;; the font to use for the subtitles.
(have-message? symbol) ;; if there is a message displaying at the bottom, move subtitles up
(have-minimap? symbol) ;; if there is a minimap displaying at the bottom, shrink subtitles
(have-subtitles? symbol) ;; #t if we rendered any subtitles on the last frame.
(movie-mode? symbol) ;; #t if we're in movie mode
(movie-line string) ;; a copy of the current movie line
(movie-gui gui-connection) ;; the gui entry for the movie. we need this to put it in the gui queue
(movie-pos float)
(gui-id sound-id)
;; store the gui id of channels with subtitles that we find.
;; that way if subtitle B appears above A, it wont move back down
;; if A ends before B
(queue subtitle2-queue-element PC_SUBTITLE_QUEUE_SIZE :inline)
(lines-0 subtitle2-line-queue-element PC_SUBTITLE_MAX_LINES :inline)
(lines-1 subtitle2-line-queue-element PC_SUBTITLE_MAX_LINES :inline)
(line-queue-idx int8)
;; debug
(cheat-backup symbol)
(checking-lines? symbol)
(current-debug-subtitle subtitle2-line)
(current-debug-scene int32)
(current-debug-line int32)
)
(:methods
(clear-queue (_type_) int)
(update-gui-connections (_type_) int)
(get-empty-queue (_type_) int)
(gui-queued? (_type_ gui-connection) symbol)
(add-to-queue (_type_ gui-connection) gui-connection)
(get-active-subtitles (_type_) int)
(subtitle-format (_type_ subtitle2-line) string)
(draw-subtitles (_type_) int)
(debug-print-queue (_type_) int)
(debug-print-speakers (_type_) int)
(start-gui (_type_) sound-id)
(stop-gui (_type_) sound-id)
)
(:states
subtitle2-debug
subtitle2-debug-checking-lines)
)
;;;----------------------------------------------
;; globals
;;;----------------------------------------------
;; the subtitle2 process.
(define *subtitle2* (the (pointer subtitle2) #f))
;; subtitle2 text data
(define *subtitle2-text* (the subtitle2-text-info #f))
(kheap-alloc (define *subtitle2-text-heap* (new 'global 'kheap)) PC_SUBTITLE_FILE_SIZE)
;; temp strings for name look-up
(define *vag-temp-string* (new 'global 'string 128 (the string #f)))
(define *vag-temp-string-2* (new 'global 'string 128 (the string #f)))
;; speaker color table
(define *subtitle2-speaker-color-table* (the (pointer rgba) (malloc 'global (* (size-of rgba) (pc-subtitle2-speaker max)))))
;; debug option
(define *display-subtitle-speakers* #f)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; helper functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod length subtitle2-text-info ((obj subtitle2-text-info))
"Get the length (number of subtitle2 scenes) in a subtitle2-text-info."
(-> obj length)
)
(defmethod length subtitle2-scene ((obj subtitle2-scene))
"Get the length (number of subtitle2 lines) in a subtitle2-scene."
(-> obj length)
)
(defmacro set-subtitle-speaker-color! (speaker color)
"macro for setting a color in *subtitle2-speaker-color-table*"
`(set! (-> *subtitle2-speaker-color-table* (pc-subtitle2-speaker ,speaker)) ,color))
(defmacro set-subtitle-speaker-color<-speaker! (speaker speaker-from)
"macro for setting a color in *subtitle2-speaker-color-table* the same as a different speaker"
`(set-subtitle-speaker-color! ,speaker (-> *subtitle2-speaker-color-table* (pc-subtitle2-speaker ,speaker-from))))
(defun set-subtitle-speaker-colors ()
"fill the subtitle speaker color table"
(dotimes (i (pc-subtitle2-speaker max))
(set! (-> *subtitle2-speaker-color-table* i) (-> *font-work* color-table (font-color red) color 0))
)
(set-subtitle-speaker-color! computer (static-rgba #x60 #x60 #x60 #x80))
(set-subtitle-speaker-color! jak (static-rgba #x70 #x80 #x00 #x80))
(set-subtitle-speaker-color! darkjak (static-rgba #x68 #x68 #x80 #x80))
(set-subtitle-speaker-color! samos (static-rgba #x30 #x80 #x08 #x80))
(set-subtitle-speaker-color! youngsamos (static-rgba #x58 #x80 #x08 #x80))
(set-subtitle-speaker-color! kor (static-rgba #x00 #x50 #x80 #x80))
(set-subtitle-speaker-color! torn (static-rgba #x40 #x40 #x50 #x80))
(set-subtitle-speaker-color! metalkor (static-rgba #x00 #x28 #x40 #x80))
(set-subtitle-speaker-color! baron (static-rgba #x60 #x00 #x00 #x80))
(set-subtitle-speaker-color! errol (static-rgba #x80 #x10 #x00 #x80))
(set-subtitle-speaker-color! ashelin (static-rgba #x80 #x18 #x18 #x80))
(set-subtitle-speaker-color! sig (static-rgba #x70 #x70 #x80 #x80))
(set-subtitle-speaker-color! vin (static-rgba #x38 #x80 #x80 #x80))
(set-subtitle-speaker-color! oracle (static-rgba #x80 #x40 #x00 #x80))
(set-subtitle-speaker-color! brutter (static-rgba #x58 #x00 #x18 #x80))
(set-subtitle-speaker-color! guard (static-rgba #x80 #x00 #x00 #x80))
(set-subtitle-speaker-color! krew (static-rgba #x10 #x48 #x10 #x80))
(set-subtitle-speaker-color! keira (static-rgba #x00 #x40 #x28 #x80))
(set-subtitle-speaker-color! tess (static-rgba #x80 #x80 #x38 #x80))
(set-subtitle-speaker-color! pecker (static-rgba #x80 #x80 #x00 #x80))
(set-subtitle-speaker-color! onin (static-rgba #x80 #x80 #x80 #x80))
(set-subtitle-speaker-color! jinx (static-rgba #x50 #x40 #x00 #x80))
(set-subtitle-speaker-color! mog (static-rgba #x08 #x08 #x80 #x80))
(set-subtitle-speaker-color! grim (static-rgba #x80 #x08 #x20 #x80))
(set-subtitle-speaker-color! precursor (static-rgba #x00 #x60 #x80 #x80))
(set-subtitle-speaker-color! citizen-male (static-rgba #x70 #x70 #x70 #x80))
(set-subtitle-speaker-color! citizen-female (static-rgba #x70 #x70 #x70 #x80))
(set-subtitle-speaker-color<-speaker! kid jak)
(set-subtitle-speaker-color<-speaker! guard-a guard)
(set-subtitle-speaker-color<-speaker! guard-b guard)
(set-subtitle-speaker-color<-speaker! keira-before-class-3 keira)
(set-subtitle-speaker-color<-speaker! youngsamos-before-rescue youngsamos)
)

View File

@ -0,0 +1,875 @@
;;-*-Lisp-*-
(in-package goal)
#|
Code for subtitles for the PC port. A PC actor pool is provided, and the subtitle2 process lives there.
Jak 2 has subtitles, but only for cutscenes and only for the actual spoken text.
The subtitle process automatically looks for currently-playing audio in the gui control.
It looks for specific channels there, NOT including the movie or subtitle channel.
This updated subtitle system has a few different features than the Jak 1 subtitle system:
- you can have multiple playing subtitles at once. Additional subtitles are rendered above the older ones,
just like real subtitles. This goes for both multiple subtitles within the same scene, and also multiple scenes
playing at once.
- it can "merge" with the pre-existing subtitle system. Some code in scene.gc is changed to redirect subtitles
to here to do that.
- you supply the start AND end times as opposed to just the start time.
- the speaker names are color-coded.
Note that subtitle images are NOT supported with this! Merge mode will also NOT work with subtitle images.
Similarly to the generic text file, only one subtitles text file is loaded at once, stored in a specific
heap.
|#
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; constants
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconstant PC_SUBTITLE_Y_RECALC -99.0)
(defconstant PC_SUBTITLE_DISABLE_MOVIE_MODE #f)
(defconstant PC_SUB_DBG_Y 60)
(defconstant PC_SUB_DBG_CHECK_GROUP_SIZE 64)
(defglobalconstant PC_SUBTITLE_DEBUG #f)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; access subtitle heap
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod get-speaker subtitle2-text-info ((obj subtitle2-text-info) (speaker pc-subtitle2-speaker))
"get the translated string for that speaker"
(if (and (> speaker (pc-subtitle2-speaker none)) (< speaker (-> obj speaker-length)))
(-> obj speaker-names speaker)
(the string #f))
)
(defmethod get-scene-by-name subtitle2-text-info ((obj subtitle2-text-info) (name string))
"get a subtitle scene info with the corresponding name. #f = none found"
;; invalid name so return invalid scene.
(if (not name)
(return (the subtitle2-scene #f)))
;; bounds checking
(when (> (length name) (-> *vag-temp-string* allocated-length))
(format 0 "vag temp string is too short!! wanted: ~D chars~%" (length name)))
;; uppercase the string so we have a consistent name format
(string-upcase name *vag-temp-string*)
(dotimes (i (length obj))
;; bounds checking
(when (> (length (-> obj data i name)) (-> *vag-temp-string-2* allocated-length))
(format 0 "vag temp string is too short!! wanted: ~D chars~%" (length name)))
;; name and kind matches, return that!
(string-upcase (-> obj data i name) *vag-temp-string-2*)
(when (string= *vag-temp-string-2* *vag-temp-string*)
(return (-> obj data i)))
)
(the subtitle2-scene #f))
(defmethod get-line-at-pos subtitle2-scene ((obj subtitle2-scene) (pos float) (index int))
"return the subtitle line at that position. #f = none found
index is which line to return, since you can have multiple lines that cover the same position."
(let ((found 0))
(dotimes (i (length obj))
(when (and (>= pos (-> obj lines i start-frame))
(< pos (-> obj lines i end-frame)))
(when (= found index)
(return (-> obj lines i)))
(1+! found)
)))
(the subtitle2-line #f))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; loading files
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun load-subtitle2-text-info ((txt-name string) (curr-text symbol) (heap kheap))
"load a subtitles text file onto a heap.
txt-name = file name suffix
curr-text = a symbol to a subtitle2-text-info to link the file to
heap = the text heap to load the file onto"
(let ((heap-sym-heap (the-as subtitle2-text-info (-> curr-text value)))
(lang (-> *setting-control* user-current subtitle-language))
(load-status 0)
(heap-free (&- (-> heap top) (the-as uint (-> heap base)))))
;; current text has nothing loaded, or language doesn't match.
(when (or (= heap-sym-heap #f)
(!= (-> heap-sym-heap lang) lang))
;; so reload.
;; reset the text heap.
(kheap-reset heap)
;; try to load load...
(while (not (str-load (string-format "~D~S.TXT" lang txt-name) -1 (logand -64 (&+ (-> heap current) 63)) (&- (-> heap top) (-> heap current))))
(return 0)
)
;; load succeeded. check status.
(label retry)
(let ((status (str-load-status (the-as (pointer int32) (& load-status)))))
(when (= status 'error)
(format 0 "Error loading subtitle2~%")
(return 0)
(goto loaded)
)
(cond
((>= load-status (+ heap-free -300))
(format 0 "Game subtitle2 heap overrun!~%")
(return 0)
)
((= status 'busy)
;; still loading.
(goto retry)
)
)
)
(label loaded)
;; link the text file!
(let ((new-mem (logand -64 (&+ (-> heap current) 63))))
(flush-cache 0)
(set! (-> curr-text value) (link new-mem (-> (string-format "~D~S.TXT" lang txt-name) data) load-status heap 0))
)
;; if linking failed just make the text invalid.
(if (<= (the-as int (-> curr-text value)) 0)
(set! (-> curr-text value) (the-as object #f))
)
))
0)
(defun load-level-subtitle2-files ((idx int))
"Load the subtitle2 files needed for level idx.
This function made more sense back when text files were split up, but in the end they put everything
in a single text group and file."
;; just load common.
(if (or *level-text-file-load-flag* (>= idx 0))
(load-subtitle2-text-info PC_SUBTITLE_FILE_NAME '*subtitle2-text* *subtitle2-text-heap*)
)
(none))
(defmacro reload-subtitles ()
"rebuild and reload subtitles."
`(begin
(asm-text-file subtitle2 :files ("game/assets/jak2/game_subtitle.gp"))
(if *subtitle2-text*
(+! (-> *subtitle2-text* lang) 1))
(load-level-subtitle2-files 0)))
(defmacro reload-text ()
"rebuild and reload text."
`(begin
(mng)
(if *common-text*
(+! (-> *common-text* language-id) 1))
(load-level-text-files 0)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; subtitle2 queue
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun subtitle-channel? ((ch gui-channel))
"can this gui channel be checked for subtitles?"
(and (>= ch (gui-channel jak)) (<= ch (gui-channel krew)))
)
(defun valid-subtitle-gui? ((gui gui-connection))
"is this gui connection valid for checking subtitles?"
(and gui (nonzero? (-> gui id))
(subtitle-channel? (-> gui channel))
(or (= (-> gui action) (gui-action playing))
(= (-> gui action) (gui-action play)))
(let ((status (get-status *gui-control* (-> gui id))))
(or (= status (gui-status ready))
(= status (gui-status active)))))
)
(defun subtitle-bump-up? ()
"should subtitles be moved up?"
;; have a query or message up?
(or (nonzero? (lookup-gui-connection-id *gui-control* (the string #f) (gui-channel query) (gui-action playing)))
(nonzero? (lookup-gui-connection-id *gui-control* (the string #f) (gui-channel message) (gui-action playing)))
(nonzero? (lookup-gui-connection-id *gui-control* (the string #f) (gui-channel notice-low) (gui-action playing)))
)
)
(defmethod clear-line subtitle2-queue-element ((obj subtitle2-queue-element))
"make this queue element invalid"
(set! (-> obj gui) #f)
(set! (-> obj id) (new 'static 'sound-id))
0)
(defmethod clear-queue subtitle2 ((obj subtitle2))
"mark all slots in the gui queue as available"
(dotimes (i PC_SUBTITLE_QUEUE_SIZE)
(clear-line (-> obj queue i)))
0)
(defmethod update-gui-connections subtitle2 ((obj subtitle2))
"mark all inactive slots in the gui queue as available"
(dotimes (i PC_SUBTITLE_QUEUE_SIZE)
(let ((gui (lookup-gui-connection *gui-control* (the process #f) (gui-channel none) (the string #f) (-> obj queue i id))))
(if (not (valid-subtitle-gui? gui))
(clear-line (-> obj queue i)))))
0)
(defmethod gui-queued? subtitle2 ((obj subtitle2) (gui gui-connection))
"return #t is the gui is in the queue"
(dotimes (i PC_SUBTITLE_QUEUE_SIZE)
(if (= (-> gui id) (-> obj queue i id))
(return #t)))
#f)
(defmethod get-empty-queue subtitle2 ((obj subtitle2))
"return the first available gui queue slot"
(dotimes (i PC_SUBTITLE_QUEUE_SIZE)
(if (not (-> obj queue i gui))
(return i)))
(format #t "ran out of subtitle queue slots!")
0
)
(defmethod add-to-queue subtitle2 ((obj subtitle2) (gui gui-connection))
"add a gui connection to the first empty queue slot available"
(let ((slot (get-empty-queue obj)))
(set! (-> obj queue slot id) (-> gui id))
(set! (-> obj queue slot gui) gui))
gui)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; subtitle2 process and drawing!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun set-speaker-color ((speaker pc-subtitle2-speaker))
"set the color for the speaker font color"
(let ((spk-col (-> *subtitle2-speaker-color-table* speaker)))
(set-font-color (font-color progress-selected) 0 (new 'static 'rgba :r (-> spk-col r))
(new 'static 'rgba :r (-> spk-col g))
(new 'static 'rgba :r (-> spk-col b)))
(set-font-color (font-color progress-selected) 1 (new 'static 'rgba :r (-> spk-col r))
(new 'static 'rgba :r (-> spk-col g))
(new 'static 'rgba :r (-> spk-col b))))
speaker)
(defmethod get-active-subtitles subtitle2 ((obj subtitle2))
"collect active subtitles and add them to the queue
if a gui connection is already in the queue,
it will stay in the same slot when it was first added"
;; todo
(-> *gui-control* engine)
(let ((current (-> *gui-control* engine alive-list-end prev0)))
(-> *gui-control* engine)
(let ((next (-> current prev0)))
(while (!= current (-> *gui-control* engine alive-list))
(let ((gui-conn (the gui-connection current)))
(when (and (valid-subtitle-gui? gui-conn)
(not (gui-queued? obj gui-conn)))
(add-to-queue obj gui-conn)
)
)
(set! current next)
(-> *gui-control* engine)
(set! next (-> next prev0))
)
)
)
0)
(defmethod subtitle-format subtitle2 ((obj subtitle2) (line subtitle2-line))
"format the string for a subtitle line to *temp-string*"
(when (subtitle2-flags? line merge)
(if (and (-> obj movie-mode?) (< 0 (length (-> obj movie-line))))
(set! (-> line text) (-> obj movie-line))
(return (the string #f))))
(cond
((= (pc-subtitle2-speaker none) (-> line speaker))
;; there's no speaker so who cares.
(string-format "~S" (-> line text)))
((or (= #t (-> *pc-settings* subtitle-speaker?))
(and (= 'auto (-> *pc-settings* subtitle-speaker?)) (subtitle2-flags? line offscreen)))
;; there is a speaker and we do want it.
;; we use color 33 which gets set at runtime to any color we want
(string-format "~33L~S:~0L ~S" (get-speaker *subtitle2-text* (-> line speaker)) (-> line text)))
(else
(string-format "~S" (-> line text)))
)
*temp-string*)
(defbehavior current-subtitle2-pos subtitle2 ((id sound-id))
"get the str position for this sound id in a 30/sec measurement"
(if (and (-> self movie-mode?) (= id (-> self movie-gui id)))
(return (-> self movie-pos)))
(let ((pos (the float (current-str-pos id))))
(if (< pos 0.0) -1.0 (/ pos (/ 1024.0 30)))))
(defbehavior setup-subtitle2-font subtitle2 ((font font-context))
"setup a font and parameters for the subtitle2 subtitles."
;; set font settings.
(if (!= (language-enum japanese) (-> *setting-control* user-current subtitle-language))
(set-scale! font (* 0.5 (-> *SUBTITLE2-bank* scale)))
(set-scale! font (* 0.5 (-> *SUBTITLE2-bank* scale) 1.2)))
(set-width! font (the int (* (-> *SUBTITLE2-bank* width) 0.91 512)))
(set-origin! font (the int (/ (- 512.0 (-> font width)) 2))
(the int (* (if (-> self have-message?) 0.524 0.698) 416)))
(set-height! font (the int (* (-> *SUBTITLE2-bank* lines) 44)))
;; if we have the minimap, set the right border to 74.4% of screen width. shrink if larger than that.
;; TODO scale this with aspect.
(when (and (-> self have-minimap?)
(< (get-screen-x 0.744) (+ (-> font width) (-> font origin x))))
(let ((new-width (- (get-screen-x 0.744) (-> font origin x))))
(set-scale! font (* (-> font scale) (/ (the float new-width) (-> font width))))
(set-width! font new-width)))
)
(defmethod draw-subtitles subtitle2 ((self subtitle2))
"do the subtitle drawing"
;; check the gui queue for lines to add to the line queue
(let ((line-queue-old (if (zero? (-> self line-queue-idx)) (-> self lines-0) (-> self lines-1)))
(line-queue (if (zero? (-> self line-queue-idx)) (-> self lines-1) (-> self lines-0)))
(find-line (lambda ((queue (inline-array subtitle2-line-queue-element)) (line subtitle2-line))
(dotimes (i PC_SUBTITLE_MAX_LINES)
(if (= line (-> queue i line))
(return i)))
-1)))
(logxor! (-> self line-queue-idx) 1)
;; clear the queue we're writing to first
(dotimes (i PC_SUBTITLE_MAX_LINES)
(set! (-> line-queue i line) #f)
(set! (-> line-queue i y) PC_SUBTITLE_Y_RECALC)
)
;; we won't be able to render any subtitles with no text loaded.
(when (not *subtitle2-text*)
(false! (-> self have-subtitles?))
(return 0))
;; font has already been set up in movie mode
(unless (-> self movie-mode?)
;; set up our font to the initial parameters
(let ((map-gui (lookup-gui-connection *gui-control* (the process #f) (gui-channel hud-lower-right) "hud-map" (new 'static 'sound-id))))
(set! (-> self have-message?) (or (subtitle-bump-up?) (and (-> self have-message?) (-> self have-subtitles?))))
(set! (-> self have-minimap?) (and (logtest? (minimap-flag minimap) (-> *setting-control* user-current minimap))
(!= map-gui #f)
(!= (gui-status pending) (get-status *gui-control* (-> map-gui id)))
(!= (gui-action hidden) (-> map-gui action))))
)
(setup-subtitle2-font (-> self font)))
;; do two passes - on the first one we add lines that were already being used,
;; on the second pass we add new lines
(dotimes (q 2)
(dotimes (i PC_SUBTITLE_QUEUE_SIZE)
(when (-> self queue i gui)
(let ((pos (current-subtitle2-pos (-> self queue i id))))
(when (and (zero? q) *debug-segment*)
(format *stdcon* "subtitle pos: ~3L~D~0L (~S)~%" (the int pos) (-> self queue i gui name)))
(let ((scene (get-scene-by-name *subtitle2-text* (-> self queue i gui name))))
(when scene
(dotimes (ii PC_SUBTITLE_QUEUE_MAX_LINES)
(awhen (get-line-at-pos scene pos ii)
(case q
((0)
(let ((index-in-old (find-line line-queue-old it)))
(when (!= -1 index-in-old)
;; this line exists in the previous frame, put it in the new queue at the same spot
(set! (-> line-queue index-in-old line) it)
(set! (-> line-queue index-in-old y) (-> line-queue-old index-in-old y)))))
((1)
(when (= -1 (find-line line-queue it))
;; line not in the queue. find empty spot.
(let ((index-empty (find-line line-queue (the subtitle2-line #f))))
(if (!= -1 index-empty)
(set! (-> line-queue index-empty line) it)))
))
)
)
))
)
)
)
))
(let ((cur-y (-> self font origin y)) ;; the current y for the text
(start-y (-> self font origin y)) ;; the starting y for the text
(last-height 0.0) ;; the height of the previous subtitle
(this-height 0.0) ;; the height of the current subtitle
(lines-done 0)
(subtitles-drawn? #f)
)
(dotimes (i PC_SUBTITLE_QUEUE_MAX_LINES)
(when (and (-> line-queue i line) (subtitle-format self (-> line-queue i line)))
(set! this-height (print-game-text *temp-string* (-> self font) #t 44 (bucket-id debug-no-zbuf2)))
;; push subtitle up since we are not the first one
(when (nonzero? lines-done)
(-! cur-y (/ last-height 2))
(-! cur-y (/ this-height 2))
)
;; set the current y, it shall not be lower than the previous line!
(if (= (-> line-queue i y) PC_SUBTITLE_Y_RECALC)
(set! (-> line-queue i y) (- start-y cur-y))
(set! cur-y (min cur-y (- start-y (-> line-queue i y)))))
(set! (-> self font origin y) cur-y)
;; check if we should actually draw subtitles and do it
(when (and (-> *setting-control* user-current subtitle) (or *gui-kick-str* (= *master-mode* 'game)))
(set-action! *gui-control* (gui-action play) (-> self gui-id)
(gui-channel none) (gui-action none) (the-as string #f) (the-as (function gui-connection symbol) #f) (the-as process #f))
(when (= (gui-status active) (get-status *gui-control* (-> self gui-id)))
(true! subtitles-drawn?)
(protect (*display-text-box*)
(set! *display-text-box* (or *display-text-box* PC_SUBTITLE_DEBUG))
(set-speaker-color (-> line-queue i line speaker))
(print-game-text *temp-string* (-> self font) #f 44 (bucket-id debug-no-zbuf2))))
)
;; save this for later usage
(set! last-height this-height)
(1+! lines-done)
)
)
(set! (-> self have-subtitles?) subtitles-drawn?)
(when (not (-> self have-subtitles?))
(set-action! *gui-control* (gui-action hidden) (-> self gui-id)
(gui-channel none) (gui-action none) (the-as string #f) (the-as (function gui-connection symbol) #f) (the-as process #f)))
(set! (-> self font origin y) start-y)))
0)
(when *debug-segment*
(defmethod debug-print-queue subtitle2 ((self subtitle2))
"print the queue to *stdcon*"
(format *stdcon* "q: ~%")
(dotimes (i PC_SUBTITLE_QUEUE_SIZE)
(if (-> self queue i gui)
(format *stdcon* "~D: ~S ~3L~D~0L ~D ~`gui-connection`P~%" i
(-> self queue i gui name)
(the int (current-subtitle2-pos (-> self queue i id)))
(-> self queue i id)
(-> self queue i gui))))
(format *stdcon* "l: ~%")
(let ((line-queue (if (zero? (-> self line-queue-idx)) (-> self lines-0) (-> self lines-1))))
(dotimes (i PC_SUBTITLE_MAX_LINES)
(format *stdcon* "~D: ~D ~S~%" i (the int (-> line-queue i y)) (aif (-> line-queue i line) (-> it text)))))
0)
(defmethod debug-print-speakers subtitle2 ((self subtitle2))
"print all speakers onscreen"
(if (not *subtitle2-text*)
(return 0))
(let ((font (new 'stack 'font-context *font-default-matrix* 0 0 0.0 (font-color default) (font-flags shadow kerning large)))
(col-wid (/ 512.0 3)))
(set-width! font (the int col-wid))
(set-height! font 44)
(set-scale! font 0.5)
(dotimes (i (-> *subtitle2-text* speaker-length))
(set-speaker-color (the pc-subtitle2-speaker i))
(+! (-> font origin y) (print-game-text (string-format "~33L~S" (get-speaker *subtitle2-text* (the pc-subtitle2-speaker i)))
font #f 44 (bucket-id debug-no-zbuf2)))
(when (< 416.0 (-> font origin y))
(set! (-> font origin y) 0.0)
(+! (-> font origin x) col-wid))
))
0)
)
(defmethod start-gui subtitle2 ((self subtitle2))
"start gui queueing"
(set! (-> self gui-id) (add-process *gui-control* self (gui-channel subtitle-pc) (gui-action hidden) "subtitle2" (meters 20) 0))
)
(defmethod stop-gui subtitle2 ((self subtitle2))
"stop gui queueing"
(set-action! *gui-control* (gui-action stop) (-> self gui-id)
(gui-channel none)
(gui-action none)
(the-as string #f)
(the-as (function gui-connection symbol) #f)
(the-as process #f))
(set! (-> self gui-id) (new 'static 'sound-id))
)
(defstate subtitle2-process (subtitle2)
:event (behavior ((from process) (argc int) (msg symbol) (block event-message-block))
(case msg
(('movie 'movie-no-subtitle)
;; we are receiving parameters for a movie subtitle!
(when (not *subtitle2-text*)
(format 0 "movie subtitle: no text loaded~%")
(return #f))
(set! (-> self movie-gui) (lookup-gui-connection *gui-control* (the process #f) (gui-channel art-load) (the-as string (-> block param 0)) (new 'static 'sound-id)))
(when (not (-> self movie-gui))
(format 0 "movie subtitle: no gui found~%")
(return #f))
(set! (-> self movie-mode?) #t)
(set! (-> self movie-pos) (the-as float (-> block param 2)))
(when (!= msg 'movie-no-subtitle)
(copyn-charp<-string (-> self movie-line data) (the-as string (-> block param 1))
(-> self movie-line allocated-length))
(set! (-> self have-message?) #f)
(set! (-> self have-minimap?) #f)
(set! (-> self have-subtitles?) #f)
(setup-subtitle2-font (-> self font))
;; we're gonna use the same font as the movie subtitles
(set-origin! (-> self font) 20 290)
(set-width! (-> self font) 465)
(set-height! (-> self font) 70)
(set-scale! (-> self font) 0.5)
(when (= (-> *setting-control* user-current subtitle-language) (language-enum korean))
(set-scale! (-> self font) 0.6))
)
#t)
)
)
:code (behavior ()
(loop
(suspend))
)
:trans (behavior ()
(when *debug-segment*
(when (and (cpad-hold? 0 l3) (cpad-pressed? 0 r3))
(cpad-clear! 0 r3)
(set! (-> self cheat-backup) *cheat-mode*)
(set! *cheat-mode* 'camera)
(set-master-mode 'pause)
(go subtitle2-debug)
)
)
(load-level-subtitle2-files 0)
;; get subtitles
(cond
((not (-> self movie-mode?))
;; get rid of invalid gui entries
(update-gui-connections self)
;; queue up valid ones
(get-active-subtitles self)
)
((-> self movie-gui)
;; wipe the queue
(clear-queue self)
;; queue up the movie gui - this is the only one we want in movie mode
(add-to-queue self (-> self movie-gui))
)
(else
;; something weird happened
(if *debug-segment*
(format #t "bad movie gui~%"))
(set! (-> self movie-mode?) #f)
(clear-queue self))
)
(none))
:post (behavior ()
(draw-subtitles self)
(when *debug-segment*
(if *display-subtitle-speakers*
(debug-print-speakers self))
(if PC_SUBTITLE_DEBUG
(debug-print-queue self))
)
(when (-> self movie-mode?)
(if *debug-segment*
(format *stdcon* "subtitle2 movie-mode~%"))
(set! (-> self movie-gui) #f)
(set! (-> self movie-mode?) #f)
(clear (-> self movie-line))
)
0)
)
(defstate subtitle2-debug (subtitle2)
:trans (behavior ()
(with-dma-buffer-add-bucket ((buf (-> (current-frame) debug-buf))
(bucket-id debug-no-zbuf2))
(draw-string-xy "~3LSUBTITLE DEBUG!~0L" buf 14 (+ PC_SUB_DBG_Y (* 0 15)) (font-color default) (font-flags shadow kerning))
(draw-string-xy "L3+R3: exit" buf 14 (+ PC_SUB_DBG_Y (* 1 15)) (font-color default) (font-flags shadow kerning))
(if (!= 'pause *master-mode*)
(draw-string-xy "Pause the game to continue" buf 14 (+ PC_SUB_DBG_Y (* 2 15)) (font-color default) (font-flags shadow kerning)))
(when (= 'pause *master-mode*)
;(draw-string-xy "L3+X: debug lines" buf 14 (+ PC_SUB_DBG_Y (* 2 15)) (font-color default) (font-flags shadow kerning))
;(draw-string-xy "L3+Triangle: debug box" buf 14 (+ PC_SUB_DBG_Y (* 3 15)) (font-color default) (font-flags shadow kerning))
(cond
((or (not *subtitle2-text*) (zero? (-> *subtitle2-text* length)))
(draw-string-xy "NO SUBTITLES LOADED!!!" buf 14 (+ PC_SUB_DBG_Y (* 10 15)) (font-color red) (font-flags shadow kerning))
(load-level-subtitle2-files 0)
(set! (-> self current-debug-scene) 0)
(set! (-> self current-debug-line) 0)
)
(else
(cond
((cpad-pressed? 0 square)
(true! (-> self checking-lines?))
)
((cpad-pressed? 0 left)
(if (> (-> self current-debug-line) 0)
(1-! (-> self current-debug-line)))
)
((cpad-pressed? 0 right)
(if (< (-> self current-debug-line) (1- (-> *subtitle2-text* data (-> self current-debug-scene) length)))
(1+! (-> self current-debug-line)))
)
((or (cpad-pressed? 0 up) (and (cpad-hold? 0 l2) (cpad-hold? 0 up)))
(when (> (-> self current-debug-scene) 0)
(1-! (-> self current-debug-scene))
(set! (-> self current-debug-line) 0))
)
((or (cpad-pressed? 0 down) (and (cpad-hold? 0 l2) (cpad-hold? 0 down)))
(when (< (-> self current-debug-scene) (1- (-> *subtitle2-text* length)))
(1+! (-> self current-debug-scene))
(set! (-> self current-debug-line) 0))
)
)
(let ((cur-scene (-> *subtitle2-text* data (-> self current-debug-scene))))
(if (nonzero? (-> cur-scene length))
(set! (-> self current-debug-subtitle) (-> *subtitle2-text* data (-> self current-debug-scene) lines (-> self current-debug-line)))
(set! (-> self current-debug-subtitle) #f))
(draw-string-xy "Up/down: Pick scene" buf 14 (+ PC_SUB_DBG_Y (* 4 15)) (font-color default) (font-flags shadow kerning))
(draw-string-xy "L2+Up/down: Pick scene (fast)" buf 14 (+ PC_SUB_DBG_Y (* 5 15)) (font-color default) (font-flags shadow kerning))
(draw-string-xy "Left/right: Pick line" buf 14 (+ PC_SUB_DBG_Y (* 6 15)) (font-color default) (font-flags shadow kerning))
(draw-string-xy "Square: Check all line heights" buf 14 (+ PC_SUB_DBG_Y (* 7 15)) (font-color default) (font-flags shadow kerning))
(draw-string-xy (string-format "Scene: ~D/~D (~S)" (1+ (-> self current-debug-scene)) (-> *subtitle2-text* length) (-> cur-scene name))
buf 14 (+ PC_SUB_DBG_Y (* 8 15)) (font-color default) (font-flags shadow kerning))
(draw-string-xy (string-format "Line: ~D/~D" (1+ (-> self current-debug-line)) (-> cur-scene length))
buf 14 (+ PC_SUB_DBG_Y (* 9 15)) (font-color default) (font-flags shadow kerning))
)
)
)
))
(when (-> self checking-lines?)
(false! (-> self checking-lines?))
(go subtitle2-debug-checking-lines)
)
(when (and (cpad-hold? 0 l3) (cpad-pressed? 0 r3))
(cpad-clear! 0 r3)
(set! *cheat-mode* (-> self cheat-backup))
(set-master-mode 'game)
(go subtitle2-process)
)
(none))
:code (-> subtitle2-process code)
:post (behavior ()
(set! (-> self movie-mode?) #f)
(set! (-> self have-message?) #f)
(set! (-> self have-minimap?) #f)
(set! (-> self have-subtitles?) #f)
(setup-subtitle2-font (-> self font))
(when (-> self current-debug-subtitle)
(set-speaker-color (-> self current-debug-subtitle speaker))
(print-game-text (subtitle-format self (-> self current-debug-subtitle)) (-> self font) #f 44 (bucket-id debug-no-zbuf2))
)
0)
)
(defstate subtitle2-debug-checking-lines (subtitle2)
:trans (behavior ()
(set! (-> self movie-mode?) #f)
(set! (-> self have-message?) #f)
(set! (-> self have-minimap?) #f)
(set! (-> self have-subtitles?) #f)
(setup-subtitle2-font (-> self font))
(none))
:code (behavior ()
(protect ((-> *pc-settings* subtitle-speaker?))
(set! (-> *pc-settings* subtitle-speaker?) #t)
(let ((lines-so-far 0)
(lines-this-frame 0)
(bad-lines 0))
(dotimes (i (length *subtitle2-text*))
(dotimes (ii (length (-> *subtitle2-text* data i)))
(when (= lines-this-frame PC_SUB_DBG_CHECK_GROUP_SIZE)
(set! lines-this-frame 0)
(suspend))
(1+! lines-this-frame)
(set! (-> self current-debug-subtitle) (-> *subtitle2-text* data i lines ii))
(set-speaker-color (-> self current-debug-subtitle speaker))
(when (< (* (-> *SUBTITLE2-bank* lines) 22) (print-game-text (subtitle-format self (-> self current-debug-subtitle)) (-> self font) #f 44 (bucket-id debug-no-zbuf2)))
(format 0 "ERROR: LINE ~D IN SCENE ~D IS TOO LARGE!~%" (1+ ii) (1+ i))
(format #t "ERROR: LINE ~D IN SCENE ~D IS TOO LARGE!~%" (1+ ii) (1+ i))
(1+! bad-lines)
)
)
)
(suspend)
(if (> bad-lines 0)
(format 0 "error: ~D bad lines detected.~%" bad-lines)
(format 0 "no bad lines detected!~%" bad-lines))
))
(go subtitle2-debug)
)
:post (behavior ()
(with-dma-buffer-add-bucket ((buf (-> (current-frame) debug-buf))
(bucket-id debug2))
(draw-string-xy "Checking for bad lines... See console for info" buf 14 PC_SUB_DBG_Y (font-color red) (font-flags shadow kerning))
)
(draw-debug-text-box (-> self font))
0)
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; helper functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod deactivate subtitle2 ((self subtitle2))
(stop-gui self)
;; not sure this works...
(if (= (ppointer->process *subtitle2*) self)
(set! *subtitle2* #f))
((method-of-type process deactivate) self)
(none)
)
(defbehavior subtitle2-init-by-other subtitle2 ()
"external initializer for subtitle2 process"
(set! (-> self font) (new 'process 'font-context *font-default-matrix*
0 0 0.0 (font-color default) (font-flags shadow kerning left middle large)))
(clear-queue self)
(dotimes (i PC_SUBTITLE_MAX_LINES)
(set! (-> self lines-0 i line) #f)
(set! (-> self lines-0 i y) PC_SUBTITLE_Y_RECALC)
(set! (-> self lines-1 i line) #f)
(set! (-> self lines-1 i y) PC_SUBTITLE_Y_RECALC)
)
(set! (-> self have-message?) #f)
(set! (-> self have-minimap?) #f)
(set! (-> self have-subtitles?) #f)
(set! (-> self movie-mode?) #f)
(set! (-> self movie-line) (new 'process 'string (+ 7 (* 15 16)) (the string #f)))
(set! (-> self current-debug-scene) 0)
(set! (-> self current-debug-line) 0)
(set! (-> self current-debug-subtitle) #f)
(set! (-> self checking-lines?) #f)
(start-gui self)
(go subtitle2-process)
)
(defun subtitle2-stop ()
"kill the subtitle2 process"
(if *subtitle2*
(deactivate (ppointer->process *subtitle2*)))
*subtitle2*)
(defun subtitle2-start ()
"start the subtitle2 process"
;; fill the subtitle speaker table
(set-subtitle-speaker-colors)
(if *subtitle2*
(subtitle2-stop))
(set! *subtitle2* (process-spawn subtitle2 :from *pc-dead-pool* :to *pc-pool*))
)
;; start the subtitle2 process when this file loads.
(subtitle2-start)

View File

@ -103,6 +103,18 @@ Val* Compiler::compile_asm_text_file(const goos::Object& form, const goos::Objec
db.m_subtitle_groups = std::make_unique<GameSubtitleGroups>();
db.m_subtitle_groups->hydrate_from_asset_file();
compile_game_subtitle(inputs, db, m_make.compiler_output_prefix());
} else if (kind == "subtitle2") {
std::vector<GameSubtitle2DefinitionFile> inputs;
// open all project files specified (usually one).
for_each_in_list(args.named.at("files"), [this, &inputs, &form, &kind](const goos::Object& o) {
if (o.is_string()) {
open_subtitle2_project(kind, o.as_string()->data, inputs);
} else {
throw_compiler_error(form, "Invalid object {} in asm-text-file files list.", o.print());
}
});
GameSubtitle2DB db(m_version);
compile_game_subtitle2(inputs, db, m_make.compiler_output_prefix());
} else if (kind == "text") {
std::vector<GameTextDefinitionFile> inputs;
// open all project files specified (usually one).

View File

@ -143,6 +143,83 @@ void compile_subtitle(GameSubtitleDB& db, const std::string& output_prefix) {
data.data(), data.size());
}
}
/*!
* Write game subtitle2 data to a file. Uses the V2 object format which is identical between GOAL
* and OpenGOAL.
*/
void compile_subtitle2(GameSubtitle2DB& db, const std::string& output_prefix) {
auto& speaker_names = get_speaker_names(db.version());
for (const auto& [lang, bank] : db.banks()) {
auto font = get_font_bank(bank->text_version);
DataObjectGenerator gen;
gen.add_type_tag("subtitle2-text-info"); // type
gen.add_word((bank->scenes.size() & 0xffff) | (1 << 16)); // length (lo) + version (hi)
// note: we add 1 because "none" isn't included
gen.add_word((lang & 0xffff) | ((speaker_names.size() + 1) << 16)); // lang + speaker-length
int speaker_array_link = gen.add_word(0); // speaker array (dummy for now)
auto speaker_index_by_name = [&speaker_names](const std::string& name) {
for (int i = 0; i < speaker_names.size(); ++i) {
if (speaker_names.at(i) == name) {
return i + 1;
}
}
return 0;
};
// fifo queue for scene data arrays
std::queue<int> array_link_sources;
// now add all the scenes inline
for (auto& [name, scene] : bank->scenes) {
gen.add_ref_to_string_in_pool(name); // scene name
gen.add_word(scene.lines.size()); // line amount
array_link_sources.push(gen.words());
gen.add_word(0); // line array (linked later)
}
// now add all the line arrays and link them to their scene
for (auto& [name, scene] : bank->scenes) {
// link inline-array with reference from earlier
gen.link_word_to_word(array_link_sources.front(), gen.words());
array_link_sources.pop();
for (auto& line : scene.lines) {
gen.add_word_float(line.start); // start frame
gen.add_word_float(line.end); // end frame
if (!line.merge) {
gen.add_ref_to_string_in_pool(font->convert_utf8_to_game(line.text)); // line text
} else {
gen.add_symbol_link("#f");
}
u16 speaker = speaker_index_by_name(line.speaker);
u16 flags = 0;
flags |= line.offscreen << 0;
flags |= line.merge << 1;
gen.add_word(speaker | (flags << 16)); // speaker (lo) + flags (hi)
}
}
// now write the array of strings for the speakers
gen.link_word_to_word(speaker_array_link, gen.words());
// we write #f for invalid entries, including the "none" at the start
gen.add_symbol_link("#f");
for (auto& speaker_name : speaker_names) {
if (bank->speakers.count(speaker_name) == 0) {
// no speaker for this
gen.add_symbol_link("#f");
} else {
gen.add_ref_to_string_in_pool(font->convert_utf8_to_game(bank->speakers.at(speaker_name)));
}
}
auto data = gen.generate_v2();
file_util::create_dir_if_needed(file_util::get_file_path({"out", output_prefix, "iso"}));
file_util::write_binary_file(
file_util::get_file_path(
{"out", output_prefix, "iso", fmt::format("{}{}.TXT", lang, uppercase("subti2"))}),
data.data(), data.size());
}
}
} // namespace
/*!
@ -183,3 +260,14 @@ void compile_game_subtitle(const std::vector<GameSubtitleDefinitionFile>& files,
}
compile_subtitle(db, output_prefix);
}
void compile_game_subtitle2(const std::vector<GameSubtitle2DefinitionFile>& files,
GameSubtitle2DB& db,
const std::string& output_prefix) {
goos::Reader reader;
for (auto& file : files) {
lg::print("[Build Game Subtitle] JSON {}\n", file.file_path);
parse_subtitle2_json(db, file);
}
compile_subtitle2(db, output_prefix);
}

View File

@ -6,6 +6,7 @@
#include <unordered_set>
#include "common/serialization/subtitles/subtitles_ser.h"
#include "common/serialization/subtitles2/subtitles2_ser.h"
#include "common/util/Assert.h"
#include "common/util/FontUtils.h"
@ -15,3 +16,6 @@ void compile_game_text(const std::vector<GameTextDefinitionFile>& filenames,
void compile_game_subtitle(const std::vector<GameSubtitleDefinitionFile>& filenames,
GameSubtitleDB& db,
const std::string& output_prefix);
void compile_game_subtitle2(const std::vector<GameSubtitle2DefinitionFile>& filenames,
GameSubtitle2DB& db,
const std::string& output_prefix);

View File

@ -31,7 +31,7 @@ int main(int argc, char** argv) {
std::string cmd = "";
std::string username = "#f";
std::string game = "jak1";
int nrepl_port = 8181;
int nrepl_port = -1;
fs::path project_path_override;
// TODO - a lot of these flags could be deprecated and moved into `repl-config.json`
@ -40,7 +40,8 @@ int main(int argc, char** argv) {
app.add_option("-c,--cmd", cmd, "Specify a command to run, no REPL is launched in this mode");
app.add_option("-u,--user", username,
"Specify the username to use for your user profile in 'goal_src/user/'");
app.add_option("-p,--port", nrepl_port, "Specify the nREPL port. Defaults to 8181");
app.add_option("-p,--port", nrepl_port,
"Specify the nREPL port. Defaults to 8181 for Jak 1 and 8182 for Jak 2");
app.add_flag("--user-auto", auto_find_user,
"Attempt to automatically deduce the user, overrides '--user'");
app.add_option("-g,--game", game, "The game name: 'jak1' or 'jak2'");
@ -50,6 +51,17 @@ int main(int argc, char** argv) {
CLI11_PARSE(app, argc, argv);
GameVersion game_version = game_name_to_version(game);
if (nrepl_port == -1) {
switch (game_version) {
default:
case GameVersion::Jak1:
nrepl_port = 8181;
break;
case GameVersion::Jak2:
nrepl_port = 8182;
break;
}
}
if (!project_path_override.empty()) {
if (!fs::exists(project_path_override)) {

View File

@ -100,6 +100,7 @@ MakeSystem::MakeSystem(const std::optional<REPL::Config> repl_config, const std:
add_tool<GroupTool>();
add_tool<TextTool>();
add_tool<SubtitleTool>();
add_tool<Subtitle2Tool>();
add_tool<BuildLevelTool>();
}

Some files were not shown because too many files have changed in this diff Show More