Super Mario 64 OpenGL port for PC. Mirror of https://github.com/sm64pc/sm64pc
https://github.com/sm64pc/sm64pc
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
11627 lines
394 KiB
11627 lines
394 KiB
#include <ultra64.h>
|
|
|
|
#define INCLUDED_FROM_CAMERA_C
|
|
|
|
#include "sm64.h"
|
|
#include "camera.h"
|
|
#include "seq_ids.h"
|
|
#include "dialog_ids.h"
|
|
#include "audio/external.h"
|
|
#include "mario_misc.h"
|
|
#include "game_init.h"
|
|
#include "hud.h"
|
|
#include "engine/math_util.h"
|
|
#include "area.h"
|
|
#include "engine/surface_collision.h"
|
|
#include "engine/behavior_script.h"
|
|
#include "level_update.h"
|
|
#include "ingame_menu.h"
|
|
#include "mario_actions_cutscene.h"
|
|
#include "save_file.h"
|
|
#include "object_helpers.h"
|
|
#include "print.h"
|
|
#include "spawn_sound.h"
|
|
#include "behavior_actions.h"
|
|
#include "behavior_data.h"
|
|
#include "object_list_processor.h"
|
|
#include "paintings.h"
|
|
#include "engine/graph_node.h"
|
|
#include "level_table.h"
|
|
|
|
#define CBUTTON_MASK (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS)
|
|
|
|
/**
|
|
* @file camera.c
|
|
* Implements the camera system, including C-button input, camera modes, camera triggers, and cutscenes.
|
|
*
|
|
* When working with the camera, you should be familiar with sm64's coordinate system.
|
|
* Relative to the camera, the coordinate system follows the right hand rule:
|
|
* +X points right.
|
|
* +Y points up.
|
|
* +Z points out of the screen.
|
|
*
|
|
* You should also be familiar with Euler angles: 'pitch', 'yaw', and 'roll'.
|
|
* pitch: rotation about the X-axis, measured from +Y.
|
|
* Unlike yaw and roll, pitch is bounded in +-0x4000 (90 degrees).
|
|
* Pitch is 0 when the camera points parallel to the xz-plane (+Y points straight up).
|
|
*
|
|
* yaw: rotation about the Y-axis, measured from (absolute) +Z.
|
|
* Positive yaw rotates clockwise, towards +X.
|
|
*
|
|
* roll: rotation about the Z-axis, measured from the camera's right direction.
|
|
* Unfortunately, it's weird: For some reason, roll is flipped. Positive roll makes the camera
|
|
* rotate counterclockwise, which means the WORLD rotates clockwise. Luckily roll is rarely
|
|
* used.
|
|
*
|
|
* Remember the right hand rule: make a thumbs-up with your right hand, stick your thumb in the
|
|
* +direction (except for roll), and the angle follows the rotation of your curled fingers.
|
|
*
|
|
* Illustrations:
|
|
* Following the right hand rule, each hidden axis's positive direction points out of the screen.
|
|
*
|
|
* YZ-Plane (pitch) XZ-Plane (yaw) XY-Plane (roll -- Note flipped)
|
|
* +Y -Z +Y
|
|
* ^ ^ (into the ^
|
|
* --|-- | screen) |<-
|
|
* +pitch / | \ -pitch | | \ -roll
|
|
* v | v | | |
|
|
* +Z <------O------> -Z -X <------O------> +X -X <------O------> +X
|
|
* | ^ | ^ | |
|
|
* | \ | / | / +roll
|
|
* | -yaw --|-- +yaw |<-
|
|
* v v v
|
|
* -Y +Z -Y
|
|
*
|
|
*/
|
|
|
|
// BSS
|
|
/**
|
|
* Stores lakitu's position from the last frame, used for transitioning in next_lakitu_state()
|
|
*/
|
|
Vec3f sOldPosition;
|
|
/**
|
|
* Stores lakitu's focus from the last frame, used for transitioning in next_lakitu_state()
|
|
*/
|
|
Vec3f sOldFocus;
|
|
/**
|
|
* Global array of PlayerCameraState.
|
|
* L is real.
|
|
*/
|
|
struct PlayerCameraState gPlayerCameraState[2];
|
|
/**
|
|
* Direction controlled by player 2, moves the focus during the credits.
|
|
*/
|
|
Vec3f sPlayer2FocusOffset;
|
|
/**
|
|
* The pitch used for the credits easter egg.
|
|
*/
|
|
s16 sCreditsPlayer2Pitch;
|
|
/**
|
|
* The yaw used for the credits easter egg.
|
|
*/
|
|
s16 sCreditsPlayer2Yaw;
|
|
/**
|
|
* Used to decide when to zoom out in the pause menu.
|
|
*/
|
|
u8 sFramesPaused;
|
|
|
|
#ifndef VERSION_EU
|
|
extern struct CameraFOVStatus sFOVState;
|
|
extern struct TransitionInfo sModeTransition;
|
|
extern struct PlayerGeometry sMarioGeometry;
|
|
extern s16 unusedFreeRoamWallYaw;
|
|
extern s16 sAvoidYawVel;
|
|
extern s16 sCameraYawAfterDoorCutscene;
|
|
extern s16 unusedSplinePitch;
|
|
extern s16 unusedSplineYaw;
|
|
extern struct HandheldShakePoint sHandheldShakeSpline[4];
|
|
extern s16 sHandheldShakeMag;
|
|
extern f32 sHandheldShakeTimer;
|
|
extern f32 sHandheldShakeInc;
|
|
extern s16 sHandheldShakePitch;
|
|
extern s16 sHandheldShakeYaw;
|
|
extern s16 sHandheldShakeRoll;
|
|
extern u32 unused8033B30C;
|
|
extern u32 unused8033B310;
|
|
extern s16 sSelectionFlags;
|
|
extern s16 unused8033B316;
|
|
extern s16 s2ndRotateFlags;
|
|
extern s16 unused8033B31A;
|
|
extern s16 sCameraSoundFlags;
|
|
extern u16 sCButtonsPressed;
|
|
extern s16 sCutsceneDialogID;
|
|
extern struct LakituState gLakituState;
|
|
extern s16 unused8033B3E8;
|
|
extern s16 sAreaYaw;
|
|
extern s16 sAreaYawChange;
|
|
extern s16 sLakituDist;
|
|
extern s16 sLakituPitch;
|
|
extern f32 sZoomAmount;
|
|
extern s16 sCSideButtonYaw;
|
|
extern s16 sBehindMarioSoundTimer;
|
|
extern f32 sZeroZoomDist;
|
|
extern s16 sCUpCameraPitch;
|
|
extern s16 sModeOffsetYaw;
|
|
extern s16 sSpiralStairsYawOffset;
|
|
extern s16 s8DirModeBaseYaw;
|
|
extern s16 s8DirModeYawOffset;
|
|
extern f32 sPanDistance;
|
|
extern f32 sCannonYOffset;
|
|
extern struct ModeTransitionInfo sModeInfo;
|
|
extern Vec3f sCastleEntranceOffset;
|
|
extern u32 sParTrackIndex;
|
|
extern struct ParallelTrackingPoint *sParTrackPath;
|
|
extern struct CameraStoredInfo sParTrackTransOff;
|
|
extern struct CameraStoredInfo sCameraStoreCUp;
|
|
extern struct CameraStoredInfo sCameraStoreCutscene;
|
|
extern s16 gCameraMovementFlags;
|
|
extern s16 sStatusFlags;
|
|
extern struct CutsceneSplinePoint sCurCreditsSplinePos[32];
|
|
extern struct CutsceneSplinePoint sCurCreditsSplineFocus[32];
|
|
extern s16 sCutsceneSplineSegment;
|
|
extern f32 sCutsceneSplineSegmentProgress;
|
|
extern s16 unused8033B6E8;
|
|
extern s16 sCutsceneShot;
|
|
extern s16 gCutsceneTimer;
|
|
extern struct CutsceneVariable sCutsceneVars[10];
|
|
extern s32 gObjCutsceneDone;
|
|
extern u32 gCutsceneObjSpawn;
|
|
extern struct Camera *gCamera;
|
|
#endif
|
|
|
|
/**
|
|
* Lakitu's position and focus.
|
|
* @see LakituState
|
|
*/
|
|
struct LakituState gLakituState;
|
|
struct CameraFOVStatus sFOVState;
|
|
struct TransitionInfo sModeTransition;
|
|
struct PlayerGeometry sMarioGeometry;
|
|
struct Camera *gCamera;
|
|
s16 unusedFreeRoamWallYaw;
|
|
s16 sAvoidYawVel;
|
|
s16 sCameraYawAfterDoorCutscene;
|
|
/**
|
|
* The current spline that controls the camera's position during the credits.
|
|
*/
|
|
struct CutsceneSplinePoint sCurCreditsSplinePos[32];
|
|
|
|
/**
|
|
* The current spline that controls the camera's focus during the credits.
|
|
*/
|
|
struct CutsceneSplinePoint sCurCreditsSplineFocus[32];
|
|
|
|
s16 unusedSplinePitch;
|
|
s16 unusedSplineYaw;
|
|
|
|
/**
|
|
* The progress (from 0 to 1) through the current spline segment.
|
|
* When it becomes >= 1, 1.0 is subtracted from it and sCutsceneSplineSegment is increased.
|
|
*/
|
|
f32 sCutsceneSplineSegmentProgress;
|
|
|
|
/**
|
|
* The current segment of the CutsceneSplinePoint[] being used.
|
|
*/
|
|
s16 sCutsceneSplineSegment;
|
|
s16 unused8033B6E8;
|
|
|
|
// Shaky Hand-held Camera effect variables
|
|
struct HandheldShakePoint sHandheldShakeSpline[4];
|
|
s16 sHandheldShakeMag;
|
|
f32 sHandheldShakeTimer;
|
|
f32 sHandheldShakeInc;
|
|
s16 sHandheldShakePitch;
|
|
s16 sHandheldShakeYaw;
|
|
s16 sHandheldShakeRoll;
|
|
|
|
/**
|
|
* Controls which object to spawn in the intro and ending cutscenes.
|
|
*/
|
|
u32 gCutsceneObjSpawn;
|
|
/**
|
|
* Controls when an object-based cutscene should end. It's only used in the star spawn cutscenes, but
|
|
* yoshi also toggles this.
|
|
*/
|
|
s32 gObjCutsceneDone;
|
|
|
|
u32 unused8033B30C;
|
|
u32 unused8033B310;
|
|
|
|
/**
|
|
* Determines which R-Trigger mode is selected in the pause menu.
|
|
*/
|
|
s16 sSelectionFlags;
|
|
|
|
/**
|
|
* Flags that determine what movements the camera should start / do this frame.
|
|
*/
|
|
s16 gCameraMovementFlags;
|
|
s16 unused8033B316;
|
|
|
|
/**
|
|
* Flags that change how modes operate and how lakitu moves.
|
|
* The most commonly used flag is CAM_FLAG_SMOOTH_MOVEMENT, which makes lakitu fly to the next position,
|
|
* instead of warping.
|
|
*/
|
|
s16 sStatusFlags;
|
|
/**
|
|
* Flags that determine whether the player has already rotated left or right. Used in radial mode to
|
|
* determine whether to rotate all the way, or just to 60 degrees.
|
|
*/
|
|
s16 s2ndRotateFlags;
|
|
s16 unused8033B31A;
|
|
/**
|
|
* Flags that control buzzes and sounds that play, mostly for C-button input.
|
|
*/
|
|
s16 sCameraSoundFlags;
|
|
/**
|
|
* Stores what C-Buttons are pressed this frame.
|
|
*/
|
|
u16 sCButtonsPressed;
|
|
/**
|
|
* A copy of gDialogID, the dialog displayed during the cutscene.
|
|
*/
|
|
s16 sCutsceneDialogID;
|
|
/**
|
|
* The currently playing shot in the cutscene.
|
|
*/
|
|
s16 sCutsceneShot;
|
|
/**
|
|
* The current frame of the cutscene shot.
|
|
*/
|
|
s16 gCutsceneTimer;
|
|
s16 unused8033B3E8;
|
|
#ifdef VERSION_EU
|
|
s16 unused8033B3E82;
|
|
#endif
|
|
/**
|
|
* The angle of the direction vector from the area's center to mario's position.
|
|
*/
|
|
s16 sAreaYaw;
|
|
|
|
/**
|
|
* How much sAreaYaw changed when mario moved.
|
|
*/
|
|
s16 sAreaYawChange;
|
|
|
|
/**
|
|
* Lakitu's distance from mario in C-Down mode
|
|
*/
|
|
s16 sLakituDist;
|
|
|
|
/**
|
|
* How much lakitu looks down in C-Down mode
|
|
*/
|
|
s16 sLakituPitch;
|
|
|
|
/**
|
|
* The amount of distance left to zoom out
|
|
*/
|
|
f32 sZoomAmount;
|
|
|
|
s16 sCSideButtonYaw;
|
|
|
|
/**
|
|
* Sound timer used to space out sounds in behind mario mode
|
|
*/
|
|
s16 sBehindMarioSoundTimer;
|
|
|
|
/**
|
|
* Virtually unused aside being set to 0 and compared with gCameraZoomDist (which is never < 0)
|
|
*/
|
|
f32 sZeroZoomDist;
|
|
|
|
/**
|
|
* The camera's pitch in C-Up mode. Mainly controls mario's head rotation.
|
|
*/
|
|
s16 sCUpCameraPitch;
|
|
/**
|
|
* The current mode's yaw, which gets added to the camera's yaw.
|
|
*/
|
|
s16 sModeOffsetYaw;
|
|
|
|
/**
|
|
* Stores mario's yaw around the stairs, relative to the camera's position.
|
|
*
|
|
* Used in update_spiral_stairs_camera()
|
|
*/
|
|
s16 sSpiralStairsYawOffset;
|
|
|
|
/**
|
|
* The constant offset to 8-direction mode's yaw.
|
|
*/
|
|
s16 s8DirModeBaseYaw;
|
|
/**
|
|
* Player-controlled yaw offset in 8-direction mode, a multiple of 45 degrees.
|
|
*/
|
|
s16 s8DirModeYawOffset;
|
|
|
|
/**
|
|
* The distance that the camera will look ahead of mario in the direction mario is facing.
|
|
*/
|
|
f32 sPanDistance;
|
|
|
|
/**
|
|
* When mario gets in the cannon, it is pointing straight up and rotates down.
|
|
* This is used to make the camera start up and rotate down, like the cannon.
|
|
*/
|
|
f32 sCannonYOffset;
|
|
/**
|
|
* These structs are used by the cutscenes. Most of the fields are unused, and some (all?) of the used
|
|
* ones have multiple uses.
|
|
* Check the cutscene_start functions for documentation on the cvars used by a specific cutscene.
|
|
*/
|
|
struct CutsceneVariable sCutsceneVars[10];
|
|
struct ModeTransitionInfo sModeInfo;
|
|
/**
|
|
* Offset added to sFixedModeBasePosition when mario is inside, near the castle lobby entrance
|
|
*/
|
|
Vec3f sCastleEntranceOffset;
|
|
|
|
/**
|
|
* The index into the current parallel tracking path
|
|
*/
|
|
u32 sParTrackIndex;
|
|
|
|
/**
|
|
* The current list of ParallelTrackingPoints used in update_parallel_tracking_camera()
|
|
*/
|
|
struct ParallelTrackingPoint *sParTrackPath;
|
|
|
|
/**
|
|
* On the first frame after the camera changes to a different parallel tracking path, this stores the
|
|
* displacement between the camera's calculated new position and its previous positions
|
|
*
|
|
* This transition offset is then used to smoothly interpolate the camera's position between the two
|
|
* paths
|
|
*/
|
|
struct CameraStoredInfo sParTrackTransOff;
|
|
|
|
/**
|
|
* The information stored when C-Up is active, used to update lakitu's rotation when exiting C-Up
|
|
*/
|
|
struct CameraStoredInfo sCameraStoreCUp;
|
|
|
|
/**
|
|
* The information stored during cutscenes
|
|
*/
|
|
struct CameraStoredInfo sCameraStoreCutscene;
|
|
|
|
// first iteration of data
|
|
u32 unused8032CFC0 = 0;
|
|
struct Object *gCutsceneFocus = NULL;
|
|
// other camera focuses?
|
|
u32 unused8032CFC8 = 0;
|
|
u32 unused8032CFCC = 0;
|
|
struct Object *gSecondCameraFocus = NULL;
|
|
|
|
/**
|
|
* How fast the camera's yaw should approach the next yaw.
|
|
*/
|
|
s16 sYawSpeed = 0x400;
|
|
s32 gCurrLevelArea = 0;
|
|
u32 gPrevLevel = 0;
|
|
|
|
f32 unused8032CFE0 = 1000.0f;
|
|
f32 unused8032CFE4 = 800.0f;
|
|
u32 unused8032CFE8 = 0;
|
|
f32 gCameraZoomDist = 800.0f;
|
|
|
|
/**
|
|
* A cutscene that plays when the player interacts with an object
|
|
*/
|
|
u8 sObjectCutscene = 0;
|
|
|
|
/**
|
|
* The ID of the cutscene that ended. It's set to 0 if no cutscene ended less than 8 frames ago.
|
|
*
|
|
* It is only used to prevent the same cutscene from playing twice before 8 frames have passed.
|
|
*/
|
|
u8 gRecentCutscene = 0;
|
|
|
|
/**
|
|
* A timer that increments for 8 frames when a cutscene ends.
|
|
* When it reaches 8, it sets gRecentCutscene to 0.
|
|
*/
|
|
u8 sFramesSinceCutsceneEnded = 0;
|
|
/**
|
|
* Mario's response to a dialog.
|
|
* 0 = No response yet
|
|
* 1 = Yes
|
|
* 2 = No
|
|
* 3 = Dialog doesn't have a response
|
|
*/
|
|
u8 sCutsceneDialogResponse = 0;
|
|
struct PlayerCameraState *sMarioCamState = &gPlayerCameraState[0];
|
|
struct PlayerCameraState *sLuigiCamState = &gPlayerCameraState[1];
|
|
u32 unused8032D008 = 0;
|
|
Vec3f sFixedModeBasePosition = { 646.0f, 143.0f, -1513.0f };
|
|
Vec3f sUnusedModeBasePosition_2 = { 646.0f, 143.0f, -1513.0f };
|
|
Vec3f sUnusedModeBasePosition_3 = { 646.0f, 143.0f, -1513.0f };
|
|
Vec3f sUnusedModeBasePosition_4 = { 646.0f, 143.0f, -1513.0f };
|
|
Vec3f sUnusedModeBasePosition_5 = { 646.0f, 143.0f, -1513.0f };
|
|
|
|
s32 update_radial_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_outward_radial_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_behind_mario_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_mario_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 unused_update_mode_5_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_c_up(struct Camera *c, Vec3f, Vec3f);
|
|
s32 nop_update_water_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_slide_or_0f_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_in_cannon(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_boss_fight_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_parallel_tracking_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_fixed_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_8_directions_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_slide_or_0f_camera(struct Camera *c, Vec3f, Vec3f);
|
|
s32 update_spiral_stairs_camera(struct Camera *c, Vec3f, Vec3f);
|
|
|
|
typedef s32 (*CameraTransition)(struct Camera *c, Vec3f, Vec3f);
|
|
CameraTransition sModeTransitions[] = {
|
|
NULL,
|
|
update_radial_camera,
|
|
update_outward_radial_camera,
|
|
update_behind_mario_camera,
|
|
update_mario_camera,
|
|
unused_update_mode_5_camera,
|
|
update_c_up,
|
|
update_mario_camera,
|
|
nop_update_water_camera,
|
|
update_slide_or_0f_camera,
|
|
update_in_cannon,
|
|
update_boss_fight_camera,
|
|
update_parallel_tracking_camera,
|
|
update_fixed_camera,
|
|
update_8_directions_camera,
|
|
update_slide_or_0f_camera,
|
|
update_mario_camera,
|
|
update_spiral_stairs_camera
|
|
};
|
|
|
|
// Move these two tables to another include file?
|
|
extern u8 sDanceCutsceneIndexTable[][4];
|
|
extern u8 sZoomOutAreaMasks[];
|
|
|
|
/**
|
|
* Starts a camera shake triggered by an interaction
|
|
*/
|
|
void set_camera_shake_from_hit(s16 shake) {
|
|
switch (shake) {
|
|
// Makes the camera stop for a bit
|
|
case SHAKE_ATTACK:
|
|
gLakituState.focHSpeed = 0;
|
|
gLakituState.posHSpeed = 0;
|
|
break;
|
|
|
|
case SHAKE_FALL_DAMAGE:
|
|
set_camera_pitch_shake(0x60, 0x3, 0x8000);
|
|
set_camera_roll_shake(0x60, 0x3, 0x8000);
|
|
break;
|
|
|
|
case SHAKE_GROUND_POUND:
|
|
set_camera_pitch_shake(0x60, 0xC, 0x8000);
|
|
break;
|
|
|
|
case SHAKE_SMALL_DAMAGE:
|
|
if (sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
|
|
set_camera_yaw_shake(0x200, 0x10, 0x1000);
|
|
set_camera_roll_shake(0x400, 0x20, 0x1000);
|
|
set_fov_shake(0x100, 0x30, 0x8000);
|
|
} else {
|
|
set_camera_yaw_shake(0x80, 0x8, 0x4000);
|
|
set_camera_roll_shake(0x80, 0x8, 0x4000);
|
|
set_fov_shake(0x100, 0x30, 0x8000);
|
|
}
|
|
|
|
gLakituState.focHSpeed = 0;
|
|
gLakituState.posHSpeed = 0;
|
|
break;
|
|
|
|
case SHAKE_MED_DAMAGE:
|
|
if (sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
|
|
set_camera_yaw_shake(0x400, 0x20, 0x1000);
|
|
set_camera_roll_shake(0x600, 0x30, 0x1000);
|
|
set_fov_shake(0x180, 0x40, 0x8000);
|
|
} else {
|
|
set_camera_yaw_shake(0x100, 0x10, 0x4000);
|
|
set_camera_roll_shake(0x100, 0x10, 0x4000);
|
|
set_fov_shake(0x180, 0x40, 0x8000);
|
|
}
|
|
|
|
gLakituState.focHSpeed = 0;
|
|
gLakituState.posHSpeed = 0;
|
|
break;
|
|
|
|
case SHAKE_LARGE_DAMAGE:
|
|
if (sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
|
|
set_camera_yaw_shake(0x600, 0x30, 0x1000);
|
|
set_camera_roll_shake(0x800, 0x40, 0x1000);
|
|
set_fov_shake(0x200, 0x50, 0x8000);
|
|
} else {
|
|
set_camera_yaw_shake(0x180, 0x20, 0x4000);
|
|
set_camera_roll_shake(0x200, 0x20, 0x4000);
|
|
set_fov_shake(0x200, 0x50, 0x8000);
|
|
}
|
|
|
|
gLakituState.focHSpeed = 0;
|
|
gLakituState.posHSpeed = 0;
|
|
break;
|
|
|
|
case SHAKE_HIT_FROM_BELOW:
|
|
gLakituState.focHSpeed = 0.07;
|
|
gLakituState.posHSpeed = 0.07;
|
|
break;
|
|
|
|
case SHAKE_SHOCK:
|
|
set_camera_pitch_shake(random_float() * 64.f, 0x8, 0x8000);
|
|
set_camera_yaw_shake(random_float() * 64.f, 0x8, 0x8000);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start a shake from the environment
|
|
*/
|
|
void set_environmental_camera_shake(s16 shake) {
|
|
switch (shake) {
|
|
case SHAKE_ENV_EXPLOSION:
|
|
set_camera_pitch_shake(0x60, 0x8, 0x4000);
|
|
break;
|
|
|
|
case SHAKE_ENV_BOWSER_THROW_BOUNCE:
|
|
set_camera_pitch_shake(0xC0, 0x8, 0x4000);
|
|
break;
|
|
|
|
case SHAKE_ENV_BOWSER_JUMP:
|
|
set_camera_pitch_shake(0x100, 0x8, 0x3000);
|
|
break;
|
|
|
|
case SHAKE_ENV_UNUSED_6:
|
|
set_camera_roll_shake(0x80, 0x10, 0x3000);
|
|
break;
|
|
|
|
case SHAKE_ENV_UNUSED_7:
|
|
set_camera_pitch_shake(0x20, 0x8, 0x8000);
|
|
break;
|
|
|
|
case SHAKE_ENV_PYRAMID_EXPLODE:
|
|
set_camera_pitch_shake(0x40, 0x8, 0x8000);
|
|
break;
|
|
|
|
case SHAKE_ENV_JRB_SHIP_DRAIN:
|
|
set_camera_pitch_shake(0x20, 0x8, 0x8000);
|
|
set_camera_roll_shake(0x400, 0x10, 0x100);
|
|
break;
|
|
|
|
case SHAKE_ENV_FALLING_BITS_PLAT:
|
|
set_camera_pitch_shake(0x40, 0x2, 0x8000);
|
|
break;
|
|
|
|
case SHAKE_ENV_UNUSED_5:
|
|
set_camera_yaw_shake(-0x200, 0x80, 0x200);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts a camera shake, but scales the amplitude by the point's distance from the camera
|
|
*/
|
|
void set_camera_shake_from_point(s16 shake, f32 posX, f32 posY, f32 posZ) {
|
|
switch (shake) {
|
|
case SHAKE_POS_BOWLING_BALL:
|
|
set_pitch_shake_from_point(0x28, 0x8, 0x4000, 2000.f, posX, posY, posZ);
|
|
break;
|
|
|
|
case SHAKE_POS_SMALL:
|
|
set_pitch_shake_from_point(0x80, 0x8, 0x4000, 4000.f, posX, posY, posZ);
|
|
set_fov_shake_from_point_preset(SHAKE_FOV_SMALL, posX, posY, posZ);
|
|
break;
|
|
|
|
case SHAKE_POS_MEDIUM:
|
|
set_pitch_shake_from_point(0xC0, 0x8, 0x4000, 6000.f, posX, posY, posZ);
|
|
set_fov_shake_from_point_preset(SHAKE_FOV_MEDIUM, posX, posY, posZ);
|
|
break;
|
|
|
|
case SHAKE_POS_LARGE:
|
|
set_pitch_shake_from_point(0x100, 0x8, 0x3000, 8000.f, posX, posY, posZ);
|
|
set_fov_shake_from_point_preset(SHAKE_FOV_LARGE, posX, posY, posZ);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start a camera shake from an environmental source, but only shake the camera's pitch.
|
|
*/
|
|
void unused_set_camera_pitch_shake_env(s16 shake) {
|
|
switch (shake) {
|
|
case SHAKE_ENV_EXPLOSION:
|
|
set_camera_pitch_shake(0x60, 0x8, 0x4000);
|
|
break;
|
|
|
|
case SHAKE_ENV_BOWSER_THROW_BOUNCE:
|
|
set_camera_pitch_shake(0xC0, 0x8, 0x4000);
|
|
break;
|
|
|
|
case SHAKE_ENV_BOWSER_JUMP:
|
|
set_camera_pitch_shake(0x100, 0x8, 0x3000);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculates mario's distance to the floor, or the water level if it is above the floor. Then:
|
|
* `posOff` is set to the distance multiplied by posMul and bounded to [-posBound, posBound]
|
|
* `focOff` is set to the distance multiplied by focMul and bounded to [-focBound, focBound]
|
|
*
|
|
* Notes:
|
|
* posMul is always 1.0f, focMul is always 0.9f
|
|
* both ranges are always 200.f
|
|
* Since focMul is 0.9, `focOff` is closer to the floor than `posOff`
|
|
* posOff and focOff are sometimes the same address, which just ignores the pos calculation
|
|
*! Doesn't return anything, but required to match EU
|
|
*/
|
|
f32 calc_y_to_curr_floor(f32 *posOff, f32 posMul, f32 posBound, f32 *focOff, f32 focMul, f32 focBound) {
|
|
f32 floorHeight = sMarioGeometry.currFloorHeight;
|
|
f32 waterHeight;
|
|
UNUSED s32 filler;
|
|
|
|
if (!(sMarioCamState->action & ACT_FLAG_METAL_WATER)) {
|
|
//! @bug this should use sMarioGeometry.waterHeight
|
|
if (floorHeight < (waterHeight = find_water_level(sMarioCamState->pos[0], sMarioCamState->pos[2]))) {
|
|
floorHeight = waterHeight;
|
|
}
|
|
}
|
|
|
|
if (sMarioCamState->action & ACT_FLAG_ON_POLE) {
|
|
if (sMarioGeometry.currFloorHeight >= gMarioStates[0].usedObj->oPosY && sMarioCamState->pos[1]
|
|
< 0.7f * gMarioStates[0].usedObj->hitboxHeight + gMarioStates[0].usedObj->oPosY) {
|
|
posBound = 1200;
|
|
}
|
|
}
|
|
|
|
*posOff = (floorHeight - sMarioCamState->pos[1]) * posMul;
|
|
|
|
if (*posOff > posBound) {
|
|
*posOff = posBound;
|
|
}
|
|
|
|
if (*posOff < -posBound) {
|
|
*posOff = -posBound;
|
|
}
|
|
|
|
*focOff = (floorHeight - sMarioCamState->pos[1]) * focMul;
|
|
|
|
if (*focOff > focBound) {
|
|
*focOff = focBound;
|
|
}
|
|
|
|
if (*focOff < -focBound) {
|
|
*focOff = -focBound;
|
|
}
|
|
}
|
|
//Compiler gets mad if I put this any further above. thanks refresh 7
|
|
#ifdef BETTERCAMERA
|
|
#include "bettercamera.inc.h"
|
|
#endif
|
|
|
|
void focus_on_mario(Vec3f focus, Vec3f pos, f32 posYOff, f32 focYOff, f32 dist, s16 pitch, s16 yaw) {
|
|
Vec3f marioPos;
|
|
|
|
marioPos[0] = sMarioCamState->pos[0];
|
|
marioPos[1] = sMarioCamState->pos[1] + posYOff;
|
|
marioPos[2] = sMarioCamState->pos[2];
|
|
|
|
vec3f_set_dist_and_angle(marioPos, pos, dist, pitch + sLakituPitch, yaw);
|
|
|
|
focus[0] = sMarioCamState->pos[0];
|
|
focus[1] = sMarioCamState->pos[1] + focYOff;
|
|
focus[2] = sMarioCamState->pos[2];
|
|
}
|
|
|
|
static UNUSED void set_pos_to_mario(Vec3f foc, Vec3f pos, f32 yOff, f32 focYOff, f32 dist, s16 pitch, s16 yaw) {
|
|
Vec3f marioPos;
|
|
f32 posDist;
|
|
f32 focDist;
|
|
|
|
s16 posPitch;
|
|
s16 posYaw;
|
|
s16 focPitch;
|
|
s16 focYaw;
|
|
|
|
vec3f_copy(marioPos, sMarioCamState->pos);
|
|
marioPos[1] += yOff;
|
|
|
|
vec3f_set_dist_and_angle(marioPos, pos, dist, pitch + sLakituPitch, yaw);
|
|
vec3f_get_dist_and_angle(pos, sMarioCamState->pos, &posDist, &posPitch, &posYaw);
|
|
|
|
//! Useless get and set
|
|
vec3f_get_dist_and_angle(pos, foc, &focDist, &focPitch, &focYaw);
|
|
vec3f_set_dist_and_angle(pos, foc, focDist, focPitch, focYaw);
|
|
|
|
foc[1] = sMarioCamState->pos[1] + focYOff;
|
|
}
|
|
|
|
/**
|
|
* Set the camera's y coordinate to goalHeight, respecting floors and ceilings in the way
|
|
*/
|
|
void set_camera_height(struct Camera *c, f32 goalHeight) {
|
|
struct Surface *surface;
|
|
f32 marioFloorHeight;
|
|
f32 marioCeilHeight;
|
|
f32 camFloorHeight;
|
|
UNUSED u8 filler[8];
|
|
UNUSED s16 action = sMarioCamState->action;
|
|
f32 baseOff = 125.f;
|
|
f32 camCeilHeight = find_ceil(c->pos[0], gLakituState.goalPos[1] - 50.f, c->pos[2], &surface);
|
|
|
|
if (sMarioCamState->action & ACT_FLAG_HANGING) {
|
|
marioCeilHeight = sMarioGeometry.currCeilHeight;
|
|
marioFloorHeight = sMarioGeometry.currFloorHeight;
|
|
|
|
if (marioFloorHeight < marioCeilHeight - 400.f) {
|
|
marioFloorHeight = marioCeilHeight - 400.f;
|
|
}
|
|
|
|
goalHeight = marioFloorHeight + (marioCeilHeight - marioFloorHeight) * 0.4f;
|
|
|
|
if (sMarioCamState->pos[1] - 400 > goalHeight) {
|
|
goalHeight = sMarioCamState->pos[1] - 400;
|
|
}
|
|
|
|
approach_camera_height(c, goalHeight, 5.f);
|
|
} else {
|
|
camFloorHeight = find_floor(c->pos[0], c->pos[1] + 100.f, c->pos[2], &surface) + baseOff;
|
|
marioFloorHeight = baseOff + sMarioGeometry.currFloorHeight;
|
|
|
|
if (camFloorHeight < marioFloorHeight) {
|
|
camFloorHeight = marioFloorHeight;
|
|
}
|
|
if (goalHeight < camFloorHeight) {
|
|
goalHeight = camFloorHeight;
|
|
c->pos[1] = goalHeight;
|
|
}
|
|
// Warp camera to goalHeight if further than 1000 and mario is stuck in the ground
|
|
if (sMarioCamState->action == ACT_BUTT_STUCK_IN_GROUND ||
|
|
sMarioCamState->action == ACT_HEAD_STUCK_IN_GROUND ||
|
|
sMarioCamState->action == ACT_FEET_STUCK_IN_GROUND) {
|
|
if (ABS(c->pos[1] - goalHeight) > 1000.f) {
|
|
c->pos[1] = goalHeight;
|
|
}
|
|
}
|
|
approach_camera_height(c, goalHeight, 20.f);
|
|
if (camCeilHeight != 20000.f) {
|
|
camCeilHeight -= baseOff;
|
|
if ((c->pos[1] > camCeilHeight && sMarioGeometry.currFloorHeight + baseOff < camCeilHeight)
|
|
|| (sMarioGeometry.currCeilHeight != 20000.f
|
|
&& sMarioGeometry.currCeilHeight > camCeilHeight && c->pos[1] > camCeilHeight)) {
|
|
c->pos[1] = camCeilHeight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Pitch the camera down when the camera is facing down a slope
|
|
*/
|
|
s16 look_down_slopes(s16 camYaw) {
|
|
struct Surface *floor;
|
|
f32 floorDY;
|
|
// Default pitch
|
|
s16 pitch = 0x05B0;
|
|
// x and z offsets towards the camera
|
|
f32 xOff = sMarioCamState->pos[0] + sins(camYaw) * 40.f;
|
|
f32 zOff = sMarioCamState->pos[2] + coss(camYaw) * 40.f;
|
|
|
|
floorDY = find_floor(xOff, sMarioCamState->pos[1], zOff, &floor) - sMarioCamState->pos[1];
|
|
|
|
if (floor != NULL) {
|
|
if (floor->type != SURFACE_WALL_MISC && floorDY > 0) {
|
|
if (floor->normal.z == 0.f && floorDY < 100.f) {
|
|
pitch = 0x05B0;
|
|
} else {
|
|
// Add the slope's angle of declination to the pitch
|
|
pitch += atan2s(40.f, floorDY);
|
|
}
|
|
}
|
|
}
|
|
|
|
return pitch;
|
|
}
|
|
|
|
/**
|
|
* Look ahead to the left or right in the direction the player is facing
|
|
* The calculation for pan[0] could be simplified to:
|
|
* yaw = -yaw;
|
|
* pan[0] = sins(sMarioCamState->faceAngle[1] + yaw) * sins(0xC00) * dist;
|
|
* Perhaps, early in development, the pan used to be calculated for both the x and z directions
|
|
*
|
|
* Since this function only affects the camera's focus, mario's movement direction isn't affected.
|
|
*/
|
|
void pan_ahead_of_player(struct Camera *c) {
|
|
f32 dist;
|
|
s16 pitch;
|
|
s16 yaw;
|
|
Vec3f pan = { 0, 0, 0 };
|
|
|
|
// Get distance and angle from camera to mario.
|
|
vec3f_get_dist_and_angle(c->pos, sMarioCamState->pos, &dist, &pitch, &yaw);
|
|
|
|
// The camera will pan ahead up to about 30% of the camera's distance to mario.
|
|
pan[2] = sins(0xC00) * dist;
|
|
|
|
rotate_in_xz(pan, pan, sMarioCamState->faceAngle[1]);
|
|
// rotate in the opposite direction
|
|
yaw = -yaw;
|
|
rotate_in_xz(pan, pan, yaw);
|
|
// Only pan left or right
|
|
pan[2] = 0.f;
|
|
|
|
// If mario is long jumping, or on a flag pole (but not at the top), then pan in the opposite direction
|
|
if (sMarioCamState->action == ACT_LONG_JUMP ||
|
|
(sMarioCamState->action != ACT_TOP_OF_POLE && (sMarioCamState->action & ACT_FLAG_ON_POLE))) {
|
|
pan[0] = -pan[0];
|
|
}
|
|
|
|
// Slowly make the actual pan, sPanDistance, approach the calculated pan
|
|
// If mario is sleeping, then don't pan
|
|
if (sStatusFlags & CAM_FLAG_SLEEPING) {
|
|
approach_f32_asymptotic_bool(&sPanDistance, 0.f, 0.025f);
|
|
} else {
|
|
approach_f32_asymptotic_bool(&sPanDistance, pan[0], 0.025f);
|
|
}
|
|
|
|
// Now apply the pan. It's a dir vector to the left or right, rotated by the camera's yaw to mario
|
|
pan[0] = sPanDistance;
|
|
yaw = -yaw;
|
|
rotate_in_xz(pan, pan, yaw);
|
|
vec3f_add(c->focus, pan);
|
|
}
|
|
|
|
s16 find_in_bounds_yaw_wdw_bob_thi(Vec3f pos, Vec3f origin, s16 yaw) {
|
|
switch (gCurrLevelArea) {
|
|
case AREA_WDW_MAIN:
|
|
yaw = clamp_positions_and_find_yaw(pos, origin, 4508.f, -3739.f, 4508.f, -3739.f);
|
|
break;
|
|
case AREA_BOB:
|
|
yaw = clamp_positions_and_find_yaw(pos, origin, 8000.f, -8000.f, 7050.f, -8000.f);
|
|
break;
|
|
case AREA_THI_HUGE:
|
|
yaw = clamp_positions_and_find_yaw(pos, origin, 8192.f, -8192.f, 8192.f, -8192.f);
|
|
break;
|
|
case AREA_THI_TINY:
|
|
yaw = clamp_positions_and_find_yaw(pos, origin, 2458.f, -2458.f, 2458.f, -2458.f);
|
|
break;
|
|
}
|
|
return yaw;
|
|
}
|
|
|
|
/**
|
|
* Rotates the camera around the area's center point.
|
|
*/
|
|
s32 update_radial_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
f32 cenDistX = sMarioCamState->pos[0] - c->areaCenX;
|
|
f32 cenDistZ = sMarioCamState->pos[2] - c->areaCenZ;
|
|
s16 camYaw = atan2s(cenDistZ, cenDistX) + sModeOffsetYaw;
|
|
s16 pitch = look_down_slopes(camYaw);
|
|
UNUSED f32 unused1;
|
|
f32 posY;
|
|
f32 focusY;
|
|
UNUSED f32 unused2;
|
|
UNUSED f32 unused3;
|
|
f32 yOff = 125.f;
|
|
f32 baseDist = 1000.f;
|
|
|
|
sAreaYaw = camYaw - sModeOffsetYaw;
|
|
calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f);
|
|
focus_on_mario(focus, pos, posY + yOff, focusY + yOff, sLakituDist + baseDist, pitch, camYaw);
|
|
camYaw = find_in_bounds_yaw_wdw_bob_thi(pos, focus, camYaw);
|
|
|
|
return camYaw;
|
|
}
|
|
|
|
/**
|
|
* Update the camera during 8 directional mode
|
|
*/
|
|
s32 update_8_directions_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
UNUSED f32 cenDistX = sMarioCamState->pos[0] - c->areaCenX;
|
|
UNUSED f32 cenDistZ = sMarioCamState->pos[2] - c->areaCenZ;
|
|
s16 camYaw = s8DirModeBaseYaw + s8DirModeYawOffset;
|
|
s16 pitch = look_down_slopes(camYaw);
|
|
f32 posY;
|
|
f32 focusY;
|
|
UNUSED f32 unused1;
|
|
UNUSED f32 unused2;
|
|
UNUSED f32 unused3;
|
|
f32 yOff = 125.f;
|
|
f32 baseDist = 1000.f;
|
|
|
|
sAreaYaw = camYaw;
|
|
calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f);
|
|
focus_on_mario(focus, pos, posY + yOff, focusY + yOff, sLakituDist + baseDist, pitch, camYaw);
|
|
pan_ahead_of_player(c);
|
|
if (gCurrLevelArea == AREA_DDD_SUB) {
|
|
camYaw = clamp_positions_and_find_yaw(pos, focus, 6839.f, 995.f, 5994.f, -3945.f);
|
|
}
|
|
|
|
return camYaw;
|
|
}
|
|
|
|
/**
|
|
* Moves the camera for the radial and outward radial camera modes.
|
|
*
|
|
* If sModeOffsetYaw is 0, the camera points directly at the area center point.
|
|
*/
|
|
void radial_camera_move(struct Camera *c) {
|
|
s16 maxAreaYaw = DEGREES(60);
|
|
s16 minAreaYaw = DEGREES(-60);
|
|
s16 rotateSpeed = 0x1000;
|
|
s16 avoidYaw;
|
|
s32 avoidStatus;
|
|
UNUSED s16 unused1 = 0;
|
|
UNUSED s32 unused2 = 0;
|
|
f32 areaDistX = sMarioCamState->pos[0] - c->areaCenX;
|
|
f32 areaDistZ = sMarioCamState->pos[2] - c->areaCenZ;
|
|
UNUSED s32 filler;
|
|
|
|
// How much the camera's yaw changed
|
|
s16 yawOffset = calculate_yaw(sMarioCamState->pos, c->pos) - atan2s(areaDistZ, areaDistX);
|
|
|
|
if (yawOffset > maxAreaYaw) {
|
|
yawOffset = maxAreaYaw;
|
|
}
|
|
if (yawOffset < minAreaYaw) {
|
|
yawOffset = minAreaYaw;
|
|
}
|
|
|
|
// Check if mario stepped on a surface that rotates the camera. For example, when mario enters the
|
|
// gate in BoB, the camera turns right to face up the hill path
|
|
if (!(gCameraMovementFlags & CAM_MOVE_ROTATE)) {
|
|
if (sMarioGeometry.currFloorType == SURFACE_CAMERA_MIDDLE
|
|
&& sMarioGeometry.prevFloorType != SURFACE_CAMERA_MIDDLE) {
|
|
gCameraMovementFlags |= (CAM_MOVE_RETURN_TO_MIDDLE | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
}
|
|
if (sMarioGeometry.currFloorType == SURFACE_CAMERA_ROTATE_RIGHT
|
|
&& sMarioGeometry.prevFloorType != SURFACE_CAMERA_ROTATE_RIGHT) {
|
|
gCameraMovementFlags |= (CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
}
|
|
if (sMarioGeometry.currFloorType == SURFACE_CAMERA_ROTATE_LEFT
|
|
&& sMarioGeometry.prevFloorType != SURFACE_CAMERA_ROTATE_LEFT) {
|
|
gCameraMovementFlags |= (CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
}
|
|
}
|
|
|
|
if (gCameraMovementFlags & CAM_MOVE_ENTERED_ROTATE_SURFACE) {
|
|
rotateSpeed = 0x200;
|
|
}
|
|
|
|
if (c->mode == CAMERA_MODE_OUTWARD_RADIAL) {
|
|
areaDistX = -areaDistX;
|
|
areaDistZ = -areaDistZ;
|
|
}
|
|
|
|
// Avoid obstructing walls
|
|
avoidStatus = rotate_camera_around_walls(c, c->pos, &avoidYaw, 0x400);
|
|
if (avoidStatus == 3) {
|
|
if (avoidYaw - atan2s(areaDistZ, areaDistX) + DEGREES(90) < 0) {
|
|
avoidYaw += DEGREES(180);
|
|
}
|
|
|
|
// We want to change sModeOffsetYaw so that the player is no longer obstructed by the wall.
|
|
// So, we make avoidYaw relative to the yaw around the area center
|
|
avoidYaw -= atan2s(areaDistZ, areaDistX);
|
|
|
|
// Bound avoid yaw to radial mode constraints
|
|
if (avoidYaw > DEGREES(105)) {
|
|
avoidYaw = DEGREES(105);
|
|
}
|
|
if (avoidYaw < DEGREES(-105)) {
|
|
avoidYaw = DEGREES(-105);
|
|
}
|
|
}
|
|
|
|
if (gCameraMovementFlags & CAM_MOVE_RETURN_TO_MIDDLE) {
|
|
if (camera_approach_s16_symmetric_bool(&sModeOffsetYaw, 0, rotateSpeed) == 0) {
|
|
gCameraMovementFlags &= ~CAM_MOVE_RETURN_TO_MIDDLE;
|
|
}
|
|
} else {
|
|
// Prevent the player from rotating into obstructing walls
|
|
if ((gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT) && avoidStatus == 3
|
|
&& avoidYaw + 0x10 < sModeOffsetYaw) {
|
|
sModeOffsetYaw = avoidYaw;
|
|
gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
}
|
|
if ((gCameraMovementFlags & CAM_MOVE_ROTATE_LEFT) && avoidStatus == 3
|
|
&& avoidYaw - 0x10 > sModeOffsetYaw) {
|
|
sModeOffsetYaw = avoidYaw;
|
|
gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
}
|
|
|
|
// If it's the first time rotating, just rotate to +-60 degrees
|
|
if (!(s2ndRotateFlags & CAM_MOVE_ROTATE_RIGHT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT)
|
|
&& camera_approach_s16_symmetric_bool(&sModeOffsetYaw, maxAreaYaw, rotateSpeed) == 0) {
|
|
gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
}
|
|
if (!(s2ndRotateFlags & CAM_MOVE_ROTATE_LEFT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_LEFT)
|
|
&& camera_approach_s16_symmetric_bool(&sModeOffsetYaw, minAreaYaw, rotateSpeed) == 0) {
|
|
gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
}
|
|
|
|
// If it's the second time rotating, rotate all the way to +-105 degrees.
|
|
if ((s2ndRotateFlags & CAM_MOVE_ROTATE_RIGHT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_RIGHT)
|
|
&& camera_approach_s16_symmetric_bool(&sModeOffsetYaw, DEGREES(105), rotateSpeed) == 0) {
|
|
gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_RIGHT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
s2ndRotateFlags &= ~CAM_MOVE_ROTATE_RIGHT;
|
|
}
|
|
if ((s2ndRotateFlags & CAM_MOVE_ROTATE_LEFT) && (gCameraMovementFlags & CAM_MOVE_ROTATE_LEFT)
|
|
&& camera_approach_s16_symmetric_bool(&sModeOffsetYaw, DEGREES(-105), rotateSpeed) == 0) {
|
|
gCameraMovementFlags &= ~(CAM_MOVE_ROTATE_LEFT | CAM_MOVE_ENTERED_ROTATE_SURFACE);
|
|
s2ndRotateFlags &= ~CAM_MOVE_ROTATE_LEFT;
|
|
}
|
|
}
|
|
if (!(gCameraMovementFlags & CAM_MOVE_ROTATE)) {
|
|
// If not rotating, rotate away from walls obscuring mario from view
|
|
if (avoidStatus == 3) {
|
|
approach_s16_asymptotic_bool(&sModeOffsetYaw, avoidYaw, 10);
|
|
} else {
|
|
if (c->mode == CAMERA_MODE_RADIAL) {
|
|
// sModeOffsetYaw only updates when mario is moving
|
|
rotateSpeed = gMarioStates[0].forwardVel / 32.f * 128.f;
|
|
camera_approach_s16_symmetric_bool(&sModeOffsetYaw, yawOffset, rotateSpeed);
|
|
}
|
|
if (c->mode == CAMERA_MODE_OUTWARD_RADIAL) {
|
|
sModeOffsetYaw = offset_yaw_outward_radial(c, atan2s(areaDistZ, areaDistX));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bound sModeOffsetYaw within (-120, 120) degrees
|
|
if (sModeOffsetYaw > 0x5554) {
|
|
sModeOffsetYaw = 0x5554;
|
|
}
|
|
if (sModeOffsetYaw < -0x5554) {
|
|
sModeOffsetYaw = -0x5554;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Moves lakitu from zoomed in to zoomed out and vice versa.
|
|
* When C-Down mode is not active, sLakituDist and sLakituPitch decrease to 0.
|
|
*/
|
|
void lakitu_zoom(f32 rangeDist, s16 rangePitch) {
|
|
if (sLakituDist < 0) {
|
|
if ((sLakituDist += 30) > 0) {
|
|
sLakituDist = 0;
|
|
}
|
|
} else if (rangeDist < sLakituDist) {
|
|
if ((sLakituDist -= 30) < rangeDist) {
|
|
sLakituDist = rangeDist;
|
|
}
|
|
} else if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
|
|
if ((sLakituDist += 30) > rangeDist) {
|
|
sLakituDist = rangeDist;
|
|
}
|
|
} else {
|
|
if ((sLakituDist -= 30) < 0) {
|
|
sLakituDist = 0;
|
|
}
|
|
}
|
|
|
|
if (gCurrLevelArea == AREA_SSL_PYRAMID && gCamera->mode == CAMERA_MODE_OUTWARD_RADIAL) {
|
|
rangePitch /= 2;
|
|
}
|
|
|
|
if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
|
|
if ((sLakituPitch += rangePitch / 13) > rangePitch) {
|
|
sLakituPitch = rangePitch;
|
|
}
|
|
} else {
|
|
if ((sLakituPitch -= rangePitch / 13) < 0) {
|
|
sLakituPitch = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void radial_camera_input_default(struct Camera *c) {
|
|
radial_camera_input(c, 0.f);
|
|
}
|
|
|
|
/**
|
|
* Makes lakitu cam's yaw match the angle turned towards in C-Up mode, and makes lakitu slowly fly back
|
|
* to the distance he was at before C-Up
|
|
*/
|
|
void update_yaw_and_dist_from_c_up(UNUSED struct Camera *c) {
|
|
f32 dist = 1000.f;
|
|
|
|
sModeOffsetYaw = sModeInfo.transitionStart.yaw - sAreaYaw;
|
|
sLakituDist = sModeInfo.transitionStart.dist - dist;
|
|
// No longer in C-Up
|
|
gCameraMovementFlags &= ~CAM_MOVING_INTO_MODE;
|
|
}
|
|
|
|
/**
|
|
* Handles input and updates for the radial camera mode
|
|
*/
|
|
void mode_radial_camera(struct Camera *c) {
|
|
Vec3f pos;
|
|
UNUSED u8 unused1[8];
|
|
s16 oldAreaYaw = sAreaYaw;
|
|
UNUSED u8 unused2[4];
|
|
|
|
if (gCameraMovementFlags & CAM_MOVING_INTO_MODE) {
|
|
update_yaw_and_dist_from_c_up(c);
|
|
}
|
|
|
|
radial_camera_input_default(c);
|
|
radial_camera_move(c);
|
|
|
|
if (c->mode == CAMERA_MODE_RADIAL) {
|
|
lakitu_zoom(400.f, 0x900);
|
|
}
|
|
c->nextYaw = update_radial_camera(c, c->focus, pos);
|
|
c->pos[0] = pos[0];
|
|
c->pos[2] = pos[2];
|
|
sAreaYawChange = sAreaYaw - oldAreaYaw;
|
|
if (sMarioCamState->action == ACT_RIDING_HOOT) {
|
|
pos[1] += 500.f;
|
|
}
|
|
set_camera_height(c, pos[1]);
|
|
pan_ahead_of_player(c);
|
|
}
|
|
|
|
/**
|
|
* A mode that only has 8 camera angles, 45 degrees apart
|
|
*/
|
|
void mode_8_directions_camera(struct Camera *c) {
|
|
Vec3f pos;
|
|
UNUSED u8 unused[8];
|
|
s16 oldAreaYaw = sAreaYaw;
|
|
|
|
radial_camera_input(c, 0.f);
|
|
|
|
if (gPlayer1Controller->buttonPressed & R_CBUTTONS) {
|
|
s8DirModeYawOffset += DEGREES(45);
|
|
play_sound_cbutton_side();
|
|
}
|
|
if (gPlayer1Controller->buttonPressed & L_CBUTTONS) {
|
|
s8DirModeYawOffset -= DEGREES(45);
|
|
play_sound_cbutton_side();
|
|
}
|
|
|
|
lakitu_zoom(400.f, 0x900);
|
|
c->nextYaw = update_8_directions_camera(c, c->focus, pos);
|
|
c->pos[0] = pos[0];
|
|
c->pos[2] = pos[2];
|
|
sAreaYawChange = sAreaYaw - oldAreaYaw;
|
|
set_camera_height(c, pos[1]);
|
|
}
|
|
|
|
/**
|
|
* Updates the camera in outward radial mode.
|
|
* sModeOffsetYaw is calculated in radial_camera_move, which calls offset_yaw_outward_radial
|
|
*/
|
|
s32 update_outward_radial_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
f32 xDistFocToMario = sMarioCamState->pos[0] - c->areaCenX;
|
|
f32 zDistFocToMario = sMarioCamState->pos[2] - c->areaCenZ;
|
|
s16 camYaw = atan2s(zDistFocToMario, xDistFocToMario) + sModeOffsetYaw + DEGREES(180);
|
|
s16 pitch = look_down_slopes(camYaw);
|
|
f32 baseDist = 1000.f;
|
|
// A base offset of 125.f is ~= mario's eye height
|
|
f32 yOff = 125.f;
|
|
f32 posY;
|
|
f32 focusY;
|
|
|
|
sAreaYaw = camYaw - sModeOffsetYaw - DEGREES(180);
|
|
calc_y_to_curr_floor(&posY, 1.f, 200.f, &focusY, 0.9f, 200.f);
|
|
focus_on_mario(focus, pos, posY + yOff, focusY + yOff, sLakituDist + baseDist, pitch, camYaw);
|
|
|
|
return camYaw;
|
|
}
|
|
|
|
/**
|
|
* Input and updates for the outward radial mode.
|
|
*/
|
|
void mode_outward_radial_camera(struct Camera *c) {
|
|
Vec3f pos;
|
|
s16 oldAreaYaw = sAreaYaw;
|
|
|
|
if (gCameraMovementFlags & CAM_MOVING_INTO_MODE) {
|
|
update_yaw_and_dist_from_c_up(c);
|
|
}
|
|
radial_camera_input_default(c);
|
|
radial_camera_move(c);
|
|
lakitu_zoom(400.f, 0x900);
|
|
c->nextYaw = update_outward_radial_camera(c, c->focus, pos);
|
|
c->pos[0] = pos[0];
|
|
c->pos[2] = pos[2];
|
|
sAreaYawChange = sAreaYaw - oldAreaYaw;
|
|
if (sMarioCamState->action == ACT_RIDING_HOOT) {
|
|
pos[1] += 500.f;
|
|
}
|
|
set_camera_height(c, pos[1]);
|
|
pan_ahead_of_player(c);
|
|
}
|
|
|
|
/**
|
|
* Move the camera in parallel tracking mode
|
|
*
|
|
* Uses the line between the next two points in sParTrackPath
|
|
* The camera can move forward/back and side to side, but it will face perpendicular to that line
|
|
*
|
|
* Although, annoyingly, it's not truly parallel, the function returns the yaw from the camera to mario,
|
|
* so mario will run slightly towards the camera.
|
|
*/
|
|
s32 update_parallel_tracking_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
Vec3f path[2];
|
|
Vec3f parMidPoint;
|
|
Vec3f marioOffset;
|
|
Vec3f camOffset;
|
|
/// Adjusts the focus to look where mario is facing. Unused since marioOffset is copied to focus
|
|
Vec3f focOffset;
|
|
s16 pathPitch;
|
|
s16 pathYaw;
|
|
UNUSED u8 filler[4];
|
|
f32 distThresh;
|
|
f32 zoom;
|
|
f32 camParDist;
|
|
UNUSED u8 filler2[8];
|
|
f32 pathLength;
|
|
UNUSED u8 filler3[8];
|
|
UNUSED f32 unusedScale = 0.5f;
|
|
f32 parScale = 0.5f;
|
|
f32 marioFloorDist;
|
|
Vec3f marioPos;
|
|
UNUSED u8 filler4[12];
|
|
UNUSED Vec3f unused4;
|
|
Vec3s pathAngle;
|
|
// Variables for changing to the next/prev path in the list
|
|
Vec3f oldPos;
|
|
Vec3f prevPathPos;
|
|
Vec3f nextPathPos;
|
|
f32 distToNext;
|
|
f32 distToPrev;
|
|
s16 prevPitch;
|
|
s16 nextPitch;
|
|
s16 prevYaw;
|
|
s16 nextYaw;
|
|
|
|
unused4[0] = 0.f;
|
|
unused4[1] = 0.f;
|
|
unused4[2] = 0.f;
|
|
|
|
// Store camera pos, for changing between paths
|
|
vec3f_copy(oldPos, pos);
|
|
|
|
vec3f_copy(path[0], sParTrackPath[sParTrackIndex].pos);
|
|
vec3f_copy(path[1], sParTrackPath[sParTrackIndex + 1].pos);
|
|
|
|
distThresh = sParTrackPath[sParTrackIndex].distThresh;
|
|
zoom = sParTrackPath[sParTrackIndex].zoom;
|
|
calc_y_to_curr_floor(&marioFloorDist, 1.f, 200.f, &marioFloorDist, 0.9f, 200.f);
|
|
|
|
marioPos[0] = sMarioCamState->pos[0];
|
|
// Mario's y pos + ~mario's height + mario's height above the floor
|
|
marioPos[1] = sMarioCamState->pos[1] + 150.f + marioFloorDist;
|
|
marioPos[2] = sMarioCamState->pos[2];
|
|
|
|
// Calculate middle of the path (parScale is 0.5f)
|
|
parMidPoint[0] = path[0][0] + (path[1][0] - path[0][0]) * parScale;
|
|
parMidPoint[1] = path[0][1] + (path[1][1] - path[0][1]) * parScale;
|
|
parMidPoint[2] = path[0][2] + (path[1][2] - path[0][2]) * parScale;
|
|
|
|
// Get direction of path
|
|
vec3f_get_dist_and_angle(path[0], path[1], &pathLength, &pathPitch, &pathYaw);
|
|
|
|
marioOffset[0] = marioPos[0] - parMidPoint[0];
|
|
marioOffset[1] = marioPos[1] - parMidPoint[1];
|
|
marioOffset[2] = marioPos[2] - parMidPoint[2];
|
|
|
|
// Make marioOffset point from the midpoint -> the start of the path
|
|
// Rotating by -yaw then -pitch moves the hor dist from the midpoint into marioOffset's z coordinate
|
|
// marioOffset[0] = the (perpendicular) horizontal distance from the path
|
|
// marioOffset[1] = the vertical distance from the path
|
|
// marioOffset[2] = the (parallel) horizontal distance from the path's midpoint
|
|
pathYaw = -pathYaw;
|
|
rotate_in_xz(marioOffset, marioOffset, pathYaw);
|
|
pathYaw = -pathYaw;
|
|
pathPitch = -pathPitch;
|
|
rotate_in_yz(marioOffset, marioOffset, pathPitch);
|
|
pathPitch = -pathPitch;
|
|
vec3f_copy(focOffset, marioOffset);
|
|
|
|
// OK
|
|
focOffset[0] = -focOffset[0] * 0.f;
|
|
focOffset[1] = focOffset[1] * 0.f;
|
|
|
|
// Repeat above calcs with camOffset
|
|
camOffset[0] = pos[0] - parMidPoint[0];
|
|
camOffset[1] = pos[1] - parMidPoint[1];
|
|
camOffset[2] = pos[2] - parMidPoint[2];
|
|
pathYaw = -pathYaw;
|
|
rotate_in_xz(camOffset, camOffset, pathYaw);
|
|
pathYaw = -pathYaw;
|
|
pathPitch = -pathPitch;
|
|
rotate_in_yz(camOffset, camOffset, pathPitch);
|
|
pathPitch = -pathPitch;
|
|
|
|
// If mario is distThresh units away from the camera along the path, move the camera
|
|
//! When distThresh != 0, it causes mario to move slightly towards the camera when running sideways
|
|
//! Set each ParallelTrackingPoint's distThresh to 0 to make Mario truly run parallel to the path
|
|
if (marioOffset[2] > camOffset[2]) {
|
|
if (marioOffset[2] - camOffset[2] > distThresh) {
|
|
camOffset[2] = marioOffset[2] - distThresh;
|
|
}
|
|
} else {
|
|
if (marioOffset[2] - camOffset[2] < -distThresh) {
|
|
camOffset[2] = marioOffset[2] + distThresh;
|
|
}
|
|
}
|
|
|
|
// If zoom != 0.0, the camera will move zoom% closer to mario
|
|
marioOffset[0] = -marioOffset[0] * zoom;
|
|
marioOffset[1] = marioOffset[1] * zoom;
|
|
marioOffset[2] = camOffset[2];
|
|
|
|
//! Does nothing because focOffset[0] is always 0
|
|
focOffset[0] *= 0.3f;
|
|
//! Does nothing because focOffset[1] is always 0
|
|
focOffset[1] *= 0.3f;
|
|
|
|
pathAngle[0] = pathPitch;
|
|
pathAngle[1] = pathYaw; //! No effect
|
|
|
|
// make marioOffset[2] == distance from the start of the path
|
|
marioOffset[2] = pathLength / 2 - marioOffset[2];
|
|
|
|
pathAngle[1] = pathYaw + DEGREES(180);
|
|
pathAngle[2] = 0;
|
|
|
|
// Rotate the offset in the direction of the path again
|
|
offset_rotated(pos, path[0], marioOffset, pathAngle);
|
|
vec3f_get_dist_and_angle(path[0], c->pos, &camParDist, &pathPitch, &pathYaw);
|
|
|
|
// Adjust the focus. Does nothing, focus is set to mario at the end
|
|
focOffset[2] = pathLength / 2 - focOffset[2];
|
|
offset_rotated(c->focus, path[0], focOffset, pathAngle);
|
|
|
|
// Changing paths, update the stored position offset
|
|
if (sStatusFlags & CAM_FLAG_CHANGED_PARTRACK_INDEX) {
|
|
sStatusFlags &= ~CAM_FLAG_CHANGED_PARTRACK_INDEX;
|
|
sParTrackTransOff.pos[0] = oldPos[0] - c->pos[0];
|
|
sParTrackTransOff.pos[1] = oldPos[1] - c->pos[1];
|
|
sParTrackTransOff.pos[2] = oldPos[2] - c->pos[2];
|
|
}
|
|
// Slowly transition to the next path
|
|
approach_f32_asymptotic_bool(&sParTrackTransOff.pos[0], 0.f, 0.025f);
|
|
approach_f32_asymptotic_bool(&sParTrackTransOff.pos[1], 0.f, 0.025f);
|
|
approach_f32_asymptotic_bool(&sParTrackTransOff.pos[2], 0.f, 0.025f);
|
|
vec3f_add(c->pos, sParTrackTransOff.pos);
|
|
|
|
// Check if the camera should go to the next path
|
|
if (sParTrackPath[sParTrackIndex + 1].startOfPath != 0) {
|
|
// get Mario's distance to the next path
|
|
calculate_angles(sParTrackPath[sParTrackIndex + 1].pos, sParTrackPath[sParTrackIndex + 2].pos, &nextPitch, &nextYaw);
|
|
vec3f_set_dist_and_angle(sParTrackPath[sParTrackIndex + 1].pos, nextPathPos, 400.f, nextPitch, nextYaw);
|
|
distToPrev = calc_abs_dist(marioPos, nextPathPos);
|
|
|
|
// get Mario's distance to the previous path
|
|
calculate_angles(sParTrackPath[sParTrackIndex + 1].pos, sParTrackPath[sParTrackIndex].pos, &prevPitch, &prevYaw);
|
|
vec3f_set_dist_and_angle(sParTrackPath[sParTrackIndex + 1].pos, prevPathPos, 400.f, prevPitch, prevYaw);
|
|
distToNext = calc_abs_dist(marioPos, prevPathPos);
|
|
if (distToPrev < distToNext) {
|
|
sParTrackIndex++;
|
|
sStatusFlags |= CAM_FLAG_CHANGED_PARTRACK_INDEX;
|
|
}
|
|
}
|
|
|
|
// Check if the camera should go to the previous path
|
|
if (sParTrackIndex != 0) {
|
|
// get Mario's distance to the next path
|
|
calculate_angles((*(sParTrackPath + sParTrackIndex)).pos, (*(sParTrackPath + sParTrackIndex + 1)).pos, &nextPitch, &nextYaw);
|
|
vec3f_set_dist_and_angle(sParTrackPath[sParTrackIndex].pos, nextPathPos, 700.f, nextPitch, nextYaw);
|
|
distToPrev = calc_abs_dist(marioPos, nextPathPos);
|
|
|
|
// get Mario's distance to the previous path
|
|
calculate_angles((*(sParTrackPath + sParTrackIndex)).pos, (*(sParTrackPath + sParTrackIndex - 1)).pos, &prevPitch, &prevYaw);
|
|
vec3f_set_dist_and_angle(sParTrackPath[sParTrackIndex].pos, prevPathPos, 700.f, prevPitch, prevYaw);
|
|
distToNext = calc_abs_dist(marioPos, prevPathPos);
|
|
if (distToPrev > distToNext) {
|
|
sParTrackIndex--;
|
|
sStatusFlags |= CAM_FLAG_CHANGED_PARTRACK_INDEX;
|
|
}
|
|
}
|
|
|
|
// Update the camera focus and return the camera's yaw
|
|
vec3f_copy(focus, marioPos);
|
|
vec3f_get_dist_and_angle(focus, pos, &camParDist, &pathPitch, &pathYaw);
|
|
return pathYaw;
|
|
}
|
|
|
|
/**
|
|
* Updates the camera during fixed mode.
|
|
*/
|
|
s32 update_fixed_camera(struct Camera *c, Vec3f focus, UNUSED Vec3f pos) {
|
|
f32 focusFloorOff;
|
|
f32 goalHeight;
|
|
f32 ceilHeight;
|
|
f32 heightOffset;
|
|
f32 distCamToFocus;
|
|
UNUSED u8 filler2[8];
|
|
f32 scaleToMario = 0.5f;
|
|
s16 pitch;
|
|
s16 yaw;
|
|
Vec3s faceAngle;
|
|
struct Surface *ceiling;
|
|
Vec3f basePos;
|
|
UNUSED u8 filler[12];
|
|
|
|
play_camera_buzz_if_c_sideways();
|
|
|
|
// Don't move closer to mario in these areas
|
|
switch (gCurrLevelArea) {
|
|
case AREA_RR:
|
|
scaleToMario = 0.f;
|
|
heightOffset = 0.f;
|
|
break;
|
|
|
|
case AREA_CASTLE_LOBBY:
|
|
scaleToMario = 0.3f;
|
|
heightOffset = 0.f;
|
|
break;
|
|
|
|
case AREA_BBH:
|
|
scaleToMario = 0.f;
|
|
heightOffset = 0.f;
|
|
break;
|
|
}
|
|
|
|
handle_c_button_movement(c);
|
|
play_camera_buzz_if_cdown();
|
|
|
|
calc_y_to_curr_floor(&focusFloorOff, 1.f, 200.f, &focusFloorOff, 0.9f, 200.f);
|
|
vec3f_copy(focus, sMarioCamState->pos);
|
|
focus[1] += focusFloorOff + 125.f;
|
|
vec3f_get_dist_and_angle(focus, c->pos, &distCamToFocus, &faceAngle[0], &faceAngle[1]);
|
|
faceAngle[2] = 0;
|
|
|
|
vec3f_copy(basePos, sFixedModeBasePosition);
|
|
vec3f_add(basePos, sCastleEntranceOffset);
|
|
|
|
if (sMarioGeometry.currFloorType != SURFACE_DEATH_PLANE
|
|
&& sMarioGeometry.currFloorHeight != -11000.f) {
|
|
goalHeight = sMarioGeometry.currFloorHeight + basePos[1] + heightOffset;
|
|
} else {
|
|
goalHeight = gLakituState.goalPos[1];
|
|
}
|
|
|
|
if (300 > distCamToFocus) {
|
|
goalHeight += 300 - distCamToFocus;
|
|
}
|
|
|
|
ceilHeight = find_ceil(c->pos[0], goalHeight - 100.f, c->pos[2], &ceiling);
|
|
if (ceilHeight != 20000.f) {
|
|
if (goalHeight > (ceilHeight -= 125.f)) {
|
|
goalHeight = ceilHeight;
|
|
}
|
|
}
|
|
|
|
if (sStatusFlags & CAM_FLAG_SMOOTH_MOVEMENT) {
|
|
camera_approach_f32_symmetric_bool(&c->pos[1], goalHeight, 15.f);
|
|
} else {
|
|
if (goalHeight < sMarioCamState->pos[1] - 500.f) {
|
|
goalHeight = sMarioCamState->pos[1] - 500.f;
|
|
}
|
|
c->pos[1] = goalHeight;
|
|
}
|
|
|
|
c->pos[0] = basePos[0] + (sMarioCamState->pos[0] - basePos[0]) * scaleToMario;
|
|
c->pos[2] = basePos[2] + (sMarioCamState->pos[2] - basePos[2]) * scaleToMario;
|
|
|
|
if (scaleToMario != 0.f) {
|
|
vec3f_get_dist_and_angle(c->focus, c->pos, &distCamToFocus, &pitch, &yaw);
|
|
if (distCamToFocus > 1000.f) {
|
|
distCamToFocus = 1000.f;
|
|
vec3f_set_dist_and_angle(c->focus, c->pos, distCamToFocus, pitch, yaw);
|
|
}
|
|
}
|
|
|
|
return faceAngle[1];
|
|
}
|
|
|
|
/**
|
|
* Updates the camera during a boss fight
|
|
*/
|
|
s32 update_boss_fight_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
struct Object *o;
|
|
UNUSED u8 filler2[12];
|
|
f32 focusDistance;
|
|
UNUSED u8 filler3[4];
|
|
// Floor normal values
|
|
f32 nx;
|
|
f32 ny;
|
|
f32 nz;
|
|
/// Floor originOffset
|
|
f32 oo;
|
|
UNUSED u8 filler4[4];
|
|
UNUSED s16 unused;
|
|
s16 yaw;
|
|
s16 heldState;
|
|
struct Surface *floor;
|
|
UNUSED u8 filler[20];
|
|
Vec3f secondFocus;
|
|
Vec3f holdFocOffset = { 0.f, -150.f, -125.f };
|
|
|
|
handle_c_button_movement(c);
|
|
|
|
// Start camera shakes if bowser jumps or gets thrown.
|
|
if (sMarioCamState->cameraEvent == CAM_EVENT_BOWSER_JUMP) {
|
|
set_environmental_camera_shake(SHAKE_ENV_BOWSER_JUMP);
|
|
sMarioCamState->cameraEvent = 0;
|
|
}
|
|
if (sMarioCamState->cameraEvent == CAM_EVENT_BOWSER_THROW_BOUNCE) {
|
|
set_environmental_camera_shake(SHAKE_ENV_BOWSER_THROW_BOUNCE);
|
|
sMarioCamState->cameraEvent = 0;
|
|
}
|
|
|
|
yaw = sModeOffsetYaw + DEGREES(45);
|
|
// Get boss's position and whether mario is holding it.
|
|
if ((o = gSecondCameraFocus) != NULL) {
|
|
object_pos_to_vec3f(secondFocus, o);
|
|
heldState = o->oHeldState;
|
|
} else {
|
|
// If no boss is there, just rotate around the area's center point.
|
|
secondFocus[0] = c->areaCenX;
|
|
secondFocus[1] = sMarioCamState->pos[1];
|
|
secondFocus[2] = c->areaCenZ;
|
|
heldState = 0;
|
|
}
|
|
|
|
focusDistance = calc_abs_dist(sMarioCamState->pos, secondFocus) * 1.6f;
|
|
if (focusDistance < 800.f) {
|
|
focusDistance = 800.f;
|
|
}
|
|
if (focusDistance > 5000.f) {
|
|
focusDistance = 5000.f;
|
|
}
|
|
|
|
// If holding the boss, add a slight offset to secondFocus so that the spinning is more pronounced.
|
|
if (heldState == 1) {
|
|
offset_rotated(secondFocus, sMarioCamState->pos, holdFocOffset, sMarioCamState->faceAngle);
|
|
}
|
|
|
|
// Set the camera focus to the average of mario and secondFocus
|
|
focus[0] = (sMarioCamState->pos[0] + secondFocus[0]) / 2.f;
|
|
focus[1] = (sMarioCamState->pos[1] + secondFocus[1]) / 2.f + 125.f;
|
|
focus[2] = (sMarioCamState->pos[2] + secondFocus[2]) / 2.f;
|
|
|
|
// Calculate the camera's position as an offset from the focus
|
|
// When C-Down is not active, this
|
|
vec3f_set_dist_and_angle(focus, pos, focusDistance, 0x1000, yaw);
|
|
// Find the floor of the arena
|
|
pos[1] = find_floor(c->areaCenX, 20000.f, c->areaCenZ, &floor);
|
|
if (floor != NULL) {
|
|
nx = floor->normal.x;
|
|
ny = floor->normal.y;
|
|
nz = floor->normal.z;
|
|
oo = floor->originOffset;
|
|
pos[1] = 300.f - (nx * pos[0] + nz * pos[2] + oo) / ny;
|
|
switch (gCurrLevelArea) {
|
|
case AREA_BOB:
|
|
pos[1] += 125.f;
|
|
//! fall through, makes the BoB boss fight camera move up twice as high as it should
|
|
case AREA_WF:
|
|
pos[1] += 125.f;
|
|
}
|
|
}
|
|
|
|
//! Must be same line to match EU
|
|
// Prevent the camera from going to the ground in the outside boss fight
|
|
if (gCurrLevelNum == LEVEL_BBH) { pos[1] = 2047.f; }
|
|
|
|
// Rotate from C-Button input
|
|
if (sCSideButtonYaw < 0) {
|
|
sModeOffsetYaw += 0x200;
|
|
if ((sCSideButtonYaw += 0x100) > 0) {
|
|
sCSideButtonYaw = 0;
|
|
}
|
|
}
|
|
if (sCSideButtonYaw > 0) {
|
|
sModeOffsetYaw -= 0x200;
|
|
if ((sCSideButtonYaw -= 0x100) < 0) {
|
|
sCSideButtonYaw = 0;
|
|
}
|
|
}
|
|
|
|
focus[1] = (sMarioCamState->pos[1] + secondFocus[1]) / 2.f + 100.f;
|
|
if (heldState == 1) {
|
|
focus[1] += 300.f * sins((gMarioStates[0].angleVel[1] > 0.f) ? gMarioStates[0].angleVel[1]
|
|
: -gMarioStates[0].angleVel[1]);
|
|
}
|
|
|
|
//! Unnecessary conditional, focusDistance is already bounded to 800
|
|
if (focusDistance < 400.f) {
|
|
focusDistance = 400.f;
|
|
}
|
|
|
|
// Set C-Down distance and pitch.
|
|
// C-Down will essentially double the distance from the center.
|
|
// sLakituPitch approaches 33.75 degrees.
|
|
lakitu_zoom(focusDistance, 0x1800);
|
|
|
|
// Move the camera position back as sLakituDist and sLakituPitch increase.
|
|
// This doesn't zoom out of bounds because pos is set above each frame.
|
|
// The constant 0x1000 doubles the pitch from the center when sLakituPitch is 0
|
|
// When lakitu is fully zoomed out, the pitch comes to 0x3800, or 78.75 degrees, up from the focus.
|
|
vec3f_set_dist_and_angle(pos, pos, sLakituDist, sLakituPitch + 0x1000, yaw);
|
|
|
|
return yaw;
|
|
}
|
|
|
|
// 2nd iteration of data
|
|
s16 unused8032D0A8[] = { 14, 1, 2, 4 };
|
|
s16 unused8032D0B0[] = { 16, 9, 17, 0 };
|
|
|
|
/**
|
|
* Maps cutscene to numbers in [0,4]. Used in determine_dance_cutscene() with sDanceCutsceneIndexTable.
|
|
*
|
|
* Only the first 5 entries are used. Perhaps the last 5 were bools used to indicate whether the star
|
|
* type exits the course or not.
|
|
*/
|
|
u8 sDanceCutsceneTable[] = {
|
|
CUTSCENE_DANCE_FLY_AWAY, CUTSCENE_DANCE_ROTATE, CUTSCENE_DANCE_CLOSEUP, CUTSCENE_KEY_DANCE, CUTSCENE_DANCE_DEFAULT,
|
|
FALSE, FALSE, FALSE, FALSE, TRUE,
|
|
};
|
|
|
|
/**
|
|
* Perhaps used by different dance cutscenes.
|
|
*/
|
|
struct UnusedDanceInfo {
|
|
Vec3f point;
|
|
f32 distTarget;
|
|
f32 distMultiplier;
|
|
};
|
|
|
|
struct UnusedDanceInfo unusedDanceInfo1 = {
|
|
{-3026.0f, 912.0f, -2148.0f},
|
|
600.0f,
|
|
0.3f
|
|
};
|
|
|
|
u32 unusedDanceType = 0;
|
|
struct UnusedDanceInfo unusedDanceInfo2 = {
|
|
{-4676.0f, 917.0f, -3802.0f},
|
|
600.0f,
|
|
0.3f
|
|
};
|
|
|
|
|
|
/**
|
|
* Table that dictates camera movement in bookend room.
|
|
* Due to only the X being varied in the table, this only moves along the X axis linearly.
|
|
* Third entry is seemingly unused.
|
|
*/
|
|
struct ParallelTrackingPoint sBBHLibraryParTrackPath[] = {
|
|
{ 1, { -929.0f, 1619.0f, -1490.0f }, 50.0f, 0.0f },
|
|
{ 0, { -2118.0f, 1619.0f, -1490.0f }, 50.0f, 0.0f },
|
|
{ 0, { 0.0f, 0.0f, 0.0f }, 0.0f, 0.0f },
|
|
};
|
|
|
|
s32 unused_update_mode_5_camera(UNUSED struct Camera *c, UNUSED Vec3f focus, UNUSED Vec3f pos) {
|
|
}
|
|
|
|
static void stub_camera_1(UNUSED s32 unused) {
|
|
}
|
|
|
|
void mode_boss_fight_camera(struct Camera *c) {
|
|
c->nextYaw = update_boss_fight_camera(c, c->focus, c->pos);
|
|
}
|
|
|
|
/**
|
|
* Parallel tracking mode, the camera faces perpendicular to a line defined by sParTrackPath
|
|
*
|
|
* @see update_parallel_tracking_camera
|
|
*/
|
|
void mode_parallel_tracking_camera(struct Camera *c) {
|
|
s16 dummy;
|
|
|
|
radial_camera_input(c, 0.f);
|
|
set_fov_function(CAM_FOV_DEFAULT);
|
|
c->nextYaw = update_parallel_tracking_camera(c, c->focus, c->pos);
|
|
camera_approach_s16_symmetric_bool(&dummy, 0, 0x0400);
|
|
}
|
|
|
|
/**
|
|
* Fixed camera mode, the camera rotates around a point and looks and zooms toward mario.
|
|
*/
|
|
void mode_fixed_camera(struct Camera *c) {
|
|
UNUSED u8 unused[8];
|
|
|
|
if (gCurrLevelNum == LEVEL_BBH) {
|
|
set_fov_function(CAM_FOV_BBH);
|
|
} else {
|
|
set_fov_function(CAM_FOV_APP_45);
|
|
}
|
|
c->nextYaw = update_fixed_camera(c, c->focus, c->pos);
|
|
c->yaw = c->nextYaw;
|
|
pan_ahead_of_player(c);
|
|
vec3f_set(sCastleEntranceOffset, 0.f, 0.f, 0.f);
|
|
}
|
|
|
|
/**
|
|
* Updates the camera in BEHIND_MARIO mode.
|
|
*
|
|
* The C-Buttons rotate the camera 90 degrees left/right and 67.5 degrees up/down.
|
|
*/
|
|
s32 update_behind_mario_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
UNUSED u8 unused2[12];
|
|
f32 dist;
|
|
UNUSED u8 unused3[4];
|
|
s16 absPitch;
|
|
s16 pitch;
|
|
s16 yaw;
|
|
s16 goalPitch = -sMarioCamState->faceAngle[0];
|
|
s16 marioYaw = sMarioCamState->faceAngle[1] + DEGREES(180);
|
|
s16 goalYawOff = 0;
|
|
s16 yawSpeed;
|
|
s16 pitchInc = 32;
|
|
UNUSED u8 unused[12];
|
|
f32 maxDist = 800.f;
|
|
f32 focYOff = 125.f;
|
|
|
|
// Zoom in when mario R_TRIG mode is active
|
|
if (sSelectionFlags & CAM_MODE_MARIO_ACTIVE) {
|
|
maxDist = 350.f;
|
|
focYOff = 120.f;
|
|
}
|
|
if (!(sMarioCamState->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER))) {
|
|
pitchInc = 128;
|
|
}
|
|
|
|
// Focus on mario
|
|
vec3f_copy(focus, sMarioCamState->pos);
|
|
c->focus[1] += focYOff;
|
|
//! @bug unnecessary
|
|
dist = calc_abs_dist(focus, pos);
|
|
//! @bug unnecessary
|
|
pitch = calculate_pitch(focus, pos);
|
|
vec3f_get_dist_and_angle(focus, pos, &dist, &pitch, &yaw);
|
|
if (dist > maxDist) {
|
|
dist = maxDist;
|
|
}
|
|
if ((absPitch = pitch) < 0) {
|
|
absPitch = -absPitch;
|
|
}
|
|
|
|
// Determine the yaw speed based on absPitch. A higher absPitch (further away from looking straight)
|
|
// translates to a slower speed
|
|
// Note: Pitch is always within +- 90 degrees or +-0x4000, and 0x4000 / 0x200 = 32
|
|
yawSpeed = 32 - absPitch / 0x200;
|
|
if (yawSpeed < 1) {
|
|
yawSpeed = 1;
|
|
}
|
|
if (yawSpeed > 32) {
|
|
yawSpeed = 32;
|
|
}
|
|
|
|
if (sCSideButtonYaw != 0) {
|
|
camera_approach_s16_symmetric_bool(&sCSideButtonYaw, 0, 1);
|
|
yawSpeed = 8;
|
|
}
|
|
if (sBehindMarioSoundTimer != 0) {
|
|
goalPitch = 0;
|
|
camera_approach_s16_symmetric_bool(&sBehindMarioSoundTimer, 0, 1);
|
|
pitchInc = 0x800;
|
|
}
|
|
|
|
if (sBehindMarioSoundTimer == 28) {
|
|
if (sCSideButtonYaw < 5 || sCSideButtonYaw > 28) {
|
|
play_sound_cbutton_up();
|
|
}
|
|
}
|
|
if (sCSideButtonYaw == 28) {
|
|
if (sBehindMarioSoundTimer < 5 || sBehindMarioSoundTimer > 28) {
|
|
play_sound_cbutton_up();
|
|
}
|
|
}
|
|
|
|
// C-Button input. Note: Camera rotates in the opposite direction of the button (airplane controls)
|
|
//! @bug C-Right and C-Up take precedence due to the way input is handled here
|
|
|
|
// Rotate right
|
|
if (sCButtonsPressed & L_CBUTTONS) {
|
|
if (gPlayer1Controller->buttonPressed & L_CBUTTONS) {
|
|
play_sound_cbutton_side();
|
|
}
|
|
if (dist < maxDist) {
|
|
camera_approach_f32_symmetric_bool(&dist, maxDist, 5.f);
|
|
}
|
|
goalYawOff = -0x3FF8;
|
|
sCSideButtonYaw = 30;
|
|
yawSpeed = 2;
|
|
}
|
|
// Rotate left
|
|
if (sCButtonsPressed & R_CBUTTONS) {
|
|
if (gPlayer1Controller->buttonPressed & R_CBUTTONS) {
|
|
play_sound_cbutton_side();
|
|
}
|
|
if (dist < maxDist) {
|
|
camera_approach_f32_symmetric_bool(&dist, maxDist, 5.f);
|
|
}
|
|
goalYawOff = 0x3FF8;
|
|
sCSideButtonYaw = 30;
|
|
yawSpeed = 2;
|
|
}
|
|
// Rotate up
|
|
if (sCButtonsPressed & D_CBUTTONS) {
|
|
if (gPlayer1Controller->buttonPressed & (U_CBUTTONS | D_CBUTTONS)) {
|
|
play_sound_cbutton_side();
|
|
}
|
|
if (dist < maxDist) {
|
|
camera_approach_f32_symmetric_bool(&dist, maxDist, 5.f);
|
|
}
|
|
goalPitch = -0x3000;
|
|
sBehindMarioSoundTimer = 30;
|
|
pitchInc = 0x800;
|
|
}
|
|
// Rotate down
|
|
if (sCButtonsPressed & U_CBUTTONS) {
|
|
if (gPlayer1Controller->buttonPressed & (U_CBUTTONS | D_CBUTTONS)) {
|
|
play_sound_cbutton_side();
|
|
}
|
|
if (dist < maxDist) {
|
|
camera_approach_f32_symmetric_bool(&dist, maxDist, 5.f);
|
|
}
|
|
goalPitch = 0x3000;
|
|
sBehindMarioSoundTimer = 30;
|
|
pitchInc = 0x800;
|
|
}
|
|
|
|
approach_s16_asymptotic_bool(&yaw, marioYaw + goalYawOff, yawSpeed);
|
|
camera_approach_s16_symmetric_bool(&pitch, goalPitch, pitchInc);
|
|
if (dist < 300.f) {
|
|
dist = 300.f;
|
|
}
|
|
vec3f_set_dist_and_angle(focus, pos, dist, pitch, yaw);
|
|
if (gCurrLevelArea == AREA_WDW_MAIN) {
|
|
yaw = clamp_positions_and_find_yaw(pos, focus, 4508.f, -3739.f, 4508.f, -3739.f);
|
|
}
|
|
if (gCurrLevelArea == AREA_THI_HUGE) {
|
|
yaw = clamp_positions_and_find_yaw(pos, focus, 8192.f, -8192.f, 8192.f, -8192.f);
|
|
}
|
|
if (gCurrLevelArea == AREA_THI_TINY) {
|
|
yaw = clamp_positions_and_find_yaw(pos, focus, 2458.f, -2458.f, 2458.f, -2458.f);
|
|
}
|
|
|
|
return yaw;
|
|
}
|
|
|
|
/**
|
|
* "Behind Mario" mode: used when mario is flying, on the water's surface, or shot from a cannon
|
|
*/
|
|
s32 mode_behind_mario(struct Camera *c) {
|
|
struct MarioState *marioState = &gMarioStates[0];
|
|
struct Surface *floor;
|
|
Vec3f newPos;
|
|
//! @bug oldPos is unused, see resolve_geometry_collisions
|
|
Vec3f oldPos;
|
|
f32 waterHeight;
|
|
f32 floorHeight;
|
|
f32 distCamToFocus;
|
|
s16 camPitch;
|
|
s16 camYaw;
|
|
s16 yaw;
|
|
|
|
vec3f_copy(oldPos, c->pos);
|
|
gCameraMovementFlags &= ~CAM_MOVING_INTO_MODE;
|
|
vec3f_copy(newPos, c->pos);
|
|
yaw = update_behind_mario_camera(c, c->focus, newPos);
|
|
c->pos[0] = newPos[0];
|
|
c->pos[2] = newPos[2];
|
|
|
|
// Keep the camera above the water surface if swimming
|
|
if (c->mode == CAMERA_MODE_WATER_SURFACE) {
|
|
floorHeight = find_floor(c->pos[0], c->pos[1], c->pos[2], &floor);
|
|
newPos[1] = marioState->waterLevel + 120;
|
|
if (newPos[1] < (floorHeight += 120.f)) {
|
|
newPos[1] = floorHeight;
|
|
}
|
|
}
|
|
approach_camera_height(c, newPos[1], 50.f);
|
|
waterHeight = find_water_level(c->pos[0], c->pos[2]) + 100.f;
|
|
if (c->pos[1] <= waterHeight) {
|
|
gCameraMovementFlags |= CAM_MOVE_SUBMERGED;
|
|
} else {
|
|
gCameraMovementFlags &= ~CAM_MOVE_SUBMERGED;
|
|
}
|
|
|
|
resolve_geometry_collisions(c->pos, oldPos);
|
|
// Prevent camera getting too far away
|
|
vec3f_get_dist_and_angle(c->focus, c->pos, &distCamToFocus, &camPitch, &camYaw);
|
|
if (distCamToFocus > 800.f) {
|
|
distCamToFocus = 800.f;
|
|
vec3f_set_dist_and_angle(c->focus, c->pos, distCamToFocus, camPitch, camYaw);
|
|
}
|
|
pan_ahead_of_player(c);
|
|
|
|
return yaw;
|
|
}
|
|
|
|
/**
|
|
* Update the camera in slide and hoot mode.
|
|
*
|
|
* In slide mode, keep the camera 800 units from mario
|
|
*/
|
|
s16 update_slide_camera(struct Camera *c) {
|
|
struct Surface *floor;
|
|
f32 floorHeight;
|
|
Vec3f pos;
|
|
f32 distCamToFocus;
|
|
f32 maxCamDist;
|
|
f32 pitchScale;
|
|
s16 camPitch;
|
|
s16 camYaw;
|
|
UNUSED struct MarioState *marioState = &gMarioStates[0];
|
|
s16 goalPitch = 0x1555;
|
|
s16 goalYaw = sMarioCamState->faceAngle[1] + DEGREES(180);
|
|
|
|
// Zoom in when inside the CCM shortcut
|
|
if (sStatusFlags & CAM_FLAG_CCM_SLIDE_SHORTCUT) {
|
|
sLakituDist = approach_f32(sLakituDist, -600.f, 20.f, 20.f);
|
|
} else {
|
|
sLakituDist = approach_f32(sLakituDist, 0.f, 20.f, 20.f);
|
|
}
|
|
|
|
// No C-Button input in this mode, notify the player with a buzzer
|
|
play_camera_buzz_if_cbutton();
|
|
|
|
// Focus on mario
|
|
vec3f_copy(c->focus, sMarioCamState->pos);
|
|
c->focus[1] += 50.f;
|
|
|
|
vec3f_get_dist_and_angle(c->focus, c->pos, &distCamToFocus, &camPitch, &camYaw);
|
|
maxCamDist = 800.f;
|
|
|
|
// In hoot mode, zoom further out and rotate faster
|
|
if (sMarioCamState->action == ACT_RIDING_HOOT) {
|
|
maxCamDist = 1000.f;
|
|
goalPitch = 0x2800;
|
|
camera_approach_s16_symmetric_bool(&camYaw, goalYaw, 0x100);
|
|
} else {
|
|
camera_approach_s16_symmetric_bool(&camYaw, goalYaw, 0x80);
|
|
}
|
|
camera_approach_s16_symmetric_bool(&camPitch, goalPitch, 0x100);
|
|
|
|
// Hoot mode
|
|
if (sMarioCamState->action != ACT_RIDING_HOOT && sMarioGeometry.currFloorType == SURFACE_DEATH_PLANE) {
|
|
vec3f_set_dist_and_angle(c->focus, pos, maxCamDist + sLakituDist, camPitch, camYaw);
|
|
c->pos[0] = pos[0];
|
|
c->pos[2] = pos[2];
|
|
camera_approach_f32_symmetric_bool(&c->pos[1], c->focus[1], 30.f);
|
|
vec3f_get_dist_and_angle(c->pos, c->focus, &distCamToFocus, &camPitch, &camYaw);
|
|
pitchScale = (distCamToFocus - maxCamDist + sLakituDist) / 10000.f;
|
|
if (pitchScale > 1.f) {
|
|
pitchScale = 1.f;
|
|
}
|
|
camPitch += 0x1000 * pitchScale;
|
|
vec3f_set_dist_and_angle(c->pos, c->focus, distCamToFocus, camPitch, camYaw);
|
|
|
|
// Slide mode
|
|
} else {
|
|
vec3f_set_dist_and_angle(c->focus, c->pos, maxCamDist + sLakituDist, camPitch, camYaw);
|
|
sStatusFlags |= CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
|
|
|
|
// Stay above the slide floor
|
|
floorHeight = find_floor(c->pos[0], c->pos[1] + 200.f, c->pos[2], &floor) + 125.f;
|
|
if (c->pos[1] < floorHeight) {
|
|
c->pos[1] = floorHeight;
|
|
}
|
|
// Stay closer than maxCamDist
|
|
vec3f_get_dist_and_angle(c->focus, c->pos, &distCamToFocus, &camPitch, &camYaw);
|
|
if (distCamToFocus > maxCamDist + sLakituDist) {
|
|
distCamToFocus = maxCamDist + sLakituDist;
|
|
vec3f_set_dist_and_angle(c->focus, c->pos, distCamToFocus, camPitch, camYaw);
|
|
}
|
|
}
|
|
|
|
camYaw = calculate_yaw(c->focus, c->pos);
|
|
return camYaw;
|
|
}
|
|
|
|
void mode_behind_mario_camera(struct Camera *c) {
|
|
c->nextYaw = mode_behind_mario(c);
|
|
}
|
|
|
|
s32 nop_update_water_camera(UNUSED struct Camera *c, UNUSED Vec3f focus, UNUSED Vec3f pos) {
|
|
}
|
|
|
|
/**
|
|
* Exactly the same as BEHIND_MARIO
|
|
*/
|
|
void mode_water_surface_camera(struct Camera *c) {
|
|
c->nextYaw = mode_behind_mario(c);
|
|
}
|
|
|
|
/**
|
|
* Used in sModeTransitions for CLOSE and FREE_ROAM mode
|
|
*/
|
|
s32 update_mario_camera(UNUSED struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
s16 yaw = sMarioCamState->faceAngle[1] + sModeOffsetYaw + DEGREES(180);
|
|
focus_on_mario(focus, pos, 125.f, 125.f, gCameraZoomDist, 0x05B0, yaw);
|
|
|
|
return sMarioCamState->faceAngle[1];
|
|
}
|
|
|
|
/**
|
|
* Update the camera in default, close, and free roam mode
|
|
*
|
|
* The camera moves behind mario, and can rotate all the way around
|
|
*/
|
|
s16 update_default_camera(struct Camera *c) {
|
|
Vec3f tempPos;
|
|
Vec3f cPos;
|
|
UNUSED u8 unused1[12];
|
|
struct Surface *marioFloor;
|
|
struct Surface *cFloor;
|
|
struct Surface *tempFloor;
|
|
struct Surface *ceil;
|
|
f32 camFloorHeight;
|
|
f32 tempFloorHeight;
|
|
f32 marioFloorHeight;
|
|
UNUSED u8 unused2[4];
|
|
f32 dist;
|
|
f32 zoomDist;
|
|
f32 waterHeight;
|
|
f32 gasHeight;
|
|
s16 avoidYaw;
|
|
s16 pitch;
|
|
s16 yaw;
|
|
s16 yawGoal = sMarioCamState->faceAngle[1] + DEGREES(180);
|
|
f32 posHeight;
|
|
f32 focHeight;
|
|
f32 distFromWater;
|
|
s16 tempPitch;
|
|
s16 tempYaw;
|
|
f32 xzDist;
|
|
UNUSED u8 unused4[4];
|
|
s16 nextYawVel;
|
|
s16 yawVel = 0;
|
|
f32 scale;
|
|
s32 avoidStatus = 0;
|
|
s32 closeToMario = 0;
|
|
f32 ceilHeight = find_ceil(gLakituState.goalPos[0],
|
|
gLakituState.goalPos[1],
|
|
gLakituState.goalPos[2], &ceil);
|
|
s16 yawDir;
|
|
|
|
handle_c_button_movement(c);
|
|
vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
|
|
|
|
// If C-Down is active, determine what distance the camera should be from mario
|
|
if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
|
|
//! In Mario mode, the camera is zoomed out further than in lakitu mode (1400 vs 1200)
|
|
if (set_cam_angle(0) == CAM_ANGLE_MARIO) {
|
|
zoomDist = gCameraZoomDist + 1050;
|
|
} else {
|
|
zoomDist = gCameraZoomDist + 400;
|
|
}
|
|
} else {
|
|
zoomDist = gCameraZoomDist;
|
|
}
|
|
|
|
if (sMarioCamState->action & ACT_FLAG_HANGING ||
|
|
sMarioCamState->action == ACT_RIDING_HOOT) {
|
|
zoomDist *= 0.8f;
|
|
set_handheld_shake(HAND_CAM_SHAKE_HANG_OWL);
|
|
}
|
|
|
|
// If not zooming out, only allow dist to decrease
|
|
if (sZoomAmount == 0.f) {
|
|
if (dist > zoomDist) {
|
|
if ((dist -= 50.f) < zoomDist) {
|
|
dist = zoomDist;
|
|
}
|
|
}
|
|
} else {
|
|
if ((sZoomAmount -= 30.f) < 0.f) {
|
|
sZoomAmount = 0.f;
|
|
}
|
|
if (dist > zoomDist) {
|
|
if ((dist -= 30.f) < zoomDist) {
|
|
dist = zoomDist;
|
|
}
|
|
}
|
|
if (dist < zoomDist) {
|
|
if ((dist += 30.f) > zoomDist) {
|
|
dist = zoomDist;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine how fast to rotate the camera
|
|
if (sCSideButtonYaw == 0) {
|
|
if (c->mode == CAMERA_MODE_FREE_ROAM) {
|
|
nextYawVel = 0xC0;
|
|
} else {
|
|
nextYawVel = 0x100;
|
|
}
|
|
if ((gPlayer1Controller->stickX != 0.f || gPlayer1Controller->stickY != 0.f) != 0) {
|
|
nextYawVel = 0x20;
|
|
}
|
|
} else {
|
|
if (sCSideButtonYaw < 0) {
|
|
yaw += 0x200;
|
|
}
|
|
if (sCSideButtonYaw > 0) {
|
|
yaw -= 0x200;
|
|
}
|
|
camera_approach_s16_symmetric_bool(&sCSideButtonYaw, 0, 0x100);
|
|
nextYawVel = 0;
|
|
}
|
|
sYawSpeed = 0x400;
|
|
xzDist = calc_hor_dist(sMarioCamState->pos, c->pos);
|
|
|
|
if (sStatusFlags & CAM_FLAG_BEHIND_MARIO_POST_DOOR) {
|
|
if (xzDist >= 250) {
|
|
sStatusFlags &= ~CAM_FLAG_BEHIND_MARIO_POST_DOOR;
|
|
}
|
|
if (ABS((sMarioCamState->faceAngle[1] - yaw) / 2) < 0x1800) {
|
|
sStatusFlags &= ~CAM_FLAG_BEHIND_MARIO_POST_DOOR;
|
|
yaw = sCameraYawAfterDoorCutscene + DEGREES(180);
|
|
dist = 800.f;
|
|
sStatusFlags |= CAM_FLAG_BLOCK_SMOOTH_MOVEMENT;
|
|
}
|
|
} else if (xzDist < 250) {
|
|
// Turn rapidly if very close to mario
|
|
c->pos[0] += (250 - xzDist) * sins(yaw);
|
|
c->pos[2] += (250 - xzDist) * coss(yaw);
|
|
if (sCSideButtonYaw == 0) {
|
|
nextYawVel = 0x1000;
|
|
sYawSpeed = 0;
|
|
vec3f_get_dist_and_angle(sMarioCamState->pos, c->pos, &dist, &pitch, &yaw);
|
|
}
|
|
closeToMario |= 1;
|
|
}
|
|
|
|
if (-16 < gPlayer1Controller->stickY) {
|
|
c->yaw = yaw;
|
|
}
|
|
|
|
calc_y_to_curr_floor(&posHeight, 1, 200, &focHeight, 0.9f, 200);
|
|
vec3f_copy(cPos, c->pos);
|
|
avoidStatus = rotate_camera_around_walls(c, cPos, &avoidYaw, 0x600);
|
|
// If a wall is blocking the view of mario, then rotate in the calculated direction
|
|
if (avoidStatus == 3) {
|
|
unusedFreeRoamWallYaw = avoidYaw;
|
|
sAvoidYawVel = yaw;
|
|
sStatusFlags |= CAM_FLAG_COLLIDED_WITH_WALL;
|
|
//! Does nothing
|
|
vec3f_get_dist_and_angle(sMarioCamState->pos, cPos, &xzDist, &tempPitch, &tempYaw);
|
|
// Rotate to avoid the wall
|
|
approach_s16_asymptotic_bool(&yaw, avoidYaw, 10);
|
|
//! Does nothing
|
|
vec3f_set_dist_and_angle(sMarioCamState->pos, cPos, xzDist, tempPitch, tempYaw);
|
|
sAvoidYawVel = (sAvoidYawVel - yaw) / 0x100;
|
|
} else {
|
|
if (gMarioStates[0].forwardVel == 0.f) {
|
|
if (sStatusFlags & CAM_FLAG_COLLIDED_WITH_WALL) {
|
|
if ((yawGoal - yaw) / 0x100 >= 0) {
|
|
yawDir = -1;
|
|
} else {
|
|
yawDir = 1;
|
|
}
|
|
if ((sAvoidYawVel > 0 && yawDir > 0) || (sAvoidYawVel < 0 && yawDir < 0)) {
|
|
yawVel = nextYawVel;
|
|
}
|
|
} else {
|
|
yawVel = nextYawVel;
|
|
}
|
|
} else {
|
|
if (nextYawVel == 0x1000) {
|
|
yawVel = nextYawVel;
|
|
}
|
|
sStatusFlags &= ~CAM_FLAG_COLLIDED_WITH_WALL;
|
|
}
|
|
|
|
// If a wall is near the camera, turn twice as fast
|
|
if (avoidStatus != 0) {
|
|
yawVel += yawVel;
|
|
}
|
|
// ...Unless the camera already rotated from being close to mario
|
|
if ((closeToMario & 1) && avoidStatus != 0) {
|
|
yawVel = 0;
|
|
}
|
|
if (yawVel != 0 && get_dialog_id() == -1) {
|
|
camera_approach_s16_symmetric_bool(&yaw, yawGoal, yawVel);
|
|
}
|
|
}
|
|
|
|
// Only zoom out if not obstructed by walls and lakitu hasn't collided with any
|
|
if (avoidStatus == 0 && !(sStatusFlags & CAM_FLAG_COLLIDED_WITH_WALL)) {
|
|
approach_f32_asymptotic_bool(&dist, zoomDist - 100.f, 0.05f);
|
|
}
|
|
vec3f_set_dist_and_angle(sMarioCamState->pos, cPos, dist, pitch, yaw);
|
|
cPos[1] += posHeight + 125.f;
|
|
|
|
// Move the camera away from walls and set the collision flag
|
|
if (collide_with_walls(cPos, 10.f, 80.f) != 0) {
|
|
sStatusFlags |= CAM_FLAG_COLLIDED_WITH_WALL;
|
|
}
|
|
|
|
c->focus[0] = sMarioCamState->pos[0];
|
|
c->focus[1] = sMarioCamState->pos[1] + 125.f + focHeight;
|
|
c->focus[2] = sMarioCamState->pos[2];
|
|
|
|
marioFloorHeight = 125.f + sMarioGeometry.currFloorHeight;
|
|
marioFloor = sMarioGeometry.currFloor;
|
|
camFloorHeight = find_floor(cPos[0], cPos[1] + 50.f, cPos[2], &cFloor) + 125.f;
|
|
for (scale = 0.1f; scale < 1.f; scale += 0.2f) {
|
|
scale_along_line(tempPos, cPos, sMarioCamState->pos, scale);
|
|
tempFloorHeight = find_floor(tempPos[0], tempPos[1], tempPos[2], &tempFloor) + 125.f;
|
|
if (tempFloor != NULL && tempFloorHeight > marioFloorHeight) {
|
|
marioFloorHeight = tempFloorHeight;
|
|
marioFloor = tempFloor;
|
|
}
|
|
}
|
|
|
|
// Lower the camera in mario mode
|
|
if (sSelectionFlags & CAM_MODE_MARIO_ACTIVE) {
|
|
marioFloorHeight -= 35.f;
|
|
camFloorHeight -= 35.f;
|
|
c->focus[1] -= 25.f;
|
|
}
|
|
|
|
// If there's water below the camera, decide whether to keep the camera above the water surface
|
|
waterHeight = find_water_level(cPos[0], cPos[2]);
|
|
if (waterHeight != -11000.f) {
|
|
waterHeight += 125.f;
|
|
distFromWater = waterHeight - marioFloorHeight;
|
|
if (!(gCameraMovementFlags & CAM_MOVE_METAL_BELOW_WATER)) {
|
|
if (distFromWater > 800.f && (sMarioCamState->action & ACT_FLAG_METAL_WATER)) {
|
|
gCameraMovementFlags |= CAM_MOVE_METAL_BELOW_WATER;
|
|
}
|
|
} else {
|
|
if (distFromWater < 400.f || !(sMarioCamState->action & ACT_FLAG_METAL_WATER)) {
|
|
gCameraMovementFlags &= ~CAM_MOVE_METAL_BELOW_WATER;
|
|
}
|
|
}
|
|
// If not wearing the metal cap, always stay above
|
|
if (!(gCameraMovementFlags & CAM_MOVE_METAL_BELOW_WATER) && camFloorHeight < waterHeight) {
|
|
camFloorHeight = waterHeight;
|
|
}
|
|
} else {
|
|
gCameraMovementFlags &= ~CAM_MOVE_METAL_BELOW_WATER;
|
|
}
|
|
|
|
cPos[1] = camFloorHeight;
|
|
vec3f_copy(tempPos, cPos);
|
|
tempPos[1] -= 125.f;
|
|
if (marioFloor != NULL && camFloorHeight <= marioFloorHeight) {
|
|
avoidStatus = is_range_behind_surface(c->focus, tempPos, marioFloor, 0, -1);
|
|
if (avoidStatus != 1 && ceilHeight > marioFloorHeight) {
|
|
camFloorHeight = marioFloorHeight;
|
|
}
|
|
}
|
|
|
|
posHeight = 0.f;
|
|
if (c->mode == CAMERA_MODE_FREE_ROAM) {
|
|
if (gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) {
|
|
posHeight = 375.f;
|
|
if (gCurrLevelArea == AREA_SSL_PYRAMID) {
|
|
posHeight /= 2;
|
|
}
|
|
} else {
|
|
posHeight = 100.f;
|
|
}
|
|
}
|
|
if ((gCameraMovementFlags & CAM_MOVE_ZOOMED_OUT) && (sSelectionFlags & CAM_MODE_MARIO_ACTIVE)) {
|
|
posHeight = 610.f;
|
|
if (gCurrLevelArea == AREA_SSL_PYRAMID || gCurrLevelNum == LEVEL_CASTLE) {
|
|
posHeight /= 2;
|
|
}
|
|
}
|
|
|
|
// Make lakitu fly above the gas
|
|
gasHeight = find_poison_gas_level(cPos[0], cPos[2]);
|
|
if (gasHeight != -11000.f) {
|
|
if ((gasHeight += 130.f) > c->pos[1]) {
|
|
c->pos[1] = gasHeight;
|
|
}
|
|
}
|
|
|
|
if (sMarioCamState->action & ACT_FLAG_HANGING || sMarioCamState->action == ACT_RIDING_HOOT) {
|
|
camFloorHeight = sMarioCamState->pos[1] + 400.f;
|
|
if (c->mode == CAMERA_MODE_FREE_ROAM) {
|
|
camFloorHeight -= 100.f;
|
|
}
|
|
ceilHeight = 20000.f;
|
|
vec3f_copy(c->focus, sMarioCamState->pos);
|
|
}
|
|
|
|
if (sMarioCamState->action & ACT_FLAG_ON_POLE) {
|
|
camFloorHeight = gMarioStates[0].usedObj->oPosY + 125.f;
|
|
if (sMarioCamState->pos[1] - 100.f > camFloorHeight) {
|
|
camFloorHeight = sMarioCamState->pos[1] - 100.f;
|
|
}
|
|
ceilHeight = 20000.f;
|
|
vec3f_copy(c->focus, sMarioCamState->pos);
|
|
}
|
|
if (camFloorHeight != -11000.f) {
|
|
camFloorHeight += posHeight;
|
|
approach_camera_height(c, camFloorHeight, 20.f);
|
|
}
|
|
c->pos[0] = cPos[0];
|
|
c->pos[2] = cPos[2];
|
|
cPos[0] = gLakituState.goalPos[0];
|
|
cPos[1] = c->pos[1];
|
|
cPos[2] = gLakituState.goalPos[2];
|
|
vec3f_get_dist_and_angle(cPos, c->pos, &dist, &tempPitch, &tempYaw);
|
|
// Prevent the camera from lagging behind too much
|
|
if (dist > 50.f) {
|
|
dist = 50.f;
|
|
vec3f_set_dist_and_angle(cPos, c->pos, dist, tempPitch, tempYaw);
|
|
}
|
|
if (sMarioGeometry.currFloorType != SURFACE_DEATH_PLANE) {
|
|
vec3f_get_dist_and_angle(c->focus, c->pos, &dist, &tempPitch, &tempYaw);
|
|
if (dist > zoomDist) {
|
|
dist = zoomDist;
|
|
vec3f_set_dist_and_angle(c->focus, c->pos, dist, tempPitch, tempYaw);
|
|
}
|
|
}
|
|
if (ceilHeight != 20000.f) {
|
|
if (c->pos[1] > (ceilHeight -= 150.f)
|
|
&& (avoidStatus = is_range_behind_surface(c->pos, sMarioCamState->pos, ceil, 0, -1)) == 1) {
|
|
c->pos[1] = ceilHeight;
|
|
}
|
|
}
|
|
if (gCurrLevelArea == AREA_WDW_TOWN) {
|
|
yaw = clamp_positions_and_find_yaw(c->pos, c->focus, 2254.f, -3789.f, 3790.f, -2253.f);
|
|
}
|
|
return yaw;
|
|
}
|
|
|
|
/**
|
|
* The default camera mode
|
|
* Used by close and free roam modes
|
|
*/
|
|
void mode_default_camera(struct Camera *c) {
|
|
set_fov_function(CAM_FOV_DEFAULT);
|
|
c->nextYaw = update_default_camera(c);
|
|
pan_ahead_of_player(c);
|
|
}
|
|
|
|
/**
|
|
* The mode used by close and free roam
|
|
*/
|
|
void mode_lakitu_camera(struct Camera *c) {
|
|
gCameraZoomDist = 800.f;
|
|
mode_default_camera(c);
|
|
}
|
|
|
|
/**
|
|
* When no other mode is active and the current R button mode is mario
|
|
*/
|
|
void mode_mario_camera(struct Camera *c) {
|
|
gCameraZoomDist = 350.f;
|
|
mode_default_camera(c);
|
|
}
|
|
|
|
/**
|
|
* Rotates the camera around the spiral staircase.
|
|
*/
|
|
s32 update_spiral_stairs_camera(struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
UNUSED s16 unused1;
|
|
/// The returned yaw
|
|
s16 camYaw;
|
|
// unused
|
|
s16 focPitch;
|
|
/// The focus (mario)'s yaw around the stairs
|
|
s16 focYaw;
|
|
// unused
|
|
s16 posPitch;
|
|
/// The camera's yaw around the stairs
|
|
s16 posYaw;
|
|
UNUSED s32 unused2;
|
|
Vec3f cPos;
|
|
Vec3f checkPos;
|
|
struct Surface *floor;
|
|
// unused
|
|
f32 dist;
|
|
f32 focusHeight;
|
|
f32 floorHeight;
|
|
f32 focY;
|
|
|
|
handle_c_button_movement(c);
|
|
// Set base pos to the center of the staircase
|
|
vec3f_set(sFixedModeBasePosition, -1280.f, 614.f, 1740.f);
|
|
|
|
// Focus on mario, and move the focus up the staircase with him
|
|
calc_y_to_curr_floor(&focusHeight, 1.f, 200.f, &focusHeight, 0.9f, 200.f);
|
|
focus[0] = sMarioCamState->pos[0];
|
|
focY = sMarioCamState->pos[1] + 125.f + focusHeight;
|
|
focus[2] = sMarioCamState->pos[2];
|
|
|
|
vec3f_copy(cPos, pos);
|
|
vec3f_get_dist_and_angle(sFixedModeBasePosition, focus, &dist, &focPitch, &focYaw);
|
|
vec3f_get_dist_and_angle(sFixedModeBasePosition, cPos, &dist, &posPitch, &posYaw);
|
|
|
|
sSpiralStairsYawOffset = posYaw - focYaw;
|
|
// posYaw will change if mario is more than 90 degrees around the stairs, relative to the camera
|
|
if (sSpiralStairsYawOffset < DEGREES(-90)) {
|
|
sSpiralStairsYawOffset = DEGREES(-90);
|
|
}
|
|
if (sSpiralStairsYawOffset > DEGREES(90)) {
|
|
sSpiralStairsYawOffset = DEGREES(90);
|
|
}
|
|
focYaw += sSpiralStairsYawOffset;
|
|
posYaw = focYaw;
|
|
//! @bug unnecessary
|
|
camera_approach_s16_symmetric_bool(&posYaw, focYaw, 0x1000);
|
|
|
|
vec3f_set_dist_and_angle(sFixedModeBasePosition, cPos, 300.f, 0, posYaw);
|
|
|
|
// Move the camera's y coord up/down the staircase
|
|
checkPos[0] = focus[0] + (cPos[0] - focus[0]) * 0.7f;
|
|
checkPos[1] = focus[1] + (cPos[1] - focus[1]) * 0.7f + 300.f;
|
|
checkPos[2] = focus[2] + (cPos[2] - focus[2]) * 0.7f;
|
|
floorHeight = find_floor(checkPos[0], checkPos[1] + 50.f, checkPos[2], &floor);
|
|
|
|
if (floorHeight != -11000.f) {
|
|
if (floorHeight < sMarioGeometry.currFloorHeight) {
|
|
floorHeight = sMarioGeometry.currFloorHeight;
|
|
}
|
|
pos[1] = approach_f32(pos[1], (floorHeight += 125.f), 30.f, 30.f);
|
|
}
|
|
|
|
camera_approach_f32_symmetric_bool(&focus[1], focY, 30.f);
|
|
pos[0] = cPos[0];
|
|
pos[2] = cPos[2];
|
|
camYaw = calculate_yaw(focus, pos);
|
|
|
|
return camYaw;
|
|
}
|
|
|
|
/**
|
|
* The mode used in the spiral staircase in the castle
|
|
*/
|
|
void mode_spiral_stairs_camera(struct Camera *c) {
|
|
c->nextYaw = update_spiral_stairs_camera(c, c->focus, c->pos);
|
|
}
|
|
|
|
s32 update_slide_or_0f_camera(UNUSED struct Camera *c, Vec3f focus, Vec3f pos) {
|
|
s16 yaw = sMarioCamState->faceAngle[1] + sModeOffsetYaw + DEGREES(180);
|
|
|
|
focus_on_mario(focus, pos, 125.f, 125.f, 800.f, 5461, yaw);
|
|
return sMarioCamState->faceAngle[1];
|
|
}
|
|
|
|
static UNUSED void unused_mode_0f_camera(struct Camera *c) {
|
|
if (gPlayer1Controller->buttonPressed & U_CBUTTONS) {
|
|
gCameraMovementFlags |= CAM_MOVE_C_UP_MODE;
|
|
}
|
|
c->nextYaw = update_slide_camera(c);
|
|
}
|
|
|
|
/**
|
|
* Slide/hoot mode.
|
|
* In this mode, the camera is always at the back of mario, because mario generally only moves forward.
|
|
*/
|
|
void mode_slide_camera(struct Camera *c) {
|
|
if (sMarioGeometry.currFloorType == SURFACE_CLOSE_CAMERA ||
|
|
sMarioGeometry.currFloorType == SURFACE_NO_CAM_COL_SLIPPERY) {
|
|
mode_lakitu_camera(c);
|
|
} else {
|
|
if (gPlayer1Controller->buttonPressed & U_CBUTTONS) {
|
|
gCameraMovementFlags |= CAM_MOVE_C_UP_MODE;
|
|
}
|
|
c->nextYaw = update_slide_camera(c);
|
|
}
|
|
}
|
|
|
|
void store_lakitu_cam_info_for_c_up(struct Camera *c) {
|
|
vec3f_copy(sCameraStoreCUp.pos, c->pos);
|
|
vec3f_sub(sCameraStoreCUp.pos, sMarioCamState->pos);
|
|
// Only store the y value, and as an offset from mario, for some reason
|
|
vec3f_set(sCameraStoreCUp.focus, 0.f, c->focus[1] - sMarioCamState->pos[1], 0.f);
|
|
}
|
|
|
|
/**
|
|
* Start C-Up mode. The actual mode change is handled in update_mario_inputs() in mario.c
|
|
*
|
|
* @see update_mario_inputs
|
|
*/
|
|
s32 set_mode_c_up(struct Camera *c) {
|
|
if (!(gCameraMovementFlags & CAM_MOVE_C_UP_MODE)) {
|
|
gCameraMovementFlags |= CAM_MOVE_C_UP_MODE;
|
|
store_lakitu_cam_info_for_c_up(c);
|
|
sCameraSoundFlags &= ~CAM_SOUND_C_UP_PLAYED;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Zoom the camera out of C-Up mode, avoiding moving into a wall, if possible, by searching for an open
|
|
* direction.
|
|
*/
|
|
s32 exit_c_up(struct Camera *c) {
|
|
struct Surface *surface;
|
|
Vec3f checkFoc;
|
|
Vec3f curPos;
|
|
// Variables for searching for an open direction
|
|
s32 searching = 0;
|
|
/// The current sector of the circle that we are checking
|
|
s32 sector;
|
|
f32 ceilHeight;
|
|
f32 floorHeight;
|
|
f32 curDist;
|
|
f32 d;
|
|
s16 curPitch;
|
|
s16 curYaw;
|
|
s16 checkYaw = 0;
|
|
Vec3f storePos; // unused
|
|
Vec3f storeFoc; // unused
|
|
|
|
if ((gCameraMovementFlags & CAM_MOVE_C_UP_MODE) && !(gCameraMovementFlags & CAM_MOVE_STARTED_EXITING_C_UP)) {
|
|
// Copy the stored pos and focus. This is unused.
|
|
vec3f_copy(storePos, sCameraStoreCUp.pos);
|
|
vec3f_add(storePos, sMarioCamState->pos);
|
|
vec3f_copy(storeFoc, sCameraStoreCUp.focus);
|
|
vec3f_add(storeFoc, sMarioCamState->pos);
|
|
|
|
vec3f_copy(checkFoc, c->focus);
|
|
checkFoc[0] = sMarioCamState->pos[0];
|
|
checkFoc[2] = sMarioCamState->pos[2];
|
|
vec3f_get_dist_and_angle(checkFoc, c->pos, &curDist, &curPitch, &curYaw);
|
|
vec3f_copy(curPos, c->pos);
|
|
curDist = 80.f;
|
|
|
|
// Search for an open direction to zoom out in, if the camera is changing to close, free roam,
|
|
// or spiral-stairs mode
|
|
if (sModeInfo.< |