From e8b6a8cffa3d1620c011c5b144be52da1e7acbb4 Mon Sep 17 00:00:00 2001 From: Agent X <44549182+Agent-11@users.noreply.github.com> Date: Sat, 4 Nov 2023 21:32:03 -0400 Subject: [PATCH] Build first person player camera into the game --- autogen/lua_definitions/functions.lua | 11 ++ docs/lua/functions-5.md | 38 ++++++ docs/lua/functions.md | 2 + src/game/camera.c | 7 +- src/game/first_person_cam.c | 168 ++++++++++++++++++++++++++ src/game/first_person_cam.h | 14 +++ src/game/mario.c | 3 + src/pc/djui/djui_hud_utils.c | 5 +- src/pc/interop.h | 6 - src/pc/lua/smlua_functions_autogen.c | 34 ++++++ src/pc/lua/utils/smlua_misc_utils.c | 12 ++ src/pc/lua/utils/smlua_misc_utils.h | 3 + src/pc/mods/mod_storage.cpp | 19 +-- src/pc/mods/mod_storage.h | 2 +- 14 files changed, 306 insertions(+), 18 deletions(-) create mode 100644 src/game/first_person_cam.c create mode 100644 src/game/first_person_cam.h delete mode 100644 src/pc/interop.h diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 9a1354c76..039f1dd75 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -8670,6 +8670,11 @@ function get_environment_region(index) -- ... end +--- @return boolean +function get_first_person_camera_enabled() + -- ... +end + --- @param index integer --- @return integer function get_fog_color(index) @@ -8881,6 +8886,12 @@ function set_environment_region(index, value) -- ... end +--- @param enable boolean +--- @return nil +function set_first_person_camera_enabled(enable) + -- ... +end + --- @param index integer --- @param value integer --- @return nil diff --git a/docs/lua/functions-5.md b/docs/lua/functions-5.md index 80739dd14..1e3741637 100644 --- a/docs/lua/functions-5.md +++ b/docs/lua/functions-5.md @@ -793,6 +793,24 @@
+## [get_first_person_camera_enabled](#get_first_person_camera_enabled) + +### Lua Example +`local booleanValue = get_first_person_camera_enabled()` + +### Parameters +- None + +### Returns +- `boolean` + +### C Prototype +`bool get_first_person_camera_enabled(void);` + +[:arrow_up_small:](#) + +
+ ## [get_fog_color](#get_fog_color) ### Lua Example @@ -1463,6 +1481,26 @@
+## [set_first_person_camera_enabled](#set_first_person_camera_enabled) + +### Lua Example +`set_first_person_camera_enabled(enable)` + +### Parameters +| Field | Type | +| ----- | ---- | +| enable | `boolean` | + +### Returns +- None + +### C Prototype +`void set_first_person_camera_enabled(bool enable);` + +[:arrow_up_small:](#) + +
+ ## [set_fog_color](#set_fog_color) ### Lua Example diff --git a/docs/lua/functions.md b/docs/lua/functions.md index cd86f053b..10c70ac95 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1624,6 +1624,7 @@ - [get_dialog_id](functions-5.md#get_dialog_id) - [get_envfx](functions-5.md#get_envfx) - [get_environment_region](functions-5.md#get_environment_region) + - [get_first_person_camera_enabled](functions-5.md#get_first_person_camera_enabled) - [get_fog_color](functions-5.md#get_fog_color) - [get_fog_intensity](functions-5.md#get_fog_intensity) - [get_got_file_coin_hi_score](functions-5.md#get_got_file_coin_hi_score) @@ -1658,6 +1659,7 @@ - [save_file_get_using_backup_slot](functions-5.md#save_file_get_using_backup_slot) - [save_file_set_using_backup_slot](functions-5.md#save_file_set_using_backup_slot) - [set_environment_region](functions-5.md#set_environment_region) + - [set_first_person_camera_enabled](functions-5.md#set_first_person_camera_enabled) - [set_fog_color](functions-5.md#set_fog_color) - [set_fog_intensity](functions-5.md#set_fog_intensity) - [set_got_file_coin_hi_score](functions-5.md#set_got_file_coin_hi_score) diff --git a/src/game/camera.c b/src/game/camera.c index dc1343c2a..66749859e 100644 --- a/src/game/camera.c +++ b/src/game/camera.c @@ -34,6 +34,7 @@ #include "pc/network/network.h" #include "pc/lua/smlua_hooks.h" #include "pc/djui/djui.h" +#include "first_person_cam.h" #define CBUTTON_MASK (U_CBUTTONS | D_CBUTTONS | L_CBUTTONS | R_CBUTTONS) @@ -3147,7 +3148,7 @@ void update_camera(struct Camera *c) { gCamera = c; update_camera_hud_status(c); - if (gOverrideFreezeCamera && !gDjuiInMainMenu) { + if ((gOverrideFreezeCamera || update_first_person()) && !gDjuiInMainMenu) { return; } @@ -3447,6 +3448,10 @@ void reset_camera(struct Camera *c) { gRecentCutscene = 0; unused8033B30C = 0; unused8033B310 = 0; + + if (gFirstPersonEnabled) { + gFirstPersonYaw = gMarioStates[0].faceAngle[1] + 0x8000; + } } void init_camera(struct Camera *c) { diff --git a/src/game/first_person_cam.c b/src/game/first_person_cam.c new file mode 100644 index 000000000..f2a68a25b --- /dev/null +++ b/src/game/first_person_cam.c @@ -0,0 +1,168 @@ +#include "first_person_cam.h" + +#include "sm64.h" +#include "behavior_data.h" +#include "camera.h" +#include "level_update.h" +#include "object_list_processor.h" +#include "object_helpers.h" + +#include "engine/math_util.h" + +#include "pc/controller/controller_mouse.h" +#include "pc/djui/djui_hud_utils.h" +#include "pc/lua/utils/smlua_misc_utils.h" +#include "pc/lua/smlua_hooks.h" + +#define MARIO_HEAD_POS 120 +#define MARIO_HEAD_POS_SHORT MARIO_HEAD_POS / 2 + +#define CLAMP(_val, _min, _max) MAX(MIN((_val), _max), _min) + +bool gFirstPersonEnabled = false; +s16 gFirstPersonYaw = 0; +static s16 sFirstPersonPitch = 0; +static f32 sFirstPersonCrouch = 0; + +extern s16 gMenuMode; + +/** + * A mode that implements an first person player camera. (referenced from Gun Mod v3) + */ +void update_first_person_camera(void) { + struct MarioState *m = &gMarioStates[0]; + f32 sensX = 0.3f * camera_config_get_x_sensitivity(); + f32 sensY = 0.4f * camera_config_get_y_sensitivity(); + u8 invX = camera_config_is_x_inverted() ? 1 : -1; + u8 invY = camera_config_is_y_inverted() ? 1 : -1; + + if (gMenuMode == -1) { + // update pitch + sFirstPersonPitch -= sensY * (invY * m->controller->extStickY - 1.5f * mouse_y); + sFirstPersonPitch = CLAMP(sFirstPersonPitch, -0x3F00, 0x3F00); + + // update yaw + if (m->controller->buttonPressed & L_TRIG) { + gFirstPersonYaw = m->faceAngle[1] + 0x8000; + } else { + gFirstPersonYaw += sensX * (invX * m->controller->extStickX - 1.5f * mouse_x); + } + + gDjuiHudLockMouse = true; + } else { + gDjuiHudLockMouse = false; + } + + // fix yaw for some specific actions + // if the left stick is held, use Mario's yaw to set the camera's yaw + // otherwise, set Mario's yaw to the camera's yaw + u32 actions[] = { ACT_FLYING, ACT_HOLDING_BOWSER, ACT_TORNADO_TWIRLING, ACT_FLAG_ON_POLE, ACT_FLAG_SWIMMING }; + for (s32 i = 0; i < 4; i++) { + u32 flag = actions[i]; + if ((m->action & flag) == flag) { + if (ABS(m->controller->stickX) > 4) { + gFirstPersonYaw = m->faceAngle[1] + 0x8000; + } else { + m->faceAngle[1] = gFirstPersonYaw - 0x8000; + } + break; + } + } + if (m->action == ACT_LEDGE_GRAB) { + gFirstPersonYaw = m->faceAngle[1] + 0x8000; + } + + gLakituState.yaw = gFirstPersonYaw; + m->area->camera->yaw = gFirstPersonYaw; + + // update crouch + if (m->action == ACT_START_CROUCHING || m->action == ACT_CROUCHING || m->action == ACT_STOP_CROUCHING || + m->action == ACT_START_CRAWLING || m->action == ACT_CRAWLING || m->action == ACT_STOP_CRAWLING || + m->action == ACT_CROUCH_SLIDE || m->action == ACT_LEDGE_GRAB) { + f32 inc = 10 * (m->controller->buttonDown & Z_TRIG) != 0 || m->action == ACT_CROUCH_SLIDE || m->action == ACT_LEDGE_GRAB ? 1 : -1; + sFirstPersonCrouch = CLAMP(sFirstPersonCrouch + inc, 0, MARIO_HEAD_POS - MARIO_HEAD_POS_SHORT); + } else { + sFirstPersonCrouch = CLAMP(sFirstPersonCrouch - 10, 0, MARIO_HEAD_POS - MARIO_HEAD_POS_SHORT); + } + + if (m->action == ACT_LEDGE_GRAB) { + sFirstPersonCrouch = MARIO_HEAD_POS - MARIO_HEAD_POS_SHORT; + } + + // update pos + gLakituState.pos[0] = m->pos[0] + coss(sFirstPersonPitch) * sins(gFirstPersonYaw); + gLakituState.pos[1] = m->pos[1] + sins(sFirstPersonPitch) + (MARIO_HEAD_POS - sFirstPersonCrouch); + gLakituState.pos[2] = m->pos[2] + coss(sFirstPersonPitch) * coss(gFirstPersonYaw); + vec3f_copy(m->area->camera->pos, gLakituState.pos); + vec3f_copy(gLakituState.curPos, gLakituState.pos); + vec3f_copy(gLakituState.goalPos, gLakituState.pos); + + // update focus + gLakituState.focus[0] = m->pos[0] - 100 * coss(sFirstPersonPitch) * sins(gFirstPersonYaw); + gLakituState.focus[1] = m->pos[1] - 100 * sins(sFirstPersonPitch) + (MARIO_HEAD_POS - sFirstPersonCrouch); + gLakituState.focus[2] = m->pos[2] - 100 * coss(sFirstPersonPitch) * coss(gFirstPersonYaw); + vec3f_copy(m->area->camera->focus, gLakituState.focus); + vec3f_copy(gLakituState.curFocus, gLakituState.focus); + vec3f_copy(gLakituState.goalFocus, gLakituState.focus); + + // set other values + gLakituState.posHSpeed = 0; + gLakituState.posVSpeed = 0; + gLakituState.focHSpeed = 0; + gLakituState.focVSpeed = 0; + vec3s_set(gLakituState.shakeMagnitude, 0, 0, 0); + + gFOVState.fov = FIRST_PERSON_FOV; +} + +bool update_first_person(void) { + if (gFirstPersonEnabled && !gDjuiInMainMenu) { + if (gCurrActNum == 99) { + return false; + } + + struct MarioState *m = &gMarioStates[0]; + + // check cancels + if (m->action == ACT_FIRST_PERSON || m->action == ACT_IN_CANNON || m->action == ACT_READING_NPC_DIALOG) { + gFOVState.fov = 45.0f; + return false; + } + if (m->action == ACT_DISAPPEARED) { + gFOVState.fov = 45.0f; + return false; + } + struct Object *bowser = find_object_with_behavior(bhvBowser); + if (bowser != NULL && (bowser->oAction == 5 || bowser->oAction == 6)) { + gFOVState.fov = 45.0f; + return false; + } + + if (m->action == ACT_SHOT_FROM_CANNON && m->area->camera->mode == CAMERA_MODE_INSIDE_CANNON) { + gFirstPersonYaw = m->faceAngle[1] + 0x8000; + m->area->camera->mode = CAMERA_MODE_FREE_ROAM; + } + + gDjuiHudLockMouse = gMenuMode == -1; + + m->marioBodyState->modelState = 0x100; + if (m->heldObj) { + Vec3f camDir = { + m->area->camera->focus[0] - m->area->camera->pos[0], + m->area->camera->focus[1] - m->area->camera->pos[1], + m->area->camera->focus[2] - m->area->camera->pos[2], + }; + vec3f_normalize(camDir); + vec3f_mul(camDir, 100); + vec3f_sum(m->marioObj->header.gfx.pos, m->pos, camDir); + } + + update_first_person_camera(); + + return true; + } else if (!camera_config_is_mouse_look_enabled()) { + gDjuiHudLockMouse = false; + } + + return false; +} diff --git a/src/game/first_person_cam.h b/src/game/first_person_cam.h new file mode 100644 index 000000000..479cdebcf --- /dev/null +++ b/src/game/first_person_cam.h @@ -0,0 +1,14 @@ +#ifndef FIRST_PERSON_CAM_H +#define FIRST_PERSON_CAM_H + +#include +#include + +#define FIRST_PERSON_FOV 70 + +extern bool gFirstPersonEnabled; +extern s16 gFirstPersonYaw; + +bool update_first_person(void); + +#endif \ No newline at end of file diff --git a/src/game/mario.c b/src/game/mario.c index 8f02cb418..db283d153 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -44,6 +44,7 @@ #include "pc/lua/smlua.h" #include "pc/network/socket/socket.h" #include "bettercamera.h" +#include "first_person_cam.h" #define MAX_HANG_PREVENTION 64 @@ -1571,6 +1572,8 @@ void update_mario_joystick_inputs(struct MarioState *m) { if (m->intendedMag > 0.0f) { if (gLakituState.mode != CAMERA_MODE_NEWCAM) { m->intendedYaw = atan2s(-controller->stickY, controller->stickX) + m->area->camera->yaw; + } else if (gFirstPersonEnabled) { + m->intendedYaw = atan2s(-controller->stickY, controller->stickX) + gLakituState.yaw; } else { m->intendedYaw = atan2s(-controller->stickY, controller->stickX) - newcam_yaw + 0x4000; } diff --git a/src/pc/djui/djui_hud_utils.c b/src/pc/djui/djui_hud_utils.c index 0e698be91..291c0413b 100644 --- a/src/pc/djui/djui_hud_utils.c +++ b/src/pc/djui/djui_hud_utils.c @@ -19,6 +19,9 @@ #include "djui_panel_pause.h" #include "game/camera.h" #include "game/hud.h" +#include "game/rendering_graph_node.h" + +#include "engine/math_util.h" static enum HudUtilsResolution sResolution = RESOLUTION_DJUI; @@ -575,7 +578,7 @@ bool djui_hud_world_pos_to_screen_pos(Vec3f pos, Vec3f out) { out[1] *= 256.0f / out[2]; // fov of 45.0 is the default fov - f32 fovDefault = tanf(45.0f * ((f32)M_PI / 360.0f)); + f32 fovDefault = tanf(not_zero(45.0f, gOverrideFOV) * ((f32)M_PI / 360.0f)); f32 fovCurrent = tanf((gFOVState.fov + gFOVState.fovOffset) * ((f32)M_PI / 360.0f)); f32 fovDifference = (fovDefault / fovCurrent) * 1.13f; diff --git a/src/pc/interop.h b/src/pc/interop.h deleted file mode 100644 index 18f0bbcb9..000000000 --- a/src/pc/interop.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef INTEROP_H -#define INTEROP_H - -#define C_FUNC extern "C" - -#endif diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index 646d14d2b..01d5ebd39 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -28670,6 +28670,21 @@ int smlua_func_get_environment_region(lua_State* L) { return 1; } +int smlua_func_get_first_person_camera_enabled(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "get_first_person_camera_enabled", 0, top); + return 0; + } + + + lua_pushboolean(L, get_first_person_camera_enabled()); + + return 1; +} + int smlua_func_get_fog_color(lua_State* L) { if (L == NULL) { return 0; } @@ -29262,6 +29277,23 @@ int smlua_func_set_environment_region(lua_State* L) { return 1; } +int smlua_func_set_first_person_camera_enabled(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "set_first_person_camera_enabled", 1, top); + return 0; + } + + bool enable = smlua_to_boolean(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "set_first_person_camera_enabled"); return 0; } + + set_first_person_camera_enabled(enable); + + return 1; +} + int smlua_func_set_fog_color(lua_State* L) { if (L == NULL) { return 0; } @@ -32740,6 +32772,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "get_dialog_id", smlua_func_get_dialog_id); smlua_bind_function(L, "get_envfx", smlua_func_get_envfx); smlua_bind_function(L, "get_environment_region", smlua_func_get_environment_region); + smlua_bind_function(L, "get_first_person_camera_enabled", smlua_func_get_first_person_camera_enabled); smlua_bind_function(L, "get_fog_color", smlua_func_get_fog_color); smlua_bind_function(L, "get_fog_intensity", smlua_func_get_fog_intensity); smlua_bind_function(L, "get_got_file_coin_hi_score", smlua_func_get_got_file_coin_hi_score); @@ -32774,6 +32807,7 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "save_file_get_using_backup_slot", smlua_func_save_file_get_using_backup_slot); smlua_bind_function(L, "save_file_set_using_backup_slot", smlua_func_save_file_set_using_backup_slot); smlua_bind_function(L, "set_environment_region", smlua_func_set_environment_region); + smlua_bind_function(L, "set_first_person_camera_enabled", smlua_func_set_first_person_camera_enabled); smlua_bind_function(L, "set_fog_color", smlua_func_set_fog_color); smlua_bind_function(L, "set_fog_intensity", smlua_func_set_fog_intensity); smlua_bind_function(L, "set_got_file_coin_hi_score", smlua_func_set_got_file_coin_hi_score); diff --git a/src/pc/lua/utils/smlua_misc_utils.c b/src/pc/lua/utils/smlua_misc_utils.c index 17c940a6c..bf0d48b15 100644 --- a/src/pc/lua/utils/smlua_misc_utils.c +++ b/src/pc/lua/utils/smlua_misc_utils.c @@ -22,6 +22,7 @@ #include "pc/gfx/gfx_pc.h" #include "include/course_table.h" #include "game/level_geo.h" +#include "game/first_person_cam.h" static struct DateTime sDateTime; @@ -624,6 +625,17 @@ void set_override_envfx(s32 envfx) { /// +bool get_first_person_camera_enabled(void) { + return gFirstPersonEnabled; +} + +void set_first_person_camera_enabled(bool enable) { + if (gFirstPersonEnabled && !enable) { gFOVState.fov = 45.0f; } + gFirstPersonEnabled = enable; +} + +/// + const char* get_os_name(void) { #if defined(_WIN32) || defined(_WIN64) return "Windows"; diff --git a/src/pc/lua/utils/smlua_misc_utils.h b/src/pc/lua/utils/smlua_misc_utils.h index 1f82b1422..1e997f233 100644 --- a/src/pc/lua/utils/smlua_misc_utils.h +++ b/src/pc/lua/utils/smlua_misc_utils.h @@ -151,6 +151,9 @@ struct DateTime* get_date_and_time(void); u16 get_envfx(void); void set_override_envfx(s32 envfx); +bool get_first_person_camera_enabled(void); +void set_first_person_camera_enabled(bool enable); + const char* get_os_name(void); #endif diff --git a/src/pc/mods/mod_storage.cpp b/src/pc/mods/mod_storage.cpp index d7453d609..3fa6f5879 100644 --- a/src/pc/mods/mod_storage.cpp +++ b/src/pc/mods/mod_storage.cpp @@ -4,7 +4,6 @@ #include #include #include "pc/mini.h" -#include "pc/interop.h" extern "C" { #include "pc/platform.h" @@ -15,6 +14,8 @@ extern "C" { #include "pc/debuglog.h" } +#define C_FIELD extern "C" + void strdelete(char string[], char substr[]) { // i is used to loop through the string u16 i = 0; @@ -62,7 +63,7 @@ void mod_storage_get_filename(char* dest) { normalize_path(dest); // fix any out of place slashes } -C_FUNC bool mod_storage_save(const char* key, const char* value) { +C_FIELD bool mod_storage_save(const char* key, const char* value) { if (strlen(key) > MAX_KEY_VALUE_LENGTH || strlen(value) > MAX_KEY_VALUE_LENGTH) { return false; } @@ -96,15 +97,15 @@ C_FUNC bool mod_storage_save(const char* key, const char* value) { return true; } -C_FUNC bool mod_storage_save_number(const char* key, double value) { +C_FIELD bool mod_storage_save_number(const char* key, double value) { return mod_storage_save(key, std::to_string(value).c_str()); } -C_FUNC bool mod_storage_save_bool(const char* key, bool value) { +C_FIELD bool mod_storage_save_bool(const char* key, bool value) { return mod_storage_save(key, value ? "true" : "false"); } -C_FUNC const char* mod_storage_load(const char* key) { +C_FIELD const char* mod_storage_load(const char* key) { if (strlen(key) > MAX_KEY_VALUE_LENGTH) { return NULL; } @@ -126,21 +127,21 @@ C_FUNC const char* mod_storage_load(const char* key) { return const_cast(ini["storage"][key].c_str()); } -C_FUNC double mod_storage_load_number(const char* key) { +C_FIELD double mod_storage_load_number(const char* key) { const char* value = mod_storage_load(key); if (value == NULL) { return 0; } return std::strtod(value, nullptr); } -C_FUNC bool mod_storage_load_bool(const char* key) { +C_FIELD bool mod_storage_load_bool(const char* key) { const char* value = mod_storage_load(key); if (value == NULL) { return false; } return !strcmp(value, "true"); } -C_FUNC bool mod_storage_remove(const char* key) { +C_FIELD bool mod_storage_remove(const char* key) { if (strlen(key) > MAX_KEY_VALUE_LENGTH) { return false; } @@ -168,7 +169,7 @@ C_FUNC bool mod_storage_remove(const char* key) { return false; } -C_FUNC bool mod_storage_clear(void) { +C_FIELD bool mod_storage_clear(void) { char filename[SYS_MAX_PATH] = {0}; mod_storage_get_filename(filename); diff --git a/src/pc/mods/mod_storage.h b/src/pc/mods/mod_storage.h index 9a388a9e3..1b614c1bf 100644 --- a/src/pc/mods/mod_storage.h +++ b/src/pc/mods/mod_storage.h @@ -25,4 +25,4 @@ bool mod_storage_clear(void); } #endif -#endif +#endif // MOD_STORAGE_H