From 5f98f4ee6a26a3206ceae2b83e6ed5c13b573262 Mon Sep 17 00:00:00 2001 From: lahm86 <33758420+lahm86@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:33:53 +0100 Subject: [PATCH] camera: add photo mode --- meson.build | 1 + src/game/camera.c | 112 ++++++++++++++++++++++++++++++ src/game/phase/phase.c | 5 ++ src/game/phase/phase.h | 1 + src/game/phase/phase_game.c | 3 + src/game/phase/phase_photo_mode.c | 77 ++++++++++++++++++++ src/game/phase/phase_photo_mode.h | 5 ++ src/global/types.h | 3 + src/specific/s_input.c | 6 ++ 9 files changed, 213 insertions(+) create mode 100644 src/game/phase/phase_photo_mode.c create mode 100644 src/game/phase/phase_photo_mode.h diff --git a/meson.build b/meson.build index df8f093c9..397089b3e 100644 --- a/meson.build +++ b/meson.build @@ -253,6 +253,7 @@ sources = [ 'src/game/phase/phase_game.c', 'src/game/phase/phase_inventory.c', 'src/game/phase/phase_pause.c', + 'src/game/phase/phase_photo_mode.c', 'src/game/phase/phase_picture.c', 'src/game/phase/phase_stats.c', 'src/game/random.c', diff --git a/src/game/camera.c b/src/game/camera.c index 1a1722a5c..9f799ee40 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -5,6 +5,7 @@ #include "game/items.h" #include "game/los.h" #include "game/music.h" +#include "game/phase/phase.h" #include "game/random.h" #include "game/room.h" #include "game/sound.h" @@ -12,6 +13,7 @@ #include "global/const.h" #include "global/vars.h" #include "math/math.h" +#include "math/math_misc.h" #include "math/matrix.h" #include @@ -20,11 +22,31 @@ #include #include +#define PHOTO_AXIS_SHIFT (STEP_L / 2) +#define PHOTO_ROT_SHIFT (PHD_DEGREE * 3) +#define PHOTO_CLAMP (STEP_L + 50) + +#define CAM_INPUT_SHIFT(neg, pos) \ + ((g_Input.neg ? -1 : 0) + (g_Input.pos ? 1 : 0)) * PHOTO_AXIS_SHIFT + +#define SHIFT_POS(a, b) \ + do { \ + a.x += b.x; \ + a.y += b.y; \ + a.z += b.z; \ + } while (false) + // Camera speed option ranges from 1-10, so index 0 is unused. static double m_ManualCameraMultiplier[11] = { 1.0, .5, .625, .75, .875, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, }; +static bool m_PhotoMode = false; +static CAMERA_INFO m_OldCamera = { 0 }; + +static void M_UpdatePhotoMode(void); +static void M_ExitPhotoMode(void); + static bool M_BadPosition(int32_t x, int32_t y, int32_t z, int16_t room_num); static int32_t M_ShiftClamp(GAME_VECTOR *pos, int32_t clamp); static void M_SmartShift( @@ -467,6 +489,7 @@ void Camera_ResetPosition(void) void Camera_Initialise(void) { + m_PhotoMode = false; Camera_ResetPosition(); Camera_Update(); } @@ -616,8 +639,97 @@ void Camera_Fixed(void) } } +static void M_UpdatePhotoMode(void) +{ + if (!m_PhotoMode) { + m_OldCamera = g_Camera; + m_PhotoMode = true; + } + + PHD_ANGLE angles[2]; + Math_GetVectorAngles( + g_Camera.target.x - g_Camera.pos.x, g_Camera.target.y - g_Camera.pos.y, + g_Camera.target.z - g_Camera.pos.z, angles); + g_Camera.target_angle = angles[0]; + // [1] is x_rot + + XYZ_16 shift = { + .x = CAM_INPUT_SHIFT(camera_left, camera_right), + .y = CAM_INPUT_SHIFT(photo_mode_down, photo_mode_up), + .z = CAM_INPUT_SHIFT(camera_down, camera_up), + }; + + const DIRECTION direction = + (uint16_t)(g_Camera.target_angle + PHD_45) / PHD_90; + switch (direction) { + case DIR_EAST: { + int16_t temp; + SWAP(shift.x, shift.z, temp); + shift.z *= -1; + break; + } + case DIR_SOUTH: { + shift.z *= -1; + shift.x *= -1; + break; + } + case DIR_WEST: { + int16_t temp; + SWAP(shift.x, shift.z, temp); + shift.x *= -1; + break; + } + default: + break; + } + + SHIFT_POS(g_Camera.pos, shift); + SHIFT_POS(g_Camera.target, shift); + + if (g_Input.left) { + g_Camera.target_angle -= PHOTO_ROT_SHIFT; + } else if (g_Input.right) { + g_Camera.target_angle += PHOTO_ROT_SHIFT; + } + + if (g_Input.forward) { + g_Camera.target.y += PHOTO_AXIS_SHIFT; + } else if (g_Input.back) { + g_Camera.target.y -= PHOTO_AXIS_SHIFT; + } + + if (g_Input.left || g_Input.right || g_Input.forward || g_Input.back) { + const int32_t distance = + g_Camera.target_distance * Math_Cos(g_Camera.target_elevation) + >> W2V_SHIFT; + g_Camera.target_square = SQUARE(distance); + + const PHD_ANGLE angle = g_Camera.target_angle; + g_Camera.pos.x = + g_Camera.target.x - (distance * Math_Sin(angle) >> W2V_SHIFT); + g_Camera.pos.z = + g_Camera.target.z - (distance * Math_Cos(angle) >> W2V_SHIFT); + } + + g_Camera.pos.y += M_ShiftClamp(&g_Camera.pos, PHOTO_CLAMP); + g_Camera.target.y += M_ShiftClamp(&g_Camera.target, PHOTO_CLAMP); +} + +static void M_ExitPhotoMode(void) +{ + g_Camera = m_OldCamera; + m_PhotoMode = false; +} + void Camera_Update(void) { + if (Phase_Get() == PHASE_PHOTO_MODE) { + M_UpdatePhotoMode(); + return; + } else if (m_PhotoMode) { + M_ExitPhotoMode(); + } + if (g_Camera.type == CAM_CINEMATIC) { M_LoadCutsceneFrame(); return; diff --git a/src/game/phase/phase.c b/src/game/phase/phase.c index 5edce5f4c..2571560fc 100644 --- a/src/game/phase/phase.c +++ b/src/game/phase/phase.c @@ -8,6 +8,7 @@ #include "game/phase/phase_game.h" #include "game/phase/phase_inventory.h" #include "game/phase/phase_pause.h" +#include "game/phase/phase_photo_mode.h" #include "game/phase/phase_picture.h" #include "game/phase/phase_stats.h" #include "global/types.h" @@ -93,6 +94,10 @@ static void M_SetUnconditionally(const PHASE phase, void *arg) case PHASE_INVENTORY: m_Phaser = &g_InventoryPhaser; break; + + case PHASE_PHOTO_MODE: + m_Phaser = &g_PhotoModePhaser; + break; } if (m_Phaser && m_Phaser->start) { diff --git a/src/game/phase/phase.h b/src/game/phase/phase.h index e7d90b5ca..59e387f25 100644 --- a/src/game/phase/phase.h +++ b/src/game/phase/phase.h @@ -28,6 +28,7 @@ typedef enum PHASE { PHASE_PICTURE, PHASE_STATS, PHASE_INVENTORY, + PHASE_PHOTO_MODE, } PHASE; typedef struct PHASER { diff --git a/src/game/phase/phase_game.c b/src/game/phase/phase_game.c index fa61b40c3..9ace87f4b 100644 --- a/src/game/phase/phase_game.c +++ b/src/game/phase/phase_game.c @@ -106,6 +106,9 @@ static PHASE_CONTROL M_Control(int32_t nframes) if (!g_Lara.death_timer && g_InputDB.pause) { Phase_Set(PHASE_PAUSE, NULL); return (PHASE_CONTROL) { .end = false }; + } else if (g_InputDB.toggle_photo_mode) { + Phase_Set(PHASE_PHOTO_MODE, NULL); + return (PHASE_CONTROL) { .end = false }; } else { Item_Control(); Effect_Control(); diff --git a/src/game/phase/phase_photo_mode.c b/src/game/phase/phase_photo_mode.c new file mode 100644 index 000000000..20f10902f --- /dev/null +++ b/src/game/phase/phase_photo_mode.c @@ -0,0 +1,77 @@ +#include "game/phase/phase_photo_mode.h" + +#include "game/camera.h" +#include "game/game.h" +#include "game/input.h" +#include "game/interpolation.h" +#include "game/music.h" +#include "game/overlay.h" +#include "game/sound.h" +#include "game/text.h" +#include "global/vars.h" + +static TEXTSTRING *m_PhotoText = NULL; + +static void M_Start(void *arg); +static void M_End(void); +static PHASE_CONTROL M_Control(int32_t nframes); +static void M_Draw(void); + +static void M_Start(void *arg) +{ + g_OldInputDB = g_Input; + + Overlay_HideGameInfo(); + Music_Pause(); + Sound_PauseAll(); +} + +static void M_End(void) +{ + g_Input = g_OldInputDB; + + Text_Remove(m_PhotoText); + m_PhotoText = NULL; +} + +static void M_UpdateText(void) +{ + if (m_PhotoText != NULL) { + return; + } + + m_PhotoText = Text_Create(0, -24, "Photo Mode"); + Text_CentreH(m_PhotoText, 1); + Text_AlignBottom(m_PhotoText, 1); +} + +static PHASE_CONTROL M_Control(int32_t nframes) +{ + Input_Update(); + Camera_Update(); + M_UpdateText(); + + if (g_InputDB.toggle_photo_mode) { + Music_Unpause(); + Sound_UnpauseAll(); + Phase_Set(PHASE_GAME, NULL); + } + + return (PHASE_CONTROL) { .end = false }; +} + +static void M_Draw(void) +{ + Interpolation_Disable(); + Game_DrawScene(false); + Interpolation_Enable(); + Text_Draw(); +} + +PHASER g_PhotoModePhaser = { + .start = M_Start, + .end = M_End, + .control = M_Control, + .draw = M_Draw, + .wait = NULL, +}; diff --git a/src/game/phase/phase_photo_mode.h b/src/game/phase/phase_photo_mode.h new file mode 100644 index 000000000..78974a042 --- /dev/null +++ b/src/game/phase/phase_photo_mode.h @@ -0,0 +1,5 @@ +#pragma once + +#include "game/phase/phase.h" + +extern PHASER g_PhotoModePhaser; diff --git a/src/global/types.h b/src/global/types.h index c7841362d..fb1d6acd0 100644 --- a/src/global/types.h +++ b/src/global/types.h @@ -1673,6 +1673,9 @@ typedef union INPUT_STATE { uint64_t toggle_bilinear_filter : 1; uint64_t toggle_perspective_filter : 1; uint64_t toggle_fps_counter : 1; + uint64_t toggle_photo_mode : 1; + uint64_t photo_mode_up : 1; + uint64_t photo_mode_down : 1; uint64_t menu_up : 1; uint64_t menu_down : 1; uint64_t menu_left : 1; diff --git a/src/specific/s_input.c b/src/specific/s_input.c index 38f2b59b8..95b9184e7 100644 --- a/src/specific/s_input.c +++ b/src/specific/s_input.c @@ -981,6 +981,12 @@ INPUT_STATE S_Input_GetCurrentState( linput.toggle_fps_counter = M_Key(INPUT_ROLE_FPS, layout_num); linput.toggle_bilinear_filter = M_Key(INPUT_ROLE_BILINEAR, layout_num); linput.toggle_perspective_filter = KEY_DOWN(SDL_SCANCODE_F4); + + // Temp - use roles + linput.toggle_photo_mode = KEY_DOWN(SDL_SCANCODE_F1); + linput.photo_mode_up = KEY_DOWN(SDL_SCANCODE_E); + linput.photo_mode_down = KEY_DOWN(SDL_SCANCODE_Q); + // clang-format on if (m_Controller) {