diff --git a/include/config/config_camera.h b/include/config/config_camera.h index 5752ab85..a1585533 100644 --- a/include/config/config_camera.h +++ b/include/config/config_camera.h @@ -33,6 +33,10 @@ // Enables Puppy Camera 2, a rewritten camera that can be freely configured and modified. // #define PUPPYCAM +// Enables Reonucam, a custom camera that aims to be a more feature-rich "aglabcam" that also uses less buttons. +// An explanation the features can be seen here: https://www.youtube.com/watch?v=TQNkznX9Z3k (please note that the analog feature shown at the end is no longer present) +#define REONUCAM + // Note: Reonucam is available, but because we had no time to test it properly, it's included as a patch rather than being in the code by default. // Run this command to apply the patch if you want to use it: // tools/apply_patch.sh enhancements/reonucam.patch @@ -70,3 +74,22 @@ #ifndef FLYING_CAMERA_MODE #define FLYING_CAMERA_MODE CAMERA_MODE_BEHIND_MARIO #endif + +// Reonucam overrides +#ifdef REONUCAM + // Use course default mode + #ifndef USE_COURSE_DEFAULT_MODE + #define USE_COURSE_DEFAULT_MODE + #endif + + // Force camera mode to 8 Dir + #ifdef FORCED_CAMERA_MODE + #undef FORCED_CAMERA_MODE + #endif + #define FORCED_CAMERA_MODE CAMERA_MODE_8_DIRECTIONS + + // Disable vanilla cam processing + #ifdef ENABLE_VANILLA_CAM_PROCESSING + #undef ENABLE_VANILLA_CAM_PROCESSING + #endif +#endif diff --git a/include/text_strings.h.in b/include/text_strings.h.in index c5ee3fa8..22b43a2f 100644 --- a/include/text_strings.h.in +++ b/include/text_strings.h.in @@ -92,6 +92,15 @@ #define TEXT_HUD_PRESS_L _("PRESS L TO SWITCH") #endif +//Reonucam +#ifdef REONUCAM +#define TEXT_CAM_INFO_SLOWEST _("CAM SPEED: SLOWEST") +#define TEXT_CAM_INFO_SLOW _("CAM SPEED: SLOW") +#define TEXT_CAM_INFO_MEDIUM _("CAM SPEED: MEDIUM") +#define TEXT_CAM_INFO_FAST _("CAM SPEED: FAST") +#define TEXT_CAM_INFO_FASTEST _("CAM SPEED: FASTEST") +#endif + #if defined(VERSION_JP) || defined(VERSION_SH) /** diff --git a/src/game/camera.c b/src/game/camera.c index 819f235c..bdc07159 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -74,6 +74,10 @@ * */ +#ifdef REONUCAM +struct ReonucamState gReonucamState = { 2, FALSE, FALSE, FALSE, 0, 0, }; +#endif + // BSS /** * Stores Lakitu's position from the last frame, used for transitioning in next_lakitu_state() @@ -452,6 +456,25 @@ CameraTransition sModeTransitions[] = { extern u8 sDanceCutsceneIndexTable[][4]; extern u8 sZoomOutAreaMasks[]; +#ifdef REONUCAM +// Returns the camera speed based on the user's camera speed setting +f32 set_camera_speed(void) { + switch(gReonucamState.speed) { + case 0: + return 0.5f; + case 1: + return 1; + case 2: + return 1.5f; + case 3: + return 2; + case 4: + return 3.5f; + } + return 0; +} +#endif + /** * Starts a camera shake triggered by an interaction */ @@ -871,9 +894,30 @@ s32 update_8_directions_camera(struct Camera *c, Vec3f focus, Vec3f pos) { s16 pitch = look_down_slopes(camYaw); f32 posY; f32 focusY; +#ifdef REONUCAM + f32 yOff; +#else f32 yOff = 125.f; +#endif f32 baseDist = 1000.f; +#ifdef REONUCAM + if (gMarioState->action & ACT_FLAG_SWIMMING) { + yOff = -125.f; + } else { + yOff = 125.f; + } + + if ((gPlayer1Controller->buttonDown & R_TRIG) && (gPlayer1Controller->buttonDown & U_CBUTTONS)) { + gReonucamState.keepCliffCam = 1; + pitch = DEGREES(60); + } else if (((gPlayer1Controller->buttonDown & U_CBUTTONS) || (gPlayer1Controller->buttonDown & R_TRIG)) && gReonucamState.keepCliffCam) { + pitch = DEGREES(60); + } else { + gReonucamState.keepCliffCam = 0; + } +#endif + sAreaYaw = camYaw; calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f); focus_on_mario(focus, pos, posY + yOff, focusY + yOff, sLakituDist + baseDist, pitch, camYaw); @@ -1115,13 +1159,60 @@ s32 snap_to_45_degrees(s16 angle) { return angle; } +#ifdef REONUCAM +void reonucam_handler(void) { + // Get the camera speed based on the user's setting + f32 cameraSpeed = set_camera_speed(); + //45ยบ rotations + if ((gPlayer1Controller->buttonPressed & L_CBUTTONS) && !(gPlayer1Controller->buttonDown & R_TRIG)) { + s8DirModeBaseYaw -= DEGREES(45); + } else if ((gPlayer1Controller->buttonPressed & R_CBUTTONS) && !(gPlayer1Controller->buttonDown & R_TRIG)) { + s8DirModeBaseYaw += DEGREES(45); + } + //Smooth rotation + if (gPlayer1Controller->buttonDown & R_TRIG) { + if (gPlayer1Controller->buttonDown & L_CBUTTONS) { + s8DirModeBaseYaw -= DEGREES(cameraSpeed); + } else if (gPlayer1Controller->buttonDown & R_CBUTTONS) { + s8DirModeBaseYaw += DEGREES(cameraSpeed); + } + gReonucamState.rButtonCounter++; // This increses whenever R is held. + } else { + if (gReonucamState.rButtonCounter > 0 && gReonucamState.rButtonCounter <= 5 && !((gPlayer1Controller->buttonDown & L_CBUTTONS) || (gPlayer1Controller->buttonDown & R_CBUTTONS) || (gMarioState->action & ACT_FLAG_SWIMMING_OR_FLYING))) { + // This centers the camera behind mario. It triggers when you let go of R in less than 5 frames. + s8DirModeYawOffset = 0; + s8DirModeBaseYaw = gMarioState->faceAngle[1]-0x8000; + gMarioState->area->camera->yaw = s8DirModeBaseYaw; + play_sound_rbutton_changed(); + } + gReonucamState.rButtonCounter = 0; + } + if (gPlayer1Controller->buttonPressed & R_TRIG) { + if (gReonucamState.rButtonCounter2 <= 5) { + set_cam_angle(CAM_ANGLE_MARIO); // Enter mario cam if R is pressed 2 times in less than 5 frames + gReonucamState.rButtonCounter2 = 6; + } else { + gReonucamState.rButtonCounter2 = 0; + } + } else { + gReonucamState.rButtonCounter2++; + } + if (gPlayer1Controller->buttonPressed & D_JPAD) { + s8DirModeBaseYaw = snap_to_45_degrees(s8DirModeBaseYaw); // Lock the camera to the nearest 45deg axis + } +} +#endif + /** * A mode that only has 8 camera angles, 45 degrees apart */ void mode_8_directions_camera(struct Camera *c) { Vec3f pos; s16 oldAreaYaw = sAreaYaw; - +#ifdef REONUCAM + reonucam_handler(); + radial_camera_input(c); +#else radial_camera_input(c); if (gPlayer1Controller->buttonPressed & R_CBUTTONS) { @@ -1148,13 +1239,18 @@ void mode_8_directions_camera(struct Camera *c) { s8DirModeYawOffset = snap_to_45_degrees(s8DirModeYawOffset); } #endif - +#endif lakitu_zoom(400.f, 0x900); c->nextYaw = update_8_directions_camera(c, c->focus, pos); +#ifdef REONUCAM + set_camera_height(c, pos[1]); +#endif c->pos[0] = pos[0]; c->pos[2] = pos[2]; sAreaYawChange = sAreaYaw - oldAreaYaw; +#ifndef REONUCAM set_camera_height(c, pos[1]); +#endif } /** @@ -2750,6 +2846,9 @@ void set_camera_mode(struct Camera *c, s16 mode, s16 frames) { #ifndef ENABLE_VANILLA_CAM_PROCESSING if (mode == CAMERA_MODE_8_DIRECTIONS) { // Helps transition from any camera mode to 8dir +#ifdef REONUCAM + s8DirModeBaseYaw = 0; +#endif s8DirModeYawOffset = snap_to_45_degrees(c->yaw); } #endif @@ -2875,6 +2974,7 @@ void update_camera(struct Camera *c) { OSTime first = osGetTime(); OSTime colTime = collisionTime[perfIteration]; #endif + gCamera = c; update_camera_hud_status(c); if (c->cutscene == CUTSCENE_NONE @@ -2885,14 +2985,23 @@ void update_camera(struct Camera *c) { // Only process R_TRIG if 'fixed' is not selected in the menu if (cam_select_alt_mode(CAM_SELECTION_NONE) == CAM_SELECTION_MARIO) { if (gPlayer1Controller->buttonPressed & R_TRIG) { +#ifdef REONUCAM + if (set_cam_angle(0) == CAM_ANGLE_MARIO) { + s8DirModeBaseYaw = snap_to_45_degrees(s8DirModeBaseYaw); + set_cam_angle(CAM_ANGLE_LAKITU); + } +#else if (set_cam_angle(0) == CAM_ANGLE_LAKITU) { set_cam_angle(CAM_ANGLE_MARIO); } else { set_cam_angle(CAM_ANGLE_LAKITU); } +#endif } } +#ifndef REONUCAM play_sound_if_cam_switched_to_lakitu_or_mario(); +#endif } // Initialize the camera @@ -4551,15 +4660,21 @@ void play_camera_buzz_if_c_sideways(void) { } void play_sound_cbutton_up(void) { +#ifndef REONUCAM play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gGlobalSoundSource); +#endif } void play_sound_cbutton_down(void) { +#ifndef REONUCAM play_sound(SOUND_MENU_CAMERA_ZOOM_OUT, gGlobalSoundSource); +#endif } void play_sound_cbutton_side(void) { +#ifndef REONUCAM play_sound(SOUND_MENU_CAMERA_TURN, gGlobalSoundSource); +#endif } void play_sound_button_change_blocked(void) { @@ -4658,7 +4773,11 @@ void radial_camera_input(struct Camera *c) { } // Zoom in / enter C-Up +#ifdef REONUCAM + if ((gPlayer1Controller->buttonPressed & U_CBUTTONS) && !(gPlayer1Controller->buttonDown & R_TRIG)) { +#else if (gPlayer1Controller->buttonPressed & U_CBUTTONS) { +#endif if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) { gCameraMovementFlags &= ~CAM_MOVE_ZOOMED_OUT; play_sound_cbutton_up(); @@ -4747,6 +4866,7 @@ void handle_c_button_movement(struct Camera *c) { } } + /** * Zero the 10 cvars. */ @@ -5212,7 +5332,11 @@ void set_camera_mode_8_directions(struct Camera *c) { if (c->mode != CAMERA_MODE_8_DIRECTIONS) { c->mode = CAMERA_MODE_8_DIRECTIONS; sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT; +#ifdef REONUCAM + s8DirModeBaseYaw = snap_to_45_degrees(s8DirModeBaseYaw); +#else s8DirModeBaseYaw = 0; +#endif s8DirModeYawOffset = 0; } } diff --git a/src/game/camera.h b/src/game/camera.h index 23872607..4ea5cef7 100644 --- a/src/game/camera.h +++ b/src/game/camera.h @@ -90,6 +90,18 @@ #define CAM_MODE_LAKITU_WAS_ZOOMED_OUT 0x02 #define CAM_MODE_MARIO_SELECTED 0x04 +#ifdef REONUCAM +struct ReonucamState { + u8 speed; + u8 waterCamOverride; + u8 flyingCamOverride; + u8 keepCliffCam; + u16 rButtonCounter; + u16 rButtonCounter2; +}; +extern struct ReonucamState gReonucamState; +#endif + enum CameraSelection { CAM_SELECTION_NONE, CAM_SELECTION_MARIO, diff --git a/src/game/game_init.c b/src/game/game_init.c index ad9fdb7d..26e1f000 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -29,6 +29,9 @@ #include "puppycam2.h" #include "debug_box.h" #include "vc_check.h" +#ifdef REONUCAM +#include "camera.h" +#endif // First 3 controller slots struct Controller gControllers[3]; @@ -751,6 +754,9 @@ void thread5_game_loop(UNUSED void *arg) { play_music(SEQ_PLAYER_SFX, SEQUENCE_ARGS(0, SEQ_SOUND_PLAYER), 0); set_sound_mode(save_file_get_sound_mode()); +#ifdef REONUCAM + gReonucamState.speed = save_file_get_camera_speed(); +#endif #ifdef WIDE gConfig.widescreen = save_file_get_widescreen_mode(); #endif diff --git a/src/game/ingame_menu.c b/src/game/ingame_menu.c index 28c80f34..37dc71c1 100644 --- a/src/game/ingame_menu.c +++ b/src/game/ingame_menu.c @@ -63,6 +63,14 @@ void *languageTable[][3] = { #endif }; +#ifdef REONUCAM +u8 textCamInfoSlowest[] = { TEXT_CAM_INFO_SLOWEST }; +u8 textCamInfoSlow[] = { TEXT_CAM_INFO_SLOW }; +u8 textCamInfoMedium[] = { TEXT_CAM_INFO_MEDIUM }; +u8 textCamInfoFast[] = { TEXT_CAM_INFO_FAST}; +u8 textCamInfoFastest[] = { TEXT_CAM_INFO_FASTEST }; +#endif + extern u8 gLastCompletedCourseNum; extern u8 gLastCompletedStarNum; @@ -1455,6 +1463,48 @@ void reset_red_coins_collected(void) { gRedCoinsCollected = 0; } +#ifdef REONUCAM +void render_reonucam_speed_setting(void) { + gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); + gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gDialogTextAlpha); + switch (gReonucamState.speed) { + case 0: + print_generic_string(190, 20, textCamInfoSlowest); + break; + case 1: + print_generic_string(190, 20, textCamInfoSlow); + break; + case 2: + print_generic_string(190, 20, textCamInfoMedium); + break; + case 3: + print_generic_string(190, 20, textCamInfoFast); + break; + case 4: + print_generic_string(190, 20, textCamInfoFastest); + break; + } + gSPDisplayList(gDisplayListHead++, dl_ia_text_end); + + if (gPlayer1Controller->buttonPressed & R_JPAD) { + if (gReonucamState.speed < 4) { + gReonucamState.speed += 1; + } else { + gReonucamState.speed = 0; + } + save_file_set_camera_speed(gReonucamState.speed); + } else if (gPlayer1Controller->buttonPressed & L_JPAD) { + if (gReonucamState.speed > 0) { + gReonucamState.speed -= 1; + } else { + gReonucamState.speed = 4; + } + save_file_set_camera_speed(gReonucamState.speed); + } +} +#endif + + void change_dialog_camera_angle(void) { if (cam_select_alt_mode(0) == CAM_SELECTION_MARIO) { gDialogCameraAngleIndex = CAM_SELECTION_MARIO; @@ -1917,6 +1967,9 @@ s32 render_pause_courses_and_castle(void) { } #if defined(WIDE) && !defined(PUPPYCAM) render_widescreen_setting(); +#endif +#ifdef REONUCAM + render_reonucam_speed_setting(); #endif if (gDialogTextAlpha < 250) { gDialogTextAlpha += 25; diff --git a/src/game/mario.c b/src/game/mario.c index 757c7f0b..a998bac5 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1389,8 +1389,22 @@ void update_mario_inputs(struct MarioState *m) { void set_submerged_cam_preset_and_spawn_bubbles(struct MarioState *m) { f32 heightBelowWater; s16 camPreset; +#ifdef REONUCAM + // skip if not submerged + if ((m->action & ACT_GROUP_MASK) != ACT_GROUP_SUBMERGED) return; + // R Trigger toggles camera mode override + if ((gPlayer1Controller->buttonPressed & R_TRIG) && (m->action & ACT_FLAG_SWIMMING)) { + gReonucamState.waterCamOverride ^= 1; + } + + // If override, set mode to 8 dir. Otherwise, use normal water processing + if (gReonucamState.waterCamOverride) { + if (m->area->camera->mode != CAMERA_MODE_8_DIRECTIONS) set_camera_mode(m->area->camera, CAMERA_MODE_8_DIRECTIONS, 1); + } else { +#else if ((m->action & ACT_GROUP_MASK) == ACT_GROUP_SUBMERGED) { +#endif heightBelowWater = (f32)(m->waterLevel - 80) - m->pos[1]; camPreset = m->area->camera->mode; diff --git a/src/game/mario_actions_airborne.c b/src/game/mario_actions_airborne.c index 13c2b626..c4b4f664 100644 --- a/src/game/mario_actions_airborne.c +++ b/src/game/mario_actions_airborne.c @@ -1696,6 +1696,12 @@ s32 act_shot_from_cannon(struct MarioState *m) { s32 act_flying(struct MarioState *m) { s16 startPitch = m->faceAngle[0]; +#ifdef REONUCAM + if (gPlayer1Controller->buttonPressed & R_TRIG) { + gReonucamState.flyingCamOverride ^= 1; + } +#endif + if (m->input & INPUT_Z_PRESSED) { if (m->area->camera->mode == FLYING_CAMERA_MODE) { set_camera_mode(m->area->camera, m->area->camera->defMode, 1); @@ -1710,9 +1716,17 @@ s32 act_flying(struct MarioState *m) { return set_mario_action(m, ACT_FREEFALL, 0); } +#ifdef REONUCAM + if (!gReonucamState.flyingCamOverride && m->area->camera->mode != FLYING_CAMERA_MODE) { + set_camera_mode(m->area->camera, FLYING_CAMERA_MODE, 1); + } else if (gReonucamState.flyingCamOverride && m->area->camera->mode != CAMERA_MODE_8_DIRECTIONS) { + set_camera_mode(m->area->camera, CAMERA_MODE_8_DIRECTIONS, 1); + } +#else if (m->area->camera->mode != FLYING_CAMERA_MODE) { set_camera_mode(m->area->camera, FLYING_CAMERA_MODE, 1); } +#endif if (m->actionState == ACT_STATE_FLYING_SPIN) { if (m->actionArg == ACT_ARG_FLYING_FROM_CANNON) { diff --git a/src/game/save_file.c b/src/game/save_file.c index 07f0faad..db76574b 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -715,6 +715,19 @@ u32 save_file_get_sound_mode(void) { return gSaveBuffer.menuData.soundMode; } +#ifdef REONUCAM +void save_file_set_camera_speed(u8 speed) { + gSaveBuffer.menuData.cameraSpeedSetting = speed; + gMainMenuDataModified = TRUE; + save_main_menu_data(); +} + +u8 save_file_get_camera_speed(void) { + return gSaveBuffer.menuData.cameraSpeedSetting; +} + +#endif + void save_file_move_cap_to_default_location(void) { if (save_file_get_flags() & SAVE_FLAG_CAP_ON_GROUND) { switch (gSaveBuffer.files[gCurrSaveFileNum - 1][0].capLevel) { diff --git a/src/game/save_file.h b/src/game/save_file.h index a03dbafb..8ba8725f 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -67,6 +67,9 @@ struct MainMenuSaveData { #ifdef WIDE u8 wideMode: 1; #endif +#ifdef REONUCAM + u8 cameraSpeedSetting: 3; +#endif #if MULTILANG u8 language: 2; @@ -200,6 +203,10 @@ u32 save_file_get_sound_mode(void); u32 save_file_get_widescreen_mode(void); void save_file_set_widescreen_mode(u8 mode); #endif +#ifdef REONUCAM +u8 save_file_get_camera_speed(void); +void save_file_set_camera_speed(u8 speed); +#endif void save_file_move_cap_to_default_location(void); void disable_warp_checkpoint(void);