Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0374e2a
Overhauled Camera system + Decoupled Mouse Crosshair
AL2009man Aug 22, 2025
dedfee0
Normalized Mouse Crosshair Swivel
AL2009man Aug 23, 2025
18206c2
moved `inputMouseGetScaledDeltaCrosshair` to `bondmove.c` directly
AL2009man Aug 23, 2025
d32b82d
Merge branch 'fgsfdsfgs:port' into PerfectQuakeDark3
AL2009man Aug 24, 2025
9f680d2
fixed regression on CamSpy and Hoverbike
AL2009man Aug 24, 2025
fdbc9c0
re-move MouseGetScaledDeltaCrosshair scaling back to bmove
AL2009man Aug 24, 2025
9e2f436
fixed regression in Crosshair Swivel.
AL2009man Aug 31, 2025
938171b
fixed regression on Crosshair Sway while using Mixed Input and clampe…
AL2009man Aug 31, 2025
48a24f5
cleaned up AngleCamera, attempt to address netplay/demo compatibility
AL2009man Oct 13, 2025
e223727
fixed reverse whitespace
AL2009man Oct 13, 2025
db7b94b
Merge branch 'fgsfdsfgs:port' into PerfectQuakeDark3
AL2009man Oct 13, 2025
f856865
fixed regression with Mouse Crosshair Aiming always being active rega…
AL2009man Oct 13, 2025
610f8f1
remove decoupled crosshair infavor with `mouseaimspeedx/y`
AL2009man Dec 4, 2025
ea0704b
Merge branch 'port' into PerfectQuakeDark3
AL2009man Dec 5, 2025
2348fff
replaced temp movedata with actual camera yaw/pitch
AL2009man Dec 8, 2025
44f6a61
Revert "replaced temp movedata with actual camera yaw/pitch"
AL2009man Dec 8, 2025
908de2a
Reworked Angle-based Camera system to directly use the engine camera …
AL2009man Jan 23, 2026
08e739b
Merge branch 'port' into PerfectQuakeDark3
AL2009man Mar 23, 2026
c27ad50
Fix missing newline at end of `optionsmenu.c`
AL2009man Mar 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions port/include/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,19 @@ void inputMouseGetRawDelta(s32 *dx, s32 *dy);
// returns 0, 0 when the mouse is not locked into the window
void inputMouseGetScaledDelta(f32 *dx, f32 *dy);

// returns changes in mouse position since last frame, scaled by sensitivity and crosshair size
void inputMouseGetScaledDeltaCrosshair(f32* dx, f32* dy);

// returns changes in mouse position since last frame, scaled by absolute sensitivity
// 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);

void inputMouseGetAimSpeed(f32* x, f32* y);
void inputMouseSetAimSpeed(f32 x, f32 y);

s32 inputMouseIsEnabled(void);
void inputMouseEnable(s32 enabled);

Expand Down
28 changes: 26 additions & 2 deletions port/src/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "utils.h"
#include "system.h"
#include "fs.h"
#include "data.h"

#if !SDL_VERSION_ATLEAST(2, 0, 14)
// this was added in 2.0.14
Expand Down Expand Up @@ -1247,8 +1248,8 @@ void inputMouseGetScaledDelta(f32* dx, f32* dy)
{
f32 mdx = 0.f, mdy = 0.f;
if (mouseLocked) {
mdx = mouseDX * (0.022f / 3.5f) * mouseSensX;
mdy = mouseDY * (0.022f / 3.5f) * mouseSensY;
mdx = mouseDX;
mdy = mouseDY;
}
if (dx) *dx = mdx;
if (dy) *dy = mdy;
Expand Down Expand Up @@ -1277,6 +1278,29 @@ void inputMouseSetSpeed(f32 x, f32 y)
mouseSensY = y;
}

void inputMouseGetScaledDeltaCrosshair(f32* dx, f32* dy)
{
f32 mdx = 0.f, mdy = 0.f;
if (mouseLocked) {
mdx = mouseDX * (0.022f / 90.0f) * g_PlayerExtCfg[0].mouseaimsensx;
mdy = mouseDY * (0.022f / 90.0f) * g_PlayerExtCfg[0].mouseaimsensy;
}
if (dx) *dx = mdx;
if (dy) *dy = mdy;
}

void inputMouseGetAimSpeed(f32* x, f32* y)
{
if (x) *x = g_PlayerExtCfg[0].mouseaimsensx;
if (y) *y = g_PlayerExtCfg[0].mouseaimsensy;
}

void inputMouseSetAimSpeed(f32 x, f32 y)
{
g_PlayerExtCfg[0].mouseaimsensx = x;
g_PlayerExtCfg[0].mouseaimsensy = y;
}

s32 inputMouseIsEnabled(void)
{
return mouseEnabled;
Expand Down
4 changes: 2 additions & 2 deletions port/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ PD_CONSTRUCTOR static void gameConfigInit(void)
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.MouseAimSensX", i), &g_PlayerExtCfg[j].mouseaimsensx, 0.f, 10.f);
configRegisterFloat(strFmt("Game.Player%d.MouseAimSensY", i), &g_PlayerExtCfg[j].mouseaimsensy, 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.CrouchMode", i), &g_PlayerExtCfg[j].crouchmode, 0, CROUCHMODE_TOGGLE_ANALOG);
Expand Down
26 changes: 16 additions & 10 deletions port/src/optionsmenu.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,45 +213,51 @@ static MenuItemHandlerResult menuhandlerMouseSpeedY(s32 operation, struct menuit

static MenuItemHandlerResult menuhandlerMouseAimSpeedX(s32 operation, struct menuitem *item, union handlerdata *data)
{
f32 x, y;
switch (operation) {
case MENUOP_GETSLIDER:
if (g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedx < 0.f) {
inputMouseGetAimSpeed(&x, &y);
if (x < 0.f) {
data->slider.value = 0;
} else if (g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedx > 10.f) {
} else if (x > 10.f) {
data->slider.value = 1000;
} else {
data->slider.value = g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedx * 100.f + 0.5f;
data->slider.value = x * 100.f + 0.5f;
}
break;
case MENUOP_SET:
g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedx = (f32)data->slider.value / 100.f;
inputMouseGetAimSpeed(&x, &y);
inputMouseSetAimSpeed((f32)data->slider.value / 100.f, y);
break;
case MENUOP_GETSLIDERLABEL:
sprintf(data->slider.label, "%.2f", (f32)data->slider.value / 100.f);
break;
}

return 0;
}

static MenuItemHandlerResult menuhandlerMouseAimSpeedY(s32 operation, struct menuitem *item, union handlerdata *data)
{
f32 x, y;
switch (operation) {
case MENUOP_GETSLIDER:
if (g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedy < 0.f) {
inputMouseGetAimSpeed(&x, &y);
if (y < 0.f) {
data->slider.value = 0;
} else if (g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedy > 10.f) {
} else if (y > 10.f) {
data->slider.value = 1000;
} else {
data->slider.value = g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedy * 100.f + 0.5f;
data->slider.value = y * 100.f + 0.5f;
}
break;
case MENUOP_SET:
g_PlayerExtCfg[g_ExtMenuPlayer].mouseaimspeedy = (f32)data->slider.value / 100.f;
inputMouseGetAimSpeed(&x, &y);
inputMouseSetAimSpeed(x, (f32)data->slider.value / 100.f);
break;
case MENUOP_GETSLIDERLABEL:
sprintf(data->slider.label, "%.2f", (f32)data->slider.value / 100.f);
break;
}

return 0;
}

Expand Down
187 changes: 160 additions & 27 deletions src/game/bondmove.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,141 @@ void bmoveUpdateSpeedThetaControl(f32 value)
}
}

/**
* Apply camera movement with scaling based on input type
* Supports angle-based movement (mouse) and delta-based movement (analog stick)
*/
static void bmoveApplyCameraMovement(struct movedata *data, f32 mlookscale, f32 *pitchValue, f32 *turnValue)
{
#ifndef PLATFORM_N64
// Check for input types
bool mouseActive = (data->freelookdx != 0.0f || data->freelookdy != 0.0f);

// Use angle-based camera system for mouse
if (mouseActive) {
f32 mouseSensX, mouseSensY;
inputMouseGetSpeed(&mouseSensX, &mouseSensY);

// horizontal movement
if (data->freelookdx != 0.0f) {
// Mouse scaling is derived from id Tech 2/Quake Engine's mouse multiplier (0.022)
g_Vars.currentplayer->vv_theta += data->freelookdx * 0.022f * mouseSensX;

// Normalize theta to 0-360 degrees
while (g_Vars.currentplayer->vv_theta < 0) {
g_Vars.currentplayer->vv_theta += 360.0f;
}
while (g_Vars.currentplayer->vv_theta >= 360.0f) {
g_Vars.currentplayer->vv_theta -= 360.0f;
}
}

// vertical movement
if (data->freelookdy != 0.0f) {
g_Vars.currentplayer->vv_verta -= data->freelookdy * 0.022f * mouseSensY;

// Clamp pitch to prevent over-rotation
if (g_Vars.currentplayer->vv_verta > 90.0f) {
g_Vars.currentplayer->vv_verta = 90.0f;
} else if (g_Vars.currentplayer->vv_verta < -90.0f) {
g_Vars.currentplayer->vv_verta = -90.0f;
}
}
} else {
// Delta-based movement (analog stick)
f32 mouseSensX, mouseSensY;
inputMouseGetSpeed(&mouseSensX, &mouseSensY);

if (turnValue && data->freelookdx != 0.0f) {
*turnValue += data->freelookdx * mouseSensX * mlookscale;
}
if (pitchValue && data->freelookdy != 0.0f) {
*pitchValue += data->freelookdy * mouseSensY * mlookscale;
}
}
#else
// N64 platform - only delta-based movement (analog stick)
f32 mouseSensX, mouseSensY;
inputMouseGetSpeed(&mouseSensX, &mouseSensY);

if (turnValue && data->freelookdx != 0.0f) {
*turnValue += data->freelookdx * mouseSensX * mlookscale;
}
if (pitchValue && data->freelookdy != 0.0f) {
*pitchValue += data->freelookdy * mouseSensY * mlookscale;
}
#endif
}

/**
* Apply crosshair swivel based on camera movement with precision input detection
*/
static void bmoveApplyCrosshairSwivel(struct movedata *movedata, f32 mlookscale, f32 *x, f32 *y)
{
f32 mouseSwivelX, mouseSwivelY;
f32 xscale, yscale;
f32 effective_speedtheta, effective_speedverta;
f32 fov_factor;

// Get input sensitivity for proper detection
inputMouseGetSpeed(&mouseSwivelX, &mouseSwivelY);

// Crosshair sway scaling for mouse and classic joystick input
int mouse_active = (movedata->freelookdx && mouseSwivelX > 0.0f) || (movedata->freelookdy && mouseSwivelY > 0.0f);
int joystick_active = (movedata->c1stickxraw != 0 || movedata->c1stickyraw != 0);

if (mouse_active && joystick_active) {
// Mouse + joystick sway
xscale = PLAYER_EXTCFG().crosshairsway * 0.80f; // 80% for precision+joystick sway
yscale = PLAYER_EXTCFG().crosshairsway * 0.80f; // 80% for precision+joystick sway
} else if (mouse_active) {
// Mouse sway
xscale = PLAYER_EXTCFG().crosshairsway * 0.20f; // 20% for mouse sway
yscale = PLAYER_EXTCFG().crosshairsway * 0.20f; // 20% for mouse sway
} else {
// Joystick only or no input - full sway
xscale = yscale = PLAYER_EXTCFG().crosshairsway;
}

// Calculate effective speed values including mouse input
if (mouse_active) {
fov_factor = viGetFovY() / PLAYER_DEFAULT_FOV;
effective_speedtheta = g_Vars.currentplayer->speedtheta +
(movedata->freelookdx && mouseSwivelX > 0.0f ? movedata->freelookdx * mlookscale * fov_factor : 0.0f);
effective_speedverta = g_Vars.currentplayer->speedverta -
(movedata->freelookdy && mouseSwivelY > 0.0f ? movedata->freelookdy * mlookscale * fov_factor : 0.0f);
} else {
// Analog stick only: use existing speed values
effective_speedtheta = g_Vars.currentplayer->speedtheta;
effective_speedverta = g_Vars.currentplayer->speedverta;
}

// Joystick x/y scaling
*x = effective_speedtheta * 0.3f * xscale + g_Vars.currentplayer->gunextraaimx;
*y = -effective_speedverta * 0.1f * yscale + g_Vars.currentplayer->gunextraaimy;
}

/**
* Apply crosshair movement with scaling and clamping
*/
static void bmoveApplyCrosshairAimingMovement(f32 aimspeedx, f32 aimspeedy, f32 dx, f32 dy)
{
// Resolution/Aspect Ratio-based scaling coefficients
const f32 xscale = (aimspeedx * 320.f / 1080.f) / g_Vars.currentplayer->aspect;
const f32 yscale = aimspeedy * 240.f / 1080.f;

// Calculate new positions with input directly
const f32 x = g_Vars.currentplayer->swivelpos[0] + (dx * xscale);
const f32 y = g_Vars.currentplayer->swivelpos[1] + (dy * yscale);

// Clamping
g_Vars.currentplayer->swivelpos[0] = (x < -1.f) ? -1.f : (x > 1.f) ? 1.f : x;
g_Vars.currentplayer->swivelpos[1] = (y < -1.f) ? -1.f : (y > 1.f) ? 1.f : y;

// Applying to gun swivel system
bgunSwivelWithDamp(g_Vars.currentplayer->swivelpos[0], g_Vars.currentplayer->swivelpos[1], 0.01f);
}

/**
* Calculate the lookahead angle.
*
Expand Down Expand Up @@ -712,7 +847,7 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
f32 increment2;
f32 newverta;
#ifndef PLATFORM_N64
const f32 mlookscale = g_Vars.lvupdate240 ? (4.f / (f32)g_Vars.lvupdate240) : 4.f;
const f32 mlookscale = g_Vars.lvupdate240 ? (0.1f / (f32)g_Vars.lvupdate240) : 0.1f;
const bool allowmlook = (g_Vars.currentplayernum == 0) && (allowc1x || allowc1y);
bool allowmcross = false;
#endif
Expand Down Expand Up @@ -2048,7 +2183,13 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
}

#ifndef PLATFORM_N64
fVar25 += movedata.freelookdy * mlookscale;
// Handle mouse input directly via centralized system (not analog stick)
if (movedata.freelookdy != 0.0f) {
// Use temporary movedata for centralized camera movement (mouse only)
struct movedata tempMoveData = {0};
tempMoveData.freelookdy = movedata.freelookdy;
bmoveApplyCameraMovement(&tempMoveData, mlookscale, NULL, NULL);
}
#endif

g_Vars.currentplayer->speedverta = -fVar25 * tmp;
Expand Down Expand Up @@ -2089,7 +2230,13 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
}

#ifndef PLATFORM_N64
fVar25 += movedata.freelookdx * mlookscale;
// Handle mouse input directly via centralized system (not analog stick)
if (movedata.freelookdx != 0.0f) {
// Use temporary movedata for centralized camera movement (mouse only)
struct movedata tempMoveData = {0};
tempMoveData.freelookdx = movedata.freelookdx;
bmoveApplyCameraMovement(&tempMoveData, mlookscale, NULL, NULL);
}
#endif

g_Vars.currentplayer->speedthetacontrol = fVar25 * tmp;
Expand Down Expand Up @@ -2186,15 +2333,8 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
x = g_Vars.currentplayer->speedtheta * 0.3f + g_Vars.currentplayer->gunextraaimx;
y = -g_Vars.currentplayer->speedverta * 0.1f + g_Vars.currentplayer->gunextraaimy;
#else
f32 xscale, yscale;
if (movedata.freelookdx || movedata.freelookdy) {
xscale = PLAYER_EXTCFG().crosshairsway * 0.20f;
yscale = PLAYER_EXTCFG().crosshairsway * 0.30f;
} else {
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;
// Crosshair swivel movement system
bmoveApplyCrosshairSwivel(&movedata, mlookscale, &x, &y);
#endif

bgunSwivelWithDamp(x, y, PAL ? 0.955f : 0.963f);
Expand All @@ -2204,21 +2344,14 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i
// when holding aim and moving stick
bgunSetAimType(0);
#ifndef PLATFORM_N64
if (allowmcross) {
// joystick is inactive, move crosshair using the mouse
const f32 xcoeff = 320.f / 1080.f;
const f32 ycoeff = 240.f / 1080.f;
const f32 xscale = (PLAYER_EXTCFG().mouseaimspeedx * xcoeff) / g_Vars.currentplayer->aspect;
const f32 yscale = PLAYER_EXTCFG().mouseaimspeedy * ycoeff;
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);
y = (y < -1.f) ? -1.f : ((y > 1.f) ? 1.f : y);
g_Vars.currentplayer->swivelpos[0] = x;
g_Vars.currentplayer->swivelpos[1] = y;
bgunSwivelWithDamp(x, y, 0.01f);
return;
}
if (allowmcross) {
// joystick is inactive, move crosshair using the mouse
f32 dx, dy;
inputMouseGetScaledDeltaCrosshair(&dx, &dy);
const f32 norm = g_Vars.lvupdate60freal;
bmoveApplyCrosshairAimingMovement(PLAYER_EXTCFG().mouseaimsensx, PLAYER_EXTCFG().mouseaimsensy, dx, dy);
return;
}
#endif
bgunSwivelWithoutDamp((movedata.c1stickxraw * 0.65f) / 80.0f, (movedata.c1stickyraw * 0.65f) / 80.0f);
}
Expand Down
4 changes: 2 additions & 2 deletions src/game/mplayer/mplayer.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ struct mpweapon g_MpWeapons[NUM_MPWEAPONS] = {
.fovzoommult = 1.f, \
.fovzoom = true, \
.mouseaimmode = MOUSEAIM_CLASSIC, \
.mouseaimspeedx = 0.7f, \
.mouseaimspeedy = 0.7f, \
.mouseaimsensx = 5.0f, \
.mouseaimsensy = 5.0f, \
.radialmenuspeed = 4.f, \
.crosshairsway = 1.f, \
.crouchmode = CROUCHMODE_TOGGLE_ANALOG, \
Expand Down
4 changes: 2 additions & 2 deletions src/include/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -6150,8 +6150,8 @@ struct extplayerconfig {
f32 fovzoommult;
s32 fovzoom;
s32 mouseaimmode;
f32 mouseaimspeedx;
f32 mouseaimspeedy;
f32 mouseaimsensx;
f32 mouseaimsensy;
s32 crouchmode;
f32 radialmenuspeed;
f32 crosshairsway;
Expand Down
Loading