From be689a049595fbd29df7b7533b8b50e4e7310862 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 22 Oct 2023 15:29:36 +0200 Subject: [PATCH 01/36] port: add forgotten DPAD_RIGHT setting --- port/src/input.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/port/src/input.c b/port/src/input.c index c11c6a8e0..06a7566ff 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -181,8 +181,9 @@ void inputSetDefaultKeyBinds(void) { CK_START, SDL_CONTROLLER_BUTTON_START }, { CK_C_D, SDL_CONTROLLER_BUTTON_DPAD_DOWN }, { CK_C_U, SDL_CONTROLLER_BUTTON_DPAD_UP }, - { CK_8000, SDL_CONTROLLER_BUTTON_LEFTSTICK }, + { CK_C_R, SDL_CONTROLLER_BUTTON_DPAD_RIGHT }, { CK_C_L, SDL_CONTROLLER_BUTTON_DPAD_LEFT }, + { CK_8000, SDL_CONTROLLER_BUTTON_LEFTSTICK }, }; for (u32 i = 0; i < sizeof(kbbinds) / sizeof(kbbinds[0]); ++i) { From 6957c1540d5c5b9fec1c2df4b08800fcba37ff8d Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 22 Oct 2023 18:49:39 +0200 Subject: [PATCH 02/36] port: fix digital grab movement --- src/game/bondmove.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/game/bondmove.c b/src/game/bondmove.c index e4b6c3046..bceacb72e 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -1198,7 +1198,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i if (!g_Vars.currentplayer->insightaimmode) { movedata.analogstrafe = c2stickx; movedata.analogwalk = c2sticky; - movedata.unk14 = 1; + movedata.unk14 = (c2stickx || c2sticky); } else { movedata.analogstrafe = 0.f; movedata.analogwalk = 0.f; @@ -1256,7 +1256,11 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i movedata.digitalstepforward = !g_Vars.currentplayer->insightaimmode && (c1buttons & (U_CBUTTONS)); movedata.digitalstepback = !g_Vars.currentplayer->insightaimmode && (c1buttons & (D_CBUTTONS)); - movedata.canlookahead = !g_Vars.currentplayer->insightaimmode; +#ifdef PLATFORM_N64 + movedata.canlookahead = false; +#else + movedata.canlookahead = !g_Vars.currentplayer->insightaimmode && (c2stickx || c2sticky); +#endif movedata.cannaturalpitch = !g_Vars.currentplayer->insightaimmode; movedata.speedvertadown = 0; movedata.speedvertaup = 0; @@ -1320,7 +1324,11 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i } movedata.cannaturalturn = !g_Vars.currentplayer->insightaimmode; - movedata.unk14 = !g_Vars.currentplayer->insightaimmode; +#ifdef PLATFORM_N64 + movedata.unk14 = false; +#else + movedata.unk14 = !g_Vars.currentplayer->insightaimmode && (c2stickx || c2sticky); +#endif if (g_Vars.tickmode == TICKMODE_AUTOWALK) { movedata.analogstrafe = 0; @@ -1699,9 +1707,9 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i movedata.farsighttempautoseek = g_Vars.currentplayer->insightaimmode && (c1buttons & (L_CBUTTONS | R_CBUTTONS)); if (g_Vars.currentplayer->insightaimmode) { movedata.unk14 = 1; - #ifndef PLATFORM_N64 +#ifndef PLATFORM_N64 movedata.analogstrafe = c2stickx; - #endif +#endif } } else { movedata.rleanleft = g_Vars.currentplayer->insightaimmode && (c1buttons & (L_CBUTTONS)); From 05c108a950573804617af2ff3dcb9fb146deb83f Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Sun, 22 Oct 2023 11:18:38 -0700 Subject: [PATCH 03/36] fixup: quick detonate --- src/game/bondmove.c | 15 ++------------- src/include/game/bondmove.h | 1 + 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/game/bondmove.c b/src/game/bondmove.c index bceacb72e..d9501b001 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -1734,8 +1734,8 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i g_Vars.currentplayer->usedowntime = -2; } #else - if ((((c1buttons & BUTTON_RADIAL) && (c1buttonsthisframe & BUTTON_CANCEL_USE)) - || ((c1buttons & BUTTON_CANCEL_USE) && (c1buttonsthisframe & BUTTON_RADIAL))) + if ((((c1buttons & (BUTTON_CANCEL_USE | BUTTON_ACCEPT_USE)) && (c1buttonsthisframe & (BUTTON_WPNBACK | BUTTON_RADIAL))) + || ((c1buttons & (BUTTON_WPNBACK | BUTTON_RADIAL)) && (c1buttonsthisframe & (BUTTON_CANCEL_USE | BUTTON_ACCEPT_USE)))) && weaponnum == WEAPON_REMOTEMINE) { movedata.detonating = true; movedata.weaponbackoffset = 0; @@ -1744,17 +1744,6 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i amClose(); g_Vars.currentplayer->invdowntime = -2; g_Vars.currentplayer->usedowntime = -2; - g_Vars.currentplayer->amdowntime = 0; - } - if ((((c1buttons & BUTTON_WPNBACK) && (c1buttonsthisframe & BUTTON_CANCEL_USE)) - || ((c1buttons & BUTTON_CANCEL_USE) && (c1buttonsthisframe & BUTTON_WPNBACK))) - && weaponnum == WEAPON_REMOTEMINE) { - movedata.detonating = true; - movedata.weaponbackoffset = 0; - movedata.weaponforwardoffset = 0; - movedata.btapcount = 0; - g_Vars.currentplayer->invdowntime = -2; - g_Vars.currentplayer->usedowntime = -2; } #endif } diff --git a/src/include/game/bondmove.h b/src/include/game/bondmove.h index 7733b613f..fc951649b 100644 --- a/src/include/game/bondmove.h +++ b/src/include/game/bondmove.h @@ -21,6 +21,7 @@ void bmoveGrabProp(struct prop *prop); void bmoveSetMode(u32 movemode); void bmoveSetModeForAllPlayers(u32 movemode); void bmoveHandleActivate(void); +static void bgunProcessInputAltButton(struct movedata *data, s8 contpad, s32 i); void bmoveApplyMoveData(struct movedata *data); void bmoveUpdateSpeedTheta(void); f32 bmoveGetSpeedVertaLimit(f32 value); From 0ea3bea0968e66448ebd8bda1065cf0a1bf964d6 Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Sun, 22 Oct 2023 16:35:30 -0700 Subject: [PATCH 04/36] port: put instant detonate flow in own function --- src/game/bondmove.c | 26 +++++++++++++++----------- src/include/game/bondmove.h | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/game/bondmove.c b/src/game/bondmove.c index d9501b001..a7ea7dfbe 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -43,6 +43,20 @@ #include "input.h" #include "video.h" +static void bgunProcessQuickDetonate(struct movedata *data, u32 c1buttons, u32 c1buttonsthisframe, u32 buttons1, u32 buttons2) { + if ((((c1buttons & (buttons1)) && (c1buttonsthisframe & (buttons2))) + || ((c1buttons & (buttons2)) && (c1buttonsthisframe & (buttons1)))) + && bgunGetWeaponNum(HAND_RIGHT) == WEAPON_REMOTEMINE) { + data->detonating = true; + data->weaponbackoffset = 0; + data->weaponforwardoffset = 0; + data->btapcount = 0; + amClose(); + g_Vars.currentplayer->invdowntime = -2; + g_Vars.currentplayer->usedowntime = -2; + } +} + static void bgunProcessInputAltButton(struct movedata *data, s8 contpad, s32 i) { s32 buttons = joyGetButtonsOnSample(i, contpad, 0xffffffff); @@ -1734,17 +1748,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i g_Vars.currentplayer->usedowntime = -2; } #else - if ((((c1buttons & (BUTTON_CANCEL_USE | BUTTON_ACCEPT_USE)) && (c1buttonsthisframe & (BUTTON_WPNBACK | BUTTON_RADIAL))) - || ((c1buttons & (BUTTON_WPNBACK | BUTTON_RADIAL)) && (c1buttonsthisframe & (BUTTON_CANCEL_USE | BUTTON_ACCEPT_USE)))) - && weaponnum == WEAPON_REMOTEMINE) { - movedata.detonating = true; - movedata.weaponbackoffset = 0; - movedata.weaponforwardoffset = 0; - movedata.btapcount = 0; - amClose(); - g_Vars.currentplayer->invdowntime = -2; - g_Vars.currentplayer->usedowntime = -2; - } + bgunProcessQuickDetonate(&movedata, c1buttons, c1buttonsthisframe, (BUTTON_CANCEL_USE | BUTTON_ACCEPT_USE), (BUTTON_WPNBACK | BUTTON_RADIAL)); #endif } diff --git a/src/include/game/bondmove.h b/src/include/game/bondmove.h index fc951649b..a37768377 100644 --- a/src/include/game/bondmove.h +++ b/src/include/game/bondmove.h @@ -21,6 +21,7 @@ void bmoveGrabProp(struct prop *prop); void bmoveSetMode(u32 movemode); void bmoveSetModeForAllPlayers(u32 movemode); void bmoveHandleActivate(void); +static void bgunProcessQuickDetonate(struct movedata *data, u32 c1buttons, u32 c1buttonsthisframe, u32 buttons1, u32 buttons2); static void bgunProcessInputAltButton(struct movedata *data, s8 contpad, s32 i); void bmoveApplyMoveData(struct movedata *data); void bmoveUpdateSpeedTheta(void); From 77fcac4912100f61cb926280708c5e7018ad45cc Mon Sep 17 00:00:00 2001 From: fgsfds Date: Mon, 23 Oct 2023 20:34:51 +0200 Subject: [PATCH 05/36] port: remove static decls from bondmove.h --- src/include/game/bondmove.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/include/game/bondmove.h b/src/include/game/bondmove.h index a37768377..7733b613f 100644 --- a/src/include/game/bondmove.h +++ b/src/include/game/bondmove.h @@ -21,8 +21,6 @@ void bmoveGrabProp(struct prop *prop); void bmoveSetMode(u32 movemode); void bmoveSetModeForAllPlayers(u32 movemode); void bmoveHandleActivate(void); -static void bgunProcessQuickDetonate(struct movedata *data, u32 c1buttons, u32 c1buttonsthisframe, u32 buttons1, u32 buttons2); -static void bgunProcessInputAltButton(struct movedata *data, s8 contpad, s32 i); void bmoveApplyMoveData(struct movedata *data); void bmoveUpdateSpeedTheta(void); f32 bmoveGetSpeedVertaLimit(f32 value); From 30b8400d86245bd16fa3d356897a358fb28d3988 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 24 Oct 2023 11:14:33 +0200 Subject: [PATCH 06/36] port: fix drug blur not resetting on respawn fix taken from Graslu's decomp fork --- src/game/bot.c | 4 ++++ src/game/player.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/game/bot.c b/src/game/bot.c index 017405eec..0658a1ce3 100644 --- a/src/game/bot.c +++ b/src/game/bot.c @@ -88,6 +88,10 @@ void botReset(struct chrdata *chr, u8 respawning) chrSetShield(chr, 0); chr->cmnum = 0; chr->cmnum2 = 0; +#ifndef PLATFORM_N64 + chr->blurdrugamount = 0; + chr->poisoncounter = 0; +#endif #if VERSION >= VERSION_NTSC_1_0 bgunFreeFireslot(chr->fireslots[0]); diff --git a/src/game/player.c b/src/game/player.c index 7260312dc..351e6a7b8 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -530,6 +530,10 @@ void playerStartNewLife(void) g_Vars.currentplayer->damagetype = DAMAGETYPE_7; g_Vars.currentplayer->gunammooff = 0; g_Vars.currentplayer->gunsightoff = 2; +#ifndef PLATFORM_N64 + g_Vars.currentplayer->prop->chr->blurdrugamount = 0; + g_Vars.currentplayer->prop->chr->poisoncounter = 0; +#endif hudmsgsSetOn(0xffffffff); From 368021201bea6a5744a153e46948789bd25500aa Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 24 Oct 2023 15:55:04 +0200 Subject: [PATCH 07/36] Update README.md --- README.md | 50 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7c11e29a7..4db9b92ab 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,35 @@ This repository contains a work-in-progress port of the [Perfect Dark decompilation](https://github.com/n64decomp/perfect_dark) to modern platforms. -To run the port, you must already have a Perfect Dark ROM, specifically the ntsc-final/revision 1 version -(md5 `e03b088b6ac9e0080440efed07c1e40f`). +To run the port, you must already have a Perfect Dark ROM, specifically one of the following: +* `ntsc-final`/`US V1.1`/`US Rev 1` (md5 `e03b088b6ac9e0080440efed07c1e40f`). + **This is the recommended version to use**. + Called `NTSC version 8.7 final` on the boot screen. +* `ntsc-1.0`/`US V1.0` (md5 `7f4171b0c8d17815be37913f535e4e93`). + Technically supported, but not recommended. + Called `NTSC version 8.7 final` on the boot screen as well. +* `jpn-final` (md5 `538d2b75945eae069b29c46193e74790`). + Technically supported, but requires a separate custom-built executable. + Called `JPN version 8.9 final` on the boot screen. ## Status -Currently only 32-bit platforms are supported, namely x86 Windows and Linux. -Note that 32-bit binaries will still work on 64-bit versions of those platforms, -though you might have to install some additional libraries. +The game is in a mostly functional state, with both singleplayer and split-screen multiplayer modes fully working. +There are minor graphics- and gameplay-related issues, and possibly occasional crashes. -The game is in a somewhat functional but probably unstable state. -There are major graphical issues, minor audio issues and possibly other issues. +**The following extra features are implemented:** +* mouselook; +* dual analog controller support; +* widescreen resolution support; +* configurable field of view; +* 60 FPS support, including fixes for some framerate-related issues; +* fixes for a couple original bugs and crashes; +* basic mod support, currently enough to load a few custom levels; +* slightly expanded memory heap size. -There are currently no "extra features" implemented -except for janky mouselook, arbitrary resolution support (with partial widescreen support), some extra controls -and somewhat expanded heap size. +Currently only 32-bit platforms are supported, namely x86 Windows and Linux. +Note that 32-bit binaries will still work on 64-bit versions of those platforms, +though you might have to install some additional libraries. ## Download @@ -26,12 +40,16 @@ Latest [automatic builds](https://github.com/fgsfdsfgs/perfect_dark/actions) for ## Running -You must already have a Perfect Dark ROM to run the game, as specified above. +You must already have a Perfect Dark ROM to run the game, as specified above. 1. Create a directory named `data` next to `pd.exe`. 2. Put your Perfect Dark ROM named `pd.ntsc-final.z64` into it. 3. Run `pd.exe`. +Additional information can be found in the [wiki](https://github.com/fgsfdsfgs/perfect_dark/wiki). + +A GPU supporting OpenGL 3.0 or above is required to run the port. + ## Controls 1964GEPD-style and Xbox-style bindings are implemented. @@ -40,6 +58,9 @@ N64 pad buttons X and Y (or `X_BUTTON`, `Y_BUTTON` in the code) refer to the res Support for one controller, two-stick configurations are enabled for 1.2. +Note that the mouse only controls player 1. + +Controls can be rebound in `pd.ini`. Default control scheme is as follows: | Action | Keyboard and mouse | Xbox pad | N64 pad | | - | - | - | - | @@ -80,6 +101,11 @@ Support for one controller, two-stick configurations are enabled for 1.2. Currently only `i686-linux` and `i686-windows` are supported, using `gcc -m32` and `i686-w64-mingw32-gcc` as compilers, respectively. Alternate compilers can be specified by passing `TOOLCHAIN=i686-whatever-` as a command line argument. +For both platforms you can optionally specify `ROMID=jpn-final` on the `make` command line to build the JPN executable. +You will need to provide a `jpn-final` ROM for that, named `pd.jpn-final.z64`. + +It might be possible to build a 32-bit ARM executable, but this has not been tested. + ## Credits * the original [decompilation project](https://github.com/n64decomp/perfect_dark) authors; @@ -87,4 +113,6 @@ Alternate compilers can be specified by passing `TOOLCHAIN=i686-whatever-` as a * [sm64-port](https://github.com/sm64-port/sm64-port) authors for the audio mixer and some other changes; * [Ship of Harkinian team](https://github.com/Kenix3/libultraship/tree/main/src/graphic/Fast3D), Emill and MaikelChan for the libultraship version of fast3d that this port uses; * lieff for [minimp3](https://github.com/lieff/minimp3); +* Mouse Injector and 1964GEPD authors for some of the 60FPS- and mouselook-related fixes; +* everyone who has submitted pull requests and issues to this repository and tested the port; * probably more I'm forgetting. From da7e1493a5c9abb4890e003afa8368b08f863e8d Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 25 Oct 2023 12:54:47 +0200 Subject: [PATCH 08/36] port: extend sky slightly beyond screen to push glitchy corners out of view --- src/game/sky.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/game/sky.c b/src/game/sky.c index 9e6e92e5c..026878d0e 100644 --- a/src/game/sky.c +++ b/src/game/sky.c @@ -307,10 +307,17 @@ Gfx *skyRender(Gfx *gdl) if (&tl3dpos); +#ifdef PLATFORM_N64 skyGetWorldPosFromScreenPos(0.0f, 0.0f, &tl3dpos); skyGetWorldPosFromScreenPos(camGetScreenWidth() - 0.1f, 0.0f, &tr3dpos); skyGetWorldPosFromScreenPos(0.0f, camGetScreenHeight() - 0.1f, &bl3dpos); skyGetWorldPosFromScreenPos(camGetScreenWidth() - 0.1f, camGetScreenHeight() - 0.1f, &br3dpos); +#else + skyGetWorldPosFromScreenPos(-4.0f, -4.0f, &tl3dpos); + skyGetWorldPosFromScreenPos(camGetScreenWidth() + 4.0f, -4.0f, &tr3dpos); + skyGetWorldPosFromScreenPos(-4.0f, camGetScreenHeight() + 4.0f, &bl3dpos); + skyGetWorldPosFromScreenPos(camGetScreenWidth() + 4.0f, camGetScreenHeight() + 4.0f, &br3dpos); +#endif tlcornerissky = skyIsScreenCornerInSky(&tl3dpos, &sp644, &sp58c); trcornerissky = skyIsScreenCornerInSky(&tr3dpos, &sp638, &sp588); From 015817f2042bc0664d7c902591ca6a1a40637184 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 28 Oct 2023 05:33:09 +0200 Subject: [PATCH 09/36] port: add extended options menu as well as support for string literals in the menu system --- port/include/input.h | 15 + port/include/video.h | 7 + port/src/input.c | 69 +++++ port/src/main.c | 32 ++- port/src/optionsmenu.c | 597 ++++++++++++++++++++++++++++++++++++++++ port/src/video.c | 41 ++- src/game/bondwalk.c | 3 + src/game/mainmenu.c | 63 ++++- src/game/menu.c | 15 + src/game/player.c | 4 +- src/include/constants.h | 4 + 11 files changed, 831 insertions(+), 19 deletions(-) create mode 100644 port/src/optionsmenu.c diff --git a/port/include/input.h b/port/include/input.h index 49c94de0e..85b14641b 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -93,6 +93,12 @@ s32 inputControllerConnected(s32 idx); // returns bitmask of connected controllers s32 inputControllerMask(void); +s32 inputControllerGetSticksSwapped(void); +void inputControllerSetSticksSwapped(s32 swapped); + +s32 inputControllerGetDualAnalog(void); +void inputControllerSetDualAnalog(s32 enable); + // vk is a value from the virtkey enum above s32 inputKeyPressed(u32 vk); @@ -118,6 +124,9 @@ const char *inputGetContKeyName(u32 ck); // strength is 0 .. 1; 0 strength turns it off void inputRumble(s32 idx, f32 strength, f32 time); +f32 inputRumbleGetStrength(void); +void inputRumbleSetStrength(f32 val); + // locks the mouse cursor in the window and makes it invisible if argument is true void inputLockMouse(s32 lock); @@ -138,6 +147,12 @@ void inputMouseGetScaledDelta(f32 *dx, f32 *dy); // returns 0, 0 when the mouse is not locked into the window void inputMouseGetAbsScaledDelta(f32 *dx, f32 *dy); +void inputMouseGetSpeed(f32 *x, f32 *y); +void inputMouseSetSpeed(f32 x, f32 y); + +s32 inputMouseIsEnabled(void); +void inputMouseEnable(s32 enabled); + // call this every frame void inputUpdate(void); diff --git a/port/include/video.h b/port/include/video.h index b067ecbc3..a68ac75d3 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -19,9 +19,14 @@ s32 videoGetNativeHeight(void); s32 videoGetWidth(void); s32 videoGetHeight(void); f32 videoGetAspect(void); +s32 videoGetFullscreen(void); +u32 videoGetTextureFilter(void); u32 videoGetTextureFilter2D(void); void videoSetWindowOffset(s32 x, s32 y); +void videoSetFullscreen(s32 fs); +void videoSetTextureFilter(u32 filter); +void videoSetTextureFilter2D(u32 filter); s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize); void videoSetFramebuffer(s32 target); @@ -33,4 +38,6 @@ s32 videoFramebuffersSupported(void); void videoResetTextureCache(void); void videoFreeCachedTexture(const void *texptr); +void videoSaveConfig(void); + #endif diff --git a/port/src/input.c b/port/src/input.c index 06a7566ff..2a1b291af 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -407,6 +407,9 @@ static inline void inputLoadBinds(void) void inputSaveConfig(void) { inputSaveBinds(); + + configSetFloat("Input.MouseSpeedX", mouseSensX); + configSetFloat("Input.MouseSpeedY", mouseSensY); } s32 inputInit(void) @@ -646,11 +649,51 @@ void inputRumble(s32 idx, f32 strength, f32 time) } } +f32 inputRumbleGetStrength(void) +{ + return rumbleScale; +} + +void inputRumbleSetStrength(f32 val) +{ + rumbleScale = val; +} + s32 inputControllerMask(void) { return connectedMask; } +s32 inputControllerGetSticksSwapped(void) +{ + return (axisMap[0][0] == SDL_CONTROLLER_AXIS_RIGHTX); +} + +void inputControllerSetSticksSwapped(s32 swapped) +{ + if (swapped) { + axisMap[0][0] = SDL_CONTROLLER_AXIS_RIGHTX; + axisMap[0][1] = SDL_CONTROLLER_AXIS_RIGHTY; + axisMap[1][0] = SDL_CONTROLLER_AXIS_LEFTX; + axisMap[1][1] = SDL_CONTROLLER_AXIS_LEFTY; + } else { + axisMap[0][0] = SDL_CONTROLLER_AXIS_LEFTX; + axisMap[0][1] = SDL_CONTROLLER_AXIS_LEFTY; + axisMap[1][0] = SDL_CONTROLLER_AXIS_RIGHTX; + axisMap[1][1] = SDL_CONTROLLER_AXIS_RIGHTY; + } +} + +s32 inputControllerGetDualAnalog(void) +{ + return !stickCButtons; +} + +void inputControllerSetDualAnalog(s32 enable) +{ + stickCButtons = !enable; +} + void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk) { if (idx < 0 || idx >= INPUT_MAX_CONTROLLERS || bind >= MAX_BINDS || ck >= CK_TOTAL_COUNT) { @@ -766,6 +809,32 @@ void inputMouseGetAbsScaledDelta(f32 *dx, f32 *dy) if (dy) *dy = mdy; } +void inputMouseGetSpeed(f32 *x, f32 *y) +{ + *x = mouseSensX; + *y = mouseSensY; +} + +void inputMouseSetSpeed(f32 x, f32 y) +{ + mouseSensX = x; + mouseSensY = y; +} + +s32 inputMouseIsEnabled(void) +{ + return mouseEnabled; +} + +void inputMouseEnable(s32 enabled) +{ + mouseEnabled = !!enabled; + if (!mouseEnabled && mouseLocked) { + inputLockMouse(0); + } +} + + const char *inputGetContKeyName(u32 ck) { if (ck >= CK_TOTAL_COUNT) { diff --git a/port/src/main.c b/port/src/main.c index 74a3205a6..056a489a8 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -56,15 +56,6 @@ void bootCreateSched(void) } } -static void cleanup(void) -{ - sysLogPrintf(LOG_NOTE, "shutdown"); - inputSaveConfig(); - configSave(CONFIG_PATH); - crashShutdown(); - // TODO: actually shut down all subsystems -} - static void gameLoadConfig(void) { osMemSize = configGetIntClamped("Game.MemorySize", 16, 4, 2048) * 1024 * 1024; @@ -84,6 +75,29 @@ static void gameLoadConfig(void) } } +static void gameSaveConfig(void) +{ + configSetFloat("Game.FovY", g_PlayerDefaultFovY); + configSetInt("Game.CenterHUD", g_HudAlignModeL == G_ASPECT_CENTER_EXT); + configSetInt("Game.ClassicCrouch", g_PlayerClassicCrouch); + configSetInt("Game.MouseAimMode", g_PlayerMouseAimMode); + configSetFloat("Game.MouseAimSpeedX", g_PlayerMouseAimSpeedX); + configSetFloat("Game.MouseAimSpeedY", g_PlayerMouseAimSpeedY); + configSetFloat("Game.CrosshairSway", g_PlayerCrosshairSway); + configSetFloat("Game.ScreenShakeIntensity", g_ViShakeIntensityMult); +} + +static void cleanup(void) +{ + sysLogPrintf(LOG_NOTE, "shutdown"); + inputSaveConfig(); + videoSaveConfig(); + gameSaveConfig(); + configSave(CONFIG_PATH); + crashShutdown(); + // TODO: actually shut down all subsystems +} + int main(int argc, const char **argv) { sysInitArgs(argc, argv); diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c new file mode 100644 index 000000000..3c2715d6c --- /dev/null +++ b/port/src/optionsmenu.c @@ -0,0 +1,597 @@ +#include +#include +#include +#include +#include "platform.h" +#include "data.h" +#include "types.h" +#include "game/mainmenu.h" +#include "game/menu.h" +#include "video.h" +#include "input.h" +#include "config.h" + +static MenuItemHandlerResult menuhandlerMouseEnabled(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return inputMouseIsEnabled(); + case MENUOP_SET: + inputMouseEnable(data->checkbox.value); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerMouseAimLock(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return g_PlayerMouseAimMode; + case MENUOP_SET: + g_PlayerMouseAimMode = data->checkbox.value; + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerMouseSpeedX(s32 operation, struct menuitem *item, union handlerdata *data) +{ + f32 x, y; + + switch (operation) { + case MENUOP_GETSLIDER: + inputMouseGetSpeed(&x, &y); + if (x < 0.f) { + data->slider.value = 0; + } else if (x > 10.f) { + data->slider.value = 100; + } else { + data->slider.value = x * 10.f + 0.5f; + } + break; + case MENUOP_SET: + inputMouseGetSpeed(&x, &y); + inputMouseSetSpeed((f32)data->slider.value / 10.f, y); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerMouseSpeedY(s32 operation, struct menuitem *item, union handlerdata *data) +{ + f32 x, y; + + switch (operation) { + case MENUOP_GETSLIDER: + inputMouseGetSpeed(&x, &y); + if (y < 0.f) { + data->slider.value = 0; + } else if (y > 10.f) { + data->slider.value = 100; + } else { + data->slider.value = y * 10.f + 0.5f; + } + break; + case MENUOP_SET: + inputMouseGetSpeed(&x, &y); + inputMouseSetSpeed(x, (f32)data->slider.value / 10.f); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerMouseAimSpeedX(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETSLIDER: + if (g_PlayerMouseAimSpeedX < 0.f) { + data->slider.value = 0; + } else if (g_PlayerMouseAimSpeedX > 10.f) { + data->slider.value = 100; + } else { + data->slider.value = g_PlayerMouseAimSpeedX * 10.f + 0.5f; + } + break; + case MENUOP_SET: + g_PlayerMouseAimSpeedX = (f32)data->slider.value / 10.f; + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerMouseAimSpeedY(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETSLIDER: + if (g_PlayerMouseAimSpeedY < 0.f) { + data->slider.value = 0; + } else if (g_PlayerMouseAimSpeedY > 10.f) { + data->slider.value = 100; + } else { + data->slider.value = g_PlayerMouseAimSpeedY * 10.f + 0.5f; + } + break; + case MENUOP_SET: + g_PlayerMouseAimSpeedY = (f32)data->slider.value / 10.f; + break; + } + + return 0; +} + +struct menuitem g_ExtendedMouseMenuItems[] = { + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Mouse Enabled", + 0, + menuhandlerMouseEnabled, + }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Mouse Aim Lock", + 0, + menuhandlerMouseAimLock, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + (uintptr_t)"Mouse Speed X", + 100, + menuhandlerMouseSpeedX, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + (uintptr_t)"Mouse Speed Y", + 100, + menuhandlerMouseSpeedY, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + (uintptr_t)"Crosshair Speed X", + 100, + menuhandlerMouseAimSpeedX, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + (uintptr_t)"Crosshair Speed Y", + 100, + menuhandlerMouseAimSpeedY, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ExtendedMouseMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Extended Mouse Options", + g_ExtendedMouseMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static MenuItemHandlerResult menuhandlerVibration(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETSLIDER: + data->slider.value = inputRumbleGetStrength() * 100.f + 0.5f; + break; + case MENUOP_SET: + inputRumbleSetStrength((f32)data->slider.value / 100.f); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerAnalogMovement(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return inputControllerGetDualAnalog(); + case MENUOP_SET: + inputControllerSetDualAnalog(data->checkbox.value); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerSwapSticks(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return inputControllerGetSticksSwapped(); + case MENUOP_SET: + inputControllerSetSticksSwapped(data->checkbox.value); + break; + } + + return 0; +} + +struct menuitem g_ExtendedControllerMenuItems[] = { + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Analog Movement", + 0, + menuhandlerAnalogMovement, + }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Swap Sticks", + 0, + menuhandlerSwapSticks, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Vibration", + 100, + menuhandlerVibration, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ExtendedControllerMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Extended Controller Options", + g_ExtendedControllerMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static MenuItemHandlerResult menuhandlerFullScreen(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return videoGetFullscreen(); + case MENUOP_SET: + videoSetFullscreen(data->checkbox.value); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerTexFilter(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return (videoGetTextureFilter() != 0); + case MENUOP_SET: + videoSetTextureFilter(data->checkbox.value); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerTexFilter2D(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return videoGetTextureFilter2D(); + case MENUOP_SET: + videoSetTextureFilter2D(data->checkbox.value); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerCenterHUD(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return g_HudAlignModeL == G_ASPECT_CENTER_EXT; + case MENUOP_SET: + if (data->checkbox.value) { + g_HudAlignModeL = G_ASPECT_CENTER_EXT; + g_HudAlignModeR = G_ASPECT_CENTER_EXT; + } else { + g_HudAlignModeL = G_ASPECT_LEFT_EXT; + g_HudAlignModeR = G_ASPECT_RIGHT_EXT; + } + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerFieldOfView(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETSLIDER: + data->slider.value = g_PlayerDefaultFovY + 0.5f; + break; + case MENUOP_SET: + if (data->slider.value >= 15) { + g_PlayerDefaultFovY = data->slider.value; + if (g_PlayerFovAffectsZoom) { + g_PlayerFovZoomMultiplier = g_PlayerDefaultFovY / 60.0f; + } + } + break; + } + + return 0; +} + +struct menuitem g_ExtendedDisplayMenuItems[] = { + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Full Screen", + 0, + menuhandlerFullScreen, + }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Texture Filtering", + 0, + menuhandlerTexFilter, + }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"GUI Texture Filtering", + 0, + menuhandlerTexFilter2D, + }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Center HUD", + 0, + menuhandlerCenterHUD, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Vert FOV", + 170, + menuhandlerFieldOfView, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ExtendedDisplayMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Extended Display Options", + g_ExtendedDisplayMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static MenuItemHandlerResult menuhandlerClassicCrouch(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return g_PlayerClassicCrouch; + case MENUOP_SET: + g_PlayerClassicCrouch = data->checkbox.value; + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerCrosshairSway(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETSLIDER: + data->slider.value = g_PlayerCrosshairSway * 10.f; + break; + case MENUOP_SET: + g_PlayerCrosshairSway = data->slider.value / 10.f; + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerScreenShake(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETSLIDER: + data->slider.value = g_ViShakeIntensityMult * 10.f; + break; + case MENUOP_SET: + g_ViShakeIntensityMult = data->slider.value / 10.f; + break; + } + + return 0; +} + +struct menuitem g_ExtendedGameMenuItems[] = { + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Allow Classic Crouch", + 0, + menuhandlerClassicCrouch, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + (uintptr_t)"Crosshair Sway", + 20, + menuhandlerCrosshairSway, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + (uintptr_t)"Explosion Shake", + 20, + menuhandlerScreenShake, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ExtendedGameMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Extended Game Options", + g_ExtendedGameMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +struct menuitem g_ExtendedMenuItems[] = { + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Display\n", + 0, + (void *)&g_ExtendedDisplayMenuDialog, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Mouse\n", + 0, + (void *)&g_ExtendedMouseMenuDialog, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Controller\n", + 0, + (void *)&g_ExtendedControllerMenuDialog, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Game\n", + 0, + (void *)&g_ExtendedGameMenuDialog, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ExtendedMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Extended Options", + g_ExtendedMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; diff --git a/port/src/video.c b/port/src/video.c index 48fbd4821..778303100 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -15,6 +15,7 @@ static struct GfxWindowManagerAPI *wmAPI; static struct GfxRenderingAPI *renderingAPI; static bool initDone = false; +static bool isFullscreen = false; static u32 dlcount = 0; static u32 frames = 0; @@ -31,7 +32,7 @@ s32 videoInit(void) const s32 w = configGetInt("Video.DefaultWidth", 640); const s32 h = configGetInt("Video.DefaultHeight", 480); - const bool fs = configGetInt("Video.DefaultFullscreen", false); + isFullscreen = configGetInt("Video.DefaultFullscreen", false); gfx_framebuffers_enabled = (bool)configGetIntClamped("Video.FramebufferEffects", 1, 0, 1); @@ -40,7 +41,7 @@ s32 videoInit(void) gfx_msaa_level = 1; } - gfx_init(wmAPI, renderingAPI, "PD", fs, w, h, 100, 100); + gfx_init(wmAPI, renderingAPI, "PD", isFullscreen, w, h, 100, 100); wmAPI->set_swap_interval(configGetInt("Video.VSync", 1)); wmAPI->set_target_fps(configGetInt("Video.FramerateLimit", 0)); // disabled because vsync is on @@ -134,6 +135,11 @@ s32 videoGetHeight(void) return gfx_current_dimensions.height; } +s32 videoGetFullscreen(void) +{ + return isFullscreen; +} + f32 videoGetAspect(void) { return gfx_current_dimensions.aspect_ratio; @@ -144,12 +150,36 @@ u32 videoGetTextureFilter2D(void) return texFilter2D; } +u32 videoGetTextureFilter(void) +{ + return renderingAPI->get_texture_filter(); +} + void videoSetWindowOffset(s32 x, s32 y) { gfx_current_game_window_viewport.x = x; gfx_current_game_window_viewport.y = y; } +void videoSetFullscreen(s32 fs) +{ + if (fs != isFullscreen) { + isFullscreen = !!fs; + wmAPI->set_fullscreen(isFullscreen); + } +} + +void videoSetTextureFilter(u32 filter) +{ + if (filter > FILTER_THREE_POINT) filter = FILTER_THREE_POINT; + renderingAPI->set_texture_filter((enum FilteringMode)filter); +} + +void videoSetTextureFilter2D(u32 filter) +{ + texFilter2D = !!filter; +} + s32 videoCreateFramebuffer(u32 w, u32 h, s32 upscale, s32 autoresize) { return gfx_create_framebuffer(w, h, upscale, autoresize); @@ -190,3 +220,10 @@ void videoFreeCachedTexture(const void *texptr) { gfx_texture_cache_delete(texptr); } + +void videoSaveConfig(void) +{ + configSetInt("Video.DefaultFullscreen", isFullscreen); + configSetInt("Video.TextureFilter", (u32)renderingAPI->get_texture_filter()); + configSetInt("Video.TextureFilter2D", texFilter2D); +} diff --git a/src/game/bondwalk.c b/src/game/bondwalk.c index ae9ab2263..edbfeaba4 100644 --- a/src/game/bondwalk.c +++ b/src/game/bondwalk.c @@ -26,6 +26,9 @@ #include "lib/collision.h" #include "data.h" #include "types.h" +#ifndef PLATFORM_N64 +extern f32 fabsf(f32); +#endif void bwalkInit(void) { diff --git a/src/game/mainmenu.c b/src/game/mainmenu.c index 5a0710ca9..29ab9fab0 100644 --- a/src/game/mainmenu.c +++ b/src/game/mainmenu.c @@ -39,6 +39,9 @@ u8 g_InventoryWeapon; struct menudialogdef g_2PMissionControlStyleMenuDialog; struct menudialogdef g_CiControlPlayer2MenuDialog; struct menudialogdef g_CinemaMenuDialog; +#ifndef PLATFORM_N64 +extern struct menudialogdef g_ExtendedMenuDialog; +#endif char *menuTextCurrentStageName(struct menuitem *item) { @@ -3429,15 +3432,23 @@ struct menuitem g_ExitGameMenuItems[] = { { MENUITEMTYPE_LABEL, 0, - MENUITEMFLAG_00000002 | MENUITEMFLAG_LESSLEFTPADDING, - L_OPTIONS_110, // "Exit" + MENUITEMFLAG_SELECTABLE_CENTRE | MENUITEMFLAG_LESSHEIGHT | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Are you sure you want to exit?\n", 0, NULL, }, { - MENUITEMTYPE_SELECTABLE, + MENUITEMTYPE_SEPARATOR, 0, 0, + 0x00000082, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CENTRE, L_OPTIONS_190, // "Yes" 0, menuhandlerExitGame, @@ -3445,7 +3456,7 @@ struct menuitem g_ExitGameMenuItems[] = { { MENUITEMTYPE_SELECTABLE, 0, - MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + MENUITEMFLAG_SELECTABLE_CENTRE | MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, L_OPTIONS_191, // "No" 0, NULL, @@ -3497,6 +3508,16 @@ struct menuitem g_SoloMissionOptionsMenuItems[] = { 0, (void *)&g_MissionDisplayOptionsMenuDialog, }, +#ifndef PLATFORM_N64 + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_BIGFONT | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Extended", + 0, + (void *)&g_ExtendedMenuDialog, + }, +#endif { MENUITEMTYPE_END }, }; @@ -3533,6 +3554,16 @@ struct menuitem g_2PMissionOptionsHMenuItems[] = { 0, (void *)&g_MissionDisplayOptionsMenuDialog, }, +#ifndef PLATFORM_N64 + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Extended", + 0, + (void *)&g_ExtendedMenuDialog, + }, +#endif { MENUITEMTYPE_SEPARATOR, 0, @@ -3589,6 +3620,16 @@ struct menuitem g_2PMissionOptionsVMenuItems[] = { 0, (void *)&g_2PMissionDisplayOptionsVMenuDialog, }, +#ifndef PLATFORM_N64 + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Extended", + 0, + (void *)&g_ExtendedMenuDialog, + }, +#endif { MENUITEMTYPE_SEPARATOR, 0, @@ -3657,6 +3698,16 @@ struct menuitem g_CiOptionsMenuItems[] = { 6, (void *)&g_CinemaMenuDialog, }, +#ifndef PLATFORM_N64 + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_BIGFONT | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Extended", + 7, + (void *)&g_ExtendedMenuDialog, + }, +#endif { MENUITEMTYPE_END }, }; @@ -4886,8 +4937,8 @@ struct menuitem g_MainMenuMenuItems[] = { { MENUITEMTYPE_SELECTABLE, 0, - MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_BIGFONT, - L_OPTIONS_110, // "Exit Game" + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_BIGFONT | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Exit Game", 0x00000007, (void *)&g_ExitGameMenuDialog, }, diff --git a/src/game/menu.c b/src/game/menu.c index 507dacc09..fa29c900d 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -488,11 +488,21 @@ char *menuResolveText(uintptr_t thing, void *dialogoritem) char *menuResolveParam2Text(struct menuitem *item) { +#ifndef PLATFORM_N64 + if (item->flags & MENUITEMFLAG_LITERAL_TEXT) { + return (const char *)item->param2; + } +#endif return menuResolveText(item->param2, item); } char *menuResolveDialogTitle(struct menudialogdef *dialogdef) { +#ifndef PLATFORM_N64 + if (dialogdef->flags & MENUDIALOGFLAG_LITERAL_TEXT) { + return (const char *)dialogdef->title; + } +#endif return menuResolveText(dialogdef->title, dialogdef); } @@ -746,6 +756,11 @@ void menuCalculateItemSize(struct menuitem *item, s16 *width, s16 *height, struc #endif if ((item->flags & (MENUITEMFLAG_LABEL_HASRIGHTTEXT | MENUITEMFLAG_BIGFONT)) == 0) { +#ifndef PLATFORM_N64 + if (item->flags & MENUITEMFLAG_LITERAL_TEXT) { + text = (const char *)item->param3; + } else +#endif text = menuResolveText(item->param3, item); // @bug: This is not how you check for an empty string diff --git a/src/game/player.c b/src/game/player.c index 351e6a7b8..2ecffb6a2 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -206,8 +206,8 @@ s32 g_NumDeathAnimations = 0; f32 g_PlayerDefaultFovY = 60.f; f32 g_PlayerCrosshairSway = 1.f; s32 g_PlayerMouseAimMode = MOUSEAIM_CLASSIC; -f32 g_PlayerMouseAimSpeedX = 0.75f; -f32 g_PlayerMouseAimSpeedY = 0.75f; +f32 g_PlayerMouseAimSpeedX = 0.7f; +f32 g_PlayerMouseAimSpeedY = 0.7f; s32 g_PlayerFovAffectsZoom = 1; f32 g_PlayerFovZoomMultiplier = 1.0f; s32 g_PlayerClassicCrouch = true; diff --git a/src/include/constants.h b/src/include/constants.h index 14a1bc83b..e2751bdcf 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -1618,6 +1618,9 @@ #define MENUDIALOGFLAG_0400 0x0400 #define MENUDIALOGFLAG_DROPOUTONCLOSE 0x0800 #define MENUDIALOGFLAG_1000 0x1000 +#ifndef PLATFORM_N64 +#define MENUDIALOGFLAG_LITERAL_TEXT 0x2000 +#endif #define MENUDIALOGSTATE_PREOPEN 0 #define MENUDIALOGSTATE_OPENING 1 @@ -1658,6 +1661,7 @@ #define MENUITEMFLAG_LABEL_CUSTOMCOLOUR 0x01000000 #define MENUITEMFLAG_LESSHEIGHT 0x02000000 #define MENUITEMFLAG_CAROUSEL_04000000 0x04000000 +#define MENUITEMFLAG_LITERAL_TEXT 0x08000000 #define MENUITEMTYPE_LABEL 0x01 #define MENUITEMTYPE_LIST 0x02 From 2813ecedeb92d6e658e23dbb5714caba1f6578ec Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 28 Oct 2023 13:45:12 +0200 Subject: [PATCH 10/36] port: fix some sliders in extended options --- port/src/optionsmenu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 3c2715d6c..33efa966d 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -458,10 +458,10 @@ static MenuItemHandlerResult menuhandlerCrosshairSway(s32 operation, struct menu { switch (operation) { case MENUOP_GETSLIDER: - data->slider.value = g_PlayerCrosshairSway * 10.f; + data->slider.value = g_PlayerCrosshairSway * 10.f + 0.5f; break; case MENUOP_SET: - g_PlayerCrosshairSway = data->slider.value / 10.f; + g_PlayerCrosshairSway = (f32)data->slider.value / 10.f; break; } @@ -472,10 +472,10 @@ static MenuItemHandlerResult menuhandlerScreenShake(s32 operation, struct menuit { switch (operation) { case MENUOP_GETSLIDER: - data->slider.value = g_ViShakeIntensityMult * 10.f; + data->slider.value = g_ViShakeIntensityMult * 10.f + 0.5f; break; case MENUOP_SET: - g_ViShakeIntensityMult = data->slider.value / 10.f; + g_ViShakeIntensityMult = (f32)data->slider.value / 10.f; break; } From cea95279362b5a231f084a048669708a909da248 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 28 Oct 2023 14:36:00 +0200 Subject: [PATCH 11/36] port: add controller axis settings also make sliders look nicer and save some input settings on quit --- port/include/input.h | 6 ++ port/src/input.c | 37 +++++++++ port/src/optionsmenu.c | 174 +++++++++++++++++++++++++++++++++++++--- src/game/menu.c | 3 + src/include/constants.h | 1 + 5 files changed, 212 insertions(+), 9 deletions(-) diff --git a/port/include/input.h b/port/include/input.h index 85b14641b..e3f21f0b3 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -99,6 +99,12 @@ void inputControllerSetSticksSwapped(s32 swapped); s32 inputControllerGetDualAnalog(void); void inputControllerSetDualAnalog(s32 enable); +f32 inputControllerGetAxisScale(s32 stick, s32 axis); +void inputControllerSetAxisScale(s32 stick, s32 axis, f32 value); + +f32 inputControllerGetAxisDeadzone(s32 stick, s32 axis); +void inputControllerSetAxisDeadzone(s32 stick, s32 axis, f32 value); + // vk is a value from the virtkey enum above s32 inputKeyPressed(u32 vk); diff --git a/port/src/input.c b/port/src/input.c index 2a1b291af..5103dd037 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -408,8 +408,25 @@ void inputSaveConfig(void) { inputSaveBinds(); + configSetInt("Input.MouseEnabled", mouseEnabled); configSetFloat("Input.MouseSpeedX", mouseSensX); configSetFloat("Input.MouseSpeedY", mouseSensY); + + configSetInt("Input.LStickDeadzoneX", deadzone[0]); + configSetInt("Input.LStickDeadzoneY", deadzone[1]); + configSetInt("Input.RStickDeadzoneX", deadzone[2]); + configSetInt("Input.RStickDeadzoneY", deadzone[3]); + + configSetFloat("Input.LStickScaleX", stickSens[0]); + configSetFloat("Input.LStickScaleY", stickSens[1]); + configSetFloat("Input.RStickScaleX", stickSens[2]); + configSetFloat("Input.RStickScaleY", stickSens[3]); + + configSetFloat("Input.RumbleScale", rumbleScale); + + configSetInt("Input.StickCButtons", stickCButtons); + + configGetInt("Input.SwapSticks", axisMap[0][0] == SDL_CONTROLLER_AXIS_RIGHTX); } s32 inputInit(void) @@ -694,6 +711,26 @@ void inputControllerSetDualAnalog(s32 enable) stickCButtons = !enable; } +f32 inputControllerGetAxisScale(s32 stick, s32 axis) +{ + return stickSens[stick * 2 + axis]; +} + +void inputControllerSetAxisScale(s32 stick, s32 axis, f32 value) +{ + stickSens[stick * 2 + axis] = value; +} + +f32 inputControllerGetAxisDeadzone(s32 stick, s32 axis) +{ + return (f32)deadzone[stick * 2 + axis] / 32767.f; +} + +void inputControllerSetAxisDeadzone(s32 stick, s32 axis, f32 value) +{ + deadzone[stick * 2 + axis] = value * 32767.f; +} + void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk) { if (idx < 0 || idx >= INPUT_MAX_CONTROLLERS || bind >= MAX_BINDS || ck >= CK_TOTAL_COUNT) { diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 33efa966d..f1d827108 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -142,10 +142,18 @@ struct menuitem g_ExtendedMouseMenuItems[] = { 0, menuhandlerMouseAimLock, }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, { MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, (uintptr_t)"Mouse Speed X", 100, menuhandlerMouseSpeedX, @@ -153,7 +161,7 @@ struct menuitem g_ExtendedMouseMenuItems[] = { { MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, (uintptr_t)"Mouse Speed Y", 100, menuhandlerMouseSpeedY, @@ -161,7 +169,7 @@ struct menuitem g_ExtendedMouseMenuItems[] = { { MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, (uintptr_t)"Crosshair Speed X", 100, menuhandlerMouseAimSpeedX, @@ -169,7 +177,7 @@ struct menuitem g_ExtendedMouseMenuItems[] = { { MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, (uintptr_t)"Crosshair Speed Y", 100, menuhandlerMouseAimSpeedY, @@ -202,14 +210,154 @@ struct menudialogdef g_ExtendedMouseMenuDialog = { NULL, }; +static MenuItemHandlerResult menuhandlerStickSpeed(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerStickDeadzone(s32 operation, struct menuitem *item, union handlerdata *data); + +struct menuitem g_ExtendedStickMenuItems[] = { + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"LStick Scale X", + 20, + menuhandlerStickSpeed, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"LStick Scale Y", + 20, + menuhandlerStickSpeed, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"RStick Scale X", + 20, + menuhandlerStickSpeed, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"RStick Scale Y", + 20, + menuhandlerStickSpeed, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"LStick Deadzone X", + 32, + menuhandlerStickDeadzone, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"LStick Deadzone Y", + 32, + menuhandlerStickDeadzone, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"RStick Deadzone X", + 32, + menuhandlerStickDeadzone, + }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"RStick Deadzone Y", + 32, + menuhandlerStickDeadzone, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +static MenuItemHandlerResult menuhandlerStickSpeed(s32 operation, struct menuitem *item, union handlerdata *data) +{ + const s32 idx = item - g_ExtendedStickMenuItems; + const s32 stick = idx / 2; + const s32 axis = idx % 2; + + switch (operation) { + case MENUOP_GETSLIDER: + data->slider.value = inputControllerGetAxisScale(stick, axis) * 10.f + 0.5f; + break; + case MENUOP_SET: + inputControllerSetAxisScale(stick, axis, (f32)data->slider.value / 10.f); + break; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerStickDeadzone(s32 operation, struct menuitem *item, union handlerdata *data) +{ + const s32 idx = item - (g_ExtendedStickMenuItems + 5); + const s32 stick = idx / 2; + const s32 axis = idx % 2; + + switch (operation) { + case MENUOP_GETSLIDER: + data->slider.value = inputControllerGetAxisDeadzone(stick, axis) * 32.f + 0.5f; + break; + case MENUOP_SET: + inputControllerSetAxisDeadzone(stick, axis, (f32)data->slider.value / 32.f); + break; + } + + return 0; +} + +struct menudialogdef g_ExtendedStickMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Analog Stick Settings", + g_ExtendedStickMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + static MenuItemHandlerResult menuhandlerVibration(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { case MENUOP_GETSLIDER: - data->slider.value = inputRumbleGetStrength() * 100.f + 0.5f; + data->slider.value = inputRumbleGetStrength() * 10.f + 0.5f; break; case MENUOP_SET: - inputRumbleSetStrength((f32)data->slider.value / 100.f); + inputRumbleSetStrength((f32)data->slider.value / 10.f); break; } @@ -259,12 +407,20 @@ struct menuitem g_ExtendedControllerMenuItems[] = { 0, menuhandlerSwapSticks, }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Stick Settings...\n", + 0, + (void *)&g_ExtendedStickMenuDialog, + }, { MENUITEMTYPE_SLIDER, 0, MENUITEMFLAG_LITERAL_TEXT, (uintptr_t)"Vibration", - 100, + 10, menuhandlerVibration, }, { @@ -494,7 +650,7 @@ struct menuitem g_ExtendedGameMenuItems[] = { { MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, (uintptr_t)"Crosshair Sway", 20, menuhandlerCrosshairSway, @@ -502,7 +658,7 @@ struct menuitem g_ExtendedGameMenuItems[] = { { MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_ALTSIZE, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, (uintptr_t)"Explosion Shake", 20, menuhandlerScreenShake, diff --git a/src/game/menu.c b/src/game/menu.c index fa29c900d..b8d1633e9 100644 --- a/src/game/menu.c +++ b/src/game/menu.c @@ -670,6 +670,9 @@ void menuCalculateItemSize(struct menuitem *item, s16 *width, s16 *height, struc if (item->flags & MENUITEMFLAG_SLIDER_ALTSIZE) { *height = 22; *width = 120; + } else if (item->flags & MENUITEMFLAG_SLIDER_WIDE) { + *width = 200; + *height = VERSION == VERSION_JPN_FINAL ? 14 : 12; } break; case MENUITEMTYPE_CHECKBOX: diff --git a/src/include/constants.h b/src/include/constants.h index e2751bdcf..cb7b93a7f 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -1662,6 +1662,7 @@ #define MENUITEMFLAG_LESSHEIGHT 0x02000000 #define MENUITEMFLAG_CAROUSEL_04000000 0x04000000 #define MENUITEMFLAG_LITERAL_TEXT 0x08000000 +#define MENUITEMFLAG_SLIDER_WIDE 0x10000000 #define MENUITEMTYPE_LABEL 0x01 #define MENUITEMTYPE_LIST 0x02 From c8db5fa106a813c4e76941d49233d482ee7dbefc Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 28 Oct 2023 18:17:35 +0200 Subject: [PATCH 12/36] port: add keybind menu --- port/include/input.h | 8 ++ port/src/input.c | 96 ++++++++++++-- port/src/optionsmenu.c | 288 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 378 insertions(+), 14 deletions(-) diff --git a/port/include/input.h b/port/include/input.h index e3f21f0b3..9e68791f7 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -6,6 +6,7 @@ #define INPUT_MAX_CONTROLLERS MAXCONTROLLERS #define INPUT_MAX_CONTROLLER_BUTTONS 32 +#define INPUT_MAX_BINDS 4 #define CONT_STICK_XNEG 0x10000 #define CONT_STICK_XPOS 0x20000 @@ -115,6 +116,8 @@ s32 inputButtonPressed(s32 idx, u32 contbtn); // if bind is -1, picks a bind slot automatically void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk); +const u32 *inputKeyGetBinds(s32 idx, u32 ck); + // get VK_ value from human-readable name s32 inputGetKeyByName(const char *name); @@ -165,4 +168,9 @@ void inputUpdate(void); // call this before configSave() void inputSaveConfig(void); +void inputSetDefaultKeyBinds(void); + +void inputClearLastKey(void); +s32 inputGetLastKey(void); + #endif diff --git a/port/src/input.c b/port/src/input.c index 5103dd037..ddb5b5675 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -10,7 +10,6 @@ #include "config.h" #include "system.h" -#define MAX_BINDS 4 #define TRIG_THRESHOLD (30 * 256) #define DEFAULT_DEADZONE 4096 #define DEFAULT_DEADZONE_RY 6144 @@ -21,7 +20,7 @@ static SDL_GameController *pads[INPUT_MAX_CONTROLLERS]; static s32 rumbleSupported[INPUT_MAX_CONTROLLERS]; -static u32 binds[MAXCONTROLLERS][CK_TOTAL_COUNT][MAX_BINDS]; // [i][CK_][b] = [VK_] +static u32 binds[MAXCONTROLLERS][CK_TOTAL_COUNT][INPUT_MAX_BINDS]; // [i][CK_][b] = [VK_] static s32 connectedMask = 0; @@ -37,6 +36,8 @@ static f32 mouseSensY = 1.5f; static f32 rumbleScale = 0.5f; +static s32 lastKey = 0; + // NOTE: by default this gets inverted for 1.2: "right stick" here means left stick on your controller static u32 axisMap[2][2] = { { SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY }, @@ -186,6 +187,8 @@ void inputSetDefaultKeyBinds(void) { CK_8000, SDL_CONTROLLER_BUTTON_LEFTSTICK }, }; + memset(binds, 0, sizeof(binds)); + for (u32 i = 0; i < sizeof(kbbinds) / sizeof(kbbinds[0]); ++i) { for (s32 j = 1; j < 3; ++j) { if (kbbinds[i][j]) { @@ -233,6 +236,18 @@ static inline void inputCloseController(const s32 cidx) } } + +static inline s32 inputControllerGetIndex(SDL_GameController *ctrl) { + if (ctrl) { + for (s32 i = 0; i < INPUT_MAX_CONTROLLERS; ++i) { + if (pads[i] == ctrl) { + return i; + } + } + } + return -1; +} + static int inputEventFilter(void *data, SDL_Event *event) { switch (event->type) { @@ -250,19 +265,54 @@ static int inputEventFilter(void *data, SDL_Event *event) case SDL_CONTROLLERDEVICEREMOVED: { SDL_GameController *ctrl = SDL_GameControllerFromInstanceID(event->cdevice.which); - if (ctrl) { - for (s32 i = 0; i < INPUT_MAX_CONTROLLERS; ++i) { - if (pads[i] == ctrl) { - inputCloseController(i); - break; - } - } + const s32 idx = inputControllerGetIndex(ctrl); + if (idx >= 0) { + inputCloseController(idx); } break; } case SDL_MOUSEWHEEL: mouseWheel = event->wheel.y; + if (!lastKey && mouseWheel) { + lastKey = (mouseWheel < 0) + VK_MOUSE_WHEEL_UP; + } + break; + + case SDL_MOUSEBUTTONDOWN: + if (!lastKey) { + lastKey = VK_MOUSE_BEGIN - 1 + event->button.button; + } + break; + + case SDL_KEYDOWN: + if (!lastKey) { + lastKey = VK_KEYBOARD_BEGIN + event->key.keysym.scancode; + } + break; + + case SDL_CONTROLLERBUTTONDOWN: + if (!lastKey) { + lastKey = VK_JOY1_BEGIN + event->cbutton.button; + SDL_GameController *ctrl = SDL_GameControllerFromInstanceID(event->cdevice.which); + const s32 idx = inputControllerGetIndex(ctrl); + if (idx >= 0) { + lastKey += idx * INPUT_MAX_CONTROLLER_BUTTONS; + } + } + break; + + case SDL_CONTROLLERAXISMOTION: + if (!lastKey) { + if (event->caxis.axis >= SDL_CONTROLLER_AXIS_TRIGGERLEFT && event->caxis.value > TRIG_THRESHOLD) { + lastKey = VK_JOY1_LTRIG + (event->caxis.axis - SDL_CONTROLLER_AXIS_TRIGGERLEFT); + SDL_GameController *ctrl = SDL_GameControllerFromInstanceID(event->cdevice.which); + const s32 idx = inputControllerGetIndex(ctrl); + if (idx >= 0) { + lastKey += idx * INPUT_MAX_CONTROLLER_BUTTONS; + } + } + } break; default: @@ -349,7 +399,7 @@ static inline void inputSaveBinds(void) for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); bindstr[0] = '\0'; - for (s32 b = 0; b < MAX_BINDS; ++b) { + for (s32 b = 0; b < INPUT_MAX_BINDS; ++b) { if (binds[i][ck][b]) { if (b) { strncat(bindstr, ", ", sizeof(bindstr) - 1); @@ -500,7 +550,7 @@ s32 inputInit(void) static inline s32 inputBindPressed(const s32 idx, const u32 ck) { - for (s32 i = 0; i < MAX_BINDS; ++i) { + for (s32 i = 0; i < INPUT_MAX_BINDS; ++i) { if (binds[idx][ck][i]) { if (inputKeyPressed(binds[idx][ck][i])) { return 1; @@ -733,26 +783,34 @@ void inputControllerSetAxisDeadzone(s32 stick, s32 axis, f32 value) void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk) { - if (idx < 0 || idx >= INPUT_MAX_CONTROLLERS || bind >= MAX_BINDS || ck >= CK_TOTAL_COUNT) { + if (idx < 0 || idx >= INPUT_MAX_CONTROLLERS || bind >= INPUT_MAX_BINDS || ck >= CK_TOTAL_COUNT) { return; } if (bind < 0) { - for (s32 i = 0; i < MAX_BINDS; ++i) { + for (s32 i = 0; i < INPUT_MAX_BINDS; ++i) { if (binds[idx][ck][i] == 0) { bind = i; break; } } if (bind < 0) { - bind = MAX_BINDS - 1; // just overwrite last + bind = INPUT_MAX_BINDS - 1; // just overwrite last } } binds[idx][ck][bind] = vk; } +const u32 *inputKeyGetBinds(s32 idx, u32 ck) +{ + if (idx < 0 || idx >= INPUT_MAX_CONTROLLERS || ck >= CK_TOTAL_COUNT) { + return NULL; + } + return binds[idx][ck]; +} + s32 inputKeyPressed(u32 vk) { if (vk >= VK_KEYBOARD_BEGIN && vk < VK_MOUSE_BEGIN) { @@ -935,3 +993,13 @@ s32 inputGetKeyByName(const char *name) return -1; } + +void inputClearLastKey(void) +{ + lastKey = 0; +} + +s32 inputGetLastKey(void) +{ + return lastKey; +} diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index f1d827108..3fde8001f 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -11,6 +11,11 @@ #include "input.h" #include "config.h" +static s32 g_BindsPlayer = 0; +static s32 g_BindIndex = 0; +static u32 g_BindContKey = 0; +static char g_BindsPlayerText[] = "Player 1 Bindings"; + static MenuItemHandlerResult menuhandlerMouseEnabled(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { @@ -691,6 +696,281 @@ struct menudialogdef g_ExtendedGameMenuDialog = { NULL, }; +static MenuItemHandlerResult menuhandlerDoBind(s32 operation, struct menuitem *item, union handlerdata *data); + +struct menuitem g_ExtendedBindKeyMenuItems[] = { + { + MENUITEMTYPE_LABEL, + 0, + MENUITEMFLAG_SELECTABLE_CENTRE | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"\n", + 0, + NULL, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CENTRE | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Press new key or button...\n", + 0, + menuhandlerDoBind, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ExtendedBindKeyMenuDialog = { + MENUDIALOGTYPE_WHITE, + (uintptr_t)"Bind", + g_ExtendedBindKeyMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT | MENUDIALOGFLAG_IGNOREBACK | MENUDIALOGFLAG_STARTSELECTS, + NULL, +}; + +struct menubind { + u32 ck; + const char *name; +}; + +static const struct menubind menuBinds[] = { + { CK_ZTRIG, "Fire [ZT]\n" }, + { CK_LTRIG, "Fire Mode [LT]\n" }, + { CK_RTRIG, "Aim Mode [RT]\n" }, + { CK_A, "Use / Accept [A]\n" }, + { CK_B, "Use / Cancel [B]\n" }, + { CK_X, "Reload [X]\n" }, + { CK_Y, "Next Weapon [Y]\n" }, + { CK_DPAD_L, "Prev Weapon [DL]\n" }, + { CK_DPAD_L, "Radial Menu [DD]\n" }, + { CK_START, "Pause Menu [ST]\n" }, + { CK_8000, "Cycle Crouch [+]\n" }, + { CK_4000, "Half Crouch [+]\n" }, + { CK_2000, "Full Crouch [+]\n" }, +}; + +static const char *menutextBind(struct menuitem *item); +static MenuItemHandlerResult menuhandlerBind(s32 operation, struct menuitem *item, union handlerdata *data); +static MenuItemHandlerResult menuhandlerResetBinds(s32 operation, struct menuitem *item, union handlerdata *data); + +#define DEFINE_MENU_BIND() \ + { \ + MENUITEMTYPE_DROPDOWN, \ + 0, \ + 0, \ + (uintptr_t)menutextBind, \ + 0, \ + menuhandlerBind, \ + } + +struct menuitem g_ExtendedBindsMenuItems[] = { + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + DEFINE_MENU_BIND(), + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Reset to Defaults\n", + 0, + menuhandlerResetBinds, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +static MenuItemHandlerResult menuhandlerDoBind(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (!menuIsDialogOpen(&g_ExtendedBindKeyMenuDialog)) { + return 0; + } + + if (inputKeyPressed(VK_ESCAPE)) { + menuPopDialog(); + return 0; + } + + const s32 key = inputGetLastKey(); + if (key && key != VK_ESCAPE) { + inputKeyBind(g_BindsPlayer, g_BindContKey, g_BindIndex, key); + menuPopDialog(); + } + + return 0; +} + +static const char *menutextBind(struct menuitem *item) +{ + return menuBinds[item - g_ExtendedBindsMenuItems].name; +} + +static MenuItemHandlerResult menuhandlerBind(s32 operation, struct menuitem *item, union handlerdata *data) +{ + const s32 idx = item - g_ExtendedBindsMenuItems; + const u32 *binds; + + static char keyname[128]; + + switch (operation) { + case MENUOP_GETOPTIONCOUNT: + data->dropdown.value = INPUT_MAX_BINDS; + break; + case MENUOP_GETOPTIONTEXT: + binds = inputKeyGetBinds(g_BindsPlayer, menuBinds[idx].ck); + if (binds && binds[data->dropdown.value]) { + strncpy(keyname, inputGetKeyName(binds[data->dropdown.value]), sizeof(keyname) - 1); + for (char *p = keyname; *p; ++p) { + if (*p == '_') *p = ' '; + } + return (intptr_t)keyname; + } + return (intptr_t)"NONE"; + case MENUOP_SET: + g_ExtendedBindKeyMenuItems[0].param2 = (uintptr_t)menuBinds[idx].name; + g_BindIndex = data->dropdown.value; + g_BindContKey = menuBinds[idx].ck; + inputClearLastKey(); + menuPushDialog(&g_ExtendedBindKeyMenuDialog); + break; + case MENUOP_GETSELECTEDINDEX: + data->dropdown.value = 0; + } + + return 0; +} + +static MenuItemHandlerResult menuhandlerResetBinds(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + inputSetDefaultKeyBinds(); + } + + return 0; +} + +struct menudialogdef g_ExtendedBindsMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)g_BindsPlayerText, + g_ExtendedBindsMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT | MENUDIALOGFLAG_STARTSELECTS | MENUDIALOGFLAG_IGNOREBACK, + NULL, +}; + +static MenuItemHandlerResult menuhandlerOpenBinds(s32 operation, struct menuitem *item, union handlerdata *data); + +struct menuitem g_ExtendedSelectBindsMenuItems[] = { + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Player 1\n", + 0, + menuhandlerOpenBinds, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Player 2\n", + 0, + menuhandlerOpenBinds, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Player 3\n", + 0, + menuhandlerOpenBinds, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Player 4\n", + 0, + menuhandlerOpenBinds, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +static MenuItemHandlerResult menuhandlerOpenBinds(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + g_BindsPlayer = item - g_ExtendedSelectBindsMenuItems; + g_BindsPlayerText[7] = g_BindsPlayer + '1'; + menuPushDialog(&g_ExtendedBindsMenuDialog); + } + + return 0; +} + +struct menudialogdef g_ExtendedSelectBindsMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Key Bindings", + g_ExtendedSelectBindsMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + struct menuitem g_ExtendedMenuItems[] = { { MENUITEMTYPE_SELECTABLE, @@ -724,6 +1004,14 @@ struct menuitem g_ExtendedMenuItems[] = { 0, (void *)&g_ExtendedGameMenuDialog, }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Key Bindings\n", + 0, + (void *)&g_ExtendedSelectBindsMenuDialog, + }, { MENUITEMTYPE_SEPARATOR, 0, From 50b74e4280fabb9f3a35b05e350eb023c9a48c13 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 29 Oct 2023 18:28:04 +0100 Subject: [PATCH 13/36] port: fix #204; allow players 2-4 to use lstick in radial menu --- src/game/activemenutick.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/game/activemenutick.c b/src/game/activemenutick.c index 577068a49..6638ad876 100644 --- a/src/game/activemenutick.c +++ b/src/game/activemenutick.c @@ -50,8 +50,14 @@ void amTick(void) s8 csticky = joyGetStickYOnSample(j, contpadnum); s8 crstickx = joyGetRStickXOnSample(j, contpadnum); s8 crsticky = joyGetRStickYOnSample(j, contpadnum); +#ifdef AVOID_UB + // if cstickx is -128, it will get negated and stored into absstickx, negating it again if it's 8 bit + s32 absstickx; + s32 abssticky; +#else s8 absstickx; s8 abssticky; +#endif u32 buttonsstate = joyGetButtonsOnSample(j, contpadnum, 0xffffffff); u32 buttonspressed = joyGetButtonsPressedOnSample(j, contpadnum, 0xffffffff); bool stickpushed = false; @@ -69,6 +75,8 @@ void amTick(void) g_AmMenus[g_AmIndex].allbots = false; #ifndef PLATFORM_N64 + s32 newstickx = (s32)cstickx + (s32)crstickx; + s32 newsticky = (s32)csticky + (s32)crsticky; if (j == 0 && g_Vars.currentplayernum == 0 && inputMouseIsLocked()) { f32 mdx, mdy; struct activemenu *am = &g_AmMenus[g_AmIndex]; @@ -79,11 +87,11 @@ void amTick(void) am->mousex = (am->mousex > 127.f) ? 127.f : (am->mousex < -128.f) ? -128.f : am->mousex; am->mousey = (am->mousey > 127.f) ? 127.f : (am->mousey < -128.f) ? -128.f : am->mousey; } - const s32 newstickx = (s32)cstickx + (s32)crstickx + (s32)am->mousex; - const s32 newsticky = (s32)csticky + (s32)crsticky - (s32)am->mousey; - cstickx = (newstickx < -128) ? -128 : (newstickx > 127) ? 127 : newstickx; - csticky = (newsticky < -128) ? -128 : (newsticky > 127) ? 127 : newsticky; + newstickx += (s32)am->mousex; + newsticky -= (s32)am->mousey; } + cstickx = (newstickx < -128) ? -128 : (newstickx > 127) ? 127 : newstickx; + csticky = (newsticky < -128) ? -128 : (newsticky > 127) ? 127 : newsticky; #endif if (g_Vars.currentplayer->activemenumode == AMMODE_EDIT) { From 9b65199d3d42d539b885e5798b698b03ada5d052 Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Sun, 29 Oct 2023 15:48:50 -0700 Subject: [PATCH 14/36] fix: port: xbla-style quick detonate fix - adds A + X to list of quick-detonate keybinds: correct xbla behavior for quick detonating per the xbox implementation --- README.md | 32 ++++++++++++++++---------------- src/game/bondmove.c | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 4db9b92ab..58ce13c3e 100644 --- a/README.md +++ b/README.md @@ -62,22 +62,22 @@ Note that the mouse only controls player 1. Controls can be rebound in `pd.ini`. Default control scheme is as follows: -| Action | Keyboard and mouse | Xbox pad | N64 pad | -| - | - | - | - | -| Fire / Accept | LMB/Space | RT | Z Trigger | -| Aim mode | RMB/Z | LT | R Trigger | -| Use / Cancel | E | N/A | B | -| Use / Accept | N/A | A | A | -| Crouch cycle | N/A | L3 | `0x80000000` (Extra) | -| Half-Crouch | Shift | N/A | `0x40000000` (Extra) | -| Full-Crouch | Control | N/A | `0x20000000` (Extra) | -| Reload | R | X | X `(0x40)` | -| Previous weapon | Mousewheel forward | B | D-Left | -| Next weapon | Mousewheel back | Y | Y `(0x80)` | -| Radial menu | Q | LB | D-Down | -| Alt fire mode | F | RB | L Trigger | -| Alt-fire oneshot | F + LMB or E + LMB | A + RT or RB + RT | A + Z, L + Z | -| Quick-detonate | E + Q | A + B | A + D-Left | +| Action | Keyboard and mouse | Xbox pad | N64 pad | +| - | - | - | - | +| Fire / Accept | LMB/Space | RT | Z Trigger | +| Aim mode | RMB/Z | LT | R Trigger | +| Use / Cancel | E | N/A | B | +| Use / Accept | N/A | A | A | +| Crouch cycle | N/A | L3 | `0x80000000` (Extra) | +| Half-Crouch | Shift | N/A | `0x40000000` (Extra) | +| Full-Crouch | Control | N/A | `0x20000000` (Extra) | +| Reload | R | X | X `(0x40)` | +| Previous weapon | Mousewheel forward | B | D-Left | +| Next weapon | Mousewheel back | Y | Y `(0x80)` | +| Radial menu | Q | LB | D-Down | +| Alt fire mode | F | RB | L Trigger | +| Alt-fire oneshot | `F + LMB` or `E + LMB` | `A + RT`` or 'RB + RT` | `A + Z` or `L + Z` | +| Quick-detonate | `E + Q` or `E + R` | `A + B` or `A + X` | `A + D-Left`or `A + 0x40` | ## Building diff --git a/src/game/bondmove.c b/src/game/bondmove.c index a7ea7dfbe..47e778af6 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -1748,7 +1748,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i g_Vars.currentplayer->usedowntime = -2; } #else - bgunProcessQuickDetonate(&movedata, c1buttons, c1buttonsthisframe, (BUTTON_CANCEL_USE | BUTTON_ACCEPT_USE), (BUTTON_WPNBACK | BUTTON_RADIAL)); + bgunProcessQuickDetonate(&movedata, c1buttons, c1buttonsthisframe, (BUTTON_CANCEL_USE | BUTTON_ACCEPT_USE), (BUTTON_WPNBACK | BUTTON_RADIAL | BUTTON_RELOAD)); #endif } From 3feddd8c641de920283e690559f4566375b371ff Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Sun, 29 Oct 2023 16:13:04 -0700 Subject: [PATCH 15/36] port: fixes #205 radial movement issue --- src/game/activemenutick.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/game/activemenutick.c b/src/game/activemenutick.c index 6638ad876..23554f857 100644 --- a/src/game/activemenutick.c +++ b/src/game/activemenutick.c @@ -48,8 +48,6 @@ void amTick(void) s8 gotonextscreen = false; s8 cstickx = joyGetStickXOnSample(j, contpadnum); s8 csticky = joyGetStickYOnSample(j, contpadnum); - s8 crstickx = joyGetRStickXOnSample(j, contpadnum); - s8 crsticky = joyGetRStickYOnSample(j, contpadnum); #ifdef AVOID_UB // if cstickx is -128, it will get negated and stored into absstickx, negating it again if it's 8 bit s32 absstickx; @@ -75,8 +73,8 @@ void amTick(void) g_AmMenus[g_AmIndex].allbots = false; #ifndef PLATFORM_N64 - s32 newstickx = (s32)cstickx + (s32)crstickx; - s32 newsticky = (s32)csticky + (s32)crsticky; + s32 newstickx = (s32)cstickx; + s32 newsticky = (s32)csticky; if (j == 0 && g_Vars.currentplayernum == 0 && inputMouseIsLocked()) { f32 mdx, mdy; struct activemenu *am = &g_AmMenus[g_AmIndex]; @@ -98,8 +96,6 @@ void amTick(void) buttonsstate = buttonsstate & D_JPAD; cstickx = 0; csticky = 0; - crstickx = 0; - crsticky = 0; buttonspressed = 0; } From afb91ab9e9fd67da0030d5bff3a66774ff43aeb6 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Mon, 30 Oct 2023 19:00:17 +0100 Subject: [PATCH 16/36] port: add radial menu sensitivity slider --- port/src/main.c | 2 ++ port/src/optionsmenu.c | 29 +++++++++++++++++++++++++++++ src/game/activemenutick.c | 4 ++-- src/game/player.c | 1 + src/include/data.h | 1 + 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/port/src/main.c b/port/src/main.c index 056a489a8..8db10b5fe 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -64,6 +64,7 @@ static void gameLoadConfig(void) g_PlayerMouseAimMode = configGetIntClamped("Game.MouseAimMode", g_PlayerMouseAimMode, 0, 1); g_PlayerMouseAimSpeedX = configGetFloatClamped("Game.MouseAimSpeedX", g_PlayerMouseAimSpeedX, 0.f, 10.f); g_PlayerMouseAimSpeedY = configGetFloatClamped("Game.MouseAimSpeedY", g_PlayerMouseAimSpeedY, 0.f, 10.f); + g_PlayerRadialMenuSpeed = configGetFloatClamped("Game.RadialMenuSpeed", g_PlayerRadialMenuSpeed, 0.f, 10.f); g_PlayerFovAffectsZoom = configGetIntClamped("Game.FovAffectsZoom", g_PlayerFovAffectsZoom, 0, 1); g_PlayerFovZoomMultiplier = g_PlayerFovAffectsZoom ? g_PlayerDefaultFovY / 60.0f : 1.0f; g_PlayerClassicCrouch = configGetIntClamped("Game.ClassicCrouch", 0, 0, 1); @@ -83,6 +84,7 @@ static void gameSaveConfig(void) configSetInt("Game.MouseAimMode", g_PlayerMouseAimMode); configSetFloat("Game.MouseAimSpeedX", g_PlayerMouseAimSpeedX); configSetFloat("Game.MouseAimSpeedY", g_PlayerMouseAimSpeedY); + configSetFloat("Game.RadialMenuSpeed", g_PlayerRadialMenuSpeed); configSetFloat("Game.CrosshairSway", g_PlayerCrosshairSway); configSetFloat("Game.ScreenShakeIntensity", g_ViShakeIntensityMult); } diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 3fde8001f..efb81b0b4 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -130,6 +130,27 @@ static MenuItemHandlerResult menuhandlerMouseAimSpeedY(s32 operation, struct men return 0; } +static MenuItemHandlerResult menuhandlerRadialMenuSpeed(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GETSLIDER: + if (g_PlayerRadialMenuSpeed < 0.f) { + data->slider.value = 0; + } else if (g_PlayerRadialMenuSpeed> 10.f) { + data->slider.value = 100; + } else { + data->slider.value = g_PlayerRadialMenuSpeed * 10.f + 0.5f; + } + break; + case MENUOP_SET: + g_PlayerRadialMenuSpeed = (f32)data->slider.value / 10.f; + break; + } + + return 0; +} + + struct menuitem g_ExtendedMouseMenuItems[] = { { MENUITEMTYPE_CHECKBOX, @@ -187,6 +208,14 @@ struct menuitem g_ExtendedMouseMenuItems[] = { 100, menuhandlerMouseAimSpeedY, }, + { + MENUITEMTYPE_SLIDER, + 0, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"Radial Menu Speed", + 100, + menuhandlerRadialMenuSpeed, + }, { MENUITEMTYPE_SEPARATOR, 0, diff --git a/src/game/activemenutick.c b/src/game/activemenutick.c index 23554f857..4424926cc 100644 --- a/src/game/activemenutick.c +++ b/src/game/activemenutick.c @@ -80,8 +80,8 @@ void amTick(void) struct activemenu *am = &g_AmMenus[g_AmIndex]; inputMouseGetAbsScaledDelta(&mdx, &mdy); if (mdx || mdy) { - am->mousex += mdx * 4.f; - am->mousey += mdy * 4.f; + am->mousex += mdx * g_PlayerRadialMenuSpeed; + am->mousey += mdy * g_PlayerRadialMenuSpeed; am->mousex = (am->mousex > 127.f) ? 127.f : (am->mousex < -128.f) ? -128.f : am->mousex; am->mousey = (am->mousey > 127.f) ? 127.f : (am->mousey < -128.f) ? -128.f : am->mousey; } diff --git a/src/game/player.c b/src/game/player.c index 2ecffb6a2..4b0645428 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -208,6 +208,7 @@ f32 g_PlayerCrosshairSway = 1.f; s32 g_PlayerMouseAimMode = MOUSEAIM_CLASSIC; f32 g_PlayerMouseAimSpeedX = 0.7f; f32 g_PlayerMouseAimSpeedY = 0.7f; +f32 g_PlayerRadialMenuSpeed = 4.0f; s32 g_PlayerFovAffectsZoom = 1; f32 g_PlayerFovZoomMultiplier = 1.0f; s32 g_PlayerClassicCrouch = true; diff --git a/src/include/data.h b/src/include/data.h index dba6ff5d0..f36ffbeef 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -540,6 +540,7 @@ extern f32 g_PlayerDefaultFovY; extern s32 g_PlayerMouseAimMode; extern f32 g_PlayerMouseAimSpeedX; extern f32 g_PlayerMouseAimSpeedY; +extern f32 g_PlayerRadialMenuSpeed; extern s32 g_PlayerFovAffectsZoom; extern f32 g_PlayerFovZoomMultiplier; extern s32 g_PlayerClassicCrouch; From 4914aa335b8db10fef08c54df9bc9b343129b362 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Mon, 30 Oct 2023 19:01:52 +0100 Subject: [PATCH 17/36] fix README typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 58ce13c3e..fa68eb49a 100644 --- a/README.md +++ b/README.md @@ -76,8 +76,8 @@ Controls can be rebound in `pd.ini`. Default control scheme is as follows: | Next weapon | Mousewheel back | Y | Y `(0x80)` | | Radial menu | Q | LB | D-Down | | Alt fire mode | F | RB | L Trigger | -| Alt-fire oneshot | `F + LMB` or `E + LMB` | `A + RT`` or 'RB + RT` | `A + Z` or `L + Z` | -| Quick-detonate | `E + Q` or `E + R` | `A + B` or `A + X` | `A + D-Left`or `A + 0x40` | +| Alt-fire oneshot | `F + LMB` or `E + LMB` | `A + RT` or `RB + RT` | `A + Z` or `L + Z` | +| Quick-detonate | `E + Q` or `E + R` | `A + B` or `A + X` | `A + D-Left`or `A + X` | ## Building From 83d22c3b70dc5350d505d76d121fb35f802d8ffc Mon Sep 17 00:00:00 2001 From: SilentException Date: Wed, 1 Nov 2023 04:23:34 +0100 Subject: [PATCH 18/36] Allow mouse input by default --- port/include/input.h | 3 +++ port/src/input.c | 16 ++++++++++++++++ port/src/optionsmenu.c | 21 +++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/port/include/input.h b/port/include/input.h index 9e68791f7..365bd13c2 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -173,4 +173,7 @@ void inputSetDefaultKeyBinds(void); void inputClearLastKey(void); s32 inputGetLastKey(void); +s32 inputGetMouseDefaultLocked(void); +void inputSetMouseDefaultLocked(s32 defaultLocked); + #endif diff --git a/port/src/input.c b/port/src/input.c index ddb5b5675..84f5847c1 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -26,6 +26,7 @@ static s32 connectedMask = 0; static s32 mouseEnabled = 1; static s32 mouseLocked = 0; +static s32 mouseDefaultLocked = 0; static s32 mouseX, mouseY; static s32 mouseDX, mouseDY; static u32 mouseButtons; @@ -459,6 +460,7 @@ void inputSaveConfig(void) inputSaveBinds(); configSetInt("Input.MouseEnabled", mouseEnabled); + configSetInt("Input.MouseDefaultLocked", mouseDefaultLocked); configSetFloat("Input.MouseSpeedX", mouseSensX); configSetFloat("Input.MouseSpeedY", mouseSensY); @@ -513,6 +515,8 @@ s32 inputInit(void) inputSetDefaultKeyBinds(); mouseEnabled = configGetInt("Input.MouseEnabled", 1); + mouseDefaultLocked = configGetInt("Input.MouseDefaultLocked", 0); + inputLockMouse(mouseDefaultLocked); mouseSensX = configGetFloat("Input.MouseSpeedX", 1.5f); mouseSensY = configGetFloat("Input.MouseSpeedY", 1.5f); @@ -929,6 +933,18 @@ void inputMouseEnable(s32 enabled) } } +s32 inputGetMouseDefaultLocked(void) +{ + return mouseDefaultLocked; +} + +void inputSetMouseDefaultLocked(s32 defaultLocked) +{ + mouseDefaultLocked = !!defaultLocked; + if (mouseEnabled) { + inputLockMouse(mouseDefaultLocked); + } +} const char *inputGetContKeyName(u32 ck) { diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index efb81b0b4..2d3636ad3 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -42,6 +42,19 @@ static MenuItemHandlerResult menuhandlerMouseAimLock(s32 operation, struct menui return 0; } +static MenuItemHandlerResult menuhandlerMouseDefaultLocked(s32 operation, struct menuitem *item, union handlerdata *data) +{ + switch (operation) { + case MENUOP_GET: + return inputGetMouseDefaultLocked(); + case MENUOP_SET: + inputSetMouseDefaultLocked(data->checkbox.value); + break; + } + + return 0; +} + static MenuItemHandlerResult menuhandlerMouseSpeedX(s32 operation, struct menuitem *item, union handlerdata *data) { f32 x, y; @@ -168,6 +181,14 @@ struct menuitem g_ExtendedMouseMenuItems[] = { 0, menuhandlerMouseAimLock, }, + { + MENUITEMTYPE_CHECKBOX, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Grab Mouse Input by Default", + 0, + menuhandlerMouseDefaultLocked, + }, { MENUITEMTYPE_SEPARATOR, 0, From c040c17b49d18c6c323e14ac88ea3546868645fb Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 1 Nov 2023 16:35:48 +0100 Subject: [PATCH 19/36] port: fix whitespace --- port/src/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/src/input.c b/port/src/input.c index 84f5847c1..5ae433f3d 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -460,7 +460,7 @@ void inputSaveConfig(void) inputSaveBinds(); configSetInt("Input.MouseEnabled", mouseEnabled); - configSetInt("Input.MouseDefaultLocked", mouseDefaultLocked); + configSetInt("Input.MouseDefaultLocked", mouseDefaultLocked); configSetFloat("Input.MouseSpeedX", mouseSensX); configSetFloat("Input.MouseSpeedY", mouseSensY); From 4a0296d1a44bfdf6f73bcae7695f28dd53a7fd5c Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 1 Nov 2023 16:36:07 +0100 Subject: [PATCH 20/36] port: fix radial menu bind in extended options --- port/src/optionsmenu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 2d3636ad3..af49905b2 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -799,7 +799,7 @@ static const struct menubind menuBinds[] = { { CK_X, "Reload [X]\n" }, { CK_Y, "Next Weapon [Y]\n" }, { CK_DPAD_L, "Prev Weapon [DL]\n" }, - { CK_DPAD_L, "Radial Menu [DD]\n" }, + { CK_DPAD_D, "Radial Menu [DD]\n" }, { CK_START, "Pause Menu [ST]\n" }, { CK_8000, "Cycle Crouch [+]\n" }, { CK_4000, "Half Crouch [+]\n" }, From a802d2db06bbba5973542e4a72b4bd77ed5df5bb Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 1 Nov 2023 17:46:24 +0100 Subject: [PATCH 21/36] port: allow pressing DEL to delete the binding --- port/include/input.h | 1 + port/src/optionsmenu.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/port/include/input.h b/port/include/input.h index 365bd13c2..9a91005bb 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -20,6 +20,7 @@ enum virtkey { VK_KEYBOARD_BEGIN = 0, VK_RETURN = 40, VK_ESCAPE = 41, + VK_DELETE = 76, /* same order as SDL mouse buttons */ VK_MOUSE_BEGIN = 512, diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index af49905b2..dcd656a3b 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -773,11 +773,19 @@ struct menuitem g_ExtendedBindKeyMenuItems[] = { 0, menuhandlerDoBind, }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CENTRE | MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"ESC to cancel, DEL to remove binding\n", + 0, + menuhandlerDoBind, + }, { MENUITEMTYPE_END }, }; struct menudialogdef g_ExtendedBindKeyMenuDialog = { - MENUDIALOGTYPE_WHITE, + MENUDIALOGTYPE_SUCCESS, (uintptr_t)"Bind", g_ExtendedBindKeyMenuItems, NULL, @@ -882,7 +890,7 @@ static MenuItemHandlerResult menuhandlerDoBind(s32 operation, struct menuitem *i const s32 key = inputGetLastKey(); if (key && key != VK_ESCAPE) { - inputKeyBind(g_BindsPlayer, g_BindContKey, g_BindIndex, key); + inputKeyBind(g_BindsPlayer, g_BindContKey, g_BindIndex, (key == VK_DELETE ? 0 : key)); menuPopDialog(); } From fea0a1e4ca7e57ebe0bd6ae51de98abe30bef68f Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 1 Nov 2023 23:46:39 +0100 Subject: [PATCH 22/36] port: pal: swap all the characters in extended fonts --- port/src/preprocess.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/port/src/preprocess.c b/port/src/preprocess.c index d3ca3a6da..15f54f470 100644 --- a/port/src/preprocess.c +++ b/port/src/preprocess.c @@ -1063,7 +1063,21 @@ void preprocessFont(u8 *data, u32 size) PD_SWAP_VAL(fnt->kerning[i]); } - for (s32 i = 0; i < ARRAYCOUNT(fnt->chars); ++i) { + s32 numchars = 94; + +#if PAL + // PAL has more characters in these fonts + extern u8 *_fonthandelgothicsmSegmentRomStart; + extern u8 *_fonthandelgothicxsSegmentRomStart; + extern u8 *_fonthandelgothicmdSegmentRomStart; + if (data == _fonthandelgothicsmSegmentRomStart + || data == _fonthandelgothicxsSegmentRomStart + || data == _fonthandelgothicmdSegmentRomStart) { + numchars = 135; + } +#endif + + for (s32 i = 0; i < numchars; ++i) { PD_SWAP_VAL(fnt->chars[i].kerningindex); PD_SWAP_PTR(fnt->chars[i].pixeldata); } From 3ac194f9ee7fa0063d0a3f64be90c20ae18319c0 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Thu, 2 Nov 2023 00:20:18 +0100 Subject: [PATCH 23/36] port: pal: fix (?) audio stutter --- src/lib/audiomgr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/audiomgr.c b/src/lib/audiomgr.c index 4e1deff92..7791798de 100644 --- a/src/lib/audiomgr.c +++ b/src/lib/audiomgr.c @@ -345,8 +345,8 @@ void amgrFrame(void) info->frameSamples = 184; var8005cf94 = 2; } else { - // have space in audio queue, render 2 naudio frames this frame - info->frameSamples = 368; + // have space in audio queue, render 2 naudio frames this frame (and 1 extra on PAL) + info->frameSamples = 368 + PAL * 184; if (var8005cf94 != 0) { var8005cf94--; From d021e95d70dfa2aff5c22e3407029bff7e4562fe Mon Sep 17 00:00:00 2001 From: fgsfds Date: Thu, 2 Nov 2023 00:20:35 +0100 Subject: [PATCH 24/36] port: add exe region to legal screen --- src/game/title.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/game/title.c b/src/game/title.c index faedffa6e..23f48290f 100644 --- a/src/game/title.c +++ b/src/game/title.c @@ -370,6 +370,8 @@ Gfx *titleRenderLegal(Gfx *gdl) #if !defined(PLATFORM_N64) && defined(VERSION_HASH) if (elem->textid == L_OPTIONS_084) { elem->textptr = VERSION_HASH " (" VERSION_TARGET ")"; + } else if (elem->textid == L_OPTIONS_083) { + elem->textptr = VERSION_ROMID; } #endif break; @@ -456,6 +458,11 @@ Gfx *titleRenderLegal(Gfx *gdl) gdl = text0f153628(gdl); } else { +#ifdef PLATFORM_N64 +#define ELEM_TEXT langGet(elem->textid) +#else +#define ELEM_TEXT (char *)(elem->textptr ? elem->textptr : langGet(elem->textid)) +#endif #if VERSION == VERSION_JPN_FINAL u32 stack; x = elem->x == -1 ? prevx : elem->x; @@ -471,16 +478,16 @@ Gfx *titleRenderLegal(Gfx *gdl) x += 24; } - gdl = textRenderProjected(gdl, &x, &y, langGet(elem->textid), font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); + gdl = textRenderProjected(gdl, &x, &y, ELEM_TEXT, font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); var8007fad0 = 1; var80080108jf = 1; } else { - gdl = textRenderProjected(gdl, &x, &y, langGet(elem->textid), font1, font2, (colour & 0xffffff00) | ((colour & 0xff) * 2 / 3), viGetWidth(), viGetHeight(), 0, 0); + gdl = textRenderProjected(gdl, &x, &y, ELEM_TEXT, font1, font2, (colour & 0xffffff00) | ((colour & 0xff) * 2 / 3), viGetWidth(), viGetHeight(), 0, 0); x = elem->x == -1 ? prevx : elem->x; y = elem->y; - gdl = textRenderProjected(gdl, &x, &y, langGet(elem->textid), font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); + gdl = textRenderProjected(gdl, &x, &y, ELEM_TEXT, font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); prevx = x; } @@ -489,28 +496,23 @@ Gfx *titleRenderLegal(Gfx *gdl) // Render a darker copy of the text one pixel above x = elem->x == -1 ? prevx : elem->x; y = elem->y - 1; - gdl = textRenderProjected(gdl, &x, &y, langGet(elem->textid), font1, font2, (colour & 0xffffff00) | ((colour & 0xff) * 2 / 3), viGetWidth(), viGetHeight(), 0, 0); + gdl = textRenderProjected(gdl, &x, &y, ELEM_TEXT, font1, font2, (colour & 0xffffff00) | ((colour & 0xff) * 2 / 3), viGetWidth(), viGetHeight(), 0, 0); // Render the text properly x = elem->x == -1 ? prevx : elem->x; y = elem->y; - gdl = textRenderProjected(gdl, &x, &y, langGet(elem->textid), font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); + gdl = textRenderProjected(gdl, &x, &y, ELEM_TEXT, font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); prevx = x; #elif VERSION >= VERSION_PAL_BETA x = elem->x == -1 ? prevx : elem->x; y = elem->y; - gdl = textRenderProjected(gdl, &x, &y, langGet(elem->textid), font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); + gdl = textRenderProjected(gdl, &x, &y, ELEM_TEXT, font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); prevx = x; #else x = elem->x; y = elem->y; -#ifdef PLATFORM_N64 - gdl = textRenderProjected(gdl, &x, &y, langGet(elem->textid), font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); -#else - const char *textptr = elem->textptr ? elem->textptr : langGet(elem->textid); - gdl = textRenderProjected(gdl, &x, &y, textptr, font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); -#endif + gdl = textRenderProjected(gdl, &x, &y, ELEM_TEXT, font1, font2, colour, viGetWidth(), viGetHeight(), 0, 0); #endif } } From 479c0335f3dbe021f84adbc88e858de604de36cf Mon Sep 17 00:00:00 2001 From: fgsfds Date: Thu, 2 Nov 2023 00:24:49 +0100 Subject: [PATCH 25/36] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa68eb49a..5093ed046 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,9 @@ To run the port, you must already have a Perfect Dark ROM, specifically one of t * `jpn-final` (md5 `538d2b75945eae069b29c46193e74790`). Technically supported, but requires a separate custom-built executable. Called `JPN version 8.9 final` on the boot screen. +* `pal-final` (md5 `d9b5cd305d228424891ce38e71bc9213`). + Technically supported, but requires a separate custom-built executable. + Called `PAL 8.7 final` on the boot screen. ## Status @@ -101,8 +104,8 @@ Controls can be rebound in `pd.ini`. Default control scheme is as follows: Currently only `i686-linux` and `i686-windows` are supported, using `gcc -m32` and `i686-w64-mingw32-gcc` as compilers, respectively. Alternate compilers can be specified by passing `TOOLCHAIN=i686-whatever-` as a command line argument. -For both platforms you can optionally specify `ROMID=jpn-final` on the `make` command line to build the JPN executable. -You will need to provide a `jpn-final` ROM for that, named `pd.jpn-final.z64`. +You can build an executable with PAL or JPN ROM support by adding `ROMID=pal-final` or `ROMID=jpn-final` to the `make` command. +You will need to provide a `jpn-final` or `pal-final` ROM for those, named `pd.jpn-final.z64` or `pd.pal-final.z64`. It might be possible to build a 32-bit ARM executable, but this has not been tested. From fbae610a3b6a25c079f0cacca96c2bbee09e084e Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 3 Nov 2023 16:24:10 +0100 Subject: [PATCH 26/36] port: rework config system a bit --- port/include/config.h | 20 ++--- port/include/input.h | 2 +- port/include/video.h | 2 - port/src/audio.c | 15 +++- port/src/config.c | 179 ++++++++++++++++++++--------------------- port/src/input.c | 139 +++++++++++++++----------------- port/src/main.c | 52 ++++++------ port/src/optionsmenu.c | 5 +- port/src/video.c | 66 ++++++++------- src/game/game_1531a0.c | 1 + src/include/data.h | 1 + src/include/platform.h | 9 ++- 12 files changed, 238 insertions(+), 253 deletions(-) diff --git a/port/include/config.h b/port/include/config.h index c48550345..5bc12cf11 100644 --- a/port/include/config.h +++ b/port/include/config.h @@ -13,18 +13,8 @@ s32 configLoad(const char *fname); // saves config to file (path extensions such as ! apply) s32 configSave(const char *fname); -// get value of a config variable or create a new one with specified default -// key is a string of the form "SECTION.KEY" -s32 configGetInt(const char *key, s32 defval); -f32 configGetFloat(const char *key, f32 defval); -const char *configGetString(const char *key, const char *defval); - -// get value as above and clamp it in between minval, maxval -s32 configGetIntClamped(const char *key, s32 defval, s32 minval, s32 maxval); -f32 configGetFloatClamped(const char *key, f32 defval, f32 minval, f32 maxval); - -// set value of a config variable, will create it if it doesn't exist -// key is a string of the form "SECTION.KEY" -void configSetInt(const char *key, s32 val); -void configSetFloat(const char *key, f32 val); -void configSetString(const char *key, const char *val); +// registers a variable in the config file +// this should be done before configInit() is called, preferably in a module constructor +void configRegisterInt(const char *key, s32 *var, s32 min, s32 max); +void configRegisterFloat(const char *key, f32 *var, f32 min, f32 max); +void configRegisterString(const char *key, char *var, u32 maxstr); diff --git a/port/include/input.h b/port/include/input.h index 9a91005bb..fb6c410f1 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -167,7 +167,7 @@ void inputMouseEnable(s32 enabled); void inputUpdate(void); // call this before configSave() -void inputSaveConfig(void); +void inputSaveBinds(void); void inputSetDefaultKeyBinds(void); diff --git a/port/include/video.h b/port/include/video.h index a68ac75d3..ffd2c6728 100644 --- a/port/include/video.h +++ b/port/include/video.h @@ -38,6 +38,4 @@ s32 videoFramebuffersSupported(void); void videoResetTextureCache(void); void videoFreeCachedTexture(const void *texptr); -void videoSaveConfig(void); - #endif diff --git a/port/src/audio.c b/port/src/audio.c index 8c673a851..c66cf5912 100644 --- a/port/src/audio.c +++ b/port/src/audio.c @@ -1,6 +1,7 @@ #include #include #include +#include "platform.h" #include "config.h" #include "audio.h" #include "system.h" @@ -8,7 +9,9 @@ static SDL_AudioDeviceID dev; static const s16 *nextBuf; static u32 nextSize = 0; -static u32 queueLimit = 8192; + +static s32 bufferSize = 512; +static s32 queueLimit = 8192; s32 audioInit(void) { @@ -22,7 +25,7 @@ s32 audioInit(void) want.freq = 22020; // TODO: this might cause trouble for some platforms want.format = AUDIO_S16SYS; want.channels = 2; - want.samples = configGetInt("Audio.BufferSize", 512); + want.samples = bufferSize; want.callback = NULL; nextBuf = NULL; @@ -33,8 +36,6 @@ s32 audioInit(void) return -1; } - queueLimit = configGetInt("Audio.QueueLimit", 8192); - SDL_PauseAudioDevice(dev, 0); return 0; @@ -66,3 +67,9 @@ void audioEndFrame(void) nextSize = 0; } } + +PD_CONSTRUCTOR static void audioConfigInit(void) +{ + configRegisterInt("Audio.BufferSize", &bufferSize, 0, 1 * 1024 * 1024); + configRegisterInt("Audio.QueueLimit", &queueLimit, 0, 1 * 1024 * 1024); +} diff --git a/port/src/config.c b/port/src/config.c index ced137d55..4d2383276 100644 --- a/port/src/config.c +++ b/port/src/config.c @@ -8,20 +8,42 @@ #include "system.h" #include "utils.h" -#define CONFIG_MAX_STR 512 #define CONFIG_MAX_SECNAME 128 #define CONFIG_MAX_KEYNAME 256 #define CONFIG_MAX_SETTINGS 256 +typedef enum { + CFG_NONE, + CFG_S32, + CFG_F32, + CFG_STR +} configtype; + struct configentry { char key[CONFIG_MAX_KEYNAME + 1]; - char strval[CONFIG_MAX_STR + 1]; - s32 s32val; - f32 f32val; s32 seclen; -} settings[CONFIG_MAX_SETTINGS + 1]; + configtype type; + void *ptr; + union { + struct { f32 min_f32, max_f32; }; + struct { s32 min_s32, max_s32; }; + u32 max_str; + }; +} settings[CONFIG_MAX_SETTINGS]; + static s32 numSettings = 0; +static inline s32 configClampInt(s32 val, s32 min, s32 max) +{ + return (val < min) ? min : ((val > max) ? max : val); +} + +static inline f32 configClampFloat(f32 val, f32 min, f32 max) +{ + return (val < min) ? min : ((val > max) ? max : val); +} + + static inline struct configentry *configFindEntry(const char *key) { for (s32 i = 0; i < numSettings; ++i) { @@ -68,115 +90,89 @@ static inline const char *configGetSection(char *sec, const struct configentry * return sec; } -void configSetInt(const char *key, s32 val) +void configRegisterInt(const char *key, s32 *var, s32 min, s32 max) { struct configentry *cfg = configFindOrAddEntry(key); if (cfg) { - cfg->s32val = val; - snprintf(cfg->strval, CONFIG_MAX_STR, "%d", val); + cfg->type = CFG_S32; + cfg->ptr = var; + cfg->min_s32 = min; + cfg->max_s32 = max; } } -void configSetFloat(const char *key, f32 val) +void configRegisterFloat(const char *key, f32 *var, f32 min, f32 max) { struct configentry *cfg = configFindOrAddEntry(key); if (cfg) { - cfg->f32val = val; - snprintf(cfg->strval, CONFIG_MAX_STR, "%f", val); + cfg->type = CFG_F32; + cfg->ptr = var; + cfg->min_f32 = min; + cfg->max_f32 = max; } } -void configSetString(const char *key, const char *val) +void configRegisterString(const char *key, char *var, u32 maxstr) { struct configentry *cfg = configFindOrAddEntry(key); if (cfg) { - strncpy(cfg->strval, val, CONFIG_MAX_STR); + cfg->type = CFG_STR; + cfg->ptr = var; + cfg->max_str = maxstr; } } -void configSetFromString(const char *key, const char *val) +static void configSetFromString(const char *key, const char *val) { struct configentry *cfg = configFindOrAddEntry(key); - if (cfg) { - strncpy(cfg->strval, val, CONFIG_MAX_STR); - cfg->f32val = atof(val); - cfg->s32val = atoi(val); - } -} - -s32 configGetInt(const char *key, s32 defval) -{ - struct configentry *cfg = configFindEntry(key); - if (cfg) { - return cfg->s32val; - } - - cfg = configAddEntry(key); - if (cfg) { - cfg->s32val = defval; - snprintf(cfg->strval, CONFIG_MAX_STR, "%d", defval); - } - - return defval; -} - -f32 configGetFloat(const char *key, f32 defval) -{ - struct configentry *cfg = configFindEntry(key); - if (cfg) { - return cfg->f32val; - } - - cfg = configAddEntry(key); - if (cfg) { - cfg->f32val = defval; - snprintf(cfg->strval, CONFIG_MAX_STR, "%f", defval); - } - - return defval; -} - -const char *configGetString(const char *key, const char *defval) -{ - struct configentry *cfg = configFindEntry(key); - if (cfg) { - return cfg->strval; - } - - cfg = configAddEntry(key); - if (cfg) { - strncpy(cfg->strval, defval, CONFIG_MAX_STR); + if (!cfg) return; + + s32 tmp_s32; + f32 tmp_f32; + switch (cfg->type) { + case CFG_S32: + tmp_s32 = atoi(val); + if (cfg->min_s32 < cfg->max_s32) { + tmp_f32 = configClampInt(tmp_s32, cfg->min_s32, cfg->max_s32); + } + *(s32 *)cfg->ptr = tmp_s32; + break; + case CFG_F32: + tmp_f32 = atof(val); + if (cfg->min_f32 < cfg->max_f32) { + tmp_f32 = configClampFloat(tmp_f32, cfg->min_f32, cfg->max_f32); + } + *(f32 *)cfg->ptr = tmp_f32; + break; + case CFG_STR: + strncpy(cfg->ptr, val, cfg->max_str ? cfg->max_str - 1 : 4096); + break; + default: + break; } - - return defval; -} - -s32 configGetIntClamped(const char *key, s32 defval, s32 minval, s32 maxval) -{ - const s32 ret = configGetInt(key, defval); - return (ret < minval) ? minval : ((ret > maxval) ? maxval : ret); } -f32 configGetFloatClamped(const char *key, f32 defval, f32 minval, f32 maxval) +static void configSaveEntry(struct configentry *cfg, FILE *f) { - const f32 ret = configGetFloat(key, defval); - return (ret < minval) ? minval : ((ret > maxval) ? maxval : ret); -} - -static s32 configCmp(const struct configentry *a, const struct configentry *b) { - char tmpa[CONFIG_MAX_SECNAME + 1]; - char tmpb[CONFIG_MAX_SECNAME + 1]; - configGetSection(tmpa, a); - configGetSection(tmpb, b); - - // compare section names first - const s32 seccmp = strncmp(tmpa, tmpb, CONFIG_MAX_SECNAME); - if (seccmp) { - return seccmp; + switch (cfg->type) { + case CFG_S32: + if (cfg->min_s32 < cfg->max_s32) { + *(s32 *)cfg->ptr = configClampInt(*(s32 *)cfg->ptr, cfg->min_s32, cfg->max_s32); + } + fprintf(f, "%s=%d\n", cfg->key + cfg->seclen + 1, *(s32 *)cfg->ptr); + break; + case CFG_F32: + if (cfg->min_f32 < cfg->max_f32) { + *(f32 *)cfg->ptr = configClampFloat(*(f32 *)cfg->ptr, cfg->min_f32, cfg->max_f32); + } + fprintf(f, "%s=%f\n", cfg->key + cfg->seclen + 1, *(f32 *)cfg->ptr); + break; + case CFG_STR: + fprintf(f, "%s=%s\n", cfg->key + cfg->seclen + 1, (char *)cfg->ptr); + break; + default: + break; } - - // same section; sort keys by first letter - return a->key[a->seclen + 1] - b->key[b->seclen + 1]; } s32 configSave(const char *fname) @@ -186,9 +182,6 @@ s32 configSave(const char *fname) return 0; } - // sort the config so that the sections appear in order - qsort(settings, numSettings, sizeof(*settings), (void *)configCmp); - char tmpSec[CONFIG_MAX_SECNAME + 1] = { 0 }; char curSec[CONFIG_MAX_SECNAME + 1] = { 0 }; configGetSection(curSec, &settings[0]); @@ -201,7 +194,7 @@ s32 configSave(const char *fname) fprintf(f, "\n[%s]\n", tmpSec); strncpy(curSec, tmpSec, CONFIG_MAX_SECNAME); } - fprintf(f, "%s=%s\n", cfg->key + cfg->seclen + 1, cfg->strval); + configSaveEntry(cfg, f); } fsFileClose(f); diff --git a/port/src/input.c b/port/src/input.c index 5ae433f3d..deeb9cf0f 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -10,6 +10,8 @@ #include "config.h" #include "system.h" +#define MAX_BIND_STR 256 + #define TRIG_THRESHOLD (30 * 256) #define DEFAULT_DEADZONE 4096 #define DEFAULT_DEADZONE_RY 6144 @@ -21,7 +23,10 @@ static SDL_GameController *pads[INPUT_MAX_CONTROLLERS]; static s32 rumbleSupported[INPUT_MAX_CONTROLLERS]; static u32 binds[MAXCONTROLLERS][CK_TOTAL_COUNT][INPUT_MAX_BINDS]; // [i][CK_][b] = [VK_] +static char bindStrs[MAXCONTROLLERS][CK_TOTAL_COUNT][MAX_BIND_STR]; +static s32 fakeControllers = 0; +static s32 firstController = 0; static s32 connectedMask = 0; static s32 mouseEnabled = 1; @@ -57,6 +62,7 @@ static s32 deadzone[4] = { }; static s32 stickCButtons = 1; +static s32 swapSticks = 1; static const char *ckNames[CK_TOTAL_COUNT] = { "R_CBUTTONS", @@ -253,7 +259,7 @@ static int inputEventFilter(void *data, SDL_Event *event) { switch (event->type) { case SDL_CONTROLLERDEVICEADDED: - for (s32 i = 0; i < INPUT_MAX_CONTROLLERS; ++i) { + for (s32 i = firstController; i < INPUT_MAX_CONTROLLERS; ++i) { if (!pads[i]) { pads[i] = SDL_GameControllerOpen(event->cdevice.which); if (pads[i]) { @@ -387,34 +393,36 @@ static inline void inputInitKeyNames(void) } } -static inline void inputSaveBinds(void) +void inputSaveBinds(void) { - char bindstr[256]; - char keyname[256]; - char secname[] = "Input.Player1.Binds"; - - bindstr[sizeof(bindstr) - 1] = '\0'; + char *bindstr; for (s32 i = 0; i < MAXCONTROLLERS; ++i) { - secname[12] = '1' + i; for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { - snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); + bindstr = bindStrs[i][ck]; bindstr[0] = '\0'; for (s32 b = 0; b < INPUT_MAX_BINDS; ++b) { if (binds[i][ck][b]) { if (b) { - strncat(bindstr, ", ", sizeof(bindstr) - 1); + strncat(bindstr, ", ", MAX_BIND_STR - 1); } - strncat(bindstr, inputGetKeyName(binds[i][ck][b]), sizeof(bindstr) - 1); + strncat(bindstr, inputGetKeyName(binds[i][ck][b]), MAX_BIND_STR - 1); } } - configSetString(keyname, bindstr[0] ? bindstr : "NONE"); + if (!bindstr[0]) { + strcpy(bindstr, "NONE"); + } } } } static inline void inputParseBindString(const s32 ctrl, const u32 ck, char *bindstr) { + if (!bindstr[0]) { + // empty string, keep defaults + return; + } + // unbind all first memset(binds[ctrl][ck], 0, sizeof(binds[ctrl][ck])); @@ -437,50 +445,13 @@ static inline void inputParseBindString(const s32 ctrl, const u32 ck, char *bind static inline void inputLoadBinds(void) { - char secname[] = "Input.Player1.Binds"; - char keyname[256]; - char bindstr[256]; - for (s32 i = 0; i < MAXCONTROLLERS; ++i) { - secname[12] = '1' + i; for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { - snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); - const char *cfgstr = configGetString(keyname, ""); - if (cfgstr[0]) { - strncpy(bindstr, cfgstr, sizeof(bindstr) - 1); - bindstr[sizeof(bindstr) - 1] = '\0'; - inputParseBindString(i, ck, bindstr); - } + inputParseBindString(i, ck, bindStrs[i][ck]); } } } -void inputSaveConfig(void) -{ - inputSaveBinds(); - - configSetInt("Input.MouseEnabled", mouseEnabled); - configSetInt("Input.MouseDefaultLocked", mouseDefaultLocked); - configSetFloat("Input.MouseSpeedX", mouseSensX); - configSetFloat("Input.MouseSpeedY", mouseSensY); - - configSetInt("Input.LStickDeadzoneX", deadzone[0]); - configSetInt("Input.LStickDeadzoneY", deadzone[1]); - configSetInt("Input.RStickDeadzoneX", deadzone[2]); - configSetInt("Input.RStickDeadzoneY", deadzone[3]); - - configSetFloat("Input.LStickScaleX", stickSens[0]); - configSetFloat("Input.LStickScaleY", stickSens[1]); - configSetFloat("Input.RStickScaleX", stickSens[2]); - configSetFloat("Input.RStickScaleY", stickSens[3]); - - configSetFloat("Input.RumbleScale", rumbleScale); - - configSetInt("Input.StickCButtons", stickCButtons); - - configGetInt("Input.SwapSticks", axisMap[0][0] == SDL_CONTROLLER_AXIS_RIGHTX); -} - s32 inputInit(void) { if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC)) { @@ -493,11 +464,9 @@ s32 inputInit(void) connectedMask = 1; // always report first controller as connected - // if this is set to 1, keyboard will count as a separate controller on its own, + // if firstController is set to 1, keyboard will count as a separate controller on its own, // so the first connected gamepad will go to player 2 instead of player 1 - const s32 cstart = configGetInt("Input.FirstGamepadNum", 0); - - for (s32 jidx = 0, cidx = cstart; jidx < numJoys && cidx < INPUT_MAX_CONTROLLERS; ++jidx) { + for (s32 jidx = 0, cidx = firstController; jidx < numJoys && cidx < INPUT_MAX_CONTROLLERS; ++jidx) { if (SDL_IsGameController(jidx)) { pads[cidx] = SDL_GameControllerOpen(jidx); if (pads[cidx]) { @@ -514,27 +483,9 @@ s32 inputInit(void) inputSetDefaultKeyBinds(); - mouseEnabled = configGetInt("Input.MouseEnabled", 1); - mouseDefaultLocked = configGetInt("Input.MouseDefaultLocked", 0); inputLockMouse(mouseDefaultLocked); - mouseSensX = configGetFloat("Input.MouseSpeedX", 1.5f); - mouseSensY = configGetFloat("Input.MouseSpeedY", 1.5f); - - rumbleScale = configGetFloat("Input.RumbleScale", 0.5f); - - deadzone[0] = configGetInt("Input.LStickDeadzoneX", DEFAULT_DEADZONE); - deadzone[1] = configGetInt("Input.LStickDeadzoneY", DEFAULT_DEADZONE); - deadzone[2] = configGetInt("Input.RStickDeadzoneX", DEFAULT_DEADZONE); - deadzone[3] = configGetInt("Input.RStickDeadzoneY", DEFAULT_DEADZONE_RY); - - stickSens[0] = configGetFloat("Input.LStickScaleX", 1.f); - stickSens[1] = configGetFloat("Input.LStickScaleY", 1.f); - stickSens[2] = configGetFloat("Input.RStickScaleX", 1.f); - stickSens[3] = configGetFloat("Input.RStickScaleY", 1.f); - - stickCButtons = configGetInt("Input.StickCButtons", 0); - if (configGetInt("Input.SwapSticks", 1)) { + if (swapSticks) { // invert axis map axisMap[0][0] = SDL_CONTROLLER_AXIS_RIGHTX; axisMap[0][1] = SDL_CONTROLLER_AXIS_RIGHTY; @@ -542,7 +493,7 @@ s32 inputInit(void) axisMap[1][1] = SDL_CONTROLLER_AXIS_LEFTY; } - const s32 overrideMask = (1 << configGetInt("Input.FakeGamepads", 0)) - 1; + const s32 overrideMask = (1 << fakeControllers) - 1; if (overrideMask) { connectedMask = overrideMask; } @@ -737,11 +688,12 @@ s32 inputControllerMask(void) s32 inputControllerGetSticksSwapped(void) { - return (axisMap[0][0] == SDL_CONTROLLER_AXIS_RIGHTX); + return swapSticks; } void inputControllerSetSticksSwapped(s32 swapped) { + swapSticks = swapped; if (swapped) { axisMap[0][0] = SDL_CONTROLLER_AXIS_RIGHTX; axisMap[0][1] = SDL_CONTROLLER_AXIS_RIGHTY; @@ -791,7 +743,6 @@ void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk) return; } - if (bind < 0) { for (s32 i = 0; i < INPUT_MAX_BINDS; ++i) { if (binds[idx][ck][i] == 0) { @@ -1019,3 +970,39 @@ s32 inputGetLastKey(void) { return lastKey; } + +PD_CONSTRUCTOR static void inputConfigInit(void) +{ + configRegisterInt("Input.MouseEnabled", &mouseEnabled, 0, 1); + configRegisterInt("Input.MouseDefaultLocked", &mouseDefaultLocked, 0, 1); + configRegisterFloat("Input.MouseSpeedX", &mouseSensX, -10.f, 10.f); + configRegisterFloat("Input.MouseSpeedY", &mouseSensY, -10.f, 10.f); + + configRegisterFloat("Input.RumbleScale", &rumbleScale, 0.f, 1.f); + + configRegisterInt("Input.LStickDeadzoneX", &deadzone[0], 0, 32767); + configRegisterInt("Input.LStickDeadzoneY", &deadzone[1], 0, 32767); + configRegisterInt("Input.RStickDeadzoneX", &deadzone[2], 0, 32767); + configRegisterInt("Input.RStickDeadzoneY", &deadzone[3], 0, 32767); + + configRegisterFloat("Input.LStickScaleX", &stickSens[0], -10.f, 10.f); + configRegisterFloat("Input.LStickScaleY", &stickSens[1], -10.f, 10.f); + configRegisterFloat("Input.RStickScaleX", &stickSens[2], -10.f, 10.f); + configRegisterFloat("Input.RStickScaleY", &stickSens[3], -10.f, 10.f); + + configRegisterInt("Input.StickCButtons", &stickCButtons, 0, 1); + configRegisterInt("Input.SwapSticks", &swapSticks, 0, 1); + + configRegisterInt("Input.FakeGamepads", &fakeControllers, 0, 4); + configRegisterInt("Input.FirstGamepadNum", &firstController, 0, 3); + + char secname[] = "Input.Player1.Binds"; + char keyname[256] = { 0 }; + for (s32 c = 0; c < MAXCONTROLLERS; ++c) { + secname[12] = '1' + c; + for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { + snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); + configRegisterString(keyname, bindStrs[c][ck], MAX_BIND_STR); + } + } +} diff --git a/port/src/main.c b/port/src/main.c index 8db10b5fe..9e850c02a 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -18,6 +18,7 @@ #include "system.h" u32 g_OsMemSize = 0; +s32 g_OsMemSizeMb = 16; u8 g_Is4Mb = 0; s8 g_Resetting = false; OSSched g_Sched; @@ -58,43 +59,18 @@ void bootCreateSched(void) static void gameLoadConfig(void) { - osMemSize = configGetIntClamped("Game.MemorySize", 16, 4, 2048) * 1024 * 1024; - g_PlayerCrosshairSway = configGetFloatClamped("Game.CrosshairSway", g_PlayerCrosshairSway, 0.f, 10.f); - g_PlayerDefaultFovY = configGetFloatClamped("Game.FovY", g_PlayerDefaultFovY, 5.f, 175.f); - g_PlayerMouseAimMode = configGetIntClamped("Game.MouseAimMode", g_PlayerMouseAimMode, 0, 1); - g_PlayerMouseAimSpeedX = configGetFloatClamped("Game.MouseAimSpeedX", g_PlayerMouseAimSpeedX, 0.f, 10.f); - g_PlayerMouseAimSpeedY = configGetFloatClamped("Game.MouseAimSpeedY", g_PlayerMouseAimSpeedY, 0.f, 10.f); - g_PlayerRadialMenuSpeed = configGetFloatClamped("Game.RadialMenuSpeed", g_PlayerRadialMenuSpeed, 0.f, 10.f); - g_PlayerFovAffectsZoom = configGetIntClamped("Game.FovAffectsZoom", g_PlayerFovAffectsZoom, 0, 1); + osMemSize = g_OsMemSizeMb * 1024 * 1024; g_PlayerFovZoomMultiplier = g_PlayerFovAffectsZoom ? g_PlayerDefaultFovY / 60.0f : 1.0f; - g_PlayerClassicCrouch = configGetIntClamped("Game.ClassicCrouch", 0, 0, 1); - g_ViShakeIntensityMult = configGetFloatClamped("Game.ScreenShakeIntensity", 1.f, 0.f, 100.f); - const s32 center = configGetIntClamped("Game.CenterHUD", 0, 0, 1); - if (center) { + if (g_HudCenter) { g_HudAlignModeL = G_ASPECT_CENTER_EXT; g_HudAlignModeR = G_ASPECT_CENTER_EXT; } } -static void gameSaveConfig(void) -{ - configSetFloat("Game.FovY", g_PlayerDefaultFovY); - configSetInt("Game.CenterHUD", g_HudAlignModeL == G_ASPECT_CENTER_EXT); - configSetInt("Game.ClassicCrouch", g_PlayerClassicCrouch); - configSetInt("Game.MouseAimMode", g_PlayerMouseAimMode); - configSetFloat("Game.MouseAimSpeedX", g_PlayerMouseAimSpeedX); - configSetFloat("Game.MouseAimSpeedY", g_PlayerMouseAimSpeedY); - configSetFloat("Game.RadialMenuSpeed", g_PlayerRadialMenuSpeed); - configSetFloat("Game.CrosshairSway", g_PlayerCrosshairSway); - configSetFloat("Game.ScreenShakeIntensity", g_ViShakeIntensityMult); -} - static void cleanup(void) { sysLogPrintf(LOG_NOTE, "shutdown"); - inputSaveConfig(); - videoSaveConfig(); - gameSaveConfig(); + inputSaveBinds(); configSave(CONFIG_PATH); crashShutdown(); // TODO: actually shut down all subsystems @@ -147,3 +123,23 @@ int main(int argc, const char **argv) return 0; } + +PD_CONSTRUCTOR static void gameConfigInit(void) +{ + configRegisterInt("Game.MemorySize", &g_OsMemSizeMb, 4, 2048); + + configRegisterFloat("Game.FovY", &g_PlayerDefaultFovY, 5.f, 175.f); + configRegisterInt("Game.FovAffectsZoom", &g_PlayerFovAffectsZoom, 0, 1); + + configRegisterInt("Game.MouseAimMode", &g_PlayerMouseAimMode, 0, 1); + configRegisterFloat("Game.MouseAimSpeedX", &g_PlayerMouseAimSpeedX, 0.f, 10.f); + configRegisterFloat("Game.MouseAimSpeedY", &g_PlayerMouseAimSpeedY, 0.f, 10.f); + + configRegisterFloat("Game.RadialMenuSpeed", &g_PlayerRadialMenuSpeed, 0.f, 10.f); + configRegisterFloat("Game.CrosshairSway", &g_PlayerCrosshairSway, 0.f, 10.f); + configRegisterFloat("Game.ScreenShakeIntensity", &g_ViShakeIntensityMult, 0.f, 10.f); + + configRegisterInt("Game.ClassicCrouch", &g_PlayerClassicCrouch, 0, 1); + + configRegisterInt("Game.CenterHUD", &g_HudCenter, 0, 1); +} diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index dcd656a3b..250388f5c 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -549,9 +549,10 @@ static MenuItemHandlerResult menuhandlerCenterHUD(s32 operation, struct menuitem { switch (operation) { case MENUOP_GET: - return g_HudAlignModeL == G_ASPECT_CENTER_EXT; + return g_HudCenter; case MENUOP_SET: - if (data->checkbox.value) { + g_HudCenter = data->checkbox.value; + if (g_HudCenter) { g_HudAlignModeL = G_ASPECT_CENTER_EXT; g_HudAlignModeR = G_ASPECT_CENTER_EXT; } else { diff --git a/port/src/video.c b/port/src/video.c index 778303100..afe485f5b 100644 --- a/port/src/video.c +++ b/port/src/video.c @@ -4,6 +4,7 @@ #include #include #include +#include "platform.h" #include "config.h" #include "video.h" @@ -15,42 +16,38 @@ static struct GfxWindowManagerAPI *wmAPI; static struct GfxRenderingAPI *renderingAPI; static bool initDone = false; -static bool isFullscreen = false; + +static s32 vidWidth = 640; +static s32 vidHeight = 480; +static s32 vidFramebuffers = true; +static s32 vidFullscreen = false; +static s32 vidVsync = 1; +static s32 vidMSAA = 1; +static s32 vidFramerateLimit = 0; + +static s32 texFilter = FILTER_LINEAR; +static s32 texFilter2D = true; static u32 dlcount = 0; static u32 frames = 0; static u32 framesPerSec = 0; static f64 startTime, endTime, fpsTime; -static u32 texFilter2D = 1; s32 videoInit(void) { wmAPI = &gfx_sdl; renderingAPI = &gfx_opengl_api; + gfx_current_native_viewport.width = 320; gfx_current_native_viewport.height = 240; + gfx_framebuffers_enabled = (bool)vidFramebuffers; + gfx_msaa_level = vidMSAA; - const s32 w = configGetInt("Video.DefaultWidth", 640); - const s32 h = configGetInt("Video.DefaultHeight", 480); - isFullscreen = configGetInt("Video.DefaultFullscreen", false); - - gfx_framebuffers_enabled = (bool)configGetIntClamped("Video.FramebufferEffects", 1, 0, 1); - - gfx_msaa_level = configGetInt("Video.MSAA", 0); - if (gfx_msaa_level < 1 || gfx_msaa_level > 16) { - gfx_msaa_level = 1; - } - - gfx_init(wmAPI, renderingAPI, "PD", isFullscreen, w, h, 100, 100); - - wmAPI->set_swap_interval(configGetInt("Video.VSync", 1)); - wmAPI->set_target_fps(configGetInt("Video.FramerateLimit", 0)); // disabled because vsync is on - - u32 filter = configGetInt("Video.TextureFilter", FILTER_LINEAR); - if (filter > FILTER_THREE_POINT) filter = FILTER_THREE_POINT; - renderingAPI->set_texture_filter((enum FilteringMode)filter); + gfx_init(wmAPI, renderingAPI, "PD", vidFullscreen, vidWidth, vidHeight, 100, 100); - texFilter2D = configGetIntClamped("Video.TextureFilter2D", 1, 0, 1); + wmAPI->set_swap_interval(vidVsync); + wmAPI->set_target_fps(vidFramerateLimit); // disabled because vsync is on + renderingAPI->set_texture_filter((enum FilteringMode)texFilter); initDone = true; return 0; @@ -137,7 +134,7 @@ s32 videoGetHeight(void) s32 videoGetFullscreen(void) { - return isFullscreen; + return vidFullscreen; } f32 videoGetAspect(void) @@ -152,7 +149,7 @@ u32 videoGetTextureFilter2D(void) u32 videoGetTextureFilter(void) { - return renderingAPI->get_texture_filter(); + return texFilter; } void videoSetWindowOffset(s32 x, s32 y) @@ -163,15 +160,16 @@ void videoSetWindowOffset(s32 x, s32 y) void videoSetFullscreen(s32 fs) { - if (fs != isFullscreen) { - isFullscreen = !!fs; - wmAPI->set_fullscreen(isFullscreen); + if (fs != vidFullscreen) { + vidFullscreen = !!fs; + wmAPI->set_fullscreen(vidFullscreen); } } void videoSetTextureFilter(u32 filter) { if (filter > FILTER_THREE_POINT) filter = FILTER_THREE_POINT; + texFilter = filter; renderingAPI->set_texture_filter((enum FilteringMode)filter); } @@ -221,9 +219,15 @@ void videoFreeCachedTexture(const void *texptr) gfx_texture_cache_delete(texptr); } -void videoSaveConfig(void) +PD_CONSTRUCTOR static void videoConfigInit(void) { - configSetInt("Video.DefaultFullscreen", isFullscreen); - configSetInt("Video.TextureFilter", (u32)renderingAPI->get_texture_filter()); - configSetInt("Video.TextureFilter2D", texFilter2D); + configRegisterInt("Video.DefaultFullscreen", &vidFullscreen, 0, 1); + configRegisterInt("Video.DefaultWidth", &vidWidth, 0, 32767); + configRegisterInt("Video.DefaultHeight", &vidHeight, 0, 32767); + configRegisterInt("Video.VSync", &vidVsync, -1, 10); + configRegisterInt("Video.FramebufferEffects", &vidFramebuffers, 0, 1); + configRegisterInt("Video.FramerateLimit", &vidFramerateLimit, 0, 10000); + configRegisterInt("Video.MSAA", &vidMSAA, 1, 16); + configRegisterInt("Video.TextureFilter", &texFilter, 0, 2); + configRegisterInt("Video.TextureFilter2D", &texFilter2D, 0, 1); } diff --git a/src/game/game_1531a0.c b/src/game/game_1531a0.c index a28ff87ee..3bc8e225a 100644 --- a/src/game/game_1531a0.c +++ b/src/game/game_1531a0.c @@ -143,6 +143,7 @@ u32 var8007fbc0 = 0x44444400; u32 var8007fbc4 = 0xffffff00; #ifndef PLATFORM_N64 +s32 g_HudCenter = false; u32 g_HudAlignModeL = G_ASPECT_LEFT_EXT; u32 g_HudAlignModeR = G_ASPECT_RIGHT_EXT; #endif diff --git a/src/include/data.h b/src/include/data.h index f36ffbeef..e6598f3f6 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -546,6 +546,7 @@ extern f32 g_PlayerFovZoomMultiplier; extern s32 g_PlayerClassicCrouch; extern f32 g_ViShakeIntensityMult; extern u32 g_TexFilter2D; +extern s32 g_HudCenter; extern u32 g_HudAlignModeL; extern u32 g_HudAlignModeR; extern s32 g_PrevFrameFb; diff --git a/src/include/platform.h b/src/include/platform.h index 7542a6815..30615f750 100644 --- a/src/include/platform.h +++ b/src/include/platform.h @@ -49,7 +49,7 @@ // byteswap macros -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(__clang__) #define PD_BSWAP16(x) __builtin_bswap16(x) #define PD_BSWAP32(x) __builtin_bswap32(x) #define PD_BSWAP64(x) __builtin_bswap64(x) @@ -82,4 +82,11 @@ #define PD_LEPTR(x) PD_LE32(x) #endif +// module constructor function attribute +#if defined(__GNUC__) || defined(__clang__) + #define PD_CONSTRUCTOR __attribute__((constructor)) +#else + #error "Implement PD_CONSTRUCTOR macro for your compiler." +#endif + #endif From dc6e6a507cb299aff8420d474b883e5ef545298d Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 4 Nov 2023 02:02:51 +0100 Subject: [PATCH 27/36] port: slightly more helpful rom error messages --- port/src/romdata.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/port/src/romdata.c b/port/src/romdata.c index cb4fd2c89..08629ecf4 100644 --- a/port/src/romdata.c +++ b/port/src/romdata.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -25,21 +26,23 @@ #define ROMDATA_ROM_NAME "pd." VERSION_ROMID ".z64" #define ROMDATA_ROM_SIZE 33554432 -#define ROMDATA_ROM_HASH // TODO #if VERSION == VERSION_NTSC_FINAL #define ROMDATA_ROM_TITLE "Perfect Dark" #define ROMDATA_ROM_ID "NPDE" +#define ROMDATA_ROM_DESC "NTSC v1.1" #define ROMDATA_FILES_OFS 0x28080 #define ROMDATA_DATA_OFS 0x39850 #elif VERSION == VERSION_PAL_FINAL #define ROMDATA_ROM_TITLE "Perfect Dark" #define ROMDATA_ROM_ID "NPDP" +#define ROMDATA_ROM_DESC "PAL" #define ROMDATA_FILES_OFS 0x28910 #define ROMDATA_DATA_OFS 0x39850 #elif VERSION == VERSION_JPN_FINAL #define ROMDATA_ROM_TITLE "PERFECT DARK" #define ROMDATA_ROM_ID "NPDJ" +#define ROMDATA_ROM_DESC "JPN" #define ROMDATA_FILES_OFS 0x28800 #define ROMDATA_DATA_OFS 0x39850 #else @@ -151,6 +154,19 @@ static preprocessfunc filePreprocFuncs[] = { /* LOADTYPE_GUN */ preprocessGunFile, }; +static inline void romdataWrongRomError(const char *fmt, ...) +{ + char reason[1024]; + reason[0] = '\0'; + + va_list args; + va_start(args, fmt); + vsnprintf(reason, sizeof(reason), fmt, args); + va_end(args); + + sysFatalError("Wrong ROM file.\n%s\nEnsure that you have the correct " ROMDATA_ROM_DESC " ROM in z64 format.", reason); +} + static inline void romdataLoadRom(void) { sysLogPrintf(LOG_NOTE, "ROM file: %s", romName); @@ -161,27 +177,29 @@ static inline void romdataLoadRom(void) sysFatalError("Could not open ROM file %s.\nEnsure that it is in the %s directory.", romName, fsFullPath("")); } + // zips are not guaranteed to start with PK, but might as well at least try + if (g_RomFileSize > 2 && (!memcmp(g_RomFile, "PK", 2) || !memcmp(g_RomFile, "Rar", 3) || !memcmp(g_RomFile, "7z", 2))) { + romdataWrongRomError("Your ROM is in an archive file. Please extract it."); + } + if (g_RomFileSize != ROMDATA_ROM_SIZE) { - sysFatalError("Wrong ROM file size.\nExpected: %u\nGot: %u", ROMDATA_ROM_SIZE, g_RomFileSize); + romdataWrongRomError("ROM size does not match: expected: %u, got: %u.", ROMDATA_ROM_SIZE, g_RomFileSize); } if (memcmp(g_RomFile + 0x3b, ROMDATA_ROM_ID, 4) || memcmp(g_RomFile + 0x20, ROMDATA_ROM_TITLE, sizeof(ROMDATA_ROM_TITLE) - 1)) { - sysFatalError("Wrong ROM file.\nEnsure that it is the correct NTSC v1.1 ROM in z64 format."); + romdataWrongRomError("ROM header does not match."); } // inflate the compressed data segment since that's where some useful stuff is u8 *zipped = g_RomFile + ROMDATA_DATA_OFS; - if (!zipped) { - sysFatalError("Data segment not found."); - } if (!rzipIs1173(zipped)) { - sysFatalError("Data segment is not 1173-compressed."); + romdataWrongRomError("Data segment is not 1173-compressed."); } const u32 dataSegLen = ((u32)zipped[2] << 16) | ((u32)zipped[3] << 8) | (u32)zipped[4]; if (dataSegLen < ROMDATA_FILES_OFS) { - sysFatalError("Data segment too small (%u), need at least %u.", dataSegLen, ROMDATA_FILES_OFS); + romdataWrongRomError("Data segment too small (%u), need at least %u.", dataSegLen, ROMDATA_FILES_OFS); } u8 *dataSeg = sysMemAlloc(dataSegLen); @@ -291,7 +309,7 @@ static inline void romdataInitFiles(void) if (!g_RomFile) { // no ROM; try to load the file name list from disk if (!romdataLoadExternalFileList()) { - sysFatalError("No ROM or external file for filename table."); + sysFatalError("No ROM file or external filename table found."); } return; } From d75adeb0c88ffb25edde0267ae21153f32e56c85 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 4 Nov 2023 02:08:20 +0100 Subject: [PATCH 28/36] port: per-player settings NOTE: this will reset big parts of your config --- port/include/input.h | 20 +-- port/src/config.c | 2 +- port/src/input.c | 185 +++++++++++----------- port/src/main.c | 38 +++-- port/src/optionsmenu.c | 310 ++++++++++++++++++++----------------- src/game/activemenutick.c | 5 +- src/game/bondgun.c | 4 +- src/game/bondgunreset.c | 3 + src/game/bondmove.c | 40 ++--- src/game/bondview.c | 1 + src/game/hudmsg.c | 6 +- src/game/mplayer/mplayer.c | 23 +++ src/game/player.c | 42 +---- src/include/data.h | 17 +- src/include/types.h | 16 ++ 15 files changed, 371 insertions(+), 341 deletions(-) diff --git a/port/include/input.h b/port/include/input.h index fb6c410f1..e3ba604ab 100644 --- a/port/include/input.h +++ b/port/include/input.h @@ -95,17 +95,17 @@ s32 inputControllerConnected(s32 idx); // returns bitmask of connected controllers s32 inputControllerMask(void); -s32 inputControllerGetSticksSwapped(void); -void inputControllerSetSticksSwapped(s32 swapped); +s32 inputControllerGetSticksSwapped(s32 cidx); +void inputControllerSetSticksSwapped(s32 cidx, s32 swapped); -s32 inputControllerGetDualAnalog(void); -void inputControllerSetDualAnalog(s32 enable); +s32 inputControllerGetDualAnalog(s32 cidx); +void inputControllerSetDualAnalog(s32 cidx, s32 enable); -f32 inputControllerGetAxisScale(s32 stick, s32 axis); -void inputControllerSetAxisScale(s32 stick, s32 axis, f32 value); +f32 inputControllerGetAxisScale(s32 cidx, s32 stick, s32 axis); +void inputControllerSetAxisScale(s32 cidx, s32 stick, s32 axis, f32 value); -f32 inputControllerGetAxisDeadzone(s32 stick, s32 axis); -void inputControllerSetAxisDeadzone(s32 stick, s32 axis, f32 value); +f32 inputControllerGetAxisDeadzone(s32 cidx, s32 stick, s32 axis); +void inputControllerSetAxisDeadzone(s32 cidx, s32 stick, s32 axis, f32 value); // vk is a value from the virtkey enum above s32 inputKeyPressed(u32 vk); @@ -134,8 +134,8 @@ const char *inputGetContKeyName(u32 ck); // strength is 0 .. 1; 0 strength turns it off void inputRumble(s32 idx, f32 strength, f32 time); -f32 inputRumbleGetStrength(void); -void inputRumbleSetStrength(f32 val); +f32 inputRumbleGetStrength(s32 cidx); +void inputRumbleSetStrength(s32 cidx, f32 val); // locks the mouse cursor in the window and makes it invisible if argument is true void inputLockMouse(s32 lock); diff --git a/port/src/config.c b/port/src/config.c index 4d2383276..5bce419e1 100644 --- a/port/src/config.c +++ b/port/src/config.c @@ -124,7 +124,7 @@ void configRegisterString(const char *key, char *var, u32 maxstr) static void configSetFromString(const char *key, const char *val) { - struct configentry *cfg = configFindOrAddEntry(key); + struct configentry *cfg = configFindEntry(key); if (!cfg) return; s32 tmp_s32; diff --git a/port/src/input.c b/port/src/input.c index deeb9cf0f..7ee49bd10 100644 --- a/port/src/input.c +++ b/port/src/input.c @@ -8,6 +8,7 @@ #include "input.h" #include "video.h" #include "config.h" +#include "utils.h" #include "system.h" #define MAX_BIND_STR 256 @@ -20,9 +21,36 @@ #define WHEEL_DN_MASK SDL_BUTTON(VK_MOUSE_WHEEL_DN - VK_MOUSE_BEGIN + 1) static SDL_GameController *pads[INPUT_MAX_CONTROLLERS]; -static s32 rumbleSupported[INPUT_MAX_CONTROLLERS]; -static u32 binds[MAXCONTROLLERS][CK_TOTAL_COUNT][INPUT_MAX_BINDS]; // [i][CK_][b] = [VK_] +#define CONTROLLERCFG_DEFAULT { \ + .rumbleOn = 0, \ + .rumbleScale = 0.5f, \ + .axisMap = { \ + { SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY }, \ + { SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTY }, \ + }, \ + .sens = { 1.f, 1.f, 1.f, 1.f }, \ + .deadzone = { DEFAULT_DEADZONE, DEFAULT_DEADZONE, DEFAULT_DEADZONE, DEFAULT_DEADZONE_RY }, \ + .stickCButtons = 0, \ + .swapSticks = 1 \ +} + +static struct controllercfg { + s32 rumbleOn; + f32 rumbleScale; + u32 axisMap[2][2]; + f32 sens[4]; + s32 deadzone[4]; + s32 stickCButtons; + s32 swapSticks; +} padsCfg[INPUT_MAX_CONTROLLERS] = { + CONTROLLERCFG_DEFAULT, + CONTROLLERCFG_DEFAULT, + CONTROLLERCFG_DEFAULT, + CONTROLLERCFG_DEFAULT +}; + +static u32 binds[MAXCONTROLLERS][CK_TOTAL_COUNT][INPUT_MAX_BINDS]; static char bindStrs[MAXCONTROLLERS][CK_TOTAL_COUNT][MAX_BIND_STR]; static s32 fakeControllers = 0; @@ -40,30 +68,8 @@ static s32 mouseWheel = 0; static f32 mouseSensX = 1.5f; static f32 mouseSensY = 1.5f; -static f32 rumbleScale = 0.5f; - static s32 lastKey = 0; -// NOTE: by default this gets inverted for 1.2: "right stick" here means left stick on your controller -static u32 axisMap[2][2] = { - { SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY }, - { SDL_CONTROLLER_AXIS_RIGHTX, SDL_CONTROLLER_AXIS_RIGHTY }, -}; - -static f32 stickSens[4] = { - // index == SDL_CONTROLLER_AXIS_* - 1.f, 1.f, 1.f, 1.f -}; - -static s32 deadzone[4] = { - // index == SDL_CONTROLLER_AXIS_* - DEFAULT_DEADZONE, DEFAULT_DEADZONE, - DEFAULT_DEADZONE, DEFAULT_DEADZONE_RY, -}; - -static s32 stickCButtons = 1; -static s32 swapSticks = 1; - static const char *ckNames[CK_TOTAL_COUNT] = { "R_CBUTTONS", "L_CBUTTONS", @@ -215,15 +221,15 @@ static inline void inputInitController(const s32 cidx) { #if SDL_VERSION_ATLEAST(2, 0, 18) // SDL_GameControllerHasRumble() appeared in 2.0.18 even though SDL_GameControllerRumble() is in 2.0.9 - rumbleSupported[cidx] = SDL_GameControllerHasRumble(pads[cidx]); + padsCfg[cidx].rumbleOn = SDL_GameControllerHasRumble(pads[cidx]); #else // assume that all joysticks with haptic feedback support will support rumble - rumbleSupported[cidx] = SDL_JoystickIsHaptic(SDL_GameControllerGetJoystick(pads[cidx])); - if (!rumbleSupported[cidx]) { + padsCfg[cidx].rumbleOn = SDL_JoystickIsHaptic(SDL_GameControllerGetJoystick(pads[cidx])); + if (!padsCfg[cidx].rumbleOn) { // at least on Windows some controllers will report no haptics, but rumble will still function // just assume it's supported if the controller is of known type const SDL_GameControllerType ctype = SDL_GameControllerGetType(pads[cidx]); - rumbleSupported[cidx] = ctype && (ctype != SDL_CONTROLLER_TYPE_VIRTUAL); + padsCfg[cidx].rumbleOn = ctype && (ctype != SDL_CONTROLLER_TYPE_VIRTUAL); } #endif @@ -237,7 +243,7 @@ static inline void inputCloseController(const s32 cidx) { SDL_GameControllerClose(pads[cidx]); pads[cidx] = NULL; - rumbleSupported[cidx] = 0; + padsCfg[cidx].rumbleOn = 0; if (cidx) { connectedMask &= ~(1 << cidx); } @@ -485,12 +491,10 @@ s32 inputInit(void) inputLockMouse(mouseDefaultLocked); - if (swapSticks) { - // invert axis map - axisMap[0][0] = SDL_CONTROLLER_AXIS_RIGHTX; - axisMap[0][1] = SDL_CONTROLLER_AXIS_RIGHTY; - axisMap[1][0] = SDL_CONTROLLER_AXIS_LEFTX; - axisMap[1][1] = SDL_CONTROLLER_AXIS_LEFTY; + // update the axis maps + // NOTE: by default sticks get swapped for 1.2: "right stick" here means left stick on your controller + for (s32 i = 0; i < INPUT_MAX_CONTROLLERS; ++i) { + inputControllerSetSticksSwapped(i, padsCfg[i].swapSticks); } const s32 overrideMask = (1 << fakeControllers) - 1; @@ -556,15 +560,17 @@ s32 inputReadController(s32 idx, OSContPad *npad) return 0; } - s32 leftX = SDL_GameControllerGetAxis(pads[idx], axisMap[0][0]); - s32 leftY = SDL_GameControllerGetAxis(pads[idx], axisMap[0][1]); - s32 rightX = SDL_GameControllerGetAxis(pads[idx], axisMap[1][0]); - s32 rightY = SDL_GameControllerGetAxis(pads[idx], axisMap[1][1]); + const struct controllercfg *cfg = &padsCfg[idx]; - leftX = inputAxisScale(leftX, deadzone[axisMap[0][0]], stickSens[axisMap[0][0]]); - leftY = inputAxisScale(leftY, deadzone[axisMap[0][1]], stickSens[axisMap[0][1]]); - rightX = inputAxisScale(rightX, deadzone[axisMap[1][0]], stickSens[axisMap[1][0]]); - rightY = inputAxisScale(rightY, deadzone[axisMap[1][1]], stickSens[axisMap[1][1]]); + s32 leftX = SDL_GameControllerGetAxis(pads[idx], cfg->axisMap[0][0]); + s32 leftY = SDL_GameControllerGetAxis(pads[idx], cfg->axisMap[0][1]); + s32 rightX = SDL_GameControllerGetAxis(pads[idx], cfg->axisMap[1][0]); + s32 rightY = SDL_GameControllerGetAxis(pads[idx], cfg->axisMap[1][1]); + + leftX = inputAxisScale(leftX, cfg->deadzone[cfg->axisMap[0][0]], cfg->sens[cfg->axisMap[0][0]]); + leftY = inputAxisScale(leftY, cfg->deadzone[cfg->axisMap[0][1]], cfg->sens[cfg->axisMap[0][1]]); + rightX = inputAxisScale(rightX, cfg->deadzone[cfg->axisMap[1][0]], cfg->sens[cfg->axisMap[1][0]]); + rightY = inputAxisScale(rightY, cfg->deadzone[cfg->axisMap[1][1]], cfg->sens[cfg->axisMap[1][1]]); if (!npad->stick_x && leftX) { npad->stick_x = leftX / 0x100; @@ -575,7 +581,7 @@ s32 inputReadController(s32 idx, OSContPad *npad) npad->stick_y = (stickY == 128) ? 127 : stickY; } - if (stickCButtons) { + if (cfg->stickCButtons) { // rstick emulates C buttons if (rightX < -0x4000) npad->button |= L_CBUTTONS; if (rightX > +0x4000) npad->button |= R_CBUTTONS; @@ -649,7 +655,7 @@ s32 inputRumbleSupported(s32 idx) if (idx < 0 || idx >= INPUT_MAX_CONTROLLERS) { return 0; } - return rumbleSupported[idx]; + return padsCfg[idx].rumbleOn; } void inputRumble(s32 idx, f32 strength, f32 time) @@ -658,8 +664,8 @@ void inputRumble(s32 idx, f32 strength, f32 time) return; } - if (rumbleSupported[idx]) { - strength *= rumbleScale; + if (padsCfg[idx].rumbleOn) { + strength *= padsCfg[idx].rumbleScale; if (strength <= 0.f) { strength = 0.f; time = 0.f; @@ -671,14 +677,14 @@ void inputRumble(s32 idx, f32 strength, f32 time) } } -f32 inputRumbleGetStrength(void) +f32 inputRumbleGetStrength(s32 cidx) { - return rumbleScale; + return padsCfg[cidx].rumbleScale; } -void inputRumbleSetStrength(f32 val) +void inputRumbleSetStrength(s32 cidx, f32 val) { - rumbleScale = val; + padsCfg[cidx].rumbleScale = val; } s32 inputControllerMask(void) @@ -686,55 +692,55 @@ s32 inputControllerMask(void) return connectedMask; } -s32 inputControllerGetSticksSwapped(void) +s32 inputControllerGetSticksSwapped(s32 cidx) { - return swapSticks; + return padsCfg[cidx].swapSticks; } -void inputControllerSetSticksSwapped(s32 swapped) +void inputControllerSetSticksSwapped(s32 cidx, s32 swapped) { - swapSticks = swapped; + padsCfg[cidx].swapSticks = swapped; if (swapped) { - axisMap[0][0] = SDL_CONTROLLER_AXIS_RIGHTX; - axisMap[0][1] = SDL_CONTROLLER_AXIS_RIGHTY; - axisMap[1][0] = SDL_CONTROLLER_AXIS_LEFTX; - axisMap[1][1] = SDL_CONTROLLER_AXIS_LEFTY; + padsCfg[cidx].axisMap[0][0] = SDL_CONTROLLER_AXIS_RIGHTX; + padsCfg[cidx].axisMap[0][1] = SDL_CONTROLLER_AXIS_RIGHTY; + padsCfg[cidx].axisMap[1][0] = SDL_CONTROLLER_AXIS_LEFTX; + padsCfg[cidx].axisMap[1][1] = SDL_CONTROLLER_AXIS_LEFTY; } else { - axisMap[0][0] = SDL_CONTROLLER_AXIS_LEFTX; - axisMap[0][1] = SDL_CONTROLLER_AXIS_LEFTY; - axisMap[1][0] = SDL_CONTROLLER_AXIS_RIGHTX; - axisMap[1][1] = SDL_CONTROLLER_AXIS_RIGHTY; + padsCfg[cidx].axisMap[0][0] = SDL_CONTROLLER_AXIS_LEFTX; + padsCfg[cidx].axisMap[0][1] = SDL_CONTROLLER_AXIS_LEFTY; + padsCfg[cidx].axisMap[1][0] = SDL_CONTROLLER_AXIS_RIGHTX; + padsCfg[cidx].axisMap[1][1] = SDL_CONTROLLER_AXIS_RIGHTY; } } -s32 inputControllerGetDualAnalog(void) +s32 inputControllerGetDualAnalog(s32 cidx) { - return !stickCButtons; + return !padsCfg[cidx].stickCButtons; } -void inputControllerSetDualAnalog(s32 enable) +void inputControllerSetDualAnalog(s32 cidx, s32 enable) { - stickCButtons = !enable; + padsCfg[cidx].stickCButtons = !enable; } -f32 inputControllerGetAxisScale(s32 stick, s32 axis) +f32 inputControllerGetAxisScale(s32 cidx, s32 stick, s32 axis) { - return stickSens[stick * 2 + axis]; + return padsCfg[cidx].sens[stick * 2 + axis]; } -void inputControllerSetAxisScale(s32 stick, s32 axis, f32 value) +void inputControllerSetAxisScale(s32 cidx, s32 stick, s32 axis, f32 value) { - stickSens[stick * 2 + axis] = value; + padsCfg[cidx].sens[stick * 2 + axis] = value; } -f32 inputControllerGetAxisDeadzone(s32 stick, s32 axis) +f32 inputControllerGetAxisDeadzone(s32 cidx, s32 stick, s32 axis) { - return (f32)deadzone[stick * 2 + axis] / 32767.f; + return (f32)padsCfg[cidx].deadzone[stick * 2 + axis] / 32767.f; } -void inputControllerSetAxisDeadzone(s32 stick, s32 axis, f32 value) +void inputControllerSetAxisDeadzone(s32 cidx, s32 stick, s32 axis, f32 value) { - deadzone[stick * 2 + axis] = value * 32767.f; + padsCfg[cidx].deadzone[stick * 2 + axis] = value * 32767.f; } void inputKeyBind(s32 idx, u32 ck, s32 bind, u32 vk) @@ -977,22 +983,6 @@ PD_CONSTRUCTOR static void inputConfigInit(void) configRegisterInt("Input.MouseDefaultLocked", &mouseDefaultLocked, 0, 1); configRegisterFloat("Input.MouseSpeedX", &mouseSensX, -10.f, 10.f); configRegisterFloat("Input.MouseSpeedY", &mouseSensY, -10.f, 10.f); - - configRegisterFloat("Input.RumbleScale", &rumbleScale, 0.f, 1.f); - - configRegisterInt("Input.LStickDeadzoneX", &deadzone[0], 0, 32767); - configRegisterInt("Input.LStickDeadzoneY", &deadzone[1], 0, 32767); - configRegisterInt("Input.RStickDeadzoneX", &deadzone[2], 0, 32767); - configRegisterInt("Input.RStickDeadzoneY", &deadzone[3], 0, 32767); - - configRegisterFloat("Input.LStickScaleX", &stickSens[0], -10.f, 10.f); - configRegisterFloat("Input.LStickScaleY", &stickSens[1], -10.f, 10.f); - configRegisterFloat("Input.RStickScaleX", &stickSens[2], -10.f, 10.f); - configRegisterFloat("Input.RStickScaleY", &stickSens[3], -10.f, 10.f); - - configRegisterInt("Input.StickCButtons", &stickCButtons, 0, 1); - configRegisterInt("Input.SwapSticks", &swapSticks, 0, 1); - configRegisterInt("Input.FakeGamepads", &fakeControllers, 0, 4); configRegisterInt("Input.FirstGamepadNum", &firstController, 0, 3); @@ -1000,6 +990,19 @@ PD_CONSTRUCTOR static void inputConfigInit(void) char keyname[256] = { 0 }; for (s32 c = 0; c < MAXCONTROLLERS; ++c) { secname[12] = '1' + c; + secname[13] = '\0'; + configRegisterFloat(strFmt("%s.RumbleScale", secname), &padsCfg[c].rumbleScale, 0.f, 1.f); + configRegisterInt(strFmt("%s.LStickDeadzoneX", secname), &padsCfg[c].deadzone[0], 0, 32767); + configRegisterInt(strFmt("%s.LStickDeadzoneY", secname), &padsCfg[c].deadzone[1], 0, 32767); + configRegisterInt(strFmt("%s.RStickDeadzoneX", secname), &padsCfg[c].deadzone[2], 0, 32767); + configRegisterInt(strFmt("%s.RStickDeadzoneY", secname), &padsCfg[c].deadzone[3], 0, 32767); + configRegisterFloat(strFmt("%s.LStickScaleX", secname), &padsCfg[c].sens[0], -10.f, 10.f); + configRegisterFloat(strFmt("%s.LStickScaleY", secname), &padsCfg[c].sens[1], -10.f, 10.f); + configRegisterFloat(strFmt("%s.RStickScaleX", secname), &padsCfg[c].sens[2], -10.f, 10.f); + configRegisterFloat(strFmt("%s.RStickScaleY", secname), &padsCfg[c].sens[3], -10.f, 10.f); + configRegisterInt(strFmt("%s.StickCButtons", secname), &padsCfg[c].stickCButtons, 0, 1); + configRegisterInt(strFmt("%s.SwapSticks", secname), &padsCfg[c].swapSticks, 0, 1); + secname[13] = '.'; for (u32 ck = 0; ck < CK_TOTAL_COUNT; ++ck) { snprintf(keyname, sizeof(keyname), "%s.%s", secname, inputGetContKeyName(ck)); configRegisterString(keyname, bindStrs[c][ck], MAX_BIND_STR); diff --git a/port/src/main.c b/port/src/main.c index 9e850c02a..7e6d059f4 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -16,6 +16,7 @@ #include "config.h" #include "mod.h" #include "system.h" +#include "utils.h" u32 g_OsMemSize = 0; s32 g_OsMemSizeMb = 16; @@ -57,10 +58,15 @@ void bootCreateSched(void) } } -static void gameLoadConfig(void) +static void gameInit(void) { osMemSize = g_OsMemSizeMb * 1024 * 1024; - g_PlayerFovZoomMultiplier = g_PlayerFovAffectsZoom ? g_PlayerDefaultFovY / 60.0f : 1.0f; + + for (s32 i = 0; i < MAX_PLAYERS; ++i) { + struct extplayerconfig *cfg = g_PlayerExtCfg + i; + cfg->fovzoommult = cfg->fovzoom ? cfg->fovy / 60.0f : 1.0f; + } + if (g_HudCenter) { g_HudAlignModeL = G_ASPECT_CENTER_EXT; g_HudAlignModeR = G_ASPECT_CENTER_EXT; @@ -92,7 +98,7 @@ int main(int argc, const char **argv) audioInit(); romdataInit(); - gameLoadConfig(); + gameInit(); if (fsGetModDir()) { modConfigLoad(MOD_CONFIG_FNAME); @@ -127,19 +133,17 @@ int main(int argc, const char **argv) PD_CONSTRUCTOR static void gameConfigInit(void) { configRegisterInt("Game.MemorySize", &g_OsMemSizeMb, 4, 2048); - - configRegisterFloat("Game.FovY", &g_PlayerDefaultFovY, 5.f, 175.f); - configRegisterInt("Game.FovAffectsZoom", &g_PlayerFovAffectsZoom, 0, 1); - - configRegisterInt("Game.MouseAimMode", &g_PlayerMouseAimMode, 0, 1); - configRegisterFloat("Game.MouseAimSpeedX", &g_PlayerMouseAimSpeedX, 0.f, 10.f); - configRegisterFloat("Game.MouseAimSpeedY", &g_PlayerMouseAimSpeedY, 0.f, 10.f); - - configRegisterFloat("Game.RadialMenuSpeed", &g_PlayerRadialMenuSpeed, 0.f, 10.f); - configRegisterFloat("Game.CrosshairSway", &g_PlayerCrosshairSway, 0.f, 10.f); - configRegisterFloat("Game.ScreenShakeIntensity", &g_ViShakeIntensityMult, 0.f, 10.f); - - configRegisterInt("Game.ClassicCrouch", &g_PlayerClassicCrouch, 0, 1); - configRegisterInt("Game.CenterHUD", &g_HudCenter, 0, 1); + configRegisterFloat("Game.ScreenShakeIntensity", &g_ViShakeIntensityMult, 0.f, 10.f); + for (s32 j = 0; j < MAX_PLAYERS; ++j) { + const s32 i = j + 1; + configRegisterFloat(strFmt("Game.Player%d.FovY", i), &g_PlayerExtCfg[j].fovy, 5.f, 175.f); + configRegisterInt(strFmt("Game.Player%d.FovAffectsZoom", i), &g_PlayerExtCfg[j].fovzoom, 0, 1); + configRegisterInt(strFmt("Game.Player%d.MouseAimMode", i), &g_PlayerExtCfg[j].mouseaimmode, 0, 1); + configRegisterFloat(strFmt("Game.Player%d.MouseAimSpeedX", i), &g_PlayerExtCfg[j].mouseaimspeedx, 0.f, 10.f); + configRegisterFloat(strFmt("Game.Player%d.MouseAimSpeedY", i), &g_PlayerExtCfg[j].mouseaimspeedy, 0.f, 10.f); + configRegisterFloat(strFmt("Game.Player%d.RadialMenuSpeed", i), &g_PlayerExtCfg[j].radialmenuspeed, 0.f, 10.f); + configRegisterFloat(strFmt("Game.Player%d.CrosshairSway", i), &g_PlayerExtCfg[j].crosshairsway, 0.f, 10.f); + configRegisterInt(strFmt("Game.Player%d.ClassicCrouch", i), &g_PlayerExtCfg[j].classiccrouch, 0, 1); + } } diff --git a/port/src/optionsmenu.c b/port/src/optionsmenu.c index 250388f5c..4e428071c 100644 --- a/port/src/optionsmenu.c +++ b/port/src/optionsmenu.c @@ -11,10 +11,85 @@ #include "input.h" #include "config.h" -static s32 g_BindsPlayer = 0; +static s32 g_ExtMenuPlayer = 0; +static struct menudialogdef *g_ExtNextDialog = NULL; + static s32 g_BindIndex = 0; static u32 g_BindContKey = 0; -static char g_BindsPlayerText[] = "Player 1 Bindings"; + +static MenuItemHandlerResult menuhandlerSelectPlayer(s32 operation, struct menuitem *item, union handlerdata *data); + +struct menuitem g_ExtendedSelectPlayerMenuItems[] = { + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Player 1\n", + 0, + menuhandlerSelectPlayer, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Player 2\n", + 0, + menuhandlerSelectPlayer, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Player 3\n", + 0, + menuhandlerSelectPlayer, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Player 4\n", + 0, + menuhandlerSelectPlayer, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_213, // "Back" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +struct menudialogdef g_ExtendedSelectPlayerMenuDialog = { + MENUDIALOGTYPE_DEFAULT, + (uintptr_t)"Select Player", + g_ExtendedSelectPlayerMenuItems, + NULL, + MENUDIALOGFLAG_LITERAL_TEXT, + NULL, +}; + +static MenuItemHandlerResult menuhandlerSelectPlayer(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + g_ExtMenuPlayer = item - g_ExtendedSelectPlayerMenuItems; + ((char *)g_ExtNextDialog->title)[7] = g_ExtMenuPlayer + '1'; + menuPushDialog(g_ExtNextDialog); + } + + return 0; +} static MenuItemHandlerResult menuhandlerMouseEnabled(s32 operation, struct menuitem *item, union handlerdata *data) { @@ -33,9 +108,9 @@ static MenuItemHandlerResult menuhandlerMouseAimLock(s32 operation, struct menui { switch (operation) { case MENUOP_GET: - return g_PlayerMouseAimMode; + return g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimmode; case MENUOP_SET: - g_PlayerMouseAimMode = data->checkbox.value; + g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimmode = data->checkbox.value; break; } @@ -107,16 +182,16 @@ static MenuItemHandlerResult menuhandlerMouseAimSpeedX(s32 operation, struct men { switch (operation) { case MENUOP_GETSLIDER: - if (g_PlayerMouseAimSpeedX < 0.f) { + if (g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedx < 0.f) { data->slider.value = 0; - } else if (g_PlayerMouseAimSpeedX > 10.f) { + } else if (g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedx > 10.f) { data->slider.value = 100; } else { - data->slider.value = g_PlayerMouseAimSpeedX * 10.f + 0.5f; + data->slider.value = g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedx * 10.f + 0.5f; } break; case MENUOP_SET: - g_PlayerMouseAimSpeedX = (f32)data->slider.value / 10.f; + g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedx = (f32)data->slider.value / 10.f; break; } @@ -127,16 +202,16 @@ static MenuItemHandlerResult menuhandlerMouseAimSpeedY(s32 operation, struct men { switch (operation) { case MENUOP_GETSLIDER: - if (g_PlayerMouseAimSpeedY < 0.f) { + if (g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedy < 0.f) { data->slider.value = 0; - } else if (g_PlayerMouseAimSpeedY > 10.f) { + } else if (g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedy > 10.f) { data->slider.value = 100; } else { - data->slider.value = g_PlayerMouseAimSpeedY * 10.f + 0.5f; + data->slider.value = g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedy * 10.f + 0.5f; } break; case MENUOP_SET: - g_PlayerMouseAimSpeedY = (f32)data->slider.value / 10.f; + g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedy = (f32)data->slider.value / 10.f; break; } @@ -147,23 +222,22 @@ static MenuItemHandlerResult menuhandlerRadialMenuSpeed(s32 operation, struct me { switch (operation) { case MENUOP_GETSLIDER: - if (g_PlayerRadialMenuSpeed < 0.f) { + if (g_PlayerExtCfg[0].radialmenuspeed < 0.f) { data->slider.value = 0; - } else if (g_PlayerRadialMenuSpeed> 10.f) { + } else if (g_PlayerExtCfg[0].radialmenuspeed > 10.f) { data->slider.value = 100; } else { - data->slider.value = g_PlayerRadialMenuSpeed * 10.f + 0.5f; + data->slider.value = g_PlayerExtCfg[0].radialmenuspeed * 10.f + 0.5f; } break; case MENUOP_SET: - g_PlayerRadialMenuSpeed = (f32)data->slider.value / 10.f; + g_PlayerExtCfg[0].radialmenuspeed = (f32)data->slider.value / 10.f; break; } return 0; } - struct menuitem g_ExtendedMouseMenuItems[] = { { MENUITEMTYPE_CHECKBOX, @@ -368,10 +442,10 @@ static MenuItemHandlerResult menuhandlerStickSpeed(s32 operation, struct menuite switch (operation) { case MENUOP_GETSLIDER: - data->slider.value = inputControllerGetAxisScale(stick, axis) * 10.f + 0.5f; + data->slider.value = inputControllerGetAxisScale(g_ExtMenuPlayer, stick, axis) * 10.f + 0.5f; break; case MENUOP_SET: - inputControllerSetAxisScale(stick, axis, (f32)data->slider.value / 10.f); + inputControllerSetAxisScale(g_ExtMenuPlayer, stick, axis, (f32)data->slider.value / 10.f); break; } @@ -386,10 +460,10 @@ static MenuItemHandlerResult menuhandlerStickDeadzone(s32 operation, struct menu switch (operation) { case MENUOP_GETSLIDER: - data->slider.value = inputControllerGetAxisDeadzone(stick, axis) * 32.f + 0.5f; + data->slider.value = inputControllerGetAxisDeadzone(g_ExtMenuPlayer, stick, axis) * 32.f + 0.5f; break; case MENUOP_SET: - inputControllerSetAxisDeadzone(stick, axis, (f32)data->slider.value / 32.f); + inputControllerSetAxisDeadzone(g_ExtMenuPlayer, stick, axis, (f32)data->slider.value / 32.f); break; } @@ -409,10 +483,10 @@ static MenuItemHandlerResult menuhandlerVibration(s32 operation, struct menuitem { switch (operation) { case MENUOP_GETSLIDER: - data->slider.value = inputRumbleGetStrength() * 10.f + 0.5f; + data->slider.value = inputRumbleGetStrength(g_ExtMenuPlayer) * 10.f + 0.5f; break; case MENUOP_SET: - inputRumbleSetStrength((f32)data->slider.value / 10.f); + inputRumbleSetStrength(g_ExtMenuPlayer, (f32)data->slider.value / 10.f); break; } @@ -423,9 +497,9 @@ static MenuItemHandlerResult menuhandlerAnalogMovement(s32 operation, struct men { switch (operation) { case MENUOP_GET: - return inputControllerGetDualAnalog(); + return inputControllerGetDualAnalog(g_ExtMenuPlayer); case MENUOP_SET: - inputControllerSetDualAnalog(data->checkbox.value); + inputControllerSetDualAnalog(g_ExtMenuPlayer, data->checkbox.value); break; } @@ -436,9 +510,9 @@ static MenuItemHandlerResult menuhandlerSwapSticks(s32 operation, struct menuite { switch (operation) { case MENUOP_GET: - return inputControllerGetSticksSwapped(); + return inputControllerGetSticksSwapped(g_ExtMenuPlayer); case MENUOP_SET: - inputControllerSetSticksSwapped(data->checkbox.value); + inputControllerSetSticksSwapped(g_ExtMenuPlayer, data->checkbox.value); break; } @@ -497,9 +571,10 @@ struct menuitem g_ExtendedControllerMenuItems[] = { { MENUITEMTYPE_END }, }; +static char g_ExtendedControllerMenuTitle[] = "Player 1 Controller Options"; struct menudialogdef g_ExtendedControllerMenuDialog = { MENUDIALOGTYPE_DEFAULT, - (uintptr_t)"Extended Controller Options", + (uintptr_t)g_ExtendedControllerMenuTitle, g_ExtendedControllerMenuItems, NULL, MENUDIALOGFLAG_LITERAL_TEXT, @@ -565,26 +640,21 @@ static MenuItemHandlerResult menuhandlerCenterHUD(s32 operation, struct menuitem return 0; } -static MenuItemHandlerResult menuhandlerFieldOfView(s32 operation, struct menuitem *item, union handlerdata *data) +static MenuItemHandlerResult menuhandlerScreenShake(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { case MENUOP_GETSLIDER: - data->slider.value = g_PlayerDefaultFovY + 0.5f; + data->slider.value = g_ViShakeIntensityMult * 10.f + 0.5f; break; case MENUOP_SET: - if (data->slider.value >= 15) { - g_PlayerDefaultFovY = data->slider.value; - if (g_PlayerFovAffectsZoom) { - g_PlayerFovZoomMultiplier = g_PlayerDefaultFovY / 60.0f; - } - } + g_ViShakeIntensityMult = (f32)data->slider.value / 10.f; break; } return 0; } -struct menuitem g_ExtendedDisplayMenuItems[] = { +struct menuitem g_ExtendedVideoMenuItems[] = { { MENUITEMTYPE_CHECKBOX, 0, @@ -620,10 +690,10 @@ struct menuitem g_ExtendedDisplayMenuItems[] = { { MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Vert FOV", - 170, - menuhandlerFieldOfView, + MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, + (uintptr_t)"Explosion Shake", + 20, + menuhandlerScreenShake, }, { MENUITEMTYPE_SEPARATOR, @@ -644,50 +714,55 @@ struct menuitem g_ExtendedDisplayMenuItems[] = { { MENUITEMTYPE_END }, }; -struct menudialogdef g_ExtendedDisplayMenuDialog = { +struct menudialogdef g_ExtendedVideoMenuDialog = { MENUDIALOGTYPE_DEFAULT, - (uintptr_t)"Extended Display Options", - g_ExtendedDisplayMenuItems, + (uintptr_t)"Extended Video Options", + g_ExtendedVideoMenuItems, NULL, MENUDIALOGFLAG_LITERAL_TEXT, NULL, }; -static MenuItemHandlerResult menuhandlerClassicCrouch(s32 operation, struct menuitem *item, union handlerdata *data) +static MenuItemHandlerResult menuhandlerFieldOfView(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { - case MENUOP_GET: - return g_PlayerClassicCrouch; + case MENUOP_GETSLIDER: + data->slider.value = g_PlayerExtCfg[g_ExtMenuPlayer].fovy + 0.5f; + break; case MENUOP_SET: - g_PlayerClassicCrouch = data->checkbox.value; + if (data->slider.value >= 15) { + g_PlayerExtCfg[g_ExtMenuPlayer].fovy = data->slider.value; + if (g_PlayerExtCfg[g_ExtMenuPlayer].fovzoom) { + g_PlayerExtCfg[g_ExtMenuPlayer].fovzoommult = g_PlayerExtCfg[g_ExtMenuPlayer].fovy / 60.f; + } + } break; } return 0; } -static MenuItemHandlerResult menuhandlerCrosshairSway(s32 operation, struct menuitem *item, union handlerdata *data) +static MenuItemHandlerResult menuhandlerClassicCrouch(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { - case MENUOP_GETSLIDER: - data->slider.value = g_PlayerCrosshairSway * 10.f + 0.5f; - break; + case MENUOP_GET: + return g_PlayerExtCfg[g_ExtMenuPlayer].classiccrouch; case MENUOP_SET: - g_PlayerCrosshairSway = (f32)data->slider.value / 10.f; + g_PlayerExtCfg[g_ExtMenuPlayer].classiccrouch = data->checkbox.value; break; } return 0; } -static MenuItemHandlerResult menuhandlerScreenShake(s32 operation, struct menuitem *item, union handlerdata *data) +static MenuItemHandlerResult menuhandlerCrosshairSway(s32 operation, struct menuitem *item, union handlerdata *data) { switch (operation) { case MENUOP_GETSLIDER: - data->slider.value = g_ViShakeIntensityMult * 10.f + 0.5f; + data->slider.value = g_PlayerExtCfg[g_ExtMenuPlayer].crosshairsway * 10.f + 0.5f; break; case MENUOP_SET: - g_ViShakeIntensityMult = (f32)data->slider.value / 10.f; + g_PlayerExtCfg[g_ExtMenuPlayer].crosshairsway = (f32)data->slider.value / 10.f; break; } @@ -706,18 +781,18 @@ struct menuitem g_ExtendedGameMenuItems[] = { { MENUITEMTYPE_SLIDER, 0, - MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, - (uintptr_t)"Crosshair Sway", - 20, - menuhandlerCrosshairSway, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Vert FOV", + 170, + menuhandlerFieldOfView, }, { MENUITEMTYPE_SLIDER, 0, MENUITEMFLAG_LITERAL_TEXT | MENUITEMFLAG_SLIDER_WIDE, - (uintptr_t)"Explosion Shake", + (uintptr_t)"Crosshair Sway", 20, - menuhandlerScreenShake, + menuhandlerCrosshairSway, }, { MENUITEMTYPE_SEPARATOR, @@ -738,9 +813,10 @@ struct menuitem g_ExtendedGameMenuItems[] = { { MENUITEMTYPE_END }, }; +static char g_ExtendedGameMenuTitle[] = "Player 1 Game Options"; struct menudialogdef g_ExtendedGameMenuDialog = { MENUDIALOGTYPE_DEFAULT, - (uintptr_t)"Extended Game Options", + (uintptr_t)g_ExtendedGameMenuTitle, g_ExtendedGameMenuItems, NULL, MENUDIALOGFLAG_LITERAL_TEXT, @@ -891,7 +967,7 @@ static MenuItemHandlerResult menuhandlerDoBind(s32 operation, struct menuitem *i const s32 key = inputGetLastKey(); if (key && key != VK_ESCAPE) { - inputKeyBind(g_BindsPlayer, g_BindContKey, g_BindIndex, (key == VK_DELETE ? 0 : key)); + inputKeyBind(g_ExtMenuPlayer, g_BindContKey, g_BindIndex, (key == VK_DELETE ? 0 : key)); menuPopDialog(); } @@ -915,7 +991,7 @@ static MenuItemHandlerResult menuhandlerBind(s32 operation, struct menuitem *ite data->dropdown.value = INPUT_MAX_BINDS; break; case MENUOP_GETOPTIONTEXT: - binds = inputKeyGetBinds(g_BindsPlayer, menuBinds[idx].ck); + binds = inputKeyGetBinds(g_ExtMenuPlayer, menuBinds[idx].ck); if (binds && binds[data->dropdown.value]) { strncpy(keyname, inputGetKeyName(binds[data->dropdown.value]), sizeof(keyname) - 1); for (char *p = keyname; *p; ++p) { @@ -947,97 +1023,51 @@ static MenuItemHandlerResult menuhandlerResetBinds(s32 operation, struct menuite return 0; } +static char g_ExtendedBindsMenuTitle[] = "Player 1 Bindings"; struct menudialogdef g_ExtendedBindsMenuDialog = { MENUDIALOGTYPE_DEFAULT, - (uintptr_t)g_BindsPlayerText, + (uintptr_t)g_ExtendedBindsMenuTitle, g_ExtendedBindsMenuItems, NULL, MENUDIALOGFLAG_LITERAL_TEXT | MENUDIALOGFLAG_STARTSELECTS | MENUDIALOGFLAG_IGNOREBACK, NULL, }; -static MenuItemHandlerResult menuhandlerOpenBinds(s32 operation, struct menuitem *item, union handlerdata *data); - -struct menuitem g_ExtendedSelectBindsMenuItems[] = { - { - MENUITEMTYPE_SELECTABLE, - 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Player 1\n", - 0, - menuhandlerOpenBinds, - }, - { - MENUITEMTYPE_SELECTABLE, - 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Player 2\n", - 0, - menuhandlerOpenBinds, - }, - { - MENUITEMTYPE_SELECTABLE, - 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Player 3\n", - 0, - menuhandlerOpenBinds, - }, - { - MENUITEMTYPE_SELECTABLE, - 0, - MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Player 4\n", - 0, - menuhandlerOpenBinds, - }, - { - MENUITEMTYPE_SEPARATOR, - 0, - 0, - 0, - 0, - NULL, - }, - { - MENUITEMTYPE_SELECTABLE, - 0, - MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, - L_OPTIONS_213, // "Back" - 0, - NULL, - }, - { MENUITEMTYPE_END }, -}; - -static MenuItemHandlerResult menuhandlerOpenBinds(s32 operation, struct menuitem *item, union handlerdata *data) +static MenuItemHandlerResult menuhandlerOpenControllerMenu(s32 operation, struct menuitem *item, union handlerdata *data) { if (operation == MENUOP_SET) { - g_BindsPlayer = item - g_ExtendedSelectBindsMenuItems; - g_BindsPlayerText[7] = g_BindsPlayer + '1'; - menuPushDialog(&g_ExtendedBindsMenuDialog); + g_ExtNextDialog = &g_ExtendedControllerMenuDialog; + menuPushDialog(&g_ExtendedSelectPlayerMenuDialog); } + return 0; +} +static MenuItemHandlerResult menuhandlerOpenGameMenu(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + g_ExtNextDialog = &g_ExtendedGameMenuDialog; + menuPushDialog(&g_ExtendedSelectPlayerMenuDialog); + } return 0; } -struct menudialogdef g_ExtendedSelectBindsMenuDialog = { - MENUDIALOGTYPE_DEFAULT, - (uintptr_t)"Key Bindings", - g_ExtendedSelectBindsMenuItems, - NULL, - MENUDIALOGFLAG_LITERAL_TEXT, - NULL, -}; +static MenuItemHandlerResult menuhandlerOpenBindsMenu(s32 operation, struct menuitem *item, union handlerdata *data) +{ + if (operation == MENUOP_SET) { + g_ExtNextDialog = &g_ExtendedBindsMenuDialog; + menuPushDialog(&g_ExtendedSelectPlayerMenuDialog); + } + return 0; +} struct menuitem g_ExtendedMenuItems[] = { { MENUITEMTYPE_SELECTABLE, 0, MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, - (uintptr_t)"Display\n", + (uintptr_t)"Video\n", 0, - (void *)&g_ExtendedDisplayMenuDialog, + (void *)&g_ExtendedVideoMenuDialog, }, { MENUITEMTYPE_SELECTABLE, @@ -1050,26 +1080,26 @@ struct menuitem g_ExtendedMenuItems[] = { { MENUITEMTYPE_SELECTABLE, 0, - MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + MENUITEMFLAG_LITERAL_TEXT, (uintptr_t)"Controller\n", 0, - (void *)&g_ExtendedControllerMenuDialog, + menuhandlerOpenControllerMenu, }, { MENUITEMTYPE_SELECTABLE, 0, - MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + MENUITEMFLAG_LITERAL_TEXT, (uintptr_t)"Game\n", 0, - (void *)&g_ExtendedGameMenuDialog, + menuhandlerOpenGameMenu, }, { MENUITEMTYPE_SELECTABLE, 0, - MENUITEMFLAG_SELECTABLE_OPENSDIALOG | MENUITEMFLAG_LITERAL_TEXT, + MENUITEMFLAG_LITERAL_TEXT, (uintptr_t)"Key Bindings\n", 0, - (void *)&g_ExtendedSelectBindsMenuDialog, + menuhandlerOpenBindsMenu, }, { MENUITEMTYPE_SEPARATOR, diff --git a/src/game/activemenutick.c b/src/game/activemenutick.c index 4424926cc..29d09993b 100644 --- a/src/game/activemenutick.c +++ b/src/game/activemenutick.c @@ -13,6 +13,7 @@ #include "data.h" #include "types.h" #ifndef PLATFORM_N64 +#include "game/player.h" #include "input.h" #endif @@ -80,8 +81,8 @@ void amTick(void) struct activemenu *am = &g_AmMenus[g_AmIndex]; inputMouseGetAbsScaledDelta(&mdx, &mdy); if (mdx || mdy) { - am->mousex += mdx * g_PlayerRadialMenuSpeed; - am->mousey += mdy * g_PlayerRadialMenuSpeed; + am->mousex += mdx * PLAYER_EXTCFG().radialmenuspeed; + am->mousey += mdy * PLAYER_EXTCFG().radialmenuspeed; am->mousex = (am->mousex > 127.f) ? 127.f : (am->mousex < -128.f) ? -128.f : am->mousex; am->mousey = (am->mousey > 127.f) ? 127.f : (am->mousey < -128.f) ? -128.f : am->mousey; } diff --git a/src/game/bondgun.c b/src/game/bondgun.c index 44da94a36..c7b3b4048 100644 --- a/src/game/bondgun.c +++ b/src/game/bondgun.c @@ -7516,12 +7516,12 @@ void bgunCreateFx(struct hand *hand, s32 handnum, struct weaponfunc *funcdef, s3 static inline f32 bgunGetFovOffsetZ(void) { - return (g_PlayerDefaultFovY - 60.f) / 3.f; + return (PLAYER_DEFAULT_FOV - 60.f) / 3.f; } static inline f32 bgunGetFovOffsetY(void) { - return (g_PlayerDefaultFovY - 60.f) / (2.75f * 4.f); + return (PLAYER_DEFAULT_FOV - 60.f) / (2.75f * 4.f); } #endif diff --git a/src/game/bondgunreset.c b/src/game/bondgunreset.c index 182cefd75..9e61a70b4 100644 --- a/src/game/bondgunreset.c +++ b/src/game/bondgunreset.c @@ -7,6 +7,9 @@ #include "lib/memp.h" #include "data.h" #include "types.h" +#ifndef PLATFORM_N64 +#include "game/player.h" +#endif extern u32 g_BgunGunMemBaseSize4Mb2P; diff --git a/src/game/bondmove.c b/src/game/bondmove.c index 47e778af6..2bbc9b110 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -770,7 +770,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i #ifndef PLATFORM_N64 if (allowmlook) { inputMouseGetScaledDelta(&movedata.freelookdx, &movedata.freelookdy); - allowmcross = (g_PlayerMouseAimMode == MOUSEAIM_CLASSIC) && + allowmcross = (PLAYER_EXTCFG().mouseaimmode == MOUSEAIM_CLASSIC) && (movedata.freelookdx || movedata.freelookdy || g_Vars.currentplayer->swivelpos[0] || g_Vars.currentplayer->swivelpos[1]); if (movedata.invertpitch) { movedata.freelookdy = -movedata.freelookdy; @@ -1289,7 +1289,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i movedata.analoglean = 0.f; } - if (g_PlayerMouseAimMode == MOUSEAIM_LOCKED || bgunGetWeaponNum(HAND_RIGHT) == WEAPON_HORIZONSCANNER) { + if (PLAYER_EXTCFG().mouseaimmode == MOUSEAIM_LOCKED || bgunGetWeaponNum(HAND_RIGHT) == WEAPON_HORIZONSCANNER) { movedata.cannaturalpitch = movedata.cannaturalpitch || (movedata.freelookdy != 0.0f); movedata.cannaturalturn = movedata.cannaturalturn || (movedata.freelookdx != 0.0f); } @@ -1653,7 +1653,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i #endif // Handle C-button and analog crouch and uncrouch, if enabled - if (g_PlayerClassicCrouch && allowc1buttons) { + if (PLAYER_EXTCFG().classiccrouch && allowc1buttons) { for (i = 0; i < numsamples; i++) { if (!canmanualzoom && aimonhist[i]) { bool goUp = joyGetButtonsPressedOnSample(i, contpad1, c1allowedbuttons & (U_CBUTTONS)); @@ -1831,11 +1831,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i } if (g_Vars.currentplayer->pausemode == PAUSEMODE_UNPAUSED && !g_MainIsEndscreen) { -#ifdef PLATFORM_N64 - zoomfov = 60; -#else - zoomfov = g_PlayerDefaultFovY; -#endif + zoomfov = PLAYER_DEFAULT_FOV; // FarSight in secondary function if (bgunGetWeaponNum(HAND_RIGHT) == WEAPON_FARSIGHT @@ -1844,15 +1840,9 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i && g_Vars.currentplayer->autoeraserdist > 0) { eraserfov = cam0f0b49b8(500.0f / g_Vars.currentplayer->autoeraserdist); -#ifdef PLATFORM_N64 - if (eraserfov > 60) { - eraserfov = 60; - } -#else - if (eraserfov > g_PlayerDefaultFovY) { - eraserfov = g_PlayerDefaultFovY; + if (eraserfov > PLAYER_DEFAULT_FOV) { + eraserfov = PLAYER_DEFAULT_FOV; } -#endif if (eraserfov < ADJUST_ZOOM_FOV(2)) { eraserfov = ADJUST_ZOOM_FOV(2); @@ -1886,15 +1876,9 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i zoomfov = currentPlayerGetGunZoomFov(); } -#ifdef PLATFORM_N64 - if (zoomfov <= 0) { - zoomfov = 60; - } -#else if (zoomfov <= 0) { - zoomfov = g_PlayerDefaultFovY; + zoomfov = PLAYER_DEFAULT_FOV; } -#endif playerTweenFovY(zoomfov); playerUpdateZoom(); @@ -2170,10 +2154,10 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i #else f32 xscale, yscale; if (movedata.freelookdx || movedata.freelookdy) { - xscale = g_PlayerCrosshairSway * 0.20f; - yscale = g_PlayerCrosshairSway * 0.30f; + xscale = PLAYER_EXTCFG().crosshairsway * 0.20f; + yscale = PLAYER_EXTCFG().crosshairsway * 0.30f; } else { - xscale = yscale = g_PlayerCrosshairSway; + xscale = yscale = PLAYER_EXTCFG().crosshairsway; } x = g_Vars.currentplayer->speedtheta * 0.3f * xscale + g_Vars.currentplayer->gunextraaimx; y = -g_Vars.currentplayer->speedverta * 0.1f * yscale + g_Vars.currentplayer->gunextraaimy; @@ -2188,8 +2172,8 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i #ifndef PLATFORM_N64 if (allowmcross) { // joystick is inactive, move crosshair using the mouse - const f32 xscale = g_PlayerMouseAimSpeedX * 320.f / (f32)videoGetWidth(); - const f32 yscale = g_PlayerMouseAimSpeedY * 240.f / (f32)videoGetHeight(); + const f32 xscale = PLAYER_EXTCFG().mouseaimspeedx * 320.f / (f32)videoGetWidth(); + const f32 yscale = PLAYER_EXTCFG().mouseaimspeedy * 240.f / (f32)videoGetHeight(); f32 x = g_Vars.currentplayer->swivelpos[0] + movedata.freelookdx * xscale; f32 y = g_Vars.currentplayer->swivelpos[1] + movedata.freelookdy * yscale; x = (x < -1.f) ? -1.f : ((x > 1.f) ? 1.f : x); diff --git a/src/game/bondview.c b/src/game/bondview.c index 0264567d1..1b6935ce0 100644 --- a/src/game/bondview.c +++ b/src/game/bondview.c @@ -21,6 +21,7 @@ #include "types.h" #include "gbiex.h" #ifndef PLATFORM_N64 +#include "game/player.h" #include "video.h" #endif diff --git a/src/game/hudmsg.c b/src/game/hudmsg.c index aad03591c..fe0b6cfbf 100644 --- a/src/game/hudmsg.c +++ b/src/game/hudmsg.c @@ -269,11 +269,7 @@ Gfx *hudmsgRenderZoomRange(Gfx *gdl, u32 alpha) return gdl; } } else { -#ifdef PLATFORM_N64 - maxzoom = 60.0f / zoomfov; -#else - maxzoom = g_PlayerDefaultFovY / zoomfov; -#endif + maxzoom = PLAYER_DEFAULT_FOV / zoomfov; curzoom = maxzoom - 1.0f / (zoomfov / zoominfovy) + 1; } diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 879f7ec34..b389e6215 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -109,6 +109,29 @@ struct mpweapon g_MpWeapons[NUM_MPWEAPONS] = { /*0x2e*/ { WEAPON_DISABLED }, // 0x25 on N64 }; +#ifndef PLATFORM_N64 + +#define PLAYER_EXT_CFG_DEFAULT { \ + .fovy = 60.f, \ + .fovzoommult = 1.f, \ + .fovzoom = true, \ + .mouseaimmode = MOUSEAIM_CLASSIC, \ + .mouseaimspeedx = 0.7f, \ + .mouseaimspeedy = 0.7f, \ + .radialmenuspeed = 4.f, \ + .crosshairsway = 1.f, \ + .classiccrouch = true, \ +} + +struct extplayerconfig g_PlayerExtCfg[MAX_PLAYERS] = { + PLAYER_EXT_CFG_DEFAULT, + PLAYER_EXT_CFG_DEFAULT, + PLAYER_EXT_CFG_DEFAULT, + PLAYER_EXT_CFG_DEFAULT, +}; + +#endif + /** * Converts the given value into a float on a curved scale from 0.1 to 10. * diff --git a/src/game/player.c b/src/game/player.c index 4b0645428..8bb27558f 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -202,18 +202,6 @@ s16 g_DeathAnimations[] = { s32 g_NumDeathAnimations = 0; -#ifndef PLATFORM_N64 -f32 g_PlayerDefaultFovY = 60.f; -f32 g_PlayerCrosshairSway = 1.f; -s32 g_PlayerMouseAimMode = MOUSEAIM_CLASSIC; -f32 g_PlayerMouseAimSpeedX = 0.7f; -f32 g_PlayerMouseAimSpeedY = 0.7f; -f32 g_PlayerRadialMenuSpeed = 4.0f; -s32 g_PlayerFovAffectsZoom = 1; -f32 g_PlayerFovZoomMultiplier = 1.0f; -s32 g_PlayerClassicCrouch = true; -#endif - /** * Choose which location to spawn into from the given pads. Write the position * and rooms to the dstpos and dstrooms pointers and return the angle that the @@ -2086,8 +2074,8 @@ void playerTweenFovY(f32 targetfovy) f32 speed = 15.0f / 30.0f; #ifndef PLATFORM_N64 - if (g_PlayerDefaultFovY > 60.0f) { // adjust zoom speed depending on non-default fov setting (higher fov == faster zoom) - speed /= g_PlayerDefaultFovY / 60.0f; + if (PLAYER_DEFAULT_FOV > 60.0f) { // adjust zoom speed depending on non-default fov setting (higher fov == faster zoom) + speed /= PLAYER_DEFAULT_FOV / 60.0f; } #endif @@ -2712,7 +2700,7 @@ Gfx *playerRenderHealthBar(Gfx *gdl) #ifdef PLATFORM_N64 mtx00016ae4(&matrix, 0, 370, 0, 0, 0, 0, 0, 0, -1); #else - f32 fovsc = 60.f / g_PlayerDefaultFovY; + f32 fovsc = 60.f / PLAYER_DEFAULT_FOV; if (fovsc > 1.01f) { fovsc *= 1.1f; } @@ -3167,22 +3155,14 @@ void playerConfigureVi(void) var800800f0jf = 0; #endif -#ifdef PLATFORM_N64 - playermgrSetFovY(60); -#else - playermgrSetFovY(g_PlayerDefaultFovY); -#endif + playermgrSetFovY(PLAYER_DEFAULT_FOV); playermgrSetAspectRatio(ratio); playermgrSetViewSize(playerGetViewportWidth(), playerGetViewportHeight()); playermgrSetViewPosition(playerGetViewportLeft(), playerGetViewportTop()); viSetMode(g_ViModes[g_ViRes].xscale); -#ifdef PLATFORM_N64 - viSetFovAspectAndSize(60, ratio, playerGetViewportWidth(), playerGetViewportHeight()); -#else - viSetFovAspectAndSize(g_PlayerDefaultFovY, ratio, playerGetViewportWidth(), playerGetViewportHeight()); -#endif + viSetFovAspectAndSize(PLAYER_DEFAULT_FOV, ratio, playerGetViewportWidth(), playerGetViewportHeight()); viSetViewPosition(playerGetViewportLeft(), playerGetViewportTop()); viSetSize(playerGetFbWidth(), playerGetFbHeight()); @@ -3245,21 +3225,13 @@ void playerTick(bool arg0) return; } -#ifdef PLATFORM_N64 - playermgrSetFovY(60); -#else - playermgrSetFovY(g_PlayerDefaultFovY); -#endif + playermgrSetFovY(PLAYER_DEFAULT_FOV); playermgrSetAspectRatio(aspectratio); playermgrSetViewSize(playerGetViewportWidth(), playerGetViewportHeight()); playermgrSetViewPosition(playerGetViewportLeft(), playerGetViewportTop()); viSetMode(g_ViModes[g_ViRes].xscale); -#ifdef PLATFORM_N64 - viSetFovAspectAndSize(60, aspectratio, playerGetViewportWidth(), playerGetViewportHeight()); -#else - viSetFovAspectAndSize(g_PlayerDefaultFovY, aspectratio, playerGetViewportWidth(), playerGetViewportHeight()); -#endif + viSetFovAspectAndSize(PLAYER_DEFAULT_FOV, aspectratio, playerGetViewportWidth(), playerGetViewportHeight()); viSetViewPosition(playerGetViewportLeft(), playerGetViewportTop()); viSetSize(playerGetFbWidth(), playerGetFbHeight()); viSetBufSize(playerGetFbWidth(), playerGetFbHeight()); diff --git a/src/include/data.h b/src/include/data.h index e6598f3f6..93c084c8f 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -531,19 +531,12 @@ extern struct menudialogdef g_HangarListMenuDialog; #ifndef PLATFORM_N64 +extern struct extplayerconfig g_PlayerExtCfg[MAX_PLAYERS]; + extern struct weathercfg g_WeatherConfig[WEATHERCFG_MAX_STAGES]; extern const struct weathercfg g_DefaultWeatherConfig; extern const struct weathercfg *g_CurWeatherConfig; -extern f32 g_PlayerCrosshairSway; -extern f32 g_PlayerDefaultFovY; -extern s32 g_PlayerMouseAimMode; -extern f32 g_PlayerMouseAimSpeedX; -extern f32 g_PlayerMouseAimSpeedY; -extern f32 g_PlayerRadialMenuSpeed; -extern s32 g_PlayerFovAffectsZoom; -extern f32 g_PlayerFovZoomMultiplier; -extern s32 g_PlayerClassicCrouch; extern f32 g_ViShakeIntensityMult; extern u32 g_TexFilter2D; extern s32 g_HudCenter; @@ -552,13 +545,17 @@ extern u32 g_HudAlignModeR; extern s32 g_PrevFrameFb; extern s32 g_PrevFrameCapTimer; +#define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayernum] +#define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy) + #define TEX_FILTER_2D g_TexFilter2D -#define ADJUST_ZOOM_FOV(x) ((x) * g_PlayerFovZoomMultiplier) +#define ADJUST_ZOOM_FOV(x) ((x) * PLAYER_EXTCFG().fovzoommult) #else // PLATFORM_N64 #define TEX_FILTER_2D G_TF_BILERP #define ADJUST_ZOOM_FOV(x) (x) +#define PLAYER_DEFAULT_FOV 60.f #endif // PLATFORM_N64 diff --git a/src/include/types.h b/src/include/types.h index 71930a240..9917f21f7 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -6123,4 +6123,20 @@ struct xz { f32 z; }; +#ifndef PLATFORM_N64 + +struct extplayerconfig { + f32 fovy; + f32 fovzoommult; + s32 fovzoom; + s32 mouseaimmode; + f32 mouseaimspeedx; + f32 mouseaimspeedy; + s32 classiccrouch; + f32 radialmenuspeed; + f32 crosshairsway; +}; + +#endif + #endif From 407d4724a77e916db7950c3e03a09d71391dcdcc Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 4 Nov 2023 02:39:16 +0100 Subject: [PATCH 29/36] port: use mpindex to index player specific settings --- src/include/data.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/data.h b/src/include/data.h index 93c084c8f..9d73ab162 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -545,7 +545,7 @@ extern u32 g_HudAlignModeR; extern s32 g_PrevFrameFb; extern s32 g_PrevFrameCapTimer; -#define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayernum] +#define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3] #define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy) #define TEX_FILTER_2D g_TexFilter2D From 828776e272e114ea4065b16efc10de5bf58780c7 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Nov 2023 23:08:34 +0100 Subject: [PATCH 30/36] port: account for non-segmented addresses in bgTestHitOnObj --- src/game/bg.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/game/bg.c b/src/game/bg.c index aead192ca..f7710afa9 100644 --- a/src/game/bg.c +++ b/src/game/bg.c @@ -3659,9 +3659,23 @@ bool bgTestHitOnObj(struct coord *arg0, struct coord *arg1, struct coord *arg2, } else if (gdl->dma.cmd == G_VTX) { ptr = var800a6470; count = gdl->bytes[GFX_W0_BYTE(1)] & 0xf; - offset = (UNSEGADDR(gdl->words.w1) & 0xffffff); numvertices = (((u32) gdl->bytes[GFX_W0_BYTE(1)] >> 4) & 0xf) + 1; + +#ifdef PLATFORM_N64 + offset = (UNSEGADDR(gdl->words.w1) & 0xffffff); vtx = (Vtx *)((uintptr_t)vertices + offset); +#else + if (gdl->words.w1 & 1) { + // segmented address + offset = (UNSEGADDR(gdl->words.w1) & 0xffffff); + vtx = (Vtx *)((uintptr_t)vertices + offset); + } else { + // linear address + offset = 0; + vtx = (Vtx *)gdl->words.w1; + } +#endif + vtx -= count; ptr[0] = vtx->x; From 0e043f24b526c82ae534c8097ed15489da5c7fda Mon Sep 17 00:00:00 2001 From: Catherine Reprobate Date: Mon, 6 Nov 2023 22:04:23 -0800 Subject: [PATCH 31/36] Remote mine fixes - N64 fix: fixes issue where selecting the remote mine from the active menu or the inventory menu when Jo already has a remote mine equipped causes the player to unequip the detonator Confirmed this issue presents on ntsc-final on 1964GEPD. - port fix: fixes issue with remote mine input handler where the previous active menu slotnum would be activeated. The slotnum is unconditionally set to 4 (the "resting no-op" slotnum) to prevent this --- src/game/activemenu.c | 7 ++++++- src/game/bondmove.c | 3 +++ src/game/mainmenu.c | 9 ++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/game/activemenu.c b/src/game/activemenu.c index e062acf5e..fdb3ef051 100644 --- a/src/game/activemenu.c +++ b/src/game/activemenu.c @@ -364,7 +364,12 @@ void amApply(s32 slot) bgunEquipWeapon2(HAND_RIGHT, weaponnum); } - if (bgunGetWeaponNum(HAND_LEFT) != WEAPON_NONE) { + // don't unequip detonator + // if we already have it equipped + if (weaponnum == WEAPON_REMOTEMINE) { + bgunEquipWeapon2(HAND_LEFT, weaponnum); + } + else if (bgunGetWeaponNum(HAND_LEFT) != WEAPON_NONE) { bgunEquipWeapon2(HAND_LEFT, WEAPON_NONE); } } diff --git a/src/game/bondmove.c b/src/game/bondmove.c index 2bbc9b110..1a74a6fd2 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -51,6 +51,9 @@ static void bgunProcessQuickDetonate(struct movedata *data, u32 c1buttons, u32 c data->weaponbackoffset = 0; data->weaponforwardoffset = 0; data->btapcount = 0; + // prevent the previous slotnum + // from causing Jo to switch weapons + g_AmMenus[g_AmIndex].slotnum = 4; amClose(); g_Vars.currentplayer->invdowntime = -2; g_Vars.currentplayer->usedowntime = -2; diff --git a/src/game/mainmenu.c b/src/game/mainmenu.c index 29ab9fab0..b1f0bbab6 100644 --- a/src/game/mainmenu.c +++ b/src/game/mainmenu.c @@ -4304,7 +4304,14 @@ MenuItemHandlerResult menuhandlerInventoryList(s32 operation, struct menuitem *i bgunEquipWeapon2(HAND_LEFT, weaponnum); } else { bgunEquipWeapon2(HAND_RIGHT, weaponnum); - bgunEquipWeapon2(HAND_LEFT, WEAPON_NONE); + // don't unequip detonator + // if we already have it equipped + if (weaponnum == WEAPON_REMOTEMINE) { + bgunEquipWeapon2(HAND_LEFT, weaponnum); + } + else{ + bgunEquipWeapon2(HAND_LEFT, WEAPON_NONE); + } } } From 5df6d2fdfbc3dfab8ffc0cc2beb513c74569c727 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 11 Nov 2023 17:41:10 +0100 Subject: [PATCH 32/36] port: slightly simpler fix for bgTestHitOnObj --- src/game/bg.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/game/bg.c b/src/game/bg.c index f7710afa9..0b2c0b03b 100644 --- a/src/game/bg.c +++ b/src/game/bg.c @@ -3659,23 +3659,19 @@ bool bgTestHitOnObj(struct coord *arg0, struct coord *arg1, struct coord *arg2, } else if (gdl->dma.cmd == G_VTX) { ptr = var800a6470; count = gdl->bytes[GFX_W0_BYTE(1)] & 0xf; - numvertices = (((u32) gdl->bytes[GFX_W0_BYTE(1)] >> 4) & 0xf) + 1; - #ifdef PLATFORM_N64 offset = (UNSEGADDR(gdl->words.w1) & 0xffffff); - vtx = (Vtx *)((uintptr_t)vertices + offset); #else if (gdl->words.w1 & 1) { // segmented address offset = (UNSEGADDR(gdl->words.w1) & 0xffffff); - vtx = (Vtx *)((uintptr_t)vertices + offset); } else { // linear address - offset = 0; - vtx = (Vtx *)gdl->words.w1; + offset = gdl->words.w1 - (uintptr_t)vertices; } #endif - + numvertices = (((u32) gdl->bytes[GFX_W0_BYTE(1)] >> 4) & 0xf) + 1; + vtx = (Vtx *)((uintptr_t)vertices + offset); vtx -= count; ptr[0] = vtx->x; From f1edc91fd8e787b065a16a7de935b8b49f4026be Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 11 Nov 2023 17:41:19 +0100 Subject: [PATCH 33/36] port: fix whitespace --- src/game/activemenu.c | 3 +-- src/game/mainmenu.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/game/activemenu.c b/src/game/activemenu.c index fdb3ef051..917504112 100644 --- a/src/game/activemenu.c +++ b/src/game/activemenu.c @@ -368,8 +368,7 @@ void amApply(s32 slot) // if we already have it equipped if (weaponnum == WEAPON_REMOTEMINE) { bgunEquipWeapon2(HAND_LEFT, weaponnum); - } - else if (bgunGetWeaponNum(HAND_LEFT) != WEAPON_NONE) { + } else if (bgunGetWeaponNum(HAND_LEFT) != WEAPON_NONE) { bgunEquipWeapon2(HAND_LEFT, WEAPON_NONE); } } diff --git a/src/game/mainmenu.c b/src/game/mainmenu.c index b1f0bbab6..c7e309666 100644 --- a/src/game/mainmenu.c +++ b/src/game/mainmenu.c @@ -4308,8 +4308,7 @@ MenuItemHandlerResult menuhandlerInventoryList(s32 operation, struct menuitem *i // if we already have it equipped if (weaponnum == WEAPON_REMOTEMINE) { bgunEquipWeapon2(HAND_LEFT, weaponnum); - } - else{ + } else { bgunEquipWeapon2(HAND_LEFT, WEAPON_NONE); } } From cd19c07810277fe37c911037a787b3993c50d8a9 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 11 Nov 2023 17:42:39 +0100 Subject: [PATCH 34/36] port: fix player model flinching at the start of a cutscene patch contributed by Ryan Dwyer --- src/game/chraction.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/game/chraction.c b/src/game/chraction.c index 1f1dd0f18..2d0495171 100644 --- a/src/game/chraction.c +++ b/src/game/chraction.c @@ -4811,7 +4811,9 @@ void chrDamage(struct chrdata *chr, f32 damage, struct coord *vector, struct gse chrChoke(chr, choketype); } - chrFlinchBody(chr); + if (g_Vars.currentplayer->haschrbody) { + chrFlinchBody(chr); + } } // Handle player boost From eecee9d94054d9885a960a415a79bd7f0ee5c8be Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 12 Nov 2023 13:56:50 +0100 Subject: [PATCH 35/36] port: fix #55 patch submitted by Ryan Dwyer --- src/game/hudmsg.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/game/hudmsg.c b/src/game/hudmsg.c index fe0b6cfbf..650906509 100644 --- a/src/game/hudmsg.c +++ b/src/game/hudmsg.c @@ -1515,7 +1515,9 @@ Gfx *hudmsgsRender(Gfx *gdl) gdl = hudmsgRenderBox(gdl, x - 3, y - 3, x + msg->width + 2, y + msg->height + 2, 1.0f, bordercolour, spc0); #endif - gdl = textRenderProjected(gdl, &x, &y, msg->text, msg->font1, msg->font2, textcolour, viGetWidth(), viGetHeight(), 0, 0); + if (spc0 > 0) { + gdl = textRenderProjected(gdl, &x, &y, msg->text, msg->font1, msg->font2, textcolour, viGetWidth(), viGetHeight(), 0, 0); + } } else { gdl = text0f153a34(gdl, x, y, x + msg->width, y + msg->height, 0); @@ -1597,7 +1599,9 @@ Gfx *hudmsgsRender(Gfx *gdl) gdl = hudmsgRenderBox(gdl, x - 3, y - 3, x + msg->width + 2, y + msg->height + 2, 1.0f, bordercolour, 1.0f - spa8); #endif - gdl = textRenderProjected(gdl, &x, &y, msg->text, msg->font1, msg->font2, textcolour, viGetWidth(), viGetHeight(), 0, 0); + if (spa8 < 1.0f) { + gdl = textRenderProjected(gdl, &x, &y, msg->text, msg->font1, msg->font2, textcolour, viGetWidth(), viGetHeight(), 0, 0); + } } else { gdl = text0f153a34(gdl, x, y, x + msg->width, y + msg->height, 0); From 83a155fa49be8568ec1f5f8c9752e6d50e955438 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 12 Nov 2023 15:05:21 +0100 Subject: [PATCH 36/36] port: fix #210 by using a separate fullscreen fb for blur effects --- port/src/pdsched.c | 14 ++++++++------ src/game/bondview.c | 8 ++++---- src/include/data.h | 3 ++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/port/src/pdsched.c b/port/src/pdsched.c index bd51c13f4..4501b15a5 100644 --- a/port/src/pdsched.c +++ b/port/src/pdsched.c @@ -102,7 +102,8 @@ OSScMsg g_SchedRspMsg = {OS_SC_RSP_MSG}; bool g_SchedIsFirstTask = true; s32 g_PrevFrameFb = -1; -s32 g_PrevFrameCapTimer = -1; +s32 g_BlurFb = -1; +s32 g_BlurFbCapTimer = -1; void schedSetCrashEnable1(bool enable) { @@ -180,6 +181,7 @@ void osCreateScheduler(OSSched *sc, OSThread *thread, u8 mode, u32 numFields) schedInitCrashLastRendered(); g_PrevFrameFb = videoCreateFramebuffer(0, 0, false, true); + g_BlurFb = videoCreateFramebuffer(0, 0, false, true); } void osScAddClient(OSSched *sc, OSScClient *c, OSMesgQueue *msgQ, bool is30fps) @@ -385,11 +387,11 @@ void schedConsiderScreenshot(void) g_MenuData.screenshottimer = 0; } - if (g_PrevFrameCapTimer == 0) { - videoCopyFramebuffer(g_PrevFrameFb, 0, -1, -1); - g_PrevFrameCapTimer = -1; - } else if (g_PrevFrameCapTimer > 0) { - --g_PrevFrameCapTimer; + if (g_BlurFbCapTimer == 0) { + videoCopyFramebuffer(g_BlurFb, 0, -1, -1); + g_BlurFbCapTimer = -1; + } else if (g_BlurFbCapTimer > 0) { + --g_BlurFbCapTimer; } if (g_MenuData.screenshottimer >= 2) { diff --git a/src/game/bondview.c b/src/game/bondview.c index 1b6935ce0..7101e21d1 100644 --- a/src/game/bondview.c +++ b/src/game/bondview.c @@ -307,12 +307,12 @@ Gfx *bviewDrawMotionBlur(Gfx *gdl, u32 colour, u32 alpha) somefloat += 1.0f / fyyy; } #else - gDPSetFramebufferTextureEXT(gdl++, 0, 0, 0, g_PrevFrameFb); + gDPSetFramebufferTextureEXT(gdl++, 0, 0, 0, g_BlurFb); gSPImageRectangleEXT(gdl++, viewleft << 2, viewtop << 2, viewleft, viewtop, (viewleft + viewwidth) << 2, (viewtop + viewheight) << 2, viewleft + viewwidth, viewtop + viewheight, 0, videoGetNativeWidth(), videoGetNativeHeight()); - g_PrevFrameCapTimer = 0; + g_BlurFbCapTimer = 0; #endif return gdl; @@ -545,12 +545,12 @@ Gfx *bviewDrawZoomBlur(Gfx *gdl, u32 colour, s32 alpha, f32 arg3, f32 arg4) const s32 top = ycenter - halfh; const s32 right = xcenter + halfw; const s32 bottom = ycenter + halfh; - gDPSetFramebufferTextureEXT(gdl++, 0, 0, 0, g_PrevFrameFb); + gDPSetFramebufferTextureEXT(gdl++, 0, 0, 0, g_BlurFb); gSPImageRectangleEXT(gdl++, left << 2, top << 2, viewleft, viewtop, right << 2, bottom << 2, viewleft + viewwidth, viewtop + viewheight, 0, videoGetNativeWidth(), videoGetNativeHeight()); - g_PrevFrameCapTimer = 0; + g_BlurFbCapTimer = 0; #endif return gdl; diff --git a/src/include/data.h b/src/include/data.h index 9d73ab162..5a897577d 100644 --- a/src/include/data.h +++ b/src/include/data.h @@ -543,7 +543,8 @@ extern s32 g_HudCenter; extern u32 g_HudAlignModeL; extern u32 g_HudAlignModeR; extern s32 g_PrevFrameFb; -extern s32 g_PrevFrameCapTimer; +extern s32 g_BlurFb; +extern s32 g_BlurFbCapTimer; #define PLAYER_EXTCFG() g_PlayerExtCfg[g_Vars.currentplayerstats->mpindex & 3] #define PLAYER_DEFAULT_FOV (PLAYER_EXTCFG().fovy)