Add API function export for camera Z-targeting fixes (#503)

This commit is contained in:
danielryb 2024-11-21 04:48:22 +01:00 committed by GitHub
parent 2ed55b4ff2
commit 552a82b3b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -9,6 +9,8 @@
#include "z64shrink_window.h"
#include "z64player.h"
static bool camera_fixes = false;
static bool prev_analog_cam_active = false;
static bool can_use_analog_cam = false;
static bool analog_cam_active = false;
@ -26,6 +28,10 @@ float analog_camera_y_sensitivity = 500.0f;
static const float analog_cam_threshold = 0.1f;
RECOMP_EXPORT void recomp_set_camera_fixes(bool new_val) {
camera_fixes = new_val;
}
void update_analog_camera_params(Camera* camera) {
// recomp_printf("Camera at: %.2f %.2f %.2f\n"
// " eye: %.2f %.2f %.2f\n"
@ -48,17 +54,17 @@ void update_analog_camera_params(Camera* camera) {
void update_analog_cam(Camera* c) {
can_use_analog_cam = true;
Player* player = GET_PLAYER(c->play);
// recomp_printf(" val: %d\n", func_80123434(player));
// Check if the player just started Z targeting and reset to auto cam if so.
static bool prev_targeting_held = false;
bool targeting_held = func_80123434(player) || player->lockOnActor != NULL;
if (targeting_held && !prev_targeting_held) {
analog_cam_active = false;
}
// Enable analog cam if the right stick is held.
float input_x, input_y;
recomp_get_camera_inputs(&input_x, &input_y);
@ -66,12 +72,12 @@ void update_analog_cam(Camera* c) {
if (fabsf(input_x) >= analog_cam_threshold || fabsf(input_y) >= analog_cam_threshold) {
analog_cam_active = true;
}
if (analog_cam_skip_once) {
analog_cam_active = false;
analog_cam_skip_once = false;
}
// Record the Z targeting state.
prev_targeting_held = targeting_held;
@ -92,7 +98,7 @@ void update_analog_cam(Camera* c) {
analog_camera_pos.pitch += analog_camera_pitch_vel;
analog_camera_pos.yaw += analog_camera_yaw_vel;
if (analog_camera_pos.pitch > 0x36B0) {
analog_camera_pos.pitch = 0x36B0;
}
@ -784,7 +790,7 @@ RECOMP_PATCH s32 Camera_Normal1(Camera* camera) {
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
spB4.pitch = analog_camera_pos.pitch;
// spB4.r = analog_camera_pos.r;
@ -1017,14 +1023,14 @@ RECOMP_PATCH s32 Camera_Jump2(Camera* camera) {
camera->pitchUpdateRateInv = 100.0f;
camera->rUpdateRateInv = 100.0f;
}
spB4.pitch = CLAMP_MAX(spB4.pitch, DEG_TO_BINANG(60.43f));
spB4.pitch = CLAMP_MIN(spB4.pitch, -DEG_TO_BINANG(60.43f));
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
spB4.pitch = analog_camera_pos.pitch;
// spB4.r = analog_camera_pos.r;
@ -1064,6 +1070,10 @@ RECOMP_PATCH s32 Camera_Jump2(Camera* camera) {
*/
// @recomp Patched for analog cam.
RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
// @recomp
static bool prev_targeting_held = false;
bool isChargingDekuFlowerDive = !!(((Player*)camera->focalActor)->stateFlags3 & PLAYER_STATE3_100);
Vec3f* eye = &camera->eye;
Vec3f* at = &camera->at;
Vec3f* eyeNext = &camera->eyeNext;
@ -1089,6 +1099,10 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
CameraModeValue* values;
f32 yNormal;
// @recomp Read timer4 from timer2.
s16 timer4 = rwData->timer2 >> 5; // @recomp Used to check if z-target can be quit. Mirrors timer2 values during z-target in unmodified function.
rwData->timer2 &= 0x001F;
if (!RELOAD_PARAMS(camera)) {
} else {
values = sCameraSettings[camera->setting].cameraModes[camera->mode].values;
@ -1151,6 +1165,8 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
rwData->timer2 = 20;
} else {
rwData->timer2 = 6;
// @recomp Initiate timer4 for z-target.
timer4 = 6;
}
if ((camera->focalActor == &GET_PLAYER(camera->play)->actor) && (camera->mode == CAM_MODE_CHARGE)) {
@ -1187,12 +1203,55 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
rwData->unk_26 = 1;
camera->animState = 1;
sCameraInterfaceFlags = roData->interfaceFlags;
// @recomp Reset prev_targeting_held after transition.
prev_targeting_held = false;
break;
}
// @recomp Change behavior for z-target only.
if (camera_fixes) {
if ((roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) == PARALLEL1_FLAG_1) {
Player* player = GET_PLAYER(camera->play);
bool targeting_held = func_80123434(player) || player->lockOnActor != NULL;
// @recomp Fix camera rotating with player if z-target gets released too fast after transition.
if ((targeting_held) && (!prev_targeting_held)) {
// @recomp Reset timer2 to avoid immediate rotation, if player presses, releases and presses z-target in a very short time-window.
rwData->timer2 = 6;
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
}
// @recomp Maintain vanilla behavior for quitting z-target.
if ((timer4 == 0) && (!targeting_held)) {
rwData->timer2 = 0;
}
// @recomp Decrease timer4 only in cases where timer2 would be decreased.
if ((timer4 > 0)
&& (rwData->timer3 <= 0)) {
timer4--;
}
prev_targeting_held = targeting_held;
}
}
if (rwData->timer2 != 0) {
switch (roData->interfaceFlags & (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1)) {
case PARALLEL1_FLAG_1:
if (camera_fixes) {
// @recomp Fix camera rotating with player if z-target gets released too fast after transition.
if (isChargingDekuFlowerDive) {
// @recomp Fix camera wiggle during dive into deku flower.
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
}
rwData->unk_20 = roData->unk_20;
break;
}
// @recomp fallthrough
case (PARALLEL1_FLAG_3 | PARALLEL1_FLAG_2 | PARALLEL1_FLAG_1):
rwData->unk_1E = BINANG_ROT180(camera->focalActorPosRot.rot.y) + roData->unk_22;
rwData->unk_20 = roData->unk_20;
@ -1374,7 +1433,7 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
sp90.pitch = analog_camera_pos.pitch;
// sp90.r = analog_camera_pos.r;
@ -1400,10 +1459,20 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
func_800CBFA4(camera, at, eye, 3);
}
if (rwData->timer2 != 0) {
sUpdateCameraDirection = true;
if (camera_fixes) {
// @recomp Fix camera not updating input dir for the first few frames after transition.
if ((isChargingDekuFlowerDive) && (rwData->timer2 != 0)) {
// @recomp Fix camera wiggle during dive into deku flower.
sUpdateCameraDirection = true;
} else {
sUpdateCameraDirection = false;
}
} else {
sUpdateCameraDirection = false;
if (rwData->timer2 != 0) {
sUpdateCameraDirection = true;
} else {
sUpdateCameraDirection = false;
}
}
}
@ -1412,6 +1481,9 @@ RECOMP_PATCH s32 Camera_Parallel1(Camera* camera) {
camera->atLerpStepScale = Camera_ClampLerpScale(camera, sp72 ? roData->unk_1C : roData->unk_18);
rwData->unk_26 &= ~1;
// @recomp Save timer4 in unused bits of timer2.
rwData->timer2 |= timer4 << 5;
return 1;
}
@ -1580,11 +1652,11 @@ RECOMP_PATCH s32 Camera_Normal3(Camera* camera) {
Camera_UnsetStateFlag(camera, CAM_STATE_DISABLE_MODE_CHANGE);
}
}
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
sp80.pitch = analog_camera_pos.pitch;
// sp80.r = analog_camera_pos.r;
@ -1822,7 +1894,7 @@ RECOMP_PATCH s32 Camera_Jump3(Camera* camera) {
// @recomp Update the analog camera.
if (recomp_analog_cam_enabled()) {
update_analog_cam(camera);
if (analog_cam_active) {
spAC.pitch = analog_camera_pos.pitch;
// spAC.r = analog_camera_pos.r;
@ -1871,7 +1943,7 @@ void analog_cam_post_play_update(PlayState* play) {
// recomp_printf("prev_analog_cam_active: %d can_use_analog_cam: %d\n", prev_analog_cam_active, can_use_analog_cam);
// recomp_printf("setting: %d mode: %d func: %d\n", active_cam->setting, active_cam->mode, sCameraSettings[active_cam->setting].cameraModes[active_cam->mode].funcId);
// recomp_printf("active cam yaw %d\n", Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)));
// Update parameters for the analog cam if the game is unpaused.
if (play->pauseCtx.state == PAUSE_STATE_OFF && R_PAUSE_BG_PRERENDER_STATE == PAUSE_BG_PRERENDER_OFF) {
update_analog_camera_params(active_cam);
@ -2056,7 +2128,7 @@ RECOMP_PATCH void func_809EC568(Boss04* this, PlayState* play) {
if (this->unk_704 == 70) {
this->unk_2C8 = 300;
this->unk_2D0 = 0.0f;
D_809EE4D0 = 1;
this->unk_2E2 = 60;
this->unk_2E0 = 93;