primehack/Source/Core/Core/PrimeHack/Mods/FpsControls.cpp
2022-10-22 07:18:47 -07:00

1954 lines
71 KiB
C++

#include "Core/PrimeHack/Mods/FpsControls.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/PrimeHack/Mods/ContextSensitiveControls.h"
#include "Core/PrimeHack/PrimeUtils.h" //Loads HackConfig.h for us
#include "Common/Timer.h"
#include <cmath>
namespace prime {
namespace {
const std::array<int, 4> prime_one_beams = {0, 2, 1, 3};
const std::array<int, 4> prime_two_beams = {0, 1, 2, 3};
const std::array<std::tuple<int, int>, 4> prime_one_visors = {
std::make_tuple<int, int>(0, 0x11), std::make_tuple<int, int>(2, 0x05),
std::make_tuple<int, int>(3, 0x09), std::make_tuple<int, int>(1, 0x0d)};
const std::array<std::tuple<int, int>, 4> prime_two_visors = {
std::make_tuple<int, int>(0, 0x08), std::make_tuple<int, int>(2, 0x09),
std::make_tuple<int, int>(3, 0x0a), std::make_tuple<int, int>(1, 0x0b)};
const std::array<std::tuple<int, int>, 4> prime_three_visors = {
std::make_tuple<int, int>(0, 0x0b), std::make_tuple<int, int>(1, 0x0c),
std::make_tuple<int, int>(2, 0x0d), std::make_tuple<int, int>(3, 0x0e)};
constexpr u32 ORBIT_STATE_GRAPPLE = 5;
#define RIDLEY_STR(r) (r == Region::NTSC_J ? L"メタリドリー" : L"Meta Ridley")
#define RIDLEY_STR_LEN(r) (r == Region::NTSC_J ? 6 : 11)
bool is_string_ridley(Region active_region, u32 string_base) {
if (string_base == 0) {
return false;
}
const wchar_t * ridley_str = RIDLEY_STR(active_region);
const auto str_len = RIDLEY_STR_LEN(active_region);
int str_idx = 0;
while (read16(string_base) != 0 && str_idx < str_len) {
if (static_cast<wchar_t>(read16(string_base)) != ridley_str[str_idx]) {
return false;
}
str_idx++;
string_base += 2;
}
return str_idx == str_len && read16(string_base) == 0;
}
}
void FpsControls::run_mod(Game game, Region region) {
switch (game) {
case Game::MENU:
case Game::MENU_PRIME_1:
case Game::MENU_PRIME_2:
run_mod_menu(game, region);
break;
case Game::PRIME_1:
run_mod_mp1(region);
break;
case Game::PRIME_2:
run_mod_mp2(region);
break;
case Game::PRIME_3:
case Game::PRIME_3_STANDALONE:
run_mod_mp3(game, region);
break;
case Game::PRIME_1_GCN:
case Game::PRIME_1_GCN_R1:
case Game::PRIME_1_GCN_R2:
run_mod_mp1_gc(region);
break;
case Game::PRIME_2_GCN:
run_mod_mp2_gc(region);
break;
default:
break;
}
}
bool FpsControls::input_disabled() const {
LOOKUP_DYN(menu_state);
return read32(menu_state) != 0;
}
void FpsControls::calculate_pitch_delta() {
if (input_disabled()) {
return;
}
const float compensated_sens = GetSensitivity() * kTurnrateRatio / 60.f;
if (CheckPitchRecentre()) {
calculate_pitch_to_target(0.f);
return;
} else {
// Cancel any interpolation that was taking place.
interpolating = false;
}
pitch += static_cast<float>(GetVerticalAxis()) * compensated_sens *
(InvertedY() ? 1.f : -1.f);
pitch = std::clamp(pitch, -1.52f, 1.52f);
}
void FpsControls::calculate_pitchyaw_delta() {
if (input_disabled()) {
return;
}
constexpr auto yaw_clamp = [](float t) -> float {
constexpr float PI = 3.141592654f;
constexpr float TWO_PI = PI * 2.f;
return (t > PI) ? (t - TWO_PI) : ((t < -PI) ? (t + TWO_PI) : (t));
};
const float compensated_sens = GetSensitivity() * kTurnrateRatio / 60.f;
pitch += static_cast<float>(GetVerticalAxis()) * compensated_sens *
(InvertedY() ? 1.f : -1.f);
pitch = std::clamp(pitch, -1.52f, 1.52f);
yaw += static_cast<float>(GetHorizontalAxis()) * compensated_sens *
(InvertedX() ? 1.f : -1.f);
yaw = yaw_clamp(yaw);
}
void FpsControls::calculate_pitch_locked(Game game, Region region) {
// Calculate the pitch based on the XF matrix to allow us to write out the pitch
// even while locked onto a target, the pitch will be written to match the lock
// angle throughout the entire lock-on. The very first frame when the lock is
// released (and before this mod has run again) the game will still render the
// correct angle. If we stop writing the angle during lock-on then a 1-frame snap
// occurs immediately after releasing the lock, due to the mod running after the
// frame has already been rendered.
LOOKUP_DYN(object_list);
LOOKUP_DYN(camera_manager);
const u16 camera_uid = read16(camera_manager);
if (camera_uid == 0xffff) {
return;
}
const u32 camera = read32(object_list + ((camera_uid & 0x3ff) << 3) + 4);
u32 camera_xf_offset = 0;
switch (game) {
case Game::PRIME_1:
camera_xf_offset = 0x2c;
break;
case Game::PRIME_1_GCN:
camera_xf_offset = 0x34;
break;
case Game::PRIME_2:
camera_xf_offset = 0x20;
break;
case Game::PRIME_2_GCN:
camera_xf_offset = 0x24;
break;
case Game::PRIME_3:
case Game::PRIME_3_STANDALONE:
camera_xf_offset = 0x3c;
break;
}
Transform camera_tf(camera + camera_xf_offset);
pitch = asin(camera_tf.fwd().z);
pitch = std::clamp(pitch, -1.52f, 1.52f);
}
void FpsControls::calculate_pitch_to_target(float target_pitch)
{
// Smoothly transitions pitch to target through interpolation
const float margin = 0.05f;
if (pitch >= (target_pitch - margin) && pitch <= (target_pitch + margin)) {
pitch = target_pitch;
interpolating = false;
return;
}
if (!interpolating) {
delta = 0;
start_pitch = pitch;
interpolating = true;
}
pitch = Lerp(start_pitch, target_pitch, delta / 15.f);
pitch = std::clamp(pitch, -1.52f, 1.52f);
delta++;
return;
}
float FpsControls::calculate_yaw_vel() {
return GetHorizontalAxis() * GetSensitivity() * (InvertedX() ? 1.f : -1.f);;
}
void FpsControls::handle_beam_visor_switch(std::array<int, 4> const &beams,
std::array<std::tuple<int, int>, 4> const &visors) {
// Global array of all powerups (measured in "ammunition"
// even for things like visors/beams)
LOOKUP_DYN(powerups_array);
LOOKUP(powerups_size);
LOOKUP(powerups_offset);
// We copy out the ownership status of beams and visors to our own array for
// get_beam_switch and get_visor_switch
for (int i = 0; i < 4; i++) {
const bool visor_owned =
read32(powerups_array + std::get<1>(visors[i]) *
powerups_size + powerups_offset) ? true : false;
set_visor_owned(i, visor_owned);
if (has_beams) {
const bool beam_owned =
read32(powerups_array + beams[i] *
powerups_size + powerups_offset) ? true : false;
set_beam_owned(i, beam_owned);
}
}
if (has_beams) {
const int beam_id = get_beam_switch(beams);
if (beam_id != -1) {
// Prevent triggering more than one beam swap while one is currently in progress.
std::chrono::duration<double, std::milli> elapsed = hr_clock::now() - beam_scroll_timeout;
if (elapsed.count() > 800) {
prime::GetVariableManager()->set_variable("new_beam", static_cast<u32>(beam_id));
prime::GetVariableManager()->set_variable("beamchange_flag", u32{1});
beam_scroll_timeout = hr_clock::now();
}
}
}
LOOKUP_DYN(active_visor);
int visor_id, visor_off;
std::tie(visor_id, visor_off) = get_visor_switch(visors,
read32(active_visor) == 0);
if (visor_id != -1) {
if (read32(powerups_array + (visor_off * powerups_size) + powerups_offset)) {
write32(visor_id, active_visor);
// Trigger holster animation.
// Prime 3 already animates holstering. Gamecube does not need to use the visor controls.
if (visor_id == 2) {
auto active_game = GetHackManager()->get_active_game();
if (active_game == Game::PRIME_1 || active_game == Game::PRIME_2) {
LOOKUP_DYN(gun_holster_state);
LOOKUP(holster_timer_offset);
write32(3, gun_holster_state);
writef32(0.2f, gun_holster_state + holster_timer_offset); // Holster timer
}
}
}
}
DevInfo("powerups_array", "%08X", powerups_array);
}
void FpsControls::run_mod_menu(Game game, Region region) {
if (region == Region::NTSC_U) {
u32 p0 = read32(GPR(13) - 0x2870);
if (!mem_check(p0)) {
return;
}
p0 = read32(p0 + 0xc54) + 0x9c;
if (!mem_check(p0)) {
return;
}
handle_cursor(p0, p0 + 0xc0, region);
} else if (region == Region::NTSC_J) {
if (game == Game::MENU_PRIME_1) {
handle_cursor(0x805a7da8, 0x805a7dac, region);
}
if (game == Game::MENU_PRIME_2) {
handle_cursor(0x805a7ba8, 0x805a7bac, region);
}
} else if (region == Region::PAL) {
u32 cursor_address = read32(0x80621ffc);
handle_cursor(cursor_address + 0xdc, cursor_address + 0x19c, region);
}
}
void FpsControls::run_mod_mp1(Region region) {
LOOKUP_DYN(player);
if (player == 0) {
return;
}
DevInfo("Player", "%08x", player);
handle_beam_visor_switch(prime_one_beams, prime_one_visors);
CheckBeamVisorSetting(Game::PRIME_1);
// Is beam/visor menu showing on screen
LOOKUP_DYN(beamvisor_menu_state);
bool beamvisor_menu_enabled = read32(beamvisor_menu_state) == 1;
LOOKUP_DYN(orbit_state);
LOOKUP_DYN(lockon_state);
// Allows freelook in grapple, otherwise we are orbiting (locked on) to something
bool locked = (read32(orbit_state) != ORBIT_STATE_GRAPPLE &&
read8(lockon_state) || beamvisor_menu_enabled);
LOOKUP_DYN(cursor);
LOOKUP_DYN(angular_vel);
LOOKUP_DYN(angular_momentum);
LOOKUP_DYN(firstperson_pitch);
LOOKUP(arm_cannon_matrix);
if (locked) {
write32(0, angular_momentum);
write32(0, angular_vel);
calculate_pitch_locked(Game::PRIME_1, region);
writef32(FpsControls::pitch, firstperson_pitch);
writef32(FpsControls::pitch, arm_cannon_matrix);
if (beamvisor_menu_enabled) {
LOOKUP_DYN(beamvisor_menu_mode);
// if the menu id is not null
if (read32(beamvisor_menu_mode) != 0xFFFFFFFF) {
if (menu_open == false) {
set_code_group_state("beam_change", ModState::DISABLED);
}
handle_reticle(cursor + 0x9c, cursor + 0x15c, region, GetFov());
menu_open = true;
}
} else if (HandleReticleLockOn()) { // If we handle menus, this doesn't need to be ran
handle_reticle(cursor + 0x9c, cursor + 0x15c, region, GetFov());
}
} else {
if (menu_open) {
set_code_group_state("beam_change", ModState::ENABLED);
menu_open = false;
}
set_cursor_pos(0, 0);
write32(0, cursor + 0x9c);
write32(0, cursor + 0x15c);
calculate_pitch_delta();
writef32(FpsControls::pitch, firstperson_pitch);
writef32(FpsControls::pitch, arm_cannon_matrix);
LOOKUP(tweakplayer);
// Max pitch angle, as abs val (any higher = gimbal lock)
writef32(1.52f, tweakplayer + 0x134);
write32(0, angular_vel);
LOOKUP_DYN(ball_state);
if (read32(ball_state) == 0) {
writef32(calculate_yaw_vel(), angular_momentum);
}
LOOKUP_DYN(menu_state);
swap_alt_profiles(read32(ball_state), read32(menu_state));
}
}
void FpsControls::run_mod_mp1_gc(Region region) {
const bool show_crosshair = GetShowGCCrosshair();
const u32 crosshair_color_rgba = show_crosshair ? GetGCCrosshairColor() : 0x4b7ea331;
set_code_group_state("show_crosshair", show_crosshair ? ModState::ENABLED : ModState::DISABLED);
LOOKUP(crosshair_color);
if (show_crosshair) {
write32(crosshair_color_rgba, crosshair_color);
}
LOOKUP_DYN(player);
if (player == 0) {
return;
}
DevInfo("Player", "%08x", player);
LOOKUP_DYN(player_xf);
Transform cplayer_xf(player_xf);
LOOKUP_DYN(orbit_state);
const u32 orbit_state_val = read32(orbit_state);
if (orbit_state_val != ORBIT_STATE_GRAPPLE &&
orbit_state_val != 0) {
calculate_pitch_locked(Game::PRIME_1_GCN, region);
LOOKUP_DYN(firstperson_pitch);
writef32(FpsControls::pitch, firstperson_pitch);
vec3 fwd = cplayer_xf.fwd();
yaw = atan2f(fwd.y, fwd.x);
return;
}
LOOKUP_DYN(camera_state);
if (read32(camera_state) != 0) {
vec3 fwd = cplayer_xf.fwd();
yaw = atan2f(fwd.y, fwd.x);
return;
}
calculate_pitchyaw_delta();
LOOKUP(tweak_player);
LOOKUP(grapple_swing_speed_offset);
LOOKUP_DYN(firstperson_pitch);
LOOKUP_DYN(angular_vel);
writef32(FpsControls::pitch, firstperson_pitch);
writef32(1.52f, tweak_player + 0x134);
writef32(0, angular_vel);
cplayer_xf.build_rotation(yaw);
cplayer_xf.write_to(player_xf);
for (int i = 0; i < 8; i++) {
writef32(0, (tweak_player + 0x84) + i * 4);
writef32(0, (tweak_player + 0x84) + i * 4 - 32);
}
writef32(1000.f, tweak_player + grapple_swing_speed_offset);
LOOKUP_DYN(freelook_rotation_speed);
LOOKUP_DYN(air_transitional_friction);
// Freelook rotation speed tweak
write32(0x4f800000, freelook_rotation_speed);
// Air translational friction changes to make diagonal strafe match normal speed
writef32(0.25f, air_transitional_friction);
}
void FpsControls::run_mod_mp2(Region region) {
CheckBeamVisorSetting(Game::PRIME_2);
// VERY similar to mp1, this time CPlayer isn't TOneStatic (presumably because
// of multiplayer mode in the GCN version?)
LOOKUP_DYN(player);
if (player == 0) {
return;
}
DevInfo("Player", "%08x", player);
LOOKUP_DYN(load_state);
if (read32(load_state) != 1) {
return;
}
handle_beam_visor_switch(prime_two_beams, prime_two_visors);
// Is beam/visor menu showing on screen
LOOKUP_DYN(beamvisor_menu_state);
bool beamvisor_menu = read32(beamvisor_menu_state) == 1;
// Allows freelook in grapple, otherwise we are orbiting (locked on) to something
LOOKUP_DYN(orbit_state);
LOOKUP_DYN(lockon_state);
bool locked = (read32(orbit_state) != ORBIT_STATE_GRAPPLE &&
read8(lockon_state) || beamvisor_menu);
LOOKUP_DYN(cursor);
LOOKUP_DYN(angular_momentum);
if (locked) {
// Angular velocity (not really, but momentum) is being messed with like mp1
// just being accessed relative to cplayer
LOOKUP_DYN(firstperson_pitch);
write32(0, angular_momentum);
calculate_pitch_locked(Game::PRIME_2, region);
writef32(FpsControls::pitch, firstperson_pitch);
if (beamvisor_menu) {
LOOKUP_DYN(beamvisor_menu_mode);
u32 mode = read32(beamvisor_menu_mode);
// if the menu id is not null
if (mode != 0xFFFFFFFF) {
if (menu_open == false) {
set_code_group_state("beam_change", ModState::DISABLED);
}
handle_reticle(cursor + 0x9c, cursor + 0x15c, region, GetFov());
menu_open = true;
}
} else if (HandleReticleLockOn()) {
handle_reticle(cursor + 0x9c, cursor + 0x15c, region, GetFov());
}
} else {
if (menu_open) {
set_code_group_state("beam_change", ModState::ENABLED);
menu_open = false;
}
set_cursor_pos(0, 0);
write32(0, cursor + 0x9c);
write32(0, cursor + 0x15c);
calculate_pitch_delta();
// Grab the arm cannon address, go to its transform field (NOT the
// Actor's xf @ 0x30!!)
LOOKUP_DYN(firstperson_pitch);
writef32(FpsControls::pitch, firstperson_pitch);
// For whatever god forsaken reason, writing pitch to the z component of the
// right vector for this xf makes the gun not lag. Won't fix what ain't broken
LOOKUP_DYN(armcannon_matrix);
writef32(FpsControls::pitch, armcannon_matrix + 0x24);
LOOKUP(tweak_player_offset);
u32 tweak_player_address = read32(read32(GPR(13) + tweak_player_offset));
if (mem_check(tweak_player_address)) {
// This one's stored as degrees instead of radians
writef32(87.0896f, tweak_player_address + 0x180);
}
LOOKUP_DYN(ball_state);
if (read32(ball_state) == 0) {
writef32(calculate_yaw_vel(), angular_momentum);
}
// Nothing new here
write32(0, angular_momentum + 0x18);
LOOKUP_DYN(menu_state);
swap_alt_profiles(read32(ball_state), read32(menu_state));
}
}
void null_players_on_destruct_mp2_gc(u32) {
// r27 is an iterator variable pointing to start of statemgr
write32(0, GPR(27) + 0x14fc);
// Original instruction: addi r27, r27, 4
GPR(27) += 4;
}
void FpsControls::run_mod_mp2_gc(Region region) {
LOOKUP_DYN(world);
if (world == 0) {
return;
}
// World loading phase == 4 -> complete
if (read32(world + 0x4) != 4) {
return;
}
LOOKUP_DYN(player);
if (player == 0) {
return;
}
DevInfo("Player", "%08x", player);
const bool show_crosshair = GetShowGCCrosshair();
const u32 crosshair_color_rgba = show_crosshair ? GetGCCrosshairColor() : 0x4b7ea331;
set_code_group_state("show_crosshair", show_crosshair ? ModState::ENABLED : ModState::DISABLED);
LOOKUP(tweakgui_offset);
u32 crosshair_color_addr = read32(read32(GPR(13) + tweakgui_offset)) + 0x268;
if (show_crosshair) {
write32(crosshair_color_rgba, crosshair_color_addr);
}
LOOKUP_DYN(player_xf);
Transform cplayer_xf(player_xf);
LOOKUP_DYN(orbit_state);
LOOKUP_DYN(firstperson_pitch);
if (read32(orbit_state) != ORBIT_STATE_GRAPPLE &&
read32(orbit_state) != 0) {
calculate_pitch_locked(Game::PRIME_2_GCN, region);
writef32(FpsControls::pitch, firstperson_pitch);
vec3 fwd = cplayer_xf.fwd();
yaw = atan2f(fwd.y, fwd.x);
return;
}
LOOKUP(tweak_player_offset);
const u32 tweak_player_address = read32(read32(GPR(13) + tweak_player_offset));
if (mem_check(tweak_player_address)) {
// Freelook rotation speed tweak
write32(0x4f800000, tweak_player_address + 0x188);
// Freelook pitch half-angle range tweak
writef32(87.0896f, tweak_player_address + 0x184);
// Air translational friction changes to make diagonal strafe match normal speed
writef32(0.25f, tweak_player_address + 0x88);
for (int i = 0; i < 8; i++) {
writef32(100000000.f, tweak_player_address + 0xc4 + i * 4);
writef32(1.f, tweak_player_address + 0xa4 + i * 4);
}
}
LOOKUP_DYN(ball_state);
if (read32(ball_state) == 0) {
calculate_pitchyaw_delta();
writef32(FpsControls::pitch, firstperson_pitch);
cplayer_xf.build_rotation(yaw);
cplayer_xf.write_to(player_xf);
}
}
void FpsControls::mp3_handle_lasso(u32 grapple_state_addr) {
if (GrappleCtlBound()) {
set_code_group_state("grapple_lasso", ModState::ENABLED);
// Disable animation code changes if trying to use grapple voltage.
set_code_group_state("grapple_lasso_animation",
CheckForward() || CheckBack() ? ModState::DISABLED : ModState::ENABLED);
}
else {
set_code_group_state("grapple_lasso", ModState::DISABLED);
set_code_group_state("grapple_lasso_animation", ModState::DISABLED);
}
// This is outside of the grapple detection so that people can quickly tap the bind to pull rather than wait.
bool holding_grapple = prime::CheckGrappleCtl();
// If currently locked onto a grapple point. This must be seperate from lock-on for grapple swing.
// 1 => Locked On
// 2 => Grapple Lasso(/Voltage)
// 3 => Grapple Swing
if (read32(grapple_state_addr) == 2) {
if (holding_grapple && !grapple_button_state) {
grapple_button_state = true;
grapple_swap_axis = !grapple_swap_axis;
// 0.45 for repeated taps. 1 will instantly complete the grapple.
if (GrappleTappingMode())
grapple_force += 0.40f;
}
else if (!holding_grapple) {
grapple_button_state = false;
if (!GrappleTappingMode())
grapple_force += 1.f;
}
constexpr float force_delta = 0.045f;
// Use tapping/force method
if (grapple_force > 0) {
grapple_hand_pos += force_delta;
grapple_force -= force_delta;
}
else {
grapple_hand_pos -= 0.045f;
grapple_force = 0;
}
grapple_hand_pos = std::clamp(grapple_hand_pos, 0.f, 1.0f);
prime::GetVariableManager()->set_variable("grapple_hand_x", grapple_hand_pos);
prime::GetVariableManager()->set_variable("grapple_hand_y", grapple_hand_pos / 4);
if (grapple_hand_pos >= 1.0f) {
// State 4 completes the grapple (e.g pull door from frame)
prime::GetVariableManager()->set_variable("grapple_lasso_state", (u32) 4);
} else {
// State 2 "holds" the grapple for lasso/voltage.
prime::GetVariableManager()->set_variable("grapple_lasso_state", (u32) 2);
}
} else {
prime::GetVariableManager()->set_variable("grapple_hand_x", 0.f);
prime::GetVariableManager()->set_variable("grapple_hand_y", 0.f);
prime::GetVariableManager()->set_variable("grapple_lasso_state", (u32) 0);
grapple_hand_pos = 0;
grapple_force = 0;
grapple_button_state = false;
}
}
// this game is
void FpsControls::run_mod_mp3(Game active_game, Region active_region) {
CheckBeamVisorSetting(active_game);
if (GrappleCtlBound()) {
set_code_group_state("grapple_lasso", ModState::ENABLED);
// Disable animation code changes if trying to use grapple voltage.
set_code_group_state("grapple_lasso_animation",
CheckForward() || CheckBack() ? ModState::DISABLED : ModState::ENABLED);
}
else {
set_code_group_state("grapple_lasso", ModState::DISABLED);
set_code_group_state("grapple_lasso_animation", ModState::DISABLED);
}
LOOKUP_DYN(cursor);
const auto mp3_handle_cursor = [cursor, active_region] (bool locked, bool for_reticle) {
if (locked) {
write32(0, cursor + 0x9c);
write32(0, cursor + 0x15c);
} else if (for_reticle) {
handle_reticle(cursor + 0x9c, cursor + 0x15c, active_region, GetFov());
} else {
handle_cursor(cursor + 0x9c, cursor + 0x15c, active_region);
}
};
LOOKUP(state_manager);
LOOKUP_DYN(ball_state);
LOOKUP_DYN(menu_state);
swap_alt_profiles(read32(ball_state), read32(menu_state));
// Handles menu screen cursor
LOOKUP(cursor_dlg_enabled);
if (read8(cursor_dlg_enabled)) {
mp3_handle_cursor(false, false);
return;
}
// In NTSC-J version there is a quiz to select the difficulty
// This checks if we are ingame
// I won't add (state_manager + 0x29C) to the address db, not sure what it is
if (active_region == Region::NTSC_J && read32(state_manager + 0x29C) == 0xffffffff) {
mp3_handle_cursor(false, false);
return;
}
LOOKUP_DYN(player);
if (player == 0) {
return;
}
DevInfo("Player", "%08x", player);
handle_beam_visor_switch({}, prime_three_visors);
LOOKUP_DYN(boss_name);
LOOKUP_DYN(boss_status);
bool is_boss_metaridley = is_string_ridley(active_region, boss_name);
// Compare based on boss name string, Meta Ridley only appears once
if (is_boss_metaridley) {
// If boss is dead
if (read8(boss_status) == 8) {
set_state(ModState::ENABLED);
mp3_handle_cursor(true, true);
} else {
set_state(ModState::CODE_DISABLED);
mp3_handle_cursor(false, true);
return;
}
}
prime::GetVariableManager()->set_variable("trigger_grapple", prime::CheckGrappleCtl() ? u32{1} : u32{0});
LOOKUP_DYN(firstperson_pitch);
LOOKUP_DYN(angular_momentum);
LOOKUP_DYN(beamvisor_menu_state);
LOOKUP_DYN(lockon_type);
LOOKUP(lockon_state);
bool beamvisor_menu = read32(beamvisor_menu_state) == 3;
if ((read32(lockon_type) == 0 && read8(lockon_state)) || read32(lockon_type) == 1 || beamvisor_menu) {
write32(0, angular_momentum);
calculate_pitch_locked(active_game, active_region);
if (HandleReticleLockOn() || beamvisor_menu) {
mp3_handle_cursor(false, true);
}
writef32(FpsControls::pitch, firstperson_pitch);
return;
}
// Handle grapple lasso bind
mp3_handle_lasso(lockon_type);
// Lock Camera according to ContextSensitiveControls and interpolate to pitch 0
if (prime::GetLockCamera() != Unlocked) {
const float target_pitch = Centre == prime::GetLockCamera() ? 0.f : 0.23f;
if (FpsControls::pitch == target_pitch) {
writef32(target_pitch, pitch);
mp3_handle_cursor(false, false);
return;
}
calculate_pitch_to_target(target_pitch);
writef32(FpsControls::pitch, firstperson_pitch);
// Known edge-case, if a user were to exit a camera lock without reaching
// the target (e.g loadstate), the next time they enter a camera lock,
// it will attempt to centre to the last target pitch.
// We cannot reset here due to `Reset Camera Pitch`
return;
}
mp3_handle_cursor(true, true);
set_cursor_pos(0, 0);
calculate_pitch_delta();
// Gun damping uses its own TOC value, so screw it (I checked the binary)
// Byte pattern to find the offset : c0?2???? ec23082a fc60f850
LOOKUP(gun_lag_toc_offset);
u32 rtoc_gun_damp = GPR(2) + gun_lag_toc_offset;
write32(0, rtoc_gun_damp);
writef32(FpsControls::pitch, firstperson_pitch);
if (read32(ball_state) == 0) {
writef32(calculate_yaw_vel(), angular_momentum);
}
// Nothing new here
write32(0, angular_momentum + 0x18);
}
void FpsControls::CheckBeamVisorSetting(Game game)
{
bool beam,visor;
std::tie<bool, bool>(beam, visor) = GetMenuOptions();
switch (game) {
case Game::PRIME_1:
case Game::PRIME_2:
set_code_group_state("beam_menu", beam ? ModState::DISABLED : ModState::ENABLED);
case Game::PRIME_3:
case Game::PRIME_3_STANDALONE:
set_code_group_state("visor_menu", visor ? ModState::DISABLED : ModState::ENABLED);
break;
default:
break;
}
}
bool FpsControls::init_mod(Game game, Region region) {
swap_alt_profiles(0, 0);
switch (game) {
case Game::MENU_PRIME_1:
case Game::MENU_PRIME_2:
init_mod_menu(game, region);
break;
case Game::PRIME_1:
init_mod_mp1(region);
break;
case Game::PRIME_1_GCN:
init_mod_mp1_gc(region);
break;
case Game::PRIME_1_GCN_R1:
init_mod_mp1_gc_r1();
break;
case Game::PRIME_1_GCN_R2:
init_mod_mp1_gc_r2();
break;
case Game::PRIME_2:
init_mod_mp2(region);
break;
case Game::PRIME_2_GCN:
init_mod_mp2_gc(region);
break;
case Game::PRIME_3:
init_mod_mp3(region);
break;
case Game::PRIME_3_STANDALONE:
init_mod_mp3_standalone(region);
break;
default:
break;
}
return true;
}
void FpsControls::add_beam_change_code_mp1(u32 start_point) {
u32 bcf_lis, bcf_ori;
std::tie(bcf_lis, bcf_ori) = prime::GetVariableManager()->make_lis_ori(4, "beamchange_flag");
u32 nb_lis, nb_ori;
std::tie(nb_lis, nb_ori) = prime::GetVariableManager()->make_lis_ori(5, "new_beam");
add_code_change(start_point + 0x00, bcf_lis, "beam_change"); // ; set r4 to beam change base address
add_code_change(start_point + 0x04, bcf_ori, "beam_change"); // ;
add_code_change(start_point + 0x08, 0x80640000, "beam_change"); // lwz r3, 0(r4) ; grab flag
add_code_change(start_point + 0x0c, 0x2c030000, "beam_change"); // cmpwi r3, 0 ; check if beam should change
add_code_change(start_point + 0x10, 0x41820058, "beam_change"); // beq 0x58 ; don't attempt beam change if 0
add_code_change(start_point + 0x14, nb_lis, "beam_change"); // ; set r5 to new beam base address
add_code_change(start_point + 0x18, nb_ori, "beam_change"); // ;
add_code_change(start_point + 0x1c, 0x83450000, "beam_change"); // lwz r26, 0(r5) ; get expected beam (r25, r26 used to assign beam)
add_code_change(start_point + 0x20, 0x7f59d378, "beam_change"); // mr r25, r26 ; copy expected beam to other reg
add_code_change(start_point + 0x24, 0x38600000, "beam_change"); // li r3, 0 ; reset flag
add_code_change(start_point + 0x28, 0x90640000, "beam_change"); // stw r3, 0(r4) ;
add_code_change(start_point + 0x2c, 0x4800003c, "beam_change"); // b 0x3c ; jump forward to beam assign
}
void FpsControls::add_beam_change_code_mp2(u32 start_point) {
u32 bcf_lis, bcf_ori;
std::tie(bcf_lis, bcf_ori) = prime::GetVariableManager()->make_lis_ori(4, "beamchange_flag");
u32 nb_lis, nb_ori;
std::tie(nb_lis, nb_ori) = prime::GetVariableManager()->make_lis_ori(5, "new_beam");
add_code_change(start_point + 0x00, bcf_lis, "beam_change"); // ; set r4 to beam change base address
add_code_change(start_point + 0x04, bcf_ori, "beam_change"); // ;
add_code_change(start_point + 0x08, 0x80640000, "beam_change"); // lwz r3, 0(r4) ; grab flag
add_code_change(start_point + 0x0c, 0x2c030000, "beam_change"); // cmpwi r3, 0 ; check if beam should change
add_code_change(start_point + 0x10, 0x4182005c, "beam_change"); // beq 0x5c ; don't attempt beam change if 0
add_code_change(start_point + 0x14, nb_lis, "beam_change"); // ; set r5 to new beam base address
add_code_change(start_point + 0x18, nb_ori, "beam_change"); // ;
add_code_change(start_point + 0x1c, 0x83e50000, "beam_change"); // lwz r31, 0(r5) ; get expected beam (r31, r30 used to assign beam)
add_code_change(start_point + 0x20, 0x7ffefb78, "beam_change"); // mr r30, r31 ; copy expected beam to other reg
add_code_change(start_point + 0x24, 0x38600000, "beam_change"); // li r3, 0 ; reset flag
add_code_change(start_point + 0x28, 0x90640000, "beam_change"); // stw r3, 0(r4) ;
add_code_change(start_point + 0x2c, 0x48000040, "beam_change"); // b 0x40 ; jump forward to beam assign
}
void FpsControls::add_grapple_slide_code_mp3(u32 start_point) {
add_code_change(start_point + 0x00, 0x60000000); // nop ; trashed because useless
add_code_change(start_point + 0x04, 0x60000000); // nop ; trashed because useless
add_code_change(start_point + 0x08, 0x60000000); // nop ; trashed because useless
add_code_change(start_point + 0x0c, 0xc0010240); // lfs f0, 0x240(r1) ; grab the x component of new origin
add_code_change(start_point + 0x10, 0xd01f0048); // stfs f0, 0x48(r31) ; store it into player's xform x origin (CTransform + 0x0c)
add_code_change(start_point + 0x14, 0xc0010244); // lfs f0, 0x244(r1) ; grab the y component of new origin
add_code_change(start_point + 0x18, 0xd01f0058); // stfs f0, 0x58(r31) ; store it into player's xform y origin (CTransform + 0x1c)
add_code_change(start_point + 0x1c, 0xc0010248); // lfs f0, 0x248(r1) ; grab the z component of new origin
add_code_change(start_point + 0x20, 0xd01f0068); // stfs f0, 0x68(r31) ; store it into player's xform z origin (CTransform + 0xcc)
add_code_change(start_point + 0x28, 0x389f003c); // addi r4, r31, 0x3c ; next sub call is SetTransform, so set player's transform
// ; to their own transform (safety no-op, does other updating too)
}
void FpsControls::add_grapple_lasso_code_mp3(u32 func1, u32 func2, u32 func3) {
u32 lis_x, ori_x, lis_y, ori_y;
std::tie<u32, u32>(lis_x, ori_x) = prime::GetVariableManager()->make_lis_ori(11, "grapple_hand_x");
std::tie<u32, u32>(lis_y, ori_y) = prime::GetVariableManager()->make_lis_ori(11, "grapple_hand_y");
// Controls how tight the lasso is and if to turn yellow.
add_code_change(func1, lis_x, "grapple_lasso");
add_code_change(func1 + 0x4, ori_x, "grapple_lasso");
add_code_change(func1 + 0xC, 0xC04B0000, "grapple_lasso"); // lfs f2, 0(r11)
// Skips the game's checks and lets us control grapple lasso ourselves.
add_code_change(func1 + 0x8, 0x40800158, "grapple_lasso"); // first conditional branch changed to jmp to end
add_code_change(func1 + 0x18, 0x40810148, "grapple_lasso"); // second conditional branch changed to jmp to end
add_code_change(func1 + 0x54, 0x4800010C, "grapple_lasso"); // end of 'yellow' segment jmp to end
// Controls the pulling animation.
add_code_change(func2, lis_x, "grapple_lasso_animation");
add_code_change(func2 + 0x4, ori_x, "grapple_lasso_animation");
add_code_change(func2 + 0x8, 0xC00B0000, "grapple_lasso_animation"); // lfs f0, 0(r11)
add_code_change(func2 + 0xC, lis_y, "grapple_lasso_animation");
add_code_change(func2 + 0x10, ori_y, "grapple_lasso_animation");
add_code_change(func2 + 0x14, 0xC04B0000, "grapple_lasso_animation"); // lfs f2, 0(r11)
add_code_change(func2 + 0x18, 0xD05701C8, "grapple_lasso_animation"); // stfs f2, 0x1C8(r23)
u32 lis, ori;
// Controls the return value of the "ProcessGrappleLasso" function.
std::tie<u32, u32>(lis, ori) = prime::GetVariableManager()->make_lis_ori(30, "grapple_lasso_state");
add_code_change(func1 + 0x160, lis, "grapple_lasso");
add_code_change(func1 + 0x164, ori, "grapple_lasso");
add_code_change(func1 + 0x168, 0x83DE0000, "grapple_lasso"); // lwz r30, 0(r30)
// Triggers grapple.
std::tie<u32, u32>(lis, ori) = prime::GetVariableManager()->make_lis_ori(3, "trigger_grapple");
add_code_change(func3 + 0x0, lis, "grapple_lasso");
add_code_change(func3 + 0x4, ori, "grapple_lasso");
add_code_change(func3 + 0x8, 0x80630000, "grapple_lasso"); // lwz r3, 0(r3)
add_code_change(func3 + 0xC, 0x4E800020, "grapple_lasso"); // blr
}
void FpsControls::add_control_state_hook_mp3(u32 start_point, Region region) {
Game active_game = GetHackManager()->get_active_game();
if (region == Region::NTSC_U) {
if (active_game == Game::PRIME_3) {
add_code_change(start_point + 0x00, 0x3c60805c); // lis r3, 0x805c
add_code_change(start_point + 0x04, 0x38636c40); // addi r3, r3, 0x6c40
} else {
add_code_change(start_point + 0x00, 0x3c60805c); // lis r3, 0x805c
add_code_change(start_point + 0x04, 0x38634f6c); // addi r3, r3, 0x4f6c
}
} else if (region == Region::NTSC_J) {
if (active_game == Game::PRIME_3_STANDALONE)
{
add_code_change(start_point + 0x00, 0x3c60805d); // lis r3, 0x805d
add_code_change(start_point + 0x04, 0x3863aa30); // subi r3, r3, 0x55d0
}
} else if (region == Region::PAL) {
if (active_game == Game::PRIME_3) {
add_code_change(start_point + 0x00, 0x3c60805d); // lis r3, 0x805d
add_code_change(start_point + 0x04, 0x3863a0c0); // subi r3, r3, 0x5f40
}
else {
add_code_change(start_point + 0x00, 0x3c60805c); // lis r3, 0x805c
add_code_change(start_point + 0x04, 0x38637570); // addi r3, r3, 0x7570
}
}
add_code_change(start_point + 0x08, 0x8063002c); // lwz r3, 0x2c(r3)
if (active_game == Game::PRIME_3_STANDALONE && region == Region::NTSC_U) {
add_code_change(start_point + 0x0c, 0x60000000); // nop
} else {
add_code_change(start_point + 0x0c, 0x80630004); // lwz r3, 0x04(r3)
}
add_code_change(start_point + 0x10, 0x80632184); // lwz r3, 0x2184(r3)
add_code_change(start_point + 0x14, 0x7c03f800); // cmpw r3, r31
add_code_change(start_point + 0x18, 0x4d820020); // beqlr
add_code_change(start_point + 0x1c, 0x7fe3fb78); // mr r3, r31
add_code_change(start_point + 0x20, 0x90c30078); // stw r6, 0x78(r3)
add_code_change(start_point + 0x24, 0x4e800020); // blr
}
// Truly cursed
void FpsControls::add_strafe_code_mp1_100(Game revision) {
const bool is_v100 = revision == Game::PRIME_1_GCN;
// calculate side movement @ 805afc00
// stwu r1, 0x18(r1)
// mfspr r0, LR
// stw r0, 0x1c(r1)
// lwz r5, -0x5ee8(r13)
// lwz r4, 0x2b0(r29)
// cmpwi r4, 2
// li r4, 4
// bne 0x8
// lwz r4, 0x2ac(r29)
// slwi r4, r4, 2
// add r3, r4, r5
// lfs f1, 0x44(r3)
// lfs f2, 0x4(r3)
// fmuls f3, f2, f27
// lfs f0, 0xe8(r29)
// fmuls f1, f1, f0
// fdivs f1, f1, f3
// lfs f0, 0xa4(r3)
// stfs f0, 0x10(r1)
// fmuls f1, f1, f0
// lfs f0, -0x4260(r2)
// fcmpo cr0, f30, f0
// lfs f0, -0x4238(r2)
// ble 0x8
// lfs f0, -0x4280(r2)
// fmuls f0, f0, f1
// lfs f3, 0x10(r1)
// fsubs f3, f3, f1
// fmuls f3, f3, f30
// fadds f0, f0, f3
// stfs f0, 0x18(r1)
// stfs f2, 0x14(r1)
// addi r3, r1, 0x4
// addi r4, r29, 0x34
// addi r5, r29, 0x138
// bl 0xFFD62D98
// lfs f0, 0x18(r1)
// lfs f1, 0x4(r1)
// fsubs f0, f0, f1
// lfs f1, 0x10(r1)
// fdivs f0, f0, f1
// lfs f1, -0x4238(r2)
// fcmpo cr0, f0, f1
// bge 0xc
// fmr f0, f1
// b 0x14
// lfs f1, -0x4280(r2)
// fcmpo cr0, f0, f1
// ble 0x8
// fmr f0, f1
// lfs f1, 0x14(r1)
// fmuls f1, f0, f1
// lwz r0, 0x1c(r1)
// mtspr LR, r0
// addi r1, r1, -0x18
// blr
add_code_change(0x805afc00, 0x94210018);
add_code_change(0x805afc04, 0x7c0802a6);
add_code_change(0x805afc08, 0x9001001c);
add_code_change(0x805afc0c, 0x80ada118);
add_code_change(0x805afc10, 0x809d02b0);
add_code_change(0x805afc14, 0x2c040002);
add_code_change(0x805afc18, 0x38800004);
add_code_change(0x805afc1c, 0x40820008);
add_code_change(0x805afc20, 0x809d02ac);
add_code_change(0x805afc24, 0x5484103a);
add_code_change(0x805afc28, 0x7c642a14);
add_code_change(0x805afc2c, 0xc0230044);
add_code_change(0x805afc30, 0xc0430004);
add_code_change(0x805afc34, 0xec6206f2);
add_code_change(0x805afc38, 0xc01d00e8);
add_code_change(0x805afc3c, 0xec210032);
add_code_change(0x805afc40, 0xec211824);
add_code_change(0x805afc44, 0xc00300a4);
add_code_change(0x805afc48, 0xd0010010);
add_code_change(0x805afc4c, 0xec210032);
add_code_change(0x805afc50, 0xc002bda0);
add_code_change(0x805afc54, 0xfc1e0040);
add_code_change(0x805afc58, 0xc002bdc8);
add_code_change(0x805afc5c, 0x40810008);
add_code_change(0x805afc60, 0xc002bd80);
add_code_change(0x805afc64, 0xec000072);
add_code_change(0x805afc68, 0xc0610010);
add_code_change(0x805afc6c, 0xec630828);
add_code_change(0x805afc70, 0xec6307b2);
add_code_change(0x805afc74, 0xec00182a);
add_code_change(0x805afc78, 0xd0010018);
add_code_change(0x805afc7c, 0xd0410014);
add_code_change(0x805afc80, 0x38610004);
add_code_change(0x805afc84, 0x389d0034);
add_code_change(0x805afc88, 0x38bd0138);
add_code_change(0x805afc8c, is_v100 ? 0x4bd62d99 : 0x4bd62e79);
add_code_change(0x805afc90, 0xc0010018);
add_code_change(0x805afc94, 0xc0210004);
add_code_change(0x805afc98, 0xec000828);
add_code_change(0x805afc9c, 0xc0210010);
add_code_change(0x805afca0, 0xec000824);
add_code_change(0x805afca4, 0xc022bdc8);
add_code_change(0x805afca8, 0xfc000840);
add_code_change(0x805afcac, 0x4080000c);
add_code_change(0x805afcb0, 0xfc000890);
add_code_change(0x805afcb4, 0x48000014);
add_code_change(0x805afcb8, 0xc022bd80);
add_code_change(0x805afcbc, 0xfc000840);
add_code_change(0x805afcc0, 0x40810008);
add_code_change(0x805afcc4, 0xfc000890);
add_code_change(0x805afcc8, 0xc0210014);
add_code_change(0x805afccc, 0xec200072);
add_code_change(0x805afcd0, 0x8001001c);
add_code_change(0x805afcd4, 0x7c0803a6);
add_code_change(0x805afcd8, 0x3821ffe8);
add_code_change(0x805afcdc, 0x4e800020);
u32 inject_base = is_v100 ? 0x802875c4 : 0x80287640;
// Apply strafe force instead of torque v1.00 @ 802875c4 | v1.01 @ 80287640
// lfs f1, -0x4260(r2)
// lfs f0, -0x41bc(r2)
// fsubs f1, f30, f1
// fabs f1, f1
// fcmpo cr0, f1, f0
// ble 0x2c
// bl 0x328624
// bl 0xFFD93F54
// mr r5, r3
// mr r3, r29
// lfs f0, -0x4260(r2)
// stfs f1, 0x10(r1)
// stfs f0, 0x14(r1)
// stfs f0, 0x18(r1)
// addi r4, r1, 0x10
add_code_change(inject_base + 0x0, 0xc022bda0);
add_code_change(inject_base + 0x4, 0xc002be44);
add_code_change(inject_base + 0x8, 0xec3e0828);
add_code_change(inject_base + 0xc, 0xfc200a10);
add_code_change(inject_base + 0x10, 0xfc010040);
add_code_change(inject_base + 0x14, 0x4081002c);
add_code_change(inject_base + 0x18, is_v100 ? 0x48328625 : 0x483285a9);
add_code_change(inject_base + 0x1c, is_v100 ? 0x4bd93f55 : 0x4bd93f55);
add_code_change(inject_base + 0x20, 0x7c651b78);
add_code_change(inject_base + 0x24, 0x7fa3eb78);
add_code_change(inject_base + 0x28, 0xc002bda0);
add_code_change(inject_base + 0x2c, 0xd0210010);
add_code_change(inject_base + 0x30, 0xd0010014);
add_code_change(inject_base + 0x34, 0xd0010018);
add_code_change(inject_base + 0x38, 0x38810010);
// disable rotation on LR analog
if (is_v100) {
add_code_change(0x80286fe0, 0x4bfffc71);
add_code_change(0x80286c88, 0x4800000c);
add_code_change(0x8028739c, 0x60000000);
add_code_change(0x802873e0, 0x60000000);
add_code_change(0x8028707c, 0x60000000);
add_code_change(0x802871bc, 0x60000000);
add_code_change(0x80287288, 0x60000000);
} else {
add_code_change(0x8028705c, 0x4bfffc71);
add_code_change(0x80286d04, 0x4800000c);
add_code_change(0x80287418, 0x60000000);
add_code_change(0x8028745c, 0x60000000);
add_code_change(0x802870f8, 0x60000000);
add_code_change(0x80287238, 0x60000000);
add_code_change(0x80287288, 0x60000000);
}
// Clamp current xy velocity v1.00 @ 802872a4 | v1.01 @ 80287320
// lfs f1, -0x7ec0(r2)
// fmuls f0, f30, f30
// fcmpo cr0, f0, f1
// ble 0x134
// fmuls f0, f31, f31m
// fcmpo cr0, f0, f1
// ble 0x128
// lfs f0, 0x138(r29)
// lfs f1, 0x13c(r29)
// fmuls f0, f0, f0
// fmadds f1, f1, f1, f0
// frsqrte f1, f1
// fres f1, f1
// addi r3, r2, -0x2040
// slwi r0, r0, 2
// add r3, r0, r3
// lfs f0, 0(r3)
// fcmpo cr0, f1, f0
// ble 0xf8
// lfs f3, 0xe8(r29)
// lfs f2, 0x138(r29)
// fdivs f2, f2, f1
// fmuls f2, f0, f2
// stfs f2, 0x138(r29)
// fmuls f2, f3, f2
// stfs f2, 0xfc(r29)
// lfs f2, 0x13c(r29)
// fdivs f2, f2, f1
// fmuls f2, f0, f2
// stfs f2, 0x13c(r29)
// fmuls f2, f3, f2
// stfs f2, 0x100(r29)
// b 0xc0
inject_base = is_v100 ? 0x802872a4 : 0x80287320;
add_code_change(inject_base + 0x00, 0xc0228140);
add_code_change(inject_base + 0x04, 0xec1e07b2);
add_code_change(inject_base + 0x08, 0xfc000840);
add_code_change(inject_base + 0x0c, 0x40810134);
add_code_change(inject_base + 0x10, 0xec1f07f2);
add_code_change(inject_base + 0x14, 0xfc000840);
add_code_change(inject_base + 0x18, 0x40810128);
add_code_change(inject_base + 0x1c, 0xc01d0138);
add_code_change(inject_base + 0x20, 0xc03d013c);
add_code_change(inject_base + 0x24, 0xec000032);
add_code_change(inject_base + 0x28, 0xec21007a);
add_code_change(inject_base + 0x2c, 0xfc200834);
add_code_change(inject_base + 0x30, 0xec200830);
add_code_change(inject_base + 0x34, 0x3862dfc0);
add_code_change(inject_base + 0x38, 0x5400103a);
add_code_change(inject_base + 0x3c, 0x7c601a14);
add_code_change(inject_base + 0x40, 0xc0030000);
add_code_change(inject_base + 0x44, 0xfc010040);
add_code_change(inject_base + 0x48, 0x408100f8);
add_code_change(inject_base + 0x4c, 0xc07d00e8);
add_code_change(inject_base + 0x50, 0xc05d0138);
add_code_change(inject_base + 0x54, 0xec420824);
add_code_change(inject_base + 0x58, 0xec4000b2);
add_code_change(inject_base + 0x5c, 0xd05d0138);
add_code_change(inject_base + 0x60, 0xec4300b2);
add_code_change(inject_base + 0x64, 0xd05d00fc);
add_code_change(inject_base + 0x68, 0xc05d013c);
add_code_change(inject_base + 0x6c, 0xec420824);
add_code_change(inject_base + 0x70, 0xec4000b2);
add_code_change(inject_base + 0x74, 0xd05d013c);
add_code_change(inject_base + 0x78, 0xec4300b2);
add_code_change(inject_base + 0x7c, 0xd05d0100);
add_code_change(inject_base + 0x80, 0x480000c0);
// max speed values table @ 805afce0
add_code_change(0x805afce0, 0x41480000);
add_code_change(0x805afce4, 0x41480000);
add_code_change(0x805afce8, 0x41480000);
add_code_change(0x805afcec, 0x41480000);
add_code_change(0x805afcf0, 0x41480000);
add_code_change(0x805afcf4, 0x41480000);
add_code_change(0x805afcf8, 0x41480000);
add_code_change(0x805afcfc, 0x41480000);
}
void FpsControls::add_strafe_code_mp1_102(Region region) {
const bool is_ntsc = region == Region::NTSC_U;
// calculate side movement @ 80471c00
// stwu r1, 0x18(r1)
// mfspr r0, LR
// stw r0, 0x1c(r1)
// lwz r5, -0x5e70(r13) -> -0x5ec8
// lwz r4, 0x2c0(r29)
// cmpwi r4, 2
// li r4, 4
// bne 0x8
// lwz r4, 0x2bc(r29)
// slwi r4, r4, 2
// add r3, r4, r5
// lfs f1, 0x44(r3)
// lfs f2, 0x4(r3)
// fmuls f3, f2, f27
// lfs f0, 0xf8(r29)
// fmuls f1, f1, f0
// fdivs f1, f1, f3
// lfs f0, 0xa4(r3)
// stfs f0, 0x10(r1)
// fmuls f1, f1, f0
// lfs f0, -0x4180(r2) = 0.0f
// fcmpo cr0, f30, f0
// lfs f0, -0x4158(r2) = -1
// ble 0x8
// lfs f0, -0x41A0(r2) = 1
// fmuls f0, f0, f1
// lfs f3, 0x10(r1)
// fsubs f3, f3, f1
// fmuls f3, f3, f30
// fadds f0, f0, f3
// stfs f0, 0x18(r1)
// stfs f2, 0x14(r1)
// addi r3, r1, 0x4
// addi r4, r29, 0x34
// addi r5, r29, 0x148
// bl 0xffe89b68
// lfs f0, 0x18(r1)
// lfs f1, 0x4(r1)
// fsubs f0, f0, f1
// lfs f1, 0x10(r1)
// fdivs f0, f0, f1
// lfs f1, -0x4158(r2) = -1
// fcmpo cr0, f0, f1
// bge 0xc
// fmr f0, f1
// b 0x14
// lfs f1, -0x41A0(r2) = 1
// fcmpo cr0, f0, f1
// ble 0x8
// fmr f0, f1
// lfs f1, 0x14(r1)
// fmuls f1, f0, f1
// lwz r0, 0x1c(r1)
// mtspr LR, r0
// addi r1, r1, -0x18
// blr
u32 inject_base = is_ntsc ? 0x805b0c00 : 0x80471c00;
add_code_change(inject_base + 0x00, 0x94210018);
add_code_change(inject_base + 0x04, 0x7c0802a6);
add_code_change(inject_base + 0x08, 0x9001001c);
add_code_change(inject_base + 0x0c, is_ntsc ? 0x80ada138 : 0x80ada190);
add_code_change(inject_base + 0x10, 0x809d02c0);
add_code_change(inject_base + 0x14, 0x2c040002);
add_code_change(inject_base + 0x18, 0x38800004);
add_code_change(inject_base + 0x1c, 0x40820008);
add_code_change(inject_base + 0x20, 0x809d02bc);
add_code_change(inject_base + 0x24, 0x5484103a);
add_code_change(inject_base + 0x28, 0x7c642a14);
add_code_change(inject_base + 0x2c, 0xc0230044);
add_code_change(inject_base + 0x30, 0xc0430004);
add_code_change(inject_base + 0x34, 0xec6206f2);
add_code_change(inject_base + 0x38, 0xc01d00f8);
add_code_change(inject_base + 0x3c, 0xec210032);
add_code_change(inject_base + 0x40, 0xec211824);
add_code_change(inject_base + 0x44, 0xc00300a4);
add_code_change(inject_base + 0x48, 0xd0010010);
add_code_change(inject_base + 0x4c, 0xec210032);
add_code_change(inject_base + 0x50, is_ntsc ? 0xc002be70 : 0xc002be80);
add_code_change(inject_base + 0x54, 0xfc1e0040);
add_code_change(inject_base + 0x58, is_ntsc ? 0xc002bdd0 : 0xc002bea8);
add_code_change(inject_base + 0x5c, 0x40810008);
add_code_change(inject_base + 0x60, is_ntsc ? 0xc002be80 : 0xc002be60);
add_code_change(inject_base + 0x64, 0xec000072);
add_code_change(inject_base + 0x68, 0xc0610010);
add_code_change(inject_base + 0x6c, 0xec630828);
add_code_change(inject_base + 0x70, 0xec6307b2);
add_code_change(inject_base + 0x74, 0xec00182a);
add_code_change(inject_base + 0x78, 0xd0010018);
add_code_change(inject_base + 0x7c, 0xd0410014);
add_code_change(inject_base + 0x80, 0x38610004);
add_code_change(inject_base + 0x84, 0x389d0034);
add_code_change(inject_base + 0x88, 0x38bd0148);
add_code_change(inject_base + 0x8c, is_ntsc ? 0x4bd627e9 : 0x4be89b69);
add_code_change(inject_base + 0x90, 0xc0010018);
add_code_change(inject_base + 0x94, 0xc0210004);
add_code_change(inject_base + 0x98, 0xec000828);
add_code_change(inject_base + 0x9c, 0xc0210010);
add_code_change(inject_base + 0xa0, 0xec000824);
add_code_change(inject_base + 0xa4, is_ntsc ? 0xc022bdd0 : 0xc022bea8);
add_code_change(inject_base + 0xa8, 0xfc000840);
add_code_change(inject_base + 0xac, 0x4080000c);
add_code_change(inject_base + 0xb0, 0xfc000890);
add_code_change(inject_base + 0xb4, 0x48000014);
add_code_change(inject_base + 0xb8, is_ntsc ? 0xc022be80 : 0xc022be60);
add_code_change(inject_base + 0xbc, 0xfc000840);
add_code_change(inject_base + 0xc0, 0x40810008);
add_code_change(inject_base + 0xc4, 0xfc000890);
add_code_change(inject_base + 0xc8, 0xc0210014);
add_code_change(inject_base + 0xcc, 0xec200072);
add_code_change(inject_base + 0xd0, 0x8001001c);
add_code_change(inject_base + 0xd4, 0x7c0803a6);
add_code_change(inject_base + 0xd8, 0x3821ffe8);
add_code_change(inject_base + 0xdc, 0x4e800020);
// Apply strafe force instead of torque @ 802749a8
// lfs f1, -0x4180(r2) = 0.0f
// lfs f0, -0x40DC(r2) = 1e-5
// fsubs f1, f30, f1
// fabs f1, f1
// fcmpo cr0, f1, f0
// ble 0x2c
// bl 0x1fd240
// bl 0xffda74e0
// mr r5, r3
// mr r3, r29
// lfs f0, -0x4180(r2) = 0.0f
// stfs f1, 0x10(r1)
// stfs f0, 0x14(r1)
// stfs f0, 0x18(r1)
// addi r4, r1, 0x10
inject_base = is_ntsc ? 0x80287f50 : 0x802749a8;
add_code_change(inject_base + 0x00, is_ntsc ? 0xc022be70 : 0xc022be80);
add_code_change(inject_base + 0x04, is_ntsc ? 0xc002bc94 : 0xc002bf24);
add_code_change(inject_base + 0x08, 0xec3e0828);
add_code_change(inject_base + 0x0c, 0xfc200a10);
add_code_change(inject_base + 0x10, 0xfc010040);
add_code_change(inject_base + 0x14, 0x4081002c);
add_code_change(inject_base + 0x18, is_ntsc ? 0x48328c99 : 0x481fd241);
add_code_change(inject_base + 0x1c, is_ntsc ? 0x4bd938a9 : 0x4bda74e1);
add_code_change(inject_base + 0x20, 0x7c651b78);
add_code_change(inject_base + 0x24, 0x7fa3eb78);
add_code_change(inject_base + 0x28, is_ntsc ? 0xc002be70 : 0xc002be80);
add_code_change(inject_base + 0x2c, 0xd0210010);
add_code_change(inject_base + 0x30, 0xd0010014);
add_code_change(inject_base + 0x34, 0xd0010018);
add_code_change(inject_base + 0x38, 0x38810010);
// disable rotation on LR analog
if (is_ntsc) {
add_code_change(0x8028796c, 0x4bfffc71); // jump/address updated
add_code_change(0x80287614, 0x4800000c); // updated following addresses
add_code_change(0x80287d28, 0x60000000);
add_code_change(0x80287d6c, 0x60000000);
add_code_change(0x80287a08, 0x60000000);
add_code_change(0x80287b48, 0x60000000);
add_code_change(0x80287c14, 0x60000000);
} else {
add_code_change(0x802743c4, 0x4bfffc71); // jump/address updated
add_code_change(0x8027406c, 0x4800000c); // updated following addresses
add_code_change(0x80274780, 0x60000000);
add_code_change(0x802747c4, 0x60000000);
add_code_change(0x80274460, 0x60000000);
add_code_change(0x802745a0, 0x60000000);
add_code_change(0x8027466c, 0x60000000);
}
// Clamp current xy velocity NTSC @ 80287c30 | PAL @ 80274688
// lfs f1, -0x7ec0(r2) = 0.1
// fmuls f0, f30, f30
// fcmpo cr0, f0, f1
// ble 0x134
// fmuls f0, f31, f31
// fcmpo cr0, f0, f1
// ble 0x128
// lfs f0, 0x148(r29)
// lfs f1, 0x14c(r29)
// fmuls f0, f0, f0
// fmadds f1, f1, f1, f0
// frsqrte f1, f1
// fres f1, f1
// addi r3, r2, -0x2180 or -0x2100
// slwi r0, r0, 2
// add r3, r0, r3
// lfs f0, 0(r3)
// fcmpo cr0, f1, f0
// ble 0xf8
// lfs f3, 0xf8(r29)
// lfs f2, 0x148(r29)
// fdivs f2, f2, f1
// fmuls f2, f0, f2
// stfs f2, 0x148(r29)
// fmuls f2, f3, f2
// stfs f2, 0x10c(r29)
// lfs f2, 0x14c(r29)
// fdivs f2, f2, f1
// fmuls f2, f0, f2
// stfs f2, 0x14c(r29)
// fmuls f2, f3, f2
// stfs f2, 0x110(r29)
// b 0xc0
inject_base = is_ntsc ? 0x80287c30 : 0x80274688;
add_code_change(inject_base + 0x00, 0xc0228140);
add_code_change(inject_base + 0x04, 0xec1e07b2);
add_code_change(inject_base + 0x08, 0xfc000840);
add_code_change(inject_base + 0x0c, 0x40810134);
add_code_change(inject_base + 0x10, 0xec1f07f2);
add_code_change(inject_base + 0x14, 0xfc000840);
add_code_change(inject_base + 0x18, 0x40810128);
add_code_change(inject_base + 0x1c, 0xc01d0148);
add_code_change(inject_base + 0x20, 0xc03d014c);
add_code_change(inject_base + 0x24, 0xec000032);
add_code_change(inject_base + 0x28, 0xec21007a);
add_code_change(inject_base + 0x2c, 0xfc200834);
add_code_change(inject_base + 0x30, 0xec200830);
add_code_change(inject_base + 0x34, is_ntsc ? 0x3862df00 : 0x3862de80);
add_code_change(inject_base + 0x38, 0x5400103a);
add_code_change(inject_base + 0x3c, 0x7c601a14);
add_code_change(inject_base + 0x40, 0xc0030000);
add_code_change(inject_base + 0x44, 0xfc010040);
add_code_change(inject_base + 0x48, 0x408100f8);
add_code_change(inject_base + 0x4c, 0xc07d00f8);
add_code_change(inject_base + 0x50, 0xc05d0148);
add_code_change(inject_base + 0x54, 0xec420824);
add_code_change(inject_base + 0x58, 0xec4000b2);
add_code_change(inject_base + 0x5c, 0xd05d0148);
add_code_change(inject_base + 0x60, 0xec4300b2);
add_code_change(inject_base + 0x64, 0xd05d010c);
add_code_change(inject_base + 0x68, 0xc05d014c);
add_code_change(inject_base + 0x6c, 0xec420824);
add_code_change(inject_base + 0x70, 0xec4000b2);
add_code_change(inject_base + 0x74, 0xd05d014c);
add_code_change(inject_base + 0x78, 0xec4300b2);
add_code_change(inject_base + 0x7c, 0xd05d0110);
add_code_change(inject_base + 0x80, 0x480000c0);
// No change needed
// max speed values table NTSC @ 805b2de0 | PAL @ 80471ce0
inject_base = is_ntsc ? 0x805b2de0 : 0x80471ce0;
add_code_change(inject_base + 0x00, 0x41480000);
add_code_change(inject_base + 0x04, 0x41480000);
add_code_change(inject_base + 0x08, 0x41480000);
add_code_change(inject_base + 0x0c, 0x41480000);
add_code_change(inject_base + 0x10, 0x41480000);
add_code_change(inject_base + 0x14, 0x41480000);
add_code_change(inject_base + 0x18, 0x41480000);
add_code_change(inject_base + 0x1c, 0x41480000);
}
void FpsControls::init_mod_menu(Game game, Region region)
{
if (region == Region::NTSC_J) {
if (game == Game::MENU_PRIME_1) {
// prevent wiimote pointer feedback to move the cursor
add_code_change(0x80487160, 0x60000000);
add_code_change(0x80487164, 0x60000000);
// Prevent recentering the cursor on X axis
add_code_change(0x80487090, 0x60000000);
// Prevent recentering the cursor on Y axis
add_code_change(0x80487098, 0x60000000);
}
if (game == Game::MENU_PRIME_2) {
// prevent wiimote pointer feedback to move the cursor
add_code_change(0x80486fe8, 0x60000000);
add_code_change(0x80486fec, 0x60000000);
// Prevent recentering the cursor on X axis
add_code_change(0x80486f18, 0x60000000);
// Prevent recentering the cursor on Y axis
add_code_change(0x80486f20, 0x60000000);
}
}
}
void FpsControls::init_mod_mp1(Region region) {
prime::GetVariableManager()->register_variable("new_beam");
prime::GetVariableManager()->register_variable("beamchange_flag");
if (region == Region::NTSC_U) {
// This instruction change is used in all 3 games, all 3 regions. It's an update to what I believe
// to be interpolation for player camera pitch The change is from fmuls f0, f0, f1 (0xec000072) to
// fmuls f0, f1, f1 (0xec010072), where f0 is the output. The higher f0 is, the faster the pitch
// *can* change in-game, and squaring f1 seems to do the job.
add_code_change(0x80098ee4, 0xec010072);
// NOPs for changing First Person Camera's pitch based on floor (normal? angle?)
add_code_change(0x80099138, 0x60000000);
add_code_change(0x80183a8c, 0x60000000);
add_code_change(0x80183a64, 0x60000000);
add_code_change(0x8017661c, 0x60000000);
// Cursor location, sets to f17 (always 0 due to little use)
add_code_change(0x802fb5b4, 0xd23f009c);
add_code_change(0x8019fbcc, 0x60000000);
add_code_change(0x80075f24, 0x60000000, "beam_menu");
add_code_change(0x80075f0c, 0x60000000, "visor_menu");
add_beam_change_code_mp1(0x8018e544);
// Steps over bounds checking on the reticle
add_code_change(0x80015164, 0x4800010c);
} else if (region == Region::PAL) {
// Same as NTSC but slightly offset
add_code_change(0x80099068, 0xec010072);
add_code_change(0x800992c4, 0x60000000);
add_code_change(0x80183cfc, 0x60000000);
add_code_change(0x80183d24, 0x60000000);
add_code_change(0x801768b4, 0x60000000);
add_code_change(0x802fb84c, 0xd23f009c);
add_code_change(0x8019fe64, 0x60000000);
add_code_change(0x80075f74, 0x60000000, "beam_menu");
add_code_change(0x80075f8c, 0x60000000, "visor_menu");
add_beam_change_code_mp1(0x8018e7dc);
// Steps over bounds checking on the reticle
add_code_change(0x80015164, 0x4800010c);
} else { // region == Region::NTSC-J
// Same as NTSC but slightly offset
add_code_change(0x80099060, 0xec010072);
add_code_change(0x800992b4, 0x60000000);
add_code_change(0x8018460c, 0x60000000);
add_code_change(0x801835e4, 0x60000000);
add_code_change(0x80176ff0, 0x60000000);
add_code_change(0x802fb234, 0xd23f009c);
add_code_change(0x801a074c, 0x60000000);
add_code_change(0x800760a4, 0x60000000, "beam_menu");
add_code_change(0x8007608c, 0x60000000, "visor_menu");
add_beam_change_code_mp1(0x8018f0c4);
}
has_beams = true;
}
void FpsControls::init_mod_mp1_gc(Region region) {
if (region == Region::NTSC_U) {
//add_code_change(0x8000f63c, 0x48000048);
add_code_change(0x800ea15c, 0x38810044); // output cannon bob only for viewbob
add_code_change(0x8000e538, 0x60000000);
//add_code_change(0x80016ee4, 0x4e800020);
add_code_change(0x80014820, 0x4e800020);
add_code_change(0x8000e73c, 0x60000000);
add_code_change(0x8000f810, 0x48000244);
// When attached to a grapple point and spinning around it
// the player's yaw is adjusted, this ensures only position is updated
// Grapple point yaw fix
add_code_change(0x8017a18c, 0x7fa3eb78);
add_code_change(0x8017a190, 0x3881006c);
add_code_change(0x8017a194, 0x4bed8cf9);
// Show crosshair but don't consider pressing R button
add_code_change(0x80016ee4, 0x3b000001, "show_crosshair"); // li r24, 1
add_code_change(0x80016ee8, 0x8afd09c4, "show_crosshair"); // lbz r23, 0x9c4(r29)
add_code_change(0x80016eec, 0x53173672, "show_crosshair"); // rlwimi r23, r24, 6, 25, 25 (00000001)
add_code_change(0x80016ef0, 0x9afd09c4, "show_crosshair"); // stb r23, 0x9c4(r29)
add_code_change(0x80016ef4, 0x4e800020, "show_crosshair"); // blr
add_strafe_code_mp1_100(Game::PRIME_1_GCN);
} else if (region == Region::PAL) {
//add_code_change(0x8000fb4c, 0x48000048);
add_code_change(0x800e2190, 0x38810044); // output cannon bob only for viewbob
add_code_change(0x8000ea60, 0x60000000);
//add_code_change(0x80017878, 0x4e800020);
add_code_change(0x80015258, 0x4e800020);
add_code_change(0x8000ec64, 0x60000000);
add_code_change(0x8000fd20, 0x4800022c);
// Grapple point yaw fix
add_code_change(0x8016fc54, 0x7fa3eb78);
add_code_change(0x8016fc58, 0x38810064); // 6c-8 = 64
add_code_change(0x8016fc5c, 0x4bee4345); // bl 80053fa0
// Show crosshair but don't consider pressing R button
add_code_change(0x80017878, 0x3b000001, "show_crosshair"); // li r24, 1
add_code_change(0x8001787c, 0x8afd09d4, "show_crosshair"); // lbz r23, 0x9d4(r29)
add_code_change(0x80017880, 0x53173672, "show_crosshair"); // rlwimi r23, r24, 6, 25, 25 (00000001)
add_code_change(0x80017884, 0x9afd09d4, "show_crosshair"); // stb r23, 0x9d4(r29)
add_code_change(0x80017888, 0x4e800020, "show_crosshair"); // blr
add_strafe_code_mp1_102(Region::PAL);
} else {}
}
void FpsControls::init_mod_mp1_gc_r1() {
//add_code_change(0x8000f6b8, 0x48000048);
add_code_change(0x800ea1d8, 0x38810044); // output cannon bob only for viewbob
add_code_change(0x8000e5b4, 0x60000000);
add_code_change(0x8001489c, 0x4e800020);
add_code_change(0x8000e7b8, 0x60000000);
add_code_change(0x8000f88c, 0x48000244);
// When attached to a grapple point and spinning around it
// the player's yaw is adjusted, this ensures only position is updated
// Grapple point yaw fix
add_code_change(0x8017a208, 0x7fa3eb78);
add_code_change(0x8017a20c, 0x3881006c);
add_code_change(0x8017a210, 0x4bed8cf9);
// Show crosshair but don't consider pressing R button
add_code_change(0x80016f60, 0x3b000001, "show_crosshair");
add_code_change(0x80016f64, 0x8afd09c4, "show_crosshair");
add_code_change(0x80016f68, 0x53173672, "show_crosshair");
add_code_change(0x80016f6c, 0x9afd09c4, "show_crosshair");
add_code_change(0x80016f70, 0x4e800020, "show_crosshair");
add_strafe_code_mp1_100(Game::PRIME_1_GCN_R1);
}
void FpsControls::init_mod_mp1_gc_r2() {
//add_code_change(0x8000f8f8, 0x48000048);
add_code_change(0x800ea6e0, 0x38810044); // output cannon bob only for viewbob
add_code_change(0x8000e7f4, 0x60000000);
add_code_change(0x80014aec, 0x4e800020);
add_code_change(0x8000e9f8, 0x60000000);
add_code_change(0x8000facc, 0x4800022c);
// Grapple point yaw fix
add_code_change(0x8017a970, 0x7fa3eb78);
add_code_change(0x8017a974, 0x3881006c);
add_code_change(0x8017a978, 0x4bed885d);
// Show crosshair but don't consider pressing R button
add_code_change(0x800171c4, 0x3b000001, "show_crosshair"); // li r24, 1
add_code_change(0x800171c8, 0x8afd09d4, "show_crosshair"); // lbz r23, 0x9d4(r29)
add_code_change(0x800171cc, 0x53173672, "show_crosshair"); // rlwimi r23, r24, 6, 25, 25 (00000001)
add_code_change(0x800171d0, 0x9afd09d4, "show_crosshair"); // stb r23, 0x9d4(r29)
add_code_change(0x800171d4, 0x4e800020, "show_crosshair"); // blr
add_strafe_code_mp1_102(Region::NTSC_U);
}
void FpsControls::init_mod_mp2(Region region) {
prime::GetVariableManager()->register_variable("new_beam");
prime::GetVariableManager()->register_variable("beamchange_flag");
if (region == Region::NTSC_U) {
add_code_change(0x8008ccc8, 0xc0430184);
add_code_change(0x8008cd1c, 0x60000000);
add_code_change(0x80147f70, 0x60000000);
add_code_change(0x80147f98, 0x60000000);
add_code_change(0x80135b20, 0x60000000);
add_code_change(0x8008bb48, 0x60000000);
add_code_change(0x8008bb18, 0x60000000);
add_code_change(0x803054a0, 0xd23f009c);
add_code_change(0x80169dbc, 0x60000000);
add_code_change(0x80143d00, 0x48000050);
add_code_change(0x8006fde0, 0x60000000, "beam_menu");
add_code_change(0x8006fdc4, 0x60000000, "visor_menu");
add_beam_change_code_mp2(0x8018cc88);
// Steps over bounds checking on the reticle
add_code_change(0x80018528, 0x48000144);
} else if (region == Region::PAL) {
add_code_change(0x8008e30c, 0xc0430184);
add_code_change(0x8008e360, 0x60000000);
add_code_change(0x801496e4, 0x60000000);
add_code_change(0x8014970c, 0x60000000);
add_code_change(0x80137240, 0x60000000);
add_code_change(0x8008d18c, 0x60000000);
add_code_change(0x8008d15c, 0x60000000);
add_code_change(0x80307d2c, 0xd23f009c);
add_code_change(0x8016b534, 0x60000000);
add_code_change(0x80145474, 0x48000050);
add_code_change(0x80071358, 0x60000000, "beam_menu");
add_code_change(0x8007133c, 0x60000000, "visor_menu");
add_beam_change_code_mp2(0x8018e41c);
// Steps over bounds checking on the reticle
add_code_change(0x80018528, 0x48000144);
} else if (region == Region::NTSC_J) {
add_code_change(0x8008c944, 0xc0430184);
add_code_change(0x8008c998, 0x60000000);
add_code_change(0x80147578, 0x60000000);
add_code_change(0x801475a0, 0x60000000);
add_code_change(0x8013511c, 0x60000000);
add_code_change(0x8008b7c4, 0x60000000);
add_code_change(0x8008b794, 0x60000000);
add_code_change(0x80303ec8, 0xd23f009c);
add_code_change(0x80169388, 0x60000000);
add_code_change(0x8014331c, 0x48000050);
add_code_change(0x8006fbb0, 0x60000000, "beam_menu");
add_code_change(0x8006fb94, 0x60000000, "visor_menu");
add_beam_change_code_mp2(0x8018c0d4);
} else {}
has_beams = true;
}
void FpsControls::init_mod_mp2_gc(Region region) {
if (region == Region::NTSC_U) {
//add_code_change(0x801b00b4, 0x48000050);
add_code_change(0x800bcd44, 0x38810044); // output cannon bob only for viewbob
add_code_change(0x801aef58, 0x60000000);
add_code_change(0x800129c8, 0x4e800020);
add_code_change(0x801af160, 0x60000000);
add_code_change(0x801b0248, 0x48000078);
add_code_change(0x801af450, 0x48000a34);
// Enable strafing with left/right on L-Stick
add_code_change(0x8018846c, 0xc022a5b0);
add_code_change(0x80188104, 0x4800000c);
// Grapple point yaw fix
add_code_change(0x8011d9c4, 0x389d0054);
add_code_change(0x8011d9c8, 0x4bf2d1fd);
add_code_change(0x80015ed8, 0x3aa00001, "show_crosshair"); // li r21, 1
add_code_change(0x80015edc, 0x8add1268, "show_crosshair"); // lbz r22, 0x1268(r29)
add_code_change(0x80015ee0, 0x52b63672, "show_crosshair"); // rlwimi r22, r21, 6, 25, 25 (00000001)
add_code_change(0x80015ee4, 0x9add1268, "show_crosshair"); // stb r22, 0x1268(r29)
add_code_change(0x80015ee8, 0x4e800020, "show_crosshair"); // blr
add_code_change(0x800614f8, 0xc022d3e8);
const int null_players_vmc_idx = PowerPC::RegisterVmcall(null_players_on_destruct_mp2_gc);
u32 null_players_vmc = gen_vmcall(null_players_vmc_idx, 0);
add_code_change(0x80042994, null_players_vmc);
} else if (region == Region::NTSC_J) {
// TODO: Enable arm cannon bobbing for JP
add_code_change(0x801b1e6c, 0x48000050);
add_code_change(0x801b0d10, 0x60000000);
add_code_change(0x80013414, 0x4e800020);
add_code_change(0x801b0f18, 0x60000000);
add_code_change(0x801b2000, 0x48000078);
add_code_change(0x801b1208, 0x48000a34);
// Enable strafing with left/right on L-Stick
add_code_change(0x80189f70, 0xc022a5c8);
add_code_change(0x80189c08, 0x4800000c);
// Grapple point yaw fix
add_code_change(0x8011e918, 0x389d0054);
add_code_change(0x8011e91c, 0x4bf2cd91);
add_code_change(0x8001695c, 0x3aa00001, "show_crosshair"); // li r21, 1
add_code_change(0x80016960, 0x8add1268, "show_crosshair"); // lbz r22, 0x1268(r29)
add_code_change(0x80016964, 0x52b63672, "show_crosshair"); // rlwimi r22, r21, 6, 25, 25 (00000001)
add_code_change(0x80016968, 0x9add1268, "show_crosshair"); // stb r22, 0x1268(r29)
add_code_change(0x8001696c, 0x4e800020, "show_crosshair"); // blr
add_code_change(0x80061fc0, 0xc022d400);
} else if (region == Region::PAL) {
//add_code_change(0x801b03c0, 0x48000050);
add_code_change(0x800bcdd0, 0x38810044); // output cannon bob only for viewbob
add_code_change(0x801af264, 0x60000000);
add_code_change(0x80012a2c, 0x4e800020);
add_code_change(0x801af46c, 0x60000000);
add_code_change(0x801b0554, 0x48000078);
add_code_change(0x801af75c, 0x48000a34);
// Enable strafing with left/right on L-Stick
add_code_change(0x80188754, 0xc022a5a0);
add_code_change(0x801883ec, 0x4800000c);
// Grapple point yaw fix
add_code_change(0x8011dbf8, 0x389d0054);
add_code_change(0x8011dbfc, 0x4bf2d145); // bl 8004ad40
add_code_change(0x80015f74, 0x3aa00001, "show_crosshair"); // li r21, 1
add_code_change(0x80015f78, 0x8add1268, "show_crosshair"); // lbz r22, 0x1268(r29)
add_code_change(0x80015f7c, 0x52b63672, "show_crosshair"); // rlwimi r22, r21, 6, 25, 25 (00000001)
add_code_change(0x80015f80, 0x9add1268, "show_crosshair"); // stb r22, 0x1268(r29)
add_code_change(0x80015f84, 0x4e800020, "show_crosshair"); // blr
add_code_change(0x800615f8, 0xc022d3c0); // not c022d3d8 ???
const int null_players_vmc_idx = PowerPC::RegisterVmcall(null_players_on_destruct_mp2_gc);
u32 null_players_vmc = gen_vmcall(null_players_vmc_idx, 0);
add_code_change(0x80042b04, null_players_vmc);
} else {}
}
void wiimote_shake_override(u32) {
GPR(26) = CheckJump() ? 1 : 0;
}
void FpsControls::init_mod_mp3(Region region) {
prime::GetVariableManager()->register_variable("grapple_lasso_state");
prime::GetVariableManager()->register_variable("grapple_hand_x");
prime::GetVariableManager()->register_variable("grapple_hand_y");
prime::GetVariableManager()->register_variable("trigger_grapple");
prime::GetVariableManager()->register_variable("new_beam");
prime::GetVariableManager()->register_variable("beamchange_flag");
if (region == Region::NTSC_U) {
add_code_change(0x80080ac0, 0xec010072);
add_code_change(0x8014e094, 0x60000000);
add_code_change(0x8014e06c, 0x60000000);
add_code_change(0x80134328, 0x60000000);
add_code_change(0x80133970, 0x60000000);
add_code_change(0x8000ab58, 0x4bffad29);
add_code_change(0x80080d44, 0x60000000);
add_code_change(0x8007fdc8, 0x480000e4);
add_code_change(0x8017f88c, 0x60000000);
// Grapple Lasso
add_grapple_lasso_code_mp3(0x800DDE64, 0x80170CF0, 0x80171AD8);
add_control_state_hook_mp3(0x80005880, Region::NTSC_U);
add_grapple_slide_code_mp3(0x8017f2a0);
// Steps over bounds checking on the reticle
add_code_change(0x80016f48, 0x48000120);
} else if (region == Region::PAL) {
add_code_change(0x80080ab8, 0xec010072);
add_code_change(0x8014d9e0, 0x60000000);
add_code_change(0x8014d9b8, 0x60000000);
add_code_change(0x80133c74, 0x60000000);
add_code_change(0x801332bc, 0x60000000);
add_code_change(0x8000ab58, 0x4bffad29);
add_code_change(0x80080d44, 0x60000000);
add_code_change(0x8007fdc8, 0x480000e4);
add_code_change(0x8017f1d8, 0x60000000);
// Grapple Lasso
add_grapple_lasso_code_mp3(0x800DDE44, 0x8017063C, 0x80171424);
add_control_state_hook_mp3(0x80005880, Region::PAL);
add_grapple_slide_code_mp3(0x8017ebec);
// Steps over bounds checking on the reticle
add_code_change(0x80016f48, 0x48000120);
} else {}
// Same for both.
add_code_change(0x800614d0, 0x60000000, "visor_menu");
const int wiimote_shake_override_idx = PowerPC::RegisterVmcall(wiimote_shake_override);
add_code_change(0x800a8fc0, gen_vmcall(wiimote_shake_override_idx, 0));
has_beams = false;
}
void FpsControls::init_mod_mp3_standalone(Region region) {
prime::GetVariableManager()->register_variable("grapple_lasso_state");
prime::GetVariableManager()->register_variable("grapple_hand_x");
prime::GetVariableManager()->register_variable("grapple_hand_y");
prime::GetVariableManager()->register_variable("trigger_grapple");
prime::GetVariableManager()->register_variable("new_beam");
prime::GetVariableManager()->register_variable("beamchange_flag");
if (region == Region::NTSC_U) {
add_code_change(0x80080be8, 0xec010072);
add_code_change(0x801521f0, 0x60000000);
add_code_change(0x801521c8, 0x60000000);
add_code_change(0x80139108, 0x60000000);
add_code_change(0x80138750, 0x60000000);
add_code_change(0x8000ae44, 0x4bffaa3d);
add_code_change(0x80080e6c, 0x60000000);
add_code_change(0x8007fef0, 0x480000e4);
add_code_change(0x80183288, 0x60000000);
add_code_change(0x800617c8, 0x60000000, "visor_menu");
// Grapple Lasso
add_grapple_lasso_code_mp3(0x800DF790, 0x80174D70, 0x80175B54);
add_control_state_hook_mp3(0x80005880, Region::NTSC_U);
add_grapple_slide_code_mp3(0x80182c9c);
// Steps over bounds checking on the reticle
add_code_change(0x80017290, 0x48000120);
const int wiimote_shake_override_idx = PowerPC::RegisterVmcall(wiimote_shake_override);
add_code_change(0x800a8f40, gen_vmcall(wiimote_shake_override_idx, 0));
} else if (region == Region::NTSC_J) {
add_code_change(0x80081018, 0xec010072);
add_code_change(0x80153ed4, 0x60000000);
add_code_change(0x80153eac, 0x60000000);
add_code_change(0x8013a054, 0x60000000);
add_code_change(0x8013969c, 0x60000000);
add_code_change(0x8000ae44, 0x4bffaa3d);
add_code_change(0x8008129c, 0x60000000);
add_code_change(0x80080320, 0x480000e4);
add_code_change(0x80184fd4, 0x60000000);
add_code_change(0x80075f0c, 0x80061958, "visor_menu");
// Grapple Lasso
add_grapple_lasso_code_mp3(0x800E003C, 0x80176B20, 0x80177908);
add_control_state_hook_mp3(0x80005880, Region::NTSC_J);
add_grapple_slide_code_mp3(0x801849e8);
// Steps over bounds checking on the reticle
add_code_change(0x80017258, 0x48000120);
} else if (region == Region::PAL) {
add_code_change(0x80080e84, 0xec010072);
add_code_change(0x80152d50, 0x60000000);
add_code_change(0x80152d28, 0x60000000);
add_code_change(0x80139860, 0x60000000);
add_code_change(0x80138ea8, 0x60000000);
add_code_change(0x8000ae44, 0x4bffaa3d);
add_code_change(0x80081108, 0x60000000);
add_code_change(0x8008018c, 0x480000e4);
add_code_change(0x80183dc8, 0x60000000);
add_code_change(0x80061a88, 0x60000000, "visor_menu");
// Grapple Lasso
add_grapple_lasso_code_mp3(0x800DFC4C, 0x80175914, 0x801766FC);
add_control_state_hook_mp3(0x80005880, Region::PAL);
add_grapple_slide_code_mp3(0x801837dc);
// Steps over bounds checking on the reticle
add_code_change(0x80017258, 0x48000120);
} else {}
has_beams = false;
}
}