jak3: speedrunner mode (#3761)
Some checks are pending
Build / 🖥️ Windows (push) Waiting to run
Build / 🐧 Linux (push) Waiting to run
Build / 🍎 MacOS (push) Waiting to run
Inform Pages Repo / Generate Documentation (push) Waiting to run
Lint / 📝 Formatting (push) Waiting to run
Lint / 📝 Required Checks (push) Waiting to run
Lint / 📝 Optional Checks (push) Waiting to run

Base implementation of the popup menu and speedrunner mode in Jak 3.
Autosplitter is untested because I'm on Linux.

Also a couple of other misc changes:

- Model replacements can now have custom bone weights. Needs the "Use
Custom Bone Weights" property (provided by the OpenGOAL Blender plugin)
enabled in Blender.
- Better error message for lump syntax errors in custom level JSON
files.
This commit is contained in:
Hat Kid 2024-11-17 06:45:34 +01:00 committed by GitHub
parent 5e3cb8faa6
commit 7543acfb8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 3400 additions and 49 deletions

View File

@ -90,6 +90,7 @@ def draw_func_ob(self, context):
layout = self.layout
ob = context.object
layout.prop(ob, "set_invisible")
layout.prop(ob, "enable_custom_weights")
layout.prop(ob, "set_collision")
if (ob.set_collision):
layout.prop(ob, "ignore")
@ -116,6 +117,7 @@ def register():
bpy.types.Object.set_invisible = bpy.props.BoolProperty(name="Invisible")
bpy.types.Object.set_collision = bpy.props.BoolProperty(name="Apply Collision Properties")
bpy.types.Object.enable_custom_weights = bpy.props.BoolProperty(name="Use Custom Bone Weights")
bpy.types.Object.ignore = bpy.props.BoolProperty(name="ignore")
bpy.types.Object.noedge = bpy.props.BoolProperty(name="No-Edge")
bpy.types.Object.noentity = bpy.props.BoolProperty(name="No-Entity")

View File

@ -9,7 +9,8 @@ void extract(const std::string& name,
const std::vector<NodeWithTransform>& all_nodes,
u32 index_offset,
u32 vertex_offset,
u32 tex_offset) {
u32 tex_offset,
bool& has_custom_weights) {
ASSERT(out.new_vertices.empty());
std::map<int, tfrag3::MercDraw> draw_by_material;
@ -24,6 +25,8 @@ void extract(const std::string& name,
if (node.mesh >= 0) {
const auto& mesh = model.meshes[node.mesh];
mesh_count++;
has_custom_weights = node.extras.Has("enable_custom_weights") &&
node.extras.Get("enable_custom_weights").Get<int>();
for (const auto& prim : mesh.primitives) {
prim_count++;
// extract index buffer
@ -39,6 +42,21 @@ void extract(const std::string& name,
out.normals.insert(out.normals.end(), verts.normals.begin(), verts.normals.end());
ASSERT(out.new_colors.size() == out.new_vertices.size());
if (prim.attributes.count("JOINTS_0") && prim.attributes.count("WEIGHTS_0")) {
auto joints_and_weights = gltf_util::extract_and_flatten_joints_and_weights(model, prim);
ASSERT(joints_and_weights.size() == verts.vtx.size());
out.joints_and_weights.insert(out.joints_and_weights.end(), joints_and_weights.begin(),
joints_and_weights.end());
} else {
// add fake data for vertices without this data
gltf_util::JointsAndWeights dummy;
dummy.joints[0] = 3;
dummy.weights[0] = 1.f;
for (size_t i = 0; i < out.new_vertices.size(); i++) {
out.joints_and_weights.push_back(dummy);
}
}
// TODO: just putting it all in one material
auto& draw = draw_by_material[prim.material];
draw.mode = gltf_util::make_default_draw_mode(); // todo rm
@ -119,7 +137,8 @@ const tfrag3::MercVertex& find_closest(const std::vector<tfrag3::MercVertex>& ol
void merc_convert_replacement(MercSwapData& out,
const MercExtractData& in,
const std::vector<tfrag3::MercVertex>& old_verts) {
const std::vector<tfrag3::MercVertex>& old_verts,
bool use_custom_weights) {
out.new_model = in.new_model;
out.new_indices = in.new_indices;
out.new_textures = in.tex_pool.textures_by_idx;
@ -127,6 +146,7 @@ void merc_convert_replacement(MercSwapData& out,
// convert vertices
for (size_t i = 0; i < in.new_vertices.size(); i++) {
const auto& y = in.new_vertices[i];
const auto& copy_from = find_closest(old_verts, y.x, y.y, y.z);
auto& x = out.new_vertices.emplace_back();
x.pos[0] = y.x;
@ -135,18 +155,27 @@ void merc_convert_replacement(MercSwapData& out,
x.normal[0] = in.normals.at(i).x();
x.normal[1] = in.normals.at(i).y();
x.normal[2] = in.normals.at(i).z();
x.weights[0] = copy_from.weights[0];
x.weights[1] = copy_from.weights[1];
x.weights[2] = copy_from.weights[2];
if (use_custom_weights) {
x.weights[0] = in.joints_and_weights.at(i).weights[0];
x.weights[1] = in.joints_and_weights.at(i).weights[1];
x.weights[2] = in.joints_and_weights.at(i).weights[2];
x.mats[0] = in.joints_and_weights.at(i).joints[0];
x.mats[1] = in.joints_and_weights.at(i).joints[1];
x.mats[2] = in.joints_and_weights.at(i).joints[2];
} else {
x.weights[0] = copy_from.weights[0];
x.weights[1] = copy_from.weights[1];
x.weights[2] = copy_from.weights[2];
x.mats[0] = copy_from.mats[0];
x.mats[1] = copy_from.mats[1];
x.mats[2] = copy_from.mats[2];
}
x.st[0] = y.s;
x.st[1] = y.t;
x.rgba[0] = in.new_colors[i][0];
x.rgba[1] = in.new_colors[i][1];
x.rgba[2] = in.new_colors[i][2];
x.rgba[3] = in.new_colors[i][3];
x.mats[0] = copy_from.mats[0];
x.mats[1] = copy_from.mats[1];
x.mats[2] = copy_from.mats[2];
}
}
@ -165,18 +194,18 @@ void merc_convert_custom(MercSwapData& out, const MercExtractData& in) {
x.normal[0] = in.normals.at(i).x();
x.normal[1] = in.normals.at(i).y();
x.normal[2] = in.normals.at(i).z();
x.weights[0] = 1.0f;
x.weights[1] = 0.0f;
x.weights[2] = 0.0f;
x.weights[0] = in.joints_and_weights.at(i).weights[0];
x.weights[1] = in.joints_and_weights.at(i).weights[1];
x.weights[2] = in.joints_and_weights.at(i).weights[2];
x.st[0] = y.s;
x.st[1] = y.t;
x.rgba[0] = in.new_colors[i][0];
x.rgba[1] = in.new_colors[i][1];
x.rgba[2] = in.new_colors[i][2];
x.rgba[3] = in.new_colors[i][3];
x.mats[0] = 3;
x.mats[1] = 0;
x.mats[2] = 0;
x.mats[0] = in.joints_and_weights.at(i).joints[0];
x.mats[1] = in.joints_and_weights.at(i).joints[1];
x.mats[2] = in.joints_and_weights.at(i).joints[2];
}
}
@ -199,12 +228,13 @@ MercSwapData load_replacement_merc_model(const std::string& name,
auto all_nodes = flatten_nodes_from_all_scenes(model);
MercExtractData extract_data;
auto has_custom_weights = false;
extract(name, extract_data, model, all_nodes, current_idx_count, current_vtx_count,
current_tex_count);
current_tex_count, has_custom_weights);
if (custom_mdl) {
merc_convert_custom(result, extract_data);
} else {
merc_convert_replacement(result, extract_data, old_verts);
merc_convert_replacement(result, extract_data, old_verts, has_custom_weights);
}
return result;

View File

@ -10,7 +10,7 @@ struct MercExtractData {
std::vector<tfrag3::PreloadedVertex> new_vertices;
std::vector<math::Vector<u8, 4>> new_colors;
std::vector<math::Vector3f> normals;
std::vector<gltf_util::JointsAndWeights> joints_and_weights;
tfrag3::MercModel new_model;
};

View File

@ -11,8 +11,6 @@ void pc_set_levels(u32 lev_list);
void pc_set_active_levels(u32 lev_list);
u32 alloc_vagdir_names(u32 heap_sym);
inline u64 bool_to_symbol(const bool val);
// TODO - move to common
void encode_utf8_string(u32 src_str_ptr, u32 str_dest_ptr);
void init_autosplit_struct();
void callback_fetch_external_speedrun_times(bool success,
const std::string& cache_id,

View File

@ -369,7 +369,8 @@ void InitMachine_PCPort() {
make_function_symbol_from_c("__pc-set-active-levels",
(void*)kmachine_extras::pc_set_active_levels);
make_function_symbol_from_c("__pc-get-tex-remap", (void*)lookup_jak3_texture_dest_offset);
// make_function_symbol_from_c("pc-init-autosplitter-struct", (void*)init_autosplit_struct);
make_function_symbol_from_c("pc-init-autosplitter-struct",
(void*)kmachine_extras::init_autosplit_struct);
// discord rich presence
make_function_symbol_from_c("pc-discord-rpc-update", (void*)kmachine_extras::update_discord_rpc);
@ -395,6 +396,45 @@ void InitMachine_PCPort() {
(void*)pc_get_num_external_highscores);
*/
// speedrunning stuff
make_function_symbol_from_c("pc-sr-mode-get-practice-entries-amount",
(void*)kmachine_extras::pc_sr_mode_get_practice_entries_amount);
make_function_symbol_from_c("pc-sr-mode-get-practice-entry-name",
(void*)kmachine_extras::pc_sr_mode_get_practice_entry_name);
make_function_symbol_from_c("pc-sr-mode-get-practice-entry-continue-point",
(void*)kmachine_extras::pc_sr_mode_get_practice_entry_continue_point);
make_function_symbol_from_c(
"pc-sr-mode-get-practice-entry-history-success",
(void*)kmachine_extras::pc_sr_mode_get_practice_entry_history_success);
make_function_symbol_from_c(
"pc-sr-mode-get-practice-entry-history-attempts",
(void*)kmachine_extras::pc_sr_mode_get_practice_entry_history_attempts);
make_function_symbol_from_c(
"pc-sr-mode-get-practice-entry-session-success",
(void*)kmachine_extras::pc_sr_mode_get_practice_entry_session_success);
make_function_symbol_from_c(
"pc-sr-mode-get-practice-entry-session-attempts",
(void*)kmachine_extras::pc_sr_mode_get_practice_entry_session_attempts);
make_function_symbol_from_c("pc-sr-mode-get-practice-entry-avg-time",
(void*)kmachine_extras::pc_sr_mode_get_practice_entry_avg_time);
make_function_symbol_from_c("pc-sr-mode-get-practice-entry-fastest-time",
(void*)kmachine_extras::pc_sr_mode_get_practice_entry_fastest_time);
make_function_symbol_from_c("pc-sr-mode-record-practice-entry-attempt!",
(void*)kmachine_extras::pc_sr_mode_record_practice_entry_attempt);
make_function_symbol_from_c("pc-sr-mode-init-practice-info!",
(void*)kmachine_extras::pc_sr_mode_init_practice_info);
make_function_symbol_from_c("pc-sr-mode-get-custom-category-amount",
(void*)kmachine_extras::pc_sr_mode_get_custom_category_amount);
make_function_symbol_from_c("pc-sr-mode-get-custom-category-name",
(void*)kmachine_extras::pc_sr_mode_get_custom_category_name);
make_function_symbol_from_c(
"pc-sr-mode-get-custom-category-continue-point",
(void*)kmachine_extras::pc_sr_mode_get_custom_category_continue_point);
make_function_symbol_from_c("pc-sr-mode-init-custom-category-info!",
(void*)kmachine_extras::pc_sr_mode_init_custom_category_info);
make_function_symbol_from_c("pc-sr-mode-dump-new-custom-category",
(void*)kmachine_extras::pc_sr_mode_dump_new_custom_category);
// setup string constants
auto user_dir_path = file_util::get_user_config_dir();
intern_from_c(-1, 0, "*pc-user-dir-base-path*")->value() =

View File

@ -16,7 +16,7 @@
namespace jak3 {
namespace kmachine_extras {
using namespace jak3;
AutoSplitterBlock g_auto_splitter_block_jak3;
void update_discord_rpc(u32 discord_info) {
if (gDiscordRpcEnabled) {
@ -225,5 +225,737 @@ inline bool symbol_to_bool(const u32 symptr) {
return symptr != s7.offset;
}
void init_autosplit_struct() {
g_auto_splitter_block_jak3.pointer_to_symbol =
(u64)g_ee_main_mem + (u64)intern_from_c(-1, 0, "*autosplit-info-jak3*")->value();
}
// TODO - currently using a single mutex for all background task synchronization
std::mutex background_task_lock;
std::string last_rpc_error;
// TODO - add a TTL to this
std::unordered_map<std::string, std::vector<std::pair<std::string, float>>>
external_speedrun_time_cache = {};
std::unordered_map<std::string, std::vector<std::pair<std::string, float>>>
external_race_time_cache = {};
std::unordered_map<std::string, std::vector<std::pair<std::string, float>>>
external_highscores_cache = {};
// clang-format off
// TODO - eventually don't depend on SRC
const std::unordered_map<std::string, std::string> external_speedrun_lookup_urls = {
{"any", "https://www.speedrun.com/api/v1/leaderboards/nj1nww1p/category/9d8p1qkn?embed=players&max=200"},
{"nooob", "https://www.speedrun.com/api/v1/leaderboards/nj1nww1p/category/5dwj0n0k?embed=players&max=200"},
{"allmissions", "https://www.speedrun.com/api/v1/leaderboards/nj1nww1p/category/xd1r98k8?embed=players&max=200"},
{"100", "https://www.speedrun.com/api/v1/leaderboards/nj1nww1p/category/zd30nndn?embed=players&max=200"},
{"anyorbs", "https://www.speedrun.com/api/v1/leaderboards/nj1nww1p/category/jdzw79vd?embed=players&max=200"},
{"anyhero", "https://www.speedrun.com/api/v1/leaderboards/nj1nww1p/category/9kvp50kg?embed=players&max=200"}};
const std::unordered_map<std::string, std::string> external_race_lookup_urls = {
{"time-trial", "https://www.speedrun.com/api/v1/leaderboards/nj1nww1p/level/kwjvyzwg/jdr8onk6?embed=players&max=200"},
{"rally", "https://www.speedrun.com/api/v1/leaderboards/nj1nww1p/level/owo3kyw6/jdr8onk6?embed=players&max=200"}};
const std::unordered_map<std::string, std::string> external_highscores_lookup_urls = {
{"was-pre-game", "https://api.jakspeedruns.workers.dev/v1/highscores/9"},
{"air-time", "https://api.jakspeedruns.workers.dev/v1/highscores/10"},
{"total-air-time", "https://api.jakspeedruns.workers.dev/v1/highscores/11"},
{"jump-distance", "https://api.jakspeedruns.workers.dev/v1/highscores/12"},
{"total-jump-distance", "https://api.jakspeedruns.workers.dev/v1/highscores/13"},
{"roll-count", "https://api.jakspeedruns.workers.dev/v1/highscores/14"},
{"wascity-gungame", "https://api.jakspeedruns.workers.dev/v1/highscores/15"},
{"jetboard", "https://api.jakspeedruns.workers.dev/v1/highscores/16"},
{"gungame-yellow-2", "https://api.jakspeedruns.workers.dev/v1/highscores/17"},
{"gungame-red-2", "https://api.jakspeedruns.workers.dev/v1/highscores/18"},
{"gungame-ratchet", "https://api.jakspeedruns.workers.dev/v1/highscores/19"},
{"gungame-clank", "https://api.jakspeedruns.workers.dev/v1/highscores/20"},
{"power-game", "https://api.jakspeedruns.workers.dev/v1/highscores/21"},
{"destroy-interceptors", "https://api.jakspeedruns.workers.dev/v1/highscores/22"}};
// clang-format on
void callback_fetch_external_speedrun_times(bool success,
const std::string& cache_id,
std::optional<std::string> result) {
std::scoped_lock lock{background_task_lock};
if (!success) {
intern_from_c(-1, 0, "*pc-rpc-error?*")->value() = bool_to_symbol(true);
if (result) {
last_rpc_error = result.value();
} else {
last_rpc_error = "Unexpected Error Occurred";
}
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
return;
}
// TODO - might be nice to have an error if we get an unexpected payload
if (!result) {
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
return;
}
// Parse the response
const auto data = safe_parse_json(result.value());
if (!data || !data->contains("data") || !data->at("data").contains("players") ||
!data->at("data").at("players").contains("data") || !data->at("data").contains("runs")) {
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
return;
}
auto& players = data->at("data").at("players").at("data");
auto& runs = data->at("data").at("runs");
std::vector<std::pair<std::string, float>> times = {};
for (const auto& run_info : runs) {
std::pair<std::string, float> time_info;
if (players.size() > times.size() && players.at(times.size()).contains("names") &&
players.at(times.size()).at("names").contains("international")) {
time_info.first = players.at(times.size()).at("names").at("international");
} else if (players.size() > times.size() && players.at(times.size()).contains("name")) {
time_info.first = players.at(times.size()).at("name");
} else {
time_info.first = "Unknown";
}
if (run_info.contains("run") && run_info.at("run").contains("times") &&
run_info.at("run").at("times").contains("primary_t")) {
time_info.second = run_info.at("run").at("times").at("primary_t");
times.push_back(time_info);
}
}
external_speedrun_time_cache[cache_id] = times;
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
}
// TODO - duplicate code, put it in a function
void callback_fetch_external_race_times(bool success,
const std::string& cache_id,
std::optional<std::string> result) {
std::scoped_lock lock{background_task_lock};
if (!success) {
intern_from_c(-1, 0, "*pc-rpc-error?*")->value() = bool_to_symbol(true);
if (result) {
last_rpc_error = result.value();
} else {
last_rpc_error = "Unexpected Error Occurred";
}
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
return;
}
// TODO - might be nice to have an error if we get an unexpected payload
if (!result) {
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
return;
}
// Parse the response
const auto data = safe_parse_json(result.value());
if (!data || !data->contains("data") || !data->at("data").contains("players") ||
!data->at("data").at("players").contains("data") || !data->at("data").contains("runs")) {
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
return;
}
auto& players = data->at("data").at("players").at("data");
auto& runs = data->at("data").at("runs");
std::vector<std::pair<std::string, float>> times = {};
for (const auto& run_info : runs) {
std::pair<std::string, float> time_info;
if (players.size() > times.size() && players.at(times.size()).contains("names") &&
players.at(times.size()).at("names").contains("international")) {
time_info.first = players.at(times.size()).at("names").at("international");
} else if (players.size() > times.size() && players.at(times.size()).contains("name")) {
time_info.first = players.at(times.size()).at("name");
} else {
time_info.first = "Unknown";
}
if (run_info.contains("run") && run_info.at("run").contains("times") &&
run_info.at("run").at("times").contains("primary_t")) {
time_info.second = run_info.at("run").at("times").at("primary_t");
times.push_back(time_info);
}
}
external_race_time_cache[cache_id] = times;
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
}
// TODO - duplicate code, put it in a function
void callback_fetch_external_highscores(bool success,
const std::string& cache_id,
std::optional<std::string> result) {
std::scoped_lock lock{background_task_lock};
if (!success) {
intern_from_c(-1, 0, "*pc-rpc-error?*")->value() = bool_to_symbol(true);
if (result) {
last_rpc_error = result.value();
} else {
last_rpc_error = "Unexpected Error Occurred";
}
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
return;
}
// TODO - might be nice to have an error if we get an unexpected payload
if (!result) {
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
return;
}
// Parse the response
const auto data = safe_parse_json(result.value());
std::vector<std::pair<std::string, float>> times = {};
for (const auto& highscore_info : data.value()) {
if (highscore_info.contains("playerName") && highscore_info.contains("score")) {
std::pair<std::string, float> time_info;
time_info.first = highscore_info.at("playerName");
time_info.second = highscore_info.at("score");
times.push_back(time_info);
}
}
external_highscores_cache[cache_id] = times;
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(false);
}
void pc_fetch_external_speedrun_times(u32 speedrun_id_ptr) {
std::scoped_lock lock{background_task_lock};
auto speedrun_id = std::string(Ptr<String>(speedrun_id_ptr).c()->data());
if (external_speedrun_lookup_urls.find(speedrun_id) == external_speedrun_lookup_urls.end()) {
lg::error("No URL for speedrun_id: '{}'", speedrun_id);
return;
}
// First check to see if we've already retrieved this info
if (external_speedrun_time_cache.find(speedrun_id) == external_speedrun_time_cache.end()) {
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(true);
intern_from_c(-1, 0, "*pc-rpc-error?*")->value() = bool_to_symbol(false);
// otherwise, hit the URL
WebRequestJobPayload req;
req.callback = callback_fetch_external_speedrun_times;
req.url = external_speedrun_lookup_urls.at(speedrun_id);
req.cache_id = speedrun_id;
g_background_worker.enqueue_webrequest(req);
}
}
void pc_fetch_external_race_times(u32 race_id_ptr) {
std::scoped_lock lock{background_task_lock};
auto race_id = std::string(Ptr<String>(race_id_ptr).c()->data());
if (external_race_lookup_urls.find(race_id) == external_race_lookup_urls.end()) {
lg::error("No URL for race_id: '{}'", race_id);
return;
}
// First check to see if we've already retrieved this info
if (external_race_time_cache.find(race_id) == external_race_time_cache.end()) {
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(true);
intern_from_c(-1, 0, "*pc-rpc-error?*")->value() = bool_to_symbol(false);
// otherwise, hit the URL
WebRequestJobPayload req;
req.callback = callback_fetch_external_race_times;
req.url = external_race_lookup_urls.at(race_id);
req.cache_id = race_id;
g_background_worker.enqueue_webrequest(req);
}
}
void pc_fetch_external_highscores(u32 highscore_id_ptr) {
std::scoped_lock lock{background_task_lock};
auto highscore_id = std::string(Ptr<String>(highscore_id_ptr).c()->data());
if (external_highscores_lookup_urls.find(highscore_id) == external_highscores_lookup_urls.end()) {
lg::error("No URL for highscore_id: '{}'", highscore_id);
return;
}
// First check to see if we've already retrieved this info
if (external_highscores_cache.find(highscore_id) == external_highscores_cache.end()) {
intern_from_c(-1, 0, "*pc-waiting-on-rpc?*")->value() = bool_to_symbol(true);
intern_from_c(-1, 0, "*pc-rpc-error?*")->value() = bool_to_symbol(false);
// otherwise, hit the URL
WebRequestJobPayload req;
req.callback = callback_fetch_external_highscores;
req.url = external_highscores_lookup_urls.at(highscore_id);
req.cache_id = highscore_id;
g_background_worker.enqueue_webrequest(req);
}
}
void pc_get_external_speedrun_time(u32 speedrun_id_ptr,
s32 index,
u32 name_dest_ptr,
u32 time_dest_ptr) {
std::scoped_lock lock{background_task_lock};
auto speedrun_id = std::string(Ptr<String>(speedrun_id_ptr).c()->data());
if (external_speedrun_time_cache.find(speedrun_id) != external_speedrun_time_cache.end()) {
const auto& runs = external_speedrun_time_cache.at(speedrun_id);
if (index < (int)runs.size()) {
const auto& run_info = external_speedrun_time_cache.at(speedrun_id).at(index);
std::string converted =
get_font_bank(GameTextVersion::JAK3)->convert_utf8_to_game(run_info.first);
strcpy(Ptr<String>(name_dest_ptr).c()->data(), converted.c_str());
*(Ptr<float>(time_dest_ptr).c()) = run_info.second;
} else {
std::string converted = get_font_bank(GameTextVersion::JAK3)->convert_utf8_to_game("");
strcpy(Ptr<String>(name_dest_ptr).c()->data(), converted.c_str());
*(Ptr<float>(time_dest_ptr).c()) = -1.0;
}
}
}
void pc_get_external_race_time(u32 race_id_ptr, s32 index, u32 name_dest_ptr, u32 time_dest_ptr) {
std::scoped_lock lock{background_task_lock};
auto race_id = std::string(Ptr<String>(race_id_ptr).c()->data());
if (external_race_time_cache.find(race_id) != external_race_time_cache.end()) {
const auto& runs = external_race_time_cache.at(race_id);
if (index < (int)runs.size()) {
const auto& run_info = external_race_time_cache.at(race_id).at(index);
std::string converted =
get_font_bank(GameTextVersion::JAK3)->convert_utf8_to_game(run_info.first);
strcpy(Ptr<String>(name_dest_ptr).c()->data(), converted.c_str());
*(Ptr<float>(time_dest_ptr).c()) = run_info.second;
} else {
std::string converted = get_font_bank(GameTextVersion::JAK3)->convert_utf8_to_game("");
strcpy(Ptr<String>(name_dest_ptr).c()->data(), converted.c_str());
*(Ptr<float>(time_dest_ptr).c()) = -1.0;
}
}
}
void pc_get_external_highscore(u32 highscore_id_ptr,
s32 index,
u32 name_dest_ptr,
u32 time_dest_ptr) {
std::scoped_lock lock{background_task_lock};
auto highscore_id = std::string(Ptr<String>(highscore_id_ptr).c()->data());
if (external_highscores_cache.find(highscore_id) != external_highscores_cache.end()) {
const auto& runs = external_highscores_cache.at(highscore_id);
if (index < (int)runs.size()) {
const auto& run_info = external_highscores_cache.at(highscore_id).at(index);
std::string converted =
get_font_bank(GameTextVersion::JAK3)->convert_utf8_to_game(run_info.first);
strcpy(Ptr<String>(name_dest_ptr).c()->data(), converted.c_str());
*(Ptr<float>(time_dest_ptr).c()) = run_info.second;
} else {
std::string converted = get_font_bank(GameTextVersion::JAK3)->convert_utf8_to_game("");
strcpy(Ptr<String>(name_dest_ptr).c()->data(), converted.c_str());
*(Ptr<float>(time_dest_ptr).c()) = -1.0;
}
}
}
s32 pc_get_num_external_speedrun_times(u32 speedrun_id_ptr) {
std::scoped_lock lock{background_task_lock};
auto speedrun_id = std::string(Ptr<String>(speedrun_id_ptr).c()->data());
if (external_speedrun_time_cache.find(speedrun_id) != external_speedrun_time_cache.end()) {
return external_speedrun_time_cache.at(speedrun_id).size();
}
return 0;
}
s32 pc_get_num_external_race_times(u32 race_id_ptr) {
std::scoped_lock lock{background_task_lock};
auto race_id = std::string(Ptr<String>(race_id_ptr).c()->data());
if (external_race_time_cache.find(race_id) != external_race_time_cache.end()) {
return external_race_time_cache.at(race_id).size();
}
return 0;
}
s32 pc_get_num_external_highscores(u32 highscore_id_ptr) {
std::scoped_lock lock{background_task_lock};
auto highscore_id = std::string(Ptr<String>(highscore_id_ptr).c()->data());
if (external_highscores_cache.find(highscore_id) != external_highscores_cache.end()) {
return external_highscores_cache.at(highscore_id).size();
}
return 0;
}
void to_json(json& j, const SpeedrunPracticeEntryHistoryAttempt& obj) {
if (obj.time) {
j["time"] = obj.time.value();
} else {
j["time"] = nullptr;
}
}
void from_json(const json& j, SpeedrunPracticeEntryHistoryAttempt& obj) {
if (j["time"].is_null()) {
obj.time = {};
} else {
obj.time = j["time"];
}
}
void to_json(json& j, const SpeedrunPracticeEntry& obj) {
json_serialize(name);
json_serialize(continue_point_name);
json_serialize(flags);
json_serialize(completed_task);
json_serialize(features);
json_serialize(secrets);
json_serialize(vehicles);
json_serialize(starting_position);
json_serialize(starting_rotation);
json_serialize(starting_camera_position);
json_serialize(starting_camera_rotation);
json_serialize(start_zone_v1);
json_serialize(start_zone_v2);
json_serialize_optional(end_zone_v1);
json_serialize_optional(end_zone_v2);
json_serialize_optional(end_task);
json_serialize(history);
}
void from_json(const json& j, SpeedrunPracticeEntry& obj) {
json_deserialize_if_exists(name);
json_deserialize_if_exists(continue_point_name);
json_deserialize_if_exists(flags);
json_deserialize_if_exists(completed_task);
json_deserialize_if_exists(features);
json_deserialize_if_exists(secrets);
json_deserialize_if_exists(vehicles);
json_deserialize_if_exists(starting_position);
json_deserialize_if_exists(starting_rotation);
json_deserialize_if_exists(starting_camera_position);
json_deserialize_if_exists(starting_camera_rotation);
json_deserialize_if_exists(start_zone_v1);
json_deserialize_if_exists(start_zone_v2);
json_deserialize_optional_if_exists(end_zone_v1);
json_deserialize_optional_if_exists(end_zone_v2);
json_deserialize_optional_if_exists(end_task);
json_deserialize_if_exists(history);
}
void to_json(json& j, const SpeedrunCustomCategoryEntry& obj) {
json_serialize(name);
json_serialize(secrets);
json_serialize(features);
json_serialize(vehicles);
json_serialize(forbidden_features);
json_serialize(cheats);
json_serialize(continue_point_name);
json_serialize(completed_task);
}
void from_json(const json& j, SpeedrunCustomCategoryEntry& obj) {
json_deserialize_if_exists(name);
json_deserialize_if_exists(secrets);
json_deserialize_if_exists(features);
json_deserialize_if_exists(vehicles);
json_deserialize_if_exists(forbidden_features);
json_deserialize_if_exists(cheats);
json_deserialize_if_exists(continue_point_name);
json_deserialize_if_exists(completed_task);
}
std::vector<SpeedrunPracticeEntry> g_speedrun_practice_entries;
std::unordered_map<int, SpeedrunPracticeState> g_speedrun_practice_state;
s32 pc_sr_mode_get_practice_entries_amount() {
// load practice entries from the file
const auto file_path =
file_util::get_user_features_dir(g_game_version) / "speedrun-practice.json";
if (!file_util::file_exists(file_path.string())) {
lg::info("speedrun-practice.json not found, no entries to return!");
return 0;
}
const auto file_contents = safe_parse_json(file_util::read_text_file(file_path));
if (!file_contents) {
lg::error("speedrun-practice.json could not be parsed!");
return 0;
}
g_speedrun_practice_entries = *file_contents;
for (size_t i = 0; i < g_speedrun_practice_entries.size(); i++) {
const auto& entry = g_speedrun_practice_entries.at(i);
s32 last_session_id = -1;
s32 total_attempts = 0;
s32 total_successes = 0;
s32 session_attempts = 0;
s32 session_successes = 0;
double total_time = 0;
float average_time = 0;
float fastest_time = 0;
for (const auto& [history_session, times] : entry.history) {
s32 session_id = stoi(history_session);
if (session_id > last_session_id) {
last_session_id = session_id;
}
for (const auto& time : times) {
total_attempts++;
if (time.time) {
total_successes++;
total_time += *time.time;
if (fastest_time == 0 || *time.time < fastest_time) {
fastest_time = *time.time;
}
}
}
}
if (total_successes != 0) {
average_time = total_time / total_successes;
}
g_speedrun_practice_state[i] = {last_session_id + 1, total_attempts, total_successes,
session_attempts, session_successes, total_time,
average_time, fastest_time};
}
return g_speedrun_practice_entries.size();
}
void pc_sr_mode_get_practice_entry_name(s32 entry_index, u32 name_str_ptr) {
std::string name;
if (entry_index < (int)g_speedrun_practice_entries.size()) {
name = g_speedrun_practice_entries.at(entry_index).name;
}
strcpy(Ptr<String>(name_str_ptr).c()->data(), name.c_str());
}
void pc_sr_mode_get_practice_entry_continue_point(s32 entry_index, u32 name_str_ptr) {
std::string name;
if (entry_index < (int)g_speedrun_practice_entries.size()) {
name = g_speedrun_practice_entries.at(entry_index).continue_point_name;
}
strcpy(Ptr<String>(name_str_ptr).c()->data(), name.c_str());
}
s32 pc_sr_mode_get_practice_entry_history_success(s32 entry_index) {
return g_speedrun_practice_state.at(entry_index).total_successes;
}
s32 pc_sr_mode_get_practice_entry_history_attempts(s32 entry_index) {
return g_speedrun_practice_state.at(entry_index).total_attempts;
}
s32 pc_sr_mode_get_practice_entry_session_success(s32 entry_index) {
return g_speedrun_practice_state.at(entry_index).session_successes;
}
s32 pc_sr_mode_get_practice_entry_session_attempts(s32 entry_index) {
return g_speedrun_practice_state.at(entry_index).session_attempts;
}
void pc_sr_mode_get_practice_entry_avg_time(s32 entry_index, u32 time_str_ptr) {
const auto time = fmt::format("{:.2f}", g_speedrun_practice_state.at(entry_index).average_time);
strcpy(Ptr<String>(time_str_ptr).c()->data(), time.c_str());
}
void pc_sr_mode_get_practice_entry_fastest_time(s32 entry_index, u32 time_str_ptr) {
const auto time = fmt::format("{:.2f}", g_speedrun_practice_state.at(entry_index).fastest_time);
strcpy(Ptr<String>(time_str_ptr).c()->data(), time.c_str());
}
u64 pc_sr_mode_record_practice_entry_attempt(s32 entry_index, u32 success_bool, u32 time_ptr) {
auto& state = g_speedrun_practice_state.at(entry_index);
const auto was_successful = symbol_to_bool(success_bool);
state.total_attempts++;
state.session_attempts++;
bool ret = false;
SpeedrunPracticeEntryHistoryAttempt new_history_entry;
if (was_successful) {
auto time = Ptr<float>(time_ptr).c();
new_history_entry.time = *time;
state.total_successes++;
state.session_successes++;
state.total_time += *time;
state.average_time = state.total_time / state.total_successes;
if (*time < state.fastest_time) {
state.fastest_time = *time;
ret = true;
}
}
// persist to file
const auto file_path =
file_util::get_user_features_dir(g_game_version) / "speedrun-practice.json";
if (!file_util::file_exists(file_path.string())) {
lg::info("speedrun-practice.json not found, not persisting!");
} else {
auto& history = g_speedrun_practice_entries.at(entry_index).history;
if (history.find(fmt::format("{}", state.current_session_id)) == history.end()) {
history[fmt::format("{}", state.current_session_id)] = {};
}
history[fmt::format("{}", state.current_session_id)].push_back(new_history_entry);
json data = g_speedrun_practice_entries;
file_util::write_text_file(file_path, data.dump(2));
}
// return
return bool_to_symbol(ret);
}
void pc_sr_mode_init_practice_info(s32 entry_index, u32 speedrun_practice_obj_ptr) {
if (entry_index >= (int)g_speedrun_practice_entries.size()) {
return;
}
auto objective = speedrun_practice_obj_ptr
? Ptr<SpeedrunPracticeObjective>(speedrun_practice_obj_ptr).c()
: NULL;
if (objective) {
const auto& json_info = g_speedrun_practice_entries.at(entry_index);
objective->index = entry_index;
objective->flags = json_info.flags;
objective->completed_task = json_info.completed_task;
objective->features = json_info.features;
objective->vehicles = json_info.vehicles;
objective->secrets = json_info.secrets;
auto starting_position =
objective->starting_position ? Ptr<Vector>(objective->starting_position).c() : NULL;
if (starting_position) {
for (int i = 0; i < 4; i++) {
starting_position->data[i] = json_info.starting_position.at(i) * METER_LENGTH;
}
}
auto starting_rotation =
objective->starting_rotation ? Ptr<Vector>(objective->starting_rotation).c() : NULL;
if (starting_rotation) {
for (int i = 0; i < 4; i++) {
starting_rotation->data[i] = json_info.starting_rotation.at(i);
}
}
auto starting_camera_position = objective->starting_camera_position
? Ptr<Vector>(objective->starting_camera_position).c()
: NULL;
if (starting_camera_position) {
for (int i = 0; i < 4; i++) {
starting_camera_position->data[i] = json_info.starting_camera_position.at(i) * 4096.0;
}
}
auto starting_camera_rotation = objective->starting_camera_rotation
? Ptr<Vector>(objective->starting_camera_rotation).c()
: NULL;
if (starting_camera_rotation) {
for (int i = 0; i < 16; i++) {
starting_camera_rotation->data[i] = json_info.starting_camera_rotation.at(i);
}
}
if (json_info.end_task) {
objective->end_task = *json_info.end_task;
} else {
objective->end_task = 0;
}
auto starting_zone = objective->start_zone_init_params
? Ptr<ObjectiveZoneInitParams>(objective->start_zone_init_params).c()
: NULL;
if (starting_zone) {
starting_zone->v1[0] = json_info.start_zone_v1.at(0) * METER_LENGTH;
starting_zone->v1[1] = json_info.start_zone_v1.at(1) * METER_LENGTH;
starting_zone->v1[2] = json_info.start_zone_v1.at(2) * METER_LENGTH;
starting_zone->v1[3] = json_info.start_zone_v1.at(3) * METER_LENGTH;
starting_zone->v2[0] = json_info.start_zone_v2.at(0) * METER_LENGTH;
starting_zone->v2[1] = json_info.start_zone_v2.at(1) * METER_LENGTH;
starting_zone->v2[2] = json_info.start_zone_v2.at(2) * METER_LENGTH;
starting_zone->v2[3] = json_info.start_zone_v2.at(3) * METER_LENGTH;
}
if (json_info.end_zone_v1 && json_info.end_zone_v2) {
auto ending_zone = objective->end_zone_init_params
? Ptr<ObjectiveZoneInitParams>(objective->end_zone_init_params).c()
: NULL;
if (ending_zone) {
ending_zone->v1[0] = json_info.end_zone_v1->at(0) * METER_LENGTH;
ending_zone->v1[1] = json_info.end_zone_v1->at(1) * METER_LENGTH;
ending_zone->v1[2] = json_info.end_zone_v1->at(2) * METER_LENGTH;
ending_zone->v1[3] = json_info.end_zone_v1->at(3) * METER_LENGTH;
ending_zone->v2[0] = json_info.end_zone_v2->at(0) * METER_LENGTH;
ending_zone->v2[1] = json_info.end_zone_v2->at(1) * METER_LENGTH;
ending_zone->v2[2] = json_info.end_zone_v2->at(2) * METER_LENGTH;
ending_zone->v2[3] = json_info.end_zone_v2->at(3) * METER_LENGTH;
}
}
}
}
std::vector<SpeedrunCustomCategoryEntry> g_speedrun_custom_categories;
s32 pc_sr_mode_get_custom_category_amount() {
// load practice entries from the file
const auto file_path =
file_util::get_user_features_dir(g_game_version) / "speedrun-categories.json";
if (!file_util::file_exists(file_path.string())) {
lg::info("speedrun-categories.json not found, no entries to return!");
return 0;
}
const auto file_contents = safe_parse_json(file_util::read_text_file(file_path));
if (!file_contents) {
lg::error("speedrun-categories.json could not be parsed!");
return 0;
}
g_speedrun_custom_categories = *file_contents;
return g_speedrun_custom_categories.size();
}
void pc_sr_mode_get_custom_category_name(s32 entry_index, u32 name_str_ptr) {
std::string name;
if (entry_index < (int)g_speedrun_custom_categories.size()) {
name = g_speedrun_custom_categories.at(entry_index).name;
}
strcpy(Ptr<String>(name_str_ptr).c()->data(), name.c_str());
}
void pc_sr_mode_get_custom_category_continue_point(s32 entry_index, u32 name_str_ptr) {
std::string name;
if (entry_index < (int)g_speedrun_custom_categories.size()) {
name = g_speedrun_custom_categories.at(entry_index).continue_point_name;
}
strcpy(Ptr<String>(name_str_ptr).c()->data(), name.c_str());
}
void pc_sr_mode_init_custom_category_info(s32 entry_index, u32 speedrun_custom_category_ptr) {
if (entry_index >= (int)g_speedrun_custom_categories.size()) {
return;
}
auto category = speedrun_custom_category_ptr
? Ptr<SpeedrunCustomCategory>(speedrun_custom_category_ptr).c()
: NULL;
if (category) {
const auto& json_info = g_speedrun_custom_categories.at(entry_index);
category->index = entry_index;
category->secrets = json_info.secrets;
category->features = json_info.features;
category->vehicles = json_info.vehicles;
category->forbidden_features = json_info.forbidden_features;
category->cheats = json_info.cheats;
category->completed_task = json_info.completed_task;
}
}
void pc_sr_mode_dump_new_custom_category(u32 speedrun_custom_category_ptr) {
const auto file_path =
file_util::get_user_features_dir(g_game_version) / "speedrun-categories.json";
if (file_util::file_exists(file_path.string())) {
// read current categories from file
const auto file_contents = safe_parse_json(file_util::read_text_file(file_path));
if (file_contents) {
g_speedrun_custom_categories = *file_contents;
}
}
auto category = speedrun_custom_category_ptr
? Ptr<SpeedrunCustomCategory>(speedrun_custom_category_ptr).c()
: NULL;
if (category) {
SpeedrunCustomCategoryEntry new_category;
new_category.name = fmt::format("custom-category-{}", g_speedrun_custom_categories.size());
new_category.secrets = category->secrets;
new_category.features = category->features;
new_category.vehicles = category->vehicles;
new_category.forbidden_features = category->forbidden_features;
new_category.cheats = category->cheats;
new_category.completed_task = category->completed_task;
new_category.continue_point_name = "";
g_speedrun_custom_categories.push_back(new_category);
// convert to json and write file
json data = g_speedrun_custom_categories;
file_util::write_text_file(file_path, data.dump(2));
}
}
} // namespace kmachine_extras
} // namespace jak3

View File

@ -12,8 +12,47 @@ void pc_set_levels(u32 lev_list);
void pc_set_active_levels(u32 lev_list);
u32 alloc_vagdir_names(u32 heap_sym);
inline u64 bool_to_symbol(const bool val);
// TODO - move to common
void encode_utf8_string(u32 src_str_ptr, u32 str_dest_ptr);
void init_autosplit_struct();
void callback_fetch_external_speedrun_times(bool success,
const std::string& cache_id,
std::optional<std::string> result);
void callback_fetch_external_race_times(bool success,
const std::string& cache_id,
std::optional<std::string> result);
void callback_fetch_external_highscores(bool success,
const std::string& cache_id,
std::optional<std::string> result);
void pc_fetch_external_speedrun_times(u32 speedrun_id_ptr);
void pc_fetch_external_race_times(u32 race_id_ptr);
void pc_fetch_external_highscores(u32 highscore_id_ptr);
void pc_get_external_speedrun_time(u32 speedrun_id_ptr,
s32 index,
u32 name_dest_ptr,
u32 time_dest_ptr);
void pc_get_external_race_time(u32 race_id_ptr, s32 index, u32 name_dest_ptr, u32 time_dest_ptr);
void pc_get_external_highscore(u32 highscore_id_ptr,
s32 index,
u32 name_dest_ptr,
u32 time_dest_ptr);
s32 pc_get_num_external_speedrun_times(u32 speedrun_id_ptr);
s32 pc_get_num_external_race_times(u32 race_id_ptr);
s32 pc_get_num_external_highscores(u32 highscore_id_ptr);
s32 pc_sr_mode_get_practice_entries_amount();
void pc_sr_mode_get_practice_entry_name(s32 entry_index, u32 name_str_ptr);
void pc_sr_mode_get_practice_entry_continue_point(s32 entry_index, u32 name_str_ptr);
s32 pc_sr_mode_get_practice_entry_history_success(s32 entry_index);
s32 pc_sr_mode_get_practice_entry_history_attempts(s32 entry_index);
s32 pc_sr_mode_get_practice_entry_session_success(s32 entry_index);
s32 pc_sr_mode_get_practice_entry_session_attempts(s32 entry_index);
void pc_sr_mode_get_practice_entry_avg_time(s32 entry_index, u32 time_str_ptr);
void pc_sr_mode_get_practice_entry_fastest_time(s32 entry_index, u32 time_str_ptr);
u64 pc_sr_mode_record_practice_entry_attempt(s32 entry_index, u32 success_bool, u32 time);
void pc_sr_mode_init_practice_info(s32 entry_index, u32 speedrun_practice_obj_ptr);
s32 pc_sr_mode_get_custom_category_amount();
void pc_sr_mode_get_custom_category_name(s32 entry_index, u32 name_str_ptr);
void pc_sr_mode_get_custom_category_continue_point(s32 entry_index, u32 name_str_ptr);
void pc_sr_mode_init_custom_category_info(s32 entry_index, u32 speedrun_custom_category_ptr);
void pc_sr_mode_dump_new_custom_category(u32 speedrun_custom_category_ptr);
struct DiscordInfo {
float orb_count; // float
@ -113,5 +152,110 @@ enum class FocusStatus : u64 {
#define FOCUS_TEST(status, foc) (status.test(static_cast<size_t>(foc)))
// To speed up finding the auto-splitter block in GOAL memory
// all this has is a marker for LiveSplit to find, and then the pointer
// to the symbol
struct AutoSplitterBlock {
const char marker[20] = "UnLiStEdStRaTs_JaK3";
u64 pointer_to_symbol = 0;
};
extern AutoSplitterBlock g_auto_splitter_block_jak3;
struct SpeedrunPracticeEntryHistoryAttempt {
std::optional<float> time;
};
void to_json(json& j, const SpeedrunPracticeEntryHistoryAttempt& obj);
void from_json(const json& j, SpeedrunPracticeEntryHistoryAttempt& obj);
struct SpeedrunPracticeEntry {
std::string name;
std::string continue_point_name;
u64 flags;
u64 completed_task;
u64 features;
u64 secrets;
u64 vehicles;
std::vector<float> starting_position;
std::vector<float> starting_rotation;
std::vector<float> starting_camera_position;
std::vector<float> starting_camera_rotation;
std::vector<float> start_zone_v1;
std::vector<float> start_zone_v2;
std::optional<std::vector<float>> end_zone_v1;
std::optional<std::vector<float>> end_zone_v2;
std::optional<u64> end_task;
std::map<std::string, std::vector<SpeedrunPracticeEntryHistoryAttempt>> history;
};
void to_json(json& j, const SpeedrunPracticeEntry& obj);
void from_json(const json& j, SpeedrunPracticeEntry& obj);
struct SpeedrunPracticeState {
s32 current_session_id;
s32 total_attempts;
s32 total_successes;
s32 session_attempts;
s32 session_successes;
double total_time;
float average_time;
float fastest_time;
};
struct ObjectiveZoneInitParams {
float v1[4];
float v2[4];
};
struct Vector {
float data[4];
};
struct Matrix {
float data[16];
};
struct SpeedrunPracticeObjective {
s32 index;
u8 pad1[4];
u64 flags;
u8 completed_task;
u8 pad2[7];
u64 features;
u64 secrets;
u64 vehicles;
u32 starting_position; // Vector
u32 starting_rotation; // Vector
u32 starting_camera_position; // Vector
u32 starting_camera_rotation; // Matrix
u8 end_task;
u32 start_zone_init_params; // ObjectiveZoneInitParams
u32 start_zone; // irrelevant for cpp
u32 end_zone_init_params; // ObjectiveZoneInitParams
u32 end_zone; // irrelevant for cpp
};
struct SpeedrunCustomCategoryEntry {
std::string name;
u64 secrets;
u64 features;
u64 vehicles;
u64 forbidden_features;
u64 cheats;
std::string continue_point_name;
u64 completed_task;
};
void to_json(json& j, const SpeedrunCustomCategoryEntry& obj);
void from_json(const json& j, SpeedrunCustomCategoryEntry& obj);
struct SpeedrunCustomCategory {
s32 index;
u64 secrets;
u64 features;
u64 vehicles;
u64 forbidden_features;
u64 cheats;
u8 completed_task;
};
} // namespace kmachine_extras
} // namespace jak3

View File

@ -414,11 +414,11 @@ void SubtitleEditor::draw_subtitle_options(GameSubtitleSceneInfo& scene, bool cu
play = true;
save_and_reload_text = true;
}
if (save_and_reload_text) {
m_subtitle_db.write_subtitle_db_to_files(g_game_version);
m_repl.rebuild_text();
}
if (play) {
if (save_and_reload_text) {
m_subtitle_db.write_subtitle_db_to_files(g_game_version);
m_repl.rebuild_text();
}
if (g_game_version == GameVersion::Jak1) {
m_jak1_editor_db.update();
if (scene.is_cutscene) {

View File

@ -240,6 +240,10 @@
"game-task.o"
"game-save.o"
"settings.o"
"autosplit-h.o" ;; added
"autosplit.o" ;; added
"popup-menu-h.o" ;; added
"speedruns-h.o" ;; added
"mood-tables.o"
"mood-tables2.o"
"mood.o"
@ -342,6 +346,8 @@
"board-states.o"
"mech-h.o"
"menu.o"
"popup-menu.o" ;; added
"speedruns.o" ;; added
"drawable.o"
"drawable-group.o"
"drawable-inline-array.o"

View File

@ -260,7 +260,9 @@
)
)
(('menu)
(set-master-mode (cond
;; og:preserve-this Let the popup menu code handle inputs instead of the code written for the original debug menu
(when (not *popup-menu-open*)
(set-master-mode (cond
((and *debug-segment* (cpad-hold? 0 l3) (cpad-pressed? 0 select start))
'menu
)
@ -280,7 +282,7 @@
*master-mode*
)
)
)
))
(set! *pause-lock* #f)
)
(('pause)
@ -1508,10 +1510,12 @@
; (draw-color-bars *blit-displays-work*)
; )
;; run debug menu
(*menu-hook*)
;; draw and update menus
;; og:preserve-this Let the popup menu code handle inputs instead of the code written for the original debug menu
(when (not *popup-menu-open*)
(*menu-hook*))
; load the right language file.
;; disabled for now: seems to load 255COMMON.TXT.
(load-level-text-files -1)
;; draw screen filter

View File

@ -549,7 +549,9 @@
)
)
(if (-> arg0 sky)
(set! (-> (&-> *level* level-default texture-anim-array 9) 0) *sky-texture-anim-array*)
;; og:preserve-this
(set! (-> (&-> *level* level-default texture-anim-array 9) 0) (#if PC_PORT (if *hires-sky* *sky-hires-texture-anim-array* *sky-texture-anim-array*)
*sky-texture-anim-array*))
(set! (-> (&-> *level* level-default texture-anim-array 9) 0) #f)
)
(let ((s5-2 (level-get-target-inside *level*)))

View File

@ -419,9 +419,614 @@
)
)
;; og:preserve-this
(#when PC_PORT
(define *sky-hires-texture-anim-array*
(the (texture-anim-array texture-anim)
(new 'static 'texture-anim-array :type texture-anim
(new 'static 'texture-anim
:func-id 'texture-anim-alpha-ramp-clut-upload
:init-func-id 'texture-anim-alpha-ramp-clut-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 24.0)
:color (new 'static 'rgba :a #x80)
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 0)
)
(new 'static 'texture-anim
:num-layers #x2
:func-id 'cloud-texture-anim-func
:init-func-id 'dest-texture-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 16.0 :y 4.0)
:color (new 'static 'rgba :a #x80)
:frame-delta 300.0
:frame-mod 9600.0
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 16.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 9600.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 16.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 9600.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
)
)
(new 'static 'texture-anim
:num-layers #x2
:func-id 'cloud-texture-anim-func
:init-func-id 'dest-texture-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 32.0 :y 5.0)
:color (new 'static 'rgba :a #x80)
:frame-delta 300.0
:frame-mod 4800.0
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 32.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 4800.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 32.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 4800.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
)
)
(new 'static 'texture-anim
:num-layers #x2
:func-id 'cloud-texture-anim-func
:init-func-id 'dest-texture-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 64.0 :y 6.0)
:color (new 'static 'rgba :a #x80)
:frame-delta 300.0
:frame-mod 2400.0
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 64.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 2400.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 64.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 2400.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
)
)
(new 'static 'texture-anim
:num-layers #x2
:func-id 'cloud-texture-anim-func
:init-func-id 'dest-texture-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 128.0 :y 8.0)
:color (new 'static 'rgba :a #x80)
:frame-delta 300.0
:frame-mod 1200.0
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 128.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 1200.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 128.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 1200.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
)
)
(new 'static 'texture-anim
:num-layers #x2
:func-id 'cloud-texture-anim-func
:init-func-id 'dest-texture-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 256.0 :y 8.0)
:color (new 'static 'rgba :a #x80)
:frame-delta 300.0
:frame-mod 600.0
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 256.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 600.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 256.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 600.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
)
)
(new 'static 'texture-anim
:num-layers #x2
:func-id 'cloud-texture-anim-func
:init-func-id 'dest-texture-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 512.0 :y 8.0)
:color (new 'static 'rgba :a #x80)
:frame-delta 300.0
:frame-mod 450.0
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 512.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 450.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 512.0 :y 16.0 :z 24.0)
:func-id 'cloud-texture-anim-layer-func
:init-func-id 'noise-texture-init
:tex #f
:end-time 450.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
)
)
(new 'static 'texture-anim
:num-layers 6
:func #f
:init-func-id 'dest-texture-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 512.0 :y 16.0)
:color (new 'static 'rgba :a #x80)
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 6
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 16.0 :y 4.0)
:func-id 'default-texture-anim-layer-func
:init-func-id 'src-texture-init
:tex #f
:end-time 300.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 0.49)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 32.0 :y 5.0)
:func-id 'default-texture-anim-layer-func
:init-func-id 'src-texture-init
:tex #f
:end-time 300.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 0.19)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 64.0 :y 6.0)
:func-id 'default-texture-anim-layer-func
:init-func-id 'src-texture-init
:tex #f
:end-time 300.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 0.145)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 128.0 :y 8.0)
:func-id 'default-texture-anim-layer-func
:init-func-id 'src-texture-init
:tex #f
:end-time 300.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 0.015)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 256.0 :y 8.0)
:func-id 'default-texture-anim-layer-func
:init-func-id 'src-texture-init
:tex #f
:end-time 300.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 0.01)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 512.0 :y 8.0)
:func-id 'default-texture-anim-layer-func
:init-func-id 'src-texture-init
:tex #f
:end-time 300.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x3 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x2 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 0.0075)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
)
)
(new 'static 'texture-anim
:num-layers #x1
:func #f
:init-func-id 'dest-texture-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 512.0 :y 8.0)
:color (new 'static 'rgba :a #x80)
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2
(new 'static 'texture-anim-layer
:extra (new 'static 'vector :x 512.0 :y 16.0)
:func-id 'move-rg-to-ba-texture-anim-layer-func
:init-func-id 'src-texture-init
:tex #f
:end-time 300.0
:tex-name #f
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:start-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:start-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:start-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:start-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-color (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
:end-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-st-scale (new 'static 'vector2 :data (new 'static 'array float 2 1.0 1.0))
:end-st-offset (new 'static 'vector2 :data (new 'static 'array float 2 0.5 0.5))
:end-qs (new 'static 'vector :x 1.0 :y 1.0 :z 1.0 :w 1.0)
)
)
)
(new 'static 'texture-anim
:func-id 'texture-anim-cloud-clut-upload
:init-func-id 'texture-anim-cloud-clut-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 24.0 :y 0.5 :z 1.0)
:color (new 'static 'rgba :a #x80)
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2)
)
(new 'static 'texture-anim
:func-id 'fog-texture-anim-func
:init-func-id 'fog-texture-anim-init
:tex #f
:tex-name #f
:extra (new 'static 'vector :x 4.0 :y 6.0 :z 122880.0)
:color (new 'static 'rgba :a #x80)
:test (new 'static 'gs-test :ate #x1 :afail #x1 :zte #x1 :ztst (gs-ztest always))
:alpha (new 'static 'gs-alpha :b #x1 :d #x1)
:data (new 'static 'array texture-anim-layer 2)
)
)
)
)
(defun set-layer-scale! ((n int) (val float))
"set the scale of noise layer `n` in the hires sky anim"
(set! (-> *sky-hires-texture-anim-array* array-data 7 data n start-color w) val)
)
(defun set-layer-update-time! ((n int) (val float))
"set the update time of noise layer `n` in the hires sky anim"
(set! (-> *sky-hires-texture-anim-array* array-data (1+ n) frame-mod) val)
(set! (-> *sky-hires-texture-anim-array* array-data (1+ n) data 0 end-time) val)
(set! (-> *sky-hires-texture-anim-array* array-data (1+ n) data 1 end-time) val)
)
(set-layer-scale! 0 0.49)
(set-layer-scale! 1 0.19)
(set-layer-scale! 2 0.145)
(set-layer-scale! 3 0.015)
(set-layer-scale! 4 0.01)
(set-layer-scale! 5 0.0075)
(set-layer-update-time! 0 (fsec 16))
(set-layer-update-time! 1 (fsec 8))
(set-layer-update-time! 2 (fsec 6))
(set-layer-update-time! 3 (fsec 4))
(set-layer-update-time! 4 (fsec 3))
(set-layer-update-time! 5 (fsec 2))
)
;; WARN: Return type mismatch float vs none.
(defun set-fog-height! ((arg0 float))
(set! (-> *sky-texture-anim-array* array-data 8 extra z) arg0)
;; og:preserve-this
(#when PC_PORT
(set! (-> (the (array texture-anim) *sky-hires-texture-anim-array*) 10 extra z) arg0))
(none)
)
@ -429,6 +1034,10 @@
(defun set-cloud-minmax! ((arg0 float) (arg1 float))
(set! (-> *sky-texture-anim-array* array-data 7 extra y) arg0)
(set! (-> *sky-texture-anim-array* array-data 7 extra z) arg1)
;; og:preserve-this
(#when PC_PORT
(set! (-> *sky-hires-texture-anim-array* array-data 9 extra y) arg0)
(set! (-> *sky-hires-texture-anim-array* array-data 9 extra z) arg1))
(none)
)

View File

@ -361,6 +361,9 @@
)
(define-extern *sky-texture-anim-array* (texture-anim-array texture-anim))
;; og:preserve-this
(#when PC_PORT
(define-extern *sky-hires-texture-anim-array* (texture-anim-array texture-anim)))
(define-extern *darkjak-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *darkjak-highres-texture-anim-array* (texture-anim-array texture-anim))
(define-extern *skull-gem-texture-anim-array* (texture-anim-array texture-anim))
@ -430,6 +433,22 @@
)
)
(defun make-sky-hires-input ((si sky-input))
(set! (-> si fog-height) (-> (the (array texture-anim) *sky-hires-texture-anim-array*) 10 extra z))
(set! (-> si cloud-min) (-> *sky-hires-texture-anim-array* array-data 9 extra y))
(set! (-> si cloud-max) (-> *sky-hires-texture-anim-array* array-data 9 extra z))
(set! (-> si cloud-dest) (the int (-> *sky-hires-texture-anim-array* array-data 8 tex dest 0)))
(dotimes (i (-> *sky-hires-texture-anim-array* array-data 7 num-layers))
(set! (-> si scales i) (-> *sky-hires-texture-anim-array* array-data 7 data i start-color w))
(set! (-> si max-times i) (-> *sky-hires-texture-anim-array* array-data (1+ i) frame-mod))
)
(dotimes (i 11)
(set! (-> si times i)
(-> *sky-hires-texture-anim-array* array-data i frame-time)
)
)
)
(defmacro print-anim (&rest args)
;`(format 0 ,@args)
0
@ -499,6 +518,29 @@
(set! anim-idx 8) ;; fog
;; (return #f)
)
((*sky-hires-texture-anim-array*)
(when (= bucket (bucket-id tex-lcom-sky-post))
;; skip. I believe this is only used to generate the envmap texture for the ocean.
;; it generates the exact same thing, so if we want this on PC one day, we can just
;; steal if from the beginning of the frame.
(return #f)
)
(with-dma-buffer-add-bucket ((dma-buf (-> *display* frames (-> *display* on-screen) global-buf))
bucket
)
(pc-texture-anim-flag start-anim-array dma-buf)
(pc-texture-anim-flag clouds-hires dma-buf :qwc (/ (psize-of sky-input) 16))
(make-sky-hires-input (the sky-input (-> dma-buf base)))
(&+! (-> dma-buf base) (psize-of sky-input))
(pc-texture-anim-flag finish-anim-array dma-buf)
(dotimes (i 10) ;; intentially skipping fog here!!
(pc-update-anim-frame-time (-> *sky-hires-texture-anim-array* array-data i))
)
)
;; falling through on purpose
(set! anim-idx 10) ;; fog
; (return #f)
)
((*darkjak-texture-anim-array*)
(pc-clut-blender bucket (texture-anim-pc darkjak) anim-array)
(return #f)

View File

@ -18,6 +18,8 @@
)
(init! *sky-texture-anim-array*)
;; og:preserve-this
(init! *sky-hires-texture-anim-array*)
(init! *darkjak-texture-anim-array*)
(init! *skull-gem-texture-anim-array*)
(init! *default-water-texture-anim-array*)

View File

@ -1383,3 +1383,18 @@
(define *common-text-heap* (new 'global 'kheap))
(define *common-text* (the-as game-text-info #f))
;; og:preserve-this
;; NOTE - PC PORT difference
;; Partial translations are a thing that we should support. Parts of translating the game are intentionally made
;; difficult for normal translators due to not wanting to make all the strings public (or in the case of subtitles,
;; we straight up didn't have them yet)
;;
;; So to remedy this, we always load the english text as a fallback so that if there is only a partial translation
;; the UX won't be horrible with everything displaying as UNKNOWN.
;;
;; One of the reasons we didn't do this is because it makes it obvious which strings are remaining,
;; but there are better ways to keep track or check if strings are missing.
(#when PC_PORT
(kheap-alloc (define *fallback-text-heap* (new 'global 'kheap)) (* 80 1024))
(define *fallback-text* (the game-text-info #f)))

View File

@ -194,8 +194,13 @@
(the-as string #f)
)
(else
(format (clear *temp-string*) "UNKNOWN ID ~X" arg0)
*temp-string*
;; og:preserve-this Added fallback to English if string is not found.
(#if PC_PORT
(if *fallback-text-lookup?*
(aif (lookup-text! *fallback-text* arg0 #t)
it
(string-format "UNKNOWN ID ~D" arg0)))
(string-format "UNKNOWN ID ~D" arg0))
)
)
)
@ -301,12 +306,14 @@
)
(defun load-level-text-files ((arg0 int))
(if (or *level-text-file-load-flag* (>= arg0 0))
(load-game-text-info "common" (&-> '*common-text* value) *common-text-heap*)
)
0
(none)
)
;; og:preserve-this Load in English file to use as a fallback
(when (or *level-text-file-load-flag* (>= arg0 0))
(load-game-text-info "common" (&-> '*common-text* value) *common-text-heap*)
(#when PC_PORT
(protect ((-> *setting-control* user-current language))
(set! (-> *setting-control* user-current language) (language-enum english))
(load-game-text-info "common" (&-> '*fallback-text* value) *fallback-text-heap*))))
(none))
(defun draw-debug-text-box ((arg0 font-context))
(when *cheat-mode*

View File

@ -249,6 +249,7 @@
;; Jak 3 Specific Kernel Definitions
(define *pc-waiting-on-rpc?* symbol)
(define *pc-rpc-error?* symbol)
(define-extern pc-get-last-rpc-error (function string none))
(define-extern pc-fetch-external-race-times (function string none))
(define-extern pc-fetch-external-speedrun-times (function string none))
@ -260,6 +261,27 @@
(define-extern pc-get-num-external-speedrun-times (function string int))
(define-extern pc-get-num-external-highscores (function string int))
;; Speedrunner Mode Stuff
(define-extern pc-sr-mode-get-practice-entries-amount (function int))
(define-extern pc-sr-mode-get-practice-entry-name (function int string none))
(define-extern pc-sr-mode-get-practice-entry-continue-point (function int string none))
(define-extern pc-sr-mode-get-practice-entry-history-success (function int int))
(define-extern pc-sr-mode-get-practice-entry-history-attempts (function int int))
(define-extern pc-sr-mode-get-practice-entry-session-success (function int int))
(define-extern pc-sr-mode-get-practice-entry-session-attempts (function int int))
(define-extern pc-sr-mode-get-practice-entry-avg-time (function int string none))
(define-extern pc-sr-mode-get-practice-entry-fastest-time (function int string none))
(define-extern pc-sr-mode-record-practice-entry-attempt! (function int symbol (pointer float) symbol))
(declare-type speedrun-practice-objective structure)
(define-extern pc-sr-mode-init-practice-info! (function int speedrun-practice-objective none))
;; TODO - a menu to dump out the 3 numbers with a pre-generated name to the file
(define-extern pc-sr-mode-get-custom-category-amount (function int))
(define-extern pc-sr-mode-get-custom-category-name (function int string none))
(define-extern pc-sr-mode-get-custom-category-continue-point (function int string none))
(declare-type speedrun-custom-category structure)
(define-extern pc-sr-mode-init-custom-category-info! (function int speedrun-custom-category none))
(define-extern pc-sr-mode-dump-new-custom-category (function speedrun-custom-category none))
(define-extern file-stream-open (function file-stream string symbol file-stream))
(define-extern file-stream-close (function file-stream file-stream))
(define-extern file-stream-length (function file-stream int))

View File

@ -815,8 +815,18 @@
(define *temp-string* (new 'global 'string 2048 (the-as string #f)))
;; og:preserve-this
(define *temp-string2* (new 'global 'string 2048 (the string #f)))
(defconstant EMPTY_STRING "")
(#when PC_PORT (define *pc-encoded-temp-string* (new 'global 'string 2048 (the-as string #f))))
(#when PC_PORT
(define *pc-cpp-temp-string*
"A convenient place to retrieve a string from C++"
(new 'global 'string 2048 (the string #f))))
(kmemclose)
(defmacro string-format (&rest args)

View File

@ -2603,6 +2603,9 @@
:trans (behavior ()
(cond
((= (-> self hit-points) 0.0)
;; og:preserve-this
(#when PC_PORT
(set! (-> *autosplit-info-jak3* errol-dead?) 1))
(let ((a1-0 (new 'stack-no-clear 'event-message-block)))
(set! (-> a1-0 from) (process->ppointer self))
(set! (-> a1-0 num-params) 0)

View File

@ -0,0 +1,164 @@
;;-*-Lisp-*-
(in-package goal)
;; LiveSplit ASL requires all settings to initalized _before_ you connect the process
;; Therefore everything has to be laid out in a predictable fashion before hand
;; So this is a lot of hard-coding, but not too bad when just copied from the debug menu code
;;
;; DO NOT change the order, appending to the end is safe!
(deftype autosplit-info (structure)
(;; Version Info
(version-major uint16)
(version-minor uint16)
;; General stats
(num-orbs uint32)
(num-skullgems uint32)
(errol-dead? uint8)
(all-collectables-acquired? uint8)
(padding-stats uint8 198) ;; padding for future growth
;; loading/cutscene/control related info
(game-hash uint32)
(in-cutscene? uint8)
(is-loading? uint8)
(padding-controls uint8 200) ;; padding for future growth
;; need-resolution tasks
(res-arena-training-1 uint8)
(res-arena-fight-1 uint8)
(res-wascity-chase uint8)
(res-wascity-pre-game uint8)
(res-desert-turtle-training uint8)
(res-desert-course-race uint8)
(res-desert-artifact-race-1 uint8)
(res-wascity-leaper-race uint8)
(res-desert-hover uint8)
(res-arena-fight-2 uint8)
(res-desert-catch-lizards uint8)
(res-desert-rescue uint8)
(res-wascity-gungame uint8)
(res-arena-fight-3 uint8)
(res-nest-eggs uint8)
(res-temple-climb uint8)
(res-desert-glide uint8)
(res-volcano-darkeco uint8)
(res-temple-oracle uint8)
(res-desert-oasis-defense uint8)
(res-temple-tests uint8)
(res-comb-travel uint8)
(res-mine-explore uint8)
(res-mine-blow uint8)
(res-mine-boss uint8)
(res-sewer-met-hum uint8)
(res-city-vehicle-training uint8)
(res-city-port-fight uint8)
(res-city-port-attack uint8)
(res-city-gun-course-1 uint8)
(res-city-sniper-fight uint8)
(res-sewer-kg-met uint8)
(res-city-destroy-darkeco uint8)
(res-forest-kill-plants uint8)
(res-city-destroy-grid uint8)
(res-city-hijack-vehicle uint8)
(res-city-port-assault uint8)
(res-city-gun-course-2 uint8)
(res-city-blow-barricade uint8)
(res-city-protect-hq uint8)
(res-sewer-hum-kg uint8)
(res-city-power-game uint8)
(res-desert-artifact-race-2 uint8)
(res-nest-hunt uint8)
(res-desert-beast-battle uint8)
(res-desert-jump-mission uint8)
(res-desert-chase-marauders uint8)
(res-forest-ring-chase uint8)
(res-factory-sky-battle uint8)
(res-factory-assault uint8)
(res-factory-boss uint8)
(res-temple-defend uint8)
(res-wascity-defend uint8)
(res-forest-turn-on-machine uint8)
(res-precursor-tour uint8)
(res-city-blow-tower uint8)
(res-tower-destroy uint8)
(res-palace-ruins-patrol uint8)
(res-palace-ruins-attack uint8)
(res-comb-wild-ride uint8)
(res-precursor-destroy-ship uint8)
(res-desert-final-boss uint8)
(res-city-win uint8)
(res-desert-bbush-get-to-1 uint8)
(res-desert-bbush-get-to-2 uint8)
(res-desert-bbush-get-to-3 uint8)
(res-desert-bbush-get-to-4 uint8)
(res-desert-bbush-get-to-5 uint8)
(res-desert-bbush-get-to-6 uint8)
(res-desert-bbush-get-to-7 uint8)
(res-desert-bbush-get-to-8 uint8)
(res-desert-bbush-get-to-9 uint8)
(res-desert-bbush-get-to-11 uint8)
(res-desert-bbush-get-to-12 uint8)
(res-desert-bbush-get-to-14 uint8)
(res-desert-bbush-get-to-16 uint8)
(res-desert-bbush-get-to-17 uint8)
(res-wascity-bbush-get-to-18 uint8)
(res-desert-bbush-get-to-19 uint8)
(res-wascity-bbush-get-to-20 uint8)
(res-wascity-bbush-get-to-21 uint8)
(res-wascity-bbush-get-to-22 uint8)
(res-wascity-bbush-get-to-23 uint8)
(res-wascity-bbush-get-to-24 uint8)
(res-wascity-bbush-get-to-25 uint8)
(res-city-bbush-get-to-26 uint8)
(res-city-bbush-get-to-27 uint8)
(res-city-bbush-get-to-28 uint8)
(res-city-bbush-get-to-29 uint8)
(res-city-bbush-get-to-30 uint8)
(res-city-bbush-get-to-31 uint8)
(res-city-bbush-get-to-32 uint8)
(res-city-bbush-get-to-33 uint8)
(res-city-bbush-get-to-34 uint8)
(res-city-bbush-get-to-35 uint8)
(res-city-bbush-get-to-36 uint8)
(res-city-bbush-get-to-37 uint8)
(res-city-bbush-get-to-38 uint8)
(res-city-bbush-get-to-39 uint8)
(res-city-bbush-get-to-40 uint8)
(res-city-bbush-get-to-41 uint8)
(res-city-bbush-get-to-42 uint8)
(res-city-bbush-get-to-43 uint8)
(res-city-bbush-get-to-44 uint8)
(res-desert-bbush-ring-1 uint8)
(res-desert-bbush-ring-2 uint8)
(res-wascity-bbush-ring-3 uint8)
(res-wascity-bbush-ring-4 uint8)
(res-city-bbush-ring-5 uint8)
(res-city-bbush-ring-6 uint8)
(res-desert-bbush-egg-spider-1 uint8)
(res-desert-bbush-spirit-chase-1 uint8)
(res-wascity-bbush-spirit-chase-2 uint8)
(res-city-bbush-spirit-chase-3 uint8)
(res-desert-bbush-timer-chase-1 uint8)
(res-wascity-bbush-timer-chase-2 uint8)
(res-desert-bbush-air-time uint8)
(res-desert-bbush-total-air-time uint8)
(res-desert-bbush-jump-distance uint8)
(res-desert-bbush-total-jump-distance uint8)
(res-desert-bbush-roll-count uint8)
(res-desert-bbush-time-trial-1 uint8)
(res-desert-bbush-rally uint8)
(res-city-bbush-port-attack uint8)
(res-desert-rescue-bbush uint8)
(res-city-gun-course-play-for-fun uint8)
(res-city-jetboard-bbush uint8)
(res-desert-bbush-destroy-interceptors uint8)
;; TODO misc other task-nodes
(arena-fight-1-throne uint8) ;; after arena 1 cutscene
;; TODO - orbs in level X
;; end marker just to make things look nice in a memory view
(end-marker uint8 4))
(:methods
(reset! (_type_) object)
(update! (_type_) object)
(debug-draw (_type_) object)))
(define-extern *autosplit-info-jak3* autosplit-info)

View File

@ -0,0 +1,257 @@
;;-*-Lisp-*-
(in-package goal)
(define *autosplit-info-jak3* (new 'static 'autosplit-info))
(pc-init-autosplitter-struct)
;; Setup Version
(set! (-> *autosplit-info-jak3* version-major) 0)
(set! (-> *autosplit-info-jak3* version-minor) 1)
;; Setup markers
(charp<-string (-> *autosplit-info-jak3* end-marker) "end")
;; Setup Padding
(charp<-string (-> *autosplit-info-jak3* padding-stats) "padding-stats!")
(charp<-string (-> *autosplit-info-jak3* padding-controls) "padding-controls!")
(defconstant MAX_ORBS 600)
(defconstant AUTOSPLITTER_DEBUG #f)
(defmacro autosplit-flag-task-complete! (field-name task-name)
"Given a field name in the autosplitter struct, and a [[game-task]] name to check, sets either a 0 or a 1"
`(begin
(if (!= (-> this ,field-name) (if (task-complete? *game-info* (game-task ,task-name)) 1 0))
(format 0 "AUTOSPLIT for ~A~%" (quote ,task-name)))
(set! (-> this ,field-name) (if (task-complete? *game-info* (game-task ,task-name)) 1 0))))
(defmacro autosplit-flag-task-node-closed! (field-name task-node-name)
"Given a field name in the autosplitter struct, and a [[game-task-node]] name to check, sets either a 0 or a 1"
`(begin
(if (!= (-> this ,field-name) (if (task-node-closed? (game-task-node ,task-node-name)) 1 0))
(format 0 "AUTOSPLIT for ~A~%" (quote ,task-node-name)))
(set! (-> this ,field-name) (if (task-node-closed? (game-task-node ,task-node-name)) 1 0))))
(defmethod update! ((this autosplit-info))
;; general statistics
;; when we are blacked out in loads the value of these are temporarily 0, and that messes with the auto splitter.
(let ((in-blackout? (>= (-> *game-info* blackout-time) (current-time))))
(when (not in-blackout?)
(set! (-> this num-orbs) (the int (-> *game-info* skill-total)))
(set! (-> this num-skullgems) (the int (-> *game-info* gem-total)))
;; ending conditions
;; all collectables
;; - check for all features
;; - check for all vehicles
;; - check for all inventory items
;; - check for all orbs
(set! (-> this all-collectables-acquired?)
(if (and (logtesta? (-> *game-info* features)
(game-feature jakc
board
board-launch
board-zap
darkeco
darkjak
darkjak
darkjak-smack
darkjak-bomb0
darkjak-bomb1
lighteco
lightjak
lightjak-regen
lightjak-swoop
lightjak-freeze
lightjak-shield
gun
gun-red-1
gun-yellow-1
gun-blue-1
gun-dark-1
gun-red-2
gun-yellow-2
gun-blue-2
gun-dark-2
gun-red-3
gun-yellow-3
gun-blue-3
gun-dark-3))
(logtesta? (-> *game-info* vehicles) (game-vehicles v-turtle v-snake v-scorpion v-toad v-fox v-rhino v-mirage v-x-ride))
(logtesta? (-> *game-info* items)
(game-items amulet0
amulet1
amulet2
pass-front-gate
seal-of-mar
cypher-gliph
artifact-holocube
artifact-av-reflector
artifact-av-prism
artifact-av-generator
artifact-av-map
light-eco-crystal0
light-eco-crystal1
light-eco-crystal2
light-eco-crystal3
dark-eco-crystal0
dark-eco-crystal1
dark-eco-crystal2
dark-eco-crystal3))
(>= (-> this num-orbs) MAX_ORBS))
1
0))))
;; loading/cutscene related flags
(set! (-> this in-cutscene?) (if (movie?) 1 0))
;; need resolution flags
(autosplit-flag-task-complete! res-arena-training-1 arena-training-1)
(autosplit-flag-task-complete! res-arena-fight-1 arena-fight-1)
(autosplit-flag-task-complete! res-wascity-chase wascity-chase)
(autosplit-flag-task-complete! res-wascity-pre-game wascity-pre-game)
(autosplit-flag-task-complete! res-desert-turtle-training desert-turtle-training)
(autosplit-flag-task-complete! res-desert-course-race desert-course-race)
(autosplit-flag-task-complete! res-desert-artifact-race-1 desert-artifact-race-1)
(autosplit-flag-task-complete! res-wascity-leaper-race wascity-leaper-race)
(autosplit-flag-task-complete! res-desert-hover desert-hover)
(autosplit-flag-task-complete! res-arena-fight-2 arena-fight-2)
(autosplit-flag-task-complete! res-desert-catch-lizards desert-catch-lizards)
(autosplit-flag-task-complete! res-desert-rescue desert-rescue)
(autosplit-flag-task-complete! res-wascity-gungame wascity-gungame)
(autosplit-flag-task-complete! res-arena-fight-3 arena-fight-3)
(autosplit-flag-task-complete! res-nest-eggs nest-eggs)
(autosplit-flag-task-complete! res-temple-climb temple-climb)
(autosplit-flag-task-complete! res-desert-glide desert-glide)
(autosplit-flag-task-complete! res-volcano-darkeco volcano-darkeco)
(autosplit-flag-task-complete! res-temple-oracle temple-oracle)
(autosplit-flag-task-complete! res-desert-oasis-defense desert-oasis-defense)
(autosplit-flag-task-complete! res-temple-tests temple-tests)
(autosplit-flag-task-complete! res-comb-travel comb-travel)
(autosplit-flag-task-complete! res-mine-explore mine-explore)
(autosplit-flag-task-complete! res-mine-blow mine-blow)
(autosplit-flag-task-complete! res-mine-boss mine-boss)
(autosplit-flag-task-complete! res-sewer-met-hum sewer-met-hum)
(autosplit-flag-task-complete! res-city-vehicle-training city-vehicle-training)
(autosplit-flag-task-complete! res-city-port-fight city-port-fight)
(autosplit-flag-task-complete! res-city-port-attack city-port-attack)
(autosplit-flag-task-complete! res-city-gun-course-1 city-gun-course-1)
(autosplit-flag-task-complete! res-city-sniper-fight city-sniper-fight)
(autosplit-flag-task-complete! res-sewer-kg-met sewer-kg-met)
(autosplit-flag-task-complete! res-city-destroy-darkeco city-destroy-darkeco)
(autosplit-flag-task-complete! res-forest-kill-plants forest-kill-plants)
(autosplit-flag-task-complete! res-city-destroy-grid city-destroy-grid)
(autosplit-flag-task-complete! res-city-hijack-vehicle city-hijack-vehicle)
(autosplit-flag-task-complete! res-city-port-assault city-port-assault)
(autosplit-flag-task-complete! res-city-gun-course-2 city-gun-course-2)
(autosplit-flag-task-complete! res-city-blow-barricade city-blow-barricade)
(autosplit-flag-task-complete! res-city-protect-hq city-protect-hq)
(autosplit-flag-task-complete! res-sewer-hum-kg sewer-hum-kg)
(autosplit-flag-task-complete! res-city-power-game city-power-game)
(autosplit-flag-task-complete! res-desert-artifact-race-2 desert-artifact-race-2)
(autosplit-flag-task-complete! res-nest-hunt nest-hunt)
(autosplit-flag-task-complete! res-desert-beast-battle desert-beast-battle)
(autosplit-flag-task-complete! res-desert-jump-mission desert-jump-mission)
(autosplit-flag-task-complete! res-desert-chase-marauders desert-chase-marauders)
(autosplit-flag-task-complete! res-forest-ring-chase forest-ring-chase)
(autosplit-flag-task-complete! res-factory-sky-battle factory-sky-battle)
(autosplit-flag-task-complete! res-factory-assault factory-assault)
(autosplit-flag-task-complete! res-factory-boss factory-boss)
(autosplit-flag-task-complete! res-temple-defend temple-defend)
(autosplit-flag-task-complete! res-wascity-defend wascity-defend)
(autosplit-flag-task-complete! res-forest-turn-on-machine forest-turn-on-machine)
(autosplit-flag-task-complete! res-precursor-tour precursor-tour)
(autosplit-flag-task-complete! res-city-blow-tower city-blow-tower)
(autosplit-flag-task-complete! res-tower-destroy tower-destroy)
(autosplit-flag-task-complete! res-palace-ruins-patrol palace-ruins-patrol)
(autosplit-flag-task-complete! res-palace-ruins-attack palace-ruins-attack)
(autosplit-flag-task-complete! res-comb-wild-ride comb-wild-ride)
(autosplit-flag-task-complete! res-precursor-destroy-ship precursor-destroy-ship)
(autosplit-flag-task-complete! res-desert-final-boss desert-final-boss)
(autosplit-flag-task-complete! res-city-win city-win)
(autosplit-flag-task-complete! res-desert-bbush-get-to-1 desert-bbush-get-to-1)
(autosplit-flag-task-complete! res-desert-bbush-get-to-2 desert-bbush-get-to-2)
(autosplit-flag-task-complete! res-desert-bbush-get-to-3 desert-bbush-get-to-3)
(autosplit-flag-task-complete! res-desert-bbush-get-to-4 desert-bbush-get-to-4)
(autosplit-flag-task-complete! res-desert-bbush-get-to-5 desert-bbush-get-to-5)
(autosplit-flag-task-complete! res-desert-bbush-get-to-6 desert-bbush-get-to-6)
(autosplit-flag-task-complete! res-desert-bbush-get-to-7 desert-bbush-get-to-7)
(autosplit-flag-task-complete! res-desert-bbush-get-to-8 desert-bbush-get-to-8)
(autosplit-flag-task-complete! res-desert-bbush-get-to-9 desert-bbush-get-to-9)
(autosplit-flag-task-complete! res-desert-bbush-get-to-11 desert-bbush-get-to-11)
(autosplit-flag-task-complete! res-desert-bbush-get-to-12 desert-bbush-get-to-12)
(autosplit-flag-task-complete! res-desert-bbush-get-to-14 desert-bbush-get-to-14)
(autosplit-flag-task-complete! res-desert-bbush-get-to-16 desert-bbush-get-to-16)
(autosplit-flag-task-complete! res-desert-bbush-get-to-17 desert-bbush-get-to-17)
(autosplit-flag-task-complete! res-wascity-bbush-get-to-18 wascity-bbush-get-to-18)
(autosplit-flag-task-complete! res-desert-bbush-get-to-19 desert-bbush-get-to-19)
(autosplit-flag-task-complete! res-wascity-bbush-get-to-20 wascity-bbush-get-to-20)
(autosplit-flag-task-complete! res-wascity-bbush-get-to-21 wascity-bbush-get-to-21)
(autosplit-flag-task-complete! res-wascity-bbush-get-to-22 wascity-bbush-get-to-22)
(autosplit-flag-task-complete! res-wascity-bbush-get-to-23 wascity-bbush-get-to-23)
(autosplit-flag-task-complete! res-wascity-bbush-get-to-24 wascity-bbush-get-to-24)
(autosplit-flag-task-complete! res-wascity-bbush-get-to-25 wascity-bbush-get-to-25)
(autosplit-flag-task-complete! res-city-bbush-get-to-26 city-bbush-get-to-26)
(autosplit-flag-task-complete! res-city-bbush-get-to-27 city-bbush-get-to-27)
(autosplit-flag-task-complete! res-city-bbush-get-to-28 city-bbush-get-to-28)
(autosplit-flag-task-complete! res-city-bbush-get-to-29 city-bbush-get-to-29)
(autosplit-flag-task-complete! res-city-bbush-get-to-30 city-bbush-get-to-30)
(autosplit-flag-task-complete! res-city-bbush-get-to-31 city-bbush-get-to-31)
(autosplit-flag-task-complete! res-city-bbush-get-to-32 city-bbush-get-to-32)
(autosplit-flag-task-complete! res-city-bbush-get-to-33 city-bbush-get-to-33)
(autosplit-flag-task-complete! res-city-bbush-get-to-34 city-bbush-get-to-34)
(autosplit-flag-task-complete! res-city-bbush-get-to-35 city-bbush-get-to-35)
(autosplit-flag-task-complete! res-city-bbush-get-to-36 city-bbush-get-to-36)
(autosplit-flag-task-complete! res-city-bbush-get-to-37 city-bbush-get-to-37)
(autosplit-flag-task-complete! res-city-bbush-get-to-38 city-bbush-get-to-38)
(autosplit-flag-task-complete! res-city-bbush-get-to-39 city-bbush-get-to-39)
(autosplit-flag-task-complete! res-city-bbush-get-to-40 city-bbush-get-to-40)
(autosplit-flag-task-complete! res-city-bbush-get-to-41 city-bbush-get-to-41)
(autosplit-flag-task-complete! res-city-bbush-get-to-42 city-bbush-get-to-42)
(autosplit-flag-task-complete! res-city-bbush-get-to-43 city-bbush-get-to-43)
(autosplit-flag-task-complete! res-city-bbush-get-to-44 city-bbush-get-to-44)
(autosplit-flag-task-complete! res-desert-bbush-ring-1 desert-bbush-ring-1)
(autosplit-flag-task-complete! res-desert-bbush-ring-2 desert-bbush-ring-2)
(autosplit-flag-task-complete! res-wascity-bbush-ring-3 wascity-bbush-ring-3)
(autosplit-flag-task-complete! res-wascity-bbush-ring-4 wascity-bbush-ring-4)
(autosplit-flag-task-complete! res-city-bbush-ring-5 city-bbush-ring-5)
(autosplit-flag-task-complete! res-city-bbush-ring-6 city-bbush-ring-6)
(autosplit-flag-task-complete! res-desert-bbush-egg-spider-1 desert-bbush-egg-spider-1)
(autosplit-flag-task-complete! res-desert-bbush-spirit-chase-1 desert-bbush-spirit-chase-1)
(autosplit-flag-task-complete! res-wascity-bbush-spirit-chase-2 wascity-bbush-spirit-chase-2)
(autosplit-flag-task-complete! res-city-bbush-spirit-chase-3 city-bbush-spirit-chase-3)
(autosplit-flag-task-complete! res-desert-bbush-timer-chase-1 desert-bbush-timer-chase-1)
(autosplit-flag-task-complete! res-wascity-bbush-timer-chase-2 wascity-bbush-timer-chase-2)
(autosplit-flag-task-complete! res-desert-bbush-air-time desert-bbush-air-time)
(autosplit-flag-task-complete! res-desert-bbush-total-air-time desert-bbush-total-air-time)
(autosplit-flag-task-complete! res-desert-bbush-jump-distance desert-bbush-jump-distance)
(autosplit-flag-task-complete! res-desert-bbush-total-jump-distance desert-bbush-total-jump-distance)
(autosplit-flag-task-complete! res-desert-bbush-roll-count desert-bbush-roll-count)
(autosplit-flag-task-complete! res-desert-bbush-time-trial-1 desert-bbush-time-trial-1)
(autosplit-flag-task-complete! res-desert-bbush-rally desert-bbush-rally)
(autosplit-flag-task-complete! res-city-bbush-port-attack city-bbush-port-attack)
(autosplit-flag-task-complete! res-desert-rescue-bbush desert-rescue-bbush)
(autosplit-flag-task-complete! res-city-gun-course-play-for-fun city-gun-course-play-for-fun)
(autosplit-flag-task-complete! res-city-jetboard-bbush city-jetboard-bbush)
(autosplit-flag-task-complete! res-desert-bbush-destroy-interceptors desert-bbush-destroy-interceptors)
;; misc other tasks
(autosplit-flag-task-node-closed! arena-fight-1-throne arena-fight-1-throne) ;; after arena 1 cutscene
;; debug only, draw stuff to the screen so i don't have to stare at a memory editor
(if AUTOSPLITTER_DEBUG (debug-draw this)))
(defmethod reset! ((this autosplit-info))
(set! (-> this game-hash) (pc-get-unix-timestamp))
(set! (-> this errol-dead?) 0))
(defmethod debug-draw ((this autosplit-info))
(format (clear *temp-string*) "errol-dead?: ~D~%" (-> this errol-dead?))
(format *temp-string* "all-collectables-acquired?: ~D~%" (-> this all-collectables-acquired?))
(with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf1))
;; reset bucket settings prior to drawing - font won't do this for us, and
;; draw-raw-image can sometimes mess them up. (intro sequence)
(dma-buffer-add-gs-set-flusha buf
(alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1))
(tex1-1 (new 'static 'gs-tex1 :mmag #x1 :mmin #x1)))
(let ((font-ctx (new 'stack 'font-context *font-default-matrix* 10 50 0.0 (font-color default) (font-flags shadow kerning large))))
(set! (-> font-ctx scale) 0.325)
(draw-string-adv *temp-string* buf font-ctx))))

View File

@ -0,0 +1,134 @@
;;-*-Lisp-*-
(in-package goal)
;; TEST - safe with malformed entries
(deftype speedrun-timer (process)
((draw? symbol)
(started? symbol)
(stopped? symbol)
(start-time time-frame)
(end-time time-frame)
(recorded-time float))
(:methods
(draw-timer (_type_) object)
(start! (_type_) object)
(reset! (_type_) object)
(stop! (_type_) float))
(:state-methods
idle))
(defbehavior speedrun-timer-init-by-other speedrun-timer ()
(false! (-> self draw?))
(false! (-> self started?))
(set! (-> self start-time) 0)
(set! (-> self end-time) 0)
(set! (-> self recorded-time) 0.0)
(go-virtual idle))
(defstate idle (speedrun-timer)
:virtual #t
:code
(behavior ()
(loop
(when (-> self draw?)
(draw-timer self))
(suspend))))
;; TODO - put in util
(deftype objective-zone (process)
((start? symbol)
(v1 vector :inline)
(v2 vector :inline)
(on-enter (function none))
(on-exit (function none)))
(:methods
(draw-zone (_type_) object))
(:state-methods
waiting-for-player
player-inside))
(deftype objective-zone-init-params (structure)
((v1 vector :inline)
(v2 vector :inline)))
(defenum speedrun-practice-flags
(none))
;; reset method
(deftype speedrun-practice-objective (structure)
((index int32)
(flags speedrun-practice-flags)
(completed-task game-task)
(features game-feature)
(secrets game-secrets)
(vehicles game-vehicles)
(starting-position vector)
(starting-rotation vector)
(starting-camera-position vector)
(starting-camera-rotation matrix)
(end-task game-task)
(start-zone-init-params objective-zone-init-params)
(start-zone (pointer objective-zone))
(end-zone-init-params objective-zone-init-params)
(end-zone (pointer objective-zone)))
(:methods
(draw-info (_type_) object)
(reset! (_type_) object)))
(defenum speedrun-category
:type uint32
;; Main Categories
(newgame-normal 0)
(newgame-heromode 1)
;; TODO - add ILs and such later
;; there's no point in adding categories that just start from a new-game and have later restrictions
;; because we aren't going to modify the code to make that possible
;; ie. removing mars tomb skip if you pick "all missions"
;; Random one for experimentation
(all-cheats-allowed 999)
(custom 9999))
(deftype speedrun-custom-category (structure)
((index int32)
(secrets game-secrets)
(features game-feature)
(vehicles game-vehicles)
(forbidden-features game-feature)
(pc-cheats pc-cheats)
(completed-task game-task)))
(deftype speedrun-info (structure)
((category speedrun-category)
(active-custom-category speedrun-custom-category)
(dump-custom-category speedrun-custom-category)
(display-run-info? symbol)
(practicing? symbol)
(active-practice-objective speedrun-practice-objective)
(waiting-to-record-practice-attempt? symbol)
(run-started-at time-frame))
(:methods
(set-category! (_type_ speedrun-category) object)
(start-run! (_type_) object)
(enforce-settings! (_type_) object)
(update! (_type_) object)
(draw-run-info (_type_) object)))
(define-extern *speedrun-info* speedrun-info)
(defenum speedrun-menu-command
:type uint32
(reset 0)
(exit 1))
(deftype speedrun-manager (process)
((popup-menu (pointer popup-menu))
(ignore-menu-toggle? symbol)
(opened-with-start? symbol)
(timer (pointer speedrun-timer)))
(:methods
(draw-menu (_type_) object))
(:state-methods
idle))
(define-extern *speedrun-manager* (pointer speedrun-manager))

View File

@ -0,0 +1,766 @@
;;-*-Lisp-*-
(in-package goal)
;; TODO later - customize menu open keybind
(define-extern task-close! (function string symbol))
(define-extern *pc-dead-pool* dead-pool)
(define *speedrun-info* (new 'static 'speedrun-info))
(set! (-> *speedrun-info* active-custom-category) (new 'static 'speedrun-custom-category))
(set! (-> *speedrun-info* dump-custom-category) (new 'static 'speedrun-custom-category))
(set! (-> *speedrun-info* active-practice-objective) (new 'static 'speedrun-practice-objective))
(set! (-> *speedrun-info* active-practice-objective starting-position) (new 'static 'vector))
(set! (-> *speedrun-info* active-practice-objective starting-rotation) (new 'static 'vector))
(set! (-> *speedrun-info* active-practice-objective starting-camera-position) (new 'static 'vector))
(set! (-> *speedrun-info* active-practice-objective starting-camera-rotation) (new 'static 'matrix))
(set! (-> *speedrun-info* active-practice-objective start-zone-init-params) (new 'static 'objective-zone-init-params))
(set! (-> *speedrun-info* active-practice-objective end-zone-init-params) (new 'static 'objective-zone-init-params))
(defmethod draw-timer ((this speedrun-timer))
(clear *temp-string*)
(clear *pc-encoded-temp-string*)
(cond
((-> this started?)
(format *temp-string* "~,,2fs~%" (* (the float (- (current-time) (-> this start-time))) 0.0033333334)))
((and (!= 0 (-> this end-time)))
(format *temp-string* "~,,2fs~%" (* (the float (- (-> this end-time) (-> this start-time))) 0.0033333334)))
(else (format *temp-string* "0.0s~%")))
(when *target*
(format *temp-string* "~,,2M~%" (-> *target* control ctrl-xz-vel)))
(pc-encode-utf8-string *temp-string* *pc-encoded-temp-string*)
(with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf1))
;; reset bucket settings prior to drawing - font won't do this for us, and
;; draw-raw-image can sometimes mess them up. (intro sequence)
(dma-buffer-add-gs-set-flusha buf
(alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1))
(tex1-1 (new 'static 'gs-tex1 :mmag #x1 :mmin #x1)))
(let ((font-ctx (new 'stack 'font-context *font-default-matrix* 256 350 0.0 (font-color default) (font-flags middle shadow kerning large))))
(set! (-> font-ctx scale) 0.325)
(draw-string-adv *pc-encoded-temp-string* buf font-ctx))))
(defmethod start! ((this speedrun-timer))
(true! (-> this started?))
(false! (-> this stopped?))
(set-time! (-> this start-time))
(set! (-> this end-time) 0))
(defmethod reset! ((this speedrun-timer))
(false! (-> this started?))
(false! (-> this stopped?))
(set! (-> this start-time) 0)
(set! (-> this end-time) 0))
(defmethod stop! ((this speedrun-timer))
(when (not (-> this stopped?))
(false! (-> this started?))
(true! (-> this stopped?))
(set-time! (-> this end-time))
(set! (-> this recorded-time) (* (the float (- (-> this end-time) (-> this start-time))) 0.0033333334)))
(-> this recorded-time))
(defmethod set-category! ((this speedrun-info) (category speedrun-category))
(set! (-> this category) category))
(defconstant HERO_MODE_SECRETS
(game-secrets hero-mode
endless-ammo
invulnerable
endless-dark
endless-light
unlimited-turbos
vehicle-hit-points
board-fast
vehicle-fox
vehicle-mirage
vehicle-x-ride
darkjak-tracking
button-invis
gun-upgrade-red-1
gun-upgrade-red-2
gun-upgrade-red-3
gun-upgrade-yellow-1
gun-upgrade-yellow-2
gun-upgrade-yellow-3
gun-upgrade-blue-1
gun-upgrade-blue-2
gun-upgrade-blue-3
gun-upgrade-dark-1
gun-upgrade-dark-2
gun-upgrade-dark-3
gun-upgrade-ammo-red
gun-upgrade-ammo-yellow
gun-upgrade-ammo-blue
gun-upgrade-ammo-dark))
(defconstant HERO_MODE_FEATURES
(game-feature gun
gun-red-1
gun-red-2
gun-red-3
gun-yellow-1
gun-yellow-2
gun-yellow-3
gun-blue-1
gun-blue-2
gun-blue-3
gun-dark-1
gun-dark-2
gun-dark-3
board
gun-upgrade-yellow-ammo-1
gun-upgrade-yellow-ammo-2
gun-upgrade-red-ammo-1
gun-upgrade-red-ammo-2
gun-upgrade-blue-ammo-1
gun-upgrade-blue-ammo-2
gun-upgrade-dark-ammo-1
gun-upgrade-dark-ammo-2
board-launch
board-zap
darkjak
darkjak-bomb0
darkjak-bomb1
lightjak
lightjak-regen
lightjak-freeze
lightjak-shield
armor0
armor1
armor2
armor3
lighteco
darkeco))
(defmethod start-run! ((this speedrun-info))
;; randomize game id so the autosplitter knows to restart
(reset! *autosplit-info-jak3*)
;; turn on speedrun verification display
(true! (-> this display-run-info?))
(send-event (ppointer->process *speedrun-manager*) 'start-run)
;; ensure any required settings are enabled
(enforce-settings! this)
;; finalize any category specific setup code
(case (-> this category)
(((speedrun-category newgame-normal))
(set! (-> *game-info* mode) 'debug)
(initialize! *game-info* 'game (the game-save #f) (the string #f) (the resetter-spec #f))
(set! (-> *game-info* mode) 'play)
(start 'play (get-continue-by-name *game-info* "wasstada-jump-training"))
(play-task (game-task arena-training-1) 'debug #f))
(((speedrun-category newgame-heromode))
(process-spawn-function process
(lambda :behavior process ()
(set! (-> *game-info* mode) 'debug)
(initialize! *game-info* 'game (the game-save #f) (the string #f) (the resetter-spec #f))
(set! (-> *game-info* mode) 'play)
(logior! (-> *game-info* secrets) (game-secrets hero-mode))
(logior! (-> *game-info* purchase-secrets) (game-secrets hero-mode))
(start 'play (get-continue-by-name *game-info* "wasstada-jump-training"))
(play-task (game-task arena-training-1) 'debug #f)
(until (and *target* (= (-> *target* next-state name) 'target-stance))
(suspend))
(set! (-> *game-info* secrets) HERO_MODE_SECRETS)
(set! (-> *game-info* purchase-secrets) HERO_MODE_SECRETS)
(set! (-> *game-info* features) HERO_MODE_FEATURES))))
(((speedrun-category all-cheats-allowed))
(process-spawn-function process
(lambda :behavior process ()
(set! (-> *game-info* mode) 'debug)
(initialize! *game-info* 'game (the game-save #f) (the string #f) (the resetter-spec #f))
(set! (-> *game-info* mode) 'play)
(start 'play (get-continue-by-name *game-info* "wasstada-jump-training"))
(play-task (game-task arena-training-1) 'debug #f))))
(((speedrun-category custom))
(process-spawn-function process
(lambda :behavior process ()
(clear *temp-string*)
(pc-sr-mode-get-custom-category-continue-point (-> *speedrun-info* active-custom-category index) *temp-string*)
(if (string= *temp-string* EMPTY_STRING)
(initialize! *game-info* 'game (the game-save #f) "intro-start" (the resetter-spec #f))
(initialize! *game-info* 'game (the game-save #f) *temp-string* (the resetter-spec #f)))
(until (and *target* (= (-> *target* next-state name) 'target-stance))
(suspend))
(when (nonzero? (-> *speedrun-info* active-custom-category completed-task))
(task-resolution-close! (-> *speedrun-info* active-custom-category completed-task)))))))
(if (!= -1 (-> *game-info* auto-save-which)) (set! (-> *setting-control* user-default auto-save) #t)))
(defmethod enforce-settings! ((this speedrun-info))
(true! (-> *pc-settings* ps2-actor-vis?)) ;; force PS2 actor visibility
(set-frame-rate! *pc-settings* 60 #t) ;; force FPS to `60`
;; For posterity, the main reason why changing the cheats is useful is for two main reasons:
;; - If you are playing a category that requires cheats (ie. a turbo jetboard one) you'd
;; probably like the game to automatically set the appropriate ones for you
;; - If you are playing a category that forbids cheats, you wouldn't want your run invalidated because you forgot
(case (-> this category)
(((speedrun-category newgame-normal) (speedrun-category newgame-heromode))
;; disable any active cheats
(set! (-> *pc-settings* cheats) (pc-cheats)))
(((speedrun-category custom))
(set! (-> *game-info* purchase-secrets) (-> *speedrun-info* active-custom-category secrets))
(set! (-> *game-info* secrets) (-> *speedrun-info* active-custom-category secrets))
(logior! (-> *game-info* features) (-> *speedrun-info* active-custom-category features))
(logclear! (-> *game-info* features) (-> *speedrun-info* active-custom-category forbidden-features))
(logior! (-> *game-info* vehicles) (-> *speedrun-info* active-custom-category vehicles))
(set! (-> *pc-settings* cheats) (-> *speedrun-info* active-custom-category pc-cheats)))))
(defmethod draw-zone ((this objective-zone))
(add-debug-box #t
(bucket-id debug)
(-> this v1)
(-> this v2)
(if (-> this start?) (static-rgba #xff #xff #x00 #x80) (static-rgba #xff #x00 #xff #x80))))
(defstate waiting-for-player (objective-zone)
:virtual #t
:event
(behavior ((proc process) (argc int) (message symbol) (block event-message-block))
#t)
:trans
(behavior ()
;; Check to see if we have entered the zone
(let ((min-point-x (fmin (-> self v1 x) (-> self v2 x)))
(min-point-y (fmin (-> self v1 y) (-> self v2 y)))
(min-point-z (fmin (-> self v1 z) (-> self v2 z)))
(max-point-x (fmax (-> self v1 x) (-> self v2 x)))
(max-point-y (fmax (-> self v1 y) (-> self v2 y)))
(max-point-z (fmax (-> self v1 z) (-> self v2 z)))
(pos (target-pos 0)))
(when (and (and (<= min-point-x (-> pos x)) (<= (-> pos x) max-point-x))
(and (<= min-point-y (-> pos y)) (<= (-> pos y) max-point-y))
(and (<= min-point-z (-> pos z)) (<= (-> pos z) max-point-z)))
(when (nonzero? (-> self on-enter))
((-> self on-enter)))
(go-virtual player-inside))))
:code
(behavior ()
(loop
(draw-zone self)
(suspend))))
(defstate player-inside (objective-zone)
:virtual #t
:trans
(behavior ()
;; Check to see if we have entered the zone
(let ((min-point-x (fmin (-> self v1 x) (-> self v2 x)))
(min-point-y (fmin (-> self v1 y) (-> self v2 y)))
(min-point-z (fmin (-> self v1 z) (-> self v2 z)))
(max-point-x (fmax (-> self v1 x) (-> self v2 x)))
(max-point-y (fmax (-> self v1 y) (-> self v2 y)))
(max-point-z (fmax (-> self v1 z) (-> self v2 z)))
(pos (target-pos 0)))
(when (not (and (and (<= min-point-x (-> pos x)) (<= (-> pos x) max-point-x))
(and (<= min-point-y (-> pos y)) (<= (-> pos y) max-point-y))
(and (<= min-point-z (-> pos z)) (<= (-> pos z) max-point-z))))
(when (nonzero? (-> self on-exit))
((-> self on-exit)))
(go-virtual waiting-for-player))))
:code
(behavior ()
(loop
(draw-zone self)
(suspend))))
(defbehavior objective-zone-init-by-other objective-zone ((start? symbol) (params objective-zone-init-params))
(set! (-> self start?) start?)
(vector-copy! (-> self v1) (-> params v1))
(vector-copy! (-> self v2) (-> params v2))
(go-virtual waiting-for-player))
(defmethod draw-info ((this speedrun-practice-objective))
(clear *temp-string*)
(clear *pc-encoded-temp-string*)
(pc-sr-mode-get-practice-entry-name (-> this index) *pc-encoded-temp-string*)
(format *temp-string* "<COLOR_WHITE>Practicing: <COLOR_GREEN>~S~%" *pc-encoded-temp-string*)
(if (> (pc-sr-mode-get-practice-entry-history-attempts (-> this index)) 0)
(format *temp-string*
"<COLOR_WHITE>History: <COLOR_GREEN>~D<COLOR_WHITE>/~D (~,,2f%)~%"
(pc-sr-mode-get-practice-entry-history-success (-> this index))
(pc-sr-mode-get-practice-entry-history-attempts (-> this index))
(* 100.0
(/ (the float (pc-sr-mode-get-practice-entry-history-success (-> this index)))
(the float (pc-sr-mode-get-practice-entry-history-attempts (-> this index))))))
(format *temp-string* "<COLOR_WHITE>History: --~%"))
(if (> (pc-sr-mode-get-practice-entry-session-attempts (-> this index)) 0)
(format *temp-string*
"<COLOR_WHITE>Session: <COLOR_GREEN>~D<COLOR_WHITE>/~D (~,,2f%)~%"
(pc-sr-mode-get-practice-entry-session-success (-> this index))
(pc-sr-mode-get-practice-entry-session-attempts (-> this index))
(* 100.0
(/ (the float (pc-sr-mode-get-practice-entry-session-success (-> this index)))
(the float (pc-sr-mode-get-practice-entry-session-attempts (-> this index))))))
(format *temp-string* "<COLOR_WHITE>Session: --~%"))
(pc-sr-mode-get-practice-entry-avg-time (-> this index) *pc-encoded-temp-string*)
(format *temp-string* "<COLOR_WHITE>Average Time: <COLOR_GREEN>~Ss~%" *pc-encoded-temp-string*)
(pc-sr-mode-get-practice-entry-fastest-time (-> this index) *pc-encoded-temp-string*)
(format *temp-string* "<COLOR_WHITE>Fastest Time: <COLOR_GREEN>~Ss~%" *pc-encoded-temp-string*)
(format *temp-string* "<COLOR_WHITE>\c91 L3: Reset~%")
(pc-encode-utf8-string *temp-string* *pc-encoded-temp-string*)
(with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf2))
;; reset bucket settings prior to drawing - font won't do this for us, and
;; draw-raw-image can sometimes mess them up. (intro sequence)
(dma-buffer-add-gs-set-flusha buf
(alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1))
(tex1-1 (new 'static 'gs-tex1 :mmag #x1 :mmin #x1)))
(let ((font-ctx (new 'stack 'font-context *font-default-matrix* 510 20 0.0 (font-color default) (font-flags right shadow kerning large))))
(set! (-> font-ctx scale) 0.325)
(draw-string-adv *pc-encoded-temp-string* buf font-ctx))))
(defmethod reset! ((this speedrun-practice-objective))
;; record attempt if attempt was started
(when (-> *speedrun-info* waiting-to-record-practice-attempt?)
(pc-sr-mode-record-practice-entry-attempt! (-> this index)
#f
(&-> (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))) recorded-time)))
;; TODO - load checkpoint if not already in that checkpoint
;; TODO - set features / cheats / completed-task / etc
;; Update player position
(vector-copy! (-> *target* root trans) (-> this starting-position))
(vector-copy! (-> *target* root quat) (-> this starting-rotation))
;; - get off jetboard and reset speed
(vector-copy! (-> *target* control transv) *zero-vector*)
(send-event *target* 'change-mode 'normal)
;; Update camera position and rotation
(vector-copy! (-> *camera-combiner* trans) (-> this starting-camera-position))
(matrix-identity! (-> *camera-combiner* inv-camera-rot))
(matrix-copy! (-> *camera-combiner* inv-camera-rot) (-> this starting-camera-rotation))
(process-spawn-function process
(lambda :behavior process ()
(suspend)
(send-event *camera* 'teleport)
(deactivate self)))
(cam-master-activate-slave #f))
(define *speedrun-popup-menu-entries*
(new 'static
'boxed-array
:type
popup-menu-entry
(new 'static
'popup-menu-button
:label "Reset"
:on-confirm
(lambda ()
(send-event (ppointer->process *speedrun-manager*) 'invoke (speedrun-menu-command reset))
(send-event (-> *speedrun-manager* 0 popup-menu 0) 'close-menu)))
(new 'static
'popup-menu-submenu
:label "Built-in Category Select"
:entries
(new 'static
'boxed-array
:type
popup-menu-entry
(new 'static
'popup-menu-flag
:label "Normal"
:on-confirm
(lambda ()
(set-category! *speedrun-info* (speedrun-category newgame-normal)))
:is-toggled?
(lambda ()
(= (-> *speedrun-info* category) (speedrun-category newgame-normal))))
(new 'static
'popup-menu-flag
:label "Hero Mode"
:on-confirm
(lambda ()
(set-category! *speedrun-info* (speedrun-category newgame-heromode)))
:is-toggled?
(lambda ()
(= (-> *speedrun-info* category) (speedrun-category newgame-heromode))))
(new 'static
'popup-menu-flag
:label "All Cheats Allowed"
:on-confirm
(lambda ()
(set-category! *speedrun-info* (speedrun-category all-cheats-allowed)))
:is-toggled?
(lambda ()
(= (-> *speedrun-info* category) (speedrun-category all-cheats-allowed))))))
(new 'static
'popup-menu-dynamic-submenu
:label "Custom Category Select"
:get-length
(lambda ()
(pc-sr-mode-get-custom-category-amount))
:get-entry-label
(lambda ((index int) (str-dest string))
(pc-sr-mode-get-custom-category-name index str-dest))
:on-entry-confirm
(lambda ((index int))
;; hydrate from cpp
(pc-sr-mode-init-custom-category-info! index (-> *speedrun-info* active-custom-category))
(set-category! *speedrun-info* (speedrun-category custom)))
:entry-selected?
(lambda ((index int))
(and (= (-> *speedrun-info* category) (speedrun-category custom))
(= index (-> *speedrun-info* active-custom-category index)))))
;; TODO - disabled until finalized
; (new 'static
; 'popup-menu-dynamic-submenu
; :label "Practice select"
; :entry-disabled?
; (lambda ()
; (not (-> *speedrun-info* practicing?)))
; :get-length
; (lambda ()
; (pc-sr-mode-get-practice-entries-amount))
; :get-entry-label
; (lambda ((index int) (str-dest string))
; (pc-sr-mode-get-practice-entry-name index str-dest))
; :on-entry-confirm
; (lambda ((index int))
; ;; turn on timer
; (set! (-> (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))) draw?) #t)
; ;; tear down old processes
; (when (nonzero? (-> *speedrun-info* active-practice-objective start-zone))
; (deactivate (-> *speedrun-info* active-practice-objective start-zone 0)))
; (when (nonzero? (-> *speedrun-info* active-practice-objective end-zone))
; (deactivate (-> *speedrun-info* active-practice-objective end-zone 0)))
; ;; init from cpp
; (pc-sr-mode-init-practice-info! index (-> *speedrun-info* active-practice-objective))
; ;; startup new processes
; (set! (-> *speedrun-info* active-practice-objective start-zone)
; (the (pointer objective-zone)
; (process-spawn objective-zone #t (-> *speedrun-info* active-practice-objective start-zone-init-params))))
; (set! (-> *speedrun-info* active-practice-objective start-zone 0 on-exit)
; (lambda ()
; (start! (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))))
; (set! (-> *speedrun-info* waiting-to-record-practice-attempt?) #t)
; (none)))
; (set! (-> *speedrun-info* active-practice-objective start-zone 0 on-enter)
; (lambda ()
; (when (and *target* (>= (-> *target* control ctrl-xz-vel) (meters 30.0)))
; (vector-copy! (-> *target* control transv) *zero-vector*))
; (set! (-> *speedrun-info* waiting-to-record-practice-attempt?) #f)
; (reset! (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))))
; (none)))
; (when (= 0 (-> *speedrun-info* active-practice-objective end-task))
; (set! (-> *speedrun-info* active-practice-objective end-zone)
; (the (pointer objective-zone)
; (process-spawn objective-zone #f (-> *speedrun-info* active-practice-objective end-zone-init-params))))
; (set! (-> *speedrun-info* active-practice-objective end-zone 0 on-enter)
; (lambda ()
; (when (-> *speedrun-info* waiting-to-record-practice-attempt?)
; (stop! (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))))
; (if (pc-sr-mode-record-practice-entry-attempt! (-> *speedrun-info* active-practice-objective index)
; #t
; (&-> (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))) recorded-time))
; (sound-play "skill-pickup")
; (sound-play "menu-pick"))
; (set! (-> *speedrun-info* waiting-to-record-practice-attempt?) #f))
; (none))))
; (set! (-> *speedrun-info* practicing?) #t)
; (reset! (-> *speedrun-info* active-practice-objective))
; (set-master-mode 'game)
; (send-event (ppointer->process (-> *speedrun-manager* 0 popup-menu)) 'close-menu))
; :entry-selected?
; (lambda ((index int))
; (and (-> *speedrun-info* practicing?) (= index (-> *speedrun-info* active-practice-objective index)))))
; (new 'static
; 'popup-menu-button
; :label "Stop practicing"
; :entry-disabled?
; (lambda ()
; (not (-> *speedrun-info* practicing?)))
; :on-confirm
; (lambda ()
; (when (-> *speedrun-info* practicing?)
; (when (nonzero? (-> *speedrun-info* active-practice-objective start-zone))
; (deactivate (-> *speedrun-info* active-practice-objective start-zone 0)))
; (when (nonzero? (-> *speedrun-info* active-practice-objective end-zone))
; (deactivate (-> *speedrun-info* active-practice-objective end-zone 0))))
; (set! (-> *speedrun-info* practicing?) #f)
; (set! (-> (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))) draw?) #f)))
(new 'static
'popup-menu-submenu
:label "Tools"
:entries
(new 'static
'boxed-array
:type
popup-menu-entry
(new 'static
'popup-menu-submenu
:label "Create custom category"
:entries
(new 'static
'boxed-array
:type
popup-menu-entry
(new 'static
'popup-menu-dynamic-submenu
:label "Select secrets"
:get-length
(lambda ()
58)
:get-entry-label
(lambda ((index int) (str-dest string))
(copy-string<-string str-dest (bitfield->string game-secrets index)))
:on-entry-confirm
(lambda ((index int))
(logxor! (-> *speedrun-info* dump-custom-category secrets) (shl 1 index)))
:entry-selected?
(lambda ((index int))
(logtest? (-> *speedrun-info* dump-custom-category secrets) (shl 1 index)))
:on-reset
(lambda ()
(set! (-> *speedrun-info* dump-custom-category secrets) (game-secrets))))
(new 'static
'popup-menu-dynamic-submenu
:label "Select features"
:get-length
(lambda ()
58)
:get-entry-label
(lambda ((index int) (str-dest string))
(copy-string<-string str-dest (bitfield->string game-feature index)))
:on-entry-confirm
(lambda ((index int))
(logxor! (-> *speedrun-info* dump-custom-category features) (shl 1 index)))
:entry-selected?
(lambda ((index int))
(logtest? (-> *speedrun-info* dump-custom-category features) (shl 1 index)))
:on-reset
(lambda ()
(set! (-> *speedrun-info* dump-custom-category features) (game-feature))))
(new 'static
'popup-menu-dynamic-submenu
:label "Select vehicles"
:get-length
(lambda ()
8)
:get-entry-label
(lambda ((index int) (str-dest string))
(copy-string<-string str-dest (bitfield->string game-vehicles index)))
:on-entry-confirm
(lambda ((index int))
(logxor! (-> *speedrun-info* dump-custom-category vehicles) (shl 1 index)))
:entry-selected?
(lambda ((index int))
(logtest? (-> *speedrun-info* dump-custom-category vehicles) (shl 1 index)))
:on-reset
(lambda ()
(set! (-> *speedrun-info* dump-custom-category vehicles) (game-vehicles))))
(new 'static
'popup-menu-dynamic-submenu
:label "Forbid features"
:get-length
(lambda ()
58)
:get-entry-label
(lambda ((index int) (str-dest string))
(copy-string<-string str-dest (bitfield->string game-feature index)))
:on-entry-confirm
(lambda ((index int))
(logxor! (-> *speedrun-info* dump-custom-category forbidden-features) (shl 1 index)))
:entry-selected?
(lambda ((index int))
(logtest? (-> *speedrun-info* dump-custom-category forbidden-features) (shl 1 index)))
:on-reset
(lambda ()
(set! (-> *speedrun-info* dump-custom-category forbidden-features) (game-feature))))
(new 'static
'popup-menu-dynamic-submenu
:label "Select cheats"
:get-length
(lambda ()
17)
:get-entry-label
(lambda ((index int) (str-dest string))
(copy-string<-string str-dest (bitfield->string pc-cheats index)))
:on-entry-confirm
(lambda ((index int))
(logxor! (-> *speedrun-info* dump-custom-category pc-cheats) (shl 1 index)))
:entry-selected?
(lambda ((index int))
(logtest? (-> *speedrun-info* dump-custom-category pc-cheats) (shl 1 index)))
:on-reset
(lambda ()
(set! (-> *speedrun-info* dump-custom-category pc-cheats) (pc-cheats))))
(new 'static
'popup-menu-dynamic-submenu
:label "Select completed task"
:get-length
(lambda ()
(dec (the int (game-task max))))
:get-entry-label
(lambda ((index int) (str-dest string))
(copy-string<-string str-dest (enum->string game-task index)))
:on-entry-confirm
(lambda ((index int))
(set! (-> *speedrun-info* dump-custom-category completed-task) (the game-task index)))
:entry-selected?
(lambda ((index int))
(= (-> *speedrun-info* dump-custom-category completed-task) (the game-task index)))
:on-reset
(lambda ()
(set! (-> *speedrun-info* dump-custom-category completed-task) (game-task none))))
(new 'static
'popup-menu-button
:label "Save new category to file"
:on-confirm
(lambda ()
(pc-sr-mode-dump-new-custom-category (-> *speedrun-info* dump-custom-category))))))))
(new 'static
'popup-menu-button
:label "Exit"
:on-confirm
(lambda ()
(send-event (ppointer->process *speedrun-manager*) 'invoke (speedrun-menu-command exit))))))
(define *speedrun-manager* (the (pointer speedrun-manager) #f))
(defbehavior speedrun-manager-init-by-other speedrun-manager ()
(process-mask-clear! (-> self mask) menu pause)
(set! *speedrun-manager* (the (pointer speedrun-manager) (process->ppointer self)))
(set! (-> self popup-menu) (process-spawn popup-menu "Speedrun Menu" *speedrun-popup-menu-entries* :to self))
(set! (-> self timer) (process-spawn speedrun-timer :to self))
(set! (-> self ignore-menu-toggle?) #f)
(set! (-> self opened-with-start?) #f)
(set! (-> *speedrun-info* practicing?) #f)
(set! (-> *speedrun-info* waiting-to-record-practice-attempt?) #f)
(go-virtual idle))
(defmethod update! ((this speedrun-info))
"A per frame update for speedrunning related stuff"
;; Ensure the speedrunner menu process is enabled or destroyed
(when (and (-> *pc-settings* speedrunner-mode?) (not *speedrun-manager*))
(process-spawn speedrun-manager :from *pc-dead-pool* :to *pc-pool*))
(when (and (not (-> *pc-settings* speedrunner-mode?)) *speedrun-manager*)
(deactivate (-> *speedrun-manager* 0)))
;; do speedrunner mode things
(when (-> *pc-settings* speedrunner-mode?)
;; Update auto-splitter struct
(update! *autosplit-info-jak3*)
;; see if we should stop drawing the run info (when you finish arena training)
(when (and (!= (-> this category) (speedrun-category custom)) (task-complete? *game-info* (game-task arena-training-1)))
(false! (-> this display-run-info?)))
;; Draw info to the screen
(when (and (not (-> *speedrun-info* practicing?)) (-> this display-run-info?))
(draw-run-info this))
;; enforce settings even if they've changed them
(enforce-settings! this)
;; draw objective info if practicing
(when (-> *speedrun-info* practicing?)
(draw-info (-> this active-practice-objective)))))
(defmethod draw-run-info ((this speedrun-info))
"Draw speedrun related settings in the bottom left corner"
(when (and (-> *pc-settings* speedrunner-mode?) (-> this display-run-info?))
(clear *temp-string*)
(clear *pc-encoded-temp-string*)
(clear *pc-cpp-temp-string*)
(cond
((= (-> this category) (speedrun-category custom))
(pc-sr-mode-get-custom-category-name (-> this active-custom-category index) *pc-cpp-temp-string*)
(format *temp-string*
"<COLOR_WHITE>Category: <COLOR_GREEN>~S~%<COLOR_WHITE>Secrets: <COLOR_GREEN>~D~%<COLOR_WHITE>Features: <COLOR_GREEN>~D~%<COLOR_WHITE>Forbidden Features: <COLOR_GREEN>~D~%<COLOR_WHITE>Cheats: <COLOR_GREEN>~D~%<COLOR_WHITE>Version: <COLOR_GREEN>~S~%"
*pc-cpp-temp-string*
(-> this active-custom-category secrets)
(-> this active-custom-category features)
(-> this active-custom-category forbidden-features)
(-> this active-custom-category pc-cheats)
*pc-settings-built-sha*))
(else
(format *temp-string*
"<COLOR_WHITE>Category: <COLOR_GREEN>~S~%<COLOR_WHITE>PC Cheats: <COLOR_GREEN>~S~%<COLOR_WHITE>Frame Rate: <COLOR_GREEN>~D~%<COLOR_WHITE>PS2 Actor Vis?: <COLOR_GREEN>~S~%<COLOR_WHITE>Version: <COLOR_GREEN>~S~%"
(enum->string speedrun-category (-> this category))
(if (= (-> *pc-settings* cheats) (pc-cheats)) "None" (pc-cheats->string (-> *pc-settings* cheats) *temp-string2*))
(-> *pc-settings* target-fps)
(if (-> *pc-settings* ps2-actor-vis?) "true" "false")
*pc-settings-built-sha*)))
(pc-encode-utf8-string *temp-string* *pc-encoded-temp-string*)
(with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf2))
;; reset bucket settings prior to drawing - font won't do this for us, and
;; draw-raw-image can sometimes mess them up. (intro sequence)
(dma-buffer-add-gs-set-flusha buf
(alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1))
(tex1-1 (new 'static 'gs-tex1 :mmag #x1 :mmin #x1)))
(let ((font-ctx (new 'stack
'font-context
*font-default-matrix*
510
(if (= (-> this category) (speedrun-category custom)) 355 365)
0.0
(font-color default)
(font-flags right shadow kerning large))))
(set! (-> font-ctx scale) 0.325)
(draw-string-adv *pc-encoded-temp-string* buf font-ctx)))))
;; Speedrun Menu
(defmethod deactivate ((this speedrun-manager))
(set! *speedrun-manager* (the (pointer speedrun-manager) #f))
(call-parent-method this))
(defmethod draw-menu ((this speedrun-manager))
;; don't allow the menu to open during blackouts, apparently causes bugs
(if (< (-> *game-info* blackout-time) (current-time))
;; handle opening and closing the menu
(cond
((!= (-> *pc-settings* speedrunner-mode-custom-bind) 0)
;; the user has let go of the keybind completely or partially, allow the bind to trigger again
(when (and (-> this ignore-menu-toggle?)
(!= (cpad-hold 0) (logior (cpad-hold 0) (-> *pc-settings* speedrunner-mode-custom-bind))))
(false! (-> this ignore-menu-toggle?)))
;; bind handler
(when (and (not (-> this ignore-menu-toggle?))
(= (cpad-hold 0) (logior (cpad-hold 0) (-> *pc-settings* speedrunner-mode-custom-bind))))
(send-event (ppointer->process (-> this popup-menu)) 'open-menu)
(logclear! (cpad-hold 0) (-> *pc-settings* speedrunner-mode-custom-bind))
(logclear! (cpad-pressed 0) (-> *pc-settings* speedrunner-mode-custom-bind))
(true! (-> this ignore-menu-toggle?))))
(else
(when (and (-> this ignore-menu-toggle?)
(or (not (cpad-hold? 0 l1)) (not (cpad-hold? 0 r1)))
(or (and (-> this opened-with-start?) (not (cpad-hold? 0 start)))
(and (not (-> this opened-with-start?)) (not (cpad-hold? 0 select)))))
(set! (-> this ignore-menu-toggle?) #f))
(when (and (cpad-hold? 0 l1)
(cpad-hold? 0 r1)
(or (cpad-hold? 0 select) (cpad-hold? 0 start))
(not (-> this ignore-menu-toggle?)))
(send-event (ppointer->process (-> this popup-menu)) 'open-menu)
(cpad-clear! 0 l1 r1)
(cond
((cpad-hold? 0 select) (cpad-clear! 0 select) (false! (-> this opened-with-start?)))
((cpad-hold? 0 start) (cpad-clear! 0 start) (true! (-> this opened-with-start?))))
(true! (-> this ignore-menu-toggle?)))))))
(defstate idle (speedrun-manager)
:virtual #t
:event
(behavior ((proc process) (argc int) (message symbol) (block event-message-block))
(case message
(('start-run) (set-time! (-> *speedrun-info* run-started-at)))
(('invoke)
(case (-> block param 0)
(((speedrun-menu-command reset)) (start-run! *speedrun-info*))
(((speedrun-menu-command exit)) (set-master-mode 'game) (send-event (ppointer->process (-> self popup-menu)) 'close-menu))
(else (format 0 "nyi: invoke ~D~%" (-> block param 0)))))))
:trans
(behavior ()
(draw-menu self))
:code
(behavior ()
(until #f
(when (and (-> *speedrun-info* practicing?) (cpad-pressed? 0 l3))
(reset! (-> *speedrun-info* active-practice-objective)))
(when (and (-> *speedrun-info* display-run-info?)
(= (-> *speedrun-info* category) (speedrun-category custom))
(time-elapsed? (-> *speedrun-info* run-started-at) (seconds 15)))
(false! (-> *speedrun-info* display-run-info?)))
(suspend))))

View File

@ -42,6 +42,10 @@
(weather-good)
)
(defun pc-cheats->string ((cheats pc-cheats) (buf object))
(bit-enum->string pc-cheats cheats buf)
buf)
;; pc enum for languages. this is the game's languages + custom ones.
(defenum pc-language
:type uint16
@ -72,7 +76,7 @@
(custom 999) ;; temp
)
;; The Jak 2 version of the pc-settings object.
;; The Jak 3 version of the pc-settings object.
(deftype pc-settings-jak3 (pc-settings)
(;; cheats
(cheats pc-cheats)

View File

@ -303,9 +303,9 @@
(defmethod update-speedrun ((obj pc-settings-jak3))
"update speedrun module"
;; TODO - update to new with-profiler syntax
;; (with-profiler "speedrun-update"
;(update! *speedrun-info*)
;;)
; (with-profiler "speedrun-update"
(update! *speedrun-info*)
; )
(none))
(defmethod update-video-hacks ((obj pc-settings-jak3))
@ -751,7 +751,8 @@
;; the actor pool for PC processes! it has space for 4 processes, with 16K of space.
(define *pc-dead-pool* (new 'global 'dead-pool 4 (* 16 1024) "*pc-dead-pool*"))
(set! (-> *pc-pool* clock) (-> *display* base-clock))
(+! (-> *display* base-clock ref-count) 1)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -0,0 +1,59 @@
;;-*-Lisp-*-
(in-package goal)
;; A debug-menu style popup menu, a lightweight way to make a context menu that doesn't involve the progress code
;; and isn't debug-only
(define *popup-menu-open* #f)
(deftype popup-menu-entry (basic)
((label string)
(entry-disabled? (function symbol))
(on-confirm (function none)))
(:methods
(draw-entry (_type_ font-context dma-buffer symbol) object)))
;; (deftype popup-menu-label (popup-menu-entry) ())
(deftype popup-menu-button (popup-menu-entry) ())
(deftype popup-menu-flag (popup-menu-entry)
((is-toggled? (function symbol))))
(deftype popup-menu-submenu (popup-menu-entry)
((entries (array popup-menu-entry))))
(deftype popup-menu-dynamic-submenu (popup-menu-entry)
((get-length (function int))
(get-entry-label (function int string none))
(on-entry-confirm (function int none))
(entry-selected? (function int symbol))
(on-reset (function none))))
(deftype popup-menu-state (structure)
((title string)
(entries (array popup-menu-entry))
(entry-index int32)
(dynamic-menu? symbol)
(get-dynamic-menu-length (function int))
(get-dynamic-menu-entry-label (function int string none))
(on-dynamic-menu-entry-confirm (function int none))
(dynamic-menu-entry-selected? (function int symbol))
(on-dynamic-menu-reset (function none))))
(deftype popup-menu (process)
((title string)
(entries (array popup-menu-entry))
(menu-states popup-menu-state 10 :inline)
(curr-state-index int32)
(draw? symbol))
(:methods
(update-menu! (_type_) object)
(draw-menu (_type_) object)
(move-up! (_type_ int) object)
(move-down! (_type_ int) object)
(confirm! (_type_) object)
(reset! (_type_) object)
(back! (_type_) symbol))
(:state-methods
idle))

View File

@ -0,0 +1,289 @@
;;-*-Lisp-*-
(in-package goal)
(defun get-widest-entry ((entries (array popup-menu-entry)) (title string) (font-ctx font-context) (start-index int) (end-index int))
(let ((max-len 0.0))
(dotimes (i (- end-index start-index))
(let ((label-len (-> (get-string-length (-> entries (+ start-index i) label) font-ctx) length)))
(when (> label-len max-len)
(set! max-len label-len))))
(let ((title-len (-> (get-string-length title font-ctx) length)))
(when (> title-len max-len)
(set! max-len title-len)))
(the int max-len)))
(defun get-widest-dynamic-entry ((get-entry-label (function int string none)) (title string) (font-ctx font-context) (start-index int) (end-index int))
(let ((max-len 0.0))
(dotimes (i (- end-index start-index))
(get-entry-label (+ start-index i) *pc-encoded-temp-string*)
(let ((label-len (-> (get-string-length *pc-encoded-temp-string* font-ctx) length)))
(when (> label-len max-len)
(set! max-len label-len))))
(let ((title-len (-> (get-string-length title font-ctx) length)))
(when (> title-len max-len)
(set! max-len title-len)))
(the int max-len)))
(defmethod draw-entry ((this popup-menu-entry) (font-ctx font-context) (dma-buf dma-buffer) (hovering? symbol))
(let ((old-x (-> font-ctx origin x))
(old-y (-> font-ctx origin y))
(old-color (-> font-ctx color)))
(pc-encode-utf8-string (-> this label) *pc-encoded-temp-string*)
(when hovering?
(set! (-> font-ctx color) (font-color cyan)))
(when (and (nonzero? (-> this entry-disabled?)) ((-> this entry-disabled?)))
(set! (-> font-ctx color) (font-color menu-parent)))
(draw-string-adv *pc-encoded-temp-string* dma-buf font-ctx)
(set! (-> font-ctx origin x) old-x)
(set! (-> font-ctx origin y) old-y)
(set! (-> font-ctx color) old-color)))
(defmethod draw-entry ((this popup-menu-flag) (font-ctx font-context) (dma-buf dma-buffer) (hovering? symbol))
(let ((old-x (-> font-ctx origin x))
(old-y (-> font-ctx origin y))
(old-color (-> font-ctx color)))
(when ((-> this is-toggled?))
(set! (-> font-ctx color) (font-color green))
(set! (-> font-ctx origin x) (- old-x 6.0))
(draw-string-adv "\c86" dma-buf font-ctx)
(set! (-> font-ctx origin x) old-x)
(set! (-> font-ctx origin y) old-y)
(set! (-> font-ctx color) old-color))
(pc-encode-utf8-string (-> this label) *pc-encoded-temp-string*)
(when hovering?
(set! (-> font-ctx color) (font-color cyan)))
(draw-string-adv *pc-encoded-temp-string* dma-buf font-ctx)
(set! (-> font-ctx origin x) old-x)
(set! (-> font-ctx origin y) old-y)
(set! (-> font-ctx color) old-color)))
(defun draw-dynamic-entry ((entry-id int) (get-label (function int string none)) (entry-selected? (function int symbol)) (font-ctx font-context) (dma-buf dma-buffer) (hovering? symbol))
(let ((old-x (-> font-ctx origin x))
(old-y (-> font-ctx origin y))
(old-color (-> font-ctx color)))
(when (entry-selected? entry-id)
(set! (-> font-ctx color) (font-color green))
(set! (-> font-ctx origin x) (- old-x 6.0))
(draw-string-adv "\c86" dma-buf font-ctx)
(set! (-> font-ctx origin x) old-x)
(set! (-> font-ctx origin y) old-y)
(set! (-> font-ctx color) old-color))
(clear *pc-encoded-temp-string*)
(get-label entry-id *pc-encoded-temp-string*)
(pc-encode-utf8-string *pc-encoded-temp-string* *pc-encoded-temp-string*)
(when hovering?
(set! (-> font-ctx color) (font-color cyan)))
(draw-string-adv *pc-encoded-temp-string* dma-buf font-ctx)
(set! (-> font-ctx origin x) old-x)
(set! (-> font-ctx origin y) old-y)
(set! (-> font-ctx color) old-color)))
(defmethod draw-menu ((this popup-menu))
(let ((font-ctx (new 'debug 'font-context *font-default-matrix* 0 0 0.0 (font-color default) (font-flags shadow kerning large)))
(page-title (-> this menu-states (-> this curr-state-index) title))
(dynamic-menu? (-> this menu-states (-> this curr-state-index) dynamic-menu?))
(can-reset? (and (nonzero? (-> this menu-states (-> this curr-state-index) on-dynamic-menu-reset))
(-> this menu-states (-> this curr-state-index) on-dynamic-menu-reset))))
(set! (-> font-ctx scale) 0.25)
(set! (-> font-ctx origin x) 15.0)
(set! (-> font-ctx origin y) 75.0)
(let* ((entry-count (if dynamic-menu?
((-> this menu-states (-> this curr-state-index) get-dynamic-menu-length))
(-> this menu-states (-> this curr-state-index) entries length)))
(current-index (-> this menu-states (-> this curr-state-index) entry-index))
(start-index (* (/ current-index 15) 15))
(end-index (min (+ start-index 15) entry-count))
(entry-count-to-render (- end-index start-index))
(menu-rows (if (< end-index entry-count) (inc entry-count-to-render) entry-count-to-render))
(widest-entry (if dynamic-menu?
(get-widest-dynamic-entry (-> this menu-states (-> this curr-state-index) get-dynamic-menu-entry-label) page-title font-ctx start-index end-index)
(get-widest-entry (-> this menu-states (-> this curr-state-index) entries) page-title font-ctx start-index end-index))))
(with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf2))
;; background border
(draw-sprite2d-xy buf
6
64
(+ 17 widest-entry) ;; width
(+ 17 (* 15 (inc menu-rows))) ;; height
(static-rgba 255 255 255 75)
#x3fffff)
;; background
(draw-sprite2d-xy buf
7
65
(+ 15 widest-entry) ;; width
(+ 15 (* 15 (inc menu-rows))) ;; height
(static-rgba 0 0 0 255)
#x3fffff)
;; title
;; TODO - function
(pc-encode-utf8-string page-title *pc-encoded-temp-string*)
(set! (-> font-ctx color) (font-color menu-parent))
(let ((old-x (-> font-ctx origin x))
(old-y (-> font-ctx origin y)))
(draw-string-adv *pc-encoded-temp-string* buf font-ctx)
(set! (-> font-ctx origin x) old-x)
(set! (-> font-ctx origin y) old-y))
(set! (-> font-ctx color) (font-color default))
(set! (-> font-ctx origin y) (+ 15.0 (-> font-ctx origin y)))
;; menu contents
(dotimes (i entry-count-to-render)
(if dynamic-menu?
(draw-dynamic-entry (+ i start-index)
(-> this menu-states (-> this curr-state-index) get-dynamic-menu-entry-label)
(-> this menu-states (-> this curr-state-index) dynamic-menu-entry-selected?)
font-ctx
buf
(= (+ i start-index) current-index))
(draw-entry (-> (-> this menu-states (-> this curr-state-index) entries) i) font-ctx buf (= (+ i start-index) current-index)))
(set! (-> font-ctx origin y) (+ 15.0 (-> font-ctx origin y))))
(when (< end-index entry-count)
(clear *pc-encoded-temp-string*)
(format *pc-encoded-temp-string* "~D more..." (- entry-count end-index))
(pc-encode-utf8-string *pc-encoded-temp-string* *pc-encoded-temp-string*)
(set! (-> font-ctx color) (font-color menu-parent))
(let ((old-x (-> font-ctx origin x))
(old-y (-> font-ctx origin y)))
(draw-string-adv *pc-encoded-temp-string* buf font-ctx)
(set! (-> font-ctx origin x) old-x)
(set! (-> font-ctx origin y) old-y))
(set! (-> font-ctx color) (font-color default))
(set! (-> font-ctx origin y) (+ 15.0 (-> font-ctx origin y))))
;; button prompts
(cond
((= (-> this curr-state-index) 0)
(pc-encode-utf8-string "<PAD_CIRCLE> Exit" *pc-encoded-temp-string*)
)
((and dynamic-menu? can-reset?)
(pc-encode-utf8-string "<PAD_SQUARE> Reset / <PAD_CIRCLE> Back" *pc-encoded-temp-string*))
(else
(pc-encode-utf8-string "<PAD_CIRCLE> Back" *pc-encoded-temp-string*))
)
(set! (-> font-ctx origin x) (- 25.0 (-> font-ctx origin x)))
(set! (-> font-ctx origin y) (+ 10.0 (-> font-ctx origin y)))
(let ((old-x (-> font-ctx origin x))
(old-y (-> font-ctx origin y)))
(draw-string-adv *pc-encoded-temp-string* buf font-ctx)
(set! (-> font-ctx origin x) old-x)
(set! (-> font-ctx origin y) old-y))))))
(defmethod move-up! ((this popup-menu) (amount int))
(let* ((curr-state (-> this menu-states (-> this curr-state-index)))
(new-index (max 0 (-! (-> curr-state entry-index) amount))))
;; dynamic menus don't have options that are disabled (just dont include them)
(when (not (-> curr-state dynamic-menu?))
(let ((entry (-> curr-state entries new-index)))
(when (and (nonzero? (-> entry entry-disabled?)) ((-> entry entry-disabled?)))
(set! new-index (max 0 (dec new-index))))))
(set! (-> curr-state entry-index) new-index)))
(defmethod move-down! ((this popup-menu) (amount int))
(let* ((curr-state (-> this menu-states (-> this curr-state-index)))
(max-entries (if (-> curr-state dynamic-menu?)
((-> curr-state get-dynamic-menu-length))
(-> curr-state entries length)))
(new-index (min (dec max-entries) (+! (-> curr-state entry-index) amount))))
;; dynamic menus don't have options that are disabled (just dont include them)
(when (not (-> curr-state dynamic-menu?))
(let ((entry (-> curr-state entries new-index)))
(when (and (nonzero? (-> entry entry-disabled?)) ((-> entry entry-disabled?)))
(set! new-index (min (dec max-entries) (inc new-index))))))
(set! (-> curr-state entry-index) new-index)))
(defmethod confirm! ((this popup-menu))
(let* ((menu-state (-> this menu-states (-> this curr-state-index)))
(dynamic-menu? (-> menu-state dynamic-menu?)))
(if dynamic-menu?
((-> menu-state on-dynamic-menu-entry-confirm) (-> menu-state entry-index))
(let ((entry (-> menu-state entries (-> menu-state entry-index))))
(cond
((type? entry popup-menu-dynamic-submenu)
;; TODO - dont allow more than 10 nested menus
(inc! (-> this curr-state-index))
(set! (-> this menu-states (-> this curr-state-index) entry-index) 0)
(set! (-> this menu-states (-> this curr-state-index) title) (-> entry label))
(true! (-> this menu-states (-> this curr-state-index) dynamic-menu?))
(set! (-> this menu-states (-> this curr-state-index) get-dynamic-menu-length) (-> (the-as popup-menu-dynamic-submenu entry) get-length))
(set! (-> this menu-states (-> this curr-state-index) get-dynamic-menu-entry-label) (-> (the-as popup-menu-dynamic-submenu entry) get-entry-label))
(set! (-> this menu-states (-> this curr-state-index) on-dynamic-menu-entry-confirm) (-> (the-as popup-menu-dynamic-submenu entry) on-entry-confirm))
(set! (-> this menu-states (-> this curr-state-index) dynamic-menu-entry-selected?) (-> (the-as popup-menu-dynamic-submenu entry) entry-selected?))
(set! (-> this menu-states (-> this curr-state-index) on-dynamic-menu-reset) (-> (the-as popup-menu-dynamic-submenu entry) on-reset)))
((type? entry popup-menu-submenu)
;; TODO - dont allow more than 10 nested menus
(inc! (-> this curr-state-index))
(set! (-> this menu-states (-> this curr-state-index) entry-index) 0)
(false! (-> this menu-states (-> this curr-state-index) dynamic-menu?))
(set! (-> this menu-states (-> this curr-state-index) title) (-> entry label))
(set! (-> this menu-states (-> this curr-state-index) entries) (-> (the-as popup-menu-submenu entry) entries)))
(else
((-> entry on-confirm)))))))
(sound-play "menu-pick"))
(defmethod reset! ((this popup-menu))
(let* ((menu-state (-> this menu-states (-> this curr-state-index))))
(when (and (-> menu-state dynamic-menu?)
(nonzero? (-> menu-state on-dynamic-menu-reset))
(-> menu-state on-dynamic-menu-reset)) ;; dont call if theres no function defined
((-> menu-state on-dynamic-menu-reset))
(sound-play "menu-pick"))))
(defmethod back! ((this popup-menu))
(sound-play "menu-pick")
(cond
((<= (-> this curr-state-index) 0)
#t)
(else
(dec! (-> this curr-state-index))
#f)))
(defbehavior popup-menu-init-by-other popup-menu ((title string) (entries (array popup-menu-entry)))
(process-mask-clear! (-> self mask) menu pause)
(set! (-> self curr-state-index) 0)
(set! (-> self menu-states 0 title) title)
(set! (-> self menu-states 0 entries) entries)
(set! (-> self menu-states 0 entry-index) 0)
(false! (-> self menu-states 0 dynamic-menu?))
(false! (-> self draw?))
(go-virtual idle))
(defbehavior popup-menu-event-handler popup-menu ((proc process) (argc int) (message symbol) (block event-message-block))
(case message
(('open-menu)
(set-master-mode 'menu)
(true! (-> self draw?))
(true! *popup-menu-open*)
(sound-play "menu-pick"))
(('close-menu)
(set-master-mode 'game)
(false! (-> self draw?))
(false! *popup-menu-open*))))
(defmethod update-menu! ((this popup-menu))
(when (-> this draw?)
;; handle input
(cond
((cpad-pressed? 0 select)
(send-event this 'close-menu))
((cpad-pressed? 0 up)
(move-up! this 1))
((cpad-pressed? 0 down)
(move-down! this 1))
((cpad-pressed? 0 left)
(move-up! this 5))
((cpad-pressed? 0 right)
(move-down! this 5))
((cpad-pressed? 0 x)
(confirm! this))
((cpad-pressed? 0 square)
(reset! this))
((cpad-pressed? 0 triangle circle)
(when (back! this)
(send-event this 'close-menu))))
(draw-menu this)))
(defstatehandler popup-menu :event popup-menu-event-handler)
(defstate idle (popup-menu)
:virtual #t
:trans (behavior () (update-menu! self))
:code sleep-code)

View File

@ -328,11 +328,20 @@ static std::unordered_map<std::string,
std::unique_ptr<Res> res_from_json_array(const std::string& name,
const nlohmann::json& json_array,
decompiler::DecompilerTypeSystem& dts) {
ASSERT(!json_array.empty());
std::string array_type = json_array[0].get<std::string>();
if (json_array.empty()) {
throw std::runtime_error(fmt::format("json for {} lump was empty", name));
}
auto& lump = json_array[0];
if (lump.type() != nlohmann::detail::value_t::string) {
throw std::runtime_error(
fmt::format("first entry of lump \"{}\" has json type {}, but should be string", name,
lump.type_name()));
}
auto array_type = lump.get<std::string>();
if (lump_map.find(array_type) != lump_map.end()) {
return lump_map[array_type](name, json_array, dts);
} else {
ASSERT_MSG(false, fmt::format("unsupported array type: {}\n", array_type));
throw std::runtime_error(
fmt::format("unsupported array type for lump {}: {}\n", name, array_type));
}
}