Build first person player camera into the game

This commit is contained in:
Agent X 2023-11-04 21:32:03 -04:00
parent 395ac50532
commit e8b6a8cffa
14 changed files with 306 additions and 18 deletions

View file

@ -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

View file

@ -793,6 +793,24 @@
<br />
## [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:](#)
<br />
## [get_fog_color](#get_fog_color)
### Lua Example
@ -1463,6 +1481,26 @@
<br />
## [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:](#)
<br />
## [set_fog_color](#set_fog_color)
### Lua Example

View file

@ -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)

View file

@ -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) {

168
src/game/first_person_cam.c Normal file
View file

@ -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;
}

View file

@ -0,0 +1,14 @@
#ifndef FIRST_PERSON_CAM_H
#define FIRST_PERSON_CAM_H
#include <stdbool.h>
#include <PR/ultratypes.h>
#define FIRST_PERSON_FOV 70
extern bool gFirstPersonEnabled;
extern s16 gFirstPersonYaw;
bool update_first_person(void);
#endif

View file

@ -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;
}

View file

@ -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;

View file

@ -1,6 +0,0 @@
#ifndef INTEROP_H
#define INTEROP_H
#define C_FUNC extern "C"
#endif

View file

@ -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);

View file

@ -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";

View file

@ -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

View file

@ -4,7 +4,6 @@
#include <fstream>
#include <string>
#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<char*>(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);

View file

@ -25,4 +25,4 @@ bool mod_storage_clear(void);
}
#endif
#endif
#endif // MOD_STORAGE_H