sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

camera.c (403427B)


      1 #include <ultra64.h>
      2 
      3 #define INCLUDED_FROM_CAMERA_C
      4 
      5 #include "prevent_bss_reordering.h"
      6 #include "sm64.h"
      7 #include "camera.h"
      8 #include "seq_ids.h"
      9 #include "dialog_ids.h"
     10 #include "audio/external.h"
     11 #include "mario_misc.h"
     12 #include "game_init.h"
     13 #include "hud.h"
     14 #include "engine/math_util.h"
     15 #include "area.h"
     16 #include "engine/surface_collision.h"
     17 #include "engine/behavior_script.h"
     18 #include "level_update.h"
     19 #include "ingame_menu.h"
     20 #include "mario_actions_cutscene.h"
     21 #include "save_file.h"
     22 #include "object_helpers.h"
     23 #include "print.h"
     24 #include "spawn_sound.h"
     25 #include "behavior_actions.h"
     26 #include "behavior_data.h"
     27 #include "object_list_processor.h"
     28 #include "paintings.h"
     29 #include "engine/graph_node.h"
     30 #include "level_table.h"
     31 
     32 #define CBUTTON_MASK (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS)
     33 
     34 /**
     35  * @file camera.c
     36  * Implements the camera system, including C-button input, camera modes, camera triggers, and cutscenes.
     37  *
     38  * When working with the camera, you should be familiar with sm64's coordinate system.
     39  * Relative to the camera, the coordinate system follows the right hand rule:
     40  *          +X points right.
     41  *          +Y points up.
     42  *          +Z points out of the screen.
     43  *
     44  * You should also be familiar with Euler angles: 'pitch', 'yaw', and 'roll'.
     45  *      pitch: rotation about the X-axis, measured from +Y.
     46  *          Unlike yaw and roll, pitch is bounded in +-0x4000 (90 degrees).
     47  *          Pitch is 0 when the camera points parallel to the xz-plane (+Y points straight up).
     48  *
     49  *      yaw: rotation about the Y-axis, measured from (absolute) +Z.
     50  *          Positive yaw rotates clockwise, towards +X.
     51  *
     52  *      roll: rotation about the Z-axis, measured from the camera's right direction.
     53  *          Unfortunately, it's weird: For some reason, roll is flipped. Positive roll makes the camera
     54  *          rotate counterclockwise, which means the WORLD rotates clockwise. Luckily roll is rarely
     55  *          used.
     56  *
     57  *      Remember the right hand rule: make a thumbs-up with your right hand, stick your thumb in the
     58  *      +direction (except for roll), and the angle follows the rotation of your curled fingers.
     59  *
     60  * Illustrations:
     61  * Following the right hand rule, each hidden axis's positive direction points out of the screen.
     62  *
     63  *       YZ-Plane (pitch)        XZ-Plane (yaw)          XY-Plane (roll -- Note flipped)
     64  *          +Y                      -Z                      +Y
     65  *           ^                       ^ (into the             ^
     66  *         --|--                     |   screen)             |<-
     67  * +pitch /  |  \ -pitch             |                       |  \ -roll
     68  *       v   |   v                   |                       |   |
     69  * +Z <------O------> -Z   -X <------O------> +X   -X <------O------> +X
     70  *           |                   ^   |   ^                   |   |
     71  *           |                    \  |  /                    |  / +roll
     72  *           |               -yaw  --|--  +yaw               |<-
     73  *           v                       v                       v
     74  *          -Y                      +Z                      -Y
     75  *
     76  */
     77 
     78 // BSS
     79 /**
     80  * Stores Lakitu's position from the last frame, used for transitioning in next_lakitu_state()
     81  */
     82 Vec3f sOldPosition;
     83 /**
     84  * Stores Lakitu's focus from the last frame, used for transitioning in next_lakitu_state()
     85  */
     86 Vec3f sOldFocus;
     87 /**
     88  * Global array of PlayerCameraState.
     89  * L is real.
     90  */
     91 struct PlayerCameraState gPlayerCameraState[2];
     92 /**
     93  * Direction controlled by player 2, moves the focus during the credits.
     94  */
     95 Vec3f sPlayer2FocusOffset;
     96 /**
     97  * The pitch used for the credits easter egg.
     98  */
     99 s16 sCreditsPlayer2Pitch;
    100 /**
    101  * The yaw used for the credits easter egg.
    102  */
    103 s16 sCreditsPlayer2Yaw;
    104 /**
    105  * Used to decide when to zoom out in the pause menu.
    106  */
    107 u8 sFramesPaused;
    108 
    109 extern struct CameraFOVStatus sFOVState;
    110 extern struct TransitionInfo sModeTransition;
    111 extern struct PlayerGeometry sMarioGeometry;
    112 extern s16 unusedFreeRoamWallYaw;
    113 extern s16 sAvoidYawVel;
    114 extern s16 sCameraYawAfterDoorCutscene;
    115 extern s16 unusedSplinePitch;
    116 extern s16 unusedSplineYaw;
    117 extern struct HandheldShakePoint sHandheldShakeSpline[4];
    118 extern s16 sHandheldShakeMag;
    119 extern f32 sHandheldShakeTimer;
    120 extern f32 sHandheldShakeInc;
    121 extern s16 sHandheldShakePitch;
    122 extern s16 sHandheldShakeYaw;
    123 extern s16 sHandheldShakeRoll;
    124 extern u32 unused8033B30C;
    125 extern u32 unused8033B310;
    126 extern s16 sSelectionFlags;
    127 extern s16 unused8033B316;
    128 extern s16 s2ndRotateFlags;
    129 extern s16 unused8033B31A;
    130 extern s16 sCameraSoundFlags;
    131 extern u16 sCButtonsPressed;
    132 extern s16 sCutsceneDialogID;
    133 extern struct LakituState gLakituState;
    134 extern s16 unused8033B3E8;
    135 extern s16 sAreaYaw;
    136 extern s16 sAreaYawChange;
    137 extern s16 sLakituDist;
    138 extern s16 sLakituPitch;
    139 extern f32 sZoomAmount;
    140 extern s16 sCSideButtonYaw;
    141 extern s16 sBehindMarioSoundTimer;
    142 extern f32 sZeroZoomDist;
    143 extern s16 sCUpCameraPitch;
    144 extern s16 sModeOffsetYaw;
    145 extern s16 sSpiralStairsYawOffset;
    146 extern s16 s8DirModeBaseYaw;
    147 extern s16 s8DirModeYawOffset;
    148 extern f32 sPanDistance;
    149 extern f32 sCannonYOffset;
    150 extern struct ModeTransitionInfo sModeInfo;
    151 extern Vec3f sCastleEntranceOffset;
    152 extern u32 sParTrackIndex;
    153 extern struct ParallelTrackingPoint *sParTrackPath;
    154 extern struct CameraStoredInfo sParTrackTransOff;
    155 extern struct CameraStoredInfo sCameraStoreCUp;
    156 extern struct CameraStoredInfo sCameraStoreCutscene;
    157 extern s16 gCameraMovementFlags;
    158 extern s16 sStatusFlags;
    159 extern struct CutsceneSplinePoint sCurCreditsSplinePos[32];
    160 extern struct CutsceneSplinePoint sCurCreditsSplineFocus[32];
    161 extern s16 sCutsceneSplineSegment;
    162 extern f32 sCutsceneSplineSegmentProgress;
    163 extern s16 unused8033B6E8;
    164 extern s16 sCutsceneShot;
    165 extern s16 gCutsceneTimer;
    166 extern struct CutsceneVariable sCutsceneVars[10];
    167 extern s32 gObjCutsceneDone;
    168 extern u32 gCutsceneObjSpawn;
    169 extern struct Camera *gCamera;
    170 
    171 /**
    172  * Lakitu's position and focus.
    173  * @see LakituState
    174  */
    175 struct LakituState gLakituState;
    176 struct CameraFOVStatus sFOVState;
    177 struct TransitionInfo sModeTransition;
    178 struct PlayerGeometry sMarioGeometry;
    179 struct Camera *gCamera;
    180 s16 unusedFreeRoamWallYaw;
    181 s16 sAvoidYawVel;
    182 s16 sCameraYawAfterDoorCutscene;
    183 /**
    184  * The current spline that controls the camera's position during the credits.
    185  */
    186 struct CutsceneSplinePoint sCurCreditsSplinePos[32];
    187 
    188 /**
    189  * The current spline that controls the camera's focus during the credits.
    190  */
    191 struct CutsceneSplinePoint sCurCreditsSplineFocus[32];
    192 
    193 s16 unusedSplinePitch;
    194 s16 unusedSplineYaw;
    195 
    196 /**
    197  * The progress (from 0 to 1) through the current spline segment.
    198  * When it becomes >= 1, 1.0 is subtracted from it and sCutsceneSplineSegment is increased.
    199  */
    200 f32 sCutsceneSplineSegmentProgress;
    201 
    202 /**
    203  * The current segment of the CutsceneSplinePoint[] being used.
    204  */
    205 s16 sCutsceneSplineSegment;
    206 s16 unused8033B6E8;
    207 
    208 // Shaky Hand-held Camera effect variables
    209 struct HandheldShakePoint sHandheldShakeSpline[4];
    210 s16 sHandheldShakeMag;
    211 f32 sHandheldShakeTimer;
    212 f32 sHandheldShakeInc;
    213 s16 sHandheldShakePitch;
    214 s16 sHandheldShakeYaw;
    215 s16 sHandheldShakeRoll;
    216 
    217 /**
    218  * Controls which object to spawn in the intro and ending cutscenes.
    219  */
    220 u32 gCutsceneObjSpawn;
    221 /**
    222  * Controls when an object-based cutscene should end. It's only used in the star spawn cutscenes, but
    223  * Yoshi also toggles this.
    224  */
    225 s32 gObjCutsceneDone;
    226 
    227 u32 unused8033B30C;
    228 u32 unused8033B310;
    229 
    230 /**
    231  * Determines which R-Trigger mode is selected in the pause menu.
    232  */
    233 s16 sSelectionFlags;
    234 
    235 /**
    236  * Flags that determine what movements the camera should start / do this frame.
    237  */
    238 s16 gCameraMovementFlags;
    239 s16 unused8033B316;
    240 
    241 /**
    242  * Flags that change how modes operate and how Lakitu moves.
    243  * The most commonly used flag is CAM_FLAG_SMOOTH_MOVEMENT, which makes Lakitu fly to the next position,
    244  * instead of warping.
    245  */
    246 s16 sStatusFlags;
    247 /**
    248  * Flags that determine whether the player has already rotated left or right. Used in radial mode to
    249  * determine whether to rotate all the way, or just to 60 degrees.
    250  */
    251 s16 s2ndRotateFlags;
    252 s16 unused8033B31A;
    253 /**
    254  * Flags that control buzzes and sounds that play, mostly for C-button input.
    255  */
    256 s16 sCameraSoundFlags;
    257 /**
    258  * Stores what C-Buttons are pressed this frame.
    259  */
    260 u16 sCButtonsPressed;
    261 /**
    262  * A copy of gDialogID, the dialog displayed during the cutscene.
    263  */
    264 s16 sCutsceneDialogID;
    265 /**
    266  * The currently playing shot in the cutscene.
    267  */
    268 s16 sCutsceneShot;
    269 /**
    270  * The current frame of the cutscene shot.
    271  */
    272 s16 gCutsceneTimer;
    273 s16 unused8033B3E8;
    274 #if defined(VERSION_EU) || defined(VERSION_SH) || defined(VERSION_CN)
    275 s16 unused8033B3E82;
    276 #endif
    277 /**
    278  * The angle of the direction vector from the area's center to Mario's position.
    279  */
    280 s16 sAreaYaw;
    281 
    282 /**
    283  * How much sAreaYaw changed when Mario moved.
    284  */
    285 s16 sAreaYawChange;
    286 
    287 /**
    288  * Lakitu's distance from Mario in C-Down mode
    289  */
    290 s16 sLakituDist;
    291 
    292 /**
    293  * How much Lakitu looks down in C-Down mode
    294  */
    295 s16 sLakituPitch;
    296 
    297 /**
    298  * The amount of distance left to zoom out
    299  */
    300 f32 sZoomAmount;
    301 
    302 s16 sCSideButtonYaw;
    303 
    304 /**
    305  * Sound timer used to space out sounds in behind Mario mode
    306  */
    307 s16 sBehindMarioSoundTimer;
    308 
    309 /**
    310  * Virtually unused aside being set to 0 and compared with gCameraZoomDist (which is never < 0)
    311  */
    312 f32 sZeroZoomDist;
    313 
    314 /**
    315  * The camera's pitch in C-Up mode. Mainly controls Mario's head rotation.
    316  */
    317 s16 sCUpCameraPitch;
    318 /**
    319  * The current mode's yaw, which gets added to the camera's yaw.
    320  */
    321 s16 sModeOffsetYaw;
    322 
    323 /**
    324  * Stores Mario's yaw around the stairs, relative to the camera's position.
    325  *
    326  * Used in update_spiral_stairs_camera()
    327  */
    328 s16 sSpiralStairsYawOffset;
    329 
    330 /**
    331  * The constant offset to 8-direction mode's yaw.
    332  */
    333 s16 s8DirModeBaseYaw;
    334 /**
    335  * Player-controlled yaw offset in 8-direction mode, a multiple of 45 degrees.
    336  */
    337 s16 s8DirModeYawOffset;
    338 
    339 /**
    340  * The distance that the camera will look ahead of Mario in the direction Mario is facing.
    341  */
    342 f32 sPanDistance;
    343 
    344 /**
    345  * When Mario gets in the cannon, it is pointing straight up and rotates down.
    346  * This is used to make the camera start up and rotate down, like the cannon.
    347  */
    348 f32 sCannonYOffset;
    349 /**
    350  * These structs are used by the cutscenes. Most of the fields are unused, and some (all?) of the used
    351  * ones have multiple uses.
    352  * Check the cutscene_start functions for documentation on the cvars used by a specific cutscene.
    353  */
    354 struct CutsceneVariable sCutsceneVars[10];
    355 struct ModeTransitionInfo sModeInfo;
    356 /**
    357  * Offset added to sFixedModeBasePosition when Mario is inside, near the castle lobby entrance
    358  */
    359 Vec3f sCastleEntranceOffset;
    360 
    361 /**
    362  * The index into the current parallel tracking path
    363  */
    364 u32 sParTrackIndex;
    365 
    366 /**
    367  * The current list of ParallelTrackingPoints used in update_parallel_tracking_camera()
    368  */
    369 struct ParallelTrackingPoint *sParTrackPath;
    370 
    371 /**
    372  * On the first frame after the camera changes to a different parallel tracking path, this stores the
    373  * displacement between the camera's calculated new position and its previous positions
    374  *
    375  * This transition offset is then used to smoothly interpolate the camera's position between the two
    376  * paths
    377  */
    378 struct CameraStoredInfo sParTrackTransOff;
    379 
    380 /**
    381  * The information stored when C-Up is active, used to update Lakitu's rotation when exiting C-Up
    382  */
    383 struct CameraStoredInfo sCameraStoreCUp;
    384 
    385 /**
    386  * The information stored during cutscenes
    387  */
    388 struct CameraStoredInfo sCameraStoreCutscene;
    389 
    390 // first iteration of data
    391 u32 unused8032CFC0 = 0;
    392 struct Object *gCutsceneFocus = NULL;
    393 
    394 u32 unused8032CFC8 = 0;
    395 u32 unused8032CFCC = 0;
    396 
    397 /**
    398  * The information of a second focus camera used by some objects
    399  */
    400 struct Object *gSecondCameraFocus = NULL;
    401 
    402 /**
    403  * How fast the camera's yaw should approach the next yaw.
    404  */
    405 s16 sYawSpeed = 0x400;
    406 s32 gCurrLevelArea = 0;
    407 u32 gPrevLevel = 0;
    408 
    409 f32 unused8032CFE0 = 1000.0f;
    410 f32 unused8032CFE4 = 800.0f;
    411 u32 unused8032CFE8 = 0;
    412 f32 gCameraZoomDist = 800.0f;
    413 
    414 /**
    415  * A cutscene that plays when the player interacts with an object
    416  */
    417 u8 sObjectCutscene = 0;
    418 
    419 /**
    420  * The ID of the cutscene that ended. It's set to 0 if no cutscene ended less than 8 frames ago.
    421  *
    422  * It is only used to prevent the same cutscene from playing twice before 8 frames have passed.
    423  */
    424 u8 gRecentCutscene = 0;
    425 
    426 /**
    427  * A timer that increments for 8 frames when a cutscene ends.
    428  * When it reaches 8, it sets gRecentCutscene to 0.
    429  */
    430 u8 sFramesSinceCutsceneEnded = 0;
    431 /**
    432  * Mario's response to a dialog.
    433  * 0 = No response yet
    434  * 1 = Yes
    435  * 2 = No
    436  * 3 = Dialog doesn't have a response
    437  */
    438 u8 sCutsceneDialogResponse = DIALOG_RESPONSE_NONE;
    439 struct PlayerCameraState *sMarioCamState = &gPlayerCameraState[0];
    440 struct PlayerCameraState *sLuigiCamState = &gPlayerCameraState[1];
    441 u32 unused8032D008 = 0;
    442 Vec3f sFixedModeBasePosition    = { 646.0f, 143.0f, -1513.0f };
    443 Vec3f sUnusedModeBasePosition_2 = { 646.0f, 143.0f, -1513.0f };
    444 Vec3f sUnusedModeBasePosition_3 = { 646.0f, 143.0f, -1513.0f };
    445 Vec3f sUnusedModeBasePosition_4 = { 646.0f, 143.0f, -1513.0f };
    446 Vec3f sUnusedModeBasePosition_5 = { 646.0f, 143.0f, -1513.0f };
    447 
    448 s32 update_radial_camera(struct Camera *c, Vec3f, Vec3f);
    449 s32 update_outward_radial_camera(struct Camera *c, Vec3f, Vec3f);
    450 s32 update_behind_mario_camera(struct Camera *c, Vec3f, Vec3f);
    451 s32 update_mario_camera(struct Camera *c, Vec3f, Vec3f);
    452 s32 unused_update_mode_5_camera(struct Camera *c, Vec3f, Vec3f);
    453 s32 update_c_up(struct Camera *c, Vec3f, Vec3f);
    454 s32 nop_update_water_camera(struct Camera *c, Vec3f, Vec3f);
    455 s32 update_slide_or_0f_camera(struct Camera *c, Vec3f, Vec3f);
    456 s32 update_in_cannon(struct Camera *c, Vec3f, Vec3f);
    457 s32 update_boss_fight_camera(struct Camera *c, Vec3f, Vec3f);
    458 s32 update_parallel_tracking_camera(struct Camera *c, Vec3f, Vec3f);
    459 s32 update_fixed_camera(struct Camera *c, Vec3f, Vec3f);
    460 s32 update_8_directions_camera(struct Camera *c, Vec3f, Vec3f);
    461 s32 update_slide_or_0f_camera(struct Camera *c, Vec3f, Vec3f);
    462 s32 update_spiral_stairs_camera(struct Camera *c, Vec3f, Vec3f);
    463 
    464 typedef s32 (*CameraTransition)(struct Camera *c, Vec3f, Vec3f);
    465 CameraTransition sModeTransitions[] = {
    466     NULL,
    467     update_radial_camera,
    468     update_outward_radial_camera,
    469     update_behind_mario_camera,
    470     update_mario_camera,
    471     unused_update_mode_5_camera,
    472     update_c_up,
    473     update_mario_camera,
    474     nop_update_water_camera,
    475     update_slide_or_0f_camera,
    476     update_in_cannon,
    477     update_boss_fight_camera,
    478     update_parallel_tracking_camera,
    479     update_fixed_camera,
    480     update_8_directions_camera,
    481     update_slide_or_0f_camera,
    482     update_mario_camera,
    483     update_spiral_stairs_camera
    484 };
    485 
    486 // Move these two tables to another include file?
    487 extern u8 sDanceCutsceneIndexTable[][4];
    488 extern u8 sZoomOutAreaMasks[];
    489 
    490 /**
    491  * Starts a camera shake triggered by an interaction
    492  */
    493 void set_camera_shake_from_hit(s16 shake) {
    494     switch (shake) {
    495         // Makes the camera stop for a bit
    496         case SHAKE_ATTACK:
    497             gLakituState.focHSpeed = 0;
    498             gLakituState.posHSpeed = 0;
    499             break;
    500 
    501         case SHAKE_FALL_DAMAGE:
    502             set_camera_pitch_shake(0x60, 0x3, 0x8000);
    503             set_camera_roll_shake(0x60, 0x3, 0x8000);
    504             break;
    505 
    506         case SHAKE_GROUND_POUND:
    507             set_camera_pitch_shake(0x60, 0xC, 0x8000);
    508             break;
    509 
    510         case SHAKE_SMALL_DAMAGE:
    511             if (sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
    512                 set_camera_yaw_shake(0x200, 0x10, 0x1000);
    513                 set_camera_roll_shake(0x400, 0x20, 0x1000);
    514                 set_fov_shake(0x100, 0x30, 0x8000);
    515             } else {
    516                 set_camera_yaw_shake(0x80, 0x8, 0x4000);
    517                 set_camera_roll_shake(0x80, 0x8, 0x4000);
    518                 set_fov_shake(0x100, 0x30, 0x8000);
    519             }
    520 
    521             gLakituState.focHSpeed = 0;
    522             gLakituState.posHSpeed = 0;
    523             break;
    524 
    525         case SHAKE_MED_DAMAGE:
    526             if (sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
    527                 set_camera_yaw_shake(0x400, 0x20, 0x1000);
    528                 set_camera_roll_shake(0x600, 0x30, 0x1000);
    529                 set_fov_shake(0x180, 0x40, 0x8000);
    530             } else {
    531                 set_camera_yaw_shake(0x100, 0x10, 0x4000);
    532                 set_camera_roll_shake(0x100, 0x10, 0x4000);
    533                 set_fov_shake(0x180, 0x40, 0x8000);
    534             }
    535 
    536             gLakituState.focHSpeed = 0;
    537             gLakituState.posHSpeed = 0;
    538             break;
    539 
    540         case SHAKE_LARGE_DAMAGE:
    541             if (sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
    542                 set_camera_yaw_shake(0x600, 0x30, 0x1000);
    543                 set_camera_roll_shake(0x800, 0x40, 0x1000);
    544                 set_fov_shake(0x200, 0x50, 0x8000);
    545             } else {
    546                 set_camera_yaw_shake(0x180, 0x20, 0x4000);
    547                 set_camera_roll_shake(0x200, 0x20, 0x4000);
    548                 set_fov_shake(0x200, 0x50, 0x8000);
    549             }
    550 
    551             gLakituState.focHSpeed = 0;
    552             gLakituState.posHSpeed = 0;
    553             break;
    554 
    555         case SHAKE_HIT_FROM_BELOW:
    556             gLakituState.focHSpeed = 0.07;
    557             gLakituState.posHSpeed = 0.07;
    558             break;
    559 
    560         case SHAKE_SHOCK:
    561             set_camera_pitch_shake(random_float() * 64.f, 0x8, 0x8000);
    562             set_camera_yaw_shake(random_float() * 64.f, 0x8, 0x8000);
    563             break;
    564     }
    565 }
    566 
    567 /**
    568  * Start a shake from the environment
    569  */
    570 void set_environmental_camera_shake(s16 shake) {
    571     switch (shake) {
    572         case SHAKE_ENV_EXPLOSION:
    573             set_camera_pitch_shake(0x60, 0x8, 0x4000);
    574             break;
    575 
    576         case SHAKE_ENV_BOWSER_THROW_BOUNCE:
    577             set_camera_pitch_shake(0xC0, 0x8, 0x4000);
    578             break;
    579 
    580         case SHAKE_ENV_BOWSER_JUMP:
    581             set_camera_pitch_shake(0x100, 0x8, 0x3000);
    582             break;
    583 
    584         case SHAKE_ENV_UNUSED_6:
    585             set_camera_roll_shake(0x80, 0x10, 0x3000);
    586             break;
    587 
    588         case SHAKE_ENV_UNUSED_7:
    589             set_camera_pitch_shake(0x20, 0x8, 0x8000);
    590             break;
    591 
    592         case SHAKE_ENV_PYRAMID_EXPLODE:
    593             set_camera_pitch_shake(0x40, 0x8, 0x8000);
    594             break;
    595 
    596         case SHAKE_ENV_JRB_SHIP_DRAIN:
    597             set_camera_pitch_shake(0x20, 0x8, 0x8000);
    598             set_camera_roll_shake(0x400, 0x10, 0x100);
    599             break;
    600 
    601         case SHAKE_ENV_FALLING_BITS_PLAT:
    602             set_camera_pitch_shake(0x40, 0x2, 0x8000);
    603             break;
    604 
    605         case SHAKE_ENV_UNUSED_5:
    606             set_camera_yaw_shake(-0x200, 0x80, 0x200);
    607             break;
    608     }
    609 }
    610 
    611 /**
    612  * Starts a camera shake, but scales the amplitude by the point's distance from the camera
    613  */
    614 void set_camera_shake_from_point(s16 shake, f32 posX, f32 posY, f32 posZ) {
    615     switch (shake) {
    616         case SHAKE_POS_BOWLING_BALL:
    617             set_pitch_shake_from_point(0x28, 0x8, 0x4000, 2000.f, posX, posY, posZ);
    618             break;
    619 
    620         case SHAKE_POS_SMALL:
    621             set_pitch_shake_from_point(0x80, 0x8, 0x4000, 4000.f, posX, posY, posZ);
    622             set_fov_shake_from_point_preset(SHAKE_FOV_SMALL, posX, posY, posZ);
    623             break;
    624 
    625         case SHAKE_POS_MEDIUM:
    626             set_pitch_shake_from_point(0xC0, 0x8, 0x4000, 6000.f, posX, posY, posZ);
    627             set_fov_shake_from_point_preset(SHAKE_FOV_MEDIUM, posX, posY, posZ);
    628             break;
    629 
    630         case SHAKE_POS_LARGE:
    631             set_pitch_shake_from_point(0x100, 0x8, 0x3000, 8000.f, posX, posY, posZ);
    632             set_fov_shake_from_point_preset(SHAKE_FOV_LARGE, posX, posY, posZ);
    633             break;
    634     }
    635 }
    636 
    637 /**
    638  * Start a camera shake from an environmental source, but only shake the camera's pitch.
    639  */
    640 void unused_set_camera_pitch_shake_env(s16 shake) {
    641     switch (shake) {
    642         case SHAKE_ENV_EXPLOSION:
    643             set_camera_pitch_shake(0x60, 0x8, 0x4000);
    644             break;
    645 
    646         case SHAKE_ENV_BOWSER_THROW_BOUNCE:
    647             set_camera_pitch_shake(0xC0, 0x8, 0x4000);
    648             break;
    649 
    650         case SHAKE_ENV_BOWSER_JUMP:
    651             set_camera_pitch_shake(0x100, 0x8, 0x3000);
    652             break;
    653     }
    654 }
    655 
    656 /**
    657  * Calculates Mario's distance to the floor, or the water level if it is above the floor. Then:
    658  * `posOff` is set to the distance multiplied by posMul and bounded to [-posBound, posBound]
    659  * `focOff` is set to the distance multiplied by focMul and bounded to [-focBound, focBound]
    660  *
    661  * Notes:
    662  *      posMul is always 1.0f, focMul is always 0.9f
    663  *      both ranges are always 200.f
    664  *          Since focMul is 0.9, `focOff` is closer to the floor than `posOff`
    665  *      posOff and focOff are sometimes the same address, which just ignores the pos calculation
    666  *! Doesn't return anything, but required to match on -O2
    667  */
    668 BAD_RETURN(f32) calc_y_to_curr_floor(f32 *posOff, f32 posMul, f32 posBound, f32 *focOff, f32 focMul, f32 focBound) {
    669     f32 floorHeight = sMarioGeometry.currFloorHeight;
    670     f32 waterHeight;
    671     UNUSED u8 filler[4];
    672 
    673     if (!(sMarioCamState->action & ACT_FLAG_METAL_WATER)) {
    674         //! @bug this should use sMarioGeometry.waterHeight
    675         if (floorHeight < (waterHeight = find_water_level(sMarioCamState->pos[0], sMarioCamState->pos[2]))) {
    676             floorHeight = waterHeight;
    677         }
    678     }
    679 
    680     if (sMarioCamState->action & ACT_FLAG_ON_POLE) {
    681         if (sMarioGeometry.currFloorHeight >= gMarioStates[0].usedObj->oPosY && sMarioCamState->pos[1]
    682                    < 0.7f * gMarioStates[0].usedObj->hitboxHeight + gMarioStates[0].usedObj->oPosY) {
    683             posBound = 1200;
    684         }
    685     }
    686 
    687     *posOff = (floorHeight - sMarioCamState->pos[1]) * posMul;
    688 
    689     if (*posOff > posBound) {
    690         *posOff = posBound;
    691     }
    692 
    693     if (*posOff < -posBound) {
    694         *posOff = -posBound;
    695     }
    696 
    697     *focOff = (floorHeight - sMarioCamState->pos[1]) * focMul;
    698 
    699     if (*focOff > focBound) {
    700         *focOff = focBound;
    701     }
    702 
    703     if (*focOff < -focBound) {
    704         *focOff = -focBound;
    705     }
    706 }
    707 
    708 void focus_on_mario(Vec3f focus, Vec3f pos, f32 posYOff, f32 focYOff, f32 dist, s16 pitch, s16 yaw) {
    709     Vec3f marioPos;
    710 
    711     marioPos[0] = sMarioCamState->pos[0];
    712     marioPos[1] = sMarioCamState->pos[1] + posYOff;
    713     marioPos[2] = sMarioCamState->pos[2];
    714 
    715     vec3f_set_dist_and_angle(marioPos, pos, dist, pitch + sLakituPitch, yaw);
    716 
    717     focus[0] = sMarioCamState->pos[0];
    718     focus[1] = sMarioCamState->pos[1] + focYOff;
    719     focus[2] = sMarioCamState->pos[2];
    720 }
    721 
    722 static UNUSED void set_pos_to_mario(Vec3f foc, Vec3f pos, f32 yOff, f32 focYOff, f32 dist, s16 pitch, s16 yaw) {
    723     Vec3f marioPos;
    724     f32 posDist;
    725     f32 focDist;
    726 
    727     s16 posPitch;
    728     s16 posYaw;
    729     s16 focPitch;
    730     s16 focYaw;
    731 
    732     vec3f_copy(marioPos, sMarioCamState->pos);
    733     marioPos[1] += yOff;
    734 
    735     vec3f_set_dist_and_angle(marioPos, pos, dist, pitch + sLakituPitch, yaw);
    736     vec3f_get_dist_and_angle(pos, sMarioCamState->pos, &posDist, &posPitch, &posYaw);
    737 
    738     //! Useless get and set
    739     vec3f_get_dist_and_angle(pos, foc, &focDist, &focPitch, &focYaw);
    740     vec3f_set_dist_and_angle(pos, foc, focDist, focPitch, focYaw);
    741 
    742     foc[1] = sMarioCamState->pos[1] + focYOff;
    743 }
    744 
    745 /**
    746  * Set the camera's y coordinate to goalHeight, respecting floors and ceilings in the way
    747  */
    748 void set_camera_height(struct Camera *c, f32 goalHeight) {
    749     struct Surface *surface;
    750     f32 marioFloorHeight;
    751     f32 marioCeilHeight;
    752     f32 camFloorHeight;
    753     UNUSED u8 filler[8];
    754     UNUSED s16 action = sMarioCamState->action;
    755     f32 baseOff = 125.f;
    756     f32 camCeilHeight = find_ceil(c->pos[0], gLakituState.goalPos[1] - 50.f, c->pos[2], &surface);
    757 
    758     if (sMarioCamState->action & ACT_FLAG_HANGING) {
    759         marioCeilHeight = sMarioGeometry.currCeilHeight;
    760         marioFloorHeight = sMarioGeometry.currFloorHeight;
    761 
    762         if (marioFloorHeight < marioCeilHeight - 400.f) {
    763             marioFloorHeight = marioCeilHeight - 400.f;
    764         }
    765 
    766         goalHeight = marioFloorHeight + (marioCeilHeight - marioFloorHeight) * 0.4f;
    767 
    768         if (sMarioCamState->pos[1] - 400 > goalHeight) {
    769             goalHeight = sMarioCamState->pos[1] - 400;
    770         }
    771 
    772         approach_camera_height(c, goalHeight, 5.f);
    773     } else {
    774         camFloorHeight = find_floor(c->pos[0], c->pos[1] + 100.f, c->pos[2], &surface) + baseOff;
    775         marioFloorHeight = baseOff + sMarioGeometry.currFloorHeight;
    776 
    777         if (camFloorHeight < marioFloorHeight) {
    778             camFloorHeight = marioFloorHeight;
    779         }
    780         if (goalHeight < camFloorHeight) {
    781             goalHeight = camFloorHeight;
    782             c->pos[1] = goalHeight;
    783         }
    784         // Warp camera to goalHeight if further than 1000 and Mario is stuck in the ground
    785         if (sMarioCamState->action == ACT_BUTT_STUCK_IN_GROUND ||
    786             sMarioCamState->action == ACT_HEAD_STUCK_IN_GROUND ||
    787             sMarioCamState->action == ACT_FEET_STUCK_IN_GROUND) {
    788             if (ABS(c->pos[1] - goalHeight) > 1000.f) {
    789                 c->pos[1] = goalHeight;
    790             }
    791         }
    792         approach_camera_height(c, goalHeight, 20.f);
    793         if (camCeilHeight != CELL_HEIGHT_LIMIT) {
    794             camCeilHeight -= baseOff;
    795             if ((c->pos[1] > camCeilHeight && sMarioGeometry.currFloorHeight + baseOff < camCeilHeight)
    796                 || (sMarioGeometry.currCeilHeight != CELL_HEIGHT_LIMIT
    797                     && sMarioGeometry.currCeilHeight > camCeilHeight && c->pos[1] > camCeilHeight)) {
    798                 c->pos[1] = camCeilHeight;
    799             }
    800         }
    801     }
    802 }
    803 
    804 /**
    805  * Pitch the camera down when the camera is facing down a slope
    806  */
    807 s16 look_down_slopes(s16 camYaw) {
    808     struct Surface *floor;
    809     f32 floorDY;
    810     // Default pitch
    811     s16 pitch = 0x05B0;
    812     // x and z offsets towards the camera
    813     f32 xOff = sMarioCamState->pos[0] + sins(camYaw) * 40.f;
    814     f32 zOff = sMarioCamState->pos[2] + coss(camYaw) * 40.f;
    815 
    816     floorDY = find_floor(xOff, sMarioCamState->pos[1], zOff, &floor) - sMarioCamState->pos[1];
    817 
    818     if (floor != NULL) {
    819         if (floor->type != SURFACE_WALL_MISC && floorDY > 0) {
    820             if (floor->normal.z == 0.f && floorDY < 100.f) {
    821                 pitch = 0x05B0;
    822             } else {
    823                 // Add the slope's angle of declination to the pitch
    824                 pitch += atan2s(40.f, floorDY);
    825             }
    826         }
    827     }
    828 
    829     return pitch;
    830 }
    831 
    832 /**
    833  * Look ahead to the left or right in the direction the player is facing
    834  * The calculation for pan[0] could be simplified to:
    835  *      yaw = -yaw;
    836  *      pan[0] = sins(sMarioCamState->faceAngle[1] + yaw) * sins(0xC00) * dist;
    837  * Perhaps, early in development, the pan used to be calculated for both the x and z directions
    838  *
    839  * Since this function only affects the camera's focus, Mario's movement direction isn't affected.
    840  */
    841 void pan_ahead_of_player(struct Camera *c) {
    842     f32 dist;
    843     s16 pitch;
    844     s16 yaw;
    845     Vec3f pan = { 0, 0, 0 };
    846 
    847     // Get distance and angle from camera to Mario.
    848     vec3f_get_dist_and_angle(c->pos, sMarioCamState->pos, &dist, &pitch, &yaw);
    849 
    850     // The camera will pan ahead up to about 30% of the camera's distance to Mario.
    851     pan[2] = sins(0xC00) * dist;
    852 
    853     rotate_in_xz(pan, pan, sMarioCamState->faceAngle[1]);
    854     // rotate in the opposite direction
    855     yaw = -yaw;
    856     rotate_in_xz(pan, pan, yaw);
    857     // Only pan left or right
    858     pan[2] = 0.f;
    859 
    860     // If Mario is long jumping, or on a flag pole (but not at the top), then pan in the opposite direction
    861     if (sMarioCamState->action == ACT_LONG_JUMP ||
    862        (sMarioCamState->action != ACT_TOP_OF_POLE && (sMarioCamState->action & ACT_FLAG_ON_POLE))) {
    863         pan[0] = -pan[0];
    864     }
    865 
    866     // Slowly make the actual pan, sPanDistance, approach the calculated pan
    867     // If Mario is sleeping, then don't pan
    868     if (sStatusFlags & CAM_FLAG_SLEEPING) {
    869         approach_f32_asymptotic_bool(&sPanDistance, 0.f, 0.025f);
    870     } else {
    871         approach_f32_asymptotic_bool(&sPanDistance, pan[0], 0.025f);
    872     }
    873 
    874     // Now apply the pan. It's a dir vector to the left or right, rotated by the camera's yaw to Mario
    875     pan[0] = sPanDistance;
    876     yaw = -yaw;
    877     rotate_in_xz(pan, pan, yaw);
    878     vec3f_add(c->focus, pan);
    879 }
    880 
    881 s16 find_in_bounds_yaw_wdw_bob_thi(Vec3f pos, Vec3f origin, s16 yaw) {
    882     switch (gCurrLevelArea) {
    883         case AREA_WDW_MAIN:
    884             yaw = clamp_positions_and_find_yaw(pos, origin, 4508.f, -3739.f, 4508.f, -3739.f);
    885             break;
    886         case AREA_BOB:
    887             yaw = clamp_positions_and_find_yaw(pos, origin, 8000.f, -8000.f, 7050.f, -8000.f);
    888             break;
    889         case AREA_THI_HUGE:
    890             yaw = clamp_positions_and_find_yaw(pos, origin, 8192.f, -8192.f, 8192.f, -8192.f);
    891             break;
    892         case AREA_THI_TINY:
    893             yaw = clamp_positions_and_find_yaw(pos, origin, 2458.f, -2458.f, 2458.f, -2458.f);
    894             break;
    895     }
    896     return yaw;
    897 }
    898 
    899 /**
    900  * Rotates the camera around the area's center point.
    901  */
    902 s32 update_radial_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
    903     f32 cenDistX = sMarioCamState->pos[0] - c->areaCenX;
    904     f32 cenDistZ = sMarioCamState->pos[2] - c->areaCenZ;
    905     s16 camYaw = atan2s(cenDistZ, cenDistX) + sModeOffsetYaw;
    906     s16 pitch = look_down_slopes(camYaw);
    907     UNUSED u8 filler1[4];
    908     f32 posY;
    909     f32 focusY;
    910     UNUSED u8 filler2[8];
    911     f32 yOff = 125.f;
    912     f32 baseDist = 1000.f;
    913 
    914     sAreaYaw = camYaw - sModeOffsetYaw;
    915     calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f);
    916     focus_on_mario(focus, pos, posY + yOff, focusY + yOff, sLakituDist + baseDist, pitch, camYaw);
    917     camYaw = find_in_bounds_yaw_wdw_bob_thi(pos, focus, camYaw);
    918 
    919     return camYaw;
    920 }
    921 
    922 /**
    923  * Update the camera during 8 directional mode
    924  */
    925 s32 update_8_directions_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
    926     UNUSED f32 cenDistX = sMarioCamState->pos[0] - c->areaCenX;
    927     UNUSED f32 cenDistZ = sMarioCamState->pos[2] - c->areaCenZ;
    928     s16 camYaw = s8DirModeBaseYaw + s8DirModeYawOffset;
    929     s16 pitch = look_down_slopes(camYaw);
    930     f32 posY;
    931     f32 focusY;
    932     UNUSED u8 filler[12];
    933     f32 yOff = 125.f;
    934     f32 baseDist = 1000.f;
    935 
    936     sAreaYaw = camYaw;
    937     calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f);
    938     focus_on_mario(focus, pos, posY + yOff, focusY + yOff, sLakituDist + baseDist, pitch, camYaw);
    939     pan_ahead_of_player(c);
    940     if (gCurrLevelArea == AREA_DDD_SUB) {
    941         camYaw = clamp_positions_and_find_yaw(pos, focus, 6839.f, 995.f, 5994.f, -3945.f);
    942     }
    943 
    944     return camYaw;
    945 }
    946 
    947 /**
    948  * Moves the camera for the radial and outward radial camera modes.
    949  *
    950  * If sModeOffsetYaw is 0, the camera points directly at the area center point.
    951  */
    952 void radial_camera_move(struct Camera *c) {
    953     s16 maxAreaYaw = DEGREES(60);
    954     s16 minAreaYaw = DEGREES(-60);
    955     s16 rotateSpeed = 0x1000;
    956     s16 avoidYaw;
    957     s32 avoidStatus;
    958     UNUSED s16 unused1 = 0;
    959     UNUSED s32 unused2 = 0;
    960     f32 areaDistX = sMarioCamState->pos[0] - c->areaCenX;
    961     f32 areaDistZ = sMarioCamState->pos[2] - c->areaCenZ;
    962     UNUSED u8 filler[4];
    963 
    964     // How much the camera's yaw changed
    965     s16 yawOffset = calculate_yaw(sMarioCamState->pos, c->pos) - atan2s(areaDistZ, areaDistX);
    966 
    967     if (yawOffset > maxAreaYaw) {
    968         yawOffset = maxAreaYaw;
    969     }
    970     if (yawOffset < minAreaYaw) {
    971         yawOffset = minAreaYaw;
    972     }
    973 
    974     // Check if Mario stepped on a surface that rotates the camera. For example, when Mario enters the
    975     // gate in BoB, the camera turns right to face up the hill path
    976     if (!(gCameraMovementFlags & CAM_MOVE_ROTATE)) {
    977         if (sMarioGeometry.currFloorType == SURFACE_CAMERA_MIDDLE
    978             && sMarioGeometry.prevFloorType != SURFACE_CAMERA_MIDDLE) {
    979             gCameraMovementFlags |= (CAM_MOVE_RETURN_TO_MIDDLE | CAM_MOVE_ENTERED_ROTATE_SURFACE);
    980         }
    981         if (sMarioGeometry.currFloorType == SURFACE_CAMERA_ROTATE_RIGHT
    982             && sMarioGeometry.prevFloorType != SURFACE_CAMERA_ROTATE_RIGHT) {
    983             gCameraMovementFlags |= (CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
    984         }
    985         if (sMarioGeometry.currFloorType == SURFACE_CAMERA_ROTATE_LEFT
    986             && sMarioGeometry.prevFloorType != SURFACE_CAMERA_ROTATE_LEFT) {
    987             gCameraMovementFlags |= (CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
    988         }
    989     }
    990 
    991     if (gCameraMovementFlags & CAM_MOVE_ENTERED_ROTATE_SURFACE) {
    992         rotateSpeed = 0x200;
    993     }
    994 
    995     if (c->mode == CAMERA_MODE_OUTWARD_RADIAL) {
    996         areaDistX = -areaDistX;
    997         areaDistZ = -areaDistZ;
    998     }
    999 
   1000     // Avoid obstructing walls
   1001     avoidStatus = rotate_camera_around_walls(c, c->pos, &avoidYaw, 0x400);
   1002     if (avoidStatus == 3) {
   1003         if (avoidYaw - atan2s(areaDistZ, areaDistX) + DEGREES(90) < 0) {
   1004             avoidYaw += DEGREES(180);
   1005         }
   1006 
   1007         // We want to change sModeOffsetYaw so that the player is no longer obstructed by the wall.
   1008         // So, we make avoidYaw relative to the yaw around the area center
   1009         avoidYaw -= atan2s(areaDistZ, areaDistX);
   1010 
   1011         // Bound avoid yaw to radial mode constraints
   1012         if (avoidYaw > DEGREES(105)) {
   1013             avoidYaw = DEGREES(105);
   1014         }
   1015         if (avoidYaw < DEGREES(-105)) {
   1016             avoidYaw = DEGREES(-105);
   1017         }
   1018     }
   1019 
   1020     if (gCameraMovementFlags & CAM_MOVE_RETURN_TO_MIDDLE) {
   1021         if (camera_approach_s16_symmetric_bool(&sModeOffsetYaw, 0, rotateSpeed) == 0) {
   1022             gCameraMovementFlags &= ~CAM_MOVE_RETURN_TO_MIDDLE;
   1023         }
   1024     } else {
   1025         // Prevent the player from rotating into obstructing walls
   1026         if ((gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT) && avoidStatus == 3
   1027             && avoidYaw + 0x10 < sModeOffsetYaw) {
   1028             sModeOffsetYaw = avoidYaw;
   1029             gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
   1030         }
   1031         if ((gCameraMovementFlags & CAM_MOVE_ROTATE_LEFT) && avoidStatus == 3
   1032             && avoidYaw - 0x10 > sModeOffsetYaw) {
   1033             sModeOffsetYaw = avoidYaw;
   1034             gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
   1035         }
   1036 
   1037         // If it's the first time rotating, just rotate to +-60 degrees
   1038         if (!(s2ndRotateFlags & CAM_MOVE_ROTATE_RIGHT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT)
   1039             && camera_approach_s16_symmetric_bool(&sModeOffsetYaw, maxAreaYaw, rotateSpeed) == 0) {
   1040             gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
   1041         }
   1042         if (!(s2ndRotateFlags & CAM_MOVE_ROTATE_LEFT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_LEFT)
   1043             && camera_approach_s16_symmetric_bool(&sModeOffsetYaw, minAreaYaw, rotateSpeed) == 0) {
   1044             gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
   1045         }
   1046 
   1047         // If it's the second time rotating, rotate all the way to +-105 degrees.
   1048         if ((s2ndRotateFlags & CAM_MOVE_ROTATE_RIGHT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT)
   1049             && camera_approach_s16_symmetric_bool(&sModeOffsetYaw, DEGREES(105), rotateSpeed) == 0) {
   1050             gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
   1051             s2ndRotateFlags &= ~CAM_MOVE_ROTATE_RIGHT;
   1052         }
   1053         if ((s2ndRotateFlags & CAM_MOVE_ROTATE_LEFT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_LEFT)
   1054             && camera_approach_s16_symmetric_bool(&sModeOffsetYaw, DEGREES(-105), rotateSpeed) == 0) {
   1055             gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
   1056             s2ndRotateFlags &= ~CAM_MOVE_ROTATE_LEFT;
   1057         }
   1058     }
   1059     if (!(gCameraMovementFlags & CAM_MOVE_ROTATE)) {
   1060         // If not rotating, rotate away from walls obscuring Mario from view
   1061         if (avoidStatus == 3) {
   1062             approach_s16_asymptotic_bool(&sModeOffsetYaw, avoidYaw, 10);
   1063         } else {
   1064             if (c->mode == CAMERA_MODE_RADIAL) {
   1065                 // sModeOffsetYaw only updates when Mario is moving
   1066                 rotateSpeed = gMarioStates[0].forwardVel / 32.f * 128.f;
   1067                 camera_approach_s16_symmetric_bool(&sModeOffsetYaw, yawOffset, rotateSpeed);
   1068             }
   1069             if (c->mode == CAMERA_MODE_OUTWARD_RADIAL) {
   1070                 sModeOffsetYaw = offset_yaw_outward_radial(c, atan2s(areaDistZ, areaDistX));
   1071             }
   1072         }
   1073     }
   1074 
   1075     // Bound sModeOffsetYaw within (-120, 120) degrees
   1076     if (sModeOffsetYaw > 0x5554) {
   1077         sModeOffsetYaw = 0x5554;
   1078     }
   1079     if (sModeOffsetYaw < -0x5554) {
   1080         sModeOffsetYaw = -0x5554;
   1081     }
   1082 }
   1083 
   1084 /**
   1085  * Moves Lakitu from zoomed in to zoomed out and vice versa.
   1086  * When C-Down mode is not active, sLakituDist and sLakituPitch decrease to 0.
   1087  */
   1088 void lakitu_zoom(f32 rangeDist, s16 rangePitch) {
   1089     if (sLakituDist < 0) {
   1090         if ((sLakituDist += 30) > 0) {
   1091             sLakituDist = 0;
   1092         }
   1093     } else if (rangeDist < sLakituDist) {
   1094         if ((sLakituDist -= 30) < rangeDist) {
   1095             sLakituDist = rangeDist;
   1096         }
   1097     } else if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   1098         if ((sLakituDist += 30) > rangeDist) {
   1099             sLakituDist = rangeDist;
   1100         }
   1101     } else {
   1102         if ((sLakituDist -= 30) < 0) {
   1103             sLakituDist = 0;
   1104         }
   1105     }
   1106 
   1107     if (gCurrLevelArea == AREA_SSL_PYRAMID && gCamera->mode == CAMERA_MODE_OUTWARD_RADIAL) {
   1108         rangePitch /= 2;
   1109     }
   1110 
   1111     if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   1112         if ((sLakituPitch += rangePitch / 13) > rangePitch) {
   1113             sLakituPitch = rangePitch;
   1114         }
   1115     } else {
   1116         if ((sLakituPitch -= rangePitch / 13) < 0) {
   1117             sLakituPitch = 0;
   1118         }
   1119     }
   1120 }
   1121 
   1122 void radial_camera_input_default(struct Camera *c) {
   1123     radial_camera_input(c, 0.f);
   1124 }
   1125 
   1126 /**
   1127  * Makes Lakitu cam's yaw match the angle turned towards in C-Up mode, and makes Lakitu slowly fly back
   1128  * to the distance he was at before C-Up
   1129  */
   1130 void update_yaw_and_dist_from_c_up(UNUSED struct Camera *c) {
   1131     f32 dist = 1000.f;
   1132 
   1133     sModeOffsetYaw = sModeInfo.transitionStart.yaw - sAreaYaw;
   1134     sLakituDist = sModeInfo.transitionStart.dist - dist;
   1135     // No longer in C-Up
   1136     gCameraMovementFlags &= ~CAM_MOVING_INTO_MODE;
   1137 }
   1138 
   1139 /**
   1140  * Handles input and updates for the radial camera mode
   1141  */
   1142 void mode_radial_camera(struct Camera *c) {
   1143     Vec3f pos;
   1144     UNUSED u8 filler1[8];
   1145     s16 oldAreaYaw = sAreaYaw;
   1146     UNUSED u8 filler2[4];
   1147 
   1148     if (gCameraMovementFlags & CAM_MOVING_INTO_MODE) {
   1149         update_yaw_and_dist_from_c_up(c);
   1150     }
   1151 
   1152     radial_camera_input_default(c);
   1153     radial_camera_move(c);
   1154 
   1155     if (c->mode == CAMERA_MODE_RADIAL) {
   1156         lakitu_zoom(400.f, 0x900);
   1157     }
   1158     c->nextYaw = update_radial_camera(c, c->focus, pos);
   1159     c->pos[0] = pos[0];
   1160     c->pos[2] = pos[2];
   1161     sAreaYawChange = sAreaYaw - oldAreaYaw;
   1162     if (sMarioCamState->action == ACT_RIDING_HOOT) {
   1163         pos[1] += 500.f;
   1164     }
   1165     set_camera_height(c, pos[1]);
   1166     pan_ahead_of_player(c);
   1167 }
   1168 
   1169 /**
   1170  * A mode that only has 8 camera angles, 45 degrees apart
   1171  */
   1172 void mode_8_directions_camera(struct Camera *c) {
   1173     Vec3f pos;
   1174     UNUSED u8 filler[8];
   1175     s16 oldAreaYaw = sAreaYaw;
   1176 
   1177     radial_camera_input(c, 0.f);
   1178 
   1179     if (gPlayer1Controller->buttonPressed & R_CBUTTONS) {
   1180         s8DirModeYawOffset += DEGREES(45);
   1181         play_sound_cbutton_side();
   1182     }
   1183     if (gPlayer1Controller->buttonPressed & L_CBUTTONS) {
   1184         s8DirModeYawOffset -= DEGREES(45);
   1185         play_sound_cbutton_side();
   1186     }
   1187 
   1188     lakitu_zoom(400.f, 0x900);
   1189     c->nextYaw = update_8_directions_camera(c, c->focus, pos);
   1190     c->pos[0] = pos[0];
   1191     c->pos[2] = pos[2];
   1192     sAreaYawChange = sAreaYaw - oldAreaYaw;
   1193     set_camera_height(c, pos[1]);
   1194 }
   1195 
   1196 /**
   1197  * Updates the camera in outward radial mode.
   1198  * sModeOffsetYaw is calculated in radial_camera_move, which calls offset_yaw_outward_radial
   1199  */
   1200 s32 update_outward_radial_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
   1201     f32 xDistFocToMario = sMarioCamState->pos[0] - c->areaCenX;
   1202     f32 zDistFocToMario = sMarioCamState->pos[2] - c->areaCenZ;
   1203     s16 camYaw = atan2s(zDistFocToMario, xDistFocToMario) + sModeOffsetYaw + DEGREES(180);
   1204     s16 pitch = look_down_slopes(camYaw);
   1205     f32 baseDist = 1000.f;
   1206     // A base offset of 125.f is ~= Mario's eye height
   1207     f32 yOff = 125.f;
   1208     f32 posY;
   1209     f32 focusY;
   1210 
   1211     sAreaYaw = camYaw - sModeOffsetYaw - DEGREES(180);
   1212     calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f);
   1213     focus_on_mario(focus, pos, posY + yOff, focusY + yOff, sLakituDist + baseDist, pitch, camYaw);
   1214 
   1215     return camYaw;
   1216 }
   1217 
   1218 /**
   1219  * Input and updates for the outward radial mode.
   1220  */
   1221 void mode_outward_radial_camera(struct Camera *c) {
   1222     Vec3f pos;
   1223     s16 oldAreaYaw = sAreaYaw;
   1224 
   1225     if (gCameraMovementFlags & CAM_MOVING_INTO_MODE) {
   1226         update_yaw_and_dist_from_c_up(c);
   1227     }
   1228     radial_camera_input_default(c);
   1229     radial_camera_move(c);
   1230     lakitu_zoom(400.f, 0x900);
   1231     c->nextYaw = update_outward_radial_camera(c, c->focus, pos);
   1232     c->pos[0] = pos[0];
   1233     c->pos[2] = pos[2];
   1234     sAreaYawChange = sAreaYaw - oldAreaYaw;
   1235     if (sMarioCamState->action == ACT_RIDING_HOOT) {
   1236         pos[1] += 500.f;
   1237     }
   1238     set_camera_height(c, pos[1]);
   1239     pan_ahead_of_player(c);
   1240 }
   1241 
   1242 /**
   1243  * Move the camera in parallel tracking mode
   1244  *
   1245  * Uses the line between the next two points in sParTrackPath
   1246  * The camera can move forward/back and side to side, but it will face perpendicular to that line
   1247  *
   1248  * Although, annoyingly, it's not truly parallel, the function returns the yaw from the camera to Mario,
   1249  * so Mario will run slightly towards the camera.
   1250  */
   1251 s32 update_parallel_tracking_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
   1252     Vec3f path[2];
   1253     Vec3f parMidPoint;
   1254     Vec3f marioOffset;
   1255     Vec3f camOffset;
   1256     /// Adjusts the focus to look where Mario is facing. Unused since marioOffset is copied to focus
   1257     Vec3f focOffset;
   1258     s16 pathPitch;
   1259     s16 pathYaw;
   1260     UNUSED u8 filler[4];
   1261     f32 distThresh;
   1262     f32 zoom;
   1263     f32 camParDist;
   1264     UNUSED u8 filler2[8];
   1265     f32 pathLength;
   1266     UNUSED u8 filler3[8];
   1267     UNUSED f32 unusedScale = 0.5f;
   1268     f32 parScale = 0.5f;
   1269     f32 marioFloorDist;
   1270     Vec3f marioPos;
   1271     UNUSED u8 filler4[12];
   1272     UNUSED Vec3f unused;
   1273     Vec3s pathAngle;
   1274     // Variables for changing to the next/prev path in the list
   1275     Vec3f oldPos;
   1276     Vec3f prevPathPos;
   1277     Vec3f nextPathPos;
   1278     f32 distToNext;
   1279     f32 distToPrev;
   1280     s16 prevPitch;
   1281     s16 nextPitch;
   1282     s16 prevYaw;
   1283     s16 nextYaw;
   1284 
   1285     unused[0] = 0.f;
   1286     unused[1] = 0.f;
   1287     unused[2] = 0.f;
   1288 
   1289     // Store camera pos, for changing between paths
   1290     vec3f_copy(oldPos, pos);
   1291 
   1292     vec3f_copy(path[0], sParTrackPath[sParTrackIndex].pos);
   1293     vec3f_copy(path[1], sParTrackPath[sParTrackIndex + 1].pos);
   1294 
   1295     distThresh = sParTrackPath[sParTrackIndex].distThresh;
   1296     zoom = sParTrackPath[sParTrackIndex].zoom;
   1297     calc_y_to_curr_floor(&marioFloorDist, 1.f, 200.f, &marioFloorDist, 0.9f, 200.f);
   1298 
   1299     marioPos[0] = sMarioCamState->pos[0];
   1300     // Mario's y pos + ~Mario's height + Mario's height above the floor
   1301     marioPos[1] = sMarioCamState->pos[1] + 150.f + marioFloorDist;
   1302     marioPos[2] = sMarioCamState->pos[2];
   1303 
   1304     // Calculate middle of the path (parScale is 0.5f)
   1305     parMidPoint[0] = path[0][0] + (path[1][0] - path[0][0]) * parScale;
   1306     parMidPoint[1] = path[0][1] + (path[1][1] - path[0][1]) * parScale;
   1307     parMidPoint[2] = path[0][2] + (path[1][2] - path[0][2]) * parScale;
   1308 
   1309     // Get direction of path
   1310     vec3f_get_dist_and_angle(path[0], path[1], &pathLength, &pathPitch, &pathYaw);
   1311 
   1312     marioOffset[0] = marioPos[0] - parMidPoint[0];
   1313     marioOffset[1] = marioPos[1] - parMidPoint[1];
   1314     marioOffset[2] = marioPos[2] - parMidPoint[2];
   1315 
   1316     // Make marioOffset point from the midpoint -> the start of the path
   1317     // Rotating by -yaw then -pitch moves the hor dist from the midpoint into marioOffset's z coordinate
   1318     // marioOffset[0] = the (perpendicular) horizontal distance from the path
   1319     // marioOffset[1] = the vertical distance from the path
   1320     // marioOffset[2] = the (parallel) horizontal distance from the path's midpoint
   1321     pathYaw = -pathYaw;
   1322     rotate_in_xz(marioOffset, marioOffset, pathYaw);
   1323     pathYaw = -pathYaw;
   1324     pathPitch = -pathPitch;
   1325     rotate_in_yz(marioOffset, marioOffset, pathPitch);
   1326     pathPitch = -pathPitch;
   1327     vec3f_copy(focOffset, marioOffset);
   1328 
   1329     // OK
   1330     focOffset[0] = -focOffset[0] * 0.f;
   1331     focOffset[1] = focOffset[1] * 0.f;
   1332 
   1333     // Repeat above calcs with camOffset
   1334     camOffset[0] = pos[0] - parMidPoint[0];
   1335     camOffset[1] = pos[1] - parMidPoint[1];
   1336     camOffset[2] = pos[2] - parMidPoint[2];
   1337     pathYaw = -pathYaw;
   1338     rotate_in_xz(camOffset, camOffset, pathYaw);
   1339     pathYaw = -pathYaw;
   1340     pathPitch = -pathPitch;
   1341     rotate_in_yz(camOffset, camOffset, pathPitch);
   1342     pathPitch = -pathPitch;
   1343 
   1344     // If Mario is distThresh units away from the camera along the path, move the camera
   1345     //! When distThresh != 0, it causes Mario to move slightly towards the camera when running sideways
   1346     //! Set each ParallelTrackingPoint's distThresh to 0 to make Mario truly run parallel to the path
   1347     if (marioOffset[2] > camOffset[2]) {
   1348         if (marioOffset[2] - camOffset[2] > distThresh) {
   1349             camOffset[2] = marioOffset[2] - distThresh;
   1350         }
   1351     } else {
   1352         if (marioOffset[2] - camOffset[2] < -distThresh) {
   1353             camOffset[2] = marioOffset[2] + distThresh;
   1354         }
   1355     }
   1356 
   1357     // If zoom != 0.0, the camera will move zoom% closer to Mario
   1358     marioOffset[0] = -marioOffset[0] * zoom;
   1359     marioOffset[1] = marioOffset[1] * zoom;
   1360     marioOffset[2] = camOffset[2];
   1361 
   1362     //! Does nothing because focOffset[0] is always 0
   1363     focOffset[0] *= 0.3f;
   1364     //! Does nothing because focOffset[1] is always 0
   1365     focOffset[1] *= 0.3f;
   1366 
   1367     pathAngle[0] = pathPitch;
   1368     pathAngle[1] = pathYaw; //! No effect
   1369 
   1370     // make marioOffset[2] == distance from the start of the path
   1371     marioOffset[2] = pathLength / 2 - marioOffset[2];
   1372 
   1373     pathAngle[1] = pathYaw + DEGREES(180);
   1374     pathAngle[2] = 0;
   1375 
   1376     // Rotate the offset in the direction of the path again
   1377     offset_rotated(pos, path[0], marioOffset, pathAngle);
   1378     vec3f_get_dist_and_angle(path[0], c->pos, &camParDist, &pathPitch, &pathYaw);
   1379 
   1380     // Adjust the focus. Does nothing, focus is set to Mario at the end
   1381     focOffset[2] = pathLength / 2 - focOffset[2];
   1382     offset_rotated(c->focus, path[0], focOffset, pathAngle);
   1383 
   1384     // Changing paths, update the stored position offset
   1385     if (sStatusFlags & CAM_FLAG_CHANGED_PARTRACK_INDEX) {
   1386         sStatusFlags &= ~CAM_FLAG_CHANGED_PARTRACK_INDEX;
   1387         sParTrackTransOff.pos[0] = oldPos[0] - c->pos[0];
   1388         sParTrackTransOff.pos[1] = oldPos[1] - c->pos[1];
   1389         sParTrackTransOff.pos[2] = oldPos[2] - c->pos[2];
   1390     }
   1391     // Slowly transition to the next path
   1392     approach_f32_asymptotic_bool(&sParTrackTransOff.pos[0], 0.f, 0.025f);
   1393     approach_f32_asymptotic_bool(&sParTrackTransOff.pos[1], 0.f, 0.025f);
   1394     approach_f32_asymptotic_bool(&sParTrackTransOff.pos[2], 0.f, 0.025f);
   1395     vec3f_add(c->pos, sParTrackTransOff.pos);
   1396 
   1397     // Check if the camera should go to the next path
   1398     if (sParTrackPath[sParTrackIndex + 1].startOfPath != 0) {
   1399         // get Mario's distance to the next path
   1400         calculate_angles(sParTrackPath[sParTrackIndex + 1].pos, sParTrackPath[sParTrackIndex + 2].pos, &nextPitch, &nextYaw);
   1401         vec3f_set_dist_and_angle(sParTrackPath[sParTrackIndex + 1].pos, nextPathPos, 400.f, nextPitch, nextYaw);
   1402         distToPrev = calc_abs_dist(marioPos, nextPathPos);
   1403 
   1404         // get Mario's distance to the previous path
   1405         calculate_angles(sParTrackPath[sParTrackIndex + 1].pos, sParTrackPath[sParTrackIndex].pos, &prevPitch, &prevYaw);
   1406         vec3f_set_dist_and_angle(sParTrackPath[sParTrackIndex + 1].pos, prevPathPos, 400.f, prevPitch, prevYaw);
   1407         distToNext = calc_abs_dist(marioPos, prevPathPos);
   1408         if (distToPrev < distToNext) {
   1409             sParTrackIndex++;
   1410             sStatusFlags |= CAM_FLAG_CHANGED_PARTRACK_INDEX;
   1411         }
   1412     }
   1413 
   1414     // Check if the camera should go to the previous path
   1415     if (sParTrackIndex != 0) {
   1416         // get Mario's distance to the next path
   1417         calculate_angles((*(sParTrackPath + sParTrackIndex)).pos, (*(sParTrackPath + sParTrackIndex + 1)).pos, &nextPitch, &nextYaw);
   1418         vec3f_set_dist_and_angle(sParTrackPath[sParTrackIndex].pos, nextPathPos, 700.f, nextPitch, nextYaw);
   1419         distToPrev = calc_abs_dist(marioPos, nextPathPos);
   1420 
   1421         // get Mario's distance to the previous path
   1422         calculate_angles((*(sParTrackPath + sParTrackIndex)).pos, (*(sParTrackPath + sParTrackIndex - 1)).pos, &prevPitch, &prevYaw);
   1423         vec3f_set_dist_and_angle(sParTrackPath[sParTrackIndex].pos, prevPathPos, 700.f, prevPitch, prevYaw);
   1424         distToNext = calc_abs_dist(marioPos, prevPathPos);
   1425         if (distToPrev > distToNext) {
   1426             sParTrackIndex--;
   1427             sStatusFlags |= CAM_FLAG_CHANGED_PARTRACK_INDEX;
   1428         }
   1429     }
   1430 
   1431     // Update the camera focus and return the camera's yaw
   1432     vec3f_copy(focus, marioPos);
   1433     vec3f_get_dist_and_angle(focus, pos, &camParDist, &pathPitch, &pathYaw);
   1434     return pathYaw;
   1435 }
   1436 
   1437 /**
   1438  * Updates the camera during fixed mode.
   1439  */
   1440 s32 update_fixed_camera(struct Camera *c, Vec3f focus, UNUSED Vec3f pos) {
   1441     f32 focusFloorOff;
   1442     f32 goalHeight;
   1443     f32 ceilHeight;
   1444     f32 heightOffset;
   1445     f32 distCamToFocus;
   1446     UNUSED u8 filler2[8];
   1447     f32 scaleToMario = 0.5f;
   1448     s16 pitch;
   1449     s16 yaw;
   1450     Vec3s faceAngle;
   1451     struct Surface *ceiling;
   1452     Vec3f basePos;
   1453     UNUSED u8 filler[12];
   1454 
   1455     play_camera_buzz_if_c_sideways();
   1456 
   1457     // Don't move closer to Mario in these areas
   1458     switch (gCurrLevelArea) {
   1459         case AREA_RR:
   1460             scaleToMario = 0.f;
   1461             heightOffset = 0.f;
   1462             break;
   1463 
   1464         case AREA_CASTLE_LOBBY:
   1465             scaleToMario = 0.3f;
   1466             heightOffset = 0.f;
   1467             break;
   1468 
   1469         case AREA_BBH:
   1470             scaleToMario = 0.f;
   1471             heightOffset = 0.f;
   1472             break;
   1473     }
   1474 
   1475     handle_c_button_movement(c);
   1476     play_camera_buzz_if_cdown();
   1477 
   1478     calc_y_to_curr_floor(&focusFloorOff, 1.f, 200.f, &focusFloorOff, 0.9f, 200.f);
   1479     vec3f_copy(focus, sMarioCamState->pos);
   1480     focus[1] += focusFloorOff + 125.f;
   1481     vec3f_get_dist_and_angle(focus, c->pos, &distCamToFocus, &faceAngle[0], &faceAngle[1]);
   1482     faceAngle[2] = 0;
   1483 
   1484     vec3f_copy(basePos, sFixedModeBasePosition);
   1485     vec3f_add(basePos, sCastleEntranceOffset);
   1486 
   1487     if (sMarioGeometry.currFloorType != SURFACE_DEATH_PLANE
   1488         && sMarioGeometry.currFloorHeight != FLOOR_LOWER_LIMIT) {
   1489         goalHeight = sMarioGeometry.currFloorHeight + basePos[1] + heightOffset;
   1490     } else {
   1491         goalHeight = gLakituState.goalPos[1];
   1492     }
   1493 
   1494     if (300 > distCamToFocus) {
   1495         goalHeight += 300 - distCamToFocus;
   1496     }
   1497 
   1498     ceilHeight = find_ceil(c->pos[0], goalHeight - 100.f, c->pos[2], &ceiling);
   1499     if (ceilHeight != CELL_HEIGHT_LIMIT) {
   1500         if (goalHeight > (ceilHeight -= 125.f)) {
   1501             goalHeight = ceilHeight;
   1502         }
   1503     }
   1504 
   1505     if (sStatusFlags & CAM_FLAG_SMOOTH_MOVEMENT) {
   1506         camera_approach_f32_symmetric_bool(&c->pos[1], goalHeight, 15.f);
   1507     } else {
   1508         if (goalHeight < sMarioCamState->pos[1] - 500.f) {
   1509             goalHeight = sMarioCamState->pos[1] - 500.f;
   1510         }
   1511         c->pos[1] = goalHeight;
   1512     }
   1513 
   1514     c->pos[0] = basePos[0] + (sMarioCamState->pos[0] - basePos[0]) * scaleToMario;
   1515     c->pos[2] = basePos[2] + (sMarioCamState->pos[2] - basePos[2]) * scaleToMario;
   1516 
   1517     if (scaleToMario != 0.f) {
   1518         vec3f_get_dist_and_angle(c->focus, c->pos, &distCamToFocus, &pitch, &yaw);
   1519         if (distCamToFocus > 1000.f) {
   1520             distCamToFocus = 1000.f;
   1521             vec3f_set_dist_and_angle(c->focus, c->pos, distCamToFocus, pitch, yaw);
   1522         }
   1523     }
   1524 
   1525     return faceAngle[1];
   1526 }
   1527 
   1528 /**
   1529  * Updates the camera during a boss fight
   1530  */
   1531 s32 update_boss_fight_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
   1532     struct Object *o;
   1533     UNUSED u8 filler2[12];
   1534     f32 focusDistance;
   1535     UNUSED u8 filler3[4];
   1536     // Floor normal values
   1537     f32 nx;
   1538     f32 ny;
   1539     f32 nz;
   1540     /// Floor originOffset
   1541     f32 oo;
   1542     UNUSED u8 filler4[4];
   1543     UNUSED s16 unused;
   1544     s16 yaw;
   1545     s16 heldState;
   1546     struct Surface *floor;
   1547     UNUSED u8 filler[20];
   1548     Vec3f secondFocus;
   1549     Vec3f holdFocOffset = { 0.f, -150.f, -125.f };
   1550 
   1551     handle_c_button_movement(c);
   1552 
   1553     // Start camera shakes if bowser jumps or gets thrown.
   1554     if (sMarioCamState->cameraEvent == CAM_EVENT_BOWSER_JUMP) {
   1555         set_environmental_camera_shake(SHAKE_ENV_BOWSER_JUMP);
   1556         sMarioCamState->cameraEvent = 0;
   1557     }
   1558     if (sMarioCamState->cameraEvent == CAM_EVENT_BOWSER_THROW_BOUNCE) {
   1559         set_environmental_camera_shake(SHAKE_ENV_BOWSER_THROW_BOUNCE);
   1560         sMarioCamState->cameraEvent = 0;
   1561     }
   1562 
   1563     yaw = sModeOffsetYaw + DEGREES(45);
   1564     // Get boss's position and whether Mario is holding it.
   1565     if ((o = gSecondCameraFocus) != NULL) {
   1566         object_pos_to_vec3f(secondFocus, o);
   1567         heldState = o->oHeldState;
   1568     } else {
   1569     // If no boss is there, just rotate around the area's center point.
   1570         secondFocus[0] = c->areaCenX;
   1571         secondFocus[1] = sMarioCamState->pos[1];
   1572         secondFocus[2] = c->areaCenZ;
   1573         heldState = 0;
   1574     }
   1575 
   1576     focusDistance = calc_abs_dist(sMarioCamState->pos, secondFocus) * 1.6f;
   1577     if (focusDistance < 800.f) {
   1578         focusDistance = 800.f;
   1579     }
   1580     if (focusDistance > 5000.f) {
   1581         focusDistance = 5000.f;
   1582     }
   1583 
   1584     // If holding the boss, add a slight offset to secondFocus so that the spinning is more pronounced.
   1585     if (heldState == 1) {
   1586         offset_rotated(secondFocus, sMarioCamState->pos, holdFocOffset, sMarioCamState->faceAngle);
   1587     }
   1588 
   1589     // Set the camera focus to the average of Mario and secondFocus
   1590     focus[0] = (sMarioCamState->pos[0] + secondFocus[0]) / 2.f;
   1591     focus[1] = (sMarioCamState->pos[1] + secondFocus[1]) / 2.f + 125.f;
   1592     focus[2] = (sMarioCamState->pos[2] + secondFocus[2]) / 2.f;
   1593 
   1594     // Calculate the camera's position as an offset from the focus
   1595     // When C-Down is not active, this
   1596     vec3f_set_dist_and_angle(focus, pos, focusDistance, 0x1000, yaw);
   1597     // Find the floor of the arena
   1598     pos[1] = find_floor(c->areaCenX, CELL_HEIGHT_LIMIT, c->areaCenZ, &floor);
   1599     if (floor != NULL) {
   1600         nx = floor->normal.x;
   1601         ny = floor->normal.y;
   1602         nz = floor->normal.z;
   1603         oo = floor->originOffset;
   1604         pos[1] = 300.f - (nx * pos[0] + nz * pos[2] + oo) / ny;
   1605         switch (gCurrLevelArea) {
   1606             case AREA_BOB:
   1607                 pos[1] += 125.f;
   1608                 //! fall through, makes the BoB boss fight camera move up twice as high as it should
   1609             case AREA_WF:
   1610                 pos[1] += 125.f;
   1611         }
   1612     }
   1613 
   1614     //! Must be same line to match on -O2
   1615     // Prevent the camera from going to the ground in the outside boss fight
   1616     if (gCurrLevelNum == LEVEL_BBH) { pos[1] = 2047.f; }
   1617 
   1618     // Rotate from C-Button input
   1619     if (sCSideButtonYaw < 0) {
   1620         sModeOffsetYaw += 0x200;
   1621         if ((sCSideButtonYaw += 0x100) > 0) {
   1622             sCSideButtonYaw = 0;
   1623         }
   1624     }
   1625     if (sCSideButtonYaw > 0) {
   1626         sModeOffsetYaw -= 0x200;
   1627         if ((sCSideButtonYaw -= 0x100) < 0) {
   1628             sCSideButtonYaw = 0;
   1629         }
   1630     }
   1631 
   1632     focus[1] = (sMarioCamState->pos[1] + secondFocus[1]) / 2.f + 100.f;
   1633     if (heldState == 1) {
   1634         focus[1] += 300.f * sins((gMarioStates[0].angleVel[1] > 0.f) ?  gMarioStates[0].angleVel[1]
   1635                                                                      : -gMarioStates[0].angleVel[1]);
   1636     }
   1637 
   1638     //! Unnecessary conditional, focusDistance is already bounded to 800
   1639     if (focusDistance < 400.f) {
   1640         focusDistance = 400.f;
   1641     }
   1642 
   1643     // Set C-Down distance and pitch.
   1644     // C-Down will essentially double the distance from the center.
   1645     // sLakituPitch approaches 33.75 degrees.
   1646     lakitu_zoom(focusDistance, 0x1800);
   1647 
   1648     // Move the camera position back as sLakituDist and sLakituPitch increase.
   1649     // This doesn't zoom out of bounds because pos is set above each frame.
   1650     // The constant 0x1000 doubles the pitch from the center when sLakituPitch is 0
   1651     // When Lakitu is fully zoomed out, the pitch comes to 0x3800, or 78.75 degrees, up from the focus.
   1652     vec3f_set_dist_and_angle(pos, pos, sLakituDist, sLakituPitch + 0x1000, yaw);
   1653 
   1654     return yaw;
   1655 }
   1656 
   1657 // 2nd iteration of data
   1658 s16 unused8032D0A8[] = { 14, 1, 2, 4 };
   1659 s16 unused8032D0B0[] = { 16, 9, 17, 0 };
   1660 
   1661 /**
   1662  * Maps cutscene to numbers in [0,4]. Used in determine_dance_cutscene() with sDanceCutsceneIndexTable.
   1663  *
   1664  * Only the first 5 entries are used. Perhaps the last 5 were bools used to indicate whether the star
   1665  * type exits the course or not.
   1666  */
   1667 u8 sDanceCutsceneTable[] = {
   1668     CUTSCENE_DANCE_FLY_AWAY, CUTSCENE_DANCE_ROTATE, CUTSCENE_DANCE_CLOSEUP, CUTSCENE_KEY_DANCE, CUTSCENE_DANCE_DEFAULT,
   1669     FALSE,                   FALSE,                 FALSE,                  FALSE,              TRUE,
   1670 };
   1671 
   1672 /**
   1673  * Perhaps used by different dance cutscenes.
   1674  */
   1675 struct UnusedDanceInfo {
   1676     Vec3f point;
   1677     f32 distTarget;
   1678     f32 distMultiplier;
   1679 };
   1680 
   1681 struct UnusedDanceInfo unusedDanceInfo1 = {
   1682     { -3026.0f, 912.0f, -2148.0f }, 600.0f, 0.3f
   1683 };
   1684 
   1685 u32 unusedDanceType = 0;
   1686 
   1687 struct UnusedDanceInfo unusedDanceInfo2 = {
   1688     { -4676.0f, 917.0f, -3802.0f }, 600.0f, 0.3f
   1689 };
   1690 
   1691 /**
   1692  * Table that dictates camera movement in bookend room.
   1693  * Due to only the X being varied in the table, this only moves along the X axis linearly.
   1694  * Third entry is seemingly unused.
   1695  */
   1696 struct ParallelTrackingPoint sBBHLibraryParTrackPath[] = {
   1697     { 1, { -929.0f, 1619.0f, -1490.0f }, 50.0f, 0.0f },
   1698     { 0, { -2118.0f, 1619.0f, -1490.0f }, 50.0f, 0.0f },
   1699     { 0, { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f },
   1700 };
   1701 
   1702 s32 unused_update_mode_5_camera(UNUSED struct Camera *c, UNUSED Vec3f focus, UNUSED Vec3f pos) {
   1703 #ifdef AVOID_UB
   1704    return 0;
   1705 #endif
   1706 }
   1707 
   1708 UNUSED static void stub_camera_1(UNUSED s32 unused) {
   1709 }
   1710 
   1711 void mode_boss_fight_camera(struct Camera *c) {
   1712     c->nextYaw = update_boss_fight_camera(c, c->focus, c->pos);
   1713 }
   1714 
   1715 /**
   1716  * Parallel tracking mode, the camera faces perpendicular to a line defined by sParTrackPath
   1717  *
   1718  * @see update_parallel_tracking_camera
   1719  */
   1720 void mode_parallel_tracking_camera(struct Camera *c) {
   1721     s16 dummy;
   1722 
   1723     radial_camera_input(c, 0.f);
   1724     set_fov_function(CAM_FOV_DEFAULT);
   1725     c->nextYaw = update_parallel_tracking_camera(c, c->focus, c->pos);
   1726     camera_approach_s16_symmetric_bool(&dummy, 0, 0x0400);
   1727 }
   1728 
   1729 /**
   1730  * Fixed camera mode, the camera rotates around a point and looks and zooms toward Mario.
   1731  */
   1732 void mode_fixed_camera(struct Camera *c) {
   1733     UNUSED u8 filler[8];
   1734 
   1735     if (gCurrLevelNum == LEVEL_BBH) {
   1736         set_fov_function(CAM_FOV_BBH);
   1737     } else {
   1738         set_fov_function(CAM_FOV_APP_45);
   1739     }
   1740     c->nextYaw = update_fixed_camera(c, c->focus, c->pos);
   1741     c->yaw = c->nextYaw;
   1742     pan_ahead_of_player(c);
   1743     vec3f_set(sCastleEntranceOffset, 0.f, 0.f, 0.f);
   1744 }
   1745 
   1746 /**
   1747  * Updates the camera in BEHIND_MARIO mode.
   1748  *
   1749  * The C-Buttons rotate the camera 90 degrees left/right and 67.5 degrees up/down.
   1750  */
   1751 s32 update_behind_mario_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
   1752     UNUSED u8 filler1[12];
   1753     f32 dist;
   1754     UNUSED u8 filler2[4];
   1755     s16 absPitch;
   1756     s16 pitch;
   1757     s16 yaw;
   1758     s16 goalPitch = -sMarioCamState->faceAngle[0];
   1759     s16 marioYaw = sMarioCamState->faceAngle[1] + DEGREES(180);
   1760     s16 goalYawOff = 0;
   1761     s16 yawSpeed;
   1762     s16 pitchInc = 32;
   1763     UNUSED u8 filler3[12];
   1764     f32 maxDist = 800.f;
   1765     f32 focYOff = 125.f;
   1766 
   1767     // Zoom in when Mario R_TRIG mode is active
   1768     if (sSelectionFlags & CAM_MODE_MARIO_ACTIVE) {
   1769         maxDist = 350.f;
   1770         focYOff = 120.f;
   1771     }
   1772     if (!(sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER))) {
   1773         pitchInc = 128;
   1774     }
   1775 
   1776     // Focus on Mario
   1777     vec3f_copy(focus, sMarioCamState->pos);
   1778     c->focus[1] += focYOff;
   1779     //! @bug unnecessary
   1780     dist = calc_abs_dist(focus, pos);
   1781     //! @bug unnecessary
   1782     pitch = calculate_pitch(focus, pos);
   1783     vec3f_get_dist_and_angle(focus, pos, &dist, &pitch, &yaw);
   1784     if (dist > maxDist) {
   1785         dist = maxDist;
   1786     }
   1787     if ((absPitch = pitch) < 0) {
   1788         absPitch = -absPitch;
   1789     }
   1790 
   1791     // Determine the yaw speed based on absPitch. A higher absPitch (further away from looking straight)
   1792     // translates to a slower speed
   1793     // Note: Pitch is always within +- 90 degrees or +-0x4000, and 0x4000 / 0x200 = 32
   1794     yawSpeed = 32 - absPitch / 0x200;
   1795     if (yawSpeed < 1) {
   1796         yawSpeed = 1;
   1797     }
   1798     if (yawSpeed > 32) {
   1799         yawSpeed = 32;
   1800     }
   1801 
   1802     if (sCSideButtonYaw != 0) {
   1803         camera_approach_s16_symmetric_bool(&sCSideButtonYaw, 0, 1);
   1804         yawSpeed = 8;
   1805     }
   1806     if (sBehindMarioSoundTimer != 0) {
   1807         goalPitch = 0;
   1808         camera_approach_s16_symmetric_bool(&sBehindMarioSoundTimer, 0, 1);
   1809         pitchInc = 0x800;
   1810     }
   1811 
   1812     if (sBehindMarioSoundTimer == 28) {
   1813         if (sCSideButtonYaw < 5 || sCSideButtonYaw > 28) {
   1814             play_sound_cbutton_up();
   1815         }
   1816     }
   1817     if (sCSideButtonYaw == 28) {
   1818         if (sBehindMarioSoundTimer < 5 || sBehindMarioSoundTimer > 28) {
   1819             play_sound_cbutton_up();
   1820         }
   1821     }
   1822 
   1823     // C-Button input. Note: Camera rotates in the opposite direction of the button (airplane controls)
   1824     //! @bug C-Right and C-Up take precedence due to the way input is handled here
   1825 
   1826     // Rotate right
   1827     if (sCButtonsPressed & L_CBUTTONS) {
   1828         if (gPlayer1Controller->buttonPressed & L_CBUTTONS) {
   1829             play_sound_cbutton_side();
   1830         }
   1831         if (dist < maxDist) {
   1832             camera_approach_f32_symmetric_bool(&dist, maxDist, 5.f);
   1833         }
   1834         goalYawOff = -0x3FF8;
   1835         sCSideButtonYaw = 30;
   1836         yawSpeed = 2;
   1837     }
   1838     // Rotate left
   1839     if (sCButtonsPressed & R_CBUTTONS) {
   1840         if (gPlayer1Controller->buttonPressed & R_CBUTTONS) {
   1841             play_sound_cbutton_side();
   1842         }
   1843         if (dist < maxDist) {
   1844             camera_approach_f32_symmetric_bool(&dist, maxDist, 5.f);
   1845         }
   1846         goalYawOff = 0x3FF8;
   1847         sCSideButtonYaw = 30;
   1848         yawSpeed = 2;
   1849     }
   1850     // Rotate up
   1851     if (sCButtonsPressed & D_CBUTTONS) {
   1852         if (gPlayer1Controller->buttonPressed & (U_CBUTTONS | D_CBUTTONS)) {
   1853             play_sound_cbutton_side();
   1854         }
   1855         if (dist < maxDist) {
   1856             camera_approach_f32_symmetric_bool(&dist, maxDist, 5.f);
   1857         }
   1858         goalPitch = -0x3000;
   1859         sBehindMarioSoundTimer = 30;
   1860         pitchInc = 0x800;
   1861     }
   1862     // Rotate down
   1863     if (sCButtonsPressed & U_CBUTTONS) {
   1864         if (gPlayer1Controller->buttonPressed & (U_CBUTTONS | D_CBUTTONS)) {
   1865             play_sound_cbutton_side();
   1866         }
   1867         if (dist < maxDist) {
   1868             camera_approach_f32_symmetric_bool(&dist, maxDist, 5.f);
   1869         }
   1870         goalPitch = 0x3000;
   1871         sBehindMarioSoundTimer = 30;
   1872         pitchInc = 0x800;
   1873     }
   1874 
   1875     approach_s16_asymptotic_bool(&yaw, marioYaw + goalYawOff, yawSpeed);
   1876     camera_approach_s16_symmetric_bool(&pitch, goalPitch, pitchInc);
   1877     if (dist < 300.f) {
   1878         dist = 300.f;
   1879     }
   1880     vec3f_set_dist_and_angle(focus, pos, dist, pitch, yaw);
   1881     if (gCurrLevelArea == AREA_WDW_MAIN) {
   1882         yaw = clamp_positions_and_find_yaw(pos, focus, 4508.f, -3739.f, 4508.f, -3739.f);
   1883     }
   1884     if (gCurrLevelArea == AREA_THI_HUGE) {
   1885         yaw = clamp_positions_and_find_yaw(pos, focus, 8192.f, -8192.f, 8192.f, -8192.f);
   1886     }
   1887     if (gCurrLevelArea == AREA_THI_TINY) {
   1888         yaw = clamp_positions_and_find_yaw(pos, focus, 2458.f, -2458.f, 2458.f, -2458.f);
   1889     }
   1890 
   1891     return yaw;
   1892 }
   1893 
   1894 /**
   1895  * "Behind Mario" mode: used when Mario is flying, on the water's surface, or shot from a cannon
   1896  */
   1897 s32 mode_behind_mario(struct Camera *c) {
   1898     struct MarioState *marioState = &gMarioStates[0];
   1899     struct Surface *floor;
   1900     Vec3f newPos;
   1901     //! @bug oldPos is unused, see resolve_geometry_collisions
   1902     Vec3f oldPos;
   1903     f32 waterHeight;
   1904     f32 floorHeight;
   1905     f32 distCamToFocus;
   1906     s16 camPitch;
   1907     s16 camYaw;
   1908     s16 yaw;
   1909 
   1910     vec3f_copy(oldPos, c->pos);
   1911     gCameraMovementFlags &= ~CAM_MOVING_INTO_MODE;
   1912     vec3f_copy(newPos, c->pos);
   1913     yaw = update_behind_mario_camera(c, c->focus, newPos);
   1914     c->pos[0] = newPos[0];
   1915     c->pos[2] = newPos[2];
   1916 
   1917     // Keep the camera above the water surface if swimming
   1918     if (c->mode == CAMERA_MODE_WATER_SURFACE) {
   1919         floorHeight = find_floor(c->pos[0], c->pos[1], c->pos[2], &floor);
   1920         newPos[1] = marioState->waterLevel + 120;
   1921         if (newPos[1] < (floorHeight += 120.f)) {
   1922             newPos[1] = floorHeight;
   1923         }
   1924     }
   1925     approach_camera_height(c, newPos[1], 50.f);
   1926     waterHeight = find_water_level(c->pos[0], c->pos[2]) + 100.f;
   1927     if (c->pos[1] <= waterHeight) {
   1928         gCameraMovementFlags |= CAM_MOVE_SUBMERGED;
   1929     } else {
   1930         gCameraMovementFlags &= ~CAM_MOVE_SUBMERGED;
   1931     }
   1932 
   1933     resolve_geometry_collisions(c->pos, oldPos);
   1934     // Prevent camera getting too far away
   1935     vec3f_get_dist_and_angle(c->focus, c->pos, &distCamToFocus, &camPitch, &camYaw);
   1936     if (distCamToFocus > 800.f) {
   1937         distCamToFocus = 800.f;
   1938         vec3f_set_dist_and_angle(c->focus, c->pos, distCamToFocus, camPitch, camYaw);
   1939     }
   1940     pan_ahead_of_player(c);
   1941 
   1942     return yaw;
   1943 }
   1944 
   1945 /**
   1946  * Update the camera in slide and hoot mode.
   1947  *
   1948  * In slide mode, keep the camera 800 units from Mario
   1949  */
   1950 s16 update_slide_camera(struct Camera *c) {
   1951     struct Surface *floor;
   1952     f32 floorHeight;
   1953     Vec3f pos;
   1954     f32 distCamToFocus;
   1955     f32 maxCamDist;
   1956     f32 pitchScale;
   1957     s16 camPitch;
   1958     s16 camYaw;
   1959     UNUSED struct MarioState *marioState = &gMarioStates[0];
   1960     s16 goalPitch = 0x1555;
   1961     s16 goalYaw = sMarioCamState->faceAngle[1] + DEGREES(180);
   1962 
   1963     // Zoom in when inside the CCM shortcut
   1964     if (sStatusFlags & CAM_FLAG_CCM_SLIDE_SHORTCUT) {
   1965         sLakituDist = approach_f32(sLakituDist, -600.f, 20.f, 20.f);
   1966     } else {
   1967         sLakituDist = approach_f32(sLakituDist, 0.f, 20.f, 20.f);
   1968     }
   1969 
   1970     // No C-Button input in this mode, notify the player with a buzzer
   1971     play_camera_buzz_if_cbutton();
   1972 
   1973     // Focus on Mario
   1974     vec3f_copy(c->focus, sMarioCamState->pos);
   1975     c->focus[1] += 50.f;
   1976 
   1977     vec3f_get_dist_and_angle(c->focus, c->pos, &distCamToFocus, &camPitch, &camYaw);
   1978     maxCamDist = 800.f;
   1979 
   1980     // In hoot mode, zoom further out and rotate faster
   1981     if (sMarioCamState->action == ACT_RIDING_HOOT) {
   1982         maxCamDist = 1000.f;
   1983         goalPitch = 0x2800;
   1984         camera_approach_s16_symmetric_bool(&camYaw, goalYaw, 0x100);
   1985     } else {
   1986         camera_approach_s16_symmetric_bool(&camYaw, goalYaw, 0x80);
   1987     }
   1988     camera_approach_s16_symmetric_bool(&camPitch, goalPitch, 0x100);
   1989 
   1990     // Hoot mode
   1991     if (sMarioCamState->action != ACT_RIDING_HOOT && sMarioGeometry.currFloorType == SURFACE_DEATH_PLANE) {
   1992         vec3f_set_dist_and_angle(c->focus, pos, maxCamDist + sLakituDist, camPitch, camYaw);
   1993         c->pos[0] = pos[0];
   1994         c->pos[2] = pos[2];
   1995         camera_approach_f32_symmetric_bool(&c->pos[1], c->focus[1], 30.f);
   1996         vec3f_get_dist_and_angle(c->pos, c->focus, &distCamToFocus, &camPitch, &camYaw);
   1997         pitchScale = (distCamToFocus - maxCamDist + sLakituDist) / 10000.f;
   1998         if (pitchScale > 1.f) {
   1999             pitchScale = 1.f;
   2000         }
   2001         camPitch += 0x1000 * pitchScale;
   2002         vec3f_set_dist_and_angle(c->pos, c->focus, distCamToFocus, camPitch, camYaw);
   2003 
   2004     // Slide mode
   2005     } else {
   2006         vec3f_set_dist_and_angle(c->focus, c->pos, maxCamDist + sLakituDist, camPitch, camYaw);
   2007         sStatusFlags |= CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
   2008 
   2009         // Stay above the slide floor
   2010         floorHeight = find_floor(c->pos[0], c->pos[1] + 200.f, c->pos[2], &floor) + 125.f;
   2011         if (c->pos[1] < floorHeight) {
   2012             c->pos[1] = floorHeight;
   2013         }
   2014         // Stay closer than maxCamDist
   2015         vec3f_get_dist_and_angle(c->focus, c->pos, &distCamToFocus, &camPitch, &camYaw);
   2016         if (distCamToFocus > maxCamDist + sLakituDist) {
   2017             distCamToFocus = maxCamDist + sLakituDist;
   2018             vec3f_set_dist_and_angle(c->focus, c->pos, distCamToFocus, camPitch, camYaw);
   2019         }
   2020     }
   2021 
   2022     camYaw = calculate_yaw(c->focus, c->pos);
   2023     return camYaw;
   2024 }
   2025 
   2026 void mode_behind_mario_camera(struct Camera *c) {
   2027     c->nextYaw = mode_behind_mario(c);
   2028 }
   2029 
   2030 s32 nop_update_water_camera(UNUSED struct Camera *c, UNUSED Vec3f focus, UNUSED Vec3f pos) {
   2031 #ifdef AVOID_UB
   2032    return 0;
   2033 #endif
   2034 }
   2035 
   2036 /**
   2037  * Exactly the same as BEHIND_MARIO
   2038  */
   2039 void mode_water_surface_camera(struct Camera *c) {
   2040     c->nextYaw = mode_behind_mario(c);
   2041 }
   2042 
   2043 /**
   2044  * Used in sModeTransitions for CLOSE and FREE_ROAM mode
   2045  */
   2046 s32 update_mario_camera(UNUSED struct Camera *c, Vec3f focus, Vec3f pos) {
   2047     s16 yaw = sMarioCamState->faceAngle[1] + sModeOffsetYaw + DEGREES(180);
   2048     focus_on_mario(focus, pos, 125.f, 125.f, gCameraZoomDist, 0x05B0, yaw);
   2049 
   2050     return sMarioCamState->faceAngle[1];
   2051 }
   2052 
   2053 /**
   2054  * Update the camera in default, close, and free roam mode
   2055  *
   2056  * The camera moves behind Mario, and can rotate all the way around
   2057  */
   2058 s16 update_default_camera(struct Camera *c) {
   2059     Vec3f tempPos;
   2060     Vec3f cPos;
   2061     UNUSED u8 filler1[12];
   2062     struct Surface *marioFloor;
   2063     struct Surface *cFloor;
   2064     struct Surface *tempFloor;
   2065     struct Surface *ceil;
   2066     f32 camFloorHeight;
   2067     f32 tempFloorHeight;
   2068     f32 marioFloorHeight;
   2069     UNUSED u8 filler2[4];
   2070     f32 dist;
   2071     f32 zoomDist;
   2072     f32 waterHeight;
   2073     f32 gasHeight;
   2074     s16 avoidYaw;
   2075     s16 pitch;
   2076     s16 yaw;
   2077     s16 yawGoal = sMarioCamState->faceAngle[1] + DEGREES(180);
   2078     f32 posHeight;
   2079     f32 focHeight;
   2080     f32 distFromWater;
   2081     s16 tempPitch;
   2082     s16 tempYaw;
   2083     f32 xzDist;
   2084     UNUSED u8 filler3[4];
   2085     s16 nextYawVel;
   2086     s16 yawVel = 0;
   2087     f32 scale;
   2088     s32 avoidStatus = 0;
   2089     s32 closeToMario = 0;
   2090     f32 ceilHeight = find_ceil(gLakituState.goalPos[0],
   2091                                gLakituState.goalPos[1],
   2092                                gLakituState.goalPos[2], &ceil);
   2093     s16 yawDir;
   2094 
   2095     handle_c_button_movement(c);
   2096     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   2097 
   2098     // If C-Down is active, determine what distance the camera should be from Mario
   2099     if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   2100         //! In Mario mode, the camera is zoomed out further than in Lakitu mode (1400 vs 1200)
   2101         if (set_cam_angle(0) == CAM_ANGLE_MARIO) {
   2102             zoomDist = gCameraZoomDist + 1050;
   2103         } else {
   2104             zoomDist = gCameraZoomDist + 400;
   2105         }
   2106     } else {
   2107         zoomDist = gCameraZoomDist;
   2108     }
   2109 
   2110     if (sMarioCamState->action & ACT_FLAG_HANGING ||
   2111         sMarioCamState->action == ACT_RIDING_HOOT) {
   2112         zoomDist *= 0.8f;
   2113         set_handheld_shake(HAND_CAM_SHAKE_HANG_OWL);
   2114     }
   2115 
   2116     // If not zooming out, only allow dist to decrease
   2117     if (sZoomAmount == 0.f) {
   2118         if (dist > zoomDist) {
   2119             if ((dist -= 50.f) < zoomDist) {
   2120                 dist = zoomDist;
   2121             }
   2122         }
   2123     } else {
   2124         if ((sZoomAmount -= 30.f) < 0.f) {
   2125             sZoomAmount = 0.f;
   2126         }
   2127         if (dist > zoomDist) {
   2128             if ((dist -= 30.f) < zoomDist) {
   2129                 dist = zoomDist;
   2130             }
   2131         }
   2132         if (dist < zoomDist) {
   2133             if ((dist += 30.f) > zoomDist) {
   2134                 dist = zoomDist;
   2135             }
   2136         }
   2137     }
   2138 
   2139     // Determine how fast to rotate the camera
   2140     if (sCSideButtonYaw == 0) {
   2141         if (c->mode == CAMERA_MODE_FREE_ROAM) {
   2142             nextYawVel = 0xC0;
   2143         } else {
   2144             nextYawVel = 0x100;
   2145         }
   2146         if ((gPlayer1Controller->stickX != 0.f || gPlayer1Controller->stickY != 0.f) != 0) {
   2147             nextYawVel = 0x20;
   2148         }
   2149     } else {
   2150         if (sCSideButtonYaw < 0) {
   2151             yaw += 0x200;
   2152         }
   2153         if (sCSideButtonYaw > 0) {
   2154             yaw -= 0x200;
   2155         }
   2156         camera_approach_s16_symmetric_bool(&sCSideButtonYaw, 0, 0x100);
   2157         nextYawVel = 0;
   2158     }
   2159     sYawSpeed = 0x400;
   2160     xzDist = calc_hor_dist(sMarioCamState->pos, c->pos);
   2161 
   2162     if (sStatusFlags & CAM_FLAG_BEHIND_MARIO_POST_DOOR) {
   2163         if (xzDist >= 250) {
   2164             sStatusFlags &= ~CAM_FLAG_BEHIND_MARIO_POST_DOOR;
   2165         }
   2166         if (ABS((sMarioCamState->faceAngle[1] - yaw) / 2) < 0x1800) {
   2167             sStatusFlags &= ~CAM_FLAG_BEHIND_MARIO_POST_DOOR;
   2168             yaw = sCameraYawAfterDoorCutscene + DEGREES(180);
   2169             dist = 800.f;
   2170             sStatusFlags |= CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
   2171         }
   2172     } else if (xzDist < 250) {
   2173         // Turn rapidly if very close to Mario
   2174         c->pos[0] += (250 - xzDist) * sins(yaw);
   2175         c->pos[2] += (250 - xzDist) * coss(yaw);
   2176         if (sCSideButtonYaw == 0) {
   2177             nextYawVel = 0x1000;
   2178             sYawSpeed = 0;
   2179             vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   2180         }
   2181         closeToMario |= 1;
   2182     }
   2183 
   2184     if (-16 < gPlayer1Controller->stickY) {
   2185         c->yaw = yaw;
   2186     }
   2187 
   2188     calc_y_to_curr_floor(&posHeight, 1, 200, &focHeight, 0.9f, 200);
   2189     vec3f_copy(cPos, c->pos);
   2190     avoidStatus = rotate_camera_around_walls(c, cPos, &avoidYaw, 0x600);
   2191     // If a wall is blocking the view of Mario, then rotate in the calculated direction
   2192     if (avoidStatus == 3) {
   2193         unusedFreeRoamWallYaw = avoidYaw;
   2194         sAvoidYawVel = yaw;
   2195         sStatusFlags |= CAM_FLAG_COLLIDED_WITH_WALL;
   2196         //! Does nothing
   2197         vec3f_get_dist_and_angle(sMarioCamState->pos, cPos, &xzDist, &tempPitch, &tempYaw);
   2198         // Rotate to avoid the wall
   2199         approach_s16_asymptotic_bool(&yaw, avoidYaw, 10);
   2200         //! Does nothing
   2201         vec3f_set_dist_and_angle(sMarioCamState->pos, cPos, xzDist, tempPitch, tempYaw);
   2202         sAvoidYawVel = (sAvoidYawVel - yaw) / 0x100;
   2203     } else {
   2204         if (gMarioStates[0].forwardVel == 0.f) {
   2205             if (sStatusFlags & CAM_FLAG_COLLIDED_WITH_WALL) {
   2206                 if ((yawGoal - yaw) / 0x100 >= 0) {
   2207                     yawDir = -1;
   2208                 } else {
   2209                     yawDir = 1;
   2210                 }
   2211                 if ((sAvoidYawVel > 0 && yawDir > 0) || (sAvoidYawVel < 0 && yawDir < 0)) {
   2212                     yawVel = nextYawVel;
   2213                 }
   2214             } else {
   2215                 yawVel = nextYawVel;
   2216             }
   2217         } else {
   2218             if (nextYawVel == 0x1000) {
   2219                 yawVel = nextYawVel;
   2220             }
   2221             sStatusFlags &= ~CAM_FLAG_COLLIDED_WITH_WALL;
   2222         }
   2223 
   2224         // If a wall is near the camera, turn twice as fast
   2225         if (avoidStatus != 0) {
   2226             yawVel += yawVel;
   2227         }
   2228         // ...Unless the camera already rotated from being close to Mario
   2229         if ((closeToMario & 1) && avoidStatus != 0) {
   2230             yawVel = 0;
   2231         }
   2232         if (yawVel != 0 && get_dialog_id() == DIALOG_NONE) {
   2233             camera_approach_s16_symmetric_bool(&yaw, yawGoal, yawVel);
   2234         }
   2235     }
   2236 
   2237     // Only zoom out if not obstructed by walls and Lakitu hasn't collided with any
   2238     if (avoidStatus == 0 && !(sStatusFlags & CAM_FLAG_COLLIDED_WITH_WALL)) {
   2239         approach_f32_asymptotic_bool(&dist, zoomDist - 100.f, 0.05f);
   2240     }
   2241     vec3f_set_dist_and_angle(sMarioCamState->pos, cPos, dist, pitch, yaw);
   2242     cPos[1] += posHeight + 125.f;
   2243 
   2244     // Move the camera away from walls and set the collision flag
   2245     if (collide_with_walls(cPos, 10.f, 80.f) != 0) {
   2246         sStatusFlags |= CAM_FLAG_COLLIDED_WITH_WALL;
   2247     }
   2248 
   2249     c->focus[0] = sMarioCamState->pos[0];
   2250     c->focus[1] = sMarioCamState->pos[1] + 125.f + focHeight;
   2251     c->focus[2] = sMarioCamState->pos[2];
   2252 
   2253     marioFloorHeight = 125.f + sMarioGeometry.currFloorHeight;
   2254     marioFloor = sMarioGeometry.currFloor;
   2255     camFloorHeight = find_floor(cPos[0], cPos[1] + 50.f, cPos[2], &cFloor) + 125.f;
   2256     for (scale = 0.1f; scale < 1.f; scale += 0.2f) {
   2257         scale_along_line(tempPos, cPos, sMarioCamState->pos, scale);
   2258         tempFloorHeight = find_floor(tempPos[0], tempPos[1], tempPos[2], &tempFloor) + 125.f;
   2259         if (tempFloor != NULL && tempFloorHeight > marioFloorHeight) {
   2260             marioFloorHeight = tempFloorHeight;
   2261             marioFloor = tempFloor;
   2262         }
   2263     }
   2264 
   2265     // Lower the camera in Mario mode
   2266     if (sSelectionFlags & CAM_MODE_MARIO_ACTIVE) {
   2267         marioFloorHeight -= 35.f;
   2268         camFloorHeight -= 35.f;
   2269         c->focus[1] -= 25.f;
   2270     }
   2271 
   2272     // If there's water below the camera, decide whether to keep the camera above the water surface
   2273     waterHeight = find_water_level(cPos[0], cPos[2]);
   2274     if (waterHeight != FLOOR_LOWER_LIMIT) {
   2275         waterHeight += 125.f;
   2276         distFromWater = waterHeight - marioFloorHeight;
   2277         if (!(gCameraMovementFlags & CAM_MOVE_METAL_BELOW_WATER)) {
   2278             if (distFromWater > 800.f && (sMarioCamState->action & ACT_FLAG_METAL_WATER)) {
   2279                 gCameraMovementFlags |= CAM_MOVE_METAL_BELOW_WATER;
   2280             }
   2281         } else {
   2282             if (distFromWater < 400.f || !(sMarioCamState->action & ACT_FLAG_METAL_WATER)) {
   2283                 gCameraMovementFlags &= ~CAM_MOVE_METAL_BELOW_WATER;
   2284             }
   2285         }
   2286         // If not wearing the metal cap, always stay above
   2287         if (!(gCameraMovementFlags & CAM_MOVE_METAL_BELOW_WATER) && camFloorHeight < waterHeight) {
   2288             camFloorHeight = waterHeight;
   2289         }
   2290     } else {
   2291         gCameraMovementFlags &= ~CAM_MOVE_METAL_BELOW_WATER;
   2292     }
   2293 
   2294     cPos[1] = camFloorHeight;
   2295     vec3f_copy(tempPos, cPos);
   2296     tempPos[1] -= 125.f;
   2297     if (marioFloor != NULL && camFloorHeight <= marioFloorHeight) {
   2298         avoidStatus = is_range_behind_surface(c->focus, tempPos, marioFloor, 0, -1);
   2299         if (avoidStatus != 1 && ceilHeight > marioFloorHeight) {
   2300             camFloorHeight = marioFloorHeight;
   2301         }
   2302     }
   2303 
   2304     posHeight = 0.f;
   2305     if (c->mode == CAMERA_MODE_FREE_ROAM) {
   2306         if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   2307             posHeight = 375.f;
   2308             if (gCurrLevelArea == AREA_SSL_PYRAMID) {
   2309                 posHeight /= 2;
   2310             }
   2311         } else {
   2312             posHeight = 100.f;
   2313         }
   2314     }
   2315     if ((gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) && (sSelectionFlags & CAM_MODE_MARIO_ACTIVE)) {
   2316         posHeight = 610.f;
   2317         if (gCurrLevelArea == AREA_SSL_PYRAMID || gCurrLevelNum == LEVEL_CASTLE) {
   2318             posHeight /= 2;
   2319         }
   2320     }
   2321 
   2322     // Make Lakitu fly above the gas
   2323     gasHeight = find_poison_gas_level(cPos[0], cPos[2]);
   2324     if (gasHeight != FLOOR_LOWER_LIMIT) {
   2325         if ((gasHeight += 130.f) > c->pos[1]) {
   2326             c->pos[1] = gasHeight;
   2327         }
   2328     }
   2329 
   2330     if (sMarioCamState->action & ACT_FLAG_HANGING || sMarioCamState->action == ACT_RIDING_HOOT) {
   2331         camFloorHeight = sMarioCamState->pos[1] + 400.f;
   2332         if (c->mode == CAMERA_MODE_FREE_ROAM) {
   2333             camFloorHeight -= 100.f;
   2334         }
   2335         ceilHeight = CELL_HEIGHT_LIMIT;
   2336         vec3f_copy(c->focus, sMarioCamState->pos);
   2337     }
   2338 
   2339     if (sMarioCamState->action & ACT_FLAG_ON_POLE) {
   2340         camFloorHeight = gMarioStates[0].usedObj->oPosY + 125.f;
   2341         if (sMarioCamState->pos[1] - 100.f > camFloorHeight) {
   2342             camFloorHeight = sMarioCamState->pos[1] - 100.f;
   2343         }
   2344         ceilHeight = CELL_HEIGHT_LIMIT;
   2345         vec3f_copy(c->focus, sMarioCamState->pos);
   2346     }
   2347     if (camFloorHeight != FLOOR_LOWER_LIMIT) {
   2348         camFloorHeight += posHeight;
   2349         approach_camera_height(c, camFloorHeight, 20.f);
   2350     }
   2351     c->pos[0] = cPos[0];
   2352     c->pos[2] = cPos[2];
   2353     cPos[0] = gLakituState.goalPos[0];
   2354     cPos[1] = c->pos[1];
   2355     cPos[2] = gLakituState.goalPos[2];
   2356     vec3f_get_dist_and_angle(cPos, c->pos, &dist, &tempPitch, &tempYaw);
   2357     // Prevent the camera from lagging behind too much
   2358     if (dist > 50.f) {
   2359         dist = 50.f;
   2360         vec3f_set_dist_and_angle(cPos, c->pos, dist, tempPitch, tempYaw);
   2361     }
   2362     if (sMarioGeometry.currFloorType != SURFACE_DEATH_PLANE) {
   2363         vec3f_get_dist_and_angle(c->focus, c->pos, &dist, &tempPitch, &tempYaw);
   2364         if (dist > zoomDist) {
   2365             dist = zoomDist;
   2366             vec3f_set_dist_and_angle(c->focus, c->pos, dist, tempPitch, tempYaw);
   2367         }
   2368     }
   2369     if (ceilHeight != CELL_HEIGHT_LIMIT) {
   2370         if (c->pos[1] > (ceilHeight -= 150.f)
   2371             && (avoidStatus = is_range_behind_surface(c->pos, sMarioCamState->pos, ceil, 0, -1)) == 1) {
   2372             c->pos[1] = ceilHeight;
   2373         }
   2374     }
   2375     if (gCurrLevelArea == AREA_WDW_TOWN) {
   2376         yaw = clamp_positions_and_find_yaw(c->pos, c->focus, 2254.f, -3789.f, 3790.f, -2253.f);
   2377     }
   2378     return yaw;
   2379 }
   2380 
   2381 /**
   2382  * The default camera mode
   2383  * Used by close and free roam modes
   2384  */
   2385 void mode_default_camera(struct Camera *c) {
   2386     set_fov_function(CAM_FOV_DEFAULT);
   2387     c->nextYaw = update_default_camera(c);
   2388     pan_ahead_of_player(c);
   2389 }
   2390 
   2391 /**
   2392  * The mode used by close and free roam
   2393  */
   2394 void mode_lakitu_camera(struct Camera *c) {
   2395     gCameraZoomDist = 800.f;
   2396     mode_default_camera(c);
   2397 }
   2398 
   2399 /**
   2400  * When no other mode is active and the current R button mode is Mario
   2401  */
   2402 void mode_mario_camera(struct Camera *c) {
   2403     gCameraZoomDist = 350.f;
   2404     mode_default_camera(c);
   2405 }
   2406 
   2407 /**
   2408  * Rotates the camera around the spiral staircase.
   2409  */
   2410 s32 update_spiral_stairs_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
   2411     UNUSED s16 unused;
   2412     /// The returned yaw
   2413     s16 camYaw;
   2414     // unused
   2415     s16 focPitch;
   2416     /// The focus (Mario)'s yaw around the stairs
   2417     s16 focYaw;
   2418     // unused
   2419     s16 posPitch;
   2420     /// The camera's yaw around the stairs
   2421     s16 posYaw;
   2422     UNUSED u8 filler[4];
   2423     Vec3f cPos;
   2424     Vec3f checkPos;
   2425     struct Surface *floor;
   2426     // unused
   2427     f32 dist;
   2428     f32 focusHeight;
   2429     f32 floorHeight;
   2430     f32 focY;
   2431 
   2432     handle_c_button_movement(c);
   2433     // Set base pos to the center of the staircase
   2434     vec3f_set(sFixedModeBasePosition, -1280.f, 614.f, 1740.f);
   2435 
   2436     // Focus on Mario, and move the focus up the staircase with him
   2437     calc_y_to_curr_floor(&focusHeight, 1.f, 200.f, &focusHeight, 0.9f, 200.f);
   2438     focus[0] = sMarioCamState->pos[0];
   2439     focY = sMarioCamState->pos[1] + 125.f + focusHeight;
   2440     focus[2] = sMarioCamState->pos[2];
   2441 
   2442     vec3f_copy(cPos, pos);
   2443     vec3f_get_dist_and_angle(sFixedModeBasePosition, focus, &dist, &focPitch, &focYaw);
   2444     vec3f_get_dist_and_angle(sFixedModeBasePosition, cPos, &dist, &posPitch, &posYaw);
   2445 
   2446     sSpiralStairsYawOffset = posYaw - focYaw;
   2447     // posYaw will change if Mario is more than 90 degrees around the stairs, relative to the camera
   2448     if (sSpiralStairsYawOffset < DEGREES(-90)) {
   2449         sSpiralStairsYawOffset = DEGREES(-90);
   2450     }
   2451     if (sSpiralStairsYawOffset > DEGREES(90)) {
   2452         sSpiralStairsYawOffset = DEGREES(90);
   2453     }
   2454     focYaw += sSpiralStairsYawOffset;
   2455     posYaw = focYaw;
   2456     //! @bug unnecessary
   2457     camera_approach_s16_symmetric_bool(&posYaw, focYaw, 0x1000);
   2458 
   2459     vec3f_set_dist_and_angle(sFixedModeBasePosition, cPos, 300.f, 0, posYaw);
   2460 
   2461     // Move the camera's y coord up/down the staircase
   2462     checkPos[0] = focus[0] + (cPos[0] - focus[0]) * 0.7f;
   2463     checkPos[1] = focus[1] + (cPos[1] - focus[1]) * 0.7f + 300.f;
   2464     checkPos[2] = focus[2] + (cPos[2] - focus[2]) * 0.7f;
   2465     floorHeight = find_floor(checkPos[0], checkPos[1] + 50.f, checkPos[2], &floor);
   2466 
   2467     if (floorHeight != FLOOR_LOWER_LIMIT) {
   2468         if (floorHeight < sMarioGeometry.currFloorHeight) {
   2469             floorHeight = sMarioGeometry.currFloorHeight;
   2470         }
   2471         pos[1] = approach_f32(pos[1], (floorHeight += 125.f), 30.f, 30.f);
   2472     }
   2473 
   2474     camera_approach_f32_symmetric_bool(&focus[1], focY, 30.f);
   2475     pos[0] = cPos[0];
   2476     pos[2] = cPos[2];
   2477     camYaw = calculate_yaw(focus, pos);
   2478 
   2479     return camYaw;
   2480 }
   2481 
   2482 /**
   2483  * The mode used in the spiral staircase in the castle
   2484  */
   2485 void mode_spiral_stairs_camera(struct Camera *c) {
   2486     c->nextYaw = update_spiral_stairs_camera(c, c->focus, c->pos);
   2487 }
   2488 
   2489 s32 update_slide_or_0f_camera(UNUSED struct Camera *c, Vec3f focus, Vec3f pos) {
   2490     s16 yaw = sMarioCamState->faceAngle[1] + sModeOffsetYaw + DEGREES(180);
   2491 
   2492     focus_on_mario(focus, pos, 125.f, 125.f, 800.f, 5461, yaw);
   2493     return sMarioCamState->faceAngle[1];
   2494 }
   2495 
   2496 static UNUSED void unused_mode_0f_camera(struct Camera *c) {
   2497     if (gPlayer1Controller->buttonPressed & U_CBUTTONS) {
   2498         gCameraMovementFlags |= CAM_MOVE_C_UP_MODE;
   2499     }
   2500     c->nextYaw = update_slide_camera(c);
   2501 }
   2502 
   2503 /**
   2504  * Slide/hoot mode.
   2505  * In this mode, the camera is always at the back of Mario, because Mario generally only moves forward.
   2506  */
   2507 void mode_slide_camera(struct Camera *c) {
   2508     if (sMarioGeometry.currFloorType == SURFACE_CLOSE_CAMERA ||
   2509         sMarioGeometry.currFloorType == SURFACE_NO_CAM_COL_SLIPPERY) {
   2510         mode_lakitu_camera(c);
   2511     } else {
   2512         if (gPlayer1Controller->buttonPressed & U_CBUTTONS) {
   2513             gCameraMovementFlags |= CAM_MOVE_C_UP_MODE;
   2514         }
   2515         c->nextYaw = update_slide_camera(c);
   2516     }
   2517 }
   2518 
   2519 void store_lakitu_cam_info_for_c_up(struct Camera *c) {
   2520     vec3f_copy(sCameraStoreCUp.pos, c->pos);
   2521     vec3f_sub(sCameraStoreCUp.pos, sMarioCamState->pos);
   2522     // Only store the y value, and as an offset from Mario, for some reason
   2523     vec3f_set(sCameraStoreCUp.focus, 0.f, c->focus[1] - sMarioCamState->pos[1], 0.f);
   2524 }
   2525 
   2526 /**
   2527  * Start C-Up mode. The actual mode change is handled in update_mario_inputs() in mario.c
   2528  *
   2529  * @see update_mario_inputs
   2530  */
   2531 s32 set_mode_c_up(struct Camera *c) {
   2532     if (!(gCameraMovementFlags & CAM_MOVE_C_UP_MODE)) {
   2533         gCameraMovementFlags |= CAM_MOVE_C_UP_MODE;
   2534         store_lakitu_cam_info_for_c_up(c);
   2535         sCameraSoundFlags &= ~CAM_SOUND_C_UP_PLAYED;
   2536         return 1;
   2537     }
   2538     return 0;
   2539 }
   2540 
   2541 /**
   2542  * Zoom the camera out of C-Up mode, avoiding moving into a wall, if possible, by searching for an open
   2543  * direction.
   2544  */
   2545 s32 exit_c_up(struct Camera *c) {
   2546     struct Surface *surface;
   2547     Vec3f checkFoc;
   2548     Vec3f curPos;
   2549     // Variables for searching for an open direction
   2550     s32 searching = 0;
   2551     /// The current sector of the circle that we are checking
   2552     s32 sector;
   2553     f32 ceilHeight;
   2554     f32 floorHeight;
   2555     f32 curDist;
   2556     f32 d;
   2557     s16 curPitch;
   2558     s16 curYaw;
   2559     s16 checkYaw = 0;
   2560     Vec3f storePos; // unused
   2561     Vec3f storeFoc; // unused
   2562 
   2563     if ((gCameraMovementFlags & CAM_MOVE_C_UP_MODE) && !(gCameraMovementFlags & CAM_MOVE_STARTED_EXITING_C_UP)) {
   2564         // Copy the stored pos and focus. This is unused.
   2565         vec3f_copy(storePos, sCameraStoreCUp.pos);
   2566         vec3f_add(storePos, sMarioCamState->pos);
   2567         vec3f_copy(storeFoc, sCameraStoreCUp.focus);
   2568         vec3f_add(storeFoc, sMarioCamState->pos);
   2569 
   2570         vec3f_copy(checkFoc, c->focus);
   2571         checkFoc[0] = sMarioCamState->pos[0];
   2572         checkFoc[2] = sMarioCamState->pos[2];
   2573         vec3f_get_dist_and_angle(checkFoc, c->pos, &curDist, &curPitch, &curYaw);
   2574         vec3f_copy(curPos, c->pos);
   2575         curDist = 80.f;
   2576 
   2577         // Search for an open direction to zoom out in, if the camera is changing to close, free roam,
   2578         // or spiral-stairs mode
   2579         if (sModeInfo.lastMode == CAMERA_MODE_SPIRAL_STAIRS || sModeInfo.lastMode == CAMERA_MODE_CLOSE
   2580             || sModeInfo.lastMode == CAMERA_MODE_FREE_ROAM) {
   2581             searching = 1;
   2582             // Check the whole circle around Mario for an open direction to zoom out to
   2583             for (sector = 0; sector < 16 && searching == 1; sector++) {
   2584                 vec3f_set_dist_and_angle(checkFoc, curPos, curDist, 0, curYaw + checkYaw);
   2585 
   2586                 // If there are no walls this way,
   2587                 if (f32_find_wall_collision(&curPos[0], &curPos[1], &curPos[2], 20.f, 50.f) == 0) {
   2588 
   2589                     // Start close to Mario, check for walls, floors, and ceilings all the way to the
   2590                     // zoomed out distance
   2591                     for (d = curDist; d < gCameraZoomDist; d += 20.f) {
   2592                         vec3f_set_dist_and_angle(checkFoc, curPos, d, 0, curYaw + checkYaw);
   2593 
   2594                         // Check if we're zooming out into a floor or ceiling
   2595                         ceilHeight = find_ceil(curPos[0], curPos[1] - 150.f, curPos[2], &surface) + -10.f;
   2596                         if (surface != NULL && ceilHeight < curPos[1]) {
   2597                             break;
   2598                         }
   2599                         floorHeight = find_floor(curPos[0], curPos[1] + 150.f, curPos[2], &surface) + 10.f;
   2600                         if (surface != NULL && floorHeight > curPos[1]) {
   2601                             break;
   2602                         }
   2603 
   2604                         // Stop checking this direction if there is a wall blocking the way
   2605                         if (f32_find_wall_collision(&curPos[0], &curPos[1], &curPos[2], 20.f, 50.f) == 1) {
   2606                             break;
   2607                         }
   2608                     }
   2609 
   2610                     // If there was no collision found all the way to the max distance, it's an opening
   2611                     if (d >= gCameraZoomDist) {
   2612                         searching = 0;
   2613                     }
   2614                 }
   2615 
   2616                 // Alternate left and right, checking each 1/16th (22.5 degrees) of the circle
   2617                 if (searching == 1) {
   2618                     checkYaw = -checkYaw;
   2619                     if (checkYaw < 0) {
   2620                         checkYaw -= 0x1000;
   2621                     } else {
   2622                         checkYaw += 0x1000;
   2623                     }
   2624                 }
   2625             }
   2626 
   2627             // Update the stored focus and pos to the direction found in the search
   2628             if (searching == 0) {
   2629                 vec3f_set_dist_and_angle(checkFoc, sCameraStoreCUp.pos, gCameraZoomDist, 0, curYaw + checkYaw);
   2630                 vec3f_copy(sCameraStoreCUp.focus, checkFoc);
   2631                 vec3f_sub(sCameraStoreCUp.pos, sMarioCamState->pos);
   2632                 vec3f_sub(sCameraStoreCUp.focus, sMarioCamState->pos);
   2633             }
   2634 
   2635             gCameraMovementFlags |= CAM_MOVE_STARTED_EXITING_C_UP;
   2636             transition_next_state(c, 15);
   2637         } else {
   2638             // Let the next camera mode handle it
   2639             gCameraMovementFlags &= ~(CAM_MOVE_STARTED_EXITING_C_UP | CAM_MOVE_C_UP_MODE);
   2640             vec3f_set_dist_and_angle(checkFoc, c->pos, curDist, curPitch, curYaw + checkYaw);
   2641         }
   2642         play_sound_cbutton_down();
   2643     }
   2644     return 0;
   2645 }
   2646 
   2647 /**
   2648  * The mode used when C-Up is pressed.
   2649  */
   2650 s32 update_c_up(UNUSED struct Camera *c, Vec3f focus, Vec3f pos) {
   2651     s16 pitch = sCUpCameraPitch;
   2652     s16 yaw = sMarioCamState->faceAngle[1] + sModeOffsetYaw + DEGREES(180);
   2653 
   2654     focus_on_mario(focus, pos, 125.f, 125.f, 250.f, pitch, yaw);
   2655     return sMarioCamState->faceAngle[1];
   2656 }
   2657 
   2658 /**
   2659  * Make Mario's head move in C-Up mode.
   2660  */
   2661 void move_mario_head_c_up(UNUSED struct Camera *c) {
   2662     UNUSED s16 pitch = sCUpCameraPitch;
   2663     UNUSED s16 yaw = sModeOffsetYaw;
   2664 
   2665     sCUpCameraPitch += (s16)(gPlayer1Controller->stickY * 10.f);
   2666     sModeOffsetYaw -= (s16)(gPlayer1Controller->stickX * 10.f);
   2667 
   2668     // Bound looking up to nearly 80 degrees.
   2669     if (sCUpCameraPitch > 0x38E3) {
   2670         sCUpCameraPitch = 0x38E3;
   2671     }
   2672     // Bound looking down to -45 degrees
   2673     if (sCUpCameraPitch < -0x2000) {
   2674         sCUpCameraPitch = -0x2000;
   2675     }
   2676 
   2677     // Bound the camera yaw to +-120 degrees
   2678     if (sModeOffsetYaw > 0x5555) {
   2679         sModeOffsetYaw = 0x5555;
   2680     }
   2681     if (sModeOffsetYaw < -0x5555) {
   2682         sModeOffsetYaw = -0x5555;
   2683     }
   2684 
   2685     // Give Mario's neck natural-looking constraints
   2686     sMarioCamState->headRotation[0] = sCUpCameraPitch * 3 / 4;
   2687     sMarioCamState->headRotation[1] = sModeOffsetYaw * 3 / 4;
   2688 }
   2689 
   2690 /**
   2691  * Zooms the camera in for C-Up mode
   2692  */
   2693 void move_into_c_up(struct Camera *c) {
   2694     struct LinearTransitionPoint *start = &sModeInfo.transitionStart;
   2695     struct LinearTransitionPoint *end = &sModeInfo.transitionEnd;
   2696 
   2697     f32 dist  = end->dist  - start->dist;
   2698     s16 pitch = end->pitch - start->pitch;
   2699     s16 yaw   = end->yaw   - start->yaw;
   2700 
   2701     // Linearly interpolate from start to end position's polar coordinates
   2702     dist  = start->dist  + dist  * sModeInfo.frame / sModeInfo.max;
   2703     pitch = start->pitch + pitch * sModeInfo.frame / sModeInfo.max;
   2704     yaw   = start->yaw   + yaw   * sModeInfo.frame / sModeInfo.max;
   2705 
   2706     // Linearly interpolate the focus from start to end
   2707     c->focus[0] = start->focus[0] + (end->focus[0] - start->focus[0]) * sModeInfo.frame / sModeInfo.max;
   2708     c->focus[1] = start->focus[1] + (end->focus[1] - start->focus[1]) * sModeInfo.frame / sModeInfo.max;
   2709     c->focus[2] = start->focus[2] + (end->focus[2] - start->focus[2]) * sModeInfo.frame / sModeInfo.max;
   2710 
   2711     vec3f_add(c->focus, sMarioCamState->pos);
   2712     vec3f_set_dist_and_angle(c->focus, c->pos, dist, pitch, yaw);
   2713 
   2714     sMarioCamState->headRotation[0] = 0;
   2715     sMarioCamState->headRotation[1] = 0;
   2716 
   2717     // Finished zooming in
   2718     if (++sModeInfo.frame == sModeInfo.max) {
   2719         gCameraMovementFlags &= ~CAM_MOVING_INTO_MODE;
   2720     }
   2721 }
   2722 
   2723 /**
   2724  * The main update function for C-Up mode
   2725  */
   2726 s32 mode_c_up_camera(struct Camera *c) {
   2727     UNUSED u8 filler[12];
   2728 
   2729     // Play a sound when entering C-Up mode
   2730     if (!(sCameraSoundFlags & CAM_SOUND_C_UP_PLAYED)) {
   2731         play_sound_cbutton_up();
   2732         sCameraSoundFlags |= CAM_SOUND_C_UP_PLAYED;
   2733     }
   2734 
   2735     // Zoom in first
   2736     if (gCameraMovementFlags & CAM_MOVING_INTO_MODE) {
   2737         gCameraMovementFlags |= CAM_MOVE_C_UP_MODE;
   2738         move_into_c_up(c);
   2739         return 1;
   2740     }
   2741 
   2742     if (!(gCameraMovementFlags & CAM_MOVE_STARTED_EXITING_C_UP)) {
   2743         // Normal update
   2744         move_mario_head_c_up(c);
   2745         update_c_up(c, c->focus, c->pos);
   2746     } else {
   2747         // Exiting C-Up
   2748         if (sStatusFlags & CAM_FLAG_TRANSITION_OUT_OF_C_UP) {
   2749             // Retrieve the previous position and focus
   2750             vec3f_copy(c->pos, sCameraStoreCUp.pos);
   2751             vec3f_add(c->pos, sMarioCamState->pos);
   2752             vec3f_copy(c->focus, sCameraStoreCUp.focus);
   2753             vec3f_add(c->focus, sMarioCamState->pos);
   2754             // Make Mario look forward
   2755             camera_approach_s16_symmetric_bool(&sMarioCamState->headRotation[0], 0, 1024);
   2756             camera_approach_s16_symmetric_bool(&sMarioCamState->headRotation[1], 0, 1024);
   2757         } else {
   2758             // Finished exiting C-Up
   2759             gCameraMovementFlags &= ~(CAM_MOVE_STARTED_EXITING_C_UP | CAM_MOVE_C_UP_MODE);
   2760         }
   2761     }
   2762     sPanDistance = 0.f;
   2763 
   2764     // Exit C-Up mode
   2765     if (gPlayer1Controller->buttonPressed & (A_BUTTON | B_BUTTON | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS)) {
   2766         exit_c_up(c);
   2767     }
   2768     return 0;
   2769 }
   2770 
   2771 /**
   2772  * Used when Mario is in a cannon.
   2773  */
   2774 s32 update_in_cannon(UNUSED struct Camera *c, Vec3f focus, Vec3f pos) {
   2775     focus_on_mario(pos, focus, 125.f + sCannonYOffset, 125.f, 800.f,
   2776                                     sMarioCamState->faceAngle[0], sMarioCamState->faceAngle[1]);
   2777     return sMarioCamState->faceAngle[1];
   2778 }
   2779 
   2780 /**
   2781  * Updates the camera when Mario is in a cannon.
   2782  * sCannonYOffset is used to make the camera rotate down when Mario has just entered the cannon
   2783  */
   2784 void mode_cannon_camera(struct Camera *c) {
   2785     UNUSED u8 filler[24];
   2786 
   2787     sLakituPitch = 0;
   2788     gCameraMovementFlags &= ~CAM_MOVING_INTO_MODE;
   2789     c->nextYaw = update_in_cannon(c, c->focus, c->pos);
   2790     if (gPlayer1Controller->buttonPressed & A_BUTTON) {
   2791         set_camera_mode(c, CAMERA_MODE_BEHIND_MARIO, 1);
   2792         sPanDistance = 0.f;
   2793         sCannonYOffset = 0.f;
   2794         sStatusFlags &= ~CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
   2795     } else {
   2796         sCannonYOffset = approach_f32(sCannonYOffset, 0.f, 100.f, 100.f);
   2797     }
   2798 }
   2799 
   2800 /**
   2801  * Cause Lakitu to fly to the next Camera position and focus over a number of frames.
   2802  *
   2803  * At the end of each frame, Lakitu's position and focus ("state") are stored.
   2804  * Calling this function makes next_lakitu_state() fly from the last frame's state to the
   2805  * current frame's calculated state.
   2806  *
   2807  * @see next_lakitu_state()
   2808  */
   2809 void transition_next_state(UNUSED struct Camera *c, s16 frames) {
   2810     if (!(sStatusFlags & CAM_FLAG_FRAME_AFTER_CAM_INIT)) {
   2811         sStatusFlags |= (CAM_FLAG_START_TRANSITION | CAM_FLAG_TRANSITION_OUT_OF_C_UP);
   2812         sModeTransition.framesLeft = frames;
   2813     }
   2814 }
   2815 
   2816 /**
   2817  * Sets the camera mode to `newMode` and initializes sModeTransition with `numFrames` frames
   2818  *
   2819  * Used to change the camera mode to 'level-oriented' modes
   2820  *      namely: RADIAL/OUTWARD_RADIAL, 8_DIRECTIONS, FREE_ROAM, CLOSE, SPIRAL_STAIRS, and SLIDE_HOOT
   2821  */
   2822 void transition_to_camera_mode(struct Camera *c, s16 newMode, s16 numFrames) {
   2823     if (c->mode != newMode) {
   2824         sModeInfo.newMode = (newMode != -1) ? newMode : sModeInfo.lastMode;
   2825         sModeInfo.lastMode = c->mode;
   2826         c->mode = sModeInfo.newMode;
   2827 
   2828         // Clear movement flags that would affect the transition
   2829         gCameraMovementFlags &= (u16)~(CAM_MOVE_RESTRICT | CAM_MOVE_ROTATE);
   2830         if (!(sStatusFlags & CAM_FLAG_FRAME_AFTER_CAM_INIT)) {
   2831             transition_next_state(c, numFrames);
   2832             sCUpCameraPitch = 0;
   2833             sModeOffsetYaw = 0;
   2834             sLakituDist = 0;
   2835             sLakituPitch = 0;
   2836             sAreaYawChange = 0;
   2837             sPanDistance = 0.f;
   2838             sCannonYOffset = 0.f;
   2839         }
   2840     }
   2841 }
   2842 
   2843 /**
   2844  * Used to change the camera mode between its default/previous and certain Mario-oriented modes,
   2845  *      namely: C_UP, WATER_SURFACE, CLOSE, and BEHIND_MARIO
   2846  *
   2847  * Stores the current pos and focus in sModeInfo->transitionStart, and
   2848  * stores the next pos and focus into sModeInfo->transitionEnd. These two fields are used in
   2849  * move_into_c_up().
   2850  *
   2851  * @param mode the mode to change to, or -1 to switch to the previous mode
   2852  * @param frames number of frames the transition should last, only used when entering C_UP
   2853  */
   2854 void set_camera_mode(struct Camera *c, s16 mode, s16 frames) {
   2855     struct LinearTransitionPoint *start = &sModeInfo.transitionStart;
   2856     struct LinearTransitionPoint *end = &sModeInfo.transitionEnd;
   2857 
   2858     if (mode == CAMERA_MODE_WATER_SURFACE && gCurrLevelArea == AREA_TTM_OUTSIDE) {
   2859     } else {
   2860         // Clear movement flags that would affect the transition
   2861         gCameraMovementFlags &= (u16)~(CAM_MOVE_RESTRICT | CAM_MOVE_ROTATE);
   2862         gCameraMovementFlags |= CAM_MOVING_INTO_MODE;
   2863         if (mode == CAMERA_MODE_NONE) {
   2864             mode = CAMERA_MODE_CLOSE;
   2865         }
   2866         sCUpCameraPitch = 0;
   2867         sModeOffsetYaw = 0;
   2868         sLakituDist = 0;
   2869         sLakituPitch = 0;
   2870         sAreaYawChange = 0;
   2871 
   2872         sModeInfo.newMode = (mode != -1) ? mode : sModeInfo.lastMode;
   2873         sModeInfo.lastMode = c->mode;
   2874         sModeInfo.max = frames;
   2875         sModeInfo.frame = 1;
   2876 
   2877         c->mode = sModeInfo.newMode;
   2878         gLakituState.mode = c->mode;
   2879 
   2880         vec3f_copy(end->focus, c->focus);
   2881         vec3f_sub(end->focus, sMarioCamState->pos);
   2882 
   2883         vec3f_copy(end->pos, c->pos);
   2884         vec3f_sub(end->pos, sMarioCamState->pos);
   2885 
   2886         sAreaYaw = sModeTransitions[sModeInfo.newMode](c, end->focus, end->pos);
   2887 
   2888         // End was updated by sModeTransitions
   2889         vec3f_sub(end->focus, sMarioCamState->pos);
   2890         vec3f_sub(end->pos, sMarioCamState->pos);
   2891 
   2892         vec3f_copy(start->focus, gLakituState.curFocus);
   2893         vec3f_sub(start->focus, sMarioCamState->pos);
   2894 
   2895         vec3f_copy(start->pos, gLakituState.curPos);
   2896         vec3f_sub(start->pos, sMarioCamState->pos);
   2897 
   2898         vec3f_get_dist_and_angle(start->focus, start->pos, &start->dist, &start->pitch, &start->yaw);
   2899         vec3f_get_dist_and_angle(end->focus, end->pos, &end->dist, &end->pitch, &end->yaw);
   2900     }
   2901 }
   2902 
   2903 /**
   2904  * Updates Lakitu's position/focus and applies camera shakes.
   2905  */
   2906 void update_lakitu(struct Camera *c) {
   2907     struct Surface *floor = NULL;
   2908     Vec3f newPos;
   2909     Vec3f newFoc;
   2910     UNUSED u8 filler1[12];
   2911     f32 distToFloor;
   2912     s16 newYaw;
   2913     UNUSED u8 filler2[8];
   2914 
   2915     if (gCameraMovementFlags & CAM_MOVE_PAUSE_SCREEN) {
   2916     } else {
   2917         if (c->cutscene) {
   2918         }
   2919         if (TRUE) {
   2920             newYaw = next_lakitu_state(newPos, newFoc, c->pos, c->focus, sOldPosition, sOldFocus,
   2921                                        c->nextYaw);
   2922             set_or_approach_s16_symmetric(&c->yaw, newYaw, sYawSpeed);
   2923             sStatusFlags &= ~CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   2924         } else {
   2925             //! dead code, moved to next_lakitu_state()
   2926             vec3f_copy(newPos, c->pos);
   2927             vec3f_copy(newFoc, c->focus);
   2928         }
   2929 
   2930         // Update old state
   2931         vec3f_copy(sOldPosition, newPos);
   2932         vec3f_copy(sOldFocus, newFoc);
   2933 
   2934         gLakituState.yaw = c->yaw;
   2935         gLakituState.nextYaw = c->nextYaw;
   2936         vec3f_copy(gLakituState.goalPos, c->pos);
   2937         vec3f_copy(gLakituState.goalFocus, c->focus);
   2938 
   2939         // Simulate Lakitu flying to the new position and turning towards the new focus
   2940         set_or_approach_vec3f_asymptotic(gLakituState.curPos, newPos,
   2941                                          gLakituState.posHSpeed, gLakituState.posVSpeed,
   2942                                          gLakituState.posHSpeed);
   2943         set_or_approach_vec3f_asymptotic(gLakituState.curFocus, newFoc,
   2944                                          gLakituState.focHSpeed, gLakituState.focVSpeed,
   2945                                          gLakituState.focHSpeed);
   2946         // Adjust Lakitu's speed back to normal
   2947         set_or_approach_f32_asymptotic(&gLakituState.focHSpeed, 0.8f, 0.05f);
   2948         set_or_approach_f32_asymptotic(&gLakituState.focVSpeed, 0.3f, 0.05f);
   2949         set_or_approach_f32_asymptotic(&gLakituState.posHSpeed, 0.3f, 0.05f);
   2950         set_or_approach_f32_asymptotic(&gLakituState.posVSpeed, 0.3f, 0.05f);
   2951 
   2952         // Turn on smooth movement when it hasn't been blocked for 2 frames
   2953         if (sStatusFlags & CAM_FLAG_BLOCK_SMOOTH_MOVEMENT) {
   2954             sStatusFlags &= ~CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
   2955         } else {
   2956             sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   2957         }
   2958 
   2959         vec3f_copy(gLakituState.pos, gLakituState.curPos);
   2960         vec3f_copy(gLakituState.focus, gLakituState.curFocus);
   2961 
   2962         if (c->cutscene) {
   2963             vec3f_add(gLakituState.focus, sPlayer2FocusOffset);
   2964             vec3f_set(sPlayer2FocusOffset, 0, 0, 0);
   2965         }
   2966 
   2967         vec3f_get_dist_and_angle(gLakituState.pos, gLakituState.focus, &gLakituState.focusDistance,
   2968                                  &gLakituState.oldPitch, &gLakituState.oldYaw);
   2969 
   2970         gLakituState.roll = 0;
   2971 
   2972         // Apply camera shakes
   2973         shake_camera_pitch(gLakituState.pos, gLakituState.focus);
   2974         shake_camera_yaw(gLakituState.pos, gLakituState.focus);
   2975         shake_camera_roll(&gLakituState.roll);
   2976         shake_camera_handheld(gLakituState.pos, gLakituState.focus);
   2977 
   2978         if (sMarioCamState->action == ACT_DIVE && gLakituState.lastFrameAction != ACT_DIVE) {
   2979             set_camera_shake_from_hit(SHAKE_HIT_FROM_BELOW);
   2980         }
   2981 
   2982         gLakituState.roll += sHandheldShakeRoll;
   2983         gLakituState.roll += gLakituState.keyDanceRoll;
   2984 
   2985         if (c->mode != CAMERA_MODE_C_UP && c->cutscene == 0) {
   2986             gCheckingSurfaceCollisionsForCamera = TRUE;
   2987             distToFloor = find_floor(gLakituState.pos[0],
   2988                                      gLakituState.pos[1] + 20.0f,
   2989                                      gLakituState.pos[2], &floor);
   2990             if (distToFloor != FLOOR_LOWER_LIMIT) {
   2991                 if (gLakituState.pos[1] < (distToFloor += 100.0f)) {
   2992                     gLakituState.pos[1] = distToFloor;
   2993                 } else {
   2994                     gCheckingSurfaceCollisionsForCamera = FALSE;
   2995                 }
   2996             }
   2997         }
   2998 
   2999         vec3f_copy(sModeTransition.marioPos, sMarioCamState->pos);
   3000     }
   3001     clamp_pitch(gLakituState.pos, gLakituState.focus, 0x3E00, -0x3E00);
   3002     gLakituState.mode = c->mode;
   3003     gLakituState.defMode = c->defMode;
   3004 }
   3005 
   3006 
   3007 /**
   3008  * The main camera update function.
   3009  * Gets controller input, checks for cutscenes, handles mode changes, and moves the camera
   3010  */
   3011 void update_camera(struct Camera *c) {
   3012     UNUSED u8 filler[24];
   3013 
   3014     gCamera = c;
   3015     update_camera_hud_status(c);
   3016     if (c->cutscene == 0) {
   3017         // Only process R_TRIG if 'fixed' is not selected in the menu
   3018         if (cam_select_alt_mode(0) == CAM_SELECTION_MARIO) {
   3019             if (gPlayer1Controller->buttonPressed & R_TRIG) {
   3020                 if (set_cam_angle(0) == CAM_ANGLE_LAKITU) {
   3021                     set_cam_angle(CAM_ANGLE_MARIO);
   3022                 } else {
   3023                     set_cam_angle(CAM_ANGLE_LAKITU);
   3024                 }
   3025             }
   3026         }
   3027         play_sound_if_cam_switched_to_lakitu_or_mario();
   3028     }
   3029 
   3030     // Initialize the camera
   3031     sStatusFlags &= ~CAM_FLAG_FRAME_AFTER_CAM_INIT;
   3032     if (gCameraMovementFlags & CAM_MOVE_INIT_CAMERA) {
   3033         init_camera(c);
   3034         gCameraMovementFlags &= ~CAM_MOVE_INIT_CAMERA;
   3035         sStatusFlags |= CAM_FLAG_FRAME_AFTER_CAM_INIT;
   3036     }
   3037 
   3038     // Store previous geometry information
   3039     sMarioGeometry.prevFloorHeight = sMarioGeometry.currFloorHeight;
   3040     sMarioGeometry.prevCeilHeight = sMarioGeometry.currCeilHeight;
   3041     sMarioGeometry.prevFloor = sMarioGeometry.currFloor;
   3042     sMarioGeometry.prevCeil = sMarioGeometry.currCeil;
   3043     sMarioGeometry.prevFloorType = sMarioGeometry.currFloorType;
   3044     sMarioGeometry.prevCeilType = sMarioGeometry.currCeilType;
   3045 
   3046     find_mario_floor_and_ceil(&sMarioGeometry);
   3047     gCheckingSurfaceCollisionsForCamera = TRUE;
   3048     vec3f_copy(c->pos, gLakituState.goalPos);
   3049     vec3f_copy(c->focus, gLakituState.goalFocus);
   3050 
   3051     c->yaw = gLakituState.yaw;
   3052     c->nextYaw = gLakituState.nextYaw;
   3053     c->mode = gLakituState.mode;
   3054     c->defMode = gLakituState.defMode;
   3055 
   3056     camera_course_processing(c);
   3057     stub_camera_3(c);
   3058     sCButtonsPressed = find_c_buttons_pressed(sCButtonsPressed, gPlayer1Controller->buttonPressed,
   3059                                               gPlayer1Controller->buttonDown);
   3060 
   3061     if (c->cutscene != 0) {
   3062         sYawSpeed = 0;
   3063         play_cutscene(c);
   3064         sFramesSinceCutsceneEnded = 0;
   3065     } else {
   3066         // Clear the recent cutscene after 8 frames
   3067         if (gRecentCutscene != 0 && sFramesSinceCutsceneEnded < 8) {
   3068             sFramesSinceCutsceneEnded++;
   3069             if (sFramesSinceCutsceneEnded >= 8) {
   3070                 gRecentCutscene = 0;
   3071                 sFramesSinceCutsceneEnded = 0;
   3072             }
   3073         }
   3074     }
   3075     // If not in a cutscene, do mode processing
   3076     if (c->cutscene == 0) {
   3077         sYawSpeed = 0x400;
   3078 
   3079         if (sSelectionFlags & CAM_MODE_MARIO_ACTIVE) {
   3080             switch (c->mode) {
   3081                 case CAMERA_MODE_BEHIND_MARIO:
   3082                     mode_behind_mario_camera(c);
   3083                     break;
   3084 
   3085                 case CAMERA_MODE_C_UP:
   3086                     mode_c_up_camera(c);
   3087                     break;
   3088 
   3089                 case CAMERA_MODE_WATER_SURFACE:
   3090                     mode_water_surface_camera(c);
   3091                     break;
   3092 
   3093                 case CAMERA_MODE_INSIDE_CANNON:
   3094                     mode_cannon_camera(c);
   3095                     break;
   3096 
   3097                 default:
   3098                     mode_mario_camera(c);
   3099             }
   3100         } else {
   3101             switch (c->mode) {
   3102                 case CAMERA_MODE_BEHIND_MARIO:
   3103                     mode_behind_mario_camera(c);
   3104                     break;
   3105 
   3106                 case CAMERA_MODE_C_UP:
   3107                     mode_c_up_camera(c);
   3108                     break;
   3109 
   3110                 case CAMERA_MODE_WATER_SURFACE:
   3111                     mode_water_surface_camera(c);
   3112                     break;
   3113 
   3114                 case CAMERA_MODE_INSIDE_CANNON:
   3115                     mode_cannon_camera(c);
   3116                     break;
   3117 
   3118                 case CAMERA_MODE_8_DIRECTIONS:
   3119                     mode_8_directions_camera(c);
   3120                     break;
   3121 
   3122                 case CAMERA_MODE_RADIAL:
   3123                     mode_radial_camera(c);
   3124                     break;
   3125 
   3126                 case CAMERA_MODE_OUTWARD_RADIAL:
   3127                     mode_outward_radial_camera(c);
   3128                     break;
   3129 
   3130                 case CAMERA_MODE_CLOSE:
   3131                     mode_lakitu_camera(c);
   3132                     break;
   3133 
   3134                 case CAMERA_MODE_FREE_ROAM:
   3135                     mode_lakitu_camera(c);
   3136                     break;
   3137                 case CAMERA_MODE_BOSS_FIGHT:
   3138                     mode_boss_fight_camera(c);
   3139                     break;
   3140 
   3141                 case CAMERA_MODE_PARALLEL_TRACKING:
   3142                     mode_parallel_tracking_camera(c);
   3143                     break;
   3144 
   3145                 case CAMERA_MODE_SLIDE_HOOT:
   3146                     mode_slide_camera(c);
   3147                     break;
   3148 
   3149                 case CAMERA_MODE_FIXED:
   3150                     mode_fixed_camera(c);
   3151                     break;
   3152 
   3153                 case CAMERA_MODE_SPIRAL_STAIRS:
   3154                     mode_spiral_stairs_camera(c);
   3155                     break;
   3156             }
   3157         }
   3158     }
   3159     // Start any Mario-related cutscenes
   3160     start_cutscene(c, get_cutscene_from_mario_status(c));
   3161     stub_camera_2(c);
   3162     gCheckingSurfaceCollisionsForCamera = FALSE;
   3163     if (gCurrLevelNum != LEVEL_CASTLE) {
   3164         // If fixed camera is selected as the alternate mode, then fix the camera as long as the right
   3165         // trigger is held
   3166         if ((c->cutscene == 0 &&
   3167             (gPlayer1Controller->buttonDown & R_TRIG) && cam_select_alt_mode(0) == CAM_SELECTION_FIXED)
   3168             || (gCameraMovementFlags & CAM_MOVE_FIX_IN_PLACE)
   3169             || (sMarioCamState->action) == ACT_GETTING_BLOWN) {
   3170 
   3171             // If this is the first frame that R_TRIG is held, play the "click" sound
   3172             if (c->cutscene == 0 && (gPlayer1Controller->buttonPressed & R_TRIG)
   3173                 && cam_select_alt_mode(0) == CAM_SELECTION_FIXED) {
   3174                 sCameraSoundFlags |= CAM_SOUND_FIXED_ACTIVE;
   3175                 play_sound_rbutton_changed();
   3176             }
   3177 
   3178             // Fixed mode only prevents Lakitu from moving. The camera pos still updates, so
   3179             // Lakitu will fly to his next position as normal whenever R_TRIG is released.
   3180             gLakituState.posHSpeed = 0.f;
   3181             gLakituState.posVSpeed = 0.f;
   3182 
   3183             c->nextYaw = calculate_yaw(gLakituState.focus, gLakituState.pos);
   3184             c->yaw = c->nextYaw;
   3185             gCameraMovementFlags &= ~CAM_MOVE_FIX_IN_PLACE;
   3186         } else {
   3187             // Play the "click" sound when fixed mode is released
   3188             if (sCameraSoundFlags & CAM_SOUND_FIXED_ACTIVE) {
   3189                 play_sound_rbutton_changed();
   3190                 sCameraSoundFlags &= ~CAM_SOUND_FIXED_ACTIVE;
   3191             }
   3192         }
   3193     } else {
   3194         if ((gPlayer1Controller->buttonPressed & R_TRIG) && cam_select_alt_mode(0) == CAM_SELECTION_FIXED) {
   3195             play_sound_button_change_blocked();
   3196         }
   3197     }
   3198 
   3199     update_lakitu(c);
   3200 
   3201     gLakituState.lastFrameAction = sMarioCamState->action;
   3202 }
   3203 
   3204 /**
   3205  * Reset all the camera variables to their arcane defaults
   3206  */
   3207 void reset_camera(struct Camera *c) {
   3208     UNUSED s32 unused = 0;
   3209     UNUSED u8 filler[16];
   3210     UNUSED struct LinearTransitionPoint *start = &sModeInfo.transitionStart;
   3211     UNUSED struct LinearTransitionPoint *end = &sModeInfo.transitionEnd;
   3212 
   3213     gCamera = c;
   3214     gCameraMovementFlags = 0;
   3215     s2ndRotateFlags = 0;
   3216     sStatusFlags = 0;
   3217     gCutsceneTimer = 0;
   3218     sCutsceneShot = 0;
   3219     gCutsceneObjSpawn = 0;
   3220     gObjCutsceneDone = FALSE;
   3221     gCutsceneFocus = NULL;
   3222     unused8032CFC8 = 0;
   3223     unused8032CFCC = 0;
   3224     gSecondCameraFocus = NULL;
   3225     sCButtonsPressed = 0;
   3226     vec3f_copy(sModeTransition.marioPos, sMarioCamState->pos);
   3227     sModeTransition.framesLeft = 0;
   3228     unused8032CFCC = -1;
   3229     unused8032CFC8 = -1;
   3230     gCameraMovementFlags = 0;
   3231     gCameraMovementFlags |= CAM_MOVE_INIT_CAMERA;
   3232     unused8033B316 = 0;
   3233     sStatusFlags = 0;
   3234     unused8033B31A = 0;
   3235     sCameraSoundFlags = 0;
   3236     sCUpCameraPitch = 0;
   3237     sModeOffsetYaw = 0;
   3238     sSpiralStairsYawOffset = 0;
   3239     sLakituDist = 0;
   3240     sLakituPitch = 0;
   3241     sAreaYaw = 0;
   3242     sAreaYawChange = 0.f;
   3243     sPanDistance = 0.f;
   3244     sCannonYOffset = 0.f;
   3245     sZoomAmount = 0.f;
   3246     sZeroZoomDist = 0.f;
   3247     sBehindMarioSoundTimer = 0;
   3248     sCSideButtonYaw = 0;
   3249     s8DirModeBaseYaw = 0;
   3250     s8DirModeYawOffset = 0;
   3251     c->doorStatus = DOOR_DEFAULT;
   3252     sMarioCamState->headRotation[0] = 0;
   3253     sMarioCamState->headRotation[1] = 0;
   3254     sLuigiCamState->headRotation[0] = 0;
   3255     sLuigiCamState->headRotation[1] = 0;
   3256     sMarioCamState->cameraEvent = 0;
   3257     sMarioCamState->usedObj = NULL;
   3258     gLakituState.shakeMagnitude[0] = 0;
   3259     gLakituState.shakeMagnitude[1] = 0;
   3260     gLakituState.shakeMagnitude[2] = 0;
   3261     gLakituState.unusedVec2[0] = 0;
   3262     gLakituState.unusedVec2[1] = 0;
   3263     gLakituState.unusedVec2[2] = 0;
   3264     gLakituState.unusedVec1[0] = 0.f;
   3265     gLakituState.unusedVec1[1] = 0.f;
   3266     gLakituState.unusedVec1[2] = 0.f;
   3267     gLakituState.lastFrameAction = 0;
   3268     set_fov_function(CAM_FOV_DEFAULT);
   3269     sFOVState.fov = 45.f;
   3270     sFOVState.fovOffset = 0.f;
   3271     sFOVState.unusedIsSleeping = 0;
   3272     sFOVState.shakeAmplitude = 0.f;
   3273     sFOVState.shakePhase = 0;
   3274     sObjectCutscene = 0;
   3275     gRecentCutscene = 0;
   3276     unused8033B30C = 0;
   3277     unused8033B310 = 0;
   3278 }
   3279 
   3280 void init_camera(struct Camera *c) {
   3281     struct Surface *floor = 0;
   3282     Vec3f marioOffset;
   3283     s32 i;
   3284 
   3285     sCreditsPlayer2Pitch = 0;
   3286     sCreditsPlayer2Yaw = 0;
   3287     gPrevLevel = gCurrLevelArea / 16;
   3288     gCurrLevelArea = gCurrLevelNum * 16 + gCurrentArea->index;
   3289     sSelectionFlags &= CAM_MODE_MARIO_SELECTED;
   3290     sFramesPaused = 0;
   3291     gLakituState.mode = c->mode;
   3292     gLakituState.defMode = c->defMode;
   3293     gLakituState.posHSpeed = 0.3f;
   3294     gLakituState.posVSpeed = 0.3f;
   3295     gLakituState.focHSpeed = 0.8f;
   3296     gLakituState.focHSpeed = 0.3f; // @bug set focHSpeed back-to-back
   3297     gLakituState.roll = 0;
   3298     gLakituState.keyDanceRoll = 0;
   3299     gLakituState.unused = 0;
   3300     sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   3301     vec3f_set(sCastleEntranceOffset, 0.f, 0.f, 0.f);
   3302     vec3f_set(sPlayer2FocusOffset, 0.f, 0.f, 0.f);
   3303     find_mario_floor_and_ceil(&sMarioGeometry);
   3304     sMarioGeometry.prevFloorHeight = sMarioGeometry.currFloorHeight;
   3305     sMarioGeometry.prevCeilHeight = sMarioGeometry.currCeilHeight;
   3306     sMarioGeometry.prevFloor = sMarioGeometry.currFloor;
   3307     sMarioGeometry.prevCeil = sMarioGeometry.currCeil;
   3308     sMarioGeometry.prevFloorType = sMarioGeometry.currFloorType;
   3309     sMarioGeometry.prevCeilType = sMarioGeometry.currCeilType;
   3310     for (i = 0; i < 32; i++) {
   3311         sCurCreditsSplinePos[i].index = -1;
   3312         sCurCreditsSplineFocus[i].index = -1;
   3313     }
   3314     sCutsceneSplineSegment = 0;
   3315     sCutsceneSplineSegmentProgress = 0.f;
   3316     unused8033B6E8 = 0;
   3317     sHandheldShakeInc = 0.f;
   3318     sHandheldShakeTimer = 0.f;
   3319     sHandheldShakeMag = 0;
   3320     for (i = 0; i < 4; i++) {
   3321         sHandheldShakeSpline[i].index = -1;
   3322     }
   3323     sHandheldShakePitch = 0;
   3324     sHandheldShakeYaw = 0;
   3325     sHandheldShakeRoll = 0;
   3326     c->cutscene = 0;
   3327     marioOffset[0] = 0.f;
   3328     marioOffset[1] = 125.f;
   3329     marioOffset[2] = 400.f;
   3330 
   3331     // Set the camera's starting position or start a cutscene for certain levels
   3332     switch (gCurrLevelNum) {
   3333         // Calls the initial cutscene when you enter Bowser battle levels
   3334         // Note: This replaced an "old" way to call these cutscenes using
   3335         // a camEvent value: CAM_EVENT_BOWSER_INIT
   3336         case LEVEL_BOWSER_1:
   3337 #ifndef VERSION_JP
   3338             // Since Bowser 1 has a demo entry, check for it
   3339             // If it is, then set CamAct to the end to directly activate Bowser
   3340             // If it isn't, then start cutscene
   3341             if (gCurrDemoInput == NULL) {
   3342                 start_cutscene(c, CUTSCENE_ENTER_BOWSER_ARENA);
   3343             } else if (gSecondCameraFocus != NULL) {
   3344                 gSecondCameraFocus->oBowserCamAct = BOWSER_CAM_ACT_END;
   3345             }
   3346 #else
   3347             start_cutscene(c, CUTSCENE_ENTER_BOWSER_ARENA);
   3348 #endif
   3349             break;
   3350         case LEVEL_BOWSER_2:
   3351             start_cutscene(c, CUTSCENE_ENTER_BOWSER_ARENA);
   3352             break;
   3353         case LEVEL_BOWSER_3:
   3354             start_cutscene(c, CUTSCENE_ENTER_BOWSER_ARENA);
   3355             break;
   3356 
   3357         //! Hardcoded position checks determine which cutscene to play when Mario enters castle grounds.
   3358         case LEVEL_CASTLE_GROUNDS:
   3359             if (is_within_100_units_of_mario(-1328.f, 260.f, 4664.f) != 1) {
   3360                 marioOffset[0] = -400.f;
   3361                 marioOffset[2] = -800.f;
   3362             }
   3363             if (is_within_100_units_of_mario(-6901.f, 2376.f, -6509.f) == 1) {
   3364                 start_cutscene(c, CUTSCENE_EXIT_WATERFALL);
   3365             }
   3366             if (is_within_100_units_of_mario(5408.f, 4500.f, 3637.f) == 1) {
   3367                 start_cutscene(c, CUTSCENE_EXIT_FALL_WMOTR);
   3368             }
   3369             gLakituState.mode = CAMERA_MODE_FREE_ROAM;
   3370             break;
   3371         case LEVEL_SA:
   3372             marioOffset[2] = 200.f;
   3373             break;
   3374         case LEVEL_CASTLE_COURTYARD:
   3375             marioOffset[2] = -300.f;
   3376             break;
   3377         case LEVEL_LLL:
   3378             gCameraMovementFlags |= CAM_MOVE_ZOOMED_OUT;
   3379             break;
   3380         case LEVEL_CASTLE:
   3381             marioOffset[2] = 150.f;
   3382             break;
   3383         case LEVEL_RR:
   3384             vec3f_set(sFixedModeBasePosition, -2985.f, 478.f, -5568.f);
   3385             break;
   3386     }
   3387     if (c->mode == CAMERA_MODE_8_DIRECTIONS) {
   3388         gCameraMovementFlags |= CAM_MOVE_ZOOMED_OUT;
   3389     }
   3390     switch (gCurrLevelArea) {
   3391         case AREA_SSL_EYEROK:
   3392             vec3f_set(marioOffset, 0.f, 500.f, -100.f);
   3393             break;
   3394         case AREA_CCM_SLIDE:
   3395             marioOffset[2] = -300.f;
   3396             break;
   3397         case AREA_THI_WIGGLER:
   3398             marioOffset[2] = -300.f;
   3399             break;
   3400         case AREA_SL_IGLOO:
   3401             marioOffset[2] = -300.f;
   3402             break;
   3403         case AREA_SL_OUTSIDE:
   3404             if (is_within_100_units_of_mario(257.f, 2150.f, 1399.f) == 1) {
   3405                 marioOffset[2] = -300.f;
   3406             }
   3407             break;
   3408         case AREA_CCM_OUTSIDE:
   3409             gCameraMovementFlags |= CAM_MOVE_ZOOMED_OUT;
   3410             break;
   3411         case AREA_TTM_OUTSIDE:
   3412             gLakituState.mode = CAMERA_MODE_RADIAL;
   3413             break;
   3414     }
   3415 
   3416     // Set the camera pos to marioOffset (relative to Mario), added to Mario's position
   3417     offset_rotated(c->pos, sMarioCamState->pos, marioOffset, sMarioCamState->faceAngle);
   3418     if (c->mode != CAMERA_MODE_BEHIND_MARIO) {
   3419         c->pos[1] = find_floor(sMarioCamState->pos[0], sMarioCamState->pos[1] + 100.f,
   3420                                sMarioCamState->pos[2], &floor) + 125.f;
   3421     }
   3422     vec3f_copy(c->focus, sMarioCamState->pos);
   3423     vec3f_copy(gLakituState.curPos, c->pos);
   3424     vec3f_copy(gLakituState.curFocus, c->focus);
   3425     vec3f_copy(gLakituState.goalPos, c->pos);
   3426     vec3f_copy(gLakituState.goalFocus, c->focus);
   3427     vec3f_copy(gLakituState.pos, c->pos);
   3428     vec3f_copy(gLakituState.focus, c->focus);
   3429     if (c->mode == CAMERA_MODE_FIXED) {
   3430         set_fixed_cam_axis_sa_lobby(c->mode);
   3431     }
   3432     store_lakitu_cam_info_for_c_up(c);
   3433     gLakituState.yaw = calculate_yaw(c->focus, c->pos);
   3434     gLakituState.nextYaw = gLakituState.yaw;
   3435     c->yaw = gLakituState.yaw;
   3436     c->nextYaw = gLakituState.yaw;
   3437 }
   3438 
   3439 /**
   3440  * Zooms out the camera if paused and the level is 'outside', as determined by sZoomOutAreaMasks.
   3441  *
   3442  * Because gCurrLevelArea is assigned gCurrLevelNum * 16 + gCurrentArea->index,
   3443  * dividing by 32 maps 2 levels to one index.
   3444  *
   3445  * areaBit definition:
   3446  * (gCurrLevelArea & 0x10) / 4):
   3447  *      This adds 4 to the shift if the level is an odd multiple of 16
   3448  *
   3449  * ((gCurrLevelArea & 0xF) - 1) & 3):
   3450  *      This isolates the lower 16 'area' bits, subtracts 1 because areas are 1-indexed, and effectively
   3451  *      modulo-4's the result, because each 8-bit mask only has 4 area bits for each level
   3452  */
   3453 void zoom_out_if_paused_and_outside(struct GraphNodeCamera *camera) {
   3454     UNUSED u8 filler1[8];
   3455     UNUSED f32 dist;
   3456     UNUSED s16 pitch;
   3457     s16 yaw;
   3458     UNUSED u8 filler2[4];
   3459     s32 areaMaskIndex = gCurrLevelArea / 32;
   3460     s32 areaBit = 1 << (((gCurrLevelArea & 0x10) / 4) + (((gCurrLevelArea & 0xF) - 1) & 3));
   3461 
   3462     if (areaMaskIndex >= LEVEL_MAX / 2) {
   3463         areaMaskIndex = 0;
   3464         areaBit = 0;
   3465     }
   3466     if (gCameraMovementFlags & CAM_MOVE_PAUSE_SCREEN) {
   3467         if (sFramesPaused >= 2) {
   3468             if (sZoomOutAreaMasks[areaMaskIndex] & areaBit) {
   3469 
   3470                 camera->focus[0] = gCamera->areaCenX;
   3471                 camera->focus[1] = (sMarioCamState->pos[1] + gCamera->areaCenY) / 2;
   3472                 camera->focus[2] = gCamera->areaCenZ;
   3473                 vec3f_get_dist_and_angle(camera->focus, sMarioCamState->pos, &dist, &pitch, &yaw);
   3474                 vec3f_set_dist_and_angle(sMarioCamState->pos, camera->pos, 6000.f, 0x1000, yaw);
   3475                 if (gCurrLevelNum != LEVEL_THI) {
   3476                     find_in_bounds_yaw_wdw_bob_thi(camera->pos, camera->focus, 0);
   3477                 }
   3478             }
   3479         } else {
   3480             sFramesPaused++;
   3481         }
   3482     } else {
   3483         sFramesPaused = 0;
   3484     }
   3485 }
   3486 
   3487 void select_mario_cam_mode(void) {
   3488     sSelectionFlags = CAM_MODE_MARIO_SELECTED;
   3489 }
   3490 
   3491 /**
   3492  * Allocate the GraphNodeCamera's config.camera, and copy `c`'s focus to the Camera's area center point.
   3493  */
   3494 void create_camera(struct GraphNodeCamera *gc, struct AllocOnlyPool *pool) {
   3495     s16 mode = gc->config.mode;
   3496     struct Camera *c = alloc_only_pool_alloc(pool, sizeof(struct Camera));
   3497 
   3498     gc->config.camera = c;
   3499     c->mode = mode;
   3500     c->defMode = mode;
   3501     c->cutscene = 0;
   3502     c->doorStatus = DOOR_DEFAULT;
   3503     c->areaCenX = gc->focus[0];
   3504     c->areaCenY = gc->focus[1];
   3505     c->areaCenZ = gc->focus[2];
   3506     c->yaw = 0;
   3507     vec3f_copy(c->pos, gc->pos);
   3508     vec3f_copy(c->focus, gc->focus);
   3509 }
   3510 
   3511 /**
   3512  * Copy Lakitu's pos and foc into `gc`
   3513  */
   3514 void update_graph_node_camera(struct GraphNodeCamera *gc) {
   3515     UNUSED u8 filler[8];
   3516     UNUSED struct Camera *c = gc->config.camera;
   3517 
   3518     gc->rollScreen = gLakituState.roll;
   3519     vec3f_copy(gc->pos, gLakituState.pos);
   3520     vec3f_copy(gc->focus, gLakituState.focus);
   3521     zoom_out_if_paused_and_outside(gc);
   3522 }
   3523 
   3524 Gfx *geo_camera_main(s32 callContext, struct GraphNode *g, void *context) {
   3525     struct GraphNodeCamera *gc = (struct GraphNodeCamera *) g;
   3526     UNUSED Mat4 *unusedMat = context;
   3527 
   3528     switch (callContext) {
   3529         case GEO_CONTEXT_CREATE:
   3530             create_camera(gc, context);
   3531             break;
   3532         case GEO_CONTEXT_RENDER:
   3533             update_graph_node_camera(gc);
   3534             break;
   3535     }
   3536     return NULL;
   3537 }
   3538 
   3539 void stub_camera_2(UNUSED struct Camera *c) {
   3540 }
   3541 
   3542 void stub_camera_3(UNUSED struct Camera *c) {
   3543 }
   3544 
   3545 void vec3f_sub(Vec3f dst, Vec3f src) {
   3546     dst[0] -= src[0];
   3547     dst[1] -= src[1];
   3548     dst[2] -= src[2];
   3549 }
   3550 
   3551 void object_pos_to_vec3f(Vec3f dst, struct Object *o) {
   3552     dst[0] = o->oPosX;
   3553     dst[1] = o->oPosY;
   3554     dst[2] = o->oPosZ;
   3555 }
   3556 
   3557 void vec3f_to_object_pos(struct Object *o, Vec3f src) {
   3558     o->oPosX = src[0];
   3559     o->oPosY = src[1];
   3560     o->oPosZ = src[2];
   3561 }
   3562 
   3563 void unused_object_angle_to_vec3s(Vec3s dst, struct Object *o) {
   3564     dst[0] = o->oMoveAnglePitch;
   3565     dst[1] = o->oMoveAngleYaw;
   3566     dst[2] = o->oMoveAngleRoll;
   3567 }
   3568 
   3569 /**
   3570  * Produces values using a cubic b-spline curve. Basically Q is the used output,
   3571  * u is a value between 0 and 1 that represents the position along the spline,
   3572  * and a0-a3 are parameters that define the spline.
   3573  *
   3574  * The spline is described at http://www2.cs.uregina.ca/~anima/UniformBSpline.htm
   3575  */
   3576 void evaluate_cubic_spline(f32 u, Vec3f Q, Vec3f a0, Vec3f a1, Vec3f a2, Vec3f a3) {
   3577     f32 B[4];
   3578     f32 x;
   3579     f32 y;
   3580     f32 z;
   3581     UNUSED u8 filler[16];
   3582 
   3583     if (u > 1.f) {
   3584         u = 1.f;
   3585     }
   3586 
   3587     B[0] = (1.f - u) * (1.f - u) * (1.f - u) / 6.f;
   3588     B[1] = u * u * u / 2.f - u * u + 0.6666667f;
   3589     B[2] = -u * u * u / 2.f + u * u / 2.f + u / 2.f + 0.16666667f;
   3590     B[3] = u * u * u / 6.f;
   3591 
   3592     Q[0] = B[0] * a0[0] + B[1] * a1[0] + B[2] * a2[0] + B[3] * a3[0];
   3593     Q[1] = B[0] * a0[1] + B[1] * a1[1] + B[2] * a2[1] + B[3] * a3[1];
   3594     Q[2] = B[0] * a0[2] + B[1] * a1[2] + B[2] * a2[2] + B[3] * a3[2];
   3595 
   3596     // Unused code
   3597     B[0] = -0.5f * u * u + u - 0.33333333f;
   3598     B[1] = 1.5f * u * u - 2.f * u - 0.5f;
   3599     B[2] = -1.5f * u * u + u + 1.f;
   3600     B[3] = 0.5f * u * u - 0.16666667f;
   3601 
   3602     x = B[0] * a0[0] + B[1] * a1[0] + B[2] * a2[0] + B[3] * a3[0];
   3603     y = B[0] * a0[1] + B[1] * a1[1] + B[2] * a2[1] + B[3] * a3[1];
   3604     z = B[0] * a0[2] + B[1] * a1[2] + B[2] * a2[2] + B[3] * a3[2];
   3605 
   3606     unusedSplinePitch = atan2s(sqrtf(x * x + z * z), y);
   3607     unusedSplineYaw = atan2s(z, x);
   3608 }
   3609 
   3610 /**
   3611  * Computes the point that is `progress` percent of the way through segment `splineSegment` of `spline`,
   3612  * and stores the result in `p`. `progress` and `splineSegment` are updated if `progress` becomes >= 1.0.
   3613  *
   3614  * When neither of the next two points' speeds == 0, the number of frames is between 1 and 255. Otherwise
   3615  * it's infinite.
   3616  *
   3617  * To calculate the number of frames it will take to progress through a spline segment:
   3618  * If the next two speeds are the same and nonzero, it's 1.0 / firstSpeed.
   3619  *
   3620  * s1 and s2 are short hand for first/secondSpeed. The progress at any frame n is defined by a recurrency relation:
   3621  *      p(n+1) = (s2 - s1 + 1) * p(n) + s1
   3622  * Which can be written as
   3623  *      p(n) = (s2 * ((s2 - s1 + 1)^(n) - 1)) / (s2 - s1)
   3624  *
   3625  * Solving for the number of frames:
   3626  *      n = log(((s2 - s1) / s1) + 1) / log(s2 - s1 + 1)
   3627  *
   3628  * @return 1 if the point has reached the end of the spline, when `progress` reaches 1.0 or greater, and
   3629  * the 4th CutsceneSplinePoint in the current segment away from spline[splineSegment] has an index of -1.
   3630  */
   3631 s32 move_point_along_spline(Vec3f p, struct CutsceneSplinePoint spline[], s16 *splineSegment, f32 *progress) {
   3632     s32 finished = 0;
   3633     Vec3f controlPoints[4];
   3634     s32 i = 0;
   3635     f32 u = *progress;
   3636     f32 progressChange;
   3637     f32 firstSpeed = 0;
   3638     f32 secondSpeed = 0;
   3639     s32 segment = *splineSegment;
   3640 
   3641     if (*splineSegment < 0) {
   3642         segment = 0;
   3643         u = 0;
   3644     }
   3645     if (spline[segment].index == -1 || spline[segment + 1].index == -1 || spline[segment + 2].index == -1) {
   3646         return 1;
   3647     }
   3648 
   3649     for (i = 0; i < 4; i++) {
   3650         controlPoints[i][0] = spline[segment + i].point[0];
   3651         controlPoints[i][1] = spline[segment + i].point[1];
   3652         controlPoints[i][2] = spline[segment + i].point[2];
   3653     }
   3654     evaluate_cubic_spline(u, p, controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3]);
   3655 
   3656     if (spline[*splineSegment + 1].speed != 0) {
   3657         firstSpeed = 1.0f / spline[*splineSegment + 1].speed;
   3658     }
   3659     if (spline[*splineSegment + 2].speed != 0) {
   3660         secondSpeed = 1.0f / spline[*splineSegment + 2].speed;
   3661     }
   3662     progressChange = (secondSpeed - firstSpeed) * *progress + firstSpeed;
   3663 
   3664 #ifdef VERSION_EU
   3665     if (gCamera->cutscene == CUTSCENE_INTRO_PEACH) {
   3666         progressChange += progressChange * 0.19f;
   3667     }
   3668     if (gCamera->cutscene == CUTSCENE_CREDITS) {
   3669         progressChange += progressChange * 0.15f;
   3670     }
   3671     if (gCamera->cutscene == CUTSCENE_ENDING) {
   3672         progressChange += progressChange * 0.1f;
   3673     }
   3674 #endif
   3675 
   3676     if (1 <= (*progress += progressChange)) {
   3677         (*splineSegment)++;
   3678         if (spline[*splineSegment + 3].index == -1) {
   3679             *splineSegment = 0;
   3680             finished = 1;
   3681         }
   3682         (*progress)--;
   3683     }
   3684     return finished;
   3685 }
   3686 
   3687 /**
   3688  * If `selection` is 0, just get the current selection
   3689  * If `selection` is 1, select 'Mario' as the alt mode.
   3690  * If `selection` is 2, select 'fixed' as the alt mode.
   3691  *
   3692  * @return the current selection
   3693  */
   3694 s32 cam_select_alt_mode(s32 selection) {
   3695     s32 mode = CAM_SELECTION_FIXED;
   3696 
   3697     if (selection == CAM_SELECTION_MARIO) {
   3698         if (!(sSelectionFlags & CAM_MODE_MARIO_SELECTED)) {
   3699             sSelectionFlags |= CAM_MODE_MARIO_SELECTED;
   3700         }
   3701         sCameraSoundFlags |= CAM_SOUND_UNUSED_SELECT_MARIO;
   3702     }
   3703 
   3704     // The alternate mode is up-close, but the player just selected fixed in the pause menu
   3705     if (selection == CAM_SELECTION_FIXED && (sSelectionFlags & CAM_MODE_MARIO_SELECTED)) {
   3706         // So change to normal mode in case the user paused in up-close mode
   3707         set_cam_angle(CAM_ANGLE_LAKITU);
   3708         sSelectionFlags &= ~CAM_MODE_MARIO_SELECTED;
   3709         sCameraSoundFlags |= CAM_SOUND_UNUSED_SELECT_FIXED;
   3710     }
   3711 
   3712     if (sSelectionFlags & CAM_MODE_MARIO_SELECTED) {
   3713         mode = CAM_SELECTION_MARIO;
   3714     }
   3715     return mode;
   3716 }
   3717 
   3718 /**
   3719  * Sets the camera angle to either Lakitu or Mario mode. Returns the current mode.
   3720  *
   3721  * If `mode` is 0, just returns the current mode.
   3722  * If `mode` is 1, start Mario mode
   3723  * If `mode` is 2, start Lakitu mode
   3724  */
   3725 s32 set_cam_angle(s32 mode) {
   3726     s32 curMode = CAM_ANGLE_LAKITU;
   3727 
   3728     // Switch to Mario mode
   3729     if (mode == CAM_ANGLE_MARIO && !(sSelectionFlags & CAM_MODE_MARIO_ACTIVE)) {
   3730         sSelectionFlags |= CAM_MODE_MARIO_ACTIVE;
   3731         if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   3732             sSelectionFlags |= CAM_MODE_LAKITU_WAS_ZOOMED_OUT;
   3733             gCameraMovementFlags &= ~CAM_MOVE_ZOOMED_OUT;
   3734         }
   3735         sCameraSoundFlags |= CAM_SOUND_MARIO_ACTIVE;
   3736     }
   3737 
   3738     // Switch back to normal mode
   3739     if (mode == CAM_ANGLE_LAKITU && (sSelectionFlags & CAM_MODE_MARIO_ACTIVE)) {
   3740         sSelectionFlags &= ~CAM_MODE_MARIO_ACTIVE;
   3741         if (sSelectionFlags & CAM_MODE_LAKITU_WAS_ZOOMED_OUT) {
   3742             sSelectionFlags &= ~CAM_MODE_LAKITU_WAS_ZOOMED_OUT;
   3743             gCameraMovementFlags |= CAM_MOVE_ZOOMED_OUT;
   3744         } else {
   3745             gCameraMovementFlags &= ~CAM_MOVE_ZOOMED_OUT;
   3746         }
   3747         sCameraSoundFlags |= CAM_SOUND_NORMAL_ACTIVE;
   3748     }
   3749     if (sSelectionFlags & CAM_MODE_MARIO_ACTIVE) {
   3750         curMode = CAM_ANGLE_MARIO;
   3751     }
   3752     return curMode;
   3753 }
   3754 
   3755 /**
   3756  * Enables the handheld shake effect for this frame.
   3757  *
   3758  * @see shake_camera_handheld()
   3759  */
   3760 void set_handheld_shake(u8 mode) {
   3761     switch (mode) {
   3762         // They're not in numerical order because that would be too simple...
   3763         case HAND_CAM_SHAKE_CUTSCENE: // Lowest increment
   3764             sHandheldShakeMag = 0x600;
   3765             sHandheldShakeInc = 0.04f;
   3766             break;
   3767         case HAND_CAM_SHAKE_LOW: // Lowest magnitude
   3768             sHandheldShakeMag = 0x300;
   3769             sHandheldShakeInc = 0.06f;
   3770             break;
   3771         case HAND_CAM_SHAKE_HIGH: // Highest mag and inc
   3772             sHandheldShakeMag = 0x1000;
   3773             sHandheldShakeInc = 0.1f;
   3774             break;
   3775         case HAND_CAM_SHAKE_UNUSED: // Never used
   3776             sHandheldShakeMag = 0x600;
   3777             sHandheldShakeInc = 0.07f;
   3778             break;
   3779         case HAND_CAM_SHAKE_HANG_OWL: // exactly the same as UNUSED...
   3780             sHandheldShakeMag = 0x600;
   3781             sHandheldShakeInc = 0.07f;
   3782             break;
   3783         case HAND_CAM_SHAKE_STAR_DANCE: // Slightly steadier than HANG_OWL and UNUSED
   3784             sHandheldShakeMag = 0x400;
   3785             sHandheldShakeInc = 0.07f;
   3786             break;
   3787         default:
   3788             sHandheldShakeMag = 0x0;
   3789             sHandheldShakeInc = 0.f;
   3790     }
   3791 }
   3792 
   3793 /**
   3794  * When sHandheldShakeMag is nonzero, this function adds small random offsets to `focus` every time
   3795  * sHandheldShakeTimer increases above 1.0, simulating the camera shake caused by unsteady hands.
   3796  *
   3797  * This function must be called every frame in order to actually apply the effect, since the effect's
   3798  * mag and inc are set to 0 every frame at the end of this function.
   3799  */
   3800 void shake_camera_handheld(Vec3f pos, Vec3f focus) {
   3801     s32 i;
   3802     Vec3f shakeOffset;
   3803     Vec3f shakeSpline[4];
   3804     f32 dist;
   3805     s16 pitch;
   3806     s16 yaw;
   3807     UNUSED u8 filler[8];
   3808 
   3809     if (sHandheldShakeMag == 0) {
   3810         vec3f_set(shakeOffset, 0.f, 0.f, 0.f);
   3811     } else {
   3812         for (i = 0; i < 4; i++) {
   3813             shakeSpline[i][0] = sHandheldShakeSpline[i].point[0];
   3814             shakeSpline[i][1] = sHandheldShakeSpline[i].point[1];
   3815             shakeSpline[i][2] = sHandheldShakeSpline[i].point[2];
   3816         }
   3817         evaluate_cubic_spline(sHandheldShakeTimer, shakeOffset, shakeSpline[0],
   3818                               shakeSpline[1], shakeSpline[2], shakeSpline[3]);
   3819         if (1.f <= (sHandheldShakeTimer += sHandheldShakeInc)) {
   3820             // The first 3 control points are always (0,0,0), so the random spline is always just a
   3821             // straight line
   3822             for (i = 0; i < 3; i++) {
   3823                 vec3s_copy(sHandheldShakeSpline[i].point, sHandheldShakeSpline[i + 1].point);
   3824             }
   3825             random_vec3s(sHandheldShakeSpline[3].point, sHandheldShakeMag, sHandheldShakeMag, sHandheldShakeMag / 2);
   3826             sHandheldShakeTimer -= 1.f;
   3827 
   3828             // Code dead, this is set to be 0 before it is used.
   3829             sHandheldShakeInc = random_float() * 0.5f;
   3830             if (sHandheldShakeInc < 0.02f) {
   3831                 sHandheldShakeInc = 0.02f;
   3832             }
   3833         }
   3834     }
   3835 
   3836     approach_s16_asymptotic_bool(&sHandheldShakePitch, shakeOffset[0], 0x08);
   3837     approach_s16_asymptotic_bool(&sHandheldShakeYaw, shakeOffset[1], 0x08);
   3838     approach_s16_asymptotic_bool(&sHandheldShakeRoll, shakeOffset[2], 0x08);
   3839 
   3840     if (sHandheldShakePitch | sHandheldShakeYaw) {
   3841         vec3f_get_dist_and_angle(pos, focus, &dist, &pitch, &yaw);
   3842         pitch += sHandheldShakePitch;
   3843         yaw += sHandheldShakeYaw;
   3844         vec3f_set_dist_and_angle(pos, focus, dist, pitch, yaw);
   3845     }
   3846 
   3847     // Unless called every frame, the effect will stop after the first time.
   3848     sHandheldShakeMag = 0;
   3849     sHandheldShakeInc = 0.f;
   3850 }
   3851 
   3852 /**
   3853  * Updates C Button input state and stores it in `currentState`
   3854  */
   3855 s32 find_c_buttons_pressed(u16 currentState, u16 buttonsPressed, u16 buttonsDown) {
   3856     buttonsPressed &= CBUTTON_MASK;
   3857     buttonsDown &= CBUTTON_MASK;
   3858 
   3859     if (buttonsPressed & L_CBUTTONS) {
   3860         currentState |= L_CBUTTONS;
   3861         currentState &= ~R_CBUTTONS;
   3862     }
   3863     if (!(buttonsDown & L_CBUTTONS)) {
   3864         currentState &= ~L_CBUTTONS;
   3865     }
   3866 
   3867     if (buttonsPressed & R_CBUTTONS) {
   3868         currentState |= R_CBUTTONS;
   3869         currentState &= ~L_CBUTTONS;
   3870     }
   3871     if (!(buttonsDown & R_CBUTTONS)) {
   3872         currentState &= ~R_CBUTTONS;
   3873     }
   3874 
   3875     if (buttonsPressed & U_CBUTTONS) {
   3876         currentState |= U_CBUTTONS;
   3877         currentState &= ~D_CBUTTONS;
   3878     }
   3879     if (!(buttonsDown & U_CBUTTONS)) {
   3880         currentState &= ~U_CBUTTONS;
   3881     }
   3882 
   3883     if (buttonsPressed & D_CBUTTONS) {
   3884         currentState |= D_CBUTTONS;
   3885         currentState &= ~U_CBUTTONS;
   3886     }
   3887     if (!(buttonsDown & D_CBUTTONS)) {
   3888         currentState &= ~D_CBUTTONS;
   3889     }
   3890 
   3891     return currentState;
   3892 }
   3893 
   3894 /**
   3895  * Determine which icon to show on the HUD
   3896  */
   3897 s32 update_camera_hud_status(struct Camera *c) {
   3898     s16 status = CAM_STATUS_NONE;
   3899 
   3900     if (c->cutscene != 0
   3901         || ((gPlayer1Controller->buttonDown & R_TRIG) && cam_select_alt_mode(0) == CAM_SELECTION_FIXED)) {
   3902         status |= CAM_STATUS_FIXED;
   3903     } else if (set_cam_angle(0) == CAM_ANGLE_MARIO) {
   3904         status |= CAM_STATUS_MARIO;
   3905     } else {
   3906         status |= CAM_STATUS_LAKITU;
   3907     }
   3908     if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   3909         status |= CAM_STATUS_C_DOWN;
   3910     }
   3911     if (gCameraMovementFlags & CAM_MOVE_C_UP_MODE) {
   3912         status |= CAM_STATUS_C_UP;
   3913     }
   3914     set_hud_camera_status(status);
   3915     return status;
   3916 }
   3917 
   3918 /**
   3919  * Check `pos` for collisions within `radius`, and update `pos`
   3920  *
   3921  * @return the number of collisions found
   3922  */
   3923 s32 collide_with_walls(Vec3f pos, f32 offsetY, f32 radius) {
   3924     struct WallCollisionData collisionData;
   3925     struct Surface *wall = NULL;
   3926     f32 normX;
   3927     f32 normY;
   3928     f32 normZ;
   3929     f32 originOffset;
   3930     f32 offset;
   3931     f32 offsetAbsolute;
   3932     Vec3f newPos[4];
   3933     s32 i;
   3934     s32 numCollisions = 0;
   3935 
   3936     collisionData.x = pos[0];
   3937     collisionData.y = pos[1];
   3938     collisionData.z = pos[2];
   3939     collisionData.radius = radius;
   3940     collisionData.offsetY = offsetY;
   3941     numCollisions = find_wall_collisions(&collisionData);
   3942     if (numCollisions != 0) {
   3943         for (i = 0; i < collisionData.numWalls; i++) {
   3944             wall = collisionData.walls[collisionData.numWalls - 1];
   3945             vec3f_copy(newPos[i], pos);
   3946             normX = wall->normal.x;
   3947             normY = wall->normal.y;
   3948             normZ = wall->normal.z;
   3949             originOffset = wall->originOffset;
   3950             offset = normX * newPos[i][0] + normY * newPos[i][1] + normZ * newPos[i][2] + originOffset;
   3951             offsetAbsolute = ABS2(offset);
   3952             if (offsetAbsolute < radius) {
   3953                 newPos[i][0] += normX * (radius - offset);
   3954                 newPos[i][2] += normZ * (radius - offset);
   3955                 vec3f_copy(pos, newPos[i]);
   3956             }
   3957         }
   3958     }
   3959     return numCollisions;
   3960 }
   3961 
   3962 /**
   3963  * Compare a vector to a position, return TRUE if they match.
   3964  */
   3965 s32 vec3f_compare(Vec3f pos, f32 posX, f32 posY, f32 posZ) {
   3966     s32 equal = FALSE;
   3967 
   3968     if (pos[0] == posX && pos[1] == posY && pos[2] == posZ) {
   3969         equal = TRUE;
   3970     }
   3971     return equal;
   3972 }
   3973 
   3974 s32 clamp_pitch(Vec3f from, Vec3f to, s16 maxPitch, s16 minPitch) {
   3975     s32 outOfRange = 0;
   3976     s16 pitch;
   3977     s16 yaw;
   3978     f32 dist;
   3979 
   3980     vec3f_get_dist_and_angle(from, to, &dist, &pitch, &yaw);
   3981     if (pitch > maxPitch) {
   3982         pitch = maxPitch;
   3983         outOfRange++;
   3984     }
   3985     if (pitch < minPitch) {
   3986         pitch = minPitch;
   3987         outOfRange++;
   3988     }
   3989     vec3f_set_dist_and_angle(from, to, dist, pitch, yaw);
   3990     return outOfRange;
   3991 }
   3992 
   3993 s32 is_within_100_units_of_mario(f32 posX, f32 posY, f32 posZ) {
   3994     s32 isCloseToMario = 0;
   3995     Vec3f pos;
   3996 
   3997     vec3f_set(pos, posX, posY, posZ);
   3998     if (calc_abs_dist(sMarioCamState->pos, pos) < 100.f) {
   3999         isCloseToMario = 1;
   4000     }
   4001     return isCloseToMario;
   4002 }
   4003 
   4004 s32 set_or_approach_f32_asymptotic(f32 *dst, f32 goal, f32 scale) {
   4005     if (sStatusFlags & CAM_FLAG_SMOOTH_MOVEMENT) {
   4006         approach_f32_asymptotic_bool(dst, goal, scale);
   4007     } else {
   4008         *dst = goal;
   4009     }
   4010     if (*dst == goal) {
   4011         return FALSE;
   4012     } else {
   4013         return TRUE;
   4014     }
   4015 }
   4016 
   4017 /**
   4018  * Approaches an f32 value by taking the difference between the target and current value
   4019  * and adding a fraction of that to the current value.
   4020  * Edits the current value directly, returns TRUE if the target has been reached, FALSE otherwise.
   4021  */
   4022 s32 approach_f32_asymptotic_bool(f32 *current, f32 target, f32 multiplier) {
   4023     if (multiplier > 1.f) {
   4024         multiplier = 1.f;
   4025     }
   4026     *current = *current + (target - *current) * multiplier;
   4027     if (*current == target) {
   4028         return FALSE;
   4029     } else {
   4030         return TRUE;
   4031     }
   4032 }
   4033 
   4034 /**
   4035  * Nearly the same as the above function, returns new value instead.
   4036  */
   4037 f32 approach_f32_asymptotic(f32 current, f32 target, f32 multiplier) {
   4038     current = current + (target - current) * multiplier;
   4039     return current;
   4040 }
   4041 
   4042 /**
   4043  * Approaches an s16 value in the same fashion as approach_f32_asymptotic_bool, returns TRUE if target
   4044  * is reached. Note: Since this function takes integers as parameters, the last argument is the
   4045  * reciprocal of what it would be in the previous two functions.
   4046  */
   4047 s32 approach_s16_asymptotic_bool(s16 *current, s16 target, s16 divisor) {
   4048     s16 temp = *current;
   4049 
   4050     if (divisor == 0) {
   4051         *current = target;
   4052     } else {
   4053         temp -= target;
   4054         temp -= temp / divisor;
   4055         temp += target;
   4056         *current = temp;
   4057     }
   4058     if (*current == target) {
   4059         return FALSE;
   4060     } else {
   4061         return TRUE;
   4062     }
   4063 }
   4064 
   4065 /**
   4066  * Approaches an s16 value in the same fashion as approach_f32_asymptotic, returns the new value.
   4067  * Note: last parameter is the reciprocal of what it would be in the f32 functions
   4068  */
   4069 s32 approach_s16_asymptotic(s16 current, s16 target, s16 divisor) {
   4070     s16 temp = current;
   4071 
   4072     if (divisor == 0) {
   4073         current = target;
   4074     } else {
   4075         temp -= target;
   4076         temp -= temp / divisor;
   4077         temp += target;
   4078         current = temp;
   4079     }
   4080     return current;
   4081 }
   4082 
   4083 /**
   4084  * Applies the approach_f32_asymptotic_bool function to each of the X, Y, & Z components of the given
   4085  * vector.
   4086  */
   4087 void approach_vec3f_asymptotic(Vec3f current, Vec3f target, f32 xMul, f32 yMul, f32 zMul) {
   4088     approach_f32_asymptotic_bool(&current[0], target[0], xMul);
   4089     approach_f32_asymptotic_bool(&current[1], target[1], yMul);
   4090     approach_f32_asymptotic_bool(&current[2], target[2], zMul);
   4091 }
   4092 
   4093 /**
   4094  * Applies the set_or_approach_f32_asymptotic_bool function to each of the X, Y, & Z components of the
   4095  * given vector.
   4096  */
   4097 void set_or_approach_vec3f_asymptotic(Vec3f dst, Vec3f goal, f32 xMul, f32 yMul, f32 zMul) {
   4098     set_or_approach_f32_asymptotic(&dst[0], goal[0], xMul);
   4099     set_or_approach_f32_asymptotic(&dst[1], goal[1], yMul);
   4100     set_or_approach_f32_asymptotic(&dst[2], goal[2], zMul);
   4101 }
   4102 
   4103 /**
   4104  * Applies the approach_s32_asymptotic function to each of the X, Y, & Z components of the given
   4105  * vector.
   4106  */
   4107 void approach_vec3s_asymptotic(Vec3s current, Vec3s target, s16 xMul, s16 yMul, s16 zMul) {
   4108     approach_s16_asymptotic_bool(&current[0], target[0], xMul);
   4109     approach_s16_asymptotic_bool(&current[1], target[1], yMul);
   4110     approach_s16_asymptotic_bool(&current[2], target[2], zMul);
   4111 }
   4112 
   4113 s32 camera_approach_s16_symmetric_bool(s16 *current, s16 target, s16 increment) {
   4114     s16 dist = target - *current;
   4115 
   4116     if (increment < 0) {
   4117         increment = -1 * increment;
   4118     }
   4119     if (dist > 0) {
   4120         dist -= increment;
   4121         if (dist >= 0) {
   4122             *current = target - dist;
   4123         } else {
   4124             *current = target;
   4125         }
   4126     } else {
   4127         dist += increment;
   4128         if (dist <= 0) {
   4129             *current = target - dist;
   4130         } else {
   4131             *current = target;
   4132         }
   4133     }
   4134     if (*current == target) {
   4135         return FALSE;
   4136     } else {
   4137         return TRUE;
   4138     }
   4139 }
   4140 
   4141 s32 camera_approach_s16_symmetric(s16 current, s16 target, s16 increment) {
   4142     s16 dist = target - current;
   4143 
   4144     if (increment < 0) {
   4145         increment = -1 * increment;
   4146     }
   4147     if (dist > 0) {
   4148         dist -= increment;
   4149         if (dist >= 0) {
   4150             current = target - dist;
   4151         } else {
   4152             current = target;
   4153         }
   4154     } else {
   4155         dist += increment;
   4156         if (dist <= 0) {
   4157             current = target - dist;
   4158         } else {
   4159             current = target;
   4160         }
   4161     }
   4162     return current;
   4163 }
   4164 
   4165 s32 set_or_approach_s16_symmetric(s16 *current, s16 target, s16 increment) {
   4166     if (sStatusFlags & CAM_FLAG_SMOOTH_MOVEMENT) {
   4167         camera_approach_s16_symmetric_bool(current, target, increment);
   4168     } else {
   4169         *current = target;
   4170     }
   4171     if (*current == target) {
   4172         return FALSE;
   4173     } else {
   4174         return TRUE;
   4175     }
   4176 }
   4177 
   4178 /**
   4179  * Approaches a value by a given increment, returns FALSE if the target is reached.
   4180  * Appears to be a strange way of implementing approach_f32_symmetric from object_helpers.c.
   4181  * It could possibly be an older version of the function
   4182  */
   4183 s32 camera_approach_f32_symmetric_bool(f32 *current, f32 target, f32 increment) {
   4184     f32 dist = target - *current;
   4185 
   4186     if (increment < 0) {
   4187         increment = -1 * increment;
   4188     }
   4189     if (dist > 0) {
   4190         dist -= increment;
   4191         if (dist > 0) {
   4192             *current = target - dist;
   4193         } else {
   4194             *current = target;
   4195         }
   4196     } else {
   4197         dist += increment;
   4198         if (dist < 0) {
   4199             *current = target - dist;
   4200         } else {
   4201             *current = target;
   4202         }
   4203     }
   4204     if (*current == target) {
   4205         return FALSE;
   4206     } else {
   4207         return TRUE;
   4208     }
   4209 }
   4210 
   4211 /**
   4212  * Nearly the same as the above function, this one returns the new value in place of a bool.
   4213  */
   4214 f32 camera_approach_f32_symmetric(f32 current, f32 target, f32 increment) {
   4215     f32 dist = target - current;
   4216 
   4217     if (increment < 0) {
   4218         increment = -1 * increment;
   4219     }
   4220     if (dist > 0) {
   4221         dist -= increment;
   4222         if (dist > 0) {
   4223             current = target - dist;
   4224         } else {
   4225             current = target;
   4226         }
   4227     } else {
   4228         dist += increment;
   4229         if (dist < 0) {
   4230             current = target - dist;
   4231         } else {
   4232             current = target;
   4233         }
   4234     }
   4235     return current;
   4236 }
   4237 
   4238 /**
   4239  * Generate a vector with all three values about zero. The
   4240  * three ranges determine how wide the range about zero.
   4241  */
   4242 void random_vec3s(Vec3s dst, s16 xRange, s16 yRange, s16 zRange) {
   4243     f32 randomFloat;
   4244     UNUSED u8 filler[4];
   4245     f32 tempXRange;
   4246     f32 tempYRange;
   4247     f32 tempZRange;
   4248 
   4249     randomFloat = random_float();
   4250     tempXRange = xRange;
   4251     dst[0] = randomFloat * tempXRange - tempXRange / 2;
   4252 
   4253     randomFloat = random_float();
   4254     tempYRange = yRange;
   4255     dst[1] = randomFloat * tempYRange - tempYRange / 2;
   4256 
   4257     randomFloat = random_float();
   4258     tempZRange = zRange;
   4259     dst[2] = randomFloat * tempZRange - tempZRange / 2;
   4260 }
   4261 
   4262 /**
   4263  * Decrease value by multiplying it by the distance from (`posX`, `posY`, `posZ`) to
   4264  * the camera divided by `maxDist`
   4265  *
   4266  * @return the reduced value
   4267  */
   4268 s16 reduce_by_dist_from_camera(s16 value, f32 maxDist, f32 posX, f32 posY, f32 posZ) {
   4269     Vec3f pos;
   4270     f32 dist;
   4271     s16 pitch;
   4272     s16 yaw;
   4273     s16 goalPitch;
   4274     s16 goalYaw;
   4275     s16 result = 0;
   4276     // Direction from pos to (Lakitu's) goalPos
   4277     f32 goalDX = gLakituState.goalPos[0] - posX;
   4278     f32 goalDY = gLakituState.goalPos[1] - posY;
   4279     f32 goalDZ = gLakituState.goalPos[2] - posZ;
   4280 
   4281     dist = sqrtf(goalDX * goalDX + goalDY * goalDY + goalDZ * goalDZ);
   4282     if (maxDist > dist) {
   4283         pos[0] = posX;
   4284         pos[1] = posY;
   4285         pos[2] = posZ;
   4286         vec3f_get_dist_and_angle(gLakituState.goalPos, pos, &dist, &pitch, &yaw);
   4287         if (dist < maxDist) {
   4288             calculate_angles(gLakituState.goalPos, gLakituState.goalFocus, &goalPitch, &goalYaw);
   4289             //! Must be same line to match on -O2
   4290             pitch -= goalPitch; yaw -= goalYaw;
   4291             dist -= 2000.f;
   4292             if (dist < 0.f) {
   4293                 dist = 0.f;
   4294             }
   4295             maxDist -= 2000.f;
   4296             if (maxDist < 2000.f) {
   4297                 maxDist = 2000.f;
   4298             }
   4299             result = value * (1.f - dist / maxDist);
   4300             if (pitch < -0x1800 || pitch > 0x400 ||
   4301                 yaw   < -0x1800 || yaw >   0x1800) {
   4302                 result /= 2;
   4303             }
   4304         }
   4305     }
   4306     return result;
   4307 }
   4308 
   4309 s32 clamp_positions_and_find_yaw(Vec3f pos, Vec3f origin, f32 xMax, f32 xMin, f32 zMax, f32 zMin) {
   4310     s16 yaw = gCamera->nextYaw;
   4311 
   4312     if (pos[0] >= xMax) {
   4313         pos[0] = xMax;
   4314     }
   4315     if (pos[0] <= xMin) {
   4316         pos[0] = xMin;
   4317     }
   4318     if (pos[2] >= zMax) {
   4319         pos[2] = zMax;
   4320     }
   4321     if (pos[2] <= zMin) {
   4322         pos[2] = zMin;
   4323     }
   4324     yaw = calculate_yaw(origin, pos);
   4325     return yaw;
   4326 }
   4327 
   4328 /**
   4329  * The yaw passed here is the yaw of the direction FROM Mario TO Lakitu.
   4330  *
   4331  * wallYaw always has 90 degrees added to it before this is called -- it's parallel to the wall.
   4332  *
   4333  * @return the new yaw from Mario to rotate towards.
   4334  *
   4335  * @warning this is jank. It actually returns the yaw that will rotate further INTO the wall. So, the
   4336  *          developers just add 180 degrees to the result.
   4337  */
   4338 s32 calc_avoid_yaw(s16 yawFromMario, s16 wallYaw) {
   4339     s16 yawDiff;
   4340     UNUSED u8 filler[34]; // Debug print buffer? ;)
   4341     UNUSED s32 unused1 = 0;
   4342     UNUSED s32 unused2 = 0;
   4343 
   4344     yawDiff = wallYaw - yawFromMario + DEGREES(90);
   4345 
   4346     if (yawDiff < 0) {
   4347         // Deflect to the right
   4348         yawFromMario = wallYaw;
   4349     } else {
   4350         // Note: this favors the left side if the wall is exactly perpendicular to the camera.
   4351         // Deflect to the left
   4352         yawFromMario = wallYaw + DEGREES(180);
   4353     }
   4354     return yawFromMario;
   4355 }
   4356 
   4357 /**
   4358  * Checks if `surf` is within the rect prism defined by xMax, yMax, and zMax
   4359  *
   4360  * @param surf surface to check
   4361  * @param xMax absolute-value max size in x, set to -1 to ignore
   4362  * @param yMax absolute-value max size in y, set to -1 to ignore
   4363  * @param zMax absolute-value max size in z, set to -1 to ignore
   4364  */
   4365 s32 is_surf_within_bounding_box(struct Surface *surf, f32 xMax, f32 yMax, f32 zMax) {
   4366     // Surface vertex coordinates
   4367     Vec3s sx;
   4368     Vec3s sy;
   4369     Vec3s sz;
   4370     // Max delta between x, y, and z
   4371     s16 dxMax = 0;
   4372     s16 dyMax = 0;
   4373     s16 dzMax = 0;
   4374     // Current deltas between x, y, and z
   4375     f32 dx;
   4376     f32 dy;
   4377     f32 dz;
   4378     UNUSED u8 filler[4];
   4379     s32 i;
   4380     s32 j;
   4381     // result
   4382     s32 smaller = FALSE;
   4383 
   4384     sx[0] = surf->vertex1[0];
   4385     sx[1] = surf->vertex2[0];
   4386     sx[2] = surf->vertex3[0];
   4387     sy[0] = surf->vertex1[1];
   4388     sy[1] = surf->vertex2[1];
   4389     sy[2] = surf->vertex3[1];
   4390     sz[0] = surf->vertex1[2];
   4391     sz[1] = surf->vertex2[2];
   4392     sz[2] = surf->vertex3[2];
   4393 
   4394     for (i = 0; i < 3; i++) {
   4395         j = i + 1;
   4396         if (j >= 3) {
   4397             j = 0;
   4398         }
   4399         dx = ABS(sx[i] - sx[j]);
   4400         if (dx > dxMax) {
   4401             dxMax = dx;
   4402         }
   4403         dy = ABS(sy[i] - sy[j]);
   4404         if (dy > dyMax) {
   4405             dyMax = dy;
   4406         }
   4407         dz = ABS(sz[i] - sz[j]);
   4408         if (dz > dzMax) {
   4409             dzMax = dz;
   4410         }
   4411     }
   4412     if (yMax != -1.f) {
   4413         if (dyMax < yMax) {
   4414             smaller = TRUE;
   4415         }
   4416     }
   4417     if (xMax != -1.f && zMax != -1.f) {
   4418         if (dxMax < xMax && dzMax < zMax) {
   4419             smaller = TRUE;
   4420         }
   4421     }
   4422     return smaller;
   4423 }
   4424 
   4425 /**
   4426  * Checks if `pos` is behind the surface, using the dot product.
   4427  *
   4428  * Because the function only uses `surf`s first vertex, some surfaces can shadow others.
   4429  */
   4430 s32 is_behind_surface(Vec3f pos, struct Surface *surf) {
   4431     s32 behindSurface = 0;
   4432     // Surface normal
   4433     f32 normX = (surf->vertex2[1] - surf->vertex1[1]) * (surf->vertex3[2] - surf->vertex2[2]) -
   4434                 (surf->vertex3[1] - surf->vertex2[1]) * (surf->vertex2[2] - surf->vertex1[2]);
   4435     f32 normY = (surf->vertex2[2] - surf->vertex1[2]) * (surf->vertex3[0] - surf->vertex2[0]) -
   4436                 (surf->vertex3[2] - surf->vertex2[2]) * (surf->vertex2[0] - surf->vertex1[0]);
   4437     f32 normZ = (surf->vertex2[0] - surf->vertex1[0]) * (surf->vertex3[1] - surf->vertex2[1]) -
   4438                 (surf->vertex3[0] - surf->vertex2[0]) * (surf->vertex2[1] - surf->vertex1[1]);
   4439     f32 dirX = surf->vertex1[0] - pos[0];
   4440     f32 dirY = surf->vertex1[1] - pos[1];
   4441     f32 dirZ = surf->vertex1[2] - pos[2];
   4442 
   4443     if (dirX * normX + dirY * normY + dirZ * normZ < 0) {
   4444         behindSurface = 1;
   4445     }
   4446     return behindSurface;
   4447 }
   4448 
   4449 /**
   4450  * Checks if the whole circular sector is behind the surface.
   4451  */
   4452 s32 is_range_behind_surface(Vec3f from, Vec3f to, struct Surface *surf, s16 range, s16 surfType) {
   4453     s32 behindSurface = TRUE;
   4454     s32 leftBehind = 0;
   4455     s32 rightBehind = 0;
   4456     UNUSED u8 filler[20];
   4457     f32 checkDist;
   4458     s16 checkPitch;
   4459     s16 checkYaw;
   4460     Vec3f checkPos;
   4461 
   4462     if (surf != NULL) {
   4463         if (surfType == -1 || surf->type != surfType) {
   4464             if (range == 0) {
   4465                 behindSurface = is_behind_surface(to, surf);
   4466             } else {
   4467                 vec3f_get_dist_and_angle(from, to, &checkDist, &checkPitch, &checkYaw);
   4468                 vec3f_set_dist_and_angle(from, checkPos, checkDist, checkPitch, checkYaw + range);
   4469                 leftBehind = is_behind_surface(checkPos, surf);
   4470                 vec3f_set_dist_and_angle(from, checkPos, checkDist, checkPitch, checkYaw - range);
   4471                 rightBehind = is_behind_surface(checkPos, surf);
   4472                 behindSurface = leftBehind * rightBehind;
   4473             }
   4474         }
   4475     }
   4476     return behindSurface;
   4477 }
   4478 
   4479 s32 is_mario_behind_surface(UNUSED struct Camera *c, struct Surface *surf) {
   4480     s32 behindSurface = is_behind_surface(sMarioCamState->pos, surf);
   4481 
   4482     return behindSurface;
   4483 }
   4484 
   4485 /**
   4486  * Calculates the distance between two points and sets a vector to a point
   4487  * scaled along a line between them. Typically, somewhere in the middle.
   4488  */
   4489 void scale_along_line(Vec3f dst, Vec3f from, Vec3f to, f32 scale) {
   4490     Vec3f tempVec;
   4491 
   4492     tempVec[0] = (to[0] - from[0]) * scale + from[0];
   4493     tempVec[1] = (to[1] - from[1]) * scale + from[1];
   4494     tempVec[2] = (to[2] - from[2]) * scale + from[2];
   4495     vec3f_copy(dst, tempVec);
   4496 }
   4497 /**
   4498  * Effectively created a rectangular prism defined by a vector starting at the center
   4499  * and extending to the corners. If the position is in this box, the function returns true.
   4500  */
   4501 s32 is_pos_in_bounds(Vec3f pos, Vec3f center, Vec3f bounds, s16 boundsYaw) {
   4502     s32 inBound = FALSE;
   4503     Vec3f rel;
   4504 
   4505     rel[0] = center[0] - pos[0];
   4506     rel[1] = center[1] - pos[1];
   4507     rel[2] = center[2] - pos[2];
   4508 
   4509     rotate_in_xz(rel, rel, boundsYaw);
   4510 
   4511     if (-bounds[0] < rel[0] && rel[0] < bounds[0] &&
   4512         -bounds[1] < rel[1] && rel[1] < bounds[1] &&
   4513         -bounds[2] < rel[2] && rel[2] < bounds[2]) {
   4514         inBound = TRUE;
   4515     }
   4516     return inBound;
   4517 }
   4518 
   4519 s16 calculate_pitch(Vec3f from, Vec3f to) {
   4520     f32 dx = to[0] - from[0];
   4521     f32 dy = to[1] - from[1];
   4522     f32 dz = to[2] - from[2];
   4523     s16 pitch = atan2s(sqrtf(dx * dx + dz * dz), dy);
   4524 
   4525     return pitch;
   4526 }
   4527 
   4528 s16 calculate_yaw(Vec3f from, Vec3f to) {
   4529     f32 dx = to[0] - from[0];
   4530     UNUSED f32 dy = to[1] - from[1];
   4531     f32 dz = to[2] - from[2];
   4532     s16 yaw = atan2s(dz, dx);
   4533 
   4534     return yaw;
   4535 }
   4536 
   4537 /**
   4538  * Calculates the pitch and yaw between two vectors.
   4539  */
   4540 void calculate_angles(Vec3f from, Vec3f to, s16 *pitch, s16 *yaw) {
   4541     f32 dx = to[0] - from[0];
   4542     f32 dy = to[1] - from[1];
   4543     f32 dz = to[2] - from[2];
   4544 
   4545     *pitch = atan2s(sqrtf(dx * dx + dz * dz), dy);
   4546     *yaw = atan2s(dz, dx);
   4547 }
   4548 
   4549 /**
   4550  * Finds the distance between two vectors.
   4551  */
   4552 f32 calc_abs_dist(Vec3f a, Vec3f b) {
   4553     f32 distX = b[0] - a[0];
   4554     f32 distY = b[1] - a[1];
   4555     f32 distZ = b[2] - a[2];
   4556     f32 distAbs = sqrtf(distX * distX + distY * distY + distZ * distZ);
   4557 
   4558     return distAbs;
   4559 }
   4560 
   4561 /**
   4562  * Finds the horizontal distance between two vectors.
   4563  */
   4564 f32 calc_hor_dist(Vec3f a, Vec3f b) {
   4565     f32 distX = b[0] - a[0];
   4566     f32 distZ = b[2] - a[2];
   4567     f32 distHor = sqrtf(distX * distX + distZ * distZ);
   4568 
   4569     return distHor;
   4570 }
   4571 
   4572 /**
   4573  * Rotates a vector in the horizontal plane and copies it to a new vector.
   4574  */
   4575 void rotate_in_xz(Vec3f dst, Vec3f src, s16 yaw) {
   4576     Vec3f tempVec;
   4577 
   4578     vec3f_copy(tempVec, src);
   4579     dst[0] = tempVec[2] * sins(yaw) + tempVec[0] * coss(yaw);
   4580     dst[1] = tempVec[1];
   4581     dst[2] = tempVec[2] * coss(yaw) - tempVec[0] * sins(yaw);
   4582 }
   4583 
   4584 /**
   4585  * Rotates a vector in the YZ plane and copies it to a new vector.
   4586  *
   4587  * Note: This function also flips the Z axis, so +Z moves forward, not backward like it would in world
   4588  * space. If possible, use vec3f_set_dist_and_angle()
   4589  */
   4590 void rotate_in_yz(Vec3f dst, Vec3f src, s16 pitch) {
   4591     Vec3f tempVec;
   4592 
   4593     vec3f_copy(tempVec, src);
   4594     dst[2] = -(tempVec[2] * coss(pitch) - tempVec[1] * sins(pitch));
   4595     dst[1] =   tempVec[2] * sins(pitch) + tempVec[1] * coss(pitch);
   4596     dst[0] =   tempVec[0];
   4597 }
   4598 
   4599 /**
   4600  * Start shaking the camera's pitch (up and down)
   4601  */
   4602 void set_camera_pitch_shake(s16 mag, s16 decay, s16 inc) {
   4603     if (gLakituState.shakeMagnitude[0] < mag) {
   4604         gLakituState.shakeMagnitude[0] = mag;
   4605         gLakituState.shakePitchDecay = decay;
   4606         gLakituState.shakePitchVel = inc;
   4607     }
   4608 }
   4609 
   4610 /**
   4611  * Start shaking the camera's yaw (side to side)
   4612  */
   4613 void set_camera_yaw_shake(s16 mag, s16 decay, s16 inc) {
   4614     if (ABS(mag) > ABS(gLakituState.shakeMagnitude[1])) {
   4615         gLakituState.shakeMagnitude[1] = mag;
   4616         gLakituState.shakeYawDecay = decay;
   4617         gLakituState.shakeYawVel = inc;
   4618     }
   4619 }
   4620 
   4621 /**
   4622  * Start shaking the camera's roll (rotate screen clockwise and counterclockwise)
   4623  */
   4624 void set_camera_roll_shake(s16 mag, s16 decay, s16 inc) {
   4625     if (gLakituState.shakeMagnitude[2] < mag) {
   4626         gLakituState.shakeMagnitude[2] = mag;
   4627         gLakituState.shakeRollDecay = decay;
   4628         gLakituState.shakeRollVel = inc;
   4629     }
   4630 }
   4631 
   4632 /**
   4633  * Start shaking the camera's pitch, but reduce `mag` by it's distance from the camera
   4634  */
   4635 void set_pitch_shake_from_point(s16 mag, s16 decay, s16 inc, f32 maxDist, f32 posX, f32 posY, f32 posZ) {
   4636     Vec3f pos;
   4637     f32 dist;
   4638     s16 dummyPitch;
   4639     s16 dummyYaw;
   4640 
   4641     pos[0] = posX;
   4642     pos[1] = posY;
   4643     pos[2] = posZ;
   4644     vec3f_get_dist_and_angle(gLakituState.goalPos, pos, &dist, &dummyPitch, &dummyYaw);
   4645     mag = reduce_by_dist_from_camera(mag, maxDist, posX, posY, posZ);
   4646     if (mag != 0) {
   4647         set_camera_pitch_shake(mag, decay, inc);
   4648     }
   4649 }
   4650 
   4651 /**
   4652  * Start shaking the camera's yaw, but reduce `mag` by it's distance from the camera
   4653  */
   4654 void set_yaw_shake_from_point(s16 mag, s16 decay, s16 inc, f32 maxDist, f32 posX, f32 posY, f32 posZ) {
   4655     Vec3f pos;
   4656     f32 dist;
   4657     s16 dummyPitch;
   4658     s16 dummyYaw;
   4659 
   4660     pos[0] = posX;
   4661     pos[1] = posY;
   4662     pos[2] = posZ;
   4663     vec3f_get_dist_and_angle(gLakituState.goalPos, pos, &dist, &dummyPitch, &dummyYaw);
   4664     mag = reduce_by_dist_from_camera(mag, maxDist, posX, posY, posZ);
   4665     if (mag != 0) {
   4666         set_camera_yaw_shake(mag, decay, inc);
   4667     }
   4668 }
   4669 
   4670 /**
   4671  * Update the shake offset by `increment`
   4672  */
   4673 void increment_shake_offset(s16 *offset, s16 increment) {
   4674     if (increment == -0x8000) {
   4675         *offset = (*offset & 0x8000) + 0xC000;
   4676     } else {
   4677         *offset += increment;
   4678     }
   4679 }
   4680 
   4681 /**
   4682  * Apply a vertical shake to the camera by adjusting its pitch
   4683  */
   4684 void shake_camera_pitch(Vec3f pos, Vec3f focus) {
   4685     f32 dist;
   4686     s16 pitch;
   4687     s16 yaw;
   4688 
   4689     if (gLakituState.shakeMagnitude[0] | gLakituState.shakeMagnitude[1]) {
   4690         vec3f_get_dist_and_angle(pos, focus, &dist, &pitch, &yaw);
   4691         pitch += gLakituState.shakeMagnitude[0] * sins(gLakituState.shakePitchPhase);
   4692         vec3f_set_dist_and_angle(pos, focus, dist, pitch, yaw);
   4693         increment_shake_offset(&gLakituState.shakePitchPhase, gLakituState.shakePitchVel);
   4694         if (camera_approach_s16_symmetric_bool(&gLakituState.shakeMagnitude[0], 0,
   4695                                                gLakituState.shakePitchDecay) == 0) {
   4696             gLakituState.shakePitchPhase = 0;
   4697         }
   4698     }
   4699 }
   4700 
   4701 /**
   4702  * Apply a horizontal shake to the camera by adjusting its yaw
   4703  */
   4704 void shake_camera_yaw(Vec3f pos, Vec3f focus) {
   4705     f32 dist;
   4706     s16 pitch;
   4707     s16 yaw;
   4708 
   4709     if (gLakituState.shakeMagnitude[1] != 0) {
   4710         vec3f_get_dist_and_angle(pos, focus, &dist, &pitch, &yaw);
   4711         yaw += gLakituState.shakeMagnitude[1] * sins(gLakituState.shakeYawPhase);
   4712         vec3f_set_dist_and_angle(pos, focus, dist, pitch, yaw);
   4713         increment_shake_offset(&gLakituState.shakeYawPhase, gLakituState.shakeYawVel);
   4714         if (camera_approach_s16_symmetric_bool(&gLakituState.shakeMagnitude[1], 0,
   4715                                                gLakituState.shakeYawDecay) == 0) {
   4716             gLakituState.shakeYawPhase = 0;
   4717         }
   4718     }
   4719 }
   4720 
   4721 /**
   4722  * Apply a rotational shake to the camera by adjusting its roll
   4723  */
   4724 void shake_camera_roll(s16 *roll) {
   4725     UNUSED u8 filler[8];
   4726 
   4727     if (gLakituState.shakeMagnitude[2] != 0) {
   4728         increment_shake_offset(&gLakituState.shakeRollPhase, gLakituState.shakeRollVel);
   4729         *roll += gLakituState.shakeMagnitude[2] * sins(gLakituState.shakeRollPhase);
   4730         if (camera_approach_s16_symmetric_bool(&gLakituState.shakeMagnitude[2], 0,
   4731                                                gLakituState.shakeRollDecay) == 0) {
   4732             gLakituState.shakeRollPhase = 0;
   4733         }
   4734     }
   4735 }
   4736 
   4737 /**
   4738  * Add an offset to the camera's yaw, used in levels that are inside a rectangular building, like the
   4739  * pyramid or TTC.
   4740  */
   4741 s32 offset_yaw_outward_radial(struct Camera *c, s16 areaYaw) {
   4742     s16 yawGoal = DEGREES(60);
   4743     s16 yaw = sModeOffsetYaw;
   4744     f32 distFromAreaCenter;
   4745     Vec3f areaCenter;
   4746     s16 dYaw;
   4747     switch (gCurrLevelArea) {
   4748         case AREA_TTC:
   4749             areaCenter[0] = c->areaCenX;
   4750             areaCenter[1] = sMarioCamState->pos[1];
   4751             areaCenter[2] = c->areaCenZ;
   4752             distFromAreaCenter = calc_abs_dist(areaCenter, sMarioCamState->pos);
   4753             if (800.f > distFromAreaCenter) {
   4754                 yawGoal = 0x3800;
   4755             }
   4756             break;
   4757         case AREA_SSL_PYRAMID:
   4758             // This mask splits the 360 degrees of yaw into 4 corners. It adds 45 degrees so that the yaw
   4759             // offset at the corner will be 0, but the yaw offset near the center will face more towards
   4760             // the direction Mario is running in.
   4761             yawGoal = (areaYaw & 0xC000) - areaYaw + DEGREES(45);
   4762             if (yawGoal < 0) {
   4763                 yawGoal = -yawGoal;
   4764             }
   4765             yawGoal = yawGoal / 32 * 48;
   4766             break;
   4767         case AREA_LLL_OUTSIDE:
   4768             yawGoal = 0;
   4769             break;
   4770     }
   4771     dYaw = gMarioStates[0].forwardVel / 32.f * 128.f;
   4772 
   4773     if (sAreaYawChange < 0) {
   4774         camera_approach_s16_symmetric_bool(&yaw, -yawGoal, dYaw);
   4775     }
   4776     if (sAreaYawChange > 0) {
   4777         camera_approach_s16_symmetric_bool(&yaw, yawGoal, dYaw);
   4778     }
   4779     // When the final yaw is out of [-60,60] degrees, approach yawGoal faster than dYaw will ever be,
   4780     // making the camera lock in one direction until yawGoal drops below 60 (or Mario presses a C button)
   4781     if (yaw < -DEGREES(60)) {
   4782         //! Maybe they meant to reverse yawGoal's sign?
   4783         camera_approach_s16_symmetric_bool(&yaw, -yawGoal, 0x200);
   4784     }
   4785     if (yaw > DEGREES(60)) {
   4786         //! Maybe they meant to reverse yawGoal's sign?
   4787         camera_approach_s16_symmetric_bool(&yaw, yawGoal, 0x200);
   4788     }
   4789     return yaw;
   4790 }
   4791 
   4792 /**
   4793  * Plays the background music that starts while peach reads the intro message.
   4794  */
   4795 void cutscene_intro_peach_play_message_music(void) {
   4796     play_music(SEQ_PLAYER_LEVEL, SEQUENCE_ARGS(4, SEQ_EVENT_PEACH_MESSAGE), 0);
   4797 }
   4798 
   4799 /**
   4800  * Plays the music that starts after peach fades and Lakitu appears.
   4801  */
   4802 void cutscene_intro_peach_play_lakitu_flying_music(void) {
   4803     play_music(SEQ_PLAYER_LEVEL, SEQUENCE_ARGS(15, SEQ_EVENT_CUTSCENE_INTRO), 0);
   4804 }
   4805 
   4806 void play_camera_buzz_if_cdown(void) {
   4807     if (gPlayer1Controller->buttonPressed & D_CBUTTONS) {
   4808         play_sound_button_change_blocked();
   4809     }
   4810 }
   4811 
   4812 void play_camera_buzz_if_cbutton(void) {
   4813     if (gPlayer1Controller->buttonPressed & CBUTTON_MASK) {
   4814         play_sound_button_change_blocked();
   4815     }
   4816 }
   4817 
   4818 void play_camera_buzz_if_c_sideways(void) {
   4819     if ((gPlayer1Controller->buttonPressed & L_CBUTTONS)
   4820         || (gPlayer1Controller->buttonPressed & R_CBUTTONS)) {
   4821         play_sound_button_change_blocked();
   4822     }
   4823 }
   4824 
   4825 void play_sound_cbutton_up(void) {
   4826     play_sound(SOUND_MENU_CAMERA_ZOOM_IN, gGlobalSoundSource);
   4827 }
   4828 
   4829 void play_sound_cbutton_down(void) {
   4830     play_sound(SOUND_MENU_CAMERA_ZOOM_OUT, gGlobalSoundSource);
   4831 }
   4832 
   4833 void play_sound_cbutton_side(void) {
   4834     play_sound(SOUND_MENU_CAMERA_TURN, gGlobalSoundSource);
   4835 }
   4836 
   4837 void play_sound_button_change_blocked(void) {
   4838     play_sound(SOUND_MENU_CAMERA_BUZZ, gGlobalSoundSource);
   4839 }
   4840 
   4841 void play_sound_rbutton_changed(void) {
   4842     play_sound(SOUND_MENU_CLICK_CHANGE_VIEW, gGlobalSoundSource);
   4843 }
   4844 
   4845 void play_sound_if_cam_switched_to_lakitu_or_mario(void) {
   4846     if (sCameraSoundFlags & CAM_SOUND_MARIO_ACTIVE) {
   4847         play_sound_rbutton_changed();
   4848     }
   4849     if (sCameraSoundFlags & CAM_SOUND_NORMAL_ACTIVE) {
   4850         play_sound_rbutton_changed();
   4851     }
   4852     sCameraSoundFlags &= ~(CAM_SOUND_MARIO_ACTIVE | CAM_SOUND_NORMAL_ACTIVE);
   4853 }
   4854 
   4855 /**
   4856  * Handles input for radial, outwards radial, parallel tracking, and 8 direction mode.
   4857  */
   4858 s32 radial_camera_input(struct Camera *c, UNUSED f32 unused) {
   4859     s16 dummy;
   4860 #ifdef AVOID_UB
   4861     dummy = 0;
   4862 #endif
   4863 
   4864     if ((gCameraMovementFlags & CAM_MOVE_ENTERED_ROTATE_SURFACE) || !(gCameraMovementFlags & CAM_MOVE_ROTATE)) {
   4865 
   4866         // If C-L or C-R are pressed, the camera is rotating
   4867         if (gPlayer1Controller->buttonPressed & (L_CBUTTONS | R_CBUTTONS)) {
   4868             gCameraMovementFlags &= ~CAM_MOVE_ENTERED_ROTATE_SURFACE;
   4869             //  @bug this does not clear the rotation flags set by the surface. It's possible to set
   4870             //       both ROTATE_LEFT and ROTATE_RIGHT, locking the camera.
   4871             //       Ex: If a surface set CAM_MOVE_ROTATE_RIGHT and the user presses C-R, it locks the
   4872             //       camera until a different mode is activated
   4873         }
   4874 
   4875         // Rotate Right and left
   4876         if (gPlayer1Controller->buttonPressed & R_CBUTTONS) {
   4877             if (sModeOffsetYaw > -0x800) {
   4878                 // The camera is now rotating right
   4879                 if (!(gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT)) {
   4880                     gCameraMovementFlags |= CAM_MOVE_ROTATE_RIGHT;
   4881                 }
   4882 
   4883                 if (c->mode == CAMERA_MODE_RADIAL) {
   4884                     // if > ~48 degrees, we're rotating for the second time.
   4885                     if (sModeOffsetYaw > 0x22AA) {
   4886                         s2ndRotateFlags |= CAM_MOVE_ROTATE_RIGHT;
   4887                     }
   4888 
   4889                     if (sModeOffsetYaw == DEGREES(105)) {
   4890                         play_sound_button_change_blocked();
   4891                     } else {
   4892                         play_sound_cbutton_side();
   4893                     }
   4894                 } else {
   4895                     if (sModeOffsetYaw == DEGREES(60)) {
   4896                         play_sound_button_change_blocked();
   4897                     } else {
   4898                         play_sound_cbutton_side();
   4899                     }
   4900                 }
   4901             } else {
   4902                 gCameraMovementFlags |= CAM_MOVE_RETURN_TO_MIDDLE;
   4903                 play_sound_cbutton_up();
   4904             }
   4905         }
   4906         if (gPlayer1Controller->buttonPressed & L_CBUTTONS) {
   4907             if (sModeOffsetYaw < 0x800) {
   4908                 if (!(gCameraMovementFlags & CAM_MOVE_ROTATE_LEFT)) {
   4909                     gCameraMovementFlags |= CAM_MOVE_ROTATE_LEFT;
   4910                 }
   4911 
   4912                 if (c->mode == CAMERA_MODE_RADIAL) {
   4913                     // if < ~48 degrees, we're rotating for the second time.
   4914                     if (sModeOffsetYaw < -0x22AA) {
   4915                         s2ndRotateFlags |= CAM_MOVE_ROTATE_LEFT;
   4916                     }
   4917 
   4918                     if (sModeOffsetYaw == DEGREES(-105)) {
   4919                         play_sound_button_change_blocked();
   4920                     } else {
   4921                         play_sound_cbutton_side();
   4922                     }
   4923                 } else {
   4924                     if (sModeOffsetYaw == DEGREES(-60)) {
   4925                         play_sound_button_change_blocked();
   4926                     } else {
   4927                         play_sound_cbutton_side();
   4928                     }
   4929                 }
   4930             } else {
   4931                 gCameraMovementFlags |= CAM_MOVE_RETURN_TO_MIDDLE;
   4932                 play_sound_cbutton_up();
   4933             }
   4934         }
   4935     }
   4936 
   4937     // Zoom in / enter C-Up
   4938     if (gPlayer1Controller->buttonPressed & U_CBUTTONS) {
   4939         if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   4940             gCameraMovementFlags &= ~CAM_MOVE_ZOOMED_OUT;
   4941             play_sound_cbutton_up();
   4942         } else {
   4943             set_mode_c_up(c);
   4944         }
   4945     }
   4946 
   4947     // Zoom out
   4948     if (gPlayer1Controller->buttonPressed & D_CBUTTONS) {
   4949         if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   4950             gCameraMovementFlags |= CAM_MOVE_ALREADY_ZOOMED_OUT;
   4951 #ifndef VERSION_JP
   4952             play_camera_buzz_if_cdown();
   4953 #endif
   4954         } else {
   4955             gCameraMovementFlags |= CAM_MOVE_ZOOMED_OUT;
   4956             play_sound_cbutton_down();
   4957         }
   4958     }
   4959 
   4960     //! returning uninitialized variable
   4961     return dummy;
   4962 }
   4963 
   4964 /**
   4965  * Starts a cutscene dialog. Only has an effect when `trigger` is 1
   4966  */
   4967 s32 trigger_cutscene_dialog(s32 trigger) {
   4968     s32 result = 0;
   4969     UNUSED struct Camera *c = gCamera;
   4970 
   4971     if (trigger == 1) {
   4972         start_object_cutscene_without_focus(CUTSCENE_READ_MESSAGE);
   4973     }
   4974     if (trigger == 2) {
   4975     }
   4976     return result;
   4977 }
   4978 
   4979 /**
   4980  * Updates the camera based on which C buttons are pressed this frame
   4981  */
   4982 void handle_c_button_movement(struct Camera *c) {
   4983     s16 cSideYaw;
   4984 
   4985     // Zoom in
   4986     if (gPlayer1Controller->buttonPressed & U_CBUTTONS) {
   4987         if (c->mode != CAMERA_MODE_FIXED && (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT)) {
   4988             gCameraMovementFlags &= ~CAM_MOVE_ZOOMED_OUT;
   4989             play_sound_cbutton_up();
   4990         } else {
   4991             set_mode_c_up(c);
   4992             if (sZeroZoomDist > gCameraZoomDist) {
   4993                 sZoomAmount = -gCameraZoomDist;
   4994             } else {
   4995                 sZoomAmount = gCameraZoomDist;
   4996             }
   4997         }
   4998     }
   4999     if (c->mode != CAMERA_MODE_FIXED) {
   5000         // Zoom out
   5001         if (gPlayer1Controller->buttonPressed & D_CBUTTONS) {
   5002             if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
   5003                 gCameraMovementFlags |= CAM_MOVE_ALREADY_ZOOMED_OUT;
   5004                 sZoomAmount = gCameraZoomDist + 400.f;
   5005 #ifndef VERSION_JP
   5006                 play_camera_buzz_if_cdown();
   5007 #endif
   5008             } else {
   5009                 gCameraMovementFlags |= CAM_MOVE_ZOOMED_OUT;
   5010                 sZoomAmount = gCameraZoomDist + 400.f;
   5011                 play_sound_cbutton_down();
   5012             }
   5013         }
   5014 
   5015         // Rotate left or right
   5016         cSideYaw = 0x1000;
   5017         if (gPlayer1Controller->buttonPressed & R_CBUTTONS) {
   5018             if (gCameraMovementFlags & CAM_MOVE_ROTATE_LEFT) {
   5019                 gCameraMovementFlags &= ~CAM_MOVE_ROTATE_LEFT;
   5020             } else {
   5021                 gCameraMovementFlags |= CAM_MOVE_ROTATE_RIGHT;
   5022                 if (sCSideButtonYaw == 0) {
   5023                     play_sound_cbutton_side();
   5024                 }
   5025                 sCSideButtonYaw = -cSideYaw;
   5026             }
   5027         }
   5028         if (gPlayer1Controller->buttonPressed & L_CBUTTONS) {
   5029             if (gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT) {
   5030                 gCameraMovementFlags &= ~CAM_MOVE_ROTATE_RIGHT;
   5031             } else {
   5032                 gCameraMovementFlags |= CAM_MOVE_ROTATE_LEFT;
   5033                 if (sCSideButtonYaw == 0) {
   5034                     play_sound_cbutton_side();
   5035                 }
   5036                 sCSideButtonYaw = cSideYaw;
   5037             }
   5038         }
   5039     }
   5040 }
   5041 
   5042 /**
   5043  * Zero the 10 cvars.
   5044  */
   5045 void clear_cutscene_vars(UNUSED struct Camera *c) {
   5046     s32 i;
   5047 
   5048     for (i = 0; i < 10; i++) {
   5049         sCutsceneVars[i].unused1 = 0;
   5050         vec3f_set(sCutsceneVars[i].point, 0.f, 0.f, 0.f);
   5051         vec3f_set(sCutsceneVars[i].unusedPoint, 0.f, 0.f, 0.f);
   5052         vec3s_set(sCutsceneVars[i].angle, 0, 0, 0);
   5053         sCutsceneVars[i].unused2 = 0;
   5054     }
   5055 }
   5056 
   5057 /**
   5058  * Start the cutscene, `cutscene`, if it is not already playing.
   5059  */
   5060 void start_cutscene(struct Camera *c, u8 cutscene) {
   5061     if (c->cutscene != cutscene) {
   5062         c->cutscene = cutscene;
   5063         clear_cutscene_vars(c);
   5064     }
   5065 }
   5066 
   5067 /**
   5068  * Look up the victory dance cutscene in sDanceCutsceneTable
   5069  *
   5070  * First the index entry is determined based on the course and the star that was just picked up
   5071  * Like the entries in sZoomOutAreaMasks, each entry represents two stars
   5072  * The current courses's 4 bits of the index entry are used as the actual index into sDanceCutsceneTable
   5073  *
   5074  * @return the victory cutscene to use
   5075  */
   5076 s32 determine_dance_cutscene(UNUSED struct Camera *c) {
   5077     u8 cutscene = 0;
   5078     u8 cutsceneIndex = 0;
   5079     u8 starIndex = (gLastCompletedStarNum - 1) / 2;
   5080     u8 courseNum = gCurrCourseNum;
   5081 
   5082     if (starIndex > 3) {
   5083         starIndex = 0;
   5084     }
   5085     if (courseNum > COURSE_MAX) {
   5086         courseNum = COURSE_NONE;
   5087     }
   5088     cutsceneIndex = sDanceCutsceneIndexTable[courseNum][starIndex];
   5089 
   5090     if (gLastCompletedStarNum & 1) {
   5091         // Odd stars take the lower four bytes
   5092         cutsceneIndex &= 0xF;
   5093     } else {
   5094         // Even stars use the upper four bytes
   5095         cutsceneIndex = cutsceneIndex >> 4;
   5096     }
   5097     cutscene = sDanceCutsceneTable[cutsceneIndex];
   5098     return cutscene;
   5099 }
   5100 
   5101 /**
   5102  * @return `pullResult` or `pushResult` depending on Mario's door action
   5103  */
   5104 u8 open_door_cutscene(u8 pullResult, u8 pushResult) {
   5105     s16 result;
   5106 
   5107     if (sMarioCamState->action == ACT_PULLING_DOOR) {
   5108         result = pullResult;
   5109     }
   5110     if (sMarioCamState->action == ACT_PUSHING_DOOR) {
   5111         result = pushResult;
   5112     }
   5113     return result;
   5114 }
   5115 
   5116 /**
   5117  * If no cutscenes are playing, determines if a cutscene should play based on Mario's action and
   5118  * cameraEvent
   5119  *
   5120  * @return the cutscene that should start, 0 if none
   5121  */
   5122 u8 get_cutscene_from_mario_status(struct Camera *c) {
   5123     UNUSED u8 filler1[4];
   5124     u8 cutscene = c->cutscene;
   5125     UNUSED u8 filler2[12];
   5126 
   5127     if (cutscene == 0) {
   5128         // A cutscene started by an object, if any, will start if nothing else happened
   5129         cutscene = sObjectCutscene;
   5130         sObjectCutscene = 0;
   5131         if (sMarioCamState->cameraEvent == CAM_EVENT_DOOR) {
   5132             switch (gCurrLevelArea) {
   5133                 case AREA_CASTLE_LOBBY:
   5134                     //! doorStatus is never DOOR_ENTER_LOBBY when cameraEvent == 6, because
   5135                     //! doorStatus is only used for the star door in the lobby, which uses
   5136                     //! ACT_ENTERING_STAR_DOOR
   5137                     if (c->mode == CAMERA_MODE_SPIRAL_STAIRS || c->mode == CAMERA_MODE_CLOSE
   5138                                                                  || c->doorStatus == DOOR_ENTER_LOBBY) {
   5139                         cutscene = open_door_cutscene(CUTSCENE_DOOR_PULL_MODE, CUTSCENE_DOOR_PUSH_MODE);
   5140                     } else {
   5141                         cutscene = open_door_cutscene(CUTSCENE_DOOR_PULL, CUTSCENE_DOOR_PUSH);
   5142                     }
   5143                     break;
   5144                 case AREA_BBH:
   5145                     //! Castle Lobby uses 0 to mean 'no special modes', but BBH uses 1...
   5146                     if (c->doorStatus == DOOR_LEAVING_SPECIAL) {
   5147                         cutscene = open_door_cutscene(CUTSCENE_DOOR_PULL, CUTSCENE_DOOR_PUSH);
   5148                     } else {
   5149                         cutscene = open_door_cutscene(CUTSCENE_DOOR_PULL_MODE, CUTSCENE_DOOR_PUSH_MODE);
   5150                     }
   5151                     break;
   5152                 default:
   5153                     cutscene = open_door_cutscene(CUTSCENE_DOOR_PULL, CUTSCENE_DOOR_PUSH);
   5154                     break;
   5155             }
   5156         }
   5157         if (sMarioCamState->cameraEvent == CAM_EVENT_DOOR_WARP) {
   5158             cutscene = CUTSCENE_DOOR_WARP;
   5159         }
   5160         if (sMarioCamState->cameraEvent == CAM_EVENT_CANNON) {
   5161             cutscene = CUTSCENE_ENTER_CANNON;
   5162         }
   5163         if (SURFACE_IS_PAINTING_WARP(sMarioGeometry.currFloorType)) {
   5164             cutscene = CUTSCENE_ENTER_PAINTING;
   5165         }
   5166         switch (sMarioCamState->action) {
   5167             case ACT_DEATH_EXIT:
   5168                 cutscene = CUTSCENE_DEATH_EXIT;
   5169                 break;
   5170             case ACT_EXIT_AIRBORNE:
   5171                 cutscene = CUTSCENE_EXIT_PAINTING_SUCC;
   5172                 break;
   5173             case ACT_SPECIAL_EXIT_AIRBORNE:
   5174                 if (gPrevLevel == LEVEL_BOWSER_1 || gPrevLevel == LEVEL_BOWSER_2
   5175                     || gPrevLevel == LEVEL_BOWSER_3) {
   5176                     cutscene = CUTSCENE_EXIT_BOWSER_SUCC;
   5177                 } else {
   5178                     cutscene = CUTSCENE_EXIT_SPECIAL_SUCC;
   5179                 }
   5180                 break;
   5181             case ACT_SPECIAL_DEATH_EXIT:
   5182                 if (gPrevLevel == LEVEL_BOWSER_1 || gPrevLevel == LEVEL_BOWSER_2
   5183                     || gPrevLevel == LEVEL_BOWSER_3) {
   5184                     cutscene = CUTSCENE_EXIT_BOWSER_DEATH;
   5185                 } else {
   5186                     cutscene = CUTSCENE_NONPAINTING_DEATH;
   5187                 }
   5188                 break;
   5189             case ACT_ENTERING_STAR_DOOR:
   5190                 if (c->doorStatus == DOOR_DEFAULT) {
   5191                     cutscene = CUTSCENE_SLIDING_DOORS_OPEN;
   5192                 } else {
   5193                     cutscene = CUTSCENE_DOOR_PULL_MODE;
   5194                 }
   5195                 break;
   5196             case ACT_UNLOCKING_KEY_DOOR:
   5197                 cutscene = CUTSCENE_UNLOCK_KEY_DOOR;
   5198                 break;
   5199             case ACT_WATER_DEATH:
   5200                 cutscene = CUTSCENE_WATER_DEATH;
   5201                 break;
   5202             case ACT_DEATH_ON_BACK:
   5203                 cutscene = CUTSCENE_DEATH_ON_BACK;
   5204                 break;
   5205             case ACT_DEATH_ON_STOMACH:
   5206                 cutscene = CUTSCENE_DEATH_ON_STOMACH;
   5207                 break;
   5208             case ACT_STANDING_DEATH:
   5209                 cutscene = CUTSCENE_STANDING_DEATH;
   5210                 break;
   5211             case ACT_SUFFOCATION:
   5212                 cutscene = CUTSCENE_SUFFOCATION_DEATH;
   5213                 break;
   5214             case ACT_QUICKSAND_DEATH:
   5215                 cutscene = CUTSCENE_QUICKSAND_DEATH;
   5216                 break;
   5217             case ACT_ELECTROCUTION:
   5218                 cutscene = CUTSCENE_STANDING_DEATH;
   5219                 break;
   5220             case ACT_STAR_DANCE_EXIT:
   5221                 cutscene = determine_dance_cutscene(c);
   5222                 break;
   5223             case ACT_STAR_DANCE_WATER:
   5224                 cutscene = determine_dance_cutscene(c);
   5225                 break;
   5226             case ACT_STAR_DANCE_NO_EXIT:
   5227                 cutscene = CUTSCENE_DANCE_DEFAULT;
   5228                 break;
   5229         }
   5230         switch (sMarioCamState->cameraEvent) {
   5231             case CAM_EVENT_START_INTRO:
   5232                 cutscene = CUTSCENE_INTRO_PEACH;
   5233                 break;
   5234             case CAM_EVENT_START_GRAND_STAR:
   5235                 cutscene = CUTSCENE_GRAND_STAR;
   5236                 break;
   5237             case CAM_EVENT_START_ENDING:
   5238                 cutscene = CUTSCENE_ENDING;
   5239                 break;
   5240             case CAM_EVENT_START_END_WAVING:
   5241                 cutscene = CUTSCENE_END_WAVING;
   5242                 break;
   5243             case CAM_EVENT_START_CREDITS:
   5244                 cutscene = CUTSCENE_CREDITS;
   5245                 break;
   5246         }
   5247     }
   5248     //! doorStatus is reset every frame. CameraTriggers need to constantly set doorStatus
   5249     c->doorStatus = DOOR_DEFAULT;
   5250 
   5251     return cutscene;
   5252 }
   5253 
   5254 /**
   5255  * Moves the camera when Mario has triggered a warp
   5256  */
   5257 void warp_camera(f32 displacementX, f32 displacementY, f32 displacementZ) {
   5258     Vec3f displacement;
   5259     struct MarioState *marioStates = &gMarioStates[0];
   5260     struct LinearTransitionPoint *start = &sModeInfo.transitionStart;
   5261     struct LinearTransitionPoint *end = &sModeInfo.transitionEnd;
   5262 
   5263     gCurrLevelArea = gCurrLevelNum * 16 + gCurrentArea->index;
   5264     displacement[0] = displacementX;
   5265     displacement[1] = displacementY;
   5266     displacement[2] = displacementZ;
   5267     vec3f_add(gLakituState.curPos, displacement);
   5268     vec3f_add(gLakituState.curFocus, displacement);
   5269     vec3f_add(gLakituState.goalPos, displacement);
   5270     vec3f_add(gLakituState.goalFocus, displacement);
   5271     marioStates->waterLevel += displacementY;
   5272 
   5273     vec3f_add(start->focus, displacement);
   5274     vec3f_add(start->pos, displacement);
   5275     vec3f_add(end->focus, displacement);
   5276     vec3f_add(end->pos, displacement);
   5277 }
   5278 
   5279 /**
   5280  * Make the camera's y coordinate approach `goal`,
   5281  * unless smooth movement is off, in which case the y coordinate is simply set to `goal`
   5282  */
   5283 void approach_camera_height(struct Camera *c, f32 goal, f32 inc) {
   5284     if (sStatusFlags & CAM_FLAG_SMOOTH_MOVEMENT) {
   5285         if (c->pos[1] < goal) {
   5286             if ((c->pos[1] += inc) > goal) {
   5287                 c->pos[1] = goal;
   5288             }
   5289         } else {
   5290             if ((c->pos[1] -= inc) < goal) {
   5291                 c->pos[1] = goal;
   5292             }
   5293         }
   5294     } else {
   5295         c->pos[1] = goal;
   5296     }
   5297 }
   5298 
   5299 void stub_camera_4(UNUSED s32 a, UNUSED s32 b, UNUSED s32 c, UNUSED s32 d) {
   5300 }
   5301 
   5302 /**
   5303  * Set the camera's focus to Mario's position, and add several relative offsets.
   5304  *
   5305  * @param leftRight offset to Mario's left/right, relative to his faceAngle
   5306  * @param yOff y offset
   5307  * @param forwBack offset to Mario's front/back, relative to his faceAngle
   5308  * @param yawOff offset to Mario's faceAngle, changes the direction of `leftRight` and `forwBack`
   5309  */
   5310 void set_focus_rel_mario(struct Camera *c, f32 leftRight, f32 yOff, f32 forwBack, s16 yawOff) {
   5311     s16 yaw;
   5312     UNUSED u16 unused;
   5313     f32 focFloorYOff;
   5314 
   5315     calc_y_to_curr_floor(&focFloorYOff, 1.f, 200.f, &focFloorYOff, 0.9f, 200.f);
   5316     yaw = sMarioCamState->faceAngle[1] + yawOff;
   5317     c->focus[2] = sMarioCamState->pos[2] + forwBack * coss(yaw) - leftRight * sins(yaw);
   5318     c->focus[0] = sMarioCamState->pos[0] + forwBack * sins(yaw) + leftRight * coss(yaw);
   5319     c->focus[1] = sMarioCamState->pos[1] + yOff + focFloorYOff;
   5320 }
   5321 
   5322 /**
   5323  * Set the camera's position to Mario's position, and add several relative offsets. Unused.
   5324  *
   5325  * @param leftRight offset to Mario's left/right, relative to his faceAngle
   5326  * @param yOff y offset
   5327  * @param forwBack offset to Mario's front/back, relative to his faceAngle
   5328  * @param yawOff offset to Mario's faceAngle, changes the direction of `leftRight` and `forwBack`
   5329  */
   5330 UNUSED static void unused_set_pos_rel_mario(struct Camera *c, f32 leftRight, f32 yOff, f32 forwBack, s16 yawOff) {
   5331     u16 yaw = sMarioCamState->faceAngle[1] + yawOff;
   5332 
   5333     c->pos[0] = sMarioCamState->pos[0] + forwBack * sins(yaw) + leftRight * coss(yaw);
   5334     c->pos[1] = sMarioCamState->pos[1] + yOff;
   5335     c->pos[2] = sMarioCamState->pos[2] + forwBack * coss(yaw) - leftRight * sins(yaw);
   5336 }
   5337 
   5338 /**
   5339  * Rotates the offset `to` according to the pitch and yaw values in `rotation`.
   5340  * Adds `from` to the rotated offset, and stores the result in `dst`.
   5341  *
   5342  * @warning Flips the Z axis, so that relative to `rotation`, -Z moves forwards and +Z moves backwards.
   5343  */
   5344 void offset_rotated(Vec3f dst, Vec3f from, Vec3f to, Vec3s rotation) {
   5345     Vec3f unusedCopy;
   5346     Vec3f pitchRotated;
   5347 
   5348     vec3f_copy(unusedCopy, from);
   5349 
   5350     // First rotate the direction by rotation's pitch
   5351     //! The Z axis is flipped here.
   5352     pitchRotated[2] = -(to[2] * coss(rotation[0]) - to[1] * sins(rotation[0]));
   5353     pitchRotated[1] =   to[2] * sins(rotation[0]) + to[1] * coss(rotation[0]);
   5354     pitchRotated[0] =   to[0];
   5355 
   5356     // Rotate again by rotation's yaw
   5357     dst[0] = from[0] + pitchRotated[2] * sins(rotation[1]) + pitchRotated[0] * coss(rotation[1]);
   5358     dst[1] = from[1] + pitchRotated[1];
   5359     dst[2] = from[2] + pitchRotated[2] * coss(rotation[1]) - pitchRotated[0] * sins(rotation[1]);
   5360 }
   5361 
   5362 /**
   5363  * Rotates the offset defined by (`xTo`, `yTo`, `zTo`) according to the pitch and yaw values in `rotation`.
   5364  * Adds `from` to the rotated offset, and stores the result in `dst`.
   5365  *
   5366  * @warning Flips the Z axis, so that relative to `rotation`, -Z moves forwards and +Z moves backwards.
   5367  */
   5368 void offset_rotated_coords(Vec3f dst, Vec3f from, Vec3s rotation, f32 xTo, f32 yTo, f32 zTo) {
   5369     Vec3f to;
   5370 
   5371     vec3f_set(to, xTo, yTo, zTo);
   5372     offset_rotated(dst, from, to, rotation);
   5373 }
   5374 
   5375 void determine_pushing_or_pulling_door(s16 *rotation) {
   5376     if (sMarioCamState->action == ACT_PULLING_DOOR) {
   5377         *rotation = 0;
   5378     } else {
   5379         *rotation = DEGREES(-180);
   5380     }
   5381 }
   5382 
   5383 /**
   5384  * Calculate Lakitu's next position and focus, according to gCamera's state,
   5385  * and store them in `newPos` and `newFoc`.
   5386  *
   5387  * @param newPos where Lakitu should fly towards this frame
   5388  * @param newFoc where Lakitu should look towards this frame
   5389  *
   5390  * @param curPos gCamera's pos this frame
   5391  * @param curFoc gCamera's foc this frame
   5392  *
   5393  * @param oldPos gCamera's pos last frame
   5394  * @param oldFoc gCamera's foc last frame
   5395  *
   5396  * @return Lakitu's next yaw, which is the same as the yaw passed in if no transition happened
   5397  */
   5398 s16 next_lakitu_state(Vec3f newPos, Vec3f newFoc, Vec3f curPos, Vec3f curFoc,
   5399                       Vec3f oldPos, Vec3f oldFoc, s16 yaw) {
   5400     s16 yawVelocity;
   5401     s16 pitchVelocity;
   5402     f32 distVelocity;
   5403     f32 goalDist;
   5404     UNUSED u8 filler1[4];
   5405     s16 goalPitch;
   5406     s16 goalYaw;
   5407     UNUSED u8 filler2[4];
   5408     f32 distTimer = sModeTransition.framesLeft;
   5409     s16 angleTimer = sModeTransition.framesLeft;
   5410     UNUSED s16 inTransition = FALSE;
   5411     Vec3f nextPos;
   5412     Vec3f nextFoc;
   5413     Vec3f startPos;
   5414     Vec3f startFoc;
   5415     s32 i;
   5416     f32 floorHeight;
   5417     struct Surface *floor;
   5418 
   5419     // If not transitioning, just use gCamera's current pos and foc
   5420     vec3f_copy(newPos, curPos);
   5421     vec3f_copy(newFoc, curFoc);
   5422 
   5423     if (sStatusFlags & CAM_FLAG_START_TRANSITION) {
   5424         for (i = 0; i < 3; i++) {
   5425             // Add Mario's displacement from this frame to the last frame's pos and focus
   5426             // Makes the transition start from where the camera would have moved
   5427             startPos[i] = oldPos[i] + sMarioCamState->pos[i] - sModeTransition.marioPos[i];
   5428             startFoc[i] = oldFoc[i] + sMarioCamState->pos[i] - sModeTransition.marioPos[i];
   5429         }
   5430 
   5431 
   5432         vec3f_get_dist_and_angle(curFoc, startFoc, &sModeTransition.focDist, &sModeTransition.focPitch,
   5433                                  &sModeTransition.focYaw);
   5434         vec3f_get_dist_and_angle(curFoc, startPos, &sModeTransition.posDist, &sModeTransition.posPitch,
   5435                                  &sModeTransition.posYaw);
   5436         sStatusFlags &= ~CAM_FLAG_START_TRANSITION;
   5437     }
   5438 
   5439     // Transition from the last mode to the current one
   5440     if (sModeTransition.framesLeft > 0) {
   5441         inTransition = TRUE;
   5442 
   5443         vec3f_get_dist_and_angle(curFoc, curPos, &goalDist, &goalPitch, &goalYaw);
   5444         distVelocity = ABS(goalDist - sModeTransition.posDist) / distTimer;
   5445         pitchVelocity = ABS(goalPitch - sModeTransition.posPitch) / angleTimer;
   5446         yawVelocity = ABS(goalYaw - sModeTransition.posYaw) / angleTimer;
   5447 
   5448         camera_approach_f32_symmetric_bool(&sModeTransition.posDist, goalDist, distVelocity);
   5449         camera_approach_s16_symmetric_bool(&sModeTransition.posYaw, goalYaw, yawVelocity);
   5450         camera_approach_s16_symmetric_bool(&sModeTransition.posPitch, goalPitch, pitchVelocity);
   5451         vec3f_set_dist_and_angle(curFoc, nextPos, sModeTransition.posDist, sModeTransition.posPitch,
   5452                                  sModeTransition.posYaw);
   5453 
   5454         vec3f_get_dist_and_angle(curPos, curFoc, &goalDist, &goalPitch, &goalYaw);
   5455         pitchVelocity = sModeTransition.focPitch / (s16) sModeTransition.framesLeft;
   5456         yawVelocity = sModeTransition.focYaw / (s16) sModeTransition.framesLeft;
   5457         distVelocity = sModeTransition.focDist / sModeTransition.framesLeft;
   5458 
   5459         camera_approach_s16_symmetric_bool(&sModeTransition.focPitch, goalPitch, pitchVelocity);
   5460         camera_approach_s16_symmetric_bool(&sModeTransition.focYaw, goalYaw, yawVelocity);
   5461         camera_approach_f32_symmetric_bool(&sModeTransition.focDist, 0, distVelocity);
   5462         vec3f_set_dist_and_angle(curFoc, nextFoc, sModeTransition.focDist, sModeTransition.focPitch,
   5463                                  sModeTransition.focYaw);
   5464 
   5465         vec3f_copy(newFoc, nextFoc);
   5466         vec3f_copy(newPos, nextPos);
   5467 
   5468         if (gCamera->cutscene != 0 || !(gCameraMovementFlags & CAM_MOVE_C_UP_MODE)) {
   5469             floorHeight = find_floor(newPos[0], newPos[1], newPos[2], &floor);
   5470             if (floorHeight != FLOOR_LOWER_LIMIT) {
   5471                 if ((floorHeight += 125.f) > newPos[1]) {
   5472                     newPos[1] = floorHeight;
   5473                 }
   5474             }
   5475             f32_find_wall_collision(&newPos[0], &newPos[1], &newPos[2], 0.f, 100.f);
   5476         }
   5477         sModeTransition.framesLeft--;
   5478         yaw = calculate_yaw(newFoc, newPos);
   5479     } else {
   5480         sModeTransition.posDist = 0.f;
   5481         sModeTransition.posPitch = 0;
   5482         sModeTransition.posYaw = 0;
   5483         sStatusFlags &= ~CAM_FLAG_TRANSITION_OUT_OF_C_UP;
   5484     }
   5485     vec3f_copy(sModeTransition.marioPos, sMarioCamState->pos);
   5486     return yaw;
   5487 }
   5488 
   5489 static UNUSED void stop_transitional_movement(void) {
   5490     sStatusFlags &= ~(CAM_FLAG_START_TRANSITION | CAM_FLAG_TRANSITION_OUT_OF_C_UP);
   5491     sModeTransition.framesLeft = 0;
   5492 }
   5493 
   5494 /**
   5495  * Start fixed camera mode, setting the base position to (`x`, `y`, `z`)
   5496  *
   5497  * @return TRUE if the base pos was updated
   5498  */
   5499 s32 set_camera_mode_fixed(struct Camera *c, s16 x, s16 y, s16 z) {
   5500     s32 basePosSet = FALSE;
   5501     f32 posX = x;
   5502     f32 posY = y;
   5503     f32 posZ = z;
   5504 
   5505     if (sFixedModeBasePosition[0] != posX || sFixedModeBasePosition[1] != posY
   5506         || sFixedModeBasePosition[2] != posZ) {
   5507         basePosSet = TRUE;
   5508         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5509     }
   5510     vec3f_set(sFixedModeBasePosition, posX, posY, posZ);
   5511     if (c->mode != CAMERA_MODE_FIXED) {
   5512         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5513         c->mode = CAMERA_MODE_FIXED;
   5514         vec3f_set(c->pos, sFixedModeBasePosition[0], sMarioCamState->pos[1],
   5515                   sFixedModeBasePosition[2]);
   5516     }
   5517     return basePosSet;
   5518 }
   5519 
   5520 void set_camera_mode_8_directions(struct Camera *c) {
   5521     if (c->mode != CAMERA_MODE_8_DIRECTIONS) {
   5522         c->mode = CAMERA_MODE_8_DIRECTIONS;
   5523         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5524         s8DirModeBaseYaw = 0;
   5525         s8DirModeYawOffset = 0;
   5526     }
   5527 }
   5528 
   5529 /**
   5530  * If the camera mode is not already the boss fight camera (camera with two foci)
   5531  * set it to be so.
   5532  */
   5533 void set_camera_mode_boss_fight(struct Camera *c) {
   5534     if (c->mode != CAMERA_MODE_BOSS_FIGHT) {
   5535         transition_to_camera_mode(c, CAMERA_MODE_BOSS_FIGHT, 15);
   5536         sModeOffsetYaw = c->nextYaw - DEGREES(45);
   5537     }
   5538 }
   5539 
   5540 void set_camera_mode_close_cam(u8 *mode) {
   5541     if (*mode != CAMERA_MODE_CLOSE) {
   5542         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5543         *mode = CAMERA_MODE_CLOSE;
   5544     }
   5545 }
   5546 
   5547 /**
   5548  * Change to radial mode.
   5549  * If the difference in yaw between pos -> Mario and pos > focus is < 90 degrees, transition.
   5550  * Otherwise jump to radial mode.
   5551  */
   5552 void set_camera_mode_radial(struct Camera *c, s16 transitionTime) {
   5553     Vec3f focus;
   5554     s16 yaw;
   5555 
   5556     focus[0] = c->areaCenX;
   5557     focus[1] = sMarioCamState->pos[1];
   5558     focus[2] = c->areaCenZ;
   5559     if (c->mode != CAMERA_MODE_RADIAL) {
   5560         yaw = calculate_yaw(focus, sMarioCamState->pos) - calculate_yaw(c->focus, c->pos) + DEGREES(90);
   5561         if (yaw > 0) {
   5562             transition_to_camera_mode(c, CAMERA_MODE_RADIAL, transitionTime);
   5563         } else {
   5564             c->mode = CAMERA_MODE_RADIAL;
   5565             sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5566         }
   5567         sModeOffsetYaw = 0;
   5568     }
   5569 }
   5570 
   5571 /**
   5572  * Start parallel tracking mode using the path `path`
   5573  */
   5574 void parallel_tracking_init(struct Camera *c, struct ParallelTrackingPoint *path) {
   5575     if (c->mode != CAMERA_MODE_PARALLEL_TRACKING) {
   5576         sParTrackPath = path;
   5577         sParTrackIndex = 0;
   5578         sParTrackTransOff.pos[0] = 0.f;
   5579         sParTrackTransOff.pos[1] = 0.f;
   5580         sParTrackTransOff.pos[2] = 0.f;
   5581         // Place the camera in the middle of the path
   5582         c->pos[0] = (sParTrackPath[0].pos[0] + sParTrackPath[1].pos[0]) / 2;
   5583         c->pos[1] = (sParTrackPath[0].pos[1] + sParTrackPath[1].pos[1]) / 2;
   5584         c->pos[2] = (sParTrackPath[0].pos[2] + sParTrackPath[1].pos[2]) / 2;
   5585         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5586         c->mode = CAMERA_MODE_PARALLEL_TRACKING;
   5587     }
   5588 }
   5589 
   5590 /**
   5591  * Set the fixed camera base pos depending on the current level area
   5592  */
   5593 void set_fixed_cam_axis_sa_lobby(UNUSED s16 preset) {
   5594     switch (gCurrLevelArea) {
   5595         case AREA_SA:
   5596             vec3f_set(sFixedModeBasePosition, 646.f, 143.f, -1513.f);
   5597             break;
   5598 
   5599         case AREA_CASTLE_LOBBY:
   5600             vec3f_set(sFixedModeBasePosition, -577.f, 143.f, 1443.f);
   5601             break;
   5602     }
   5603 }
   5604 
   5605 /**
   5606  * Block area-specific CameraTrigger and special surface modes.
   5607  * Generally, block area mode changes if:
   5608  *      Mario is wearing the metal cap, or at the water's surface, or the camera is in Mario mode
   5609  *
   5610  * However, if the level is WDW, DDD, or CotMC (levels that have metal cap and water):
   5611  *      Only block area mode changes if Mario is in a cannon,
   5612  *      or if the camera is in Mario mode and Mario is not swimming or in water with the metal cap
   5613  */
   5614 void check_blocking_area_processing(const u8 *mode) {
   5615     if (sMarioCamState->action & ACT_FLAG_METAL_WATER ||
   5616                         *mode == CAMERA_MODE_BEHIND_MARIO || *mode == CAMERA_MODE_WATER_SURFACE) {
   5617         sStatusFlags |= CAM_FLAG_BLOCK_AREA_PROCESSING;
   5618     }
   5619 
   5620     if (gCurrLevelNum == LEVEL_DDD || gCurrLevelNum == LEVEL_WDW || gCurrLevelNum == LEVEL_COTMC) {
   5621         sStatusFlags &= ~CAM_FLAG_BLOCK_AREA_PROCESSING;
   5622     }
   5623 
   5624     if ((*mode == CAMERA_MODE_BEHIND_MARIO &&
   5625             !(sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER))) ||
   5626          *mode == CAMERA_MODE_INSIDE_CANNON) {
   5627         sStatusFlags |= CAM_FLAG_BLOCK_AREA_PROCESSING;
   5628     }
   5629 }
   5630 
   5631 BAD_RETURN(s32) cam_rr_exit_building_side(struct Camera *c) {
   5632     set_camera_mode_8_directions(c);
   5633     s8DirModeBaseYaw = DEGREES(90);
   5634 }
   5635 
   5636 BAD_RETURN(s32) cam_rr_exit_building_top(struct Camera *c) {
   5637     set_camera_mode_8_directions(c);
   5638     if (c->pos[1] < 6343.f) {
   5639         c->pos[1] = 7543.f;
   5640         gLakituState.goalPos[1] = c->pos[1];
   5641         gLakituState.curPos[1] = c->pos[1];
   5642         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5643     }
   5644 }
   5645 
   5646 BAD_RETURN(s32) cam_rr_enter_building_window(struct Camera *c) {
   5647     if (c->mode != CAMERA_MODE_FIXED) {
   5648         set_camera_mode_fixed(c, -2974, 478, -3975);
   5649     }
   5650 }
   5651 
   5652 BAD_RETURN(s32) cam_rr_enter_building(struct Camera *c) {
   5653     if (c->mode != CAMERA_MODE_FIXED) {
   5654         set_camera_mode_fixed(c, -2953, 798, -3943);
   5655     }
   5656     // Prevent the camera from being above the roof
   5657     if (c->pos[1] > 6043.f) {
   5658         c->pos[1] = 6043.f;
   5659     }
   5660 }
   5661 
   5662 BAD_RETURN(s32) cam_rr_enter_building_side(struct Camera *c) {
   5663     if (c->mode != CAMERA_MODE_FIXED) {
   5664         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5665         c->mode = CAMERA_MODE_FIXED;
   5666     }
   5667 }
   5668 
   5669 /**
   5670  * Fix the camera in place as Mario gets exits out the MC cave into the waterfall.
   5671  */
   5672 BAD_RETURN(s32) cam_cotmc_exit_waterfall(UNUSED struct Camera *c) {
   5673     gCameraMovementFlags |= CAM_MOVE_FIX_IN_PLACE;
   5674 }
   5675 
   5676 /**
   5677  * Sets 8 directional mode and blocks the next trigger from processing.
   5678  * Activated when Mario is walking in front of the snowman's head.
   5679  */
   5680 BAD_RETURN(s32) cam_sl_snowman_head_8dir(struct Camera *c) {
   5681     sStatusFlags |= CAM_FLAG_BLOCK_AREA_PROCESSING;
   5682     transition_to_camera_mode(c, CAMERA_MODE_8_DIRECTIONS, 60);
   5683     s8DirModeBaseYaw = 0x1D27;
   5684 }
   5685 
   5686 /**
   5687  * Sets free roam mode in SL, called by a trigger that covers a large area and surrounds the 8 direction
   5688  * trigger.
   5689  */
   5690 BAD_RETURN(s32) cam_sl_free_roam(struct Camera *c) {
   5691     transition_to_camera_mode(c, CAMERA_MODE_FREE_ROAM, 60);
   5692 }
   5693 
   5694 /**
   5695  * Warps the camera underneath the floor, used in HMC to move under the elevator platforms
   5696  */
   5697 void move_camera_through_floor_while_descending(struct Camera *c, f32 height) {
   5698     UNUSED u8 filler[4];
   5699 
   5700     if ((sMarioGeometry.currFloorHeight < height - 100.f)
   5701         && (sMarioGeometry.prevFloorHeight > sMarioGeometry.currFloorHeight)) {
   5702         c->pos[1] = height - 400.f;
   5703         gLakituState.curPos[1] = height - 400.f;
   5704         gLakituState.goalPos[1] = height - 400.f;
   5705     }
   5706 }
   5707 
   5708 BAD_RETURN(s32) cam_hmc_enter_maze(struct Camera *c) {
   5709     s16 pitch, yaw;
   5710     f32 dist;
   5711 
   5712     if (c->pos[1] > -102.f) {
   5713         vec3f_get_dist_and_angle(c->focus, gLakituState.goalPos, &dist, &pitch, &yaw);
   5714         vec3f_set_dist_and_angle(c->focus, gLakituState.goalPos, 300.f, pitch, yaw);
   5715         gLakituState.goalPos[1] = -800.f;
   5716 #ifndef VERSION_JP
   5717         c->pos[1] = gLakituState.goalPos[1];
   5718         gLakituState.curPos[1] = gLakituState.goalPos[1];
   5719 #endif
   5720         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5721     }
   5722 }
   5723 
   5724 BAD_RETURN(s32) cam_hmc_elevator_black_hole(struct Camera *c) {
   5725     move_camera_through_floor_while_descending(c, 1536.f);
   5726 }
   5727 
   5728 BAD_RETURN(s32) cam_hmc_elevator_maze_emergency_exit(struct Camera *c) {
   5729     move_camera_through_floor_while_descending(c, 2355.f);
   5730 }
   5731 
   5732 BAD_RETURN(s32) cam_hmc_elevator_lake(struct Camera *c) {
   5733     move_camera_through_floor_while_descending(c, 1843.f);
   5734 }
   5735 
   5736 BAD_RETURN(s32) cam_hmc_elevator_maze(struct Camera *c) {
   5737     move_camera_through_floor_while_descending(c, 1843.f);
   5738 }
   5739 
   5740 /**
   5741  * Starts the "Enter Pyramid Top" cutscene.
   5742  */
   5743 BAD_RETURN(s32) cam_ssl_enter_pyramid_top(UNUSED struct Camera *c) {
   5744     start_object_cutscene_without_focus(CUTSCENE_ENTER_PYRAMID_TOP);
   5745 }
   5746 
   5747 /**
   5748  * Change to close mode in the center of the pyramid. Outside this trigger, the default mode is outwards
   5749  * radial.
   5750  */
   5751 BAD_RETURN(s32) cam_ssl_pyramid_center(struct Camera *c) {
   5752     sStatusFlags |= CAM_FLAG_BLOCK_AREA_PROCESSING;
   5753     transition_to_camera_mode(c, CAMERA_MODE_CLOSE, 90);
   5754 }
   5755 
   5756 /**
   5757  * Changes the mode back to outward radial in the boss room inside the pyramid.
   5758  */
   5759 BAD_RETURN(s32) cam_ssl_boss_room(struct Camera *c) {
   5760     sStatusFlags |= CAM_FLAG_BLOCK_AREA_PROCESSING;
   5761     transition_to_camera_mode(c, CAMERA_MODE_OUTWARD_RADIAL, 90);
   5762 }
   5763 
   5764 /**
   5765  * Moves the camera to through the tunnel by forcing sModeOffsetYaw
   5766  */
   5767 BAD_RETURN(s32) cam_thi_move_cam_through_tunnel(UNUSED struct Camera *c) {
   5768     if (sModeOffsetYaw < DEGREES(60)) {
   5769         sModeOffsetYaw = DEGREES(60);
   5770     }
   5771 }
   5772 
   5773 /**
   5774  * Aligns the camera to look through the tunnel
   5775  */
   5776 BAD_RETURN(s32) cam_thi_look_through_tunnel(UNUSED struct Camera *c) {
   5777     // ~82.5 degrees
   5778     if (sModeOffsetYaw > 0x3AAA) {
   5779         sModeOffsetYaw = 0x3AAA;
   5780     }
   5781 }
   5782 
   5783 /**
   5784  * Unused. Changes the camera to radial mode when Mario is on the tower.
   5785  *
   5786  * @see sCamBOB for bounds.
   5787  */
   5788 BAD_RETURN(s32) cam_bob_tower(struct Camera *c) {
   5789     sStatusFlags |= CAM_FLAG_BLOCK_AREA_PROCESSING;
   5790     transition_to_camera_mode(c, CAMERA_MODE_RADIAL, 90);
   5791 }
   5792 
   5793 /**
   5794  * Unused. Changes the camera to free roam mode when Mario is not climbing the tower.
   5795  *
   5796  * This is the only CameraTrigger event that uses the area == -1 feature:
   5797  * If this was used, it would be called by default in BoB.
   5798  *
   5799  * @see sCamBOB
   5800  */
   5801 BAD_RETURN(s32) cam_bob_default_free_roam(struct Camera *c) {
   5802     transition_to_camera_mode(c, CAMERA_MODE_FREE_ROAM, 90);
   5803 }
   5804 
   5805 /**
   5806  * Starts the pool entrance cutscene if Mario is not exiting the pool.
   5807  * Used in both the castle and HMC.
   5808  */
   5809 BAD_RETURN(s32) cam_castle_hmc_start_pool_cutscene(struct Camera *c) {
   5810     if ((sMarioCamState->action != ACT_SPECIAL_DEATH_EXIT)
   5811         && (sMarioCamState->action != ACT_SPECIAL_EXIT_AIRBORNE)) {
   5812         start_cutscene(c, CUTSCENE_ENTER_POOL);
   5813     }
   5814 }
   5815 
   5816 /**
   5817  * Sets the fixed mode pos offset so that the camera faces the doorway when Mario is near the entrance
   5818  * to the castle lobby
   5819  */
   5820 BAD_RETURN(s32) cam_castle_lobby_entrance(UNUSED struct Camera *c) {
   5821     vec3f_set(sCastleEntranceOffset, -813.f - sFixedModeBasePosition[0],
   5822               378.f - sFixedModeBasePosition[1], 1103.f - sFixedModeBasePosition[2]);
   5823 }
   5824 
   5825 /**
   5826  * Make the camera look up the stairs from the 2nd to 3rd floor of the castle
   5827  */
   5828 BAD_RETURN(s32) cam_castle_look_upstairs(struct Camera *c) {
   5829     struct Surface *floor;
   5830     f32 floorHeight = find_floor(c->pos[0], c->pos[1], c->pos[2], &floor);
   5831 
   5832     // If Mario is on the first few steps, fix the camera pos, making it look up
   5833     if ((sMarioGeometry.currFloorHeight > 1229.f) && (floorHeight < 1229.f)
   5834         && (sCSideButtonYaw == 0)) {
   5835         vec3f_set(c->pos, -227.f, 1425.f, 1533.f);
   5836     }
   5837 }
   5838 
   5839 /**
   5840  * Make the camera look down the stairs towards the basement star door
   5841  */
   5842 BAD_RETURN(s32) cam_castle_basement_look_downstairs(struct Camera *c) {
   5843     struct Surface *floor;
   5844     f32 floorHeight = find_floor(c->pos[0], c->pos[1], c->pos[2], &floor);
   5845 
   5846     // Fix the camera pos, making it look downwards. Only active on the top few steps
   5847     if ((floorHeight > -110.f) && (sCSideButtonYaw == 0)) {
   5848         vec3f_set(c->pos, -980.f, 249.f, -1398.f);
   5849     }
   5850 }
   5851 
   5852 /**
   5853  * Enter the fixed-mode castle lobby. A trigger for this is placed in every entrance so that the camera
   5854  * changes to fixed mode.
   5855  */
   5856 BAD_RETURN(s32) cam_castle_enter_lobby(struct Camera *c) {
   5857     if (c->mode != CAMERA_MODE_FIXED) {
   5858         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5859         set_fixed_cam_axis_sa_lobby(c->mode);
   5860         c->mode = CAMERA_MODE_FIXED;
   5861     }
   5862 }
   5863 
   5864 /**
   5865  * Starts spiral stairs mode.
   5866  */
   5867 BAD_RETURN(s32) cam_castle_enter_spiral_stairs(struct Camera *c) {
   5868     transition_to_camera_mode(c, CAMERA_MODE_SPIRAL_STAIRS, 20);
   5869 }
   5870 
   5871 /**
   5872  * unused, starts close mode if the camera is in spiral stairs mode.
   5873  * This was replaced with cam_castle_close_mode
   5874  */
   5875 static UNUSED BAD_RETURN(s32) cam_castle_leave_spiral_stairs(struct Camera *c) {
   5876     if (c->mode == CAMERA_MODE_SPIRAL_STAIRS) {
   5877         transition_to_camera_mode(c, CAMERA_MODE_CLOSE, 30);
   5878     } else {
   5879         set_camera_mode_close_cam(&c->mode);
   5880     }
   5881 }
   5882 
   5883 /**
   5884  * The default mode when outside of the lobby and spiral staircase. A trigger for this is placed at
   5885  * every door leaving the lobby and spiral staircase.
   5886  */
   5887 BAD_RETURN(s32) cam_castle_close_mode(struct Camera *c) {
   5888     set_camera_mode_close_cam(&c->mode);
   5889 }
   5890 
   5891 /**
   5892  * Functions the same as cam_castle_close_mode, but sets doorStatus so that the camera will enter
   5893  * fixed-mode when Mario leaves the room.
   5894  */
   5895 BAD_RETURN(s32) cam_castle_leave_lobby_sliding_door(struct Camera *c) {
   5896     cam_castle_close_mode(c);
   5897     c->doorStatus = DOOR_ENTER_LOBBY;
   5898 }
   5899 
   5900 /**
   5901  * Just calls cam_castle_enter_lobby
   5902  */
   5903 BAD_RETURN(s32) cam_castle_enter_lobby_sliding_door(struct Camera *c) {
   5904     cam_castle_enter_lobby(c);
   5905 }
   5906 
   5907 BAD_RETURN(s32) cam_bbh_room_6(struct Camera *c) {
   5908     parallel_tracking_init(c, sBBHLibraryParTrackPath);
   5909 }
   5910 
   5911 BAD_RETURN(s32) cam_bbh_fall_off_roof(struct Camera *c) {
   5912     set_camera_mode_close_cam(&c->mode);
   5913 }
   5914 
   5915 BAD_RETURN(s32) cam_bbh_fall_into_pool(struct Camera *c) {
   5916     Vec3f dir;
   5917     set_camera_mode_close_cam(&c->mode);
   5918     vec3f_set(dir, 0.f, 0.f, 300.f);
   5919     offset_rotated(gLakituState.goalPos, sMarioCamState->pos, dir, sMarioCamState->faceAngle);
   5920     gLakituState.goalPos[1] = -2300.f;
   5921     vec3f_copy(c->pos, gLakituState.goalPos);
   5922     sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   5923 }
   5924 
   5925 BAD_RETURN(s32) cam_bbh_room_1(struct Camera *c) {
   5926     set_camera_mode_fixed(c, 956, 440, 1994);
   5927 }
   5928 
   5929 BAD_RETURN(s32) cam_bbh_leave_front_door(struct Camera *c) {
   5930     c->doorStatus = DOOR_LEAVING_SPECIAL;
   5931     cam_bbh_room_1(c);
   5932 }
   5933 
   5934 BAD_RETURN(s32) cam_bbh_room_2_lower(struct Camera *c) {
   5935     set_camera_mode_fixed(c, 2591, 400, 1284);
   5936 }
   5937 
   5938 BAD_RETURN(s32) cam_bbh_room_4(struct Camera *c) {
   5939     set_camera_mode_fixed(c, 3529, 340, -1384);
   5940 }
   5941 
   5942 BAD_RETURN(s32) cam_bbh_room_8(struct Camera *c) {
   5943     set_camera_mode_fixed(c, -500, 740, -1306);
   5944 }
   5945 
   5946 /**
   5947  * In BBH's room 5's library (the first floor room with the vanish cap/boo painting)
   5948  * set the camera mode to fixed and position to (-2172, 200, 675)
   5949  */
   5950 BAD_RETURN(s32) cam_bbh_room_5_library(struct Camera *c) {
   5951     set_camera_mode_fixed(c, -2172, 200, 675);
   5952 }
   5953 
   5954 /**
   5955  * In BBH's room 5 (the first floor room with the vanish cap/boo painting)
   5956  * set the camera mode to to the hidden room's position
   5957  * if coming from the library.
   5958  */
   5959 BAD_RETURN(s32) cam_bbh_room_5_library_to_hidden_transition(struct Camera *c) {
   5960     if (set_camera_mode_fixed(c, -2172, 200, 675) == 1) {
   5961         transition_next_state(c, 20);
   5962     }
   5963 }
   5964 
   5965 BAD_RETURN(s32) cam_bbh_room_5_hidden_to_library_transition(struct Camera *c) {
   5966     if (set_camera_mode_fixed(c, -1542, 320, -307) == 1) {
   5967         transition_next_state(c, 20);
   5968     }
   5969 }
   5970 
   5971 BAD_RETURN(s32) cam_bbh_room_5_hidden(struct Camera *c) {
   5972     c->doorStatus = DOOR_LEAVING_SPECIAL;
   5973     set_camera_mode_fixed(c, -1542, 320, -307);
   5974 }
   5975 
   5976 BAD_RETURN(s32) cam_bbh_room_3(struct Camera *c) {
   5977     set_camera_mode_fixed(c, -1893, 320, 2327);
   5978 }
   5979 
   5980 BAD_RETURN(s32) cam_bbh_room_7_mr_i(struct Camera *c) {
   5981     set_camera_mode_fixed(c, 1371, 360, -1302);
   5982 }
   5983 
   5984 BAD_RETURN(s32) cam_bbh_room_7_mr_i_to_coffins_transition(struct Camera *c) {
   5985     if (set_camera_mode_fixed(c, 1371, 360, -1302) == 1) {
   5986         transition_next_state(c, 20);
   5987     }
   5988 }
   5989 
   5990 BAD_RETURN(s32) cam_bbh_room_7_coffins_to_mr_i_transition(struct Camera *c) {
   5991     if (set_camera_mode_fixed(c, 2115, 260, -772) == 1) {
   5992         transition_next_state(c, 20);
   5993     }
   5994 }
   5995 
   5996 BAD_RETURN(s32) cam_bbh_elevator_room_lower(struct Camera *c) {
   5997     c->doorStatus = DOOR_LEAVING_SPECIAL;
   5998     set_camera_mode_close_cam(&c->mode);
   5999 }
   6000 
   6001 BAD_RETURN(s32) cam_bbh_room_0_back_entrance(struct Camera *c) {
   6002     set_camera_mode_close_cam(&c->mode);
   6003 }
   6004 
   6005 BAD_RETURN(s32) cam_bbh_elevator(struct Camera *c) {
   6006     if (c->mode == CAMERA_MODE_FIXED) {
   6007         set_camera_mode_close_cam(&c->mode);
   6008         c->pos[1] = -405.f;
   6009         gLakituState.goalPos[1] = -405.f;
   6010     }
   6011 }
   6012 
   6013 BAD_RETURN(s32) cam_bbh_room_12_upper(struct Camera *c) {
   6014     c->doorStatus = DOOR_LEAVING_SPECIAL;
   6015     set_camera_mode_fixed(c, -2932, 296, 4429);
   6016 }
   6017 
   6018 BAD_RETURN(s32) cam_bbh_enter_front_door(struct Camera *c) {
   6019     set_camera_mode_close_cam(&c->mode);
   6020 }
   6021 
   6022 BAD_RETURN(s32) cam_bbh_room_2_library(struct Camera *c) {
   6023     set_camera_mode_fixed(c, 3493, 440, 617);
   6024 }
   6025 
   6026 BAD_RETURN(s32) cam_bbh_room_2_library_to_trapdoor_transition(struct Camera *c) {
   6027     if (set_camera_mode_fixed(c, 3493, 440, 617) == 1) {
   6028         transition_next_state(c, 20);
   6029     }
   6030 }
   6031 
   6032 BAD_RETURN(s32) cam_bbh_room_2_trapdoor(struct Camera *c) {
   6033     set_camera_mode_fixed(c, 3502, 440, 1217);
   6034 }
   6035 
   6036 BAD_RETURN(s32) cam_bbh_room_2_trapdoor_transition(struct Camera *c) {
   6037     if (set_camera_mode_fixed(c, 3502, 440, 1217) == 1) {
   6038         transition_next_state(c, 20);
   6039     }
   6040 }
   6041 
   6042 BAD_RETURN(s32) cam_bbh_room_9_attic(struct Camera *c) {
   6043     set_camera_mode_fixed(c, -670, 460, 372);
   6044 }
   6045 
   6046 BAD_RETURN(s32) cam_bbh_room_9_attic_transition(struct Camera *c) {
   6047     if (set_camera_mode_fixed(c, -670, 460, 372) == 1) {
   6048         transition_next_state(c, 20);
   6049     }
   6050 }
   6051 
   6052 BAD_RETURN(s32) cam_bbh_room_9_mr_i_transition(struct Camera *c) {
   6053     if (set_camera_mode_fixed(c, 131, 380, -263) == 1) {
   6054         transition_next_state(c, 20);
   6055     }
   6056 }
   6057 
   6058 BAD_RETURN(s32) cam_bbh_room_13_balcony(struct Camera *c) {
   6059     set_camera_mode_fixed(c, 210, 420, 3109);
   6060 }
   6061 
   6062 BAD_RETURN(s32) cam_bbh_room_0(struct Camera *c) {
   6063     c->doorStatus = DOOR_LEAVING_SPECIAL;
   6064     set_camera_mode_fixed(c, -204, 807, 204);
   6065 }
   6066 
   6067 BAD_RETURN(s32) cam_ccm_enter_slide_shortcut(UNUSED struct Camera *c) {
   6068     sStatusFlags |= CAM_FLAG_CCM_SLIDE_SHORTCUT;
   6069 }
   6070 
   6071 BAD_RETURN(s32) cam_ccm_leave_slide_shortcut(UNUSED struct Camera *c) {
   6072     sStatusFlags &= ~CAM_FLAG_CCM_SLIDE_SHORTCUT;
   6073 }
   6074 
   6075 /**
   6076  * Apply any modes that are triggered by special floor surface types
   6077  */
   6078 u32 surface_type_modes(struct Camera *c) {
   6079     u32 modeChanged = 0;
   6080 
   6081     switch (sMarioGeometry.currFloorType) {
   6082         case SURFACE_CLOSE_CAMERA:
   6083             transition_to_camera_mode(c, CAMERA_MODE_CLOSE, 90);
   6084             modeChanged++;
   6085             break;
   6086 
   6087         case SURFACE_CAMERA_FREE_ROAM:
   6088             transition_to_camera_mode(c, CAMERA_MODE_FREE_ROAM, 90);
   6089             modeChanged++;
   6090             break;
   6091 
   6092         case SURFACE_NO_CAM_COL_SLIPPERY:
   6093             transition_to_camera_mode(c, CAMERA_MODE_CLOSE, 90);
   6094             modeChanged++;
   6095             break;
   6096     }
   6097     return modeChanged;
   6098 }
   6099 
   6100 /**
   6101  * Set the camera mode to `mode` if Mario is not standing on a special surface
   6102  */
   6103 u32 set_mode_if_not_set_by_surface(struct Camera *c, u8 mode) {
   6104     u32 modeChanged = 0;
   6105     modeChanged = surface_type_modes(c);
   6106 
   6107     if ((modeChanged == 0) && (mode != 0)) {
   6108         transition_to_camera_mode(c, mode, 90);
   6109     }
   6110 
   6111     return modeChanged;
   6112 }
   6113 
   6114 /**
   6115  * Used in THI, check if Mario is standing on any of the special surfaces in that area
   6116  */
   6117 void surface_type_modes_thi(struct Camera *c) {
   6118     switch (sMarioGeometry.currFloorType) {
   6119         case SURFACE_CLOSE_CAMERA:
   6120             if (c->mode != CAMERA_MODE_CLOSE) {
   6121                 transition_to_camera_mode(c, CAMERA_MODE_FREE_ROAM, 90);
   6122             }
   6123             break;
   6124 
   6125         case SURFACE_CAMERA_FREE_ROAM:
   6126             if (c->mode != CAMERA_MODE_CLOSE) {
   6127                 transition_to_camera_mode(c, CAMERA_MODE_FREE_ROAM, 90);
   6128             }
   6129             break;
   6130 
   6131         case SURFACE_NO_CAM_COL_SLIPPERY:
   6132             if (c->mode != CAMERA_MODE_CLOSE) {
   6133                 transition_to_camera_mode(c, CAMERA_MODE_FREE_ROAM, 90);
   6134             }
   6135             break;
   6136 
   6137         case SURFACE_CAMERA_8_DIR:
   6138             transition_to_camera_mode(c, CAMERA_MODE_8_DIRECTIONS, 90);
   6139             break;
   6140 
   6141         default:
   6142             transition_to_camera_mode(c, CAMERA_MODE_RADIAL, 90);
   6143     }
   6144 }
   6145 
   6146 /**
   6147  * Terminates a list of CameraTriggers.
   6148  */
   6149 #define NULL_TRIGGER                                                                                    \
   6150     { 0, NULL, 0, 0, 0, 0, 0, 0, 0 }
   6151 
   6152 /**
   6153  * The SL triggers operate camera behavior in front of the snowman who blows air.
   6154  * The first sets a 8 direction mode, while the latter (which encompasses the former)
   6155  * sets free roam mode.
   6156  *
   6157  * This behavior is exploitable, since the ranges assume that Mario must pass through the latter on
   6158  * exit. Using hyperspeed, the earlier area can be directly exited from, keeping the changes it applies.
   6159  */
   6160 struct CameraTrigger sCamSL[] = {
   6161     { 1, cam_sl_snowman_head_8dir, 1119, 3584, 1125, 1177, 358, 358, -0x1D27 },
   6162     // This trigger surrounds the previous one
   6163     { 1, cam_sl_free_roam, 1119, 3584, 1125, 4096, 4096, 4096, -0x1D27 },
   6164     NULL_TRIGGER
   6165 };
   6166 
   6167 /**
   6168  * The THI triggers are specifically for the tunnel near the start of the Huge Island.
   6169  * The first helps the camera from getting stuck on the starting side, the latter aligns with the
   6170  * tunnel. Both sides achieve their effect by editing the camera yaw.
   6171  */
   6172 struct CameraTrigger sCamTHI[] = {
   6173     { 1, cam_thi_move_cam_through_tunnel, -4609, -2969, 6448, 100, 300, 300, 0 },
   6174     { 1, cam_thi_look_through_tunnel,     -4809, -2969, 6448, 100, 300, 300, 0 },
   6175     NULL_TRIGGER
   6176 };
   6177 
   6178 /**
   6179  * The HMC triggers are mostly for warping the camera below platforms, but the second trigger is used to
   6180  * start the cutscene for entering the CotMC pool.
   6181  */
   6182 struct CameraTrigger sCamHMC[] = {
   6183     { 1, cam_hmc_enter_maze, 1996, 102, 0, 205, 100, 205, 0 },
   6184     { 1, cam_castle_hmc_start_pool_cutscene, 3350, -4689, 4800, 600, 50, 600, 0 },
   6185     { 1, cam_hmc_elevator_black_hole, -3278, 1236, 1379, 358, 200, 358, 0 },
   6186     { 1, cam_hmc_elevator_maze_emergency_exit, -2816, 2055, -2560, 358, 200, 358, 0 },
   6187     { 1, cam_hmc_elevator_lake, -3532, 1543, -7040, 358, 200, 358, 0 },
   6188     { 1, cam_hmc_elevator_maze, -972, 1543, -7347, 358, 200, 358, 0 },
   6189     NULL_TRIGGER
   6190 };
   6191 
   6192 /**
   6193  * The SSL triggers are for starting the enter pyramid top cutscene,
   6194  * setting close mode in the middle of the pyramid, and setting the boss fight camera mode to outward
   6195  * radial.
   6196  */
   6197 struct CameraTrigger sCamSSL[] = {
   6198     { 1, cam_ssl_enter_pyramid_top, -2048, 1080, -1024, 150, 150, 150, 0 },
   6199     { 2, cam_ssl_pyramid_center, 0, -104, -104, 1248, 1536, 2950, 0 },
   6200     { 2, cam_ssl_pyramid_center, 0, 2500, 256, 515, 5000, 515, 0 },
   6201     { 3, cam_ssl_boss_room, 0, -1534, -2040, 1000, 800, 1000, 0 },
   6202     NULL_TRIGGER
   6203 };
   6204 
   6205 /**
   6206  * The RR triggers are for changing between fixed and 8 direction mode when entering / leaving the building at
   6207  * the end of the ride.
   6208  */
   6209 struct CameraTrigger sCamRR[] = {
   6210     { 1, cam_rr_exit_building_side, -4197, 3819, -3087, 1769, 1490, 342, 0 },
   6211     { 1, cam_rr_enter_building_side, -4197, 3819, -3771, 769, 490, 342, 0 },
   6212     { 1, cam_rr_enter_building_window, -5603, 4834, -5209, 300, 600, 591, 0 },
   6213     { 1, cam_rr_enter_building, -2609, 3730, -5463, 300, 650, 577, 0 },
   6214     { 1, cam_rr_exit_building_top, -4196, 7343, -5155, 4500, 1000, 4500, 0 },
   6215     { 1, cam_rr_enter_building, -4196, 6043, -5155, 500, 300, 500, 0 },
   6216     NULL_TRIGGER,
   6217 };
   6218 
   6219 /**
   6220  * These triggers are unused, but because the first trigger surrounds the BoB tower and activates radial
   6221  * mode (which is called "tower mode" in the patent), it's speculated they belonged to BoB.
   6222  *
   6223  * This table contains the only instance of a CameraTrigger with an area set to -1, and it sets the mode
   6224  * to free_roam when Mario is not walking up the tower.
   6225  */
   6226 struct CameraTrigger sCamBOB[] = {
   6227     {  1, cam_bob_tower, 2468, 2720, -4608, 3263, 1696, 3072, 0 },
   6228     { -1, cam_bob_default_free_roam, 0, 0, 0, 0, 0, 0, 0 },
   6229     NULL_TRIGGER
   6230 };
   6231 
   6232 /**
   6233  * The CotMC trigger is only used to prevent fix Lakitu in place when Mario exits through the waterfall.
   6234  */
   6235 struct CameraTrigger sCamCotMC[] = {
   6236     { 1, cam_cotmc_exit_waterfall, 0, 1500, 3500, 550, 10000, 1500, 0 },
   6237     NULL_TRIGGER
   6238 };
   6239 
   6240 /**
   6241  * The CCM triggers are used to set the flag that says when Mario is in the slide shortcut.
   6242  */
   6243 struct CameraTrigger sCamCCM[] = {
   6244     { 2, cam_ccm_enter_slide_shortcut, -4846, 2061, 27, 1229, 1342, 396, 0 },
   6245     { 2, cam_ccm_leave_slide_shortcut, -6412, -3917, -6246, 307, 185, 132, 0 },
   6246     NULL_TRIGGER
   6247 };
   6248 
   6249 /**
   6250  * The Castle triggers are used to set the camera to fixed mode when entering the lobby, and to set it
   6251  * to close mode when leaving it. They also set the mode to spiral staircase.
   6252  *
   6253  * There are two triggers for looking up and down straight staircases when Mario is at the start,
   6254  * and one trigger that starts the enter pool cutscene when Mario enters HMC.
   6255  */
   6256 struct CameraTrigger sCamCastle[] = {
   6257     { 1, cam_castle_close_mode, -1100, 657, -1346, 300, 150, 300, 0 },
   6258     { 1, cam_castle_enter_lobby, -1099, 657, -803, 300, 150, 300, 0 },
   6259     { 1, cam_castle_close_mode, -2304, -264, -4072, 140, 150, 140, 0 },
   6260     { 1, cam_castle_close_mode, -2304, 145, -1344, 140, 150, 140, 0 },
   6261     { 1, cam_castle_enter_lobby, -2304, 145, -802, 140, 150, 140, 0 },
   6262     //! Sets the camera mode when leaving secret aquarium
   6263     { 1, cam_castle_close_mode, 2816, 1200, -256, 100, 100, 100, 0 },
   6264     { 1, cam_castle_close_mode, 256, -161, -4226, 140, 150, 140, 0 },
   6265     { 1, cam_castle_close_mode, 256, 145, -1344, 140, 150, 140, 0 },
   6266     { 1, cam_castle_enter_lobby, 256, 145, -802, 140, 150, 140, 0 },
   6267     { 1, cam_castle_close_mode, -1023, 44, -4870, 140, 150, 140, 0 },
   6268     { 1, cam_castle_close_mode, -459, 145, -1020, 140, 150, 140, 0x6000 },
   6269     { 1, cam_castle_enter_lobby, -85, 145, -627, 140, 150, 140, 0 },
   6270     { 1, cam_castle_close_mode, -1589, 145, -1020, 140, 150, 140, -0x6000 },
   6271     { 1, cam_castle_enter_lobby, -1963, 145, -627, 140, 150, 140, 0 },
   6272     { 1, cam_castle_leave_lobby_sliding_door, -2838, 657, -1659, 200, 150, 150, 0x2000 },
   6273     { 1, cam_castle_enter_lobby_sliding_door, -2319, 512, -1266, 300, 150, 300, 0x2000 },
   6274     { 1, cam_castle_close_mode, 844, 759, -1657, 40, 150, 40, -0x2000 },
   6275     { 1, cam_castle_enter_lobby, 442, 759, -1292, 140, 150, 140, -0x2000 },
   6276     { 2, cam_castle_enter_spiral_stairs, -1000, 657, 1740, 200, 300, 200, 0 },
   6277     { 2, cam_castle_enter_spiral_stairs, -996, 1348, 1814, 200, 300, 200, 0 },
   6278     { 2, cam_castle_close_mode, -946, 657, 2721, 50, 150, 50, 0 },
   6279     { 2, cam_castle_close_mode, -996, 1348, 907, 50, 150, 50, 0 },
   6280     { 2, cam_castle_close_mode, -997, 1348, 1450, 140, 150, 140, 0 },
   6281     { 1, cam_castle_close_mode, -4942, 452, -461, 140, 150, 140, 0x4000 },
   6282     { 1, cam_castle_close_mode, -3393, 350, -793, 140, 150, 140, 0x4000 },
   6283     { 1, cam_castle_enter_lobby, -2851, 350, -792, 140, 150, 140, 0x4000 },
   6284     { 1, cam_castle_enter_lobby, 803, 350, -228, 140, 150, 140, -0x4000 },
   6285     //! Duplicate camera trigger outside JRB door
   6286     { 1, cam_castle_enter_lobby, 803, 350, -228, 140, 150, 140, -0x4000 },
   6287     { 1, cam_castle_close_mode, 1345, 350, -229, 140, 150, 140, 0x4000 },
   6288     { 1, cam_castle_close_mode, -946, -929, 622, 300, 150, 300, 0 },
   6289     { 2, cam_castle_look_upstairs, -205, 1456, 2508, 210, 928, 718, 0 },
   6290     { 1, cam_castle_basement_look_downstairs, -1027, -587, -718, 318, 486, 577, 0 },
   6291     { 1, cam_castle_lobby_entrance, -1023, 376, 1830, 300, 400, 300, 0 },
   6292     { 3, cam_castle_hmc_start_pool_cutscene, 2485, -1689, -2659, 600, 50, 600, 0 },
   6293     NULL_TRIGGER
   6294 };
   6295 
   6296 /**
   6297  * The BBH triggers are the most complex, they cause the camera to enter fixed mode for each room,
   6298  * transition between rooms, and enter free roam when outside.
   6299  *
   6300  * The triggers are also responsible for warping the camera below platforms.
   6301  */
   6302 struct CameraTrigger sCamBBH[] = {
   6303     { 1, cam_bbh_enter_front_door, 742, 0, 2369, 200, 200, 200, 0 },
   6304     { 1, cam_bbh_leave_front_door, 741, 0, 1827, 200, 200, 200, 0 },
   6305     { 1, cam_bbh_room_1, 222, 0, 1458, 200, 200, 200, 0 },
   6306     { 1, cam_bbh_room_1, 222, 0, 639, 200, 200, 200, 0 },
   6307     { 1, cam_bbh_room_1, 435, 0, 222, 200, 200, 200, 0 },
   6308     { 1, cam_bbh_room_1, 1613, 0, 222, 200, 200, 200, 0 },
   6309     { 1, cam_bbh_room_1, 1827, 0, 1459, 200, 200, 200, 0 },
   6310     { 1, cam_bbh_room_1, -495, 819, 1407, 200, 200, 200, 0 },
   6311     { 1, cam_bbh_room_1, -495, 819, 640, 250, 200, 200, 0 },
   6312     { 1, cam_bbh_room_1, 179, 819, 222, 200, 200, 200, 0 },
   6313     { 1, cam_bbh_room_1, 1613, 819, 222, 200, 200, 200, 0 },
   6314     { 1, cam_bbh_room_1, 1827, 819, 486, 200, 200, 200, 0 },
   6315     { 1, cam_bbh_room_1, 1827, 819, 1818, 200, 200, 200, 0 },
   6316     { 1, cam_bbh_room_2_lower, 2369, 0, 1459, 200, 200, 200, 0 },
   6317     { 1, cam_bbh_room_2_lower, 3354, 0, 1347, 200, 200, 200, 0 },
   6318     { 1, cam_bbh_room_2_lower, 2867, 514, 1843, 512, 102, 409, 0 },
   6319     { 1, cam_bbh_room_4, 3354, 0, 804, 200, 200, 200, 0 },
   6320     { 1, cam_bbh_room_4, 1613, 0, -320, 200, 200, 200, 0 },
   6321     { 1, cam_bbh_room_8, 435, 0, -320, 200, 200, 200, 0 },
   6322     { 1, cam_bbh_room_5_library, -2021, 0, 803, 200, 200, 200, 0 },
   6323     { 1, cam_bbh_room_5_library, -320, 0, 640, 200, 200, 200, 0 },
   6324     { 1, cam_bbh_room_5_library_to_hidden_transition, -1536, 358, -254, 716, 363, 102, 0 },
   6325     { 1, cam_bbh_room_5_hidden_to_library_transition, -1536, 358, -459, 716, 363, 102, 0 },
   6326     { 1, cam_bbh_room_5_hidden, -1560, 0, -1314, 200, 200, 200, 0 },
   6327     { 1, cam_bbh_room_3, -320, 0, 1459, 200, 200, 200, 0 },
   6328     { 1, cam_bbh_room_3, -2021, 0, 1345, 200, 200, 200, 0 },
   6329     { 1, cam_bbh_room_2_library, 2369, 819, 486, 200, 200, 200, 0 },
   6330     { 1, cam_bbh_room_2_library, 2369, 1741, 486, 200, 200, 200, 0 },
   6331     { 1, cam_bbh_room_2_library_to_trapdoor_transition, 2867, 1228, 1174, 716, 414, 102, 0 },
   6332     { 1, cam_bbh_room_2_trapdoor_transition, 2867, 1228, 1378, 716, 414, 102, 0 },
   6333     { 1, cam_bbh_room_2_trapdoor, 2369, 819, 1818, 200, 200, 200, 0 },
   6334     { 1, cam_bbh_room_9_attic, 1829, 1741, 486, 200, 200, 200, 0 },
   6335     { 1, cam_bbh_room_9_attic, 741, 1741, 1587, 200, 200, 200, 0 },
   6336     { 1, cam_bbh_room_9_attic_transition, 102, 2048, -191, 100, 310, 307, 0 },
   6337     { 1, cam_bbh_room_9_mr_i_transition, 409, 2048, -191, 100, 310, 307, 0 },
   6338     { 1, cam_bbh_room_13_balcony, 742, 1922, 2164, 200, 200, 200, 0 },
   6339     { 1, cam_bbh_fall_off_roof, 587, 1322, 2677, 1000, 400, 600, 0 },
   6340     { 1, cam_bbh_room_3, -1037, 819, 1408, 200, 200, 200, 0 },
   6341     { 1, cam_bbh_room_3, -1970, 1024, 1345, 200, 200, 200, 0 },
   6342     { 1, cam_bbh_room_8, 179, 819, -320, 200, 200, 200, 0 },
   6343     { 1, cam_bbh_room_7_mr_i, 1613, 819, -320, 200, 200, 200, 0 },
   6344     { 1, cam_bbh_room_7_mr_i_to_coffins_transition, 2099, 1228, -819, 102, 414, 716, 0 },
   6345     { 1, cam_bbh_room_7_coffins_to_mr_i_transition, 2304, 1228, -819, 102, 414, 716, 0 },
   6346     { 1, cam_bbh_room_6, -1037, 819, 640, 200, 200, 200, 0 },
   6347     { 1, cam_bbh_room_6, -1970, 1024, 803, 200, 200, 200, 0 },
   6348     { 1, cam_bbh_room_1, 1827, 819, 1818, 200, 200, 200, 0 },
   6349     { 1, cam_bbh_fall_into_pool, 2355, -1112, -193, 1228, 500, 1343, 0 },
   6350     { 1, cam_bbh_fall_into_pool, 2355, -1727, 1410, 1228, 500, 705, 0 },
   6351     { 1, cam_bbh_elevator_room_lower, 0, -2457, 1827, 250, 200, 250, 0 },
   6352     { 1, cam_bbh_elevator_room_lower, 0, -2457, 2369, 250, 200, 250, 0 },
   6353     { 1, cam_bbh_elevator_room_lower, 0, -2457, 4929, 250, 200, 250, 0 },
   6354     { 1, cam_bbh_elevator_room_lower, 0, -2457, 4387, 250, 200, 250, 0 },
   6355     { 1, cam_bbh_room_0_back_entrance, 1887, -2457, 204, 250, 200, 250, 0 },
   6356     { 1, cam_bbh_room_0, 1272, -2457, 204, 250, 200, 250, 0 },
   6357     { 1, cam_bbh_room_0, -1681, -2457, 204, 250, 200, 250, 0 },
   6358     { 1, cam_bbh_room_0_back_entrance, -2296, -2457, 204, 250, 200, 250, 0 },
   6359     { 1, cam_bbh_elevator, -2939, -605, 5367, 800, 100, 800, 0 },
   6360     { 1, cam_bbh_room_12_upper, -2939, -205, 5367, 300, 100, 300, 0 },
   6361     { 1, cam_bbh_room_12_upper, -2332, -204, 4714, 250, 200, 250, 0x6000 },
   6362     { 1, cam_bbh_room_0_back_entrance, -1939, -204, 4340, 250, 200, 250, 0x6000 },
   6363     NULL_TRIGGER
   6364 };
   6365 
   6366 #define _ NULL
   6367 #define STUB_LEVEL(_0, _1, _2, _3, _4, _5, _6, _7, cameratable) cameratable,
   6368 #define DEFINE_LEVEL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, cameratable) cameratable,
   6369 
   6370 /*
   6371  * This table has an extra 2 levels after the last unknown_38 stub level. What I think
   6372  * the programmer was thinking was that the table is null terminated and so used the
   6373  * level count as a correspondence to the ID of the final level, but the enum represents
   6374  * an ID *after* the last stub level, not before or during it.
   6375  *
   6376  * Each table is terminated with NULL_TRIGGER
   6377  */
   6378 struct CameraTrigger *sCameraTriggers[LEVEL_COUNT + 1] = {
   6379     NULL,
   6380     #include "levels/level_defines.h"
   6381 };
   6382 #undef _
   6383 #undef STUB_LEVEL
   6384 #undef DEFINE_LEVEL
   6385 
   6386 struct CutsceneSplinePoint sIntroStartToPipePosition[] = {
   6387     { 0, 0, { 2122, 8762, 9114 } },  { 0, 0, { 2122, 8762, 9114 } },  { 1, 0, { 2122, 7916, 9114 } },
   6388     { 1, 0, { 2122, 7916, 9114 } },  { 2, 0, { 957, 5166, 8613 } },   { 3, 0, { 589, 4338, 7727 } },
   6389     { 4, 0, { 690, 3366, 6267 } },   { 5, 0, { -1600, 2151, 4955 } }, { 6, 0, { -1557, 232, 1283 } },
   6390     { 7, 0, { -6962, -295, 2729 } }, { 8, 0, { -6979, 131, 3246 } },  { 9, 0, { -6360, -283, 4044 } },
   6391     { 0, 0, { -5695, -334, 5264 } }, { 1, 0, { -5568, -319, 7933 } }, { 2, 0, { -3848, -200, 6278 } },
   6392     { 3, 0, { -965, -263, 6092 } },  { 4, 0, { 1607, 2465, 6329 } },  { 5, 0, { 2824, 180, 3548 } },
   6393     { 6, 0, { 1236, 136, 945 } },    { 0, 0, { 448, 136, 564 } },     { 0, 0, { 448, 136, 564 } },
   6394     { 0, 0, { 448, 136, 564 } },     { -1, 0, { 448, 136, 564 } }
   6395 };
   6396 
   6397 struct CutsceneSplinePoint sIntroStartToPipeFocus[] = {
   6398     { 0, 50, { 1753, 29800, 8999 } }, { 0, 50, { 1753, 29800, 8999 } },
   6399     { 1, 50, { 1753, 8580, 8999 } },  { 1, 100, { 1753, 8580, 8999 } },
   6400     { 2, 50, { 520, 5400, 8674 } },   { 3, 50, { 122, 4437, 7875 } },
   6401     { 4, 50, { 316, 3333, 6538 } },   { 5, 36, { -1526, 2189, 5448 } },
   6402     { 6, 50, { -1517, 452, 1731 } },  { 7, 50, { -6659, -181, 3109 } },
   6403     { 8, 17, { -6649, 183, 3618 } },  { 9, 20, { -6009, -214, 4395 } },
   6404     { 0, 50, { -5258, -175, 5449 } }, { 1, 36, { -5158, -266, 7651 } },
   6405     { 2, 26, { -3351, -192, 6222 } }, { 3, 25, { -483, -137, 6060 } },
   6406     { 4, 100, { 1833, 2211, 5962 } }, { 5, 26, { 3022, 207, 3090 } },
   6407     { 6, 20, { 1250, 197, 449 } },    { 7, 50, { 248, 191, 227 } },
   6408     { 7, 0, { 48, 191, 227 } },       { 7, 0, { 48, 191, 227 } },
   6409     { -1, 0, { 48, 191, 227 } }
   6410 };
   6411 
   6412 /**
   6413  * Describes the spline the camera follows, starting when the camera jumps to Lakitu and ending after
   6414  * Mario jumps out of the pipe when the first dialog opens.  This table specifically updates the
   6415  * camera's position.
   6416  */
   6417 struct CutsceneSplinePoint sIntroPipeToDialogPosition[] = {
   6418     { 0, 0, { -785, 625, 4527 } },  { 1, 0, { -785, 625, 4527 } },  { 2, 0, { -1286, 644, 4376 } },
   6419     { 3, 0, { -1286, 623, 4387 } }, { 4, 0, { -1286, 388, 3963 } }, { 5, 0, { -1286, 358, 4093 } },
   6420     { 6, 0, { -1386, 354, 4159 } }, { 7, 0, { -1477, 306, 4223 } }, { 8, 0, { -1540, 299, 4378 } },
   6421     { 9, 0, { -1473, 316, 4574 } }, { 0, 0, { -1328, 485, 5017 } }, { 0, 0, { -1328, 485, 5017 } },
   6422     { 0, 0, { -1328, 485, 5017 } }, { -1, 0, { -1328, 485, 5017 } }
   6423 };
   6424 
   6425 /**
   6426  * Describes the spline that the camera's focus follows, during the same part of the intro as the above.
   6427  */
   6428 #ifdef VERSION_EU
   6429 struct CutsceneSplinePoint sIntroPipeToDialogFocus[] = {
   6430     { 0, 25, { -1248, 450, 4596 } }, { 1, 71, { -1258, 485, 4606 } }, { 2, 71, { -1379, 344, 4769 } },
   6431     { 3, 22, { -1335, 366, 4815 } }, { 4, 23, { -1315, 370, 4450 } }, { 5, 40, { -1322, 333, 4591 } },
   6432     { 6, 25, { -1185, 329, 4616 } }, { 7, 21, { -1059, 380, 4487 } }, { 8, 14, { -1086, 421, 4206 } },
   6433     { 9, 21, { -1321, 346, 4098 } }, { 0, 0, { -1328, 385, 4354 } },  { 0, 0, { -1328, 385, 4354 } },
   6434     { 0, 0, { -1328, 385, 4354 } },  { -1, 0, { -1328, 385, 4354 } }
   6435 };
   6436 #else
   6437 struct CutsceneSplinePoint sIntroPipeToDialogFocus[] = {
   6438     { 0, 20, { -1248, 450, 4596 } }, { 1, 59, { -1258, 485, 4606 } }, { 2, 59, { -1379, 344, 4769 } },
   6439     { 3, 20, { -1335, 366, 4815 } }, { 4, 23, { -1315, 370, 4450 } }, { 5, 40, { -1322, 333, 4591 } },
   6440     { 6, 25, { -1185, 329, 4616 } }, { 7, 21, { -1059, 380, 4487 } }, { 8, 14, { -1086, 421, 4206 } },
   6441     { 9, 21, { -1321, 346, 4098 } }, { 0, 0, { -1328, 385, 4354 } },  { 0, 0, { -1328, 385, 4354 } },
   6442     { 0, 0, { -1328, 385, 4354 } },  { -1, 0, { -1328, 385, 4354 } }
   6443 };
   6444 #endif
   6445 
   6446 struct CutsceneSplinePoint sEndingFlyToWindowPos[] = {
   6447     { 0, 0, { -86, 876, 640 } },   { 1, 0, { -86, 876, 610 } },   { 2, 0, { -66, 945, 393 } },
   6448     { 3, 0, { -80, 976, 272 } },   { 4, 0, { -66, 1306, -36 } },  { 5, 0, { -70, 1869, -149 } },
   6449     { 6, 0, { -10, 2093, -146 } }, { 7, 0, { -10, 2530, -248 } }, { 8, 0, { -10, 2530, -263 } },
   6450     { 9, 0, { -10, 2530, -273 } }
   6451 };
   6452 
   6453 struct CutsceneSplinePoint sEndingFlyToWindowFocus[] = {
   6454     { 0, 50, { -33, 889, -7 } },    { 1, 35, { -33, 889, -7 } },    { 2, 31, { -17, 1070, -193 } },
   6455     { 3, 25, { -65, 1182, -272 } }, { 4, 20, { -64, 1559, -542 } }, { 5, 25, { -68, 2029, -677 } },
   6456     { 6, 25, { -9, 2204, -673 } },  { 7, 25, { -8, 2529, -772 } },  { 8, 0, { -8, 2529, -772 } },
   6457     { 9, 0, { -8, 2529, -772 } },   { -1, 0, { -8, 2529, -772 } }
   6458 };
   6459 
   6460 struct CutsceneSplinePoint sEndingPeachDescentCamPos[] = {
   6461     { 0, 50, { 1, 120, -1150 } },    { 1, 50, { 1, 120, -1150 } },    { 2, 40, { 118, 121, -1199 } },
   6462     { 3, 40, { 147, 74, -1306 } },   { 4, 40, { 162, 95, -1416 } },   { 5, 40, { 25, 111, -1555 } },
   6463     { 6, 40, { -188, 154, -1439 } }, { 7, 40, { -203, 181, -1242 } }, { 8, 40, { 7, 191, -1057 } },
   6464     { 9, 40, { 262, 273, -1326 } },  { 0, 40, { -4, 272, -1627 } },   { 1, 35, { -331, 206, -1287 } },
   6465     { 2, 30, { -65, 219, -877 } },   { 3, 25, { 6, 216, -569 } },     { 4, 25, { -8, 157, 40 } },
   6466     { 5, 25, { -4, 106, 200 } },     { 6, 25, { -6, 72, 574 } },      { 7, 0, { -6, 72, 574 } },
   6467     { 8, 0, { -6, 72, 574 } },       { -1, 0, { -6, 72, 574 } }
   6468 };
   6469 
   6470 struct CutsceneSplinePoint sEndingMarioToPeachPos[] = {
   6471     { 0, 0, { -130, 1111, -1815 } }, { 1, 0, { -131, 1052, -1820 } }, { 2, 0, { -271, 1008, -1651 } },
   6472     { 3, 0, { -439, 1043, -1398 } }, { 4, 0, { -433, 1040, -1120 } }, { 5, 0, { -417, 1040, -1076 } },
   6473     { 6, 0, { -417, 1040, -1076 } }, { 7, 0, { -417, 1040, -1076 } }, { -1, 0, { -417, 1040, -1076 } }
   6474 };
   6475 
   6476 struct CutsceneSplinePoint sEndingMarioToPeachFocus[] = {
   6477     { 0, 50, { -37, 1020, -1332 } }, { 1, 20, { -36, 1012, -1330 } }, { 2, 20, { -24, 1006, -1215 } },
   6478     { 3, 20, { 28, 1002, -1224 } },  { 4, 24, { 45, 1013, -1262 } },  { 5, 35, { 34, 1000, -1287 } },
   6479     { 6, 0, { 34, 1000, -1287 } },   { 7, 0, { 34, 1000, -1287 } },   { -1, 0, { 34, 1000, -1287 } }
   6480 };
   6481 
   6482 struct CutsceneSplinePoint sEndingLookUpAtCastle[] = {
   6483     { 0, 50, { 200, 1066, -1414 } }, { 0, 50, { 200, 1066, -1414 } }, { 0, 30, { 198, 1078, -1412 } },
   6484     { 0, 33, { 15, 1231, -1474 } },  { 0, 39, { -94, 1381, -1368 } }, { 0, 0, { -92, 1374, -1379 } },
   6485     { 0, 0, { -92, 1374, -1379 } },  { -1, 0, { -92, 1374, -1379 } }
   6486 };
   6487 
   6488 struct CutsceneSplinePoint sEndingLookAtSkyFocus[] = {
   6489 #ifdef VERSION_EU
   6490     { 0, 50, { 484, 1368, -868 } }, { 0, 72, { 479, 1372, -872 } }, { 0, 50, { 351, 1817, -918 } },
   6491 #else
   6492     { 0, 50, { 484, 1368, -888 } }, { 0, 72, { 479, 1372, -892 } }, { 0, 50, { 351, 1817, -918 } },
   6493 #endif
   6494     { 0, 50, { 351, 1922, -598 } }, { 0, 0, { 636, 2027, -415 } },  { 0, 0, { 636, 2027, -415 } },
   6495     { -1, 0, { 636, 2027, -415 } }
   6496 };
   6497 
   6498 /**
   6499  * Activates any CameraTriggers that Mario is inside.
   6500  * Then, applies area-specific processing to the camera, such as setting the default mode, or changing
   6501  * the mode based on the terrain type Mario is standing on.
   6502  *
   6503  * @return the camera's mode after processing, although this is unused in the code
   6504  */
   6505 s16 camera_course_processing(struct Camera *c) {
   6506     s16 level = gCurrLevelNum;
   6507     s16 mode;
   6508     s8 area = gCurrentArea->index;
   6509     // Bounds iterator
   6510     u32 b;
   6511     // Camera trigger's bounding box
   6512     Vec3f center, bounds;
   6513     u32 insideBounds = FALSE;
   6514     UNUSED struct CameraTrigger unused;
   6515     u8 oldMode = c->mode;
   6516 
   6517     if (c->mode == CAMERA_MODE_C_UP) {
   6518         c->mode = sModeInfo.lastMode;
   6519     }
   6520     check_blocking_area_processing(&c->mode);
   6521     if (level > LEVEL_COUNT + 1) {
   6522         level = LEVEL_COUNT + 1;
   6523     }
   6524 
   6525     if (sCameraTriggers[level] != NULL) {
   6526         b = 0;
   6527 
   6528         // Process positional triggers.
   6529         // All triggered events are called, not just the first one.
   6530         while (sCameraTriggers[level][b].event != NULL) {
   6531 
   6532             // Check only the current area's triggers
   6533             if (sCameraTriggers[level][b].area == area) {
   6534                 // Copy the bounding box into center and bounds
   6535                 vec3f_set(center, sCameraTriggers[level][b].centerX,
   6536                                   sCameraTriggers[level][b].centerY,
   6537                                   sCameraTriggers[level][b].centerZ);
   6538                 vec3f_set(bounds, sCameraTriggers[level][b].boundsX,
   6539                                   sCameraTriggers[level][b].boundsY,
   6540                                   sCameraTriggers[level][b].boundsZ);
   6541 
   6542                 // Check if Mario is inside the bounds
   6543                 if (is_pos_in_bounds(sMarioCamState->pos, center, bounds,
   6544                                                    sCameraTriggers[level][b].boundsYaw) == TRUE) {
   6545                     //! This should be checked before calling is_pos_in_bounds. (It doesn't belong
   6546                     //! outside the while loop because some events disable area processing)
   6547                     if (!(sStatusFlags & CAM_FLAG_BLOCK_AREA_PROCESSING)) {
   6548                         sCameraTriggers[level][b].event(c);
   6549                         insideBounds = TRUE;
   6550                     }
   6551                 }
   6552             }
   6553 
   6554             if ((sCameraTriggers[level])[b].area == -1) {
   6555                 // Default triggers are only active if Mario is not already inside another trigger
   6556                 if (!insideBounds) {
   6557                     if (!(sStatusFlags & CAM_FLAG_BLOCK_AREA_PROCESSING)) {
   6558                         sCameraTriggers[level][b].event(c);
   6559                     }
   6560                 }
   6561             }
   6562 
   6563             b++;
   6564         }
   6565     }
   6566 
   6567     // Area-specific camera processing
   6568     if (!(sStatusFlags & CAM_FLAG_BLOCK_AREA_PROCESSING)) {
   6569         switch (gCurrLevelArea) {
   6570             case AREA_WF:
   6571                 if (sMarioCamState->action == ACT_RIDING_HOOT) {
   6572                     transition_to_camera_mode(c, CAMERA_MODE_SLIDE_HOOT, 60);
   6573                 } else {
   6574                     switch (sMarioGeometry.currFloorType) {
   6575                         case SURFACE_CAMERA_8_DIR:
   6576                             transition_to_camera_mode(c, CAMERA_MODE_8_DIRECTIONS, 90);
   6577                             s8DirModeBaseYaw = DEGREES(90);
   6578                             break;
   6579 
   6580                         case SURFACE_BOSS_FIGHT_CAMERA:
   6581                             if (gCurrActNum == 1) {
   6582                                 set_camera_mode_boss_fight(c);
   6583                             } else {
   6584                                 set_camera_mode_radial(c, 60);
   6585                             }
   6586                             break;
   6587                         default:
   6588                             set_camera_mode_radial(c, 60);
   6589                     }
   6590                 }
   6591                 break;
   6592 
   6593             case AREA_BBH:
   6594                 // if camera is fixed at bbh_room_13_balcony_camera (but as floats)
   6595                 if (vec3f_compare(sFixedModeBasePosition, 210.f, 420.f, 3109.f) == TRUE) {
   6596                     if (sMarioCamState->pos[1] < 1800.f) {
   6597                         transition_to_camera_mode(c, CAMERA_MODE_CLOSE, 30);
   6598                     }
   6599                 }
   6600                 break;
   6601 
   6602             case AREA_SSL_PYRAMID:
   6603                 set_mode_if_not_set_by_surface(c, CAMERA_MODE_OUTWARD_RADIAL);
   6604                 break;
   6605 
   6606             case AREA_SSL_OUTSIDE:
   6607                 set_mode_if_not_set_by_surface(c, CAMERA_MODE_RADIAL);
   6608                 break;
   6609 
   6610             case AREA_THI_HUGE:
   6611                 break;
   6612 
   6613             case AREA_THI_TINY:
   6614                 surface_type_modes_thi(c);
   6615                 break;
   6616 
   6617             case AREA_TTC:
   6618                 set_mode_if_not_set_by_surface(c, CAMERA_MODE_OUTWARD_RADIAL);
   6619                 break;
   6620 
   6621             case AREA_BOB:
   6622                 if (set_mode_if_not_set_by_surface(c, CAMERA_MODE_NONE) == 0) {
   6623                     if (sMarioGeometry.currFloorType == SURFACE_BOSS_FIGHT_CAMERA) {
   6624                         set_camera_mode_boss_fight(c);
   6625                     } else {
   6626                         if (c->mode == CAMERA_MODE_CLOSE) {
   6627                             transition_to_camera_mode(c, CAMERA_MODE_RADIAL, 60);
   6628                         } else {
   6629                             set_camera_mode_radial(c, 60);
   6630                         }
   6631                     }
   6632                 }
   6633                 break;
   6634 
   6635             case AREA_WDW_MAIN:
   6636                 switch (sMarioGeometry.currFloorType) {
   6637                     case SURFACE_INSTANT_WARP_1B:
   6638                         c->defMode = CAMERA_MODE_RADIAL;
   6639                         break;
   6640                 }
   6641                 break;
   6642 
   6643             case AREA_WDW_TOWN:
   6644                 switch (sMarioGeometry.currFloorType) {
   6645                     case SURFACE_INSTANT_WARP_1C:
   6646                         c->defMode = CAMERA_MODE_CLOSE;
   6647                         break;
   6648                 }
   6649                 break;
   6650 
   6651             case AREA_DDD_WHIRLPOOL:
   6652                 //! @bug this does nothing
   6653                 gLakituState.defMode = CAMERA_MODE_OUTWARD_RADIAL;
   6654                 break;
   6655 
   6656             case AREA_DDD_SUB:
   6657                 if ((c->mode != CAMERA_MODE_BEHIND_MARIO)
   6658                     && (c->mode != CAMERA_MODE_WATER_SURFACE)) {
   6659                     if (((sMarioCamState->action & ACT_FLAG_ON_POLE) != 0)
   6660                         || (sMarioGeometry.currFloorHeight > 800.f)) {
   6661                         transition_to_camera_mode(c, CAMERA_MODE_8_DIRECTIONS, 60);
   6662 
   6663                     } else {
   6664                         if (sMarioCamState->pos[1] < 800.f) {
   6665                             transition_to_camera_mode(c, CAMERA_MODE_FREE_ROAM, 60);
   6666                         }
   6667                     }
   6668                 }
   6669                 //! @bug this does nothing
   6670                 gLakituState.defMode = CAMERA_MODE_FREE_ROAM;
   6671                 break;
   6672         }
   6673     }
   6674 
   6675     sStatusFlags &= ~CAM_FLAG_BLOCK_AREA_PROCESSING;
   6676     if (oldMode == CAMERA_MODE_C_UP) {
   6677         sModeInfo.lastMode = c->mode;
   6678         c->mode = oldMode;
   6679     }
   6680     mode = c->mode;
   6681     return mode;
   6682 }
   6683 
   6684 /**
   6685  * Move `pos` between the nearest floor and ceiling
   6686  * @param lastGood unused, passed as the last position the camera was in
   6687  */
   6688 void resolve_geometry_collisions(Vec3f pos, UNUSED Vec3f lastGood) {
   6689     f32 ceilY, floorY;
   6690     struct Surface *surf;
   6691 
   6692     f32_find_wall_collision(&pos[0], &pos[1], &pos[2], 0.f, 100.f);
   6693     floorY = find_floor(pos[0], pos[1] + 50.f, pos[2], &surf);
   6694     ceilY = find_ceil(pos[0], pos[1] - 50.f, pos[2], &surf);
   6695 
   6696     if ((FLOOR_LOWER_LIMIT != floorY) && (CELL_HEIGHT_LIMIT == ceilY)) {
   6697         if (pos[1] < (floorY += 125.f)) {
   6698             pos[1] = floorY;
   6699         }
   6700     }
   6701 
   6702     if ((FLOOR_LOWER_LIMIT == floorY) && (CELL_HEIGHT_LIMIT != ceilY)) {
   6703         if (pos[1] > (ceilY -= 125.f)) {
   6704             pos[1] = ceilY;
   6705         }
   6706     }
   6707 
   6708     if ((FLOOR_LOWER_LIMIT != floorY) && (CELL_HEIGHT_LIMIT != ceilY)) {
   6709         floorY += 125.f;
   6710         ceilY -= 125.f;
   6711 
   6712         if ((pos[1] <= floorY) && (pos[1] < ceilY)) {
   6713             pos[1] = floorY;
   6714         }
   6715         if ((pos[1] > floorY) && (pos[1] >= ceilY)) {
   6716             pos[1] = ceilY;
   6717         }
   6718         if ((pos[1] <= floorY) && (pos[1] >= ceilY)) {
   6719             pos[1] = (floorY + ceilY) * 0.5f;
   6720         }
   6721     }
   6722 }
   6723 
   6724 /**
   6725  * Checks for any walls obstructing Mario from view, and calculates a new yaw that the camera should
   6726  * rotate towards.
   6727  *
   6728  * @param[out] avoidYaw the angle (from Mario) that the camera should rotate towards to avoid the wall.
   6729  *                      The camera then approaches avoidYaw until Mario is no longer obstructed.
   6730  *                      avoidYaw is always parallel to the wall.
   6731  * @param yawRange      how wide of an arc to check for walls obscuring Mario.
   6732  *
   6733  * @return 3 if a wall is covering Mario, 1 if a wall is only near the camera.
   6734  */
   6735 s32 rotate_camera_around_walls(struct Camera *c, Vec3f cPos, s16 *avoidYaw, s16 yawRange) {
   6736     UNUSED u8 filler1[4];
   6737     struct WallCollisionData colData;
   6738     struct Surface *wall;
   6739     UNUSED u8 filler2[12];
   6740     f32 dummyDist, checkDist;
   6741     UNUSED u8 filler3[4];
   6742     f32 coarseRadius;
   6743     f32 fineRadius;
   6744     s16 wallYaw, horWallNorm;
   6745     UNUSED s16 unused;
   6746     s16 dummyPitch;
   6747     // The yaw of the vector from Mario to the camera.
   6748     s16 yawFromMario;
   6749     UNUSED u8 filler4[2];
   6750     s32 status = 0;
   6751     /// The current iteration. The algorithm takes 8 equal steps from Mario back to the camera.
   6752     s32 step = 0;
   6753     UNUSED u8 filler5[4];
   6754 
   6755     vec3f_get_dist_and_angle(sMarioCamState->pos, cPos, &dummyDist, &dummyPitch, &yawFromMario);
   6756     sStatusFlags &= ~CAM_FLAG_CAM_NEAR_WALL;
   6757     colData.offsetY = 100.0f;
   6758     // The distance from Mario to Lakitu
   6759     checkDist = 0.0f;
   6760     /// The radius used to find potential walls to avoid.
   6761     /// @bug Increases to 250.f, but the max collision radius is 200.f
   6762     coarseRadius = 150.0f;
   6763     /// This only increases when there is a wall collision found in the coarse pass
   6764     fineRadius = 100.0f;
   6765 
   6766     for (step = 0; step < 8; step++) {
   6767         // Start at Mario, move backwards to Lakitu's position
   6768         colData.x = sMarioCamState->pos[0] + ((cPos[0] - sMarioCamState->pos[0]) * checkDist);
   6769         colData.y = sMarioCamState->pos[1] + ((cPos[1] - sMarioCamState->pos[1]) * checkDist);
   6770         colData.z = sMarioCamState->pos[2] + ((cPos[2] - sMarioCamState->pos[2]) * checkDist);
   6771         colData.radius = coarseRadius;
   6772         // Increase the coarse check radius
   6773         camera_approach_f32_symmetric_bool(&coarseRadius, 250.f, 30.f);
   6774 
   6775         if (find_wall_collisions(&colData) != 0) {
   6776             wall = colData.walls[colData.numWalls - 1];
   6777 
   6778             // If we're over halfway from Mario to Lakitu, then there's a wall near the camera, but
   6779             // not necessarily obstructing Mario
   6780             if (step >= 5) {
   6781                 sStatusFlags |= CAM_FLAG_CAM_NEAR_WALL;
   6782                 if (status <= 0) {
   6783                     status = 1;
   6784                     wall = colData.walls[colData.numWalls - 1];
   6785                     // wallYaw is parallel to the wall, not perpendicular
   6786                     wallYaw = atan2s(wall->normal.z, wall->normal.x) + DEGREES(90);
   6787                     // Calculate the avoid direction. The function returns the opposite direction so add 180
   6788                     // degrees.
   6789                     *avoidYaw = calc_avoid_yaw(yawFromMario, wallYaw) + DEGREES(180);
   6790                 }
   6791             }
   6792 
   6793             colData.x = sMarioCamState->pos[0] + ((cPos[0] - sMarioCamState->pos[0]) * checkDist);
   6794             colData.y = sMarioCamState->pos[1] + ((cPos[1] - sMarioCamState->pos[1]) * checkDist);
   6795             colData.z = sMarioCamState->pos[2] + ((cPos[2] - sMarioCamState->pos[2]) * checkDist);
   6796             colData.radius = fineRadius;
   6797             // Increase the fine check radius
   6798             camera_approach_f32_symmetric_bool(&fineRadius, 200.f, 20.f);
   6799 
   6800             if (find_wall_collisions(&colData) != 0) {
   6801                 wall = colData.walls[colData.numWalls - 1];
   6802                 horWallNorm = atan2s(wall->normal.z, wall->normal.x);
   6803                 wallYaw = horWallNorm + DEGREES(90);
   6804                 // If Mario would be blocked by the surface, then avoid it
   6805                 if ((is_range_behind_surface(sMarioCamState->pos, cPos, wall, yawRange, SURFACE_WALL_MISC) == 0)
   6806                     && (is_mario_behind_surface(c, wall) == TRUE)
   6807                     // Also check if the wall is tall enough to cover Mario
   6808                     && (is_surf_within_bounding_box(wall, -1.f, 150.f, -1.f) == FALSE)) {
   6809                     // Calculate the avoid direction. The function returns the opposite direction so add 180
   6810                     // degrees.
   6811                     *avoidYaw = calc_avoid_yaw(yawFromMario, wallYaw) + DEGREES(180);
   6812                     camera_approach_s16_symmetric_bool(avoidYaw, horWallNorm, yawRange);
   6813                     status = 3;
   6814                     step = 8;
   6815                 }
   6816             }
   6817         }
   6818         checkDist += 0.125f;
   6819     }
   6820 
   6821     return status;
   6822 }
   6823 
   6824 /**
   6825  * Stores type and height of the nearest floor and ceiling to Mario in `pg`
   6826  *
   6827  * Note: Also finds the water level, but waterHeight is unused
   6828  */
   6829 void find_mario_floor_and_ceil(struct PlayerGeometry *pg) {
   6830     struct Surface *surf;
   6831     s16 tempCheckingSurfaceCollisionsForCamera = gCheckingSurfaceCollisionsForCamera;
   6832     gCheckingSurfaceCollisionsForCamera = TRUE;
   6833 
   6834     if (find_floor(sMarioCamState->pos[0], sMarioCamState->pos[1] + 10.f,
   6835                    sMarioCamState->pos[2], &surf) != FLOOR_LOWER_LIMIT) {
   6836         pg->currFloorType = surf->type;
   6837     } else {
   6838         pg->currFloorType = 0;
   6839     }
   6840 
   6841     if (find_ceil(sMarioCamState->pos[0], sMarioCamState->pos[1] - 10.f,
   6842                   sMarioCamState->pos[2], &surf) != CELL_HEIGHT_LIMIT) {
   6843         pg->currCeilType = surf->type;
   6844     } else {
   6845         pg->currCeilType = 0;
   6846     }
   6847 
   6848     gCheckingSurfaceCollisionsForCamera = FALSE;
   6849     pg->currFloorHeight = find_floor(sMarioCamState->pos[0],
   6850                                      sMarioCamState->pos[1] + 10.f,
   6851                                      sMarioCamState->pos[2], &pg->currFloor);
   6852     pg->currCeilHeight = find_ceil(sMarioCamState->pos[0],
   6853                                    sMarioCamState->pos[1] - 10.f,
   6854                                    sMarioCamState->pos[2], &pg->currCeil);
   6855     pg->waterHeight = find_water_level(sMarioCamState->pos[0], sMarioCamState->pos[2]);
   6856     gCheckingSurfaceCollisionsForCamera = tempCheckingSurfaceCollisionsForCamera;
   6857 }
   6858 
   6859 /**
   6860  * Start a cutscene focusing on an object
   6861  * This will play if nothing else happened in the same frame, like exiting or warping.
   6862  */
   6863 void start_object_cutscene(u8 cutscene, struct Object *o) {
   6864     sObjectCutscene = cutscene;
   6865     gRecentCutscene = 0;
   6866     gCutsceneFocus = o;
   6867     gObjCutsceneDone = FALSE;
   6868 }
   6869 
   6870 /**
   6871  * Start a low-priority cutscene without focusing on an object
   6872  * This will play if nothing else happened in the same frame, like exiting or warping.
   6873  */
   6874 u8 start_object_cutscene_without_focus(u8 cutscene) {
   6875     sObjectCutscene = cutscene;
   6876     sCutsceneDialogResponse = DIALOG_RESPONSE_NONE;
   6877     return 0;
   6878 }
   6879 
   6880 s16 unused_dialog_cutscene_response(u8 cutscene) {
   6881     // if not in a cutscene, start this one
   6882     if ((gCamera->cutscene == 0) && (sObjectCutscene == 0)) {
   6883         sObjectCutscene = cutscene;
   6884     }
   6885 
   6886     // if playing this cutscene and Mario responded, return the response
   6887     if ((gCamera->cutscene == cutscene) && (sCutsceneDialogResponse != DIALOG_RESPONSE_NONE)) {
   6888         return sCutsceneDialogResponse;
   6889     } else {
   6890         return 0;
   6891     }
   6892 }
   6893 
   6894 s16 cutscene_object_with_dialog(u8 cutscene, struct Object *o, s16 dialogID) {
   6895     s16 response = DIALOG_RESPONSE_NONE;
   6896 
   6897     if ((gCamera->cutscene == 0) && (sObjectCutscene == 0)) {
   6898         if (gRecentCutscene != cutscene) {
   6899             start_object_cutscene(cutscene, o);
   6900             if (dialogID != DIALOG_NONE) {
   6901                 sCutsceneDialogID = dialogID;
   6902             } else {
   6903                 sCutsceneDialogID = DIALOG_001;
   6904             }
   6905         } else {
   6906             response = sCutsceneDialogResponse;
   6907         }
   6908 
   6909         gRecentCutscene = 0;
   6910     }
   6911     return response;
   6912 }
   6913 
   6914 s16 cutscene_object_without_dialog(u8 cutscene, struct Object *o) {
   6915     s16 response = cutscene_object_with_dialog(cutscene, o, DIALOG_NONE);
   6916     return response;
   6917 }
   6918 
   6919 /**
   6920  * @return 0 if not started, 1 if started, and -1 if finished
   6921  */
   6922 s16 cutscene_object(u8 cutscene, struct Object *o) {
   6923     s16 status = 0;
   6924 
   6925     if ((gCamera->cutscene == 0) && (sObjectCutscene == 0)) {
   6926         if (gRecentCutscene != cutscene) {
   6927             start_object_cutscene(cutscene, o);
   6928             status = 1;
   6929         } else {
   6930             status = -1;
   6931         }
   6932     }
   6933     return status;
   6934 }
   6935 
   6936 /**
   6937  * Update the camera's yaw and nextYaw. This is called from cutscenes to ignore the camera mode's yaw.
   6938  */
   6939 void update_camera_yaw(struct Camera *c) {
   6940     c->nextYaw = calculate_yaw(c->focus, c->pos);
   6941     c->yaw = c->nextYaw;
   6942 }
   6943 
   6944 void cutscene_reset_spline(void) {
   6945     sCutsceneSplineSegment = 0;
   6946     sCutsceneSplineSegmentProgress = 0;
   6947 }
   6948 
   6949 void stop_cutscene_and_retrieve_stored_info(struct Camera *c) {
   6950     gCutsceneTimer = CUTSCENE_STOP;
   6951     c->cutscene = 0;
   6952     vec3f_copy(c->focus, sCameraStoreCutscene.focus);
   6953     vec3f_copy(c->pos, sCameraStoreCutscene.pos);
   6954 }
   6955 
   6956 void cap_switch_save(s16 dummy) {
   6957     UNUSED s16 unused = dummy;
   6958     save_file_do_save(gCurrSaveFileNum - 1);
   6959 }
   6960 
   6961 void init_spline_point(struct CutsceneSplinePoint *splinePoint, s8 index, u8 speed, Vec3s point) {
   6962     splinePoint->index = index;
   6963     splinePoint->speed = speed;
   6964     vec3s_copy(splinePoint->point, point);
   6965 }
   6966 
   6967 // TODO: (Scrub C)
   6968 void copy_spline_segment(struct CutsceneSplinePoint dst[], struct CutsceneSplinePoint src[]) {
   6969     s32 j = 0;
   6970     s32 i = 0;
   6971     UNUSED u8 filler[8];
   6972 
   6973     init_spline_point(&dst[i], src[j].index, src[j].speed, src[j].point);
   6974     i++;
   6975     do {
   6976         do {
   6977             init_spline_point(&dst[i], src[j].index, src[j].speed, src[j].point);
   6978             i++;
   6979             j++;
   6980         } while ((src[j].index != -1) && (src[j].index != -1)); //! same comparison performed twice
   6981     } while (j > 16);
   6982 
   6983     // Create the end of the spline by duplicating the last point
   6984     do { init_spline_point(&dst[i], 0, src[j].speed, src[j].point); } while (0);
   6985     do { init_spline_point(&dst[i + 1], 0, 0, src[j].point); } while (0);
   6986     do { init_spline_point(&dst[i + 2], 0, 0, src[j].point); } while (0);
   6987     do { init_spline_point(&dst[i + 3], -1, 0, src[j].point); } while (0);
   6988 }
   6989 
   6990 /**
   6991  * Triggers Mario to enter a dialog state. This is used to make Mario look at the focus of a cutscene,
   6992  * for example, bowser.
   6993  * @param state 0 = stop, 1 = start, 2 = start and look up, and 3 = start and look down
   6994  *
   6995  * @return if Mario left the dialog state, return CUTSCENE_LOOP, else return gCutsceneTimer
   6996  */
   6997 s16 cutscene_common_set_dialog_state(s32 state) {
   6998     s16 timer = gCutsceneTimer;
   6999     // If the dialog ended, return CUTSCENE_LOOP, which would end the cutscene shot
   7000     if (set_mario_npc_dialog(state) == MARIO_DIALOG_STATUS_SPEAK) {
   7001         timer = CUTSCENE_LOOP;
   7002     }
   7003     return timer;
   7004 }
   7005 
   7006 /// Unused SSL cutscene?
   7007 static UNUSED void unused_cutscene_mario_dialog_looking_down(UNUSED struct Camera *c) {
   7008     gCutsceneTimer = cutscene_common_set_dialog_state(MARIO_DIALOG_LOOK_DOWN);
   7009 }
   7010 
   7011 /**
   7012  * Cause Mario to enter the normal dialog state.
   7013  */
   7014 static BAD_RETURN(s32) cutscene_mario_dialog(UNUSED struct Camera *c) {
   7015     gCutsceneTimer = cutscene_common_set_dialog_state(MARIO_DIALOG_LOOK_FRONT);
   7016 }
   7017 
   7018 /// Unused SSL cutscene?
   7019 static UNUSED void unused_cutscene_mario_dialog_looking_up(UNUSED struct Camera *c) {
   7020     gCutsceneTimer = cutscene_common_set_dialog_state(MARIO_DIALOG_LOOK_UP);
   7021 }
   7022 
   7023 /**
   7024  * Lower the volume (US only) and start the peach letter background music
   7025  */
   7026 BAD_RETURN(s32) cutscene_intro_peach_start_letter_music(UNUSED struct Camera *c) {
   7027 #if defined(VERSION_US) || defined(VERSION_SH) || defined(VERSION_CN)
   7028     seq_player_lower_volume(SEQ_PLAYER_LEVEL, 60, 40);
   7029 #endif
   7030     cutscene_intro_peach_play_message_music();
   7031 }
   7032 
   7033 /**
   7034  * Raise the volume (not in JP) and start the flying music.
   7035  */
   7036 BAD_RETURN(s32) cutscene_intro_peach_start_flying_music(UNUSED struct Camera *c) {
   7037 #ifndef VERSION_JP
   7038     seq_player_unlower_volume(SEQ_PLAYER_LEVEL, 60);
   7039 #endif
   7040     cutscene_intro_peach_play_lakitu_flying_music();
   7041 }
   7042 
   7043 #ifdef VERSION_EU
   7044 /**
   7045  * Lower the volume for the letter background music. In US, this happens on the same frame as the music
   7046  * starts.
   7047  */
   7048 BAD_RETURN(s32) cutscene_intro_peach_eu_lower_volume(UNUSED struct Camera *c) {
   7049     seq_player_lower_volume(SEQ_PLAYER_LEVEL, 60, 40);
   7050 }
   7051 #endif
   7052 
   7053 void reset_pan_distance(UNUSED struct Camera *c) {
   7054     sPanDistance = 0;
   7055 }
   7056 
   7057 /**
   7058  * Easter egg: the player 2 controller can move the camera's focus in the ending and credits.
   7059  */
   7060 void player2_rotate_cam(struct Camera *c, s16 minPitch, s16 maxPitch, s16 minYaw, s16 maxYaw) {
   7061     f32 distCamToFocus;
   7062     s16 pitch, yaw, pitchCap;
   7063 
   7064     // Change the camera rotation to match the 2nd player's stick
   7065     approach_s16_asymptotic_bool(&sCreditsPlayer2Yaw, -(s16)(gPlayer2Controller->stickX * 250.f), 4);
   7066     approach_s16_asymptotic_bool(&sCreditsPlayer2Pitch, -(s16)(gPlayer2Controller->stickY * 265.f), 4);
   7067     vec3f_get_dist_and_angle(c->pos, c->focus, &distCamToFocus, &pitch, &yaw);
   7068 
   7069     pitchCap = 0x3800 - pitch; if (pitchCap < 0) {
   7070         pitchCap = 0;
   7071     }
   7072     if (maxPitch > pitchCap) {
   7073         maxPitch = pitchCap;
   7074     }
   7075 
   7076     pitchCap = -0x3800 - pitch;
   7077     if (pitchCap > 0) {
   7078         pitchCap = 0;
   7079     }
   7080     if (minPitch < pitchCap) {
   7081         minPitch = pitchCap;
   7082     }
   7083 
   7084     if (sCreditsPlayer2Pitch > maxPitch) {
   7085         sCreditsPlayer2Pitch = maxPitch;
   7086     }
   7087     if (sCreditsPlayer2Pitch < minPitch) {
   7088         sCreditsPlayer2Pitch = minPitch;
   7089     }
   7090 
   7091     if (sCreditsPlayer2Yaw > maxYaw) {
   7092         sCreditsPlayer2Yaw = maxYaw;
   7093     }
   7094     if (sCreditsPlayer2Yaw < minYaw) {
   7095         sCreditsPlayer2Yaw = minYaw;
   7096     }
   7097 
   7098     pitch += sCreditsPlayer2Pitch;
   7099     yaw += sCreditsPlayer2Yaw;
   7100     vec3f_set_dist_and_angle(c->pos, sPlayer2FocusOffset, distCamToFocus, pitch, yaw);
   7101     vec3f_sub(sPlayer2FocusOffset, c->focus);
   7102 }
   7103 
   7104 /**
   7105  * Store camera info for the cannon opening cutscene
   7106  */
   7107 void store_info_cannon(struct Camera *c) {
   7108     vec3f_copy(sCameraStoreCutscene.pos, c->pos);
   7109     vec3f_copy(sCameraStoreCutscene.focus, c->focus);
   7110     sCameraStoreCutscene.panDist = sPanDistance;
   7111     sCameraStoreCutscene.cannonYOffset = sCannonYOffset;
   7112 }
   7113 
   7114 /**
   7115  * Retrieve camera info for the cannon opening cutscene
   7116  */
   7117 void retrieve_info_cannon(struct Camera *c) {
   7118     vec3f_copy(c->pos, sCameraStoreCutscene.pos);
   7119     vec3f_copy(c->focus, sCameraStoreCutscene.focus);
   7120     sPanDistance = sCameraStoreCutscene.panDist;
   7121     sCannonYOffset = sCameraStoreCutscene.cannonYOffset;
   7122 }
   7123 
   7124 /**
   7125  * Store camera info for the star spawn cutscene
   7126  */
   7127 void store_info_star(struct Camera *c) {
   7128     reset_pan_distance(c);
   7129     vec3f_copy(sCameraStoreCutscene.pos, c->pos);
   7130     sCameraStoreCutscene.focus[0] = sMarioCamState->pos[0];
   7131     sCameraStoreCutscene.focus[1] = c->focus[1];
   7132     sCameraStoreCutscene.focus[2] = sMarioCamState->pos[2];
   7133 }
   7134 
   7135 /**
   7136  * Retrieve camera info for the star spawn cutscene
   7137  */
   7138 void retrieve_info_star(struct Camera *c) {
   7139     vec3f_copy(c->pos, sCameraStoreCutscene.pos);
   7140     vec3f_copy(c->focus, sCameraStoreCutscene.focus);
   7141 }
   7142 
   7143 static UNUSED void unused_vec3s_to_vec3f(Vec3f dst, Vec3s src) {
   7144     dst[0] = src[0];
   7145     dst[1] = src[1];
   7146     dst[2] = src[2];
   7147 }
   7148 
   7149 static UNUSED void unused_vec3f_to_vec3s(Vec3s dst, Vec3f src) {
   7150     // note: unlike vec3f_to_vec3s(), this function doesn't round the numbers and instead simply
   7151     // truncates them
   7152     dst[0] = src[0];
   7153     dst[1] = src[1];
   7154     dst[2] = src[2];
   7155 }
   7156 
   7157 /**
   7158  * Rotate the camera's focus around the camera's position by incYaw and incPitch
   7159  */
   7160 void pan_camera(struct Camera *c, s16 incPitch, s16 incYaw) {
   7161     UNUSED u8 filler[12];
   7162     f32 distCamToFocus;
   7163     s16 pitch, yaw;
   7164 
   7165     vec3f_get_dist_and_angle(c->pos, c->focus, &distCamToFocus, &pitch, &yaw);
   7166     pitch += incPitch;     yaw += incYaw;
   7167     vec3f_set_dist_and_angle(c->pos, c->focus, distCamToFocus, pitch, yaw);
   7168 }
   7169 
   7170 BAD_RETURN(s32) cutscene_shake_explosion(UNUSED struct Camera *c) {
   7171     set_environmental_camera_shake(SHAKE_ENV_EXPLOSION);
   7172     cutscene_set_fov_shake_preset(1);
   7173 }
   7174 
   7175 static UNUSED void unused_start_bowser_bounce_shake(UNUSED struct Camera *c) {
   7176     set_environmental_camera_shake(SHAKE_ENV_BOWSER_THROW_BOUNCE);
   7177 }
   7178 
   7179 /**
   7180  * Change the spherical coordinates of `to` relative to `from` by `incDist`, `incPitch`, and `incYaw`
   7181  *
   7182  * @param from    the base position
   7183  * @param[out] to the destination position
   7184  */
   7185 void rotate_and_move_vec3f(Vec3f to, Vec3f from, f32 incDist, s16 incPitch, s16 incYaw) {
   7186     f32 dist;
   7187     s16 pitch, yaw;
   7188 
   7189     vec3f_get_dist_and_angle(from, to, &dist, &pitch, &yaw);
   7190     pitch += incPitch;
   7191     yaw += incYaw;
   7192     dist += incDist;
   7193     vec3f_set_dist_and_angle(from, to, dist, pitch, yaw);
   7194 }
   7195 
   7196 void set_flag_post_door(struct Camera *c) {
   7197     sStatusFlags |= CAM_FLAG_BEHIND_MARIO_POST_DOOR;
   7198     sCameraYawAfterDoorCutscene = calculate_yaw(c->focus, c->pos);
   7199 }
   7200 
   7201 void cutscene_soften_music(UNUSED struct Camera *c) {
   7202     seq_player_lower_volume(SEQ_PLAYER_LEVEL, 60, 40);
   7203 }
   7204 
   7205 void cutscene_unsoften_music(UNUSED struct Camera *c) {
   7206     seq_player_unlower_volume(SEQ_PLAYER_LEVEL, 60);
   7207 }
   7208 
   7209 UNUSED static void stub_camera_5(UNUSED struct Camera *c) {
   7210 }
   7211 
   7212 BAD_RETURN(s32) cutscene_unused_start(UNUSED struct Camera *c) {
   7213 }
   7214 
   7215 BAD_RETURN(s32) cutscene_unused_loop(UNUSED struct Camera *c) {
   7216 }
   7217 
   7218 /**
   7219  * Set the camera position and focus for when Mario falls from the sky.
   7220  */
   7221 BAD_RETURN(s32) cutscene_ending_mario_fall_start(struct Camera *c) {
   7222     vec3f_set(c->focus, -26.f, 0.f, -137.f);
   7223     vec3f_set(c->pos, 165.f, 4725.f, 324.f);
   7224 }
   7225 
   7226 /**
   7227  * Focus on Mario when he's falling from the sky.
   7228  */
   7229 BAD_RETURN(s32) cutscene_ending_mario_fall_focus_mario(struct Camera *c) {
   7230     Vec3f offset;
   7231     vec3f_set(offset, 0.f, 80.f, 0.f);
   7232 
   7233     offset[2] = ABS(sMarioCamState->pos[1] - c->pos[1]) * -0.1f;
   7234     if (offset[2] > -100.f) {
   7235         offset[2] = -100.f;
   7236     }
   7237 
   7238     offset_rotated(c->focus, sMarioCamState->pos, offset, sMarioCamState->faceAngle);
   7239 }
   7240 
   7241 /**
   7242  * Mario falls from the sky after the grand star cutscene.
   7243  */
   7244 BAD_RETURN(s32) cutscene_ending_mario_fall(struct Camera *c) {
   7245     cutscene_event(cutscene_ending_mario_fall_start, c, 0, 0);
   7246     cutscene_event(cutscene_ending_mario_fall_focus_mario, c, 0, -1);
   7247     player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000);
   7248 }
   7249 
   7250 /**
   7251  * Closeup of Mario as the wing cap fades and Mario looks up.
   7252  */
   7253 BAD_RETURN(s32) cutscene_ending_mario_land_closeup(struct Camera *c) {
   7254     vec3f_set(c->focus, 85.f, 826.f, 250.f);
   7255     vec3f_set(c->pos, -51.f, 988.f, -202.f);
   7256     player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000);
   7257 }
   7258 
   7259 /**
   7260  * Reset the spline progress and cvar9.
   7261  */
   7262 BAD_RETURN(s32) cutscene_ending_reset_spline(UNUSED struct Camera *c) {
   7263     sCutsceneVars[9].point[0] = 0.f;
   7264     cutscene_reset_spline();
   7265 }
   7266 
   7267 /**
   7268  * Follow sEndingFlyToWindowPos/Focus up to the window.
   7269  */
   7270 BAD_RETURN(s32) cutscene_ending_fly_up_to_window(struct Camera *c) {
   7271     move_point_along_spline(c->pos, sEndingFlyToWindowPos, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   7272     move_point_along_spline(c->focus, sEndingFlyToWindowFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   7273 }
   7274 
   7275 /**
   7276  * Move the camera up to the window as the star power frees peach.
   7277  */
   7278 BAD_RETURN(s32) cutscene_ending_stars_free_peach(struct Camera *c) {
   7279     cutscene_event(cutscene_ending_reset_spline, c, 0, 0);
   7280     cutscene_event(cutscene_ending_fly_up_to_window, c, 0, -1);
   7281     player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000);
   7282 }
   7283 
   7284 /**
   7285  * Move the camera to the ground as Mario lands.
   7286  */
   7287 BAD_RETURN(s32) cutscene_ending_mario_land(struct Camera *c) {
   7288     vec3f_set(c->focus, sEndingFlyToWindowFocus[0].point[0], sEndingFlyToWindowFocus[0].point[1] + 80.f, sEndingFlyToWindowFocus[0].point[2]);
   7289     vec3f_set(c->pos, sEndingFlyToWindowPos[0].point[0], sEndingFlyToWindowPos[0].point[1], sEndingFlyToWindowPos[0].point[2] + 150.f);
   7290     player2_rotate_cam(c, -0x800, 0x2000, -0x2000, 0x2000);
   7291 }
   7292 
   7293 /**
   7294  * Move the camera closer to peach appearing.
   7295  */
   7296 BAD_RETURN(s32) cutscene_ending_peach_appear_closeup(struct Camera *c) {
   7297     vec3f_set(c->pos, 179.f, 2463.f, -1216.f);
   7298     c->pos[1] = gCutsceneFocus->oPosY + 35.f;
   7299     vec3f_set(c->focus, gCutsceneFocus->oPosX, gCutsceneFocus->oPosY + 125.f, gCutsceneFocus->oPosZ);
   7300 }
   7301 
   7302 /**
   7303  * Peach fades in, the camera focuses on her.
   7304  */
   7305 BAD_RETURN(s32) cutscene_ending_peach_appears(struct Camera *c) {
   7306     cutscene_event(cutscene_ending_peach_appear_closeup, c, 0, 0);
   7307     approach_f32_asymptotic_bool(&c->pos[1], gCutsceneFocus->oPosY + 35.f, 0.02f);
   7308     approach_f32_asymptotic_bool(&c->focus[1], gCutsceneFocus->oPosY + 125.f, 0.15f);
   7309     player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000);
   7310 }
   7311 
   7312 /**
   7313  * Reset spline progress, set cvar2 y offset.
   7314  */
   7315 BAD_RETURN(s32) cutscene_ending_peach_descends_start(UNUSED struct Camera *c) {
   7316     cutscene_reset_spline();
   7317     sCutsceneVars[2].point[1] = 150.f;
   7318 }
   7319 
   7320 /**
   7321  * Follow the sEndingPeachDescentCamPos spline, which rotates around peach.
   7322  */
   7323 BAD_RETURN(s32) cutscene_ending_follow_peach_descent(struct Camera *c) {
   7324     move_point_along_spline(c->pos, sEndingPeachDescentCamPos, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   7325     c->pos[1] += gCutsceneFocus->oPosY + sCutsceneVars[3].point[1];
   7326 }
   7327 
   7328 /**
   7329  * Decrease cvar2's y offset while the camera flies backwards to Mario.
   7330  */
   7331 BAD_RETURN(s32) cutscene_ending_peach_descent_lower_focus(UNUSED struct Camera *c) {
   7332     camera_approach_f32_symmetric_bool(&(sCutsceneVars[2].point[1]), 90.f, 0.5f);
   7333 }
   7334 
   7335 /**
   7336  * Keep following the sEndingPeachDescentCamPos spline, which leads back to Mario.
   7337  */
   7338 BAD_RETURN(s32) cutscene_ending_peach_descent_back_to_mario(struct Camera *c) {
   7339     Vec3f pos;
   7340 
   7341     move_point_along_spline(pos, sEndingPeachDescentCamPos, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   7342     c->pos[0] = pos[0];
   7343     c->pos[2] = pos[2];
   7344     approach_f32_asymptotic_bool(&c->pos[1], (pos[1] += gCutsceneFocus->oPosY), 0.07f);
   7345 }
   7346 
   7347 /**
   7348  * Peach starts floating to the ground. Rotate the camera around her, then fly backwards to Mario when
   7349  * she lands.
   7350  */
   7351 BAD_RETURN(s32) cutscene_ending_peach_descends(struct Camera *c) {
   7352     cutscene_event(cutscene_ending_peach_descends_start, c, 0, 0);
   7353     cutscene_event(cutscene_ending_follow_peach_descent, c, 0, 299);
   7354     cutscene_event(cutscene_ending_peach_descent_back_to_mario, c, 300, -1);
   7355     cutscene_event(cutscene_ending_peach_descent_lower_focus, c, 300, -1);
   7356     vec3f_set(c->focus, gCutsceneFocus->oPosX, sCutsceneVars[2].point[1] + gCutsceneFocus->oPosY,
   7357               gCutsceneFocus->oPosZ);
   7358     player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000);
   7359 }
   7360 
   7361 /**
   7362  * Mario runs across the bridge to peach, and takes off his cap.
   7363  * Follow the sEndingMarioToPeach* splines while Mario runs across.
   7364  */
   7365 BAD_RETURN(s32) cutscene_ending_mario_to_peach(struct Camera *c) {
   7366     cutscene_event(cutscene_ending_reset_spline, c, 0, 0);
   7367     move_point_along_spline(c->pos, sEndingMarioToPeachPos, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   7368     move_point_along_spline(c->focus, sEndingMarioToPeachFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   7369     player2_rotate_cam(c, -0x2000, 0x2000, -0x2000, 0x2000);
   7370 }
   7371 
   7372 /**
   7373  * Make the focus follow the sEndingLookUpAtCastle spline.
   7374  */
   7375 BAD_RETURN(s32) cutscene_ending_look_up_at_castle(UNUSED struct Camera *c) {
   7376     move_point_along_spline(c->focus, sEndingLookUpAtCastle, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   7377 }
   7378 
   7379 /**
   7380  * Peach opens her eyes and the camera looks at the castle window again.
   7381  */
   7382 BAD_RETURN(s32) cutscene_ending_peach_wakeup(struct Camera *c) {
   7383     cutscene_event(cutscene_ending_reset_spline, c, 0, 0);
   7384     cutscene_event(cutscene_ending_look_up_at_castle, c, 0, 0);
   7385 #ifdef VERSION_EU
   7386     cutscene_event(cutscene_ending_look_up_at_castle, c, 265, -1);
   7387     cutscene_spawn_obj(7, 315);
   7388     cutscene_spawn_obj(9, 355);
   7389 #else
   7390     cutscene_event(cutscene_ending_look_up_at_castle, c, 250, -1);
   7391     cutscene_spawn_obj(7, 300);
   7392     cutscene_spawn_obj(9, 340);
   7393 #endif
   7394     vec3f_set(c->pos, -163.f, 978.f, -1082.f);
   7395     player2_rotate_cam(c, -0x800, 0x2000, -0x2000, 0x2000);
   7396 }
   7397 
   7398 /**
   7399  * Side view of peach and Mario. Peach thanks Mario for saving her.
   7400  */
   7401 BAD_RETURN(s32) cutscene_ending_dialog(struct Camera *c) {
   7402     vec3f_set(c->focus, 11.f, 983.f, -1273.f);
   7403     vec3f_set(c->pos, -473.f, 970.f, -1152.f);
   7404     player2_rotate_cam(c, -0x800, 0x2000, -0x2000, 0x2000);
   7405 }
   7406 
   7407 /**
   7408  * Zoom in and move the camera close to Mario and peach.
   7409  */
   7410 BAD_RETURN(s32) cutscene_ending_kiss_closeup(struct Camera *c) {
   7411     set_fov_function(CAM_FOV_SET_29);
   7412     vec3f_set(c->focus, 350.f, 1034.f, -1216.f);
   7413     vec3f_set(c->pos, -149.f, 1021.f, -1216.f);
   7414 }
   7415 
   7416 /**
   7417  * Fly back and zoom out for Mario's spin after the kiss.
   7418  */
   7419 BAD_RETURN(s32) cutscene_ending_kiss_here_we_go(struct Camera *c) {
   7420     Vec3f pos, foc;
   7421 
   7422     set_fov_function(CAM_FOV_DEFAULT);
   7423     vec3f_set(foc, 233.f, 1068.f, -1298.f);
   7424     vec3f_set(pos, -250.f, 966.f, -1111.f);
   7425     //! another double typo
   7426     approach_vec3f_asymptotic(c->pos, pos, 0.2, 0.1f, 0.2f);
   7427     approach_vec3f_asymptotic(c->focus, foc, 0.2, 0.1f, 0.2f);
   7428 }
   7429 
   7430 /**
   7431  * Peach kisses Mario on the nose.
   7432  */
   7433 BAD_RETURN(s32) cutscene_ending_kiss(struct Camera *c) {
   7434     cutscene_event(cutscene_ending_kiss_closeup, c, 0, 0);
   7435 #ifdef VERSION_EU
   7436     cutscene_event(cutscene_ending_kiss_here_we_go, c, 185, -1);
   7437 #else
   7438     cutscene_event(cutscene_ending_kiss_here_we_go, c, 155, -1);
   7439 #endif
   7440     player2_rotate_cam(c, -0x800, 0x2000, -0x2000, 0x2000);
   7441 }
   7442 
   7443 /**
   7444  * Make the focus follow sEndingLookAtSkyFocus.
   7445  */
   7446 BAD_RETURN(s32) cutscene_ending_look_at_sky(struct Camera *c) {
   7447     move_point_along_spline(c->focus, sEndingLookAtSkyFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   7448     vec3f_set(c->pos, 699.f, 1680.f, -703.f);
   7449 }
   7450 
   7451 /**
   7452  * Zoom in the fov. The fovFunc was just set to default, so it wants to approach 45. But while this is
   7453  * called, it will stay at about 37.26f
   7454  */
   7455 BAD_RETURN(s32) cutscene_ending_zoom_fov(UNUSED struct Camera *c) {
   7456     sFOVState.fov = 37.f;
   7457 }
   7458 
   7459 /**
   7460  * Peach suggests baking a cake for Mario. Mario looks back at the camera before going inside the castle.
   7461  */
   7462 BAD_RETURN(s32) cutscene_ending_cake_for_mario(struct Camera *c) {
   7463     cutscene_event(cutscene_ending_reset_spline, c, 0, 0);
   7464     cutscene_event(cutscene_ending_look_at_sky, c, 0, 0);
   7465     cutscene_event(cutscene_ending_zoom_fov, c, 0, 499);
   7466     cutscene_event(cutscene_ending_look_at_sky, c, 500, -1);
   7467     cutscene_spawn_obj(8, 600);
   7468     cutscene_spawn_obj(8, 608);
   7469     cutscene_spawn_obj(8, 624);
   7470     cutscene_spawn_obj(8, 710);
   7471 }
   7472 
   7473 /**
   7474  * Stop the ending cutscene, reset the fov.
   7475  */
   7476 BAD_RETURN(s32) cutscene_ending_stop(struct Camera *c) {
   7477     set_fov_function(CAM_FOV_SET_45);
   7478     c->cutscene = 0;
   7479     gCutsceneTimer = CUTSCENE_STOP;
   7480 }
   7481 
   7482 /**
   7483  * Start the grand star cutscene.
   7484  * cvar0 is a relative offset from Mario.
   7485  * cvar1 is the is the camera's goal position.
   7486  */
   7487 BAD_RETURN(s32) cutscene_grand_star_start(UNUSED struct Camera *c) {
   7488     vec3f_set(sCutsceneVars[0].point, 0.f, 150.f, -600.f);
   7489     offset_rotated(sCutsceneVars[1].point, sMarioCamState->pos, sCutsceneVars[0].point, sMarioCamState->faceAngle);
   7490     sCutsceneVars[1].point[1] = 457.f;
   7491 }
   7492 
   7493 /**
   7494  * Make the camera fly to the front of Mario.
   7495  */
   7496 BAD_RETURN(s32) cutscene_grand_star_front_of_mario(struct Camera *c) {
   7497     f32 goalDist;
   7498     s16 goalPitch, goalYaw;
   7499     f32 dist;
   7500     s16 pitch, yaw;
   7501 
   7502     vec3f_get_dist_and_angle(sMarioCamState->pos, sCutsceneVars[1].point, &goalDist, &goalPitch, &goalYaw);
   7503     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   7504     approach_f32_asymptotic_bool(&dist, goalDist, 0.1f);
   7505     approach_s16_asymptotic_bool(&pitch, goalPitch, 32);
   7506     approach_s16_asymptotic_bool(&yaw, goalYaw + 0x1200, 20);
   7507     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   7508 }
   7509 
   7510 /**
   7511  * Started shortly after Mario starts the triple jump. Stores Mario's face angle and zeros cvar2.
   7512  */
   7513 BAD_RETURN(s32) cutscene_grand_star_mario_jump(UNUSED struct Camera *c) {
   7514     vec3s_set(sCutsceneVars[0].angle, 0, sMarioCamState->faceAngle[1], 0);
   7515     vec3f_set(sCutsceneVars[2].point, 0.f, 0.f, 0.f);
   7516 }
   7517 
   7518 /**
   7519  * Accelerate cvar2 to point back and to the left (relative to the camera).
   7520  */
   7521 BAD_RETURN(s32) cutscene_grand_star_accel_cvar2(UNUSED struct Camera *c) {
   7522     camera_approach_f32_symmetric_bool(&sCutsceneVars[2].point[2], -40.f, 2.0f);
   7523     sCutsceneVars[2].point[0] = 5.0f;
   7524 }
   7525 
   7526 /**
   7527  * Decrease cvar2 offset, follow Mario by directly updating the camera's pos.
   7528  */
   7529 BAD_RETURN(s32) cutscene_grand_star_approach_mario(struct Camera *c) {
   7530     camera_approach_f32_symmetric_bool(&sCutsceneVars[2].point[2], 0.f, 2.f);
   7531     sCutsceneVars[2].point[0] = 0.f;
   7532     approach_f32_asymptotic_bool(&c->pos[0], sMarioCamState->pos[0], 0.01f);
   7533     approach_f32_asymptotic_bool(&c->pos[2], sMarioCamState->pos[2], 0.01f);
   7534 }
   7535 
   7536 /**
   7537  * Offset the camera's position by cvar2. Before Mario triple jumps, this moves back and to the left.
   7538  * After the triple jump, cvar2 decelerates to 0.
   7539  */
   7540 BAD_RETURN(s32) cutscene_grand_star_move_cvar2(struct Camera *c) {
   7541     offset_rotated(c->pos, c->pos, sCutsceneVars[2].point, sCutsceneVars[0].angle);
   7542 }
   7543 
   7544 BAD_RETURN(s32) cutscene_grand_star_focus_mario(struct Camera *c) {
   7545     Vec3f foc;
   7546 
   7547     vec3f_set(foc, sMarioCamState->pos[0], (sMarioCamState->pos[1] - 307.f) * 0.5f + 407.f, sMarioCamState->pos[2]);
   7548     approach_vec3f_asymptotic(c->focus, foc, 0.5f, 0.8f, 0.5f);
   7549 }
   7550 
   7551 /**
   7552  * The first part of the grand star cutscene, after Mario has collected the grand star.
   7553  */
   7554 BAD_RETURN(s32) cutscene_grand_star(struct Camera *c) {
   7555     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   7556     cutscene_event(cutscene_grand_star_start, c, 0, 0);
   7557     cutscene_event(cutscene_grand_star_front_of_mario, c, 0, 109);
   7558     cutscene_event(cutscene_grand_star_focus_mario, c, 0, -1);
   7559     cutscene_event(cutscene_grand_star_mario_jump, c, 110, 110);
   7560     cutscene_event(cutscene_grand_star_accel_cvar2, c, 110, 159);
   7561     cutscene_event(cutscene_grand_star_approach_mario, c, 160, -1);
   7562     cutscene_event(cutscene_grand_star_move_cvar2, c, 110, -1);
   7563 }
   7564 
   7565 /**
   7566  * Zero the cvars that are used when Mario is flying.
   7567  */
   7568 BAD_RETURN(s32) cutscene_grand_star_fly_start(struct Camera *c) {
   7569     //! cvar7 is unused in grand star
   7570     vec3f_set(sCutsceneVars[7].point, 0.5f, 0.5f, 0.5f);
   7571     //! cvar6 is unused in grand star
   7572     vec3f_set(sCutsceneVars[6].point, 0.01f, 0.01f, 0.01f);
   7573     vec3f_set(sCutsceneVars[4].point, 0.f, 0.f, 0.f);
   7574     vec3f_set(sCutsceneVars[5].point, 0.f, c->focus[1] - sMarioCamState->pos[1], 0.f);
   7575     sCutsceneVars[8].point[2] = 0.f;
   7576     sCutsceneVars[8].point[0] = 0.f;
   7577 }
   7578 
   7579 /**
   7580  * Decrease the cvar offsets so that Lakitu flies closer to Mario.
   7581  */
   7582 BAD_RETURN(s32) cutscene_grand_star_fly_move_to_mario(UNUSED struct Camera *c) {
   7583     Vec3f posOff;
   7584 
   7585     vec3f_set(posOff, -600.f, 0.f, -400.f);
   7586     approach_vec3f_asymptotic(sCutsceneVars[4].point, posOff, 0.05f, 0.05f, 0.05f);
   7587     camera_approach_f32_symmetric_bool(&sCutsceneVars[5].point[1], 0.f, 2.f);
   7588     camera_approach_f32_symmetric_bool(&sCutsceneVars[5].point[2], -200.f, 6.f);
   7589 }
   7590 
   7591 /**
   7592  * Gradually increase the cvar offsets so Lakitu flies away. Mario flies offscreen to the right.
   7593  *
   7594  * cvar4 is the position offset from Mario.
   7595  * cvar5 is the focus offset from Mario.
   7596  * cvar8.point[0] is the approach velocity.
   7597  */
   7598 BAD_RETURN(s32) cutscene_grand_star_fly_mario_offscreen(UNUSED struct Camera *c) {
   7599     camera_approach_f32_symmetric_bool(&sCutsceneVars[8].point[0], 15.f, 0.1f);
   7600 
   7601     camera_approach_f32_symmetric_bool(&sCutsceneVars[4].point[0], -2000.f, sCutsceneVars[8].point[0]);
   7602     camera_approach_f32_symmetric_bool(&sCutsceneVars[4].point[1], 1200.f, sCutsceneVars[8].point[0] / 10.f);
   7603     camera_approach_f32_symmetric_bool(&sCutsceneVars[4].point[2], 1000.f, sCutsceneVars[8].point[0] / 10.f);
   7604 
   7605     camera_approach_f32_symmetric_bool(&sCutsceneVars[5].point[0], 0.f, sCutsceneVars[8].point[0]);
   7606     camera_approach_f32_symmetric_bool(&sCutsceneVars[5].point[1], 1200.f, sCutsceneVars[8].point[0] / 2);
   7607     camera_approach_f32_symmetric_bool(&sCutsceneVars[5].point[2], 1000.f, sCutsceneVars[8].point[0] / 1.5f);
   7608 }
   7609 
   7610 /**
   7611  * Make Lakitu approach the cvars.
   7612  * cvar4 is the position offset.
   7613  * cvar5 is the focus offset.
   7614  */
   7615 BAD_RETURN(s32) cutscene_grand_star_fly_app_cvars(struct Camera *c) {
   7616     Vec3f goalPos, goalFoc;
   7617     f32 dist;
   7618     s16 pitch, yaw;
   7619 
   7620     camera_approach_f32_symmetric_bool(&sCutsceneVars[8].point[2], 90.f, 2.5f);
   7621     offset_rotated(goalPos, sMarioCamState->pos, sCutsceneVars[4].point, sMarioCamState->faceAngle);
   7622     offset_rotated(goalFoc, sMarioCamState->pos, sCutsceneVars[5].point, sMarioCamState->faceAngle);
   7623 
   7624     // Move towards goalPos by cvar8's Z speed
   7625     vec3f_get_dist_and_angle(goalPos, c->pos, &dist, &pitch, &yaw);
   7626     camera_approach_f32_symmetric_bool(&dist, 0, sCutsceneVars[8].point[2]);
   7627     vec3f_set_dist_and_angle(goalPos, c->pos, dist, pitch, yaw);
   7628 
   7629     approach_vec3f_asymptotic(c->pos, goalPos, 0.01f, 0.01f, 0.01f);
   7630     approach_vec3f_asymptotic(c->focus, goalFoc, 0.5f, 0.8f, 0.5f);
   7631 }
   7632 
   7633 /**
   7634  * Part of the grand star cutscene, starts after Mario is flying.
   7635  *
   7636  * cvar4 and cvar5 are directions, relative to Mario:
   7637  * cvar4 is used as the camera position's offset from Mario.
   7638  * cvar5 is used as the camera focus's offset from Mario.
   7639  *
   7640  * cvar8.point[2] is Lakitu's speed.
   7641  */
   7642 BAD_RETURN(s32) cutscene_grand_star_fly(struct Camera *c) {
   7643     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   7644     cutscene_event(cutscene_grand_star_fly_start, c, 0, 0);
   7645     cutscene_event(cutscene_grand_star_fly_move_to_mario, c, 0, 140);
   7646     cutscene_event(cutscene_grand_star_fly_mario_offscreen, c, 141, -1);
   7647     cutscene_event(cutscene_grand_star_fly_app_cvars, c, 0, -1);
   7648 }
   7649 
   7650 /**
   7651  * Adjust the camera focus towards a point `dist` units in front of Mario.
   7652  * @param dist distance in Mario's forward direction. Note that this is relative to Mario, so a negative
   7653  *        distance will focus in front of Mario, and a positive distance will focus behind him.
   7654  */
   7655 void focus_in_front_of_mario(struct Camera *c, f32 dist, f32 speed) {
   7656     Vec3f goalFocus, offset;
   7657 
   7658     offset[0] = 0.f;
   7659     offset[2] = dist;
   7660     offset[1] = 100.f;
   7661 
   7662     offset_rotated(goalFocus, sMarioCamState->pos, offset, sMarioCamState->faceAngle);
   7663     approach_vec3f_asymptotic(c->focus, goalFocus, speed, speed, speed);
   7664 }
   7665 
   7666 /**
   7667  * Approach Mario and look up. Since Mario faces the camera when he collects the star, there's no need
   7668  * to worry about the camera's yaw.
   7669  */
   7670 BAD_RETURN(s32) cutscene_dance_move_to_mario(struct Camera *c) {
   7671     s16 pitch, yaw;
   7672     f32 dist;
   7673 
   7674     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   7675     approach_f32_asymptotic_bool(&dist, 600.f, 0.3f);
   7676     approach_s16_asymptotic_bool(&pitch, 0x1000, 0x10);
   7677     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   7678 }
   7679 
   7680 BAD_RETURN(s32) cutscene_dance_rotate(struct Camera *c) {
   7681     rotate_and_move_vec3f(c->pos, sMarioCamState->pos, 0, 0, 0x200);
   7682 }
   7683 
   7684 BAD_RETURN(s32) cutscene_dance_rotate_move_back(struct Camera *c) {
   7685     rotate_and_move_vec3f(c->pos, sMarioCamState->pos, -15.f, 0, 0);
   7686 }
   7687 
   7688 BAD_RETURN(s32) cutscene_dance_rotate_move_towards_mario(struct Camera *c) {
   7689     rotate_and_move_vec3f(c->pos, sMarioCamState->pos, 20.f, 0, 0);
   7690 }
   7691 
   7692 /**
   7693  * Speculated to be dance-related due to its proximity to the other dance functions
   7694  */
   7695 UNUSED static BAD_RETURN(s32) cutscene_dance_unused(UNUSED struct Camera *c) {
   7696 }
   7697 
   7698 /**
   7699  * Slowly turn to the point 100 units in front of Mario
   7700  */
   7701 BAD_RETURN(s32) cutscene_dance_default_focus_mario(struct Camera *c) {
   7702     focus_in_front_of_mario(c, -100.f, 0.2f);
   7703 }
   7704 
   7705 /**
   7706  * Focus twice as far away as default dance, and move faster.
   7707  */
   7708 BAD_RETURN(s32) cutscene_dance_rotate_focus_mario(struct Camera *c) {
   7709     focus_in_front_of_mario(c, -200.f, 0.03f);
   7710 }
   7711 
   7712 BAD_RETURN(s32) cutscene_dance_shake_fov(UNUSED struct Camera *c) {
   7713     set_fov_shake(0x200, 0x28, 0x8000);
   7714 }
   7715 
   7716 /**
   7717  * Handles both the default and rotate dance cutscenes.
   7718  * In the default dance: the camera moves closer to Mario, then stays in place.
   7719  * In the rotate dance: the camera moves closer and rotates clockwise around Mario.
   7720  */
   7721 BAD_RETURN(s32) cutscene_dance_default_rotate(struct Camera *c) {
   7722     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   7723     sYawSpeed = 0;
   7724     set_fov_function(CAM_FOV_DEFAULT);
   7725     cutscene_event(cutscene_dance_default_focus_mario, c, 0, 20);
   7726     cutscene_event(cutscene_dance_move_to_mario, c, 0, 39);
   7727     // Shake the camera on the 4th beat of the music, when Mario gives the peace sign.
   7728     cutscene_event(cutscene_dance_shake_fov, c, 40, 40);
   7729 
   7730     if (c->cutscene != CUTSCENE_DANCE_DEFAULT) { // CUTSCENE_DANCE_ROTATE
   7731         cutscene_event(cutscene_dance_rotate_focus_mario, c, 75, 102);
   7732         cutscene_event(cutscene_dance_rotate, c, 50, -1);
   7733         // These two functions move the camera away and then towards Mario.
   7734         cutscene_event(cutscene_dance_rotate_move_back, c, 50, 80);
   7735         cutscene_event(cutscene_dance_rotate_move_towards_mario, c, 70, 90);
   7736     } else {
   7737         // secret star, 100 coin star, or bowser red coin star.
   7738         if ((sMarioCamState->action != ACT_STAR_DANCE_NO_EXIT)
   7739             && (sMarioCamState->action != ACT_STAR_DANCE_WATER)
   7740             && (sMarioCamState->action != ACT_STAR_DANCE_EXIT)) {
   7741             gCutsceneTimer = CUTSCENE_STOP;
   7742             c->cutscene = 0;
   7743             transition_next_state(c, 20);
   7744             sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   7745         }
   7746     }
   7747 }
   7748 
   7749 /**
   7750  * If the camera's yaw is out of the range of `absYaw` +- `yawMax`, then set the yaw to `absYaw`
   7751  */
   7752 BAD_RETURN(s32) star_dance_bound_yaw(struct Camera *c, s16 absYaw, s16 yawMax) {
   7753     s16 dummyPitch, yaw;
   7754     f32 distCamToMario;
   7755     s16 yawFromAbs;
   7756 
   7757     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &distCamToMario, &dummyPitch, &yaw);
   7758     yawFromAbs = yaw - absYaw;
   7759 
   7760     // Because angles are s16, this checks if yaw is negative
   7761     if ((yawFromAbs & 0x8000) != 0) {
   7762         yawFromAbs = -yawFromAbs;
   7763     }
   7764     if (yawFromAbs > yawMax) {
   7765         yaw = absYaw;
   7766         c->nextYaw = yaw;
   7767         c->yaw = yaw;
   7768     }
   7769 }
   7770 
   7771 /**
   7772  * Start the closeup dance cutscene by restricting the camera's yaw in certain areas.
   7773  * Store the camera's focus in cvar9.
   7774  */
   7775 BAD_RETURN(s32) cutscene_dance_closeup_start(struct Camera *c) {
   7776     UNUSED u8 filler[8];
   7777 
   7778     if ((gLastCompletedStarNum == 4) && (gCurrCourseNum == COURSE_JRB)) {
   7779         star_dance_bound_yaw(c, 0x0, 0x4000);
   7780     }
   7781     if ((gLastCompletedStarNum == 1) && (gCurrCourseNum == COURSE_DDD)) {
   7782         star_dance_bound_yaw(c, 0x8000, 0x5000);
   7783     }
   7784     if ((gLastCompletedStarNum == 5) && (gCurrCourseNum == COURSE_WDW)) {
   7785         star_dance_bound_yaw(c, 0x8000, 0x800);
   7786     }
   7787 
   7788     vec3f_copy(sCutsceneVars[9].point, c->focus);
   7789     //! cvar8 is unused in the closeup cutscene
   7790     sCutsceneVars[8].angle[0] = 0x2000;
   7791 }
   7792 
   7793 /**
   7794  * Focus the camera on Mario eye height.
   7795  */
   7796 BAD_RETURN(s32) cutscene_dance_closeup_focus_mario(struct Camera *c) {
   7797     Vec3f marioPos;
   7798 
   7799     vec3f_set(marioPos, sMarioCamState->pos[0], sMarioCamState->pos[1] + 125.f, sMarioCamState->pos[2]);
   7800     approach_vec3f_asymptotic(sCutsceneVars[9].point, marioPos, 0.2f, 0.2f, 0.2f);
   7801     vec3f_copy(c->focus, sCutsceneVars[9].point);
   7802 }
   7803 
   7804 /**
   7805  * Fly above Mario, looking down.
   7806  */
   7807 BAD_RETURN(s32) cutscene_dance_closeup_fly_above(struct Camera *c) {
   7808     s16 pitch, yaw;
   7809     f32 dist;
   7810     s16 goalPitch = 0x1800;
   7811 
   7812     if ((gLastCompletedStarNum == 6 && gCurrCourseNum == COURSE_SL) ||
   7813         (gLastCompletedStarNum == 4 && gCurrCourseNum == COURSE_TTC)) {
   7814         goalPitch = 0x800;
   7815     }
   7816 
   7817     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   7818     approach_f32_asymptotic_bool(&dist, 800.f, 0.05f);
   7819     approach_s16_asymptotic_bool(&pitch, goalPitch, 16);
   7820     approach_s16_asymptotic_bool(&yaw, c->yaw, 8);
   7821     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   7822 }
   7823 
   7824 /**
   7825  * Fly closer right when Mario gives the peace sign.
   7826  */
   7827 BAD_RETURN(s32) cutscene_dance_closeup_fly_closer(struct Camera *c) {
   7828     s16 pitch, yaw;
   7829     f32 dist;
   7830 
   7831     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   7832     approach_f32_asymptotic_bool(&dist, 240.f, 0.4f);
   7833     approach_s16_asymptotic_bool(&yaw, c->yaw, 8);
   7834     approach_s16_asymptotic_bool(&pitch, 0x1000, 5);
   7835     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   7836 }
   7837 
   7838 /**
   7839  * Zoom in by increasing fov to 80 degrees. Most dramatic zoom in the game.
   7840  */
   7841 BAD_RETURN(s32) cutscene_dance_closeup_zoom(UNUSED struct Camera *c) {
   7842     set_fov_function(CAM_FOV_APP_80);
   7843 }
   7844 
   7845 /**
   7846  * Shake fov, starts on the first frame Mario has the peace sign up.
   7847  */
   7848 BAD_RETURN(s32) cutscene_dance_closeup_shake_fov(UNUSED struct Camera *c) {
   7849     set_fov_shake(0x300, 0x30, 0x8000);
   7850 }
   7851 
   7852 /**
   7853  * The camera moves in for a closeup on Mario. Used for stars that are underwater or in tight places.
   7854  */
   7855 BAD_RETURN(s32) cutscene_dance_closeup(struct Camera *c) {
   7856     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   7857 
   7858     if (sMarioCamState->action == ACT_STAR_DANCE_WATER) {
   7859         cutscene_event(cutscene_dance_closeup_start, c, 0, 0);
   7860         cutscene_event(cutscene_dance_closeup_focus_mario, c, 0, -1);
   7861         cutscene_event(cutscene_dance_closeup_fly_above, c, 0, 62);
   7862         cutscene_event(cutscene_dance_closeup_fly_closer, c, 63, -1);
   7863         cutscene_event(cutscene_dance_closeup_zoom, c, 63, 63);
   7864         cutscene_event(cutscene_dance_closeup_shake_fov, c, 70, 70);
   7865     } else {
   7866         cutscene_event(cutscene_dance_closeup_start, c, 0, 0);
   7867         cutscene_event(cutscene_dance_closeup_focus_mario, c, 0, -1);
   7868         // Almost twice as fast as under water
   7869         cutscene_event(cutscene_dance_closeup_fly_above, c, 0, 32);
   7870         cutscene_event(cutscene_dance_closeup_fly_closer, c, 33, -1);
   7871         cutscene_event(cutscene_dance_closeup_zoom, c, 33, 33);
   7872         cutscene_event(cutscene_dance_closeup_shake_fov, c, 40, 40);
   7873     }
   7874     set_handheld_shake(HAND_CAM_SHAKE_CUTSCENE);
   7875 }
   7876 
   7877 /**
   7878  * cvar8.point[2] is the amount to increase distance from Mario
   7879  */
   7880 BAD_RETURN(s32) cutscene_dance_fly_away_start(struct Camera *c) {
   7881     Vec3f areaCenter;
   7882 
   7883     vec3f_copy(sCutsceneVars[9].point, c->focus);
   7884     sCutsceneVars[8].point[2] = 65.f;
   7885 
   7886     if (c->mode == CAMERA_MODE_RADIAL) {
   7887         vec3f_set(areaCenter, c->areaCenX, c->areaCenY, c->areaCenZ);
   7888         c->yaw = calculate_yaw(areaCenter, c->pos);
   7889         c->nextYaw = c->yaw;
   7890     }
   7891 
   7892     // Restrict the camera yaw in tight spaces
   7893     if ((gLastCompletedStarNum == 6) && (gCurrCourseNum == COURSE_CCM)) {
   7894         star_dance_bound_yaw(c, 0x5600, 0x800);
   7895     }
   7896     if ((gLastCompletedStarNum == 2) && (gCurrCourseNum == COURSE_TTM)) {
   7897         star_dance_bound_yaw(c, 0x0,    0x800);
   7898     }
   7899     if ((gLastCompletedStarNum == 1) && (gCurrCourseNum == COURSE_SL)) {
   7900         star_dance_bound_yaw(c, 0x2000, 0x800);
   7901     }
   7902     if ((gLastCompletedStarNum == 3) && (gCurrCourseNum == COURSE_RR)) {
   7903         star_dance_bound_yaw(c, 0x0,    0x800);
   7904     }
   7905 }
   7906 
   7907 BAD_RETURN(s32) cutscene_dance_fly_away_approach_mario(struct Camera *c) {
   7908     s16 pitch, yaw;
   7909     f32 dist;
   7910 
   7911     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   7912     approach_f32_asymptotic_bool(&dist, 600.f, 0.3f);
   7913     approach_s16_asymptotic_bool(&pitch, 0x1000, 16);
   7914     approach_s16_asymptotic_bool(&yaw, c->yaw, 8);
   7915     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   7916 }
   7917 
   7918 BAD_RETURN(s32) cutscene_dance_fly_away_focus_mario(struct Camera *c) {
   7919     Vec3f marioPos;
   7920 
   7921     vec3f_set(marioPos, sMarioCamState->pos[0], sMarioCamState->pos[1] + 125.f, sMarioCamState->pos[2]);
   7922     approach_vec3f_asymptotic(sCutsceneVars[9].point, marioPos, 0.2f, 0.2f, 0.2f);
   7923     vec3f_copy(c->focus, sCutsceneVars[9].point);
   7924 }
   7925 
   7926 /**
   7927  * Slowly pan the camera downwards and to the camera's right, using cvar9's angle.
   7928  */
   7929 void cutscene_pan_cvar9(struct Camera *c) {
   7930     vec3f_copy(c->focus, sCutsceneVars[9].point);
   7931     sCutsceneVars[9].angle[0] -= 29;
   7932     sCutsceneVars[9].angle[1] += 29;
   7933     pan_camera(c, sCutsceneVars[9].angle[0], sCutsceneVars[9].angle[1]);
   7934 }
   7935 
   7936 /**
   7937  * Move backwards and rotate slowly around Mario.
   7938  */
   7939 BAD_RETURN(s32) cutscene_dance_fly_rotate_around_mario(struct Camera *c) {
   7940     cutscene_pan_cvar9(c);
   7941     rotate_and_move_vec3f(c->pos, sMarioCamState->pos, sCutsceneVars[8].point[2], 0, 0);
   7942 }
   7943 
   7944 /**
   7945  * Rotate quickly while Lakitu flies up.
   7946  */
   7947 BAD_RETURN(s32) cutscene_dance_fly_away_rotate_while_flying(struct Camera *c) {
   7948     rotate_and_move_vec3f(c->pos, sMarioCamState->pos, 0, 0, 0x80);
   7949 }
   7950 
   7951 BAD_RETURN(s32) cutscene_dance_fly_away_shake_fov(UNUSED struct Camera *c) {
   7952     set_fov_shake(0x400, 0x30, 0x8000);
   7953 }
   7954 
   7955 /**
   7956  * After collecting the star, Lakitu flies upwards out of the course.
   7957  */
   7958 BAD_RETURN(s32) cutscene_dance_fly_away(struct Camera *c) {
   7959     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   7960     cutscene_event(cutscene_dance_fly_away_start, c, 0, 0);
   7961     cutscene_event(cutscene_dance_fly_away_focus_mario, c, 0, 30);
   7962     cutscene_event(cutscene_dance_fly_away_approach_mario, c, 0, 30);
   7963     cutscene_event(cutscene_dance_fly_rotate_around_mario, c, 55, 124);
   7964     cutscene_event(cutscene_dance_fly_away_rotate_while_flying, c, 55, 124);
   7965     cutscene_event(cutscene_dance_fly_away_shake_fov, c, 40, 40);
   7966     set_fov_function(CAM_FOV_DEFAULT);
   7967     set_handheld_shake(HAND_CAM_SHAKE_STAR_DANCE);
   7968 }
   7969 
   7970 /**
   7971  * Jump the camera pos and focus to cvar 8 and 7.
   7972  * Called every frame, starting after 10, so when these cvars are updated, the camera will jump.
   7973  */
   7974 BAD_RETURN(s32) cutscene_key_dance_jump_cvar(struct Camera *c) {
   7975     offset_rotated(c->pos, sMarioCamState->pos, sCutsceneVars[8].point, sMarioCamState->faceAngle);
   7976     offset_rotated(c->focus, sMarioCamState->pos, sCutsceneVars[7].point, sMarioCamState->faceAngle);
   7977 }
   7978 
   7979 /**
   7980  * Jump to a closeup view of Mario and the key.
   7981  */
   7982 BAD_RETURN(s32) cutscene_key_dance_jump_closeup(UNUSED struct Camera *c) {
   7983     vec3f_set(sCutsceneVars[8].point, 38.f, 171.f, -248.f);
   7984     vec3f_set(sCutsceneVars[7].point, -57.f, 51.f, 187.f);
   7985 }
   7986 
   7987 /**
   7988  * Jump to a view from the lower left (Mario's right).
   7989  */
   7990 BAD_RETURN(s32) cutscene_key_dance_jump_lower_left(UNUSED struct Camera *c) {
   7991     vec3f_set(sCutsceneVars[8].point, -178.f, 62.f, -132.f);
   7992     vec3f_set(sCutsceneVars[7].point, 299.f, 91.f, 58.f);
   7993 }
   7994 
   7995 /**
   7996  * Jump to a rotated view from above.
   7997  */
   7998 BAD_RETURN(s32) cutscene_key_dance_jump_above(UNUSED struct Camera *c) {
   7999     gLakituState.keyDanceRoll = 0x2800;
   8000     vec3f_set(sCutsceneVars[8].point, 89.f, 373.f, -304.f);
   8001     vec3f_set(sCutsceneVars[7].point, 0.f, 127.f, 0.f);
   8002 }
   8003 
   8004 /**
   8005  * Finally, jump to a further view, slightly to Mario's left.
   8006  */
   8007 BAD_RETURN(s32) cutscene_key_dance_jump_last(UNUSED struct Camera *c) {
   8008     gLakituState.keyDanceRoll = 0;
   8009     vec3f_set(sCutsceneVars[8].point, 135.f, 158.f, -673.f);
   8010     vec3f_set(sCutsceneVars[7].point, -20.f, 135.f, -198.f);
   8011 }
   8012 
   8013 BAD_RETURN(s32) cutscene_key_dance_shake_fov(UNUSED struct Camera *c) {
   8014     set_fov_shake(0x180, 0x30, 0x8000);
   8015 }
   8016 
   8017 BAD_RETURN(s32) cutscene_key_dance_handheld_shake(UNUSED struct Camera *c) {
   8018     set_handheld_shake(HAND_CAM_SHAKE_CUTSCENE);
   8019 }
   8020 
   8021 BAD_RETURN(s32) cutscene_key_dance_focus_mario(struct Camera *c) {
   8022     focus_in_front_of_mario(c, 0, 0.2f);
   8023 }
   8024 
   8025 /**
   8026  * Cutscene that plays when Mario collects a key from bowser. It's basically a sequence of four jump
   8027  * cuts.
   8028  */
   8029 BAD_RETURN(s32) cutscene_key_dance(struct Camera *c) {
   8030     cutscene_event(cutscene_dance_move_to_mario, c, 0, 10);
   8031     cutscene_event(cutscene_key_dance_focus_mario, c, 0, 10);
   8032     cutscene_event(cutscene_key_dance_jump_closeup, c, 0, 0);
   8033     cutscene_event(cutscene_key_dance_jump_lower_left, c, 20, 20);
   8034     cutscene_event(cutscene_key_dance_jump_above, c, 35, 35);
   8035     cutscene_event(cutscene_key_dance_jump_last, c, 52, 52);
   8036     cutscene_event(cutscene_key_dance_jump_cvar, c, 11, -1);
   8037     cutscene_event(cutscene_key_dance_shake_fov, c, 54, 54);
   8038     cutscene_event(cutscene_key_dance_handheld_shake, c, 52, -1);
   8039 }
   8040 
   8041 BAD_RETURN(s32) cutscene_bowser_area_shake_fov(UNUSED struct Camera *c) {
   8042     cutscene_set_fov_shake_preset(2);
   8043 }
   8044 
   8045 /**
   8046  * Set oBowserCamAct to 1, which causes bowser to start walking.
   8047  */
   8048 BAD_RETURN(s32) cutscene_bowser_area_start_bowser_walking(UNUSED struct Camera *c) {
   8049     gSecondCameraFocus->oBowserCamAct = BOWSER_CAM_ACT_WALK;
   8050 }
   8051 
   8052 /**
   8053  * Offset the camera from bowser using cvar2 and cvar3
   8054  * @bug cvar2.point is (0,0,0) on the first frame, but because of the warp transition, this behavior
   8055  *      isn't seen. After the first frame, cvar2.point is bowser's position.
   8056  */
   8057 BAD_RETURN(s32) cutscene_bowser_arena_set_pos(struct Camera *c) {
   8058     vec3f_set_dist_and_angle(sCutsceneVars[2].point, c->pos, sCutsceneVars[3].point[2],
   8059                                   sCutsceneVars[3].angle[0], sCutsceneVars[3].angle[1]);
   8060     vec3f_set(sCutsceneVars[2].point, gSecondCameraFocus->oPosX, gSecondCameraFocus->oPosY,
   8061               gSecondCameraFocus->oPosZ);
   8062 }
   8063 
   8064 /**
   8065  * Apply a sine wave to the focus's y coordinate.
   8066  * The y offset starts at 120, then decreases to 0 before reaching ~240 on the last frame.
   8067  */
   8068 BAD_RETURN(s32) cutscene_bowser_arena_focus_sine(UNUSED struct Camera *c) {
   8069     //! unused initialization
   8070     f32 yOff = 150.0f;
   8071 
   8072     // cvar4 was zeroed when the cutscene started.
   8073     yOff = sins(sCutsceneVars[4].angle[1]) * 120.0f + 120.0f;
   8074     sCutsceneVars[4].angle[1] -= 0x200;
   8075     approach_f32_asymptotic_bool(&sCutsceneVars[0].point[1], yOff, 0.5f);
   8076 }
   8077 
   8078 /**
   8079  * Set the camera focus according to cvar0 and cvar2.
   8080  */
   8081 BAD_RETURN(s32) cutscene_bowser_arena_set_focus(struct Camera *c) {
   8082     offset_rotated(c->focus, sCutsceneVars[2].point, sCutsceneVars[0].point, sCutsceneVars[2].angle);
   8083 }
   8084 
   8085 /**
   8086  * Adjust the cvar offsets, making the camera look up, move slightly further back, and focus a little
   8087  * further in front of bowser.
   8088  */
   8089 BAD_RETURN(s32) cutscene_bowser_arena_adjust_offsets(UNUSED struct Camera *c) {
   8090     approach_s16_asymptotic_bool(&sCutsceneVars[3].angle[0], 0x6C8, 30);
   8091     approach_f32_asymptotic_bool(&sCutsceneVars[0].point[2], -200.f, 0.02f);
   8092     approach_f32_asymptotic_bool(&sCutsceneVars[3].point[2], 550.f, 0.02f);
   8093 }
   8094 
   8095 /**
   8096  * Decrease cvar0's z offset, making the camera focus pan left towards bowser.
   8097  */
   8098 BAD_RETURN(s32) cutscene_bowser_arena_pan_left(UNUSED struct Camera *c) {
   8099     approach_f32_asymptotic_bool(&sCutsceneVars[0].point[2], 0.f, 0.05f);
   8100 }
   8101 
   8102 /**
   8103  * Duplicate of cutscene_mario_dialog().
   8104  */
   8105 BAD_RETURN(s32) cutscene_bowser_arena_mario_dialog(UNUSED struct Camera *c) {
   8106     cutscene_common_set_dialog_state(MARIO_DIALOG_LOOK_FRONT);
   8107 }
   8108 
   8109 void cutscene_stop_dialog(UNUSED struct Camera *c) {
   8110     cutscene_common_set_dialog_state(MARIO_DIALOG_STOP);
   8111 }
   8112 
   8113 /**
   8114  * Active for the first 5 frames of the cutscene.
   8115  * cvar3 is the camera's polar offset from bowser
   8116  * cvar2.angle is bowser's move angle
   8117  *
   8118  * cvar0 is the focus offset from bowser
   8119  */
   8120 BAD_RETURN(s32) cutscene_bowser_arena_start(struct Camera *c) {
   8121     sCutsceneVars[3].point[2] = 430.f;
   8122     sCutsceneVars[3].angle[1] = gSecondCameraFocus->oMoveAngleYaw - DEGREES(45);
   8123     sCutsceneVars[3].angle[0] = 0xD90;
   8124 
   8125     //! Tricky math: Bowser starts at (0, 307, -1000), with a moveAngle of (0,0,0). A sane person would
   8126     //! expect this offset to move the focus to (0, 427, -1800).
   8127     //! BUT because offset_rotated() flips the Z direction (to match sm64's coordinate system), this
   8128     //! offset actually moves the focus to (0, 427, -200)
   8129     vec3f_set(sCutsceneVars[0].point, 0.f, 120.f, -800.f);
   8130     vec3s_set(sCutsceneVars[2].angle, gSecondCameraFocus->oMoveAnglePitch,
   8131               gSecondCameraFocus->oMoveAngleYaw, gSecondCameraFocus->oMoveAngleRoll);
   8132 
   8133     // Set the camera's position and focus.
   8134     cutscene_bowser_arena_set_pos(c);
   8135     cutscene_bowser_arena_set_focus(c);
   8136 }
   8137 
   8138 /**
   8139  * Create the dialog box depending on which bowser fight Mario is in.
   8140  */
   8141 BAD_RETURN(s32) bowser_fight_intro_dialog(UNUSED struct Camera *c) {
   8142     s16 dialog;
   8143 
   8144     switch (gCurrLevelNum) {
   8145         case LEVEL_BOWSER_1:
   8146             dialog = DIALOG_067;
   8147             break;
   8148         case LEVEL_BOWSER_2:
   8149             dialog = DIALOG_092;
   8150             break;
   8151         default: // LEVEL_BOWSER_3
   8152             dialog = DIALOG_093;
   8153     }
   8154 
   8155     create_dialog_box(dialog);
   8156 }
   8157 
   8158 /**
   8159  * Create the dialog box and wait until it's gone.
   8160  */
   8161 BAD_RETURN(s32) cutscene_bowser_arena_dialog(struct Camera *c) {
   8162     cutscene_event(bowser_fight_intro_dialog, c, 0, 0);
   8163 
   8164     if (get_dialog_id() == DIALOG_NONE) {
   8165         gCutsceneTimer = CUTSCENE_LOOP;
   8166     }
   8167 }
   8168 
   8169 /**
   8170  * End the bowser arena cutscene.
   8171  */
   8172 BAD_RETURN(s32) cutscene_bowser_arena_end(struct Camera *c) {
   8173     cutscene_stop_dialog(c);
   8174     c->cutscene = 0;
   8175     transition_next_state(c, 20);
   8176     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   8177     sModeOffsetYaw = sMarioCamState->faceAngle[1] + DEGREES(90);
   8178     gSecondCameraFocus->oBowserCamAct = BOWSER_CAM_ACT_END;
   8179 }
   8180 
   8181 /**
   8182  * Cutscene that plays when Mario enters a bowser fight.
   8183  */
   8184 BAD_RETURN(s32) cutscene_bowser_arena(struct Camera *c) {
   8185     //! This does nothing, but may have been used in development
   8186     cutscene_spawn_obj(2, 0);
   8187 
   8188     if (gSecondCameraFocus != NULL) {
   8189         cutscene_event(cutscene_bowser_arena_mario_dialog, c, 0, -1);
   8190         cutscene_event(cutscene_bowser_arena_start, c, 0, 5);
   8191         cutscene_event(cutscene_bowser_area_start_bowser_walking, c, 40, 40);
   8192         cutscene_event(cutscene_bowser_area_shake_fov, c, 145, 145);
   8193         cutscene_event(cutscene_bowser_arena_set_pos, c, 40, -1);
   8194         cutscene_event(cutscene_bowser_arena_pan_left, c, 40, 99);
   8195         cutscene_event(cutscene_bowser_arena_adjust_offsets, c, 100, -1);
   8196         cutscene_event(cutscene_bowser_arena_focus_sine, c, 40, 140);
   8197         cutscene_event(cutscene_bowser_arena_set_focus, c, 40, -1);
   8198         cutscene_event(cutscene_shake_explosion, c, 60, 60);
   8199         cutscene_event(cutscene_shake_explosion, c, 82, 82);
   8200         cutscene_event(cutscene_shake_explosion, c, 109, 109);
   8201         cutscene_event(cutscene_shake_explosion, c, 127, 127);
   8202     }
   8203 }
   8204 
   8205 BAD_RETURN(s32) cutscene_star_spawn_store_info(struct Camera *c) {
   8206     store_info_star(c);
   8207 }
   8208 
   8209 /**
   8210  * Focus on the top of the star.
   8211  */
   8212 BAD_RETURN(s32) cutscene_star_spawn_focus_star(struct Camera *c) {
   8213     UNUSED u8 filler1[4]; // hMul?
   8214     Vec3f starPos;
   8215     UNUSED u8 filler2[4]; // vMul?
   8216 
   8217     if (gCutsceneFocus != NULL) {
   8218         object_pos_to_vec3f(starPos, gCutsceneFocus);
   8219         starPos[1] += gCutsceneFocus->hitboxHeight;
   8220         approach_vec3f_asymptotic(c->focus, starPos, 0.1f, 0.1f, 0.1f);
   8221     }
   8222 }
   8223 
   8224 /**
   8225  * Use boss fight mode's update function to move the focus back.
   8226  */
   8227 BAD_RETURN(s32) cutscene_star_spawn_update_boss_fight(struct Camera *c) {
   8228     Vec3f pos, focus;
   8229 
   8230     update_boss_fight_camera(c, focus, pos);
   8231     approach_vec3f_asymptotic(c->focus, focus, 0.2f, 0.2f, 0.2f);
   8232     approach_vec3f_asymptotic(c->pos, pos, 0.2f, 0.2f, 0.2f);
   8233 }
   8234 
   8235 /**
   8236  * Fly back to the camera's previous pos and focus.
   8237  */
   8238 BAD_RETURN(s32) cutscene_star_spawn_fly_back(struct Camera *c) {
   8239     retrieve_info_star(c);
   8240     transition_next_state(c, 15);
   8241 }
   8242 
   8243 /**
   8244  * Plays when a star spawns (ie from a box).
   8245  */
   8246 BAD_RETURN(s32) cutscene_star_spawn(struct Camera *c) {
   8247     cutscene_event(cutscene_star_spawn_store_info, c, 0, 0);
   8248     cutscene_event(cutscene_star_spawn_focus_star, c, 0, -1);
   8249     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8250 
   8251     if (gObjCutsceneDone) {
   8252         // Set the timer to CUTSCENE_LOOP, which start the next shot.
   8253         gCutsceneTimer = CUTSCENE_LOOP;
   8254     }
   8255 }
   8256 
   8257 /**
   8258  * Move the camera back to Mario.
   8259  */
   8260 BAD_RETURN(s32) cutscene_star_spawn_back(struct Camera *c) {
   8261     if ((c->mode == CAMERA_MODE_BOSS_FIGHT) && (set_cam_angle(0) == CAM_ANGLE_LAKITU)) {
   8262         cutscene_event(cutscene_star_spawn_update_boss_fight, c, 0, -1);
   8263     } else {
   8264         cutscene_event(cutscene_star_spawn_fly_back, c, 0, 0);
   8265     }
   8266 
   8267     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8268     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   8269 }
   8270 
   8271 BAD_RETURN(s32) cutscene_star_spawn_end(struct Camera *c) {
   8272     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8273     gCutsceneTimer = CUTSCENE_STOP;
   8274     c->cutscene = 0;
   8275 }
   8276 
   8277 BAD_RETURN(s32) cutscene_exit_waterfall_warp(struct Camera *c) {
   8278     //! hardcoded position
   8279     vec3f_set(c->pos, -3899.f, 39.f, -5671.f);
   8280 }
   8281 
   8282 /**
   8283  * Look at Mario, used by cutscenes that play when Mario exits a course to castle grounds.
   8284  */
   8285 BAD_RETURN(s32) cutscene_exit_to_castle_grounds_focus_mario(struct Camera *c) {
   8286     vec3f_copy(c->focus, sMarioCamState->pos);
   8287     c->focus[1] = c->pos[1] + (sMarioCamState->pos[1] + 125.f - c->pos[1]) * 0.5f;
   8288     approach_vec3f_asymptotic(c->focus, sMarioCamState->pos, 0.05f, 0.4f, 0.05f);
   8289 }
   8290 
   8291 /**
   8292  * Cutscene that plays when Mario leaves CotMC through the waterfall.
   8293  */
   8294 BAD_RETURN(s32) cutscene_exit_waterfall(struct Camera *c) {
   8295     cutscene_event(cutscene_exit_waterfall_warp, c, 0, 0);
   8296     cutscene_event(cutscene_exit_to_castle_grounds_focus_mario, c, 0, -1);
   8297     update_camera_yaw(c);
   8298 }
   8299 
   8300 /**
   8301  * End the cutscene, used by cutscenes that play when Mario exits a course to castle grounds.
   8302  */
   8303 BAD_RETURN(s32) cutscene_exit_to_castle_grounds_end(struct Camera *c) {
   8304     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8305     gCutsceneTimer = CUTSCENE_STOP;
   8306     c->cutscene = 0;
   8307     update_camera_yaw(c);
   8308 }
   8309 
   8310 BAD_RETURN(s32) cutscene_exit_fall_to_castle_grounds_warp(struct Camera *c) {
   8311     //! hardcoded position
   8312     vec3f_set(c->pos, 5830.f, 32.f, 3985.f);
   8313 }
   8314 
   8315 /**
   8316  * Cutscene that plays when Mario falls from WMotR.
   8317  */
   8318 BAD_RETURN(s32) cutscene_exit_fall_to_castle_grounds(struct Camera *c) {
   8319     cutscene_event(cutscene_exit_fall_to_castle_grounds_warp, c, 0, 0);
   8320     cutscene_event(cutscene_exit_to_castle_grounds_focus_mario, c, 0, -1);
   8321     update_camera_yaw(c);
   8322 }
   8323 
   8324 /**
   8325  * Start the red coin star spawning cutscene.
   8326  */
   8327 BAD_RETURN(s32) cutscene_red_coin_star_start(struct Camera *c) {
   8328     object_pos_to_vec3f(sCutsceneVars[1].point, gCutsceneFocus);
   8329     store_info_star(c);
   8330     // Store the default fov for after the cutscene
   8331     sCutsceneVars[2].point[2] = sFOVState.fov;
   8332 }
   8333 
   8334 /**
   8335  * Look towards the star's x and z position
   8336  */
   8337 BAD_RETURN(s32) cutscene_red_coin_star_focus_xz(struct Camera *c) {
   8338     approach_f32_asymptotic_bool(&c->focus[0], gCutsceneFocus->oPosX, 0.15f);
   8339     approach_f32_asymptotic_bool(&c->focus[2], gCutsceneFocus->oPosZ, 0.15f);
   8340 }
   8341 
   8342 /**
   8343  * Look towards the star's y position. Only active before the camera warp.
   8344  */
   8345 BAD_RETURN(s32) cutscene_red_coin_star_focus_y(struct Camera *c) {
   8346     approach_f32_asymptotic_bool(&c->focus[1], gCutsceneFocus->oPosY, 0.1f);
   8347 }
   8348 
   8349 /**
   8350  * Look 80% up towards the star. Only active after the camera warp.
   8351  */
   8352 BAD_RETURN(s32) cutscene_red_coin_star_look_up_at_star(struct Camera *c) {
   8353     c->focus[1] = sCutsceneVars[1].point[1] + (gCutsceneFocus->oPosY - sCutsceneVars[1].point[1]) * 0.8f;
   8354 }
   8355 
   8356 /**
   8357  * Warp the camera near the star's spawn point
   8358  */
   8359 BAD_RETURN(s32) cutscene_red_coin_star_warp(struct Camera *c) {
   8360     f32 dist;
   8361     s16 pitch, yaw, posYaw;
   8362     struct Object *o = gCutsceneFocus;
   8363 
   8364     vec3f_set(sCutsceneVars[1].point, o->oHomeX, o->oHomeY, o->oHomeZ);
   8365     vec3f_get_dist_and_angle(sCutsceneVars[1].point, c->pos, &dist, &pitch, &yaw);
   8366     posYaw = calculate_yaw(sCutsceneVars[1].point, c->pos);
   8367     yaw = calculate_yaw(sCutsceneVars[1].point, sMarioCamState->pos);
   8368 
   8369     if (ABS(yaw - posYaw + DEGREES(90)) < ABS(yaw - posYaw - DEGREES(90))) {
   8370         yaw += DEGREES(90);
   8371     } else {
   8372         yaw -= DEGREES(90);
   8373     }
   8374 
   8375     vec3f_set_dist_and_angle(sCutsceneVars[1].point, c->pos, 400.f, 0x1000, yaw);
   8376     sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   8377 }
   8378 
   8379 /**
   8380  * Zoom out while looking at the star.
   8381  */
   8382 BAD_RETURN(s32) cutscene_red_coin_star_set_fov(UNUSED struct Camera *c) {
   8383     sFOVState.fov = 60.f;
   8384 }
   8385 
   8386 BAD_RETURN(s32) cutscene_red_coin_star(struct Camera *c) {
   8387     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8388     cutscene_event(cutscene_red_coin_star_start, c, 0, 0);
   8389     cutscene_event(cutscene_red_coin_star_warp, c, 30, 30);
   8390     cutscene_event(cutscene_red_coin_star_focus_xz, c, 0, -1);
   8391     cutscene_event(cutscene_red_coin_star_focus_y, c, 0, 29);
   8392     cutscene_event(cutscene_red_coin_star_look_up_at_star, c, 30, -1);
   8393     cutscene_event(cutscene_red_coin_star_set_fov, c, 30, -1);
   8394 
   8395     if (gObjCutsceneDone) {
   8396         // Set the timer to CUTSCENE_LOOP, which start the next shot.
   8397         gCutsceneTimer = CUTSCENE_LOOP;
   8398     }
   8399 }
   8400 
   8401 /**
   8402  * End the red coin star spawning cutscene
   8403  */
   8404 BAD_RETURN(s32) cutscene_red_coin_star_end(struct Camera *c) {
   8405     retrieve_info_star(c);
   8406     gCutsceneTimer = CUTSCENE_STOP;
   8407     c->cutscene = 0;
   8408     // Restore the default fov
   8409     sFOVState.fov = sCutsceneVars[2].point[2];
   8410 }
   8411 
   8412 /**
   8413  * Moves the camera towards the cutscene's focus, stored in sCutsceneVars[3].point
   8414  *
   8415  * sCutsceneVars[3].point is used as the target point
   8416  * sCutsceneVars[0].point is used as the current camera focus during the transition
   8417  *
   8418  * @param rotPitch constant pitch offset to add to the camera's focus
   8419  * @param rotYaw constant yaw offset to add to the camera's focus
   8420  */
   8421 void cutscene_goto_cvar_pos(struct Camera *c, f32 goalDist, s16 goalPitch, s16 rotPitch, s16 rotYaw) {
   8422     UNUSED u8 filler[4];
   8423     f32 nextDist;
   8424     s16 nextPitch, nextYaw;
   8425     // The next 2 polar coord points are only used in CUTSCENE_PREPARE_CANNON
   8426     f32 cannonDist;
   8427     s16 cannonPitch, cannonYaw;
   8428     f32 curDist;
   8429     s16 curPitch, curYaw;
   8430     UNUSED f64 unused;
   8431 
   8432     vec3f_get_dist_and_angle(sCutsceneVars[3].point, c->pos, &nextDist, &nextPitch, &nextYaw);
   8433 
   8434     // If over 8000 units away from the cannon, just teleport there
   8435     if ((nextDist > 8000.f) && (c->cutscene == CUTSCENE_PREPARE_CANNON)) {
   8436         nextDist = goalDist * 4.f;
   8437         nextPitch = goalPitch;
   8438         vec3f_copy(sCutsceneVars[0].point, sCutsceneVars[3].point);
   8439         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   8440 
   8441         if (gCurrLevelNum == LEVEL_TTM) {
   8442             nextYaw = atan2s(sCutsceneVars[3].point[2] - c->areaCenZ,
   8443                              sCutsceneVars[3].point[0] - c->areaCenX);
   8444         }
   8445     } else {
   8446         if (c->cutscene == CUTSCENE_PREPARE_CANNON) {
   8447             vec3f_get_dist_and_angle(c->pos, sCutsceneVars[0].point, &curDist, &curPitch, &curYaw);
   8448             vec3f_get_dist_and_angle(c->pos, sCutsceneVars[3].point, &cannonDist, &cannonPitch, &cannonYaw);
   8449             approach_f32_asymptotic_bool(&curDist, cannonDist, 0.1f);
   8450             approach_s16_asymptotic_bool(&curPitch, cannonPitch, 15);
   8451             approach_s16_asymptotic_bool(&curYaw, cannonYaw, 15);
   8452             // Move the current focus, sCutsceneVars[0].point, in the direction towards the cannon
   8453             vec3f_set_dist_and_angle(c->pos, sCutsceneVars[0].point, curDist, curPitch, curYaw);
   8454         } else {
   8455             approach_vec3f_asymptotic(sCutsceneVars[0].point, sCutsceneVars[3].point, 0.1f, 0.1f, 0.1f);
   8456         }
   8457     }
   8458 
   8459     approach_f32_asymptotic_bool(&nextDist, goalDist, 0.05f);
   8460     approach_s16_asymptotic_bool(&nextPitch, goalPitch, 0x20);
   8461 
   8462     vec3f_set_dist_and_angle(sCutsceneVars[3].point, c->pos, nextDist, nextPitch, nextYaw);
   8463     vec3f_copy(c->focus, sCutsceneVars[0].point);
   8464 
   8465     // Apply the constant rotation given
   8466     pan_camera(c, rotPitch, rotYaw);
   8467     vec3f_get_dist_and_angle(c->pos, c->focus, &nextDist, &nextPitch, &nextYaw);
   8468 
   8469     if (nextPitch < -0x3000) {
   8470         nextPitch = -0x3000;
   8471     }
   8472     if (nextPitch > 0x3000) {
   8473         nextPitch = 0x3000;
   8474     }
   8475 
   8476     vec3f_set_dist_and_angle(c->pos, c->focus, nextDist, nextPitch, nextYaw);
   8477 }
   8478 
   8479 /**
   8480  * Store the camera's pos and focus, and copy the cannon's position to cvars.
   8481  */
   8482 BAD_RETURN(s32) cutscene_prepare_cannon_start(struct Camera *c) {
   8483     store_info_cannon(c);
   8484     vec3f_copy(sCutsceneVars[0].point, c->focus);
   8485     sCutsceneVars[2].point[0] = 30.f;
   8486     // Store the cannon door's position in sCutsceneVars[3]'s point
   8487     object_pos_to_vec3f(sCutsceneVars[3].point, gCutsceneFocus);
   8488     vec3s_set(sCutsceneVars[5].angle, 0, 0, 0);
   8489 }
   8490 
   8491 /**
   8492  * Fly towards the cannon door.
   8493  */
   8494 BAD_RETURN(s32) cutscene_prepare_cannon_fly_to_cannon(struct Camera *c) {
   8495     cutscene_goto_cvar_pos(c, 300.f, 0x2000, 0, sCutsceneVars[5].angle[1]);
   8496     camera_approach_s16_symmetric_bool(&sCutsceneVars[5].angle[1], 0x400, 17);
   8497     set_handheld_shake(HAND_CAM_SHAKE_CUTSCENE);
   8498 }
   8499 
   8500 /**
   8501  * Used in the cannon opening cutscene to fly back to the camera's last position and focus
   8502  */
   8503 void cannon_approach_prev(f32 *value, f32 target) {
   8504     f32 inc = ABS(target - *value) / sCutsceneVars[2].point[0];
   8505     camera_approach_f32_symmetric_bool(value, target, inc);
   8506 }
   8507 
   8508 /**
   8509  * Fly or warp back to the previous pos and focus, stored in sCameraStoreCutscene.
   8510  */
   8511 BAD_RETURN(s32) cutscene_prepare_cannon_fly_back(struct Camera *c) {
   8512     f32 distToPrevPos = calc_abs_dist(c->pos, sCameraStoreCutscene.pos);
   8513 
   8514     if (distToPrevPos < 8000.f) {
   8515         cannon_approach_prev(&c->pos[0], sCameraStoreCutscene.pos[0]);
   8516         cannon_approach_prev(&c->pos[1], sCameraStoreCutscene.pos[1]);
   8517         cannon_approach_prev(&c->pos[2], sCameraStoreCutscene.pos[2]);
   8518         cannon_approach_prev(&c->focus[0], sCameraStoreCutscene.focus[0]);
   8519         cannon_approach_prev(&c->focus[1], sCameraStoreCutscene.focus[1]);
   8520         cannon_approach_prev(&c->focus[2], sCameraStoreCutscene.focus[2]);
   8521     } else {
   8522         // If too far away, just warp back
   8523         vec3f_copy(c->focus, sCameraStoreCutscene.focus);
   8524         vec3f_copy(c->pos, sCameraStoreCutscene.pos);
   8525         sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
   8526     }
   8527     if (sCutsceneVars[2].point[0] > 1.f) {
   8528         sCutsceneVars[2].point[0] -= 1.f;
   8529     }
   8530 }
   8531 
   8532 /**
   8533  * Cutscene that plays when the cannon is opened.
   8534  */
   8535 BAD_RETURN(s32) cutscene_prepare_cannon(struct Camera *c) {
   8536     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8537     cutscene_event(cutscene_prepare_cannon_start, c, 0, 0);
   8538     cutscene_event(cutscene_prepare_cannon_fly_to_cannon, c, 0, 140);
   8539     cutscene_event(cutscene_prepare_cannon_fly_back, c, 141, -1);
   8540 }
   8541 
   8542 /**
   8543  * Stop the cannon opening cutscene.
   8544  */
   8545 BAD_RETURN(s32) cutscene_prepare_cannon_end(struct Camera *c) {
   8546     gCutsceneTimer = CUTSCENE_STOP;
   8547     c->cutscene = 0;
   8548     retrieve_info_cannon(c);
   8549     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8550 }
   8551 
   8552 /**
   8553  * Moves the camera to Mario's side when Mario starts ACT_WATER_DEATH
   8554  * Note that ACT_WATER_DEATH only starts when Mario gets hit by an enemy under water. It does not start
   8555  * when Mario drowns.
   8556  */
   8557 void water_death_move_to_mario_side(struct Camera *c) {
   8558     f32 dist;
   8559     s16 pitch, yaw;
   8560 
   8561     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   8562     approach_s16_asymptotic_bool(&yaw, (sMarioCamState->faceAngle[1] - 0x3000), 8);
   8563     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   8564 }
   8565 
   8566 /**
   8567  * Unnecessary, only used in cutscene_death_standing_goto_mario()
   8568  */
   8569 void death_goto_mario(struct Camera *c) {
   8570     cutscene_goto_cvar_pos(c, 400.f, 0x1000, 0x300, 0);
   8571 }
   8572 
   8573 BAD_RETURN(s32) cutscene_death_standing_start(struct Camera *c) {
   8574     vec3f_copy(sCutsceneVars[0].point, c->focus);
   8575     vec3f_copy(sCutsceneVars[3].point, sMarioCamState->pos);
   8576     sCutsceneVars[3].point[1] += 70.f;
   8577 }
   8578 
   8579 /**
   8580  * Fly to Mario and turn on handheld shake.
   8581  */
   8582 BAD_RETURN(s32) cutscene_death_standing_goto_mario(struct Camera *c) {
   8583     death_goto_mario(c);
   8584     set_handheld_shake(HAND_CAM_SHAKE_HIGH);
   8585 }
   8586 
   8587 /**
   8588  * Cutscene that plays when Mario dies while standing.
   8589  */
   8590 BAD_RETURN(s32) cutscene_death_standing(struct Camera *c) {
   8591     cutscene_event(cutscene_death_standing_start, c, 0, 0);
   8592     cutscene_event(cutscene_death_standing_goto_mario, c, 0, -1);
   8593     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8594 }
   8595 
   8596 BAD_RETURN(s32) cutscene_death_stomach_start(struct Camera *c) {
   8597     Vec3f offset = { 0, 40.f, -60.f };
   8598 
   8599     offset_rotated(sCutsceneVars[3].point, sMarioCamState->pos, offset, sMarioCamState->faceAngle);
   8600     vec3f_copy(sCutsceneVars[0].point, c->focus);
   8601 }
   8602 
   8603 BAD_RETURN(s32) cutscene_death_stomach_goto_mario(struct Camera *c) {
   8604     cutscene_goto_cvar_pos(c, 400.f, 0x1800, 0, -0x400);
   8605 }
   8606 
   8607 /**
   8608  * Ah, yes
   8609  */
   8610 UNUSED static void unused_water_death_move_to_side_of_mario(struct Camera *c) {
   8611     water_death_move_to_mario_side(c);
   8612 }
   8613 
   8614 /**
   8615  * Cutscene that plays when Mario dies on his stomach.
   8616  */
   8617 BAD_RETURN(s32) cutscene_death_stomach(struct Camera *c) {
   8618     cutscene_event(cutscene_death_stomach_start, c, 0, 0);
   8619     cutscene_event(cutscene_death_stomach_goto_mario, c, 0, -1);
   8620     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8621     set_handheld_shake(HAND_CAM_SHAKE_CUTSCENE);
   8622 }
   8623 
   8624 BAD_RETURN(s32) cutscene_bbh_death_start(struct Camera *c) {
   8625     Vec3f dir = { 0, 40.f, 60.f };
   8626 
   8627     offset_rotated(sCutsceneVars[3].point, sMarioCamState->pos, dir, sMarioCamState->faceAngle);
   8628     vec3f_copy(sCutsceneVars[0].point, c->focus);
   8629 }
   8630 
   8631 BAD_RETURN(s32) cutscene_bbh_death_goto_mario(struct Camera *c) {
   8632     cutscene_goto_cvar_pos(c, 400.f, 0x1800, 0, 0x400);
   8633 }
   8634 
   8635 /**
   8636  * Cutscene that plays when Mario dies in BBH.
   8637  */
   8638 BAD_RETURN(s32) cutscene_bbh_death(struct Camera *c) {
   8639     cutscene_event(cutscene_bbh_death_start, c, 0, 0);
   8640     cutscene_event(cutscene_bbh_death_goto_mario, c, 0, -1);
   8641     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8642     set_handheld_shake(HAND_CAM_SHAKE_CUTSCENE);
   8643 }
   8644 
   8645 /**
   8646  * Copy the camera's focus to cvar0
   8647  */
   8648 BAD_RETURN(s32) cutscene_quicksand_death_start(struct Camera *c) {
   8649     vec3f_copy(sCutsceneVars[0].point, c->focus);
   8650 }
   8651 
   8652 /**
   8653  * Fly closer to Mario. In WATER_DEATH, move to Mario's side.
   8654  */
   8655 BAD_RETURN(s32) cutscene_quicksand_death_goto_mario(struct Camera *c) {
   8656     cutscene_goto_cvar_pos(c, 400.f, 0x2800, 0x200, 0);
   8657 
   8658     if (c->cutscene == CUTSCENE_WATER_DEATH) {
   8659         water_death_move_to_mario_side(c);
   8660     }
   8661 }
   8662 
   8663 /**
   8664  * Cutscene that plays when Mario dies in quicksand.
   8665  */
   8666 BAD_RETURN(s32) cutscene_quicksand_death(struct Camera *c) {
   8667     sCutsceneVars[3].point[0] = sMarioCamState->pos[0];
   8668     sCutsceneVars[3].point[1] = sMarioCamState->pos[1] + 20.f;
   8669     sCutsceneVars[3].point[2] = sMarioCamState->pos[2];
   8670 
   8671     cutscene_event(cutscene_quicksand_death_start, c, 0, 0);
   8672     cutscene_event(cutscene_quicksand_death_goto_mario, c, 0, -1);
   8673     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8674     set_handheld_shake(HAND_CAM_SHAKE_HIGH);
   8675 }
   8676 
   8677 /**
   8678  * Fly away from Mario near the end of the cutscene.
   8679  */
   8680 BAD_RETURN(s32) cutscene_suffocation_fly_away(UNUSED struct Camera *c) {
   8681     Vec3f target;
   8682     Vec3f offset = { 0, 20.f, 120.f };
   8683 
   8684     offset_rotated(target, sMarioCamState->pos, offset, sMarioCamState->faceAngle);
   8685     approach_vec3f_asymptotic(sCutsceneVars[3].point, target, 0.1f, 0.1f, 0.1f);
   8686 }
   8687 
   8688 /**
   8689  * Keep Lakitu above the gas level.
   8690  */
   8691 BAD_RETURN(s32) cutscene_suffocation_stay_above_gas(struct Camera *c) {
   8692     UNUSED u8 filler1[4];
   8693     f32 gasLevel;
   8694     UNUSED u8 filler2[4];
   8695 
   8696     cutscene_goto_cvar_pos(c, 400.f, 0x2800, 0x200, 0);
   8697     gasLevel = find_poison_gas_level(sMarioCamState->pos[0], sMarioCamState->pos[2]);
   8698 
   8699     if (gasLevel != FLOOR_LOWER_LIMIT) {
   8700         if ((gasLevel += 130.f) > c->pos[1]) {
   8701             c->pos[1] = gasLevel;
   8702         }
   8703     }
   8704 }
   8705 
   8706 /**
   8707  * Quickly rotate around Mario.
   8708  */
   8709 BAD_RETURN(s32) cutscene_suffocation_rotate(struct Camera *c) {
   8710     f32 dist;
   8711     s16 pitch, yaw;
   8712 
   8713     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   8714     yaw += 0x100;
   8715     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   8716 }
   8717 
   8718 /**
   8719  * Cutscene that plays when Mario dies from suffocation (ie due to HMC gas).
   8720  */
   8721 BAD_RETURN(s32) cutscene_suffocation(struct Camera *c) {
   8722     cutscene_event(cutscene_death_stomach_start, c, 0, 0);
   8723     cutscene_event(cutscene_suffocation_rotate, c, 0, -1);
   8724     cutscene_event(cutscene_suffocation_stay_above_gas, c, 0, -1);
   8725     cutscene_event(cutscene_suffocation_fly_away, c, 50, -1);
   8726     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8727     set_handheld_shake(HAND_CAM_SHAKE_HIGH);
   8728 }
   8729 
   8730 BAD_RETURN(s32) cutscene_enter_pool_start(struct Camera *c) {
   8731     vec3f_copy(sCutsceneVars[3].point, sMarioCamState->pos);
   8732 
   8733     if (gCurrLevelNum == LEVEL_CASTLE) { // entering HMC
   8734         vec3f_set(sCutsceneVars[3].point, 2485.f, -1589.f, -2659.f);
   8735     }
   8736     if (gCurrLevelNum == LEVEL_HMC) { // entering CotMC
   8737         vec3f_set(sCutsceneVars[3].point, 3350.f, -4589.f, 4800.f);
   8738     }
   8739 
   8740     vec3f_copy(sCutsceneVars[0].point, c->focus);
   8741 }
   8742 
   8743 BAD_RETURN(s32) cutscene_enter_pool_loop(struct Camera *c) {
   8744     UNUSED u8 filler[8];
   8745 
   8746     cutscene_goto_cvar_pos(c, 1200.f, 0x2000, 0x200, 0);
   8747 }
   8748 
   8749 BAD_RETURN(s32) cutscene_enter_pool(struct Camera *c) {
   8750     cutscene_event(cutscene_enter_pool_start, c, 0, 0);
   8751     cutscene_event(cutscene_enter_pool_loop, c, 0, -1);
   8752     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8753 }
   8754 
   8755 /**
   8756  * Store the camera focus in cvar1.
   8757  * Store the area's center position (which happens to be the pyramid, in SSL) in cvar3.
   8758  */
   8759 BAD_RETURN(s32) cutscene_pyramid_top_explode_start(struct Camera *c) {
   8760     reset_pan_distance(c);
   8761     store_info_cannon(c);
   8762 
   8763     vec3f_copy(sCutsceneVars[1].point, c->focus);
   8764     vec3f_set(sCutsceneVars[3].point, c->areaCenX, 1280.f, c->areaCenZ);
   8765 }
   8766 
   8767 /**
   8768  * Zoom in on the pyramid.
   8769  */
   8770 BAD_RETURN(s32) cutscene_pyramid_top_explode_zoom_in(UNUSED struct Camera *c) {
   8771     set_fov_function(CAM_FOV_APP_30);
   8772 }
   8773 
   8774 /**
   8775  * Look at the pyramid top.
   8776  */
   8777 BAD_RETURN(s32) cutscene_pyramid_top_explode_focus(struct Camera *c) {
   8778     approach_vec3f_asymptotic(c->focus, sCutsceneVars[3].point, 0.02f, 0.02f, 0.02f);
   8779     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8780 }
   8781 
   8782 /**
   8783  * Store the old pos and focus, then warp to the pyramid top.
   8784  */
   8785 BAD_RETURN(s32) cutscene_pyramid_top_explode_warp(struct Camera *c) {
   8786     s16 pitch, yaw;
   8787     f32 dist;
   8788 
   8789     set_fov_function(CAM_FOV_DEFAULT);
   8790     sFOVState.fov = 45.f;
   8791 
   8792     vec3f_copy(sCutsceneVars[4].point, c->pos);
   8793     vec3f_copy(sCutsceneVars[5].point, c->focus);
   8794     vec3f_copy(c->focus, sCutsceneVars[3].point);
   8795 
   8796     vec3f_get_dist_and_angle(sCutsceneVars[3].point, sMarioCamState[0].pos, &dist, &pitch, &yaw);
   8797     vec3f_set_dist_and_angle(sCutsceneVars[3].point, c->pos, 2000.f, 0, yaw);
   8798     c->pos[1] += 500.f;
   8799 }
   8800 
   8801 /**
   8802  * Close up view of the spinning pyramid top as it rises.
   8803  */
   8804 BAD_RETURN(s32) cutscene_pyramid_top_explode_closeup(struct Camera *c) {
   8805     s16 pitch, yaw;
   8806     f32 dist;
   8807 
   8808     vec3f_get_dist_and_angle(sCutsceneVars[3].point, c->pos, &dist, &pitch, &yaw);
   8809     approach_f32_asymptotic_bool(&dist, 2000.f, 0.1f);
   8810     vec3f_set_dist_and_angle(sCutsceneVars[3].point, c->pos, dist, pitch, yaw);
   8811 
   8812     c->focus[1] += 4.f;
   8813     c->pos[1] -= 5.f;
   8814     sFOVState.fov = 45.f;
   8815     set_handheld_shake(HAND_CAM_SHAKE_CUTSCENE);
   8816 }
   8817 
   8818 /**
   8819  * Shake the camera during the closeup.
   8820  */
   8821 BAD_RETURN(s32) cutscene_pyramid_top_explode_cam_shake(UNUSED struct Camera *c) {
   8822     set_environmental_camera_shake(SHAKE_ENV_PYRAMID_EXPLODE);
   8823 }
   8824 
   8825 /**
   8826  * Warp back to the old position, and start a heavy camera shake.
   8827  */
   8828 BAD_RETURN(s32) cutscene_pyramid_top_explode_warp_back(struct Camera *c) {
   8829     UNUSED u8 filler[8];
   8830 
   8831     vec3f_copy(c->pos, sCutsceneVars[4].point);
   8832     vec3f_copy(c->focus, sCutsceneVars[5].point);
   8833     set_environmental_camera_shake(SHAKE_ENV_BOWSER_JUMP);
   8834 }
   8835 
   8836 /**
   8837  * An unused cutscene for when the pyramid explodes.
   8838  */
   8839 BAD_RETURN(s32) cutscene_pyramid_top_explode(struct Camera *c) {
   8840     cutscene_event(cutscene_pyramid_top_explode_start, c, 0, 0);
   8841     cutscene_event(cutscene_pyramid_top_explode_focus, c, 0, 30);
   8842     cutscene_event(cutscene_pyramid_top_explode_warp, c, 31, 31);
   8843     cutscene_event(cutscene_pyramid_top_explode_closeup, c, 31, 139);
   8844     cutscene_event(cutscene_pyramid_top_explode_zoom_in, c, 23, 23);
   8845     cutscene_event(cutscene_pyramid_top_explode_warp_back, c, 140, 140);
   8846     cutscene_event(cutscene_pyramid_top_explode_cam_shake, c, 31, 139);
   8847 }
   8848 
   8849 /**
   8850  * End the pyramid top explosion cutscene.
   8851  */
   8852 BAD_RETURN(s32) cutscene_pyramid_top_explode_end(struct Camera *c) {
   8853     cutscene_stop_dialog(c);
   8854     stop_cutscene_and_retrieve_stored_info(c);
   8855     // Move the camera back to Mario
   8856     transition_next_state(c, 30);
   8857 }
   8858 
   8859 /**
   8860  * Store the camera focus in cvar0, and store the top of the pyramid in cvar3.
   8861  */
   8862 BAD_RETURN(s32) cutscene_enter_pyramid_top_start(struct Camera *c) {
   8863     vec3f_copy(sCutsceneVars[0].point, c->focus);
   8864     vec3f_set(sCutsceneVars[3].point, c->areaCenX, 1280.f, c->areaCenZ);
   8865 }
   8866 
   8867 /**
   8868  * Cutscene that plays when Mario enters the top of the pyramid.
   8869  */
   8870 BAD_RETURN(s32) cutscene_enter_pyramid_top(struct Camera *c) {
   8871     cutscene_event(cutscene_enter_pyramid_top_start, c, 0, 0);
   8872     // Move to cvar3
   8873     cutscene_goto_cvar_pos(c, 200.f, 0x3000, 0, 0);
   8874     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8875     set_handheld_shake(HAND_CAM_SHAKE_CUTSCENE);
   8876 
   8877     if (sMarioCamState->pos[1] > 1250.f) {
   8878         // End the cutscene early if Mario ledge-grabbed.
   8879         // This only works because of the janky way that ledge-grabbing is implemented.
   8880         cutscene_exit_to_castle_grounds_end(c);
   8881     }
   8882 }
   8883 
   8884 UNUSED static void unused_cutscene_goto_cvar(struct Camera *c) {
   8885     f32 dist;
   8886 
   8887     dist = calc_abs_dist(sCutsceneVars[3].point, sMarioCamState->pos);
   8888     dist = calc_abs_dist(sCutsceneVars[9].point, sMarioCamState->pos) + 200.f;
   8889     cutscene_goto_cvar_pos(c, dist, 0x1000, 0x300, 0);
   8890 }
   8891 
   8892 /**
   8893  * cvar8 is Mario's position and faceAngle
   8894  *
   8895  * cvar9.point is gCutsceneFocus's position
   8896  * cvar9.angle[1] is the yaw between Mario and the gCutsceneFocus
   8897  */
   8898 BAD_RETURN(s32) cutscene_dialog_start(struct Camera *c) {
   8899     UNUSED u8 filler[4];
   8900     UNUSED s16 unused;
   8901     s16 yaw;
   8902 
   8903     cutscene_soften_music(c);
   8904     set_time_stop_flags(TIME_STOP_ENABLED | TIME_STOP_DIALOG);
   8905 
   8906 #ifndef VERSION_JP
   8907     if (c->mode == CAMERA_MODE_BOSS_FIGHT) {
   8908         vec3f_copy(sCameraStoreCutscene.focus, c->focus);
   8909         vec3f_copy(sCameraStoreCutscene.pos, c->pos);
   8910     } else {
   8911 #endif
   8912         store_info_star(c);
   8913 #ifndef VERSION_JP
   8914     }
   8915 #endif
   8916 
   8917     // Store Mario's position and faceAngle
   8918     sCutsceneVars[8].angle[0] = 0;
   8919     vec3f_copy(sCutsceneVars[8].point, sMarioCamState->pos);
   8920     sCutsceneVars[8].point[1] += 125.f;
   8921 
   8922     // Store gCutsceneFocus's position and yaw
   8923     object_pos_to_vec3f(sCutsceneVars[9].point, gCutsceneFocus);
   8924     sCutsceneVars[9].point[1] += gCutsceneFocus->hitboxHeight + 200.f;
   8925     sCutsceneVars[9].angle[1] = calculate_yaw(sCutsceneVars[8].point, sCutsceneVars[9].point);
   8926 
   8927     yaw = calculate_yaw(sMarioCamState->pos, gLakituState.curPos);
   8928     if ((yaw - sCutsceneVars[9].angle[1]) & 0x8000) {
   8929         sCutsceneVars[9].angle[1] -= 0x6000;
   8930     } else {
   8931         sCutsceneVars[9].angle[1] += 0x6000;
   8932     }
   8933 }
   8934 
   8935 /**
   8936  * Move closer to Mario and the object, adjusting to their difference in height.
   8937  * The camera's generally ends up looking over Mario's shoulder.
   8938  */
   8939 BAD_RETURN(s32) cutscene_dialog_move_mario_shoulder(struct Camera *c) {
   8940     f32 dist;
   8941     s16 pitch, yaw;
   8942     Vec3f focus, pos;
   8943 
   8944     scale_along_line(focus, sCutsceneVars[9].point, sMarioCamState->pos, 0.7f);
   8945     vec3f_get_dist_and_angle(c->pos, focus, &dist, &pitch, &yaw);
   8946     pitch = calculate_pitch(c->pos, sCutsceneVars[9].point);
   8947     vec3f_set_dist_and_angle(c->pos, pos, dist, pitch, yaw);
   8948 
   8949     focus[1] = focus[1] + (sCutsceneVars[9].point[1] - focus[1]) * 0.1f;
   8950     approach_vec3f_asymptotic(c->focus, focus, 0.2f, 0.2f, 0.2f);
   8951 
   8952     vec3f_copy(pos, c->pos);
   8953 
   8954     // Set y pos to cvar8's y (top of focus object)
   8955     pos[1] = sCutsceneVars[8].point[1];
   8956     vec3f_get_dist_and_angle(sCutsceneVars[8].point, pos, &dist, &pitch, &yaw);
   8957     approach_s16_asymptotic_bool(&yaw, sCutsceneVars[9].angle[1], 0x10);
   8958     approach_f32_asymptotic_bool(&dist, 180.f, 0.05f);
   8959     vec3f_set_dist_and_angle(sCutsceneVars[8].point, pos, dist, pitch, yaw);
   8960 
   8961     // Move up if Mario is below the focus object, down is Mario is above
   8962     pos[1] = sCutsceneVars[8].point[1]
   8963               + sins(calculate_pitch(sCutsceneVars[9].point, sCutsceneVars[8].point)) * 100.f;
   8964 
   8965     approach_f32_asymptotic_bool(&c->pos[1], pos[1], 0.05f);
   8966     c->pos[0] = pos[0];
   8967     c->pos[2] = pos[2];
   8968 }
   8969 
   8970 /**
   8971  * Create the dialog with sCutsceneDialogID
   8972  */
   8973 BAD_RETURN(s32) cutscene_dialog_create_dialog_box(struct Camera *c) {
   8974     if (c->cutscene == CUTSCENE_RACE_DIALOG) {
   8975         create_dialog_box_with_response(sCutsceneDialogID);
   8976     } else {
   8977         create_dialog_box(sCutsceneDialogID);
   8978     }
   8979 
   8980     //! Unused. This may have been used before sCutsceneDialogResponse was implemented.
   8981     sCutsceneVars[8].angle[0] = DIALOG_RESPONSE_NOT_DEFINED;
   8982 }
   8983 
   8984 /**
   8985  * Cutscene that plays when Mario talks to an object.
   8986  */
   8987 BAD_RETURN(s32) cutscene_dialog(struct Camera *c) {
   8988     cutscene_event(cutscene_dialog_start, c, 0, 0);
   8989     cutscene_event(cutscene_dialog_move_mario_shoulder, c, 0, -1);
   8990     cutscene_event(cutscene_dialog_create_dialog_box, c, 10, 10);
   8991     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   8992 
   8993     if (gDialogResponse != DIALOG_RESPONSE_NONE) {
   8994         sCutsceneDialogResponse = gDialogResponse;
   8995     }
   8996 
   8997     if ((get_dialog_id() == DIALOG_NONE) && (sCutsceneVars[8].angle[0] != 0)) {
   8998         if (c->cutscene != CUTSCENE_RACE_DIALOG) {
   8999             sCutsceneDialogResponse = DIALOG_RESPONSE_NOT_DEFINED;
   9000         }
   9001 
   9002         gCutsceneTimer = CUTSCENE_LOOP;
   9003         retrieve_info_star(c);
   9004         transition_next_state(c, 15);
   9005         sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9006         cutscene_unsoften_music(c);
   9007     }
   9008 }
   9009 
   9010 /**
   9011  * Sets the CAM_FLAG_UNUSED_CUTSCENE_ACTIVE flag, which does nothing.
   9012  */
   9013 BAD_RETURN(s32) cutscene_dialog_set_flag(UNUSED struct Camera *c) {
   9014     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9015 }
   9016 
   9017 /**
   9018  * Ends the dialog cutscene.
   9019  */
   9020 BAD_RETURN(s32) cutscene_dialog_end(struct Camera *c) {
   9021     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9022     c->cutscene = 0;
   9023     clear_time_stop_flags(TIME_STOP_ENABLED | TIME_STOP_DIALOG);
   9024 }
   9025 
   9026 /**
   9027  * Soften the music, clear cvar0
   9028  *
   9029  * In this cutscene, cvar0.angle[0] is used as a state variable.
   9030  */
   9031 BAD_RETURN(s32) cutscene_read_message_start(struct Camera *c) {
   9032     cutscene_soften_music(c);
   9033     transition_next_state(c, 30);
   9034     reset_pan_distance(c);
   9035     store_info_star(c);
   9036 
   9037     sCutsceneVars[1].angle[0] = sCUpCameraPitch;
   9038     sCutsceneVars[1].angle[1] = sModeOffsetYaw;
   9039     sCUpCameraPitch = -0x830;
   9040     sModeOffsetYaw = 0;
   9041     sCutsceneVars[0].angle[0] = 0;
   9042 }
   9043 
   9044 UNUSED static void unused_cam_to_mario(struct Camera *c) {
   9045     Vec3s dir;
   9046 
   9047     vec3s_set(dir, 0, sMarioCamState->faceAngle[1], 0);
   9048     offset_rotated_coords(c->pos, sMarioCamState->pos, dir, 0, 100.f, 190.f);
   9049     offset_rotated_coords(c->focus, sMarioCamState->pos, dir, 0, 70.f, -20.f);
   9050 }
   9051 
   9052 /**
   9053  * Cutscene that plays when Mario is reading a message (a sign or message on the wall)
   9054  */
   9055 BAD_RETURN(s32) cutscene_read_message(struct Camera *c) {
   9056     UNUSED u8 filler[8];
   9057 
   9058     cutscene_event(cutscene_read_message_start, c, 0, 0);
   9059     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   9060 
   9061     switch (sCutsceneVars[0].angle[0]) {
   9062         // Do nothing until message is gone.
   9063         case 0:
   9064             if (get_dialog_id() != DIALOG_NONE) {
   9065                 sCutsceneVars[0].angle[0]++;
   9066                 set_time_stop_flags(TIME_STOP_ENABLED | TIME_STOP_DIALOG);
   9067             }
   9068             break;
   9069         // Leave the dialog.
   9070         case 1:
   9071             move_mario_head_c_up(c);
   9072             update_c_up(c, c->focus, c->pos);
   9073 
   9074             // This could cause softlocks. If a message starts one frame after another one closes, the
   9075             // cutscene will never end.
   9076             if (get_dialog_id() == DIALOG_NONE) {
   9077                 gCutsceneTimer = CUTSCENE_LOOP;
   9078                 retrieve_info_star(c);
   9079                 transition_next_state(c, 15);
   9080                 sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9081                 clear_time_stop_flags(TIME_STOP_ENABLED | TIME_STOP_DIALOG);
   9082                 // Retrieve previous state
   9083                 sCUpCameraPitch = sCutsceneVars[1].angle[0];
   9084                 sModeOffsetYaw = sCutsceneVars[1].angle[1];
   9085                 cutscene_unsoften_music(c);
   9086             }
   9087     }
   9088     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9089 }
   9090 
   9091 /**
   9092  * Set CAM_FLAG_UNUSED_CUTSCENE_ACTIVE, which does nothing.
   9093  */
   9094 BAD_RETURN(s32) cutscene_read_message_set_flag(UNUSED struct Camera *c) {
   9095     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9096 }
   9097 
   9098 /**
   9099  * End the message cutscene.
   9100  */
   9101 BAD_RETURN(s32) cutscene_read_message_end(struct Camera *c) {
   9102     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9103     c->cutscene = 0;
   9104 }
   9105 
   9106 /**
   9107  * Set cvars:
   9108  * cvar7 is Mario's pos and angle
   9109  * cvar6 is the focus offset
   9110  * cvar5 is the position offset
   9111  */
   9112 BAD_RETURN(s32) cutscene_exit_succ_start(UNUSED struct Camera *c) {
   9113     vec3f_copy(sCutsceneVars[7].point, sMarioCamState->pos);
   9114     vec3s_copy(sCutsceneVars[7].angle, sMarioCamState->faceAngle);
   9115     vec3f_set(sCutsceneVars[6].point, 6.f, 363.f, 543.f);
   9116     vec3f_set(sCutsceneVars[5].point, 137.f, 226.f, 995.f);
   9117 }
   9118 
   9119 /**
   9120  * Set the camera pos depending on which level Mario exited.
   9121  */
   9122 BAD_RETURN(s32) cutscene_non_painting_set_cam_pos(struct Camera *c) {
   9123     UNUSED u8 filler1[4];
   9124     struct Surface *floor;
   9125     UNUSED u8 filler2[12];
   9126 
   9127     switch (gPrevLevel) {
   9128         case LEVEL_HMC:
   9129             vec3f_set(c->pos, 3465.f, -1008.f, -2961.f);
   9130             break;
   9131 
   9132         case LEVEL_COTMC:
   9133             vec3f_set(c->pos, 3465.f, -1008.f, -2961.f);
   9134             break;
   9135 
   9136         case LEVEL_RR:
   9137             vec3f_set(c->pos, -3741.f, 3151.f, 6065.f);
   9138             break;
   9139 
   9140         case LEVEL_WMOTR:
   9141             vec3f_set(c->pos, 1972.f, 3230.f, 5891.f);
   9142             break;
   9143 
   9144         default:
   9145             offset_rotated(c->pos, sCutsceneVars[7].point, sCutsceneVars[5].point, sCutsceneVars[7].angle);
   9146             c->pos[1] = find_floor(c->pos[0], c->pos[1] + 1000.f, c->pos[2], &floor) + 125.f;
   9147             break;
   9148     }
   9149 }
   9150 
   9151 /**
   9152  * Update the camera focus depending on which level Mario exited.
   9153  */
   9154 BAD_RETURN(s32) cutscene_non_painting_set_cam_focus(struct Camera *c) {
   9155     offset_rotated(c->focus, sCutsceneVars[7].point, sCutsceneVars[6].point, sCutsceneVars[7].angle);
   9156 
   9157     if ((gPrevLevel == LEVEL_COTMC) || (gPrevLevel == LEVEL_HMC) || (gPrevLevel == LEVEL_RR)
   9158         || (gPrevLevel == LEVEL_WMOTR)) {
   9159         c->focus[0] = c->pos[0] + (sMarioCamState->pos[0] - c->pos[0]) * 0.7f;
   9160         c->focus[1] = c->pos[1] + (sMarioCamState->pos[1] - c->pos[1]) * 0.4f;
   9161         c->focus[2] = c->pos[2] + (sMarioCamState->pos[2] - c->pos[2]) * 0.7f;
   9162     } else {
   9163         c->focus[1] = c->pos[1] + (sMarioCamState->pos[1] - c->pos[1]) * 0.2f;
   9164     }
   9165 }
   9166 
   9167 /**
   9168  * Focus slightly left of Mario. Perhaps to keep the bowser painting in view?
   9169  */
   9170 BAD_RETURN(s32) cutscene_exit_bowser_succ_focus_left(UNUSED struct Camera *c) {
   9171     approach_f32_asymptotic_bool(&sCutsceneVars[6].point[0], -24.f, 0.05f);
   9172 }
   9173 
   9174 /**
   9175  * Instead of focusing on the key, just start a pitch shake. Clever!
   9176  * The shake lasts 32 frames.
   9177  */
   9178 BAD_RETURN(s32) cutscene_exit_bowser_key_toss_shake(struct Camera *c) {
   9179     //! Unnecessary check.
   9180     if (c->cutscene == CUTSCENE_EXIT_BOWSER_SUCC) {
   9181         set_camera_pitch_shake(0x800, 0x40, 0x800);
   9182     }
   9183 }
   9184 
   9185 /**
   9186  * Start a camera shake when Mario lands on the ground.
   9187  */
   9188 BAD_RETURN(s32) cutscene_exit_succ_shake_landing(UNUSED struct Camera *c) {
   9189     set_environmental_camera_shake(SHAKE_ENV_EXPLOSION);
   9190 }
   9191 
   9192 /**
   9193  * Cutscene that plays when Mario beats bowser and exits the level.
   9194  */
   9195 BAD_RETURN(s32) cutscene_exit_bowser_succ(struct Camera *c) {
   9196     cutscene_event(cutscene_exit_succ_start, c, 0, 0);
   9197     cutscene_event(cutscene_non_painting_set_cam_pos, c, 0, -1);
   9198     cutscene_event(cutscene_exit_bowser_succ_focus_left, c, 18, -1);
   9199     cutscene_event(cutscene_non_painting_set_cam_focus, c, 0, -1);
   9200     cutscene_event(cutscene_exit_bowser_key_toss_shake, c, 125, 125);
   9201     cutscene_event(cutscene_exit_succ_shake_landing, c, 41, 41);
   9202 }
   9203 
   9204 /**
   9205  * End a non-painting exit cutscene. Used by BBH and bowser courses.
   9206  */
   9207 BAD_RETURN(s32) cutscene_non_painting_end(struct Camera *c) {
   9208     c->cutscene = 0;
   9209 
   9210     if (c->defMode == CAMERA_MODE_CLOSE) {
   9211         c->mode = CAMERA_MODE_CLOSE;
   9212     } else {
   9213         c->mode = CAMERA_MODE_FREE_ROAM;
   9214     }
   9215 
   9216     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9217     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   9218     transition_next_state(c, 60);
   9219     update_camera_yaw(c);
   9220 }
   9221 
   9222 /**
   9223  * Override the position offset.
   9224  */
   9225 BAD_RETURN(s32) cutscene_exit_non_painting_succ_override_cvar(UNUSED struct Camera *c) {
   9226     vec3f_set(sCutsceneVars[5].point, 137.f, 246.f, 1115.f);
   9227 }
   9228 
   9229 /**
   9230  * Cutscene that plays when Mario collects a star and leaves a non-painting course, like HMC or BBH.
   9231  */
   9232 BAD_RETURN(s32) cutscene_exit_non_painting_succ(struct Camera *c) {
   9233     cutscene_event(cutscene_exit_succ_start, c, 0, 0);
   9234     cutscene_event(cutscene_exit_non_painting_succ_override_cvar, c, 0, 0);
   9235     cutscene_event(cutscene_non_painting_set_cam_pos, c, 0, -1);
   9236     cutscene_event(cutscene_exit_bowser_succ_focus_left, c, 18, -1);
   9237     cutscene_event(cutscene_non_painting_set_cam_focus, c, 0, -1);
   9238     cutscene_event(cutscene_exit_succ_shake_landing, c, 41, 41);
   9239     update_camera_yaw(c);
   9240 }
   9241 
   9242 /**
   9243  * Set cvar7 to Mario's pos and faceAngle
   9244  * Set cvar6 to the focus offset from Mario.
   9245  * set cvar5 to the pos offset from Mario. (This is always overwritten)
   9246  */
   9247 BAD_RETURN(s32) cutscene_non_painting_death_start(UNUSED struct Camera *c) {
   9248     vec3f_copy(sCutsceneVars[7].point, sMarioCamState->pos);
   9249     vec3s_copy(sCutsceneVars[7].angle, sMarioCamState->faceAngle);
   9250     vec3f_set(sCutsceneVars[6].point, -42.f, 350.f, 727.f);
   9251     // This is always overwritten, except in the unused cutscene_exit_bowser_death()
   9252     vec3f_set(sCutsceneVars[5].point, 107.f, 226.f, 1187.f);
   9253 }
   9254 
   9255 /**
   9256  * This cutscene is the same as non_painting_death, but the camera is closer to Mario and lower.
   9257  * Because it it doesn't call cutscene_non_painting_death_override_offset, the value from
   9258  * cutscene_non_painting_death_start is used.
   9259  *
   9260  * This cutscene is unused, dying in bowser's arena spawns Mario near the warp pipe, not back in the
   9261  * hub.
   9262  */
   9263 BAD_RETURN(s32) cutscene_exit_bowser_death(struct Camera *c) {
   9264     cutscene_event(cutscene_non_painting_death_start, c, 0, 0);
   9265     cutscene_event(cutscene_non_painting_set_cam_pos, c, 0, -1);
   9266     cutscene_event(cutscene_non_painting_set_cam_focus, c, 0, -1);
   9267 }
   9268 
   9269 /**
   9270  * Set the offset from Mario depending on the course Mario exited.
   9271  * This overrides cutscene_non_painting_death_start()
   9272  */
   9273 BAD_RETURN(s32) cutscene_non_painting_death_override_offset(UNUSED struct Camera *c) {
   9274     switch (gPrevLevel) {
   9275         case LEVEL_HMC:
   9276             vec3f_set(sCutsceneVars[5].point, 187.f, 369.f, -197.f);
   9277             break;
   9278         case LEVEL_COTMC:
   9279             vec3f_set(sCutsceneVars[5].point, 187.f, 369.f, -197.f);
   9280             break;
   9281         default:
   9282             vec3f_set(sCutsceneVars[5].point, 107.f, 246.f, 1307.f);
   9283             break;
   9284     }
   9285 }
   9286 
   9287 /**
   9288  * Cutscene played when Mario dies in a non-painting course, like HMC or BBH.
   9289  */
   9290 BAD_RETURN(s32) cutscene_non_painting_death(struct Camera *c) {
   9291     cutscene_event(cutscene_non_painting_death_start, c, 0, 0);
   9292     cutscene_event(cutscene_non_painting_death_override_offset, c, 0, 0);
   9293     cutscene_event(cutscene_non_painting_set_cam_pos, c, 0, -1);
   9294     cutscene_event(cutscene_non_painting_set_cam_focus, c, 0, -1);
   9295     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9296 }
   9297 
   9298 /**
   9299  * Set cvars:
   9300  * cvar3 is an offset applied to the camera's rotation around Mario. It starts at 0x1200
   9301  * cvar 1 is more complicated:
   9302  *      First the yaw from Mario to the camera is calculated. cvar1 is the high byte of the difference
   9303  *      between that yaw and Mario's faceAngle plus 0x1200. The reason for taking the high byte is
   9304  *      because cvar1 rotates until is reaches 0, so it's important that it's a multiple of 0x100.
   9305  */
   9306 BAD_RETURN(s32) cutscene_cap_switch_press_start(struct Camera *c) {
   9307     UNUSED s16 unused;
   9308     s16 yaw;
   9309     UNUSED u8 filler[8];
   9310 
   9311     store_info_star(c);
   9312     yaw = calculate_yaw(sMarioCamState->pos, c->pos);
   9313     sCutsceneVars[3].angle[1] = 0x1200;
   9314     // Basically the amount of rotation to get from behind Mario to in front of Mario
   9315     sCutsceneVars[1].angle[1] = (yaw - (sMarioCamState->faceAngle[1] + sCutsceneVars[3].angle[1])) & 0xFF00;
   9316 }
   9317 
   9318 /**
   9319  * Rotate around Mario. As each cvar stops updating, the rotation slows until the camera ends up in
   9320  * front of Mario.
   9321  */
   9322 BAD_RETURN(s32) cutscene_cap_switch_press_rotate_around_mario(struct Camera *c) {
   9323     f32 dist;
   9324     s16 pitch, yaw;
   9325     UNUSED s16 unusedYaw = sMarioCamState->faceAngle[1] + 0x1000;
   9326     UNUSED u8 filler[2];
   9327     UNUSED s32 cvar1Yaw = sCutsceneVars[1].angle[1];
   9328 
   9329     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   9330 
   9331     // cvar3 wraps around until it reaches 0x1000
   9332     if (sCutsceneVars[3].angle[1] != 0x1000) {
   9333         sCutsceneVars[3].angle[1] += 0x100;
   9334     }
   9335 
   9336     // cvar1 wraps until 0
   9337     if (sCutsceneVars[1].angle[1] != 0) {
   9338         sCutsceneVars[1].angle[1] += 0x100;
   9339     }
   9340 
   9341     yaw = sMarioCamState->faceAngle[1] + sCutsceneVars[3].angle[1] + sCutsceneVars[1].angle[1];
   9342     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   9343 }
   9344 
   9345 /**
   9346  * Move the camera slightly downwards.
   9347  */
   9348 BAD_RETURN(s32) cutscene_cap_switch_press_lower_cam(struct Camera *c) {
   9349     rotate_and_move_vec3f(c->pos, sMarioCamState->pos, 0, -0x20, 0);
   9350 }
   9351 
   9352 /**
   9353  * Move the camera closer to Mario.
   9354  */
   9355 BAD_RETURN(s32) cutscene_cap_switch_press_approach_mario(struct Camera *c) {
   9356     s16 pitch, yaw;
   9357     f32 dist;
   9358 
   9359     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   9360     approach_f32_asymptotic_bool(&dist, 195.f, 0.2f);
   9361     approach_s16_asymptotic_bool(&pitch, 0, 0x10);
   9362     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   9363 
   9364     approach_f32_asymptotic_bool(&c->focus[0], sMarioCamState->pos[0], 0.1f);
   9365     approach_f32_asymptotic_bool(&c->focus[1], sMarioCamState->pos[1] + 110.f, 0.1f);
   9366     approach_f32_asymptotic_bool(&c->focus[2], sMarioCamState->pos[2], 0.1f);
   9367 }
   9368 
   9369 /**
   9370  * Pan the camera left so that Mario is on the right side of the screen when the camera stops spinning.
   9371  */
   9372 BAD_RETURN(s32) cutscene_cap_switch_press_pan_left(struct Camera *c) {
   9373     vec3f_copy(c->focus, sMarioCamState->pos);
   9374     c->focus[1] += 110.f;
   9375     camera_approach_s16_symmetric_bool(&sCutsceneVars[0].angle[1], 0x800, 0x20);
   9376     pan_camera(c, sCutsceneVars[0].angle[0], sCutsceneVars[0].angle[1]);
   9377 }
   9378 
   9379 /**
   9380  * Create a dialog box with the cap switch's text.
   9381  */
   9382 BAD_RETURN(s32) cutscene_cap_switch_press_create_dialog(UNUSED struct Camera *c) {
   9383     create_dialog_box_with_response(gCutsceneFocus->oBhvParams2ndByte + DIALOG_010);
   9384 }
   9385 
   9386 static UNUSED BAD_RETURN(s32) unused_cap_switch_retrieve_info(struct Camera *c) {
   9387     retrieve_info_star(c);
   9388     transition_next_state(c, 30);
   9389 }
   9390 
   9391 /**
   9392  * Cutscene that plays when Mario presses a cap switch.
   9393  */
   9394 BAD_RETURN(s32) cutscene_cap_switch_press(struct Camera *c) {
   9395     f32 dist;
   9396     s16 pitch, yaw;
   9397 
   9398     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   9399     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9400 
   9401     cutscene_event(cutscene_cap_switch_press_start, c, 0, 0);
   9402     cutscene_event(cutscene_cap_switch_press_approach_mario, c, 0, 30);
   9403     cutscene_event(cutscene_cap_switch_press_pan_left, c, 0, -1);
   9404     cutscene_event(cutscene_cap_switch_press_rotate_around_mario, c, 30, -1);
   9405     cutscene_event(cutscene_cap_switch_press_lower_cam, c, 10, 70);
   9406     cutscene_event(cutscene_cap_switch_press_create_dialog, c, 10, 10);
   9407     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   9408 
   9409     if (gDialogResponse != DIALOG_RESPONSE_NONE) {
   9410         sCutsceneVars[4].angle[0] = gDialogResponse;
   9411     }
   9412 
   9413     if ((get_dialog_id() == DIALOG_NONE) && (sCutsceneVars[4].angle[0] != 0)) {
   9414         sCutsceneDialogResponse = sCutsceneVars[4].angle[0];
   9415         if (sCutsceneVars[4].angle[0] == 1) {
   9416             cap_switch_save(gCutsceneFocus->oBhvParams2ndByte);
   9417         }
   9418         stop_cutscene_and_retrieve_stored_info(c);
   9419         transition_next_state(c, 30);
   9420     }
   9421 }
   9422 
   9423 /**
   9424  * Sets cvars:
   9425  * cvar0 is the camera's position
   9426  * cvar1 is the camera's focus
   9427  * cvar2 is the goal position
   9428  * cvar3 is the goal focus
   9429  */
   9430 BAD_RETURN(s32) cutscene_unlock_key_door_start(struct Camera *c) {
   9431     Vec3f posOff, focusOff;
   9432 
   9433     vec3f_copy(sCutsceneVars[0].point, c->pos);
   9434     vec3f_copy(sCutsceneVars[1].point, c->focus);
   9435     vec3f_set(posOff, -206.f, 108.f, 234.f);
   9436     vec3f_set(focusOff, 48.f, 104.f, -193.f);
   9437     offset_rotated(sCutsceneVars[2].point, sMarioCamState->pos, posOff, sMarioCamState->faceAngle);
   9438     offset_rotated(sCutsceneVars[3].point, sMarioCamState->pos, focusOff, sMarioCamState->faceAngle);
   9439 }
   9440 
   9441 /**
   9442  * Move the camera to the cvars position and focus, closer to Mario.
   9443  * Gives a better view of the key.
   9444  */
   9445 BAD_RETURN(s32) cutscene_unlock_key_door_approach_mario(struct Camera *c) {
   9446     approach_vec3f_asymptotic(c->pos, sCutsceneVars[2].point, 0.1f, 0.1f, 0.1f);
   9447     approach_vec3f_asymptotic(c->focus, sCutsceneVars[3].point, 0.1f, 0.1f, 0.1f);
   9448 }
   9449 
   9450 /**
   9451  * Move the camera focus up a bit, focusing on the key in the lock.
   9452  */
   9453 BAD_RETURN(s32) cutscene_unlock_key_door_focus_lock(UNUSED struct Camera *c) {
   9454     approach_f32_asymptotic_bool(&sCutsceneVars[3].point[1], sMarioCamState->pos[1] + 140.f, 0.07f);
   9455 }
   9456 
   9457 BAD_RETURN(s32) cutscene_unlock_key_door_stub(UNUSED struct Camera *c) {
   9458 }
   9459 
   9460 /**
   9461  * Move back to the previous pos and focus, stored in cvar0 and cvar1.
   9462  */
   9463 BAD_RETURN(s32) cutscene_unlock_key_door_fly_back(struct Camera *c) {
   9464     approach_vec3f_asymptotic(c->pos, sCutsceneVars[0].point, 0.1f, 0.1f, 0.1f);
   9465     approach_vec3f_asymptotic(c->focus, sCutsceneVars[1].point, 0.1f, 0.1f, 0.1f);
   9466 }
   9467 
   9468 /**
   9469  * Shake the camera's fov when the key is put in the lock.
   9470  */
   9471 BAD_RETURN(s32) cutscene_unlock_key_door_fov_shake(UNUSED struct Camera *c) {
   9472     cutscene_set_fov_shake_preset(1);
   9473 }
   9474 
   9475 /**
   9476  * Cutscene that plays when Mario unlocks a key door.
   9477  */
   9478 BAD_RETURN(s32) cutscene_unlock_key_door(UNUSED struct Camera *c) {
   9479     cutscene_event(cutscene_unlock_key_door_start, c, 0, 0);
   9480     cutscene_event(cutscene_unlock_key_door_approach_mario, c, 0, 123);
   9481     cutscene_event(cutscene_unlock_key_door_fly_back, c, 124, -1);
   9482     cutscene_event(cutscene_unlock_key_door_fov_shake, c, 79, 79);
   9483     cutscene_event(cutscene_unlock_key_door_focus_lock, c, 70, 110);
   9484     cutscene_event(cutscene_unlock_key_door_stub, c, 112, 112);
   9485 }
   9486 
   9487 /**
   9488  * Move the camera along `positionSpline` and point its focus at the corresponding point along
   9489  * `focusSpline`. sCutsceneSplineSegmentProgress is updated after pos and focus are calculated.
   9490  */
   9491 s32 intro_peach_move_camera_start_to_pipe(struct Camera *c, struct CutsceneSplinePoint positionSpline[],
   9492                   struct CutsceneSplinePoint focusSpline[]) {
   9493     Vec3f offset;
   9494     s32 posReturn = 0;
   9495     s32 focusReturn = 0;
   9496 
   9497     /**
   9498      * The position spline's speed parameters are all 0, so sCutsceneSplineSegmentProgress doesn't get
   9499      * updated. Otherwise position would move two frames ahead, and c->focus would always be one frame
   9500      * further along the spline than c->pos.
   9501      */
   9502     posReturn = move_point_along_spline(c->pos, positionSpline, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   9503     focusReturn = move_point_along_spline(c->focus, focusSpline, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   9504 
   9505     // The two splines used by this function are reflected in the horizontal plane for some reason,
   9506     // so they are rotated every frame. Why do this, Nintendo?
   9507     rotate_in_xz(c->focus, c->focus, DEGREES(-180));
   9508     rotate_in_xz(c->pos, c->pos, DEGREES(-180));
   9509 
   9510     vec3f_set(offset, -1328.f, 260.f, 4664.f);
   9511     vec3f_add(c->focus, offset);
   9512     vec3f_add(c->pos, offset);
   9513 
   9514     posReturn += focusReturn; // Unused
   9515     return focusReturn;
   9516 }
   9517 
   9518 /**
   9519  * Create a dialog box with the letter text
   9520  */
   9521 BAD_RETURN(s32) peach_letter_text(UNUSED struct Camera *c) {
   9522     create_dialog_box(DIALOG_020);
   9523 }
   9524 
   9525 #ifndef VERSION_JP
   9526 BAD_RETURN(s32) play_sound_peach_reading_letter(UNUSED struct Camera *c) {
   9527     play_sound(SOUND_PEACH_DEAR_MARIO, gGlobalSoundSource);
   9528 }
   9529 #endif
   9530 
   9531 /**
   9532  * Move the camera from peach reading the letter all the way to Mario's warp pipe. Follow the
   9533  * sIntroStartToPipe splines.
   9534  */
   9535 BAD_RETURN(s32) cutscene_intro_peach_start_to_pipe_spline(struct Camera *c) {
   9536     if (intro_peach_move_camera_start_to_pipe(c, sIntroStartToPipePosition, sIntroStartToPipeFocus) != 0) {
   9537         gCameraMovementFlags &= ~CAM_MOVE_C_UP_MODE;
   9538         gCutsceneTimer = CUTSCENE_LOOP;
   9539     }
   9540 }
   9541 
   9542 /**
   9543  * Loop the cutscene until Mario exits the dialog.
   9544  */
   9545 BAD_RETURN(s32) cutscene_intro_peach_dialog(struct Camera *c) {
   9546     if (get_dialog_id() == DIALOG_NONE) {
   9547         vec3f_copy(gLakituState.goalPos, c->pos);
   9548         vec3f_copy(gLakituState.goalFocus, c->focus);
   9549         sStatusFlags |= (CAM_FLAG_SMOOTH_MOVEMENT | CAM_FLAG_UNUSED_CUTSCENE_ACTIVE);
   9550         gCutsceneTimer = CUTSCENE_STOP;
   9551         c->cutscene = 0;
   9552     }
   9553 }
   9554 
   9555 BAD_RETURN(s32) cutscene_intro_peach_follow_pipe_spline(struct Camera *c) {
   9556     move_point_along_spline(c->pos, sIntroPipeToDialogPosition, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   9557     move_point_along_spline(c->focus, sIntroPipeToDialogFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   9558 }
   9559 
   9560 BAD_RETURN(s32) cutscene_intro_peach_clear_cutscene_status(UNUSED struct Camera *c) {
   9561     sMarioCamState->cameraEvent = 0;
   9562 }
   9563 
   9564 /**
   9565  * Set fov to 8 degrees, then zoom out to 30.
   9566  */
   9567 BAD_RETURN(s32) cutscene_intro_peach_zoom_fov(UNUSED struct Camera *c) {
   9568     sFOVState.fov = 8.f;
   9569     set_fov_function(CAM_FOV_ZOOM_30);
   9570 }
   9571 
   9572 /**
   9573  * Reset the spline progress, turn on handheld shake.
   9574  */
   9575 BAD_RETURN(s32) cutscene_intro_peach_reset_spline(UNUSED struct Camera *c) {
   9576     sCutsceneSplineSegment = 0;
   9577     sCutsceneSplineSegmentProgress = 0.1f;
   9578     //! @bug since this event is only called for one frame, this handheld shake is turned off on the
   9579     //! next frame.
   9580     set_handheld_shake(HAND_CAM_SHAKE_HIGH);
   9581 }
   9582 
   9583 /**
   9584  * Turn off handheld shake. This was likely written before handheld shake was changed to turn off every
   9585  * frame, as it's the only instance of HAND_CAM_SHAKE_OFF.
   9586  */
   9587 BAD_RETURN(s32) cutscene_intro_peach_handheld_shake_off(UNUSED struct Camera *c) {
   9588     set_handheld_shake(HAND_CAM_SHAKE_OFF);
   9589 }
   9590 
   9591 BAD_RETURN(s32) intro_pipe_exit_text(UNUSED struct Camera *c) {
   9592     create_dialog_box(DIALOG_033);
   9593 }
   9594 
   9595 #ifndef VERSION_JP
   9596 BAD_RETURN(s32) play_sound_intro_turn_on_hud(UNUSED struct Camera *c) {
   9597     play_sound_rbutton_changed();
   9598 }
   9599 #endif
   9600 
   9601 /**
   9602  * Fly to the pipe. Near the end, the camera jumps to Lakitu's position and the hud turns on.
   9603  */
   9604 BAD_RETURN(s32) cutscene_intro_peach_fly_to_pipe(struct Camera *c) {
   9605 #if defined(VERSION_US) || defined(VERSION_SH) || defined(VERSION_CN)
   9606     cutscene_event(play_sound_intro_turn_on_hud, c, 818, 818);
   9607 #elif defined(VERSION_EU)
   9608     cutscene_event(play_sound_intro_turn_on_hud, c, 673, 673);
   9609 #endif
   9610     cutscene_spawn_obj(6, 1);
   9611     cutscene_event(cutscene_intro_peach_start_flying_music, c, 0, 0);
   9612     cutscene_event(cutscene_intro_peach_start_to_pipe_spline, c, 0, -1);
   9613 #ifdef VERSION_EU
   9614     cutscene_event(cutscene_intro_peach_clear_cutscene_status, c, 572, 572);
   9615 #else
   9616     cutscene_event(cutscene_intro_peach_clear_cutscene_status, c, 717, 717);
   9617 #endif
   9618     clamp_pitch(c->pos, c->focus, 0x3B00, -0x3B00);
   9619     sCutsceneVars[1].point[1] = 400.f;
   9620 }
   9621 
   9622 /**
   9623  * Lakitu flies around the warp pipe, then Mario jumps out.
   9624  */
   9625 BAD_RETURN(s32) cutscene_intro_peach_mario_appears(struct Camera *c) {
   9626     UNUSED u8 filler[8];
   9627 
   9628     sMarioCamState->cameraEvent = 0;
   9629     cutscene_event(cutscene_intro_peach_reset_spline, c, 0, 0);
   9630     cutscene_event(cutscene_intro_peach_follow_pipe_spline, c, 0, -1);
   9631     cutscene_event(cutscene_intro_peach_handheld_shake_off, c, 70, 70);
   9632     cutscene_event(intro_pipe_exit_text, c, 250, 250);
   9633 
   9634     approach_f32_asymptotic_bool(&sCutsceneVars[1].point[1], 80.f + sMarioGeometry.currFloorHeight +
   9635                                  (sMarioCamState->pos[1] - sMarioGeometry.currFloorHeight) * 1.1f, 0.4f);
   9636 
   9637     // Make the camera look up as Mario jumps out of the pipe
   9638     if (c->focus[1] < sCutsceneVars[1].point[1]) {
   9639         c->focus[1] = sCutsceneVars[1].point[1];
   9640     }
   9641 
   9642     sStatusFlags |= CAM_FLAG_UNUSED_CUTSCENE_ACTIVE;
   9643 }
   9644 
   9645 /**
   9646  * Reset the fov. This gives the effect of peach zooming out as she fades.
   9647  */
   9648 BAD_RETURN(s32) cutscene_intro_peach_reset_fov(UNUSED struct Camera *c) {
   9649     set_fov_function(CAM_FOV_DEFAULT);
   9650 }
   9651 
   9652 /**
   9653  * Peach reads the letter to Mario.
   9654  */
   9655 BAD_RETURN(s32) cutscene_intro_peach_letter(struct Camera *c) {
   9656     cutscene_spawn_obj(5, 0);
   9657     cutscene_event(cutscene_intro_peach_zoom_fov, c, 0, 0);
   9658     cutscene_event(cutscene_intro_peach_start_letter_music, c, 65, 65);
   9659 #ifdef VERSION_EU
   9660     cutscene_event(cutscene_intro_peach_eu_lower_volume, c, 68, 68);
   9661 #endif
   9662     cutscene_event(cutscene_intro_peach_start_to_pipe_spline, c, 0, 0);
   9663     cutscene_event(peach_letter_text, c, 65, 65);
   9664 #ifndef VERSION_JP
   9665     cutscene_event(play_sound_peach_reading_letter, c, 83, 83);
   9666 #endif
   9667 
   9668     if ((gCutsceneTimer > 120) && (get_dialog_id() == DIALOG_NONE)) {
   9669         // Start the next scene
   9670         gCutsceneTimer = CUTSCENE_LOOP;
   9671     }
   9672 
   9673     clamp_pitch(c->pos, c->focus, 0x3B00, -0x3B00);
   9674 }
   9675 
   9676 /**
   9677  * Reset the spline progress.
   9678  */
   9679 BAD_RETURN(s32) cutscene_end_waving_start(UNUSED struct Camera *c) {
   9680     cutscene_reset_spline();
   9681 }
   9682 
   9683 // 3rd part of data
   9684 struct CutsceneSplinePoint gIntroLakituStartToPipeFocus[] = {
   9685     { 0, 32, { 58, -250, 346 } },    { 1, 50, { -159, -382, 224 } }, { 2, 37, { 0, -277, 237 } },
   9686     { 3, 15, { 1, -44, 245 } },      { 4, 35, { 0, -89, 228 } },     { 5, 15, { 28, 3, 259 } },
   9687     { 6, 25, { -38, -201, 371 } },   { 7, 20, { -642, 118, 652 } },  { 8, 25, { 103, -90, 861 } },
   9688     { 9, 25, { 294, 145, 579 } },    { 10, 30, { 220, -42, 500 } },  { 11, 20, { 10, -134, 200 } },
   9689     { 12, 20, { -143, -145, 351 } }, { 13, 14, { -256, -65, 528 } }, { 14, 20, { -251, -52, 459 } },
   9690     { 15, 25, { -382, 520, 395 } },  { 16, 25, { -341, 240, 653 } }, { 17, 5, { -262, 700, 143 } },
   9691     { 18, 15, { -760, 32, 27 } },    { 19, 20, { -756, -6, -26 } },  { 20, 20, { -613, 5, 424 } },
   9692     { 21, 20, { -22, -100, 312 } },  { 22, 25, { 212, 80, 61 } },    { 23, 20, { 230, -28, 230 } },
   9693     { 24, 35, { -83, -51, 303 } },   { 25, 17, { 126, 90, 640 } },   { 26, 9, { 158, 95, 763 } },
   9694     { 27, 8, { 113, -25, 1033 } },   { 28, 20, { 57, -53, 1291 } },  { 29, 15, { 73, -34, 1350 } },
   9695     { 30, 7, { 0, 96, 1400 } },      { 31, 8, { -59, 269, 1450 } },  { 32, 15, { 57, 1705, 1500 } },
   9696     { 0, 15, { -227, 511, 1550 } },  { -1, 15, { -227, 511, 1600 } }
   9697 };
   9698 
   9699 struct CutsceneSplinePoint gIntroLakituStartToPipeOffsetFromCamera[] = {
   9700     { 0, 0, { -46, 87, -15 } },   { 1, 0, { -38, 91, -11 } },  { 2, 0, { -31, 93, -13 } },
   9701     { 3, 0, { -50, 84, -16 } },   { 4, 0, { -52, 83, -17 } },  { 5, 0, { -10, 99, 3 } },
   9702     { 6, 0, { -54, 83, -10 } },   { 7, 0, { -31, 85, -40 } },  { 8, 0, { -34, 91, 19 } },
   9703     { 9, 0, { -9, 95, 28 } },     { 10, 0, { 17, 72, 66 } },   { 11, 0, { 88, -7, 45 } },
   9704     { 12, 0, { 96, -6, -26 } },   { 13, 0, { 56, -1, -82 } },  { 14, 0, { 40, 65, -63 } },
   9705     { 15, 0, { -26, -3, -96 } },  { 16, 0, { 92, 82, 19 } },   { 17, 0, { 92, 32, 19 } },
   9706     { 18, 0, { 92, 32, 19 } },    { 19, 0, { 92, 102, 19 } },  { 20, 0, { -69, 59, -70 } },
   9707     { 21, 0, { -77, 109, -61 } }, { 22, 0, { -87, 59, -46 } }, { 23, 0, { -99, -3, 11 } },
   9708     { 24, 0, { -99, -11, 5 } },   { 25, 0, { -97, -6, 19 } },  { 26, 0, { -97, 22, -7 } },
   9709     { 27, 0, { -98, -11, -13 } }, { 28, 0, { -97, -11, 19 } }, { 29, 0, { -91, -11, 38 } },
   9710     { 30, 0, { -76, -11, 63 } },  { 31, 0, { -13, 33, 93 } },  { 32, 0, { 51, -11, 84 } },
   9711     { 33, 0, { 51, -11, 84 } },   { -1, 0, { 51, -11, 84 } }
   9712 };
   9713 
   9714 struct CutsceneSplinePoint gEndWavingPos[] = {
   9715     { 0, 0, { -5, 975, -917 } },    { 0, 0, { -5, 975, -917 } },    { 0, 0, { -5, 975, -917 } },
   9716     { 0, 0, { -76, 1067, 742 } },   { 0, 0, { -105, 1576, 3240 } }, { 0, 0, { -177, 1709, 5586 } },
   9717     { 0, 0, { -177, 1709, 5586 } }, { 0, 0, { -177, 1709, 5586 } }, { 0, 0, { -177, 1709, 5586 } }
   9718 };
   9719 
   9720 struct CutsceneSplinePoint gEndWavingFocus[] = {
   9721     { 0, 50, { 18, 1013, -1415 } }, { 0, 100, { 17, 1037, -1412 } }, { 0, 100, { 16, 1061, -1408 } },
   9722     { 0, 100, { -54, 1053, 243 } }, { 0, 100, { -84, 1575, 2740 } }, { 0, 50, { -156, 1718, 5086 } },
   9723     { 0, 0, { -156, 1718, 5086 } }, { 0, 0, { -156, 1718, 5086 } },  { 0, 0, { -156, 1718, 5086 } }
   9724 };
   9725 
   9726 BAD_RETURN(s32) cutscene_end_waving(struct Camera *c) {
   9727     cutscene_event(cutscene_end_waving_start, c, 0, 0);
   9728     move_point_along_spline(c->pos, gEndWavingPos, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   9729     move_point_along_spline(c->focus, gEndWavingFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   9730     cutscene_spawn_obj(6, 120);
   9731 }
   9732 
   9733 /**
   9734  * Called on the first frame of the credits. Resets the spline progress.
   9735  */
   9736 BAD_RETURN(s32) cutscene_credits_reset_spline(UNUSED struct Camera *c) {
   9737     cutscene_reset_spline();
   9738 }
   9739 
   9740 extern struct CutsceneSplinePoint sBoBCreditsSplinePositions[];
   9741 extern struct CutsceneSplinePoint sBoBCreditsSplineFocus[];
   9742 extern struct CutsceneSplinePoint sWFCreditsSplinePositions[];
   9743 extern struct CutsceneSplinePoint sWFCreditsSplineFocus[];
   9744 extern struct CutsceneSplinePoint sJRBCreditsSplinePositions[];
   9745 extern struct CutsceneSplinePoint sJRBCreditsSplineFocus[];
   9746 extern struct CutsceneSplinePoint sCCMSlideCreditsSplinePositions[];
   9747 extern struct CutsceneSplinePoint sCCMSlideCreditsSplineFocus[];
   9748 extern struct CutsceneSplinePoint sBBHCreditsSplinePositions[];
   9749 extern struct CutsceneSplinePoint sBBHCreditsSplineFocus[];
   9750 extern struct CutsceneSplinePoint sHMCCreditsSplinePositions[];
   9751 extern struct CutsceneSplinePoint sHMCCreditsSplineFocus[];
   9752 extern struct CutsceneSplinePoint sTHIWigglerCreditsSplinePositions[];
   9753 extern struct CutsceneSplinePoint sTHIWigglerCreditsSplineFocus[];
   9754 extern struct CutsceneSplinePoint sVolcanoCreditsSplinePositions[];
   9755 extern struct CutsceneSplinePoint sVolcanoCreditsSplineFocus[];
   9756 extern struct CutsceneSplinePoint sSSLCreditsSplinePositions[];
   9757 extern struct CutsceneSplinePoint sSSLCreditsSplineFocus[];
   9758 extern struct CutsceneSplinePoint sDDDCreditsSplinePositions[];
   9759 extern struct CutsceneSplinePoint sDDDCreditsSplineFocus[];
   9760 extern struct CutsceneSplinePoint sSLCreditsSplinePositions[];
   9761 extern struct CutsceneSplinePoint sSLCreditsSplineFocus[];
   9762 extern struct CutsceneSplinePoint sWDWCreditsSplinePositions[];
   9763 extern struct CutsceneSplinePoint sWDWCreditsSplineFocus[];
   9764 extern struct CutsceneSplinePoint sTTMCreditsSplinePositions[];
   9765 extern struct CutsceneSplinePoint sTTMCreditsSplineFocus[];
   9766 extern struct CutsceneSplinePoint sTHIHugeCreditsSplinePositions[];
   9767 extern struct CutsceneSplinePoint sTHIHugeCreditsSplineFocus[];
   9768 extern struct CutsceneSplinePoint sTTCCreditsSplinePositions[];
   9769 extern struct CutsceneSplinePoint sTTCCreditsSplineFocus[];
   9770 extern struct CutsceneSplinePoint sRRCreditsSplinePositions[];
   9771 extern struct CutsceneSplinePoint sRRCreditsSplineFocus[];
   9772 extern struct CutsceneSplinePoint sSACreditsSplinePositions[];
   9773 extern struct CutsceneSplinePoint sSACreditsSplineFocus[];
   9774 extern struct CutsceneSplinePoint sCotMCCreditsSplinePositions[];
   9775 extern struct CutsceneSplinePoint sCotMCCreditsSplineFocus[];
   9776 extern struct CutsceneSplinePoint sDDDSubCreditsSplinePositions[];
   9777 extern struct CutsceneSplinePoint sDDDSubCreditsSplineFocus[];
   9778 extern struct CutsceneSplinePoint sCCMOutsideCreditsSplinePositions[];
   9779 extern struct CutsceneSplinePoint sCCMOutsideCreditsSplineFocus[];
   9780 
   9781 /**
   9782  * Follow splines through the courses of the game.
   9783  */
   9784 BAD_RETURN(s32) cutscene_credits(struct Camera *c) {
   9785     struct CutsceneSplinePoint *focus, *pos;
   9786 
   9787     cutscene_event(cutscene_credits_reset_spline, c, 0, 0);
   9788 
   9789     switch (gCurrLevelArea) {
   9790         case AREA_BOB:
   9791             pos = sBoBCreditsSplinePositions;
   9792             focus = sBoBCreditsSplineFocus;
   9793             break;
   9794         case AREA_WF:
   9795             pos = sWFCreditsSplinePositions;
   9796             focus = sWFCreditsSplineFocus;
   9797             break;
   9798         case AREA_JRB_MAIN:
   9799             pos = sJRBCreditsSplinePositions;
   9800             focus = sJRBCreditsSplineFocus;
   9801             break;
   9802         case AREA_CCM_SLIDE:
   9803             pos = sCCMSlideCreditsSplinePositions;
   9804             focus = sCCMSlideCreditsSplineFocus;
   9805             break;
   9806         case AREA_BBH:
   9807             pos = sBBHCreditsSplinePositions;
   9808             focus = sBBHCreditsSplineFocus;
   9809             break;
   9810         case AREA_HMC:
   9811             pos = sHMCCreditsSplinePositions;
   9812             focus = sHMCCreditsSplineFocus;
   9813             break;
   9814         case AREA_THI_WIGGLER:
   9815             pos = sTHIWigglerCreditsSplinePositions;
   9816             focus = sTHIWigglerCreditsSplineFocus;
   9817             break;
   9818         case AREA_LLL_VOLCANO:
   9819             pos = sVolcanoCreditsSplinePositions;
   9820             focus = sVolcanoCreditsSplineFocus;
   9821             break;
   9822         case AREA_SSL_OUTSIDE:
   9823             pos = sSSLCreditsSplinePositions;
   9824             focus = sSSLCreditsSplineFocus;
   9825             break;
   9826         case AREA_DDD_WHIRLPOOL:
   9827             pos = sDDDCreditsSplinePositions;
   9828             focus = sDDDCreditsSplineFocus;
   9829             break;
   9830         case AREA_SL_OUTSIDE:
   9831             pos = sSLCreditsSplinePositions;
   9832             focus = sSLCreditsSplineFocus;
   9833             break;
   9834         case AREA_WDW_MAIN:
   9835             pos = sWDWCreditsSplinePositions;
   9836             focus = sWDWCreditsSplineFocus;
   9837             break;
   9838         case AREA_TTM_OUTSIDE:
   9839             pos = sTTMCreditsSplinePositions;
   9840             focus = sTTMCreditsSplineFocus;
   9841             break;
   9842         case AREA_THI_HUGE:
   9843             pos = sTHIHugeCreditsSplinePositions;
   9844             focus = sTHIHugeCreditsSplineFocus;
   9845             break;
   9846         case AREA_TTC:
   9847             pos = sTTCCreditsSplinePositions;
   9848             focus = sTTCCreditsSplineFocus;
   9849             break;
   9850         case AREA_RR:
   9851             pos = sRRCreditsSplinePositions;
   9852             focus = sRRCreditsSplineFocus;
   9853             break;
   9854         case AREA_SA:
   9855             pos = sSACreditsSplinePositions;
   9856             focus = sSACreditsSplineFocus;
   9857             break;
   9858         case AREA_COTMC:
   9859             pos = sCotMCCreditsSplinePositions;
   9860             focus = sCotMCCreditsSplineFocus;
   9861             break;
   9862         case AREA_DDD_SUB:
   9863             pos = sDDDSubCreditsSplinePositions;
   9864             focus = sDDDSubCreditsSplineFocus;
   9865             break;
   9866         case AREA_CCM_OUTSIDE:
   9867             //! Checks if the "Snowman's Lost His Head" star was collected. The credits likely would
   9868             //! have avoided the snowman if the player didn't collect that star, but in the end the
   9869             //! developers decided against it.
   9870             if (save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(gCurrCourseNum)) & (1 << 4)) {
   9871                 pos = sCCMOutsideCreditsSplinePositions;
   9872                 focus = sCCMOutsideCreditsSplineFocus;
   9873             } else {
   9874                 pos = sCCMOutsideCreditsSplinePositions;
   9875                 focus = sCCMOutsideCreditsSplineFocus;
   9876             }
   9877             break;
   9878         default:
   9879             pos = sCCMOutsideCreditsSplinePositions;
   9880             focus = sCCMOutsideCreditsSplineFocus;
   9881     }
   9882 
   9883     copy_spline_segment(sCurCreditsSplinePos, pos);
   9884     copy_spline_segment(sCurCreditsSplineFocus, focus);
   9885     move_point_along_spline(c->pos, sCurCreditsSplinePos, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   9886     move_point_along_spline(c->focus, sCurCreditsSplineFocus, &sCutsceneSplineSegment, &sCutsceneSplineSegmentProgress);
   9887     player2_rotate_cam(c, -0x2000, 0x2000, -0x4000, 0x4000);
   9888 }
   9889 
   9890 /**
   9891  * Set the camera pos relative to Mario.
   9892  */
   9893 BAD_RETURN(s32) cutscene_sliding_doors_open_start(struct Camera *c) {
   9894     f32 dist;
   9895     s16 pitch, yaw;
   9896 
   9897     vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
   9898 
   9899     // If the camera is too close, warp it backwards set it to a better angle.
   9900     if (dist < 500.f) {
   9901         dist = 500.f;
   9902         yaw = sMarioCamState->faceAngle[1] + 0x8800;
   9903         pitch = 0x800;
   9904     }
   9905 
   9906     vec3f_set_dist_and_angle(sMarioCamState->pos, c->pos, dist, pitch, yaw);
   9907 }
   9908 
   9909 /**
   9910  * cvar1: Mario's position
   9911  * cvar0.angle: Mario's angle
   9912  * cvar0.point: offset from Mario
   9913  */
   9914 BAD_RETURN(s32) cutscene_sliding_doors_open_set_cvars(UNUSED struct Camera *c) {
   9915     vec3f_copy(sCutsceneVars[1].point, sMarioCamState->pos);
   9916     vec3s_copy(sCutsceneVars[0].angle, sMarioCamState->faceAngle);
   9917     vec3f_set(sCutsceneVars[0].point, 80.f, 325.f, 200.f);
   9918 }
   9919 
   9920 /**
   9921  * Decrease the cvar0 y offset to 75, which would simulate Lakitu flying under the doorway.
   9922  * However, the initial y offset is too high for Lakitu to reach 75 in time.
   9923  */
   9924 BAD_RETURN(s32) cutscene_sliding_doors_go_under_doorway(UNUSED struct Camera *c) {
   9925     camera_approach_f32_symmetric_bool(&sCutsceneVars[0].point[1], 75.f, 10.f);
   9926 }
   9927 
   9928 /**
   9929  * Approach a y offset of 125 again.
   9930  */
   9931 BAD_RETURN(s32) cutscene_sliding_doors_fly_back_up(UNUSED struct Camera *c) {
   9932     camera_approach_f32_symmetric_bool(&sCutsceneVars[0].point[1], 125.f, 10.f);
   9933 }
   9934 
   9935 /**
   9936  * Follow Mario through the door, by approaching cvar1.point.
   9937  */
   9938 BAD_RETURN(s32) cutscene_sliding_doors_follow_mario(struct Camera *c) {
   9939     Vec3f pos;
   9940     UNUSED u8 filler[20];
   9941 
   9942     vec3f_copy(pos, c->pos);
   9943     // Update cvar1 with Mario's position (the y value doesn't change)
   9944     sCutsceneVars[1].point[0] = sMarioCamState->pos[0];
   9945     sCutsceneVars[1].point[2] = sMarioCamState->pos[2];
   9946 
   9947     // Decrease cvar0's offsets, moving the camera behind Mario at his eye height.
   9948     approach_f32_asymptotic_bool(&sCutsceneVars[0].point[0], 0, 0.1f);
   9949     camera_approach_f32_symmetric_bool(&sCutsceneVars[0].point[2], 125.f, 50.f);
   9950     // Update cvar0's angle
   9951     approach_vec3s_asymptotic(sCutsceneVars[0].angle, sMarioCamState->faceAngle, 16, 16, 16);
   9952 
   9953     // Apply the offset to the camera's position
   9954     offset_rotated(pos, sCutsceneVars[1].point, sCutsceneVars[0].point, sCutsceneVars[0].angle);
   9955     approach_vec3f_asymptotic(c->pos, pos, 0.15f, 0.05f, 0.15f);
   9956 
   9957     // Focus on Mario's eye height
   9958     set_focus_rel_mario(c, 0, 125.f, 0, 0);
   9959 }
   9960 
   9961 /**
   9962  * Plays when Mario opens the sliding doors.
   9963  * Note: the star door unlocking event is not a cutscene, it's handled by Mario separately.
   9964  */
   9965 BAD_RETURN(s32) cutscene_sliding_doors_open(struct Camera *c) {
   9966     UNUSED u8 filler[8];
   9967 
   9968     reset_pan_distance(c);
   9969     cutscene_event(cutscene_sliding_doors_open_start, c, 0, 8);
   9970     cutscene_event(cutscene_sliding_doors_open_set_cvars, c, 8, 8);
   9971     cutscene_event(cutscene_sliding_doors_go_under_doorway, c, 8, 28);
   9972     cutscene_event(cutscene_sliding_doors_fly_back_up, c, 29, -1);
   9973     cutscene_event(cutscene_sliding_doors_follow_mario, c, 8, -1);
   9974 }
   9975 
   9976 /**
   9977  * Ends the double door cutscene.
   9978  */
   9979 BAD_RETURN(s32) cutscene_double_doors_end(struct Camera *c) {
   9980     set_flag_post_door(c);
   9981     c->cutscene = 0;
   9982     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
   9983 }
   9984 
   9985 BAD_RETURN(s32) cutscene_enter_painting_stub(UNUSED struct Camera *c) {
   9986 }
   9987 
   9988 /**
   9989  * Plays when Mario enters a painting. The camera flies up to the painting's center, then it slowly
   9990  * zooms in until the star select screen appears.
   9991  */
   9992 BAD_RETURN(s32) cutscene_enter_painting(struct Camera *c) {
   9993     struct Surface *floor, *highFloor;
   9994     Vec3f paintingPos, focus, focusOffset;
   9995     Vec3s paintingAngle;
   9996     f32 floorHeight;
   9997 
   9998     cutscene_event(cutscene_enter_painting_stub, c, 0, 0);
   9999     // Zoom in
  10000     set_fov_function(CAM_FOV_APP_20);
  10001     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
  10002 
  10003     if (gRipplingPainting != NULL) {
  10004         paintingAngle[0] = 0;
  10005         paintingAngle[1] = (s32)((gRipplingPainting->yaw / 360.f) * 65536.f); // convert degrees to IAU
  10006         paintingAngle[2] = 0;
  10007 
  10008         focusOffset[0] = gRipplingPainting->size / 2;
  10009         focusOffset[1] = focusOffset[0];
  10010         focusOffset[2] = 0;
  10011 
  10012         paintingPos[0] = gRipplingPainting->posX;
  10013         paintingPos[1] = gRipplingPainting->posY;
  10014         paintingPos[2] = gRipplingPainting->posZ;
  10015 
  10016         offset_rotated(focus, paintingPos, focusOffset, paintingAngle);
  10017         approach_vec3f_asymptotic(c->focus, focus, 0.1f, 0.1f, 0.1f);
  10018         focusOffset[2] = -(((gRipplingPainting->size * 1000.f) / 2) / 307.f);
  10019         offset_rotated(focus, paintingPos, focusOffset, paintingAngle);
  10020         floorHeight = find_floor(focus[0], focus[1] + 500.f, focus[2], &highFloor) + 125.f;
  10021 
  10022         if (focus[1] < floorHeight) {
  10023             focus[1] = floorHeight;
  10024         }
  10025 
  10026         if (c->cutscene == CUTSCENE_ENTER_PAINTING) {
  10027             approach_vec3f_asymptotic(c->pos, focus, 0.2f, 0.1f, 0.2f);
  10028         } else {
  10029             approach_vec3f_asymptotic(c->pos, focus, 0.9f, 0.9f, 0.9f);
  10030         }
  10031 
  10032         find_floor(sMarioCamState->pos[0], sMarioCamState->pos[1] + 50.f, sMarioCamState->pos[2], &floor);
  10033 
  10034         if ((floor->type < SURFACE_PAINTING_WOBBLE_A6) || (floor->type > SURFACE_PAINTING_WARP_F9)) {
  10035             c->cutscene = 0;
  10036             gCutsceneTimer = CUTSCENE_STOP;
  10037             sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
  10038         }
  10039     }
  10040     c->mode = CAMERA_MODE_CLOSE;
  10041 }
  10042 
  10043 /**
  10044  * Warp the camera to Mario, then use his faceAngle to calculate the right relative position.
  10045  *
  10046  * cvar0.point is Mario's position
  10047  * cvar0.angle is Mario's faceAngle
  10048  *
  10049  * cvar1 is the camera's position relative to Mario
  10050  * cvar2 is the camera's focus relative to Mario
  10051  */
  10052 BAD_RETURN(s32) cutscene_exit_painting_start(struct Camera *c) {
  10053     struct Surface *floor;
  10054     f32 floorHeight;
  10055 
  10056     vec3f_set(sCutsceneVars[2].point, 258.f, -352.f, 1189.f);
  10057     vec3f_set(sCutsceneVars[1].point, 65.f, -155.f, 444.f);
  10058 
  10059     if (gPrevLevel == LEVEL_TTM) {
  10060         sCutsceneVars[1].point[1] = 0.f;
  10061         sCutsceneVars[1].point[2] = 0.f;
  10062     }
  10063     vec3f_copy(sCutsceneVars[0].point, sMarioCamState->pos);
  10064     sCutsceneVars[0].angle[0] = 0;
  10065     sCutsceneVars[0].angle[1] = sMarioCamState->faceAngle[1];
  10066     sCutsceneVars[0].angle[2] = 0;
  10067     offset_rotated(c->focus, sCutsceneVars[0].point, sCutsceneVars[1].point, sCutsceneVars[0].angle);
  10068     offset_rotated(c->pos, sCutsceneVars[0].point, sCutsceneVars[2].point, sCutsceneVars[0].angle);
  10069     floorHeight = find_floor(c->pos[0], c->pos[1] + 10.f, c->pos[2], &floor);
  10070 
  10071     if (floorHeight != FLOOR_LOWER_LIMIT) {
  10072         if (c->pos[1] < (floorHeight += 60.f)) {
  10073             c->pos[1] = floorHeight;
  10074         }
  10075     }
  10076 }
  10077 
  10078 /**
  10079  * Decrease cvar2's x and z offset, moving closer to Mario.
  10080  */
  10081 BAD_RETURN(s32) cutscene_exit_painting_move_to_mario(struct Camera *c) {
  10082     Vec3f pos;
  10083 
  10084     //! Tricky math: Since offset_rotated() flips Z offsets, you'd expect a positive Z offset to move
  10085     //! the camera into the wall. However, Mario's faceAngle always points into the painting, so a
  10086     //! positive Z offset moves the camera "behind" Mario, away from the painting.
  10087     //!
  10088     //! In the success cutscene, when Mario jumps out face-first, only his gfx angle is updated. His
  10089     //! actual face angle isn't updated until after the cutscene.
  10090     approach_f32_asymptotic_bool(&sCutsceneVars[2].point[0], 178.f, 0.05f);
  10091     approach_f32_asymptotic_bool(&sCutsceneVars[2].point[2], 889.f, 0.05f);
  10092     offset_rotated(pos, sCutsceneVars[0].point, sCutsceneVars[2].point, sCutsceneVars[0].angle);
  10093     c->pos[0] = pos[0];
  10094     c->pos[2] = pos[2];
  10095 }
  10096 
  10097 /**
  10098  * Move the camera down to the floor Mario lands on.
  10099  */
  10100 BAD_RETURN(s32) cutscene_exit_painting_move_to_floor(struct Camera *c) {
  10101     struct Surface *floor;
  10102     Vec3f floorHeight;
  10103 
  10104     vec3f_copy(floorHeight, sMarioCamState->pos);
  10105     floorHeight[1] = find_floor(sMarioCamState->pos[0], sMarioCamState->pos[1] + 10.f, sMarioCamState->pos[2], &floor);
  10106 
  10107     if (floor != NULL) {
  10108         floorHeight[1] = floorHeight[1] + (sMarioCamState->pos[1] - floorHeight[1]) * 0.7f + 125.f;
  10109         approach_vec3f_asymptotic(c->focus, floorHeight, 0.2f, 0.2f, 0.2f);
  10110 
  10111         if (floorHeight[1] < c->pos[1]) {
  10112             approach_f32_asymptotic_bool(&c->pos[1], floorHeight[1], 0.05f);
  10113         }
  10114     }
  10115 }
  10116 
  10117 /**
  10118  * Cutscene played when Mario leaves a painting, either due to death or collecting a star.
  10119  */
  10120 BAD_RETURN(s32) cutscene_exit_painting(struct Camera *c) {
  10121     cutscene_event(cutscene_exit_painting_start, c, 0, 0);
  10122     cutscene_event(cutscene_exit_painting_move_to_mario, c, 5, -1);
  10123     cutscene_event(cutscene_exit_painting_move_to_floor, c, 5, -1);
  10124 
  10125     //! Hardcoded position. TTM's painting is close to an opposite wall, so just fix the pos.
  10126     if (gPrevLevel == LEVEL_TTM) {
  10127         vec3f_set(c->pos, -296.f, 1261.f, 3521.f);
  10128     }
  10129 
  10130     update_camera_yaw(c);
  10131 }
  10132 
  10133 /**
  10134  * Unused. Warp the camera to Mario.
  10135  */
  10136 BAD_RETURN(s32) cutscene_unused_exit_start(struct Camera *c) {
  10137     UNUSED u8 filler[18];
  10138     Vec3f offset;
  10139     Vec3s marioAngle;
  10140 
  10141     vec3f_set(offset, 200.f, 300.f, 200.f);
  10142     vec3s_set(marioAngle, 0, sMarioCamState->faceAngle[1], 0);
  10143     offset_rotated(c->pos, sMarioCamState->pos, offset, marioAngle);
  10144     set_focus_rel_mario(c, 0.f, 125.f, 0.f, 0);
  10145 }
  10146 
  10147 /**
  10148  * Unused. Focus on Mario as he exits.
  10149  */
  10150 BAD_RETURN(s32) cutscene_unused_exit_focus_mario(struct Camera *c) {
  10151     Vec3f focus;
  10152 
  10153     vec3f_set(focus, sMarioCamState->pos[0], sMarioCamState->pos[1] + 125.f, sMarioCamState->pos[2]);
  10154     set_focus_rel_mario(c, 0.f, 125.f, 0.f, 0);
  10155     approach_vec3f_asymptotic(c->focus, focus, 0.02f, 0.001f, 0.02f);
  10156     update_camera_yaw(c);
  10157 }
  10158 
  10159 /**
  10160  * Give control back to the player.
  10161  */
  10162 BAD_RETURN(s32) cutscene_exit_painting_end(struct Camera *c) {
  10163     c->mode = CAMERA_MODE_CLOSE;
  10164     c->cutscene = 0;
  10165     gCutsceneTimer = CUTSCENE_STOP;
  10166     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
  10167     sStatusFlags &= ~CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
  10168     update_camera_yaw(c);
  10169 }
  10170 
  10171 /**
  10172  * End the cutscene, starting cannon mode.
  10173  */
  10174 BAD_RETURN(s32) cutscene_enter_cannon_end(struct Camera *c) {
  10175     sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
  10176     sStatusFlags |= CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
  10177     c->mode = CAMERA_MODE_INSIDE_CANNON;
  10178     c->cutscene = 0;
  10179     sCannonYOffset = 800.f;
  10180 }
  10181 
  10182 /**
  10183  * Rotate around the cannon as it rises out of the hole.
  10184  */
  10185 BAD_RETURN(s32) cutscene_enter_cannon_raise(struct Camera *c) {
  10186     struct Object *o;
  10187     UNUSED u8 filler[8];
  10188     f32 floorHeight;
  10189     struct Surface *floor;
  10190     Vec3f cannonFocus;
  10191     Vec3s cannonAngle;
  10192 
  10193     // Shake the camera when the cannon is fully raised
  10194     cutscene_event(cutscene_shake_explosion, c, 70, 70);
  10195     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
  10196     camera_approach_s16_symmetric_bool(&sCutsceneVars[1].angle[0], 0, 0x80);
  10197     camera_approach_s16_symmetric_bool(&sCutsceneVars[2].angle[0], 0, 0x80);
  10198     // Move the camera around the cannon, gradually rotating and moving closer
  10199     vec3f_set_dist_and_angle(sCutsceneVars[0].point, c->pos, sCutsceneVars[1].point[2], sCutsceneVars[1].angle[0],
  10200                              sCutsceneVars[1].angle[1]);
  10201     sCutsceneVars[1].point[2] = approach_f32(sCutsceneVars[1].point[2], 400.f, 5.f, 5.f);
  10202     sCutsceneVars[1].angle[1] += 0x40;
  10203     sCutsceneVars[3].point[1] += 2.f;
  10204     c->pos[1] += sCutsceneVars[3].point[1];
  10205 
  10206     if ((o = sMarioCamState->usedObj) != NULL) {
  10207         sCutsceneVars[0].point[1] = o->oPosY;
  10208         cannonAngle[0] = o->oMoveAnglePitch;
  10209         cannonAngle[1] = o->oMoveAngleYaw;
  10210         cannonAngle[2] = o->oMoveAngleRoll;
  10211         c->focus[0] = o->oPosX;
  10212         c->focus[1] = o->oPosY;
  10213         c->focus[2] = o->oPosZ;
  10214         cannonFocus[0] = 0.f;
  10215         cannonFocus[1] = 100.f;
  10216         cannonFocus[2] = 0.f;
  10217         offset_rotated(c->focus, c->focus, cannonFocus, cannonAngle);
  10218     }
  10219 
  10220     floorHeight = find_floor(c->pos[0], c->pos[1] + 500.f, c->pos[2], &floor) + 100.f;
  10221 
  10222     if (c->pos[1] < floorHeight) {
  10223         c->pos[1] = floorHeight;
  10224     }
  10225 }
  10226 
  10227 /**
  10228  * Start the cannon entering cutscene
  10229  */
  10230 BAD_RETURN(s32) cutscene_enter_cannon_start(struct Camera *c) {
  10231     UNUSED u8 filler[8]; // cvar3Start, cvar4Start?
  10232     struct Object *o;
  10233 
  10234     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
  10235     sMarioCamState->cameraEvent = 0;
  10236 
  10237     // Store the cannon's position and angle in cvar0
  10238     if ((o = sMarioCamState->usedObj) != NULL) {
  10239         sCutsceneVars[0].point[0] = o->oPosX;
  10240         sCutsceneVars[0].point[1] = o->oPosY;
  10241         sCutsceneVars[0].point[2] = o->oPosZ;
  10242         sCutsceneVars[0].angle[0] = o->oMoveAnglePitch;
  10243         sCutsceneVars[0].angle[1] = o->oMoveAngleYaw;
  10244         sCutsceneVars[0].angle[2] = o->oMoveAngleRoll;
  10245     }
  10246 
  10247     // Store the camera's polar offset from the cannon in cvar1
  10248     vec3f_get_dist_and_angle(sCutsceneVars[0].point, c->pos, &sCutsceneVars[1].point[2],
  10249                              &sCutsceneVars[1].angle[0], &sCutsceneVars[1].angle[1]);
  10250     sCutsceneVars[3].point[1] = 0.f;
  10251     //! cvar4 is unused in this cutscene
  10252     sCutsceneVars[4].point[1] = 0.f;
  10253 }
  10254 
  10255 /**
  10256  * Store the camera's pos and focus for the door cutscene
  10257  */
  10258 BAD_RETURN(s32) cutscene_door_start(struct Camera *c) {
  10259     vec3f_copy(sCutsceneVars[0].point, c->pos);
  10260     vec3f_copy(sCutsceneVars[1].point, c->focus);
  10261 }
  10262 
  10263 /**
  10264  * Fix the camera in place while the door opens.
  10265  */
  10266 BAD_RETURN(s32) cutscene_door_fix_cam(struct Camera *c) {
  10267     vec3f_copy(c->pos, sCutsceneVars[0].point);
  10268     vec3f_copy(c->focus, sCutsceneVars[1].point);
  10269 }
  10270 
  10271 /**
  10272  * Loop until Mario is no longer using the door.
  10273  */
  10274 BAD_RETURN(s32) cutscene_door_loop(struct Camera *c) {
  10275     //! bitwise AND instead of boolean
  10276     if ((sMarioCamState->action != ACT_PULLING_DOOR) & (sMarioCamState->action != ACT_PUSHING_DOOR)) {
  10277         gCutsceneTimer = CUTSCENE_STOP;
  10278         c->cutscene = 0;
  10279     }
  10280 }
  10281 
  10282 /**
  10283  * Warp the camera behind Mario.
  10284  */
  10285 BAD_RETURN(s32) cutscene_door_move_behind_mario(struct Camera *c) {
  10286     Vec3f camOffset;
  10287     s16 doorRotation;
  10288 
  10289     reset_pan_distance(c);
  10290     determine_pushing_or_pulling_door(&doorRotation);
  10291     set_focus_rel_mario(c, 0.f, 125.f, 0.f, 0);
  10292     vec3s_set(sCutsceneVars[0].angle, 0, sMarioCamState->faceAngle[1] + doorRotation, 0);
  10293     vec3f_set(camOffset, 0.f, 125.f, 250.f);
  10294 
  10295     if (doorRotation == 0) { //! useless code
  10296         camOffset[0] = 0.f;
  10297     } else {
  10298         camOffset[0] = 0.f;
  10299     }
  10300 
  10301     offset_rotated(c->pos, sMarioCamState->pos, camOffset, sCutsceneVars[0].angle);
  10302 }
  10303 
  10304 /**
  10305  * Follow Mario through the door.
  10306  */
  10307 BAD_RETURN(s32) cutscene_door_follow_mario(struct Camera *c) {
  10308     s16 pitch, yaw;
  10309     f32 dist;
  10310 
  10311     set_focus_rel_mario(c, 0.f, 125.f, 0.f, 0);
  10312     vec3f_get_dist_and_angle(c->focus, c->pos, &dist, &pitch, &yaw);
  10313     camera_approach_f32_symmetric_bool(&dist, 150.f, 7.f);
  10314     vec3f_set_dist_and_angle(c->focus, c->pos, dist, pitch, yaw);
  10315     update_camera_yaw(c);
  10316 }
  10317 
  10318 /**
  10319  * Ends the door cutscene. Sets the camera mode to close mode unless the default is free roam.
  10320  */
  10321 BAD_RETURN(s32) cutscene_door_end(struct Camera *c) {
  10322     if (c->defMode == CAMERA_MODE_FREE_ROAM) {
  10323         c->mode = CAMERA_MODE_FREE_ROAM;
  10324     } else {
  10325         c->mode = CAMERA_MODE_CLOSE;
  10326     }
  10327 
  10328     c->cutscene = 0;
  10329     gCutsceneTimer = CUTSCENE_STOP;
  10330     sStatusFlags |= CAM_FLAG_SMOOTH_MOVEMENT;
  10331     sStatusFlags &= ~CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
  10332     set_flag_post_door(c);
  10333     update_camera_yaw(c);
  10334 }
  10335 
  10336 /**
  10337  * Used for entering a room that uses a specific camera mode, like the castle lobby or BBH
  10338  */
  10339 BAD_RETURN(s32) cutscene_door_mode(struct Camera *c) {
  10340     UNUSED u8 filler[8];
  10341 
  10342     reset_pan_distance(c);
  10343     camera_course_processing(c);
  10344 
  10345     if (c->mode == CAMERA_MODE_FIXED) {
  10346         c->nextYaw = update_fixed_camera(c, c->focus, c->pos);
  10347     }
  10348     if (c->mode == CAMERA_MODE_PARALLEL_TRACKING) {
  10349         c->nextYaw = update_parallel_tracking_camera(c, c->focus, c->pos);
  10350     }
  10351 
  10352     c->yaw = c->nextYaw;
  10353 
  10354     // Loop until Mario is no longer using the door
  10355     if (sMarioCamState->action != ACT_ENTERING_STAR_DOOR &&
  10356         sMarioCamState->action != ACT_PULLING_DOOR &&
  10357         sMarioCamState->action != ACT_PUSHING_DOOR) {
  10358         gCutsceneTimer = CUTSCENE_STOP;
  10359         c->cutscene = 0;
  10360     }
  10361 }
  10362 
  10363 /******************************************************************************************************
  10364  * Cutscenes
  10365  ******************************************************************************************************/
  10366 
  10367 /**
  10368  * Cutscene that plays when Mario beats the game.
  10369  */
  10370 struct Cutscene sCutsceneEnding[] = {
  10371     { cutscene_ending_mario_fall, 170 },
  10372     { cutscene_ending_mario_land, 70 },
  10373 #ifdef VERSION_EU
  10374     { cutscene_ending_mario_land_closeup, 0x44 },
  10375     { cutscene_ending_stars_free_peach,  0x15c },
  10376     { cutscene_ending_peach_appears, 0x6d  },
  10377     { cutscene_ending_peach_descends, 0x212 },
  10378     { cutscene_ending_mario_to_peach, 0x69 },
  10379     { cutscene_ending_peach_wakeup, 0x1a4 },
  10380     { cutscene_ending_dialog, 0x114 },
  10381     { cutscene_ending_kiss, 0x10b },
  10382 #else
  10383     { cutscene_ending_mario_land_closeup, 75 },
  10384 #if defined(VERSION_SH) || defined(VERSION_CN)
  10385     { cutscene_ending_stars_free_peach, 431 },
  10386 #else
  10387     { cutscene_ending_stars_free_peach, 386 },
  10388 #endif
  10389     { cutscene_ending_peach_appears, 139 },
  10390     { cutscene_ending_peach_descends, 590 },
  10391     { cutscene_ending_mario_to_peach, 95 },
  10392 #if defined(VERSION_SH) || defined(VERSION_CN)
  10393     { cutscene_ending_peach_wakeup, 455 },
  10394     { cutscene_ending_dialog, 286 },
  10395 #else
  10396     { cutscene_ending_peach_wakeup, 425 },
  10397     { cutscene_ending_dialog, 236 },
  10398 #endif
  10399     { cutscene_ending_kiss, 245 },
  10400 #endif
  10401     { cutscene_ending_cake_for_mario, CUTSCENE_LOOP },
  10402     { cutscene_ending_stop, 0 }
  10403 };
  10404 
  10405 /**
  10406  * Cutscene that plays when Mario collects the grand star from bowser.
  10407  */
  10408 struct Cutscene sCutsceneGrandStar[] = {
  10409     { cutscene_grand_star, 360 },
  10410     { cutscene_grand_star_fly, CUTSCENE_LOOP }
  10411 };
  10412 
  10413 struct Cutscene sCutsceneUnused[] = {
  10414     { cutscene_unused_start, 1 },
  10415     { cutscene_unused_loop, CUTSCENE_LOOP }
  10416 };
  10417 
  10418 /**
  10419  * Cutscene that plays when Mario enters a door that warps to another area.
  10420  */
  10421 struct Cutscene sCutsceneDoorWarp[] = {
  10422     { cutscene_door_start, 1 },
  10423     { cutscene_door_loop, CUTSCENE_LOOP }
  10424 };
  10425 
  10426 /**
  10427  * Cutscene that plays after the credits, when Lakitu is flying away from the castle.
  10428  */
  10429 struct Cutscene sCutsceneEndWaving[] = {
  10430     { cutscene_end_waving, CUTSCENE_LOOP }
  10431 };
  10432 
  10433 /**
  10434  * The game's credits.
  10435  */
  10436 struct Cutscene sCutsceneCredits[] = {
  10437     { cutscene_credits, CUTSCENE_LOOP }
  10438 };
  10439 
  10440 /**
  10441  * Cutscene that plays when Mario pulls open a door.
  10442  */
  10443 struct Cutscene sCutsceneDoorPull[] = {
  10444     { cutscene_door_start, 1 },
  10445     { cutscene_door_fix_cam, 30 },
  10446     { cutscene_door_move_behind_mario, 1 },
  10447     { cutscene_door_follow_mario, 50 },
  10448     { cutscene_door_end, 0 }
  10449 };
  10450 
  10451 /**
  10452  * Cutscene that plays when Mario pushes open a door.
  10453  */
  10454 struct Cutscene sCutsceneDoorPush[] = {
  10455     { cutscene_door_start, 1 },
  10456     { cutscene_door_fix_cam, 20 },
  10457     { cutscene_door_move_behind_mario, 1 },
  10458     { cutscene_door_follow_mario, 50 },
  10459     { cutscene_door_end, 0 }
  10460 };
  10461 
  10462 /**
  10463  * Cutscene that plays when Mario pulls open a door that has some special mode requirement on the other
  10464  * side.
  10465  */
  10466 struct Cutscene sCutsceneDoorPullMode[] = {
  10467     { cutscene_door_start, 1 },
  10468     { cutscene_door_fix_cam, 30 },
  10469     { cutscene_door_mode, CUTSCENE_LOOP }
  10470 };
  10471 
  10472 /**
  10473  * Cutscene that plays when Mario pushes open a door that has some special mode requirement on the other
  10474  * side.
  10475  */
  10476 struct Cutscene sCutsceneDoorPushMode[] = {
  10477     { cutscene_door_start, 1 },
  10478     { cutscene_door_fix_cam, 20 },
  10479     { cutscene_door_mode, CUTSCENE_LOOP }
  10480 };
  10481 
  10482 /**
  10483  * Cutscene that plays when Mario enters the cannon and it rises out of the hole.
  10484  */
  10485 struct Cutscene sCutsceneEnterCannon[] = {
  10486     { cutscene_enter_cannon_start, 1 },
  10487     { cutscene_enter_cannon_raise, 121 },
  10488     { cutscene_enter_cannon_end, 0 }
  10489 };
  10490 
  10491 /**
  10492  * Cutscene that plays when a star spawns from ie a box or after a boss fight.
  10493  */
  10494 struct Cutscene sCutsceneStarSpawn[] = {
  10495     { cutscene_star_spawn, CUTSCENE_LOOP },
  10496     { cutscene_star_spawn_back, 15 },
  10497     { cutscene_star_spawn_end, 0 }
  10498 };
  10499 
  10500 /**
  10501  * Cutscene for the red coin star spawning. Compared to a regular star, this cutscene can warp long
  10502  * distances.
  10503  */
  10504 struct Cutscene sCutsceneRedCoinStarSpawn[] = {
  10505     { cutscene_red_coin_star, CUTSCENE_LOOP },
  10506     { cutscene_red_coin_star_end, 0 }
  10507 };
  10508 
  10509 /**
  10510  * Cutscene that plays when Mario enters a course painting.
  10511  */
  10512 struct Cutscene sCutsceneEnterPainting[] = {
  10513     { cutscene_enter_painting, CUTSCENE_LOOP }
  10514 };
  10515 
  10516 /**
  10517  * Cutscene that plays when Mario dies and warps back to the castle.
  10518  */
  10519 struct Cutscene sCutsceneDeathExit[] = {
  10520     { cutscene_exit_painting, 118 },
  10521     { cutscene_exit_painting_end, 0 }
  10522 };
  10523 
  10524 /**
  10525  * Cutscene that plays when Mario warps to the castle after collecting a star.
  10526  */
  10527 struct Cutscene sCutsceneExitPaintingSuccess[] = {
  10528     { cutscene_exit_painting, 180 },
  10529     { cutscene_exit_painting_end, 0 }
  10530 };
  10531 
  10532 struct Cutscene sCutsceneUnusedExit[] = {
  10533     { cutscene_unused_exit_start, 1 },
  10534     { cutscene_unused_exit_focus_mario, 60 },
  10535     { cutscene_exit_painting_end, 0 }
  10536 };
  10537 
  10538 /**
  10539  * The intro of the game. Peach reads her letter and Lakitu flies down to Mario's warp pipe.
  10540  */
  10541 struct Cutscene sCutsceneIntroPeach[] = {
  10542     { cutscene_intro_peach_letter, CUTSCENE_LOOP },
  10543     { cutscene_intro_peach_reset_fov, 35 },
  10544 #ifdef VERSION_EU
  10545     { cutscene_intro_peach_fly_to_pipe, 675 },
  10546 #else
  10547     { cutscene_intro_peach_fly_to_pipe, 820 },
  10548 #endif
  10549     { cutscene_intro_peach_mario_appears, 270 },
  10550     { cutscene_intro_peach_dialog, CUTSCENE_LOOP }
  10551 };
  10552 
  10553 /**
  10554  * Cutscene that plays when a cannon door is opened.
  10555  */
  10556 struct Cutscene sCutscenePrepareCannon[] = {
  10557     { cutscene_prepare_cannon, 170 },
  10558     { cutscene_prepare_cannon_end, 0 }
  10559 };
  10560 
  10561 /**
  10562  * Cutscene that plays when Mario enters the castle grounds after leaving CotMC through the waterfall.
  10563  */
  10564 struct Cutscene sCutsceneExitWaterfall[] = {
  10565     { cutscene_exit_waterfall, 52 },
  10566     { cutscene_exit_to_castle_grounds_end, 0 }
  10567 };
  10568 
  10569 /**
  10570  * Cutscene that plays when Mario falls from WMotR.
  10571  */
  10572 struct Cutscene sCutsceneFallToCastleGrounds[] = {
  10573     { cutscene_exit_fall_to_castle_grounds, 73 },
  10574     { cutscene_exit_to_castle_grounds_end, 0 }
  10575 };
  10576 
  10577 /**
  10578  * Cutscene that plays when Mario enters the pyramid through the hole at the top.
  10579  */
  10580 struct Cutscene sCutsceneEnterPyramidTop[] = {
  10581     { cutscene_enter_pyramid_top, 90 },
  10582     { cutscene_exit_to_castle_grounds_end, 0 }
  10583 };
  10584 
  10585 /**
  10586  * Unused cutscene for when the pyramid explodes.
  10587  */
  10588 struct Cutscene sCutscenePyramidTopExplode[] = {
  10589     { cutscene_mario_dialog, CUTSCENE_LOOP },
  10590     { cutscene_pyramid_top_explode, 150 },
  10591     { cutscene_pyramid_top_explode_end, 0 }
  10592 };
  10593 
  10594 /**
  10595  * Cutscene that plays when Mario dies while standing, or from electrocution.
  10596  */
  10597 struct Cutscene sCutsceneStandingDeath[] = {
  10598     { cutscene_death_standing, CUTSCENE_LOOP }
  10599 };
  10600 
  10601 /**
  10602  * Cutscene that plays when Mario enters HMC or CotMC.
  10603  */
  10604 struct Cutscene sCutsceneEnterPool[] = {
  10605     { cutscene_enter_pool, 100 },
  10606     { cutscene_exit_to_castle_grounds_end, 0 }
  10607 };
  10608 
  10609 /**
  10610  * Cutscene that plays when Mario dies on his stomach.
  10611  */
  10612 struct Cutscene sCutsceneDeathStomach[] = {
  10613     { cutscene_death_stomach, CUTSCENE_LOOP }
  10614 };
  10615 
  10616 /**
  10617  * Cutscene that plays when Mario dies on his back.
  10618  */
  10619 struct Cutscene sCutsceneDeathOnBack[] = {
  10620     { cutscene_bbh_death, CUTSCENE_LOOP }
  10621 };
  10622 
  10623 /**
  10624  * Cutscene that plays when Mario dies in quicksand.
  10625  */
  10626 struct Cutscene sCutsceneQuicksandDeath[] = {
  10627     { cutscene_quicksand_death, CUTSCENE_LOOP },
  10628 };
  10629 
  10630 /**
  10631  * Unused cutscene for ACT_WATER_DEATH, which happens when Mario gets hit by an enemy under water.
  10632  */
  10633 struct Cutscene sCutsceneWaterDeath[] = {
  10634     { cutscene_quicksand_death, CUTSCENE_LOOP }
  10635 };
  10636 
  10637 /**
  10638  * Cutscene that plays when Mario suffocates.
  10639  */
  10640 struct Cutscene sCutsceneSuffocation[] = {
  10641     { cutscene_suffocation, CUTSCENE_LOOP }
  10642 };
  10643 
  10644 /**
  10645  * Cutscene that plays when entering bowser's arenas.
  10646  */
  10647 struct Cutscene sCutsceneEnterBowserArena[] = {
  10648     { cutscene_bowser_arena, 180 },
  10649     { cutscene_bowser_arena_dialog, CUTSCENE_LOOP },
  10650     { cutscene_bowser_arena_end, 0 }
  10651 };
  10652 
  10653 // The dance cutscenes are automatically stopped since reset_camera() is called after Mario warps.
  10654 
  10655 /**
  10656  * Star dance cutscene.
  10657  * For the default dance, the camera moves closer to Mario, then stays in place.
  10658  * For the rotate dance, the camera moves closer and rotates clockwise around Mario.
  10659  */
  10660 struct Cutscene sCutsceneDanceDefaultRotate[] = {
  10661     { cutscene_dance_default_rotate, CUTSCENE_LOOP }
  10662 };
  10663 
  10664 /**
  10665  * Star dance cutscene.
  10666  * The camera moves closer and rotates clockwise around Mario.
  10667  */
  10668 struct Cutscene sCutsceneDanceFlyAway[] = {
  10669     { cutscene_dance_fly_away, CUTSCENE_LOOP }
  10670 };
  10671 
  10672 /**
  10673  * Star dance cutscene.
  10674  * The camera moves in for a closeup on Mario. Used in tight spaces and underwater.
  10675  */
  10676 struct Cutscene sCutsceneDanceCloseup[] = {
  10677     { cutscene_dance_closeup, CUTSCENE_LOOP }
  10678 };
  10679 
  10680 /**
  10681  * Star dance cutscene.
  10682  * The camera moves closer and rotates clockwise around Mario.
  10683  */
  10684 struct Cutscene sCutsceneKeyDance[] = {
  10685     { cutscene_key_dance, CUTSCENE_LOOP }
  10686 };
  10687 
  10688 /**
  10689  * Cutscene that plays when Mario presses a cap switch.
  10690  */
  10691 struct Cutscene sCutsceneCapSwitchPress[] = {
  10692     { cutscene_cap_switch_press, CUTSCENE_LOOP }
  10693 };
  10694 
  10695 /**
  10696  * Cutscene that plays when Mario opens a sliding star door.
  10697  */
  10698 struct Cutscene sCutsceneSlidingDoorsOpen[] = {
  10699     { cutscene_sliding_doors_open, 50 },
  10700     { cutscene_double_doors_end, 0 }
  10701 };
  10702 
  10703 /**
  10704  * Cutscene that plays when Mario unlocks the basement or upstairs key door.
  10705  */
  10706 struct Cutscene sCutsceneUnlockKeyDoor[] = {
  10707     { cutscene_unlock_key_door, 200 },
  10708     { cutscene_double_doors_end, 0 }
  10709 };
  10710 
  10711 /**
  10712  * Cutscene that plays when Mario exits bowser's arena after getting the key.
  10713  */
  10714 struct Cutscene sCutsceneExitBowserSuccess[] = {
  10715     { cutscene_exit_bowser_succ, 190 },
  10716     { cutscene_non_painting_end, 0 }
  10717 };
  10718 
  10719 /**
  10720  * Unused cutscene for when Mario dies in bowser's arena. Instead, Mario just respawns at the warp pipe.
  10721  */
  10722 struct Cutscene sCutsceneExitBowserDeath[] = {
  10723     { cutscene_exit_bowser_death, 120 },
  10724     { cutscene_non_painting_end, 0 }
  10725 };
  10726 
  10727 /**
  10728  * Cutscene that plays when Mario exits a non-painting course, like HMC.
  10729  */
  10730 struct Cutscene sCutsceneExitSpecialSuccess[] = {
  10731     { cutscene_exit_non_painting_succ, 163 },
  10732     { cutscene_non_painting_end, 0 }
  10733 };
  10734 
  10735 /**
  10736  * Cutscene that plays when Mario exits from dying in a non-painting course, like HMC.
  10737  */
  10738 struct Cutscene sCutsceneNonPaintingDeath[] = {
  10739     { cutscene_non_painting_death, 120 },
  10740     { cutscene_non_painting_end, 0 }
  10741 };
  10742 
  10743 /**
  10744  * Cutscene that plays when Mario talks to a creature.
  10745  */
  10746 struct Cutscene sCutsceneDialog[] = {
  10747     { cutscene_dialog, CUTSCENE_LOOP },
  10748     { cutscene_dialog_set_flag, 12 },
  10749     { cutscene_dialog_end, 0 }
  10750 };
  10751 
  10752 /**
  10753  * Cutscene that plays when Mario reads a sign or message.
  10754  */
  10755 struct Cutscene sCutsceneReadMessage[] = {
  10756     { cutscene_read_message, CUTSCENE_LOOP },
  10757     { cutscene_read_message_set_flag, 15 },
  10758     { cutscene_read_message_end, 0 }
  10759 };
  10760 
  10761 /* TODO:
  10762  * The next two arrays are both related to levels, and they look generated.
  10763  * These should be split into their own file.
  10764  */
  10765 
  10766 /**
  10767  * Converts the u32 given in DEFINE_COURSE to a u8 with the odd and even digits rotated into the right
  10768  * order for sDanceCutsceneIndexTable
  10769  */
  10770 #define DROT(value, index) ((value >> (32 - (index + 1) * 8)) & 0xF0) >> 4 | \
  10771                            ((value >> (32 - (index + 1) * 8)) & 0x0F) << 4
  10772 
  10773 #define DANCE_ENTRY(c) { DROT(c, 0), DROT(c, 1), DROT(c, 2), DROT(c, 3) },
  10774 
  10775 #define DEFINE_COURSE(_0, cutscenes) DANCE_ENTRY(cutscenes)
  10776 #define DEFINE_COURSES_END()
  10777 #define DEFINE_BONUS_COURSE(_0, cutscenes) DANCE_ENTRY(cutscenes)
  10778 
  10779 /**
  10780  * Each hex digit is an index into sDanceCutsceneTable.
  10781  *
  10782  * 0: Lakitu flies away after the dance
  10783  * 1: Only rotates the camera, doesn't zoom out
  10784  * 2: The camera goes to a close up of Mario
  10785  * 3: Bowser keys and the grand star
  10786  * 4: Default, used for 100 coin stars, 8 red coin stars in bowser levels, and secret stars
  10787  */
  10788 u8 sDanceCutsceneIndexTable[][4] = {
  10789     #include "levels/course_defines.h"
  10790     { 0x44, 0x44, 0x44, 0x04 }, // (26) Why go to all this trouble to save bytes and do this?!
  10791 };
  10792 #undef DEFINE_COURSE
  10793 #undef DEFINE_COURSES_END
  10794 #undef DEFINE_BONUS_COURSE
  10795 
  10796 #undef DANCE_ENTRY
  10797 #undef DROT
  10798 
  10799 /**
  10800  * These masks set whether or not the camera zooms out when game is paused.
  10801  *
  10802  * Each entry is used by two levels. Even levels use the low 4 bits, odd levels use the high 4 bits
  10803  * Because areas are 1-indexed, a mask of 0x1 will make area 1 (not area 0) zoom out.
  10804  *
  10805  * In zoom_out_if_paused_and_outside(), the current area is converted to a shift.
  10806  * Then the value of (1 << shift) is &'d with the level's mask,
  10807  * and if the result is non-zero, the camera will zoom out.
  10808  */
  10809 u8 sZoomOutAreaMasks[] = {
  10810     ZOOMOUT_AREA_MASK(0,0,0,0, 0,0,0,0), // Unused         | Unused
  10811     ZOOMOUT_AREA_MASK(0,0,0,0, 0,0,0,0), // Unused         | Unused
  10812     ZOOMOUT_AREA_MASK(0,0,0,0, 1,0,0,0), // BBH            | CCM
  10813     ZOOMOUT_AREA_MASK(0,0,0,0, 0,0,0,0), // CASTLE_INSIDE  | HMC
  10814     ZOOMOUT_AREA_MASK(1,0,0,0, 1,0,0,0), // SSL            | BOB
  10815     ZOOMOUT_AREA_MASK(1,0,0,0, 1,0,0,0), // SL             | WDW
  10816     ZOOMOUT_AREA_MASK(0,0,0,0, 1,1,0,0), // JRB            | THI
  10817     ZOOMOUT_AREA_MASK(0,0,0,0, 1,0,0,0), // TTC            | RR
  10818     ZOOMOUT_AREA_MASK(1,0,0,0, 1,0,0,0), // CASTLE_GROUNDS | BITDW
  10819     ZOOMOUT_AREA_MASK(0,0,0,0, 1,0,0,0), // VCUTM          | BITFS
  10820     ZOOMOUT_AREA_MASK(0,0,0,0, 1,0,0,0), // SA             | BITS
  10821     ZOOMOUT_AREA_MASK(1,0,0,0, 0,0,0,0), // LLL            | DDD
  10822     ZOOMOUT_AREA_MASK(1,0,0,0, 0,0,0,0), // WF             | ENDING
  10823     ZOOMOUT_AREA_MASK(0,0,0,0, 0,0,0,0), // COURTYARD      | PSS
  10824     ZOOMOUT_AREA_MASK(0,0,0,0, 1,0,0,0), // COTMC          | TOTWC
  10825     ZOOMOUT_AREA_MASK(1,0,0,0, 1,0,0,0), // BOWSER_1       | WMOTR
  10826     ZOOMOUT_AREA_MASK(0,0,0,0, 1,0,0,0), // Unused         | BOWSER_2
  10827     ZOOMOUT_AREA_MASK(1,0,0,0, 0,0,0,0), // BOWSER_3       | Unused
  10828     ZOOMOUT_AREA_MASK(1,0,0,0, 0,0,0,0), // TTM            | Unused
  10829     ZOOMOUT_AREA_MASK(0,0,0,0, 0,0,0,0), // Unused         | Unused
  10830 };
  10831 
  10832 STATIC_ASSERT(ARRAY_COUNT(sZoomOutAreaMasks) - 1 == LEVEL_MAX / 2, "Make sure you edit sZoomOutAreaMasks when adding / removing courses.");
  10833 
  10834 /*
  10835  * credits spline paths.
  10836  * TODO: Separate these into their own file(s)
  10837  */
  10838 
  10839 struct CutsceneSplinePoint sBoBCreditsSplinePositions[] = {
  10840     { 1, 0, { 5984, 3255, 4975 } },
  10841     { 2, 0, { 4423, 3315, 1888 } },
  10842     { 3, 0, { 776, 2740, -1825 } },
  10843     { 4, 0, { -146, 3894, -3167 } },
  10844     { -1, 0, { 741, 4387, -5474 } }
  10845 };
  10846 
  10847 struct CutsceneSplinePoint sBoBCreditsSplineFocus[] = {
  10848     { 0, 30, { 5817, 3306, 4507 } },
  10849     { 0, 40, { 4025, 3378, 1593 } },
  10850     { 0, 50, { 1088, 2652, -2205 } },
  10851     { 0, 60, { 205, 3959, -3517 } },
  10852     { -1, 60, { 1231, 4400, -5649 } }
  10853 };
  10854 
  10855 struct CutsceneSplinePoint sWFCreditsSplinePositions[] = {
  10856     { 0, 0, { -301, 1399, 2643 } },
  10857     { 0, 0, { -182, 2374, 4572 } },
  10858     { 0, 0, { 4696, 3864, 413 } },
  10859     { 0, 0, { 1738, 4891, -1516 } },
  10860     { -1, 0, { 1783, 4891, -1516 } }
  10861 };
  10862 
  10863 struct CutsceneSplinePoint sWFCreditsSplineFocus[] = {
  10864     { 1, 30, { -249, 1484, 2153 } },
  10865     { 2, 40, { -200, 2470, 4082 } },
  10866     { 3, 40, { 4200, 3916, 370 } },
  10867     { 4, 40, { 1523, 4976, -1072 } },
  10868     { -1, 40, { 1523, 4976, -1072 } }
  10869 };
  10870 
  10871 struct CutsceneSplinePoint sJRBCreditsSplinePositions[] = {
  10872     { 0, 0, { 5538, -4272, 2376 } },
  10873     { 0, 0, { 5997, -3303, 2261 } },
  10874     { 0, 0, { 6345, -3255, 2179 } },
  10875     { 0, 0, { 6345, -3255, 2179 } },
  10876     { -1, 0, { 6694, -3203, 2116 } }
  10877 };
  10878 
  10879 struct CutsceneSplinePoint sJRBCreditsSplineFocus[] = {
  10880     { 0, 50, { 5261, -4683, 2443 } },
  10881     { 0, 50, { 5726, -3675, 2456 } },
  10882     { 0, 50, { 6268, -2817, 2409 } },
  10883     { 0, 50, { 6596, -2866, 2369 } },
  10884     { -1, 50, { 7186, -3153, 2041 } }
  10885 };
  10886 
  10887 struct CutsceneSplinePoint sCCMSlideCreditsSplinePositions[] = {
  10888     { 0, 0, { -6324, 6745, -5626 } },
  10889     { 1, 0, { -6324, 6745, -5626 } },
  10890     { 2, 0, { -6108, 6762, -5770 } },
  10891     { 3, 0, { -5771, 6787, -5962 } },
  10892     { -1, 0, { -5672, 6790, -5979 } }
  10893 };
  10894 
  10895 struct CutsceneSplinePoint sCCMSlideCreditsSplineFocus[] = {
  10896     { 0, 50, { -5911, 6758, -5908 } },
  10897     { 1, 50, { -5911, 6758, -5908 } },
  10898     { 2, 50, { -5652, 6814, -5968 } },
  10899     { 3, 50, { -5277, 6801, -6043 } },
  10900     { -1, 50, { -5179, 6804, -6060 } }
  10901 };
  10902 
  10903 struct CutsceneSplinePoint sBBHCreditsSplinePositions[] = {
  10904     { 1, 0, { 1088, 341, 2447 } },
  10905     { 2, 0, { 1338, 610, 2808 } },
  10906     { 3, 0, { 2267, 1612, 2966 } },
  10907     { -1, 0, { 2296, 1913, 2990 } }
  10908 };
  10909 
  10910 struct CutsceneSplinePoint sBBHCreditsSplineFocus[] = {
  10911     { 1, 50, { 1160, 263, 1958 } },
  10912     { 2, 50, { 1034, 472, 2436 } },
  10913     { 3, 50, { 1915, 1833, 2688 } },
  10914     { -1, 50, { 2134, 2316, 2742 } }
  10915 };
  10916 
  10917 struct CutsceneSplinePoint sHMCCreditsSplinePositions[] = {
  10918     { 1, 0, { -5952, 1807, -5882 } },
  10919     { 2, 0, { -5623, 1749, -4863 } },
  10920     { 3, 0, { -5472, 1955, -2520 } },
  10921     { 4, 0, { -5544, 1187, -1085 } },
  10922     { -1, 0, { -5547, 391, -721 } }
  10923 };
  10924 
  10925 struct CutsceneSplinePoint sHMCCreditsSplineFocus[] = {
  10926     { 1, 210, { -5952, 1884, -6376 } },
  10927     { 2, 58, { -5891, 1711, -5283 } },
  10928     { 3, 30, { -5595, 1699, -2108 } },
  10929     { 4, 31, { -5546, 794, -777 } },
  10930     { -1, 31, { -5548, -85, -572 } }
  10931 };
  10932 
  10933 struct CutsceneSplinePoint sTHIWigglerCreditsSplinePositions[] = {
  10934     { 1, 0, { -1411, 2474, -1276 } },
  10935     { 2, 0, { -1606, 2479, -434 } },
  10936     { -1, 0, { -1170, 2122, 1337 } }
  10937 };
  10938 
  10939 struct CutsceneSplinePoint sTHIWigglerCreditsSplineFocus[] = {
  10940     { 1, 50, { -1053, 2512, -928 } },
  10941     { 2, 50, { -1234, 2377, -114 } },
  10942     { -1, 50, { -758, 2147, 1054 } }
  10943 };
  10944 
  10945 struct CutsceneSplinePoint sVolcanoCreditsSplinePositions[] = {
  10946     { 0, 0, { -1445, 1094, 1617 } },
  10947     { 0, 0, { -1509, 649, 871 } },
  10948     { 0, 0, { -1133, 420, -248 } },
  10949     { 0, 0, { -778, 359, -1052 } },
  10950     { 0, 0, { -565, 260, -1730 } },
  10951     { -1, 0, { 1274, 473, -275 } }
  10952 };
  10953 
  10954 struct CutsceneSplinePoint sVolcanoCreditsSplineFocus[] = {
  10955     { 0, 50, { -1500, 757, 1251 } },
  10956     { 0, 50, { -1401, 439, 431 } },
  10957     { 0, 50, { -749, 270, -532 } },
  10958     { 0, 50, { -396, 270, -1363 } },
  10959     { 0, 50, { -321, 143, -2151 } },
  10960     { -1, 50, { 1002, 460, -694 } }
  10961 };
  10962 
  10963 struct CutsceneSplinePoint sSSLCreditsSplinePositions[] = {
  10964     { 0, 0, { -4262, 4658, -5015 } },
  10965     { 0, 0, { -3274, 2963, -4661 } },
  10966     { 0, 0, { -2568, 812, -6528 } },
  10967     { 0, 0, { -414, 660, -7232 } },
  10968     { 0, 0, { 1466, 660, -6898 } },
  10969     { -1, 0, { 2724, 660, -6298 } }
  10970 };
  10971 
  10972 struct CutsceneSplinePoint sSSLCreditsSplineFocus[] = {
  10973     { 0, 50, { -4083, 4277, -4745 } },
  10974     { 0, 50, { -2975, 2574, -4759 } },
  10975     { 0, 50, { -2343, 736, -6088 } },
  10976     { 0, 50, { -535, 572, -6755 } },
  10977     { 0, 50, { 1311, 597, -6427 } },
  10978     { -1, 50, { 2448, 612, -5884 } }
  10979 };
  10980 
  10981 struct CutsceneSplinePoint sDDDCreditsSplinePositions[] = {
  10982     { 0, 0, { -874, -4933, 366 } },
  10983     { 0, 0, { -1463, -4782, 963 } },
  10984     { 0, 0, { -1893, -4684, 1303 } },
  10985     { 0, 0, { -2818, -4503, 1583 } },
  10986     { 0, 0, { -4095, -2924, 730 } },
  10987     { 0, 0, { -4737, -1594, -63 } },
  10988     { -1, 0, { -4681, -1084, -623 } }
  10989 };
  10990 
  10991 struct CutsceneSplinePoint sDDDCreditsSplineFocus[] = {
  10992     { 0, 50, { -1276, -4683, 622 } },
  10993     { 0, 50, { -1858, -4407, 1097 } },
  10994     { 0, 50, { -2324, -4332, 1318 } },
  10995     { 0, 50, { -3138, -4048, 1434 } },
  10996     { 0, 50, { -4353, -2444, 533 } },
  10997     { 0, 50, { -4807, -1169, -436 } },
  10998     { -1, 50, { -4665, -664, -1007 } }
  10999 };
  11000 
  11001 struct CutsceneSplinePoint sSLCreditsSplinePositions[] = {
  11002     { 0, 0, { 939, 6654, 6196 } },
  11003     { 0, 0, { 1873, 5160, 3714 } },
  11004     { 0, 0, { 3120, 3564, 1314 } },
  11005     { -1, 0, { 2881, 4231, 573 } }
  11006 };
  11007 
  11008 struct CutsceneSplinePoint sSLCreditsSplineFocus[] = {
  11009     { 0, 50, { 875, 6411, 5763 } },
  11010     { 0, 50, { 1659, 4951, 3313 } },
  11011     { 0, 50, { 2630, 3565, 1215 } },
  11012     { -1, 50, { 2417, 4056, 639 } }
  11013 };
  11014 
  11015 struct CutsceneSplinePoint sWDWCreditsSplinePositions[] = {
  11016     { 0, 0, { 3927, 2573, 3685 } },
  11017     { 0, 0, { 2389, 2054, 1210 } },
  11018     { 0, 0, { 2309, 2069, 22 } },
  11019     { -1, 0, { 2122, 2271, -979 } }
  11020 };
  11021 
  11022 struct CutsceneSplinePoint sWDWCreditsSplineFocus[] = {
  11023     { 0, 50, { 3637, 2460, 3294 } },
  11024     { 0, 50, { 1984, 2067, 918 } },
  11025     { 0, 50, { 1941, 2255, -261 } },
  11026     { -1, 50, { 1779, 2587, -1158 } }
  11027 };
  11028 
  11029 struct CutsceneSplinePoint sTTMCreditsSplinePositions[] = {
  11030     { 0, 0, { 386, 2535, 644 } },
  11031     { 0, 0, { 1105, 2576, 918 } },
  11032     { 0, 0, { 3565, 2261, 2098 } },
  11033     { 0, 0, { 6715, -2791, 4554 } },
  11034     { 0, 0, { 3917, -3130, 3656 } },
  11035     { -1, 0, { 3917, -3130, 3656 } }
  11036 };
  11037 
  11038 struct CutsceneSplinePoint sTTMCreditsSplineFocus[] = {
  11039     { 1, 50, { 751, 2434, 318 } },
  11040     { 2, 50, { 768, 2382, 603 } },
  11041     { 3, 60, { 3115, 2086, 1969 } },
  11042     { 4, 30, { 6370, -3108, 4727 } },
  11043     { 5, 50, { 4172, -3385, 4001 } },
  11044     { -1, 50, { 4172, -3385, 4001 } }
  11045 };
  11046 
  11047 struct CutsceneSplinePoint sTHIHugeCreditsSplinePositions[] = {
  11048     { 0, 0, { 6990, -1000, -4858 } },
  11049     { 0, 0, { 7886, -1055, 2878 } },
  11050     { 0, 0, { 1952, -1481, 10920 } },
  11051     { 0, 0, { -1684, -219, 2819 } },
  11052     { 0, 0, { -2427, -131, 2755 } },
  11053     { 0, 0, { -3246, 416, 3286 } },
  11054     { -1, 0, { -3246, 416, 3286 } }
  11055 };
  11056 
  11057 struct CutsceneSplinePoint sTHIHugeCreditsSplineFocus[] = {
  11058     { 1, 70, { 7022, -965, -5356 } },
  11059     { 2, 40, { 7799, -915, 2405 } },
  11060     { 3, 60, { 1878, -1137, 10568 } },
  11061     { 4, 50, { -1931, -308, 2394 } },
  11062     { 5, 50, { -2066, -386, 2521 } },
  11063     { 6, 50, { -2875, 182, 3045 } },
  11064     { -1, 50, { -2875, 182, 3045 } }
  11065 };
  11066 
  11067 struct CutsceneSplinePoint sTTCCreditsSplinePositions[] = {
  11068     { 1, 0, { -1724, 277, -994 } },
  11069     { 2, 0, { -1720, 456, -995 } },
  11070     { 3, 0, { -1655, 810, -1014 } },
  11071     { -1, 0, { -1753, 883, -1009 } }
  11072 };
  11073 
  11074 struct CutsceneSplinePoint sTTCCreditsSplineFocus[] = {
  11075     { 1, 50, { -1554, 742, -1063 } },
  11076     { 2, 50, { -1245, 571, -1102 } },
  11077     { 3, 50, { -1220, 603, -1151 } },
  11078     { -1, 50, { -1412, 520, -1053 } }
  11079 };
  11080 
  11081 struct CutsceneSplinePoint sRRCreditsSplinePositions[] = {
  11082     { 0, 0, { -1818, 4036, 97 } },
  11083     { 0, 0, { -575, 3460, -505 } },
  11084     { 0, 0, { 1191, 3611, -1134 } },
  11085     { -1, 0, { 2701, 3777, -3686 } }
  11086 };
  11087 
  11088 struct CutsceneSplinePoint sRRCreditsSplineFocus[] = {
  11089     { 0, 50, { -1376, 3885, -81 } },
  11090     { 0, 50, { -146, 3343, -734 } },
  11091     { 0, 50, { 1570, 3446, -1415 } },
  11092     { -1, 50, { 2794, 3627, -3218 } }
  11093 };
  11094 
  11095 struct CutsceneSplinePoint sSACreditsSplinePositions[] = {
  11096     { 0, 0, { -295, -396, -585 } },
  11097     { 1, 0, { -295, -396, -585 } },
  11098     { 2, 0, { -292, -856, -573 } },
  11099     { 3, 0, { -312, -856, -541 } },
  11100     { -1, 0, { 175, -856, -654 } }
  11101 };
  11102 
  11103 struct CutsceneSplinePoint sSACreditsSplineFocus[] = {
  11104     { 0, 50, { -175, -594, -142 } },
  11105     { 1, 50, { -175, -594, -142 } },
  11106     { 2, 50, { -195, -956, -92 } },
  11107     { 3, 50, { -572, -956, -150 } },
  11108     { -1, 50, { -307, -956, -537 } }
  11109 };
  11110 
  11111 struct CutsceneSplinePoint sCotMCCreditsSplinePositions[] = {
  11112     { 0, 0, { -296, 495, 1607 } },
  11113     { 0, 0, { -430, 541, 654 } },
  11114     { 0, 0, { -466, 601, -359 } },
  11115     { 0, 0, { -217, 433, -1549 } },
  11116     { -1, 0, { -95, 366, -2922 } }
  11117 };
  11118 
  11119 struct CutsceneSplinePoint sCotMCCreditsSplineFocus[] = {
  11120     { 0, 50, { -176, 483, 2092 } },
  11121     { 0, 50, { -122, 392, 1019 } },
  11122     { 0, 50, { -268, 450, -792 } },
  11123     { 0, 50, { -172, 399, -2046 } },
  11124     { -1, 50, { -51, 355, -3420 } }
  11125 };
  11126 
  11127 struct CutsceneSplinePoint sDDDSubCreditsSplinePositions[] = {
  11128     { 0, 0, { 4656, 2171, 5028 } },
  11129     { 0, 0, { 4548, 1182, 4596 } },
  11130     { 0, 0, { 5007, 813, 3257 } },
  11131     { 0, 0, { 5681, 648, 1060 } },
  11132     { -1, 0, { 4644, 774, 113 } }
  11133 };
  11134 
  11135 struct CutsceneSplinePoint sDDDSubCreditsSplineFocus[] = {
  11136     { 0, 50, { 4512, 2183, 4549 } },
  11137     { 0, 50, { 4327, 838, 4308 } },
  11138     { 0, 50, { 4774, 749, 2819 } },
  11139     { 0, 50, { 5279, 660, 763 } },
  11140     { -1, 50, { 4194, 885, -75 } }
  11141 };
  11142 
  11143 struct CutsceneSplinePoint sCCMOutsideCreditsSplinePositions[] = {
  11144     { 1, 0, { 1427, -1387, 5409 } },
  11145     { 2, 0, { -1646, -1536, 4526 } },
  11146     { 3, 0, { -3852, -1448, 3913 } },
  11147     { -1, 0, { -5199, -1366, 1886 } }
  11148 };
  11149 
  11150 struct CutsceneSplinePoint sCCMOutsideCreditsSplineFocus[] = {
  11151     { 1, 50, { 958, -1481, 5262 } },
  11152     { 2, 50, { -2123, -1600, 4391 } },
  11153     { 3, 50, { -3957, -1401, 3426 } },
  11154     { -1, 50, { -4730, -1215, 1795 } }
  11155 };
  11156 
  11157 /**
  11158  * Play the current cutscene until either gCutsceneTimer reaches the max time, or c->cutscene is set to 0
  11159  *
  11160  * Note that CAM_FLAG_SMOOTH_MOVEMENT is cleared while a cutscene is playing, so cutscenes set it for
  11161  * the duration they want the flag to be active.
  11162  */
  11163 void play_cutscene(struct Camera *c) {
  11164     UNUSED u8 filler[12];
  11165     UNUSED s16 unusedYawFocToMario;
  11166     s16 cutsceneDuration;
  11167     u8 oldCutscene;
  11168 
  11169     unusedYawFocToMario = sAreaYaw;
  11170     oldCutscene = c->cutscene;
  11171     sStatusFlags &= ~CAM_FLAG_SMOOTH_MOVEMENT;
  11172     gCameraMovementFlags &= ~CAM_MOVING_INTO_MODE;
  11173 
  11174 #define CUTSCENE(id, cutscene)                                                                            \
  11175     case id:                                                                                              \
  11176         cutsceneDuration = cutscene[sCutsceneShot].duration;                                              \
  11177         cutscene[sCutsceneShot].shot(c);                                                                  \
  11178         break;
  11179 
  11180     switch (c->cutscene) {
  11181         CUTSCENE(CUTSCENE_STAR_SPAWN, sCutsceneStarSpawn)
  11182         CUTSCENE(CUTSCENE_RED_COIN_STAR_SPAWN, sCutsceneRedCoinStarSpawn)
  11183         CUTSCENE(CUTSCENE_ENDING, sCutsceneEnding)
  11184         CUTSCENE(CUTSCENE_GRAND_STAR, sCutsceneGrandStar)
  11185         CUTSCENE(CUTSCENE_DOOR_WARP, sCutsceneDoorWarp)
  11186         CUTSCENE(CUTSCENE_DOOR_PULL, sCutsceneDoorPull)
  11187         CUTSCENE(CUTSCENE_DOOR_PUSH, sCutsceneDoorPush)
  11188         CUTSCENE(CUTSCENE_DOOR_PULL_MODE, sCutsceneDoorPullMode)
  11189         CUTSCENE(CUTSCENE_DOOR_PUSH_MODE, sCutsceneDoorPushMode)
  11190         CUTSCENE(CUTSCENE_ENTER_CANNON, sCutsceneEnterCannon)
  11191         CUTSCENE(CUTSCENE_ENTER_PAINTING, sCutsceneEnterPainting)
  11192         CUTSCENE(CUTSCENE_DEATH_EXIT, sCutsceneDeathExit)
  11193         CUTSCENE(CUTSCENE_EXIT_PAINTING_SUCC, sCutsceneExitPaintingSuccess)
  11194         CUTSCENE(CUTSCENE_UNUSED_EXIT, sCutsceneUnusedExit)
  11195         CUTSCENE(CUTSCENE_INTRO_PEACH, sCutsceneIntroPeach)
  11196         CUTSCENE(CUTSCENE_ENTER_BOWSER_ARENA, sCutsceneEnterBowserArena)
  11197         CUTSCENE(CUTSCENE_DANCE_ROTATE, sCutsceneDanceDefaultRotate)
  11198         CUTSCENE(CUTSCENE_DANCE_DEFAULT, sCutsceneDanceDefaultRotate)
  11199         CUTSCENE(CUTSCENE_DANCE_FLY_AWAY, sCutsceneDanceFlyAway)
  11200         CUTSCENE(CUTSCENE_DANCE_CLOSEUP, sCutsceneDanceCloseup)
  11201         CUTSCENE(CUTSCENE_KEY_DANCE, sCutsceneKeyDance)
  11202         CUTSCENE(CUTSCENE_0F_UNUSED, sCutsceneUnused)
  11203         CUTSCENE(CUTSCENE_END_WAVING, sCutsceneEndWaving)
  11204         CUTSCENE(CUTSCENE_CREDITS, sCutsceneCredits)
  11205         CUTSCENE(CUTSCENE_CAP_SWITCH_PRESS, sCutsceneCapSwitchPress)
  11206         CUTSCENE(CUTSCENE_SLIDING_DOORS_OPEN, sCutsceneSlidingDoorsOpen)
  11207         CUTSCENE(CUTSCENE_PREPARE_CANNON, sCutscenePrepareCannon)
  11208         CUTSCENE(CUTSCENE_UNLOCK_KEY_DOOR, sCutsceneUnlockKeyDoor)
  11209         CUTSCENE(CUTSCENE_STANDING_DEATH, sCutsceneStandingDeath)
  11210         CUTSCENE(CUTSCENE_ENTER_POOL, sCutsceneEnterPool)
  11211         CUTSCENE(CUTSCENE_DEATH_ON_STOMACH, sCutsceneDeathStomach)
  11212         CUTSCENE(CUTSCENE_DEATH_ON_BACK, sCutsceneDeathOnBack)
  11213         CUTSCENE(CUTSCENE_QUICKSAND_DEATH, sCutsceneQuicksandDeath)
  11214         CUTSCENE(CUTSCENE_SUFFOCATION_DEATH, sCutsceneSuffocation)
  11215         CUTSCENE(CUTSCENE_EXIT_BOWSER_SUCC, sCutsceneExitBowserSuccess)
  11216         CUTSCENE(CUTSCENE_EXIT_BOWSER_DEATH, sCutsceneExitBowserDeath)
  11217         CUTSCENE(CUTSCENE_EXIT_SPECIAL_SUCC, sCutsceneExitSpecialSuccess)
  11218         CUTSCENE(CUTSCENE_EXIT_WATERFALL, sCutsceneExitWaterfall)
  11219         CUTSCENE(CUTSCENE_EXIT_FALL_WMOTR, sCutsceneFallToCastleGrounds)
  11220         CUTSCENE(CUTSCENE_NONPAINTING_DEATH, sCutsceneNonPaintingDeath)
  11221         CUTSCENE(CUTSCENE_DIALOG, sCutsceneDialog)
  11222         CUTSCENE(CUTSCENE_READ_MESSAGE, sCutsceneReadMessage)
  11223         CUTSCENE(CUTSCENE_RACE_DIALOG, sCutsceneDialog)
  11224         CUTSCENE(CUTSCENE_ENTER_PYRAMID_TOP, sCutsceneEnterPyramidTop)
  11225         CUTSCENE(CUTSCENE_SSL_PYRAMID_EXPLODE, sCutscenePyramidTopExplode)
  11226     }
  11227 
  11228 #undef CUTSCENE
  11229 
  11230     if ((cutsceneDuration != 0) && !(gCutsceneTimer & CUTSCENE_STOP)) {
  11231         //! @bug This should check for 0x7FFF (CUTSCENE_LOOP)
  11232         //! instead, cutscenes that last longer than 0x3FFF frames will never end on their own
  11233         if (gCutsceneTimer < 0x3FFF) {
  11234             gCutsceneTimer++;
  11235         }
  11236         //! Because gCutsceneTimer is often set to 0x7FFF (CUTSCENE_LOOP), this conditional can only
  11237         //! check for == due to overflow
  11238         if (gCutsceneTimer == cutsceneDuration) {
  11239             sCutsceneShot++;
  11240             gCutsceneTimer = 0;
  11241         }
  11242     } else {
  11243         sMarioCamState->cameraEvent = 0;
  11244         sCutsceneShot = 0;
  11245         gCutsceneTimer = 0;
  11246     }
  11247 
  11248     sAreaYawChange = 0;
  11249 
  11250     // The cutscene just ended
  11251     if ((c->cutscene == 0) && (oldCutscene != 0)) {
  11252         gRecentCutscene = oldCutscene;
  11253     }
  11254 }
  11255 
  11256 /**
  11257  * Call the event while `start` <= gCutsceneTimer <= `end`
  11258  * If `end` is -1, call for the rest of the shot.
  11259  */
  11260 s32 cutscene_event(CameraEvent event, struct Camera *c, s16 start, s16 end) {
  11261     if (start <= gCutsceneTimer) {
  11262         if (end == -1 || end >= gCutsceneTimer) {
  11263             event(c);
  11264         }
  11265     }
  11266     return 0;
  11267 }
  11268 
  11269 /**
  11270  * Set gCutsceneObjSpawn when gCutsceneTimer == `frame`.
  11271  *
  11272  * @see intro_scene.inc.c for details on which objects are spawned.
  11273  */
  11274 s32 cutscene_spawn_obj(u32 obj, s16 frame) {
  11275     if (frame == gCutsceneTimer) {
  11276         gCutsceneObjSpawn = obj;
  11277     }
  11278     return 0;
  11279 }
  11280 
  11281 /**
  11282  * Start shaking the camera's field of view.
  11283  *
  11284  * @param shakeSpeed How fast the shake should progress through its period. The shake offset is
  11285  *                   calculated from coss(), so this parameter can be thought of as an angular velocity.
  11286  */
  11287 void set_fov_shake(s16 amplitude, s16 decay, s16 shakeSpeed) {
  11288     if (amplitude > sFOVState.shakeAmplitude) {
  11289         sFOVState.shakeAmplitude = amplitude;
  11290         sFOVState.decay = decay;
  11291         sFOVState.shakeSpeed = shakeSpeed;
  11292     }
  11293 }
  11294 
  11295 /**
  11296  * Start shaking the camera's field of view, but reduce `amplitude` by distance from camera
  11297  */
  11298 void set_fov_shake_from_point(s16 amplitude, s16 decay, s16 shakeSpeed, f32 maxDist, f32 posX, f32 posY, f32 posZ) {
  11299     amplitude = reduce_by_dist_from_camera(amplitude, maxDist, posX, posY, posZ);
  11300 
  11301     if (amplitude != 0) {
  11302         if (amplitude > sFOVState.shakeAmplitude) { // literally use the function above you silly nintendo, smh
  11303             sFOVState.shakeAmplitude = amplitude;
  11304             sFOVState.decay = decay;
  11305             sFOVState.shakeSpeed = shakeSpeed;
  11306         }
  11307     }
  11308 }
  11309 
  11310 /**
  11311  * Add a cyclic offset to the camera's field of view based on a cosine wave
  11312  */
  11313 void shake_camera_fov(struct GraphNodePerspective *perspective) {
  11314     if (sFOVState.shakeAmplitude != 0.f) {
  11315         sFOVState.fovOffset = coss(sFOVState.shakePhase) * sFOVState.shakeAmplitude / 0x100;
  11316         sFOVState.shakePhase += sFOVState.shakeSpeed;
  11317         camera_approach_f32_symmetric_bool(&sFOVState.shakeAmplitude, 0.f, sFOVState.decay);
  11318         perspective->fov += sFOVState.fovOffset;
  11319     } else {
  11320         sFOVState.shakePhase = 0;
  11321     }
  11322 }
  11323 
  11324 static UNUSED void unused_deactivate_sleeping_camera(UNUSED struct MarioState *m) {
  11325     sStatusFlags &= ~CAM_FLAG_SLEEPING;
  11326 }
  11327 
  11328 void set_fov_30(UNUSED struct MarioState *m) {
  11329     sFOVState.fov = 30.f;
  11330 }
  11331 
  11332 void approach_fov_20(UNUSED struct MarioState *m) {
  11333     camera_approach_f32_symmetric_bool(&sFOVState.fov, 20.f, 0.3f);
  11334 }
  11335 
  11336 void set_fov_45(UNUSED struct MarioState *m) {
  11337     sFOVState.fov = 45.f;
  11338 }
  11339 
  11340 void set_fov_29(UNUSED struct MarioState *m) {
  11341     sFOVState.fov = 29.f;
  11342 }
  11343 
  11344 void zoom_fov_30(UNUSED struct MarioState *m) {
  11345     // Pretty sure approach_f32_asymptotic_bool would do a much nicer job here, but you do you,
  11346     // Nintendo.
  11347     camera_approach_f32_symmetric_bool(&sFOVState.fov, 30.f, (30.f - sFOVState.fov) / 60.f);
  11348 }
  11349 
  11350 /**
  11351  * This is the default fov function. It makes fov approach 45 degrees, and it handles zooming in when
  11352  * Mario falls a sleep.
  11353  */
  11354 void fov_default(struct MarioState *m) {
  11355     sStatusFlags &= ~CAM_FLAG_SLEEPING;
  11356 
  11357     if ((m->action == ACT_SLEEPING) || (m->action == ACT_START_SLEEPING)) {
  11358         camera_approach_f32_symmetric_bool(&sFOVState.fov, 30.f, (30.f - sFOVState.fov) / 30.f);
  11359         sStatusFlags |= CAM_FLAG_SLEEPING;
  11360     } else {
  11361         camera_approach_f32_symmetric_bool(&sFOVState.fov, 45.f, (45.f - sFOVState.fov) / 30.f);
  11362         sFOVState.unusedIsSleeping = 0;
  11363     }
  11364     if (m->area->camera->cutscene == CUTSCENE_0F_UNUSED) {
  11365         sFOVState.fov = 45.f;
  11366     }
  11367 }
  11368 
  11369 //??! Literally the exact same as below
  11370 static UNUSED void unused_approach_fov_30(UNUSED struct MarioState *m) {
  11371     camera_approach_f32_symmetric_bool(&sFOVState.fov, 30.f, 1.f);
  11372 }
  11373 
  11374 void approach_fov_30(UNUSED struct MarioState *m) {
  11375     camera_approach_f32_symmetric_bool(&sFOVState.fov, 30.f, 1.f);
  11376 }
  11377 
  11378 void approach_fov_60(UNUSED struct MarioState *m) {
  11379     camera_approach_f32_symmetric_bool(&sFOVState.fov, 60.f, 1.f);
  11380 }
  11381 
  11382 void approach_fov_45(struct MarioState *m) {
  11383     f32 targetFoV = sFOVState.fov;
  11384 
  11385     if (m->area->camera->mode == CAMERA_MODE_FIXED && m->area->camera->cutscene == 0) {
  11386         targetFoV = 45.f;
  11387     } else {
  11388         targetFoV = 45.f;
  11389     }
  11390 
  11391     sFOVState.fov = approach_f32(sFOVState.fov, targetFoV, 2.f, 2.f);
  11392 }
  11393 
  11394 void approach_fov_80(UNUSED struct MarioState *m) {
  11395     camera_approach_f32_symmetric_bool(&sFOVState.fov, 80.f, 3.5f);
  11396 }
  11397 
  11398 /**
  11399  * Sets the fov in BBH.
  11400  * If there's a cutscene, sets fov to 45. Otherwise sets fov to 60.
  11401  */
  11402 void set_fov_bbh(struct MarioState *m) {
  11403     f32 targetFoV = sFOVState.fov;
  11404 
  11405     if (m->area->camera->mode == CAMERA_MODE_FIXED && m->area->camera->cutscene == 0) {
  11406         targetFoV = 60.f;
  11407     } else {
  11408         targetFoV = 45.f;
  11409     }
  11410 
  11411     sFOVState.fov = approach_f32(sFOVState.fov, targetFoV, 2.f, 2.f);
  11412 }
  11413 
  11414 /**
  11415  * Sets the field of view for the GraphNodeCamera
  11416  */
  11417 Gfx *geo_camera_fov(s32 callContext, struct GraphNode *g, UNUSED void *context) {
  11418     struct GraphNodePerspective *perspective = (struct GraphNodePerspective *) g;
  11419     struct MarioState *marioState = &gMarioStates[0];
  11420     u8 fovFunc = sFOVState.fovFunc;
  11421 
  11422     if (callContext == GEO_CONTEXT_RENDER) {
  11423         switch (fovFunc) {
  11424             case CAM_FOV_SET_45:
  11425                 set_fov_45(marioState);
  11426                 break;
  11427             case CAM_FOV_SET_29:
  11428                 set_fov_29(marioState);
  11429                 break;
  11430             case CAM_FOV_ZOOM_30:
  11431                 zoom_fov_30(marioState);
  11432                 break;
  11433             case CAM_FOV_DEFAULT:
  11434                 fov_default(marioState);
  11435                 break;
  11436             case CAM_FOV_BBH:
  11437                 set_fov_bbh(marioState);
  11438                 break;
  11439             case CAM_FOV_APP_45:
  11440                 approach_fov_45(marioState);
  11441                 break;
  11442             case CAM_FOV_SET_30:
  11443                 set_fov_30(marioState);
  11444                 break;
  11445             case CAM_FOV_APP_20:
  11446                 approach_fov_20(marioState);
  11447                 break;
  11448             case CAM_FOV_APP_80:
  11449                 approach_fov_80(marioState);
  11450                 break;
  11451             case CAM_FOV_APP_30:
  11452                 approach_fov_30(marioState);
  11453                 break;
  11454             case CAM_FOV_APP_60:
  11455                 approach_fov_60(marioState);
  11456                 break;
  11457             //! No default case
  11458         }
  11459     }
  11460 
  11461     perspective->fov = sFOVState.fov;
  11462     shake_camera_fov(perspective);
  11463     return NULL;
  11464 }
  11465 
  11466 /**
  11467  * Change the camera's FOV mode.
  11468  *
  11469  * @see geo_camera_fov
  11470  */
  11471 void set_fov_function(u8 func) {
  11472     sFOVState.fovFunc = func;
  11473 }
  11474 
  11475 /**
  11476  * Start a preset fov shake. Used in cutscenes
  11477  */
  11478 void cutscene_set_fov_shake_preset(u8 preset) {
  11479     switch (preset) {
  11480         case 1:
  11481             set_fov_shake(0x100, 0x30, 0x8000);
  11482             break;
  11483         case 2:
  11484             set_fov_shake(0x400, 0x20, 0x4000);
  11485             break;
  11486     }
  11487 }
  11488 
  11489 /**
  11490  * Start a preset fov shake that is reduced by the point's distance from the camera.
  11491  * Used in set_camera_shake_from_point
  11492  *
  11493  * @see set_camera_shake_from_point
  11494  */
  11495 void set_fov_shake_from_point_preset(u8 preset, f32 posX, f32 posY, f32 posZ) {
  11496     switch (preset) {
  11497         case SHAKE_FOV_SMALL:
  11498             set_fov_shake_from_point(0x100, 0x30, 0x8000, 3000.f, posX, posY, posZ);
  11499             break;
  11500         case SHAKE_FOV_MEDIUM:
  11501             set_fov_shake_from_point(0x200, 0x30, 0x8000, 4000.f, posX, posY, posZ);
  11502             break;
  11503         case SHAKE_FOV_LARGE:
  11504             set_fov_shake_from_point(0x300, 0x30, 0x8000, 6000.f, posX, posY, posZ);
  11505             break;
  11506         case SHAKE_FOV_UNUSED:
  11507             set_fov_shake_from_point(0x800, 0x20, 0x4000, 3000.f, posX, posY, posZ);
  11508             break;
  11509     }
  11510 }
  11511 
  11512 /**
  11513  * Offset an object's position in a random direction within the given bounds.
  11514  */
  11515 static UNUSED void unused_displace_obj_randomly(struct Object *o, f32 xRange, f32 yRange, f32 zRange) {
  11516     f32 rnd = random_float();
  11517 
  11518     o->oPosX += (rnd * xRange - xRange / 2.f);
  11519     o->oPosY += (rnd * yRange - yRange / 2.f);
  11520     o->oPosZ += (rnd * zRange - zRange / 2.f);
  11521 }
  11522 
  11523 /**
  11524  * Rotate an object in a random direction within the given bounds.
  11525  */
  11526 static UNUSED void unused_rotate_obj_randomly(struct Object *o, f32 pitchRange, f32 yawRange) {
  11527     f32 rnd = random_float();
  11528 
  11529     o->oMoveAnglePitch += (s16)(rnd * pitchRange - pitchRange / 2.f);
  11530     o->oMoveAngleYaw += (s16)(rnd * yawRange - yawRange / 2.f);
  11531 }
  11532 
  11533 /**
  11534  * Rotate the object towards the point `point`.
  11535  */
  11536 void obj_rotate_towards_point(struct Object *o, Vec3f point, s16 pitchOff, s16 yawOff, s16 pitchDiv, s16 yawDiv) {
  11537     f32 dist;
  11538     s16 pitch, yaw;
  11539     Vec3f oPos;
  11540 
  11541     object_pos_to_vec3f(oPos, o);
  11542     vec3f_get_dist_and_angle(oPos, point, &dist, &pitch, &yaw);
  11543     o->oMoveAnglePitch = approach_s16_asymptotic(o->oMoveAnglePitch, pitchOff - pitch, pitchDiv);
  11544     o->oMoveAngleYaw = approach_s16_asymptotic(o->oMoveAngleYaw, yaw + yawOff, yawDiv);
  11545 }
  11546 
  11547 #define o gCurrentObject
  11548 
  11549 #include "behaviors/intro_peach.inc.c"
  11550 #include "behaviors/intro_lakitu.inc.c"
  11551 #include "behaviors/end_birds_1.inc.c"
  11552 #include "behaviors/end_birds_2.inc.c"
  11553 #include "behaviors/intro_scene.inc.c"