Tie player input to the frame rate instead of the game tickrate

This voodoo bullshit entirely eliminates any and all player viewport input latency.

git-svn-id: https://svn.eduke32.com/eduke32@8551 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
terminx 2020-01-29 11:36:45 +00:00 committed by Christoph Oelckers
parent de2c07aa6a
commit 3b43bba32f
2 changed files with 171 additions and 138 deletions

View file

@ -814,11 +814,8 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
pPlayer->opos.z + mulscale16(pPlayer->pos.z - pPlayer->opos.z, smoothRatio) };
CAMERA(pos) = camVect;
CAMERA(q16ang) = pPlayer->oq16ang
+ mulscale16(((pPlayer->q16ang + F16(1024) - pPlayer->oq16ang) & 0x7FFFFFF) - F16(1024), smoothRatio)
+ fix16_from_int(pPlayer->look_ang);
CAMERA(q16horiz) = pPlayer->oq16horiz + pPlayer->oq16horizoff
+ mulscale16((pPlayer->q16horiz + pPlayer->q16horizoff - pPlayer->oq16horiz - pPlayer->oq16horizoff), smoothRatio);
CAMERA(q16ang) = pPlayer->q16ang + fix16_from_int(pPlayer->look_ang);
CAMERA(q16horiz) = pPlayer->q16horiz + pPlayer->q16horizoff;
if (cl_viewbob)
{
@ -5927,7 +5924,18 @@ MAIN_LOOP_RESTART:
frameJustDrawn = false;
P_GetInput(myconnectindex);
inputfifo[0][myconnectindex] = localInput;
// this is where we fill the input_t struct that is actually processed by P_ProcessInput()
auto const pPlayer = g_player[myconnectindex].ps;
int16_t const q16ang = fix16_to_int(pPlayer->q16ang);
auto & input = inputfifo[0][myconnectindex];
input = localInput;
input.fvel = mulscale9(localInput.fvel, sintable[(q16ang + 2560) & 2047])
+ mulscale9(localInput.svel, sintable[(q16ang + 2048) & 2047]) + (FURY ? 0 : pPlayer->fric.x);
input.svel = mulscale9(localInput.fvel, sintable[(q16ang + 2048) & 2047])
+ mulscale9(localInput.svel, sintable[(q16ang + 1536) & 2047]) + (FURY ? 0 : pPlayer->fric.y);
localInput = {};
}
do
@ -5981,6 +5989,11 @@ MAIN_LOOP_RESTART:
}
else if (G_FPSLimit() || g_saveRequested)
{
if (!g_saveRequested)
{
P_GetInput(myconnectindex);
}
int const smoothRatio = calc_smoothratio(totalclock, ototalclock);
G_DrawRooms(screenpeek, smoothRatio);

View file

@ -2887,18 +2887,22 @@ enddisplayweapon:
#define MAXVEL ((NORMALKEYMOVE*2)+10)
#define MAXSVEL ((NORMALKEYMOVE*2)+10)
#define MAXANGVEL 1024
#define MAXHORIZ 256
#define MAXHORIZVEL 256
int32_t mouseyaxismode = -1;
static int P_CheckLockedMovement(int const playerNum)
{
auto const pPlayer = g_player[playerNum].ps;
return (pPlayer->fist_incs || pPlayer->transporter_hold > 2 || pPlayer->hard_landing || pPlayer->access_incs > 0 || pPlayer->knee_incs > 0
return (pPlayer->dead_flag || pPlayer->fist_incs || pPlayer->transporter_hold > 2 || pPlayer->hard_landing || pPlayer->access_incs > 0 || pPlayer->knee_incs > 0
|| (PWEAPON(playerNum, pPlayer->curr_weapon, WorksLike) == TRIPBOMB_WEAPON && pPlayer->kickback_pic > 1
&& pPlayer->kickback_pic < PWEAPON(playerNum, pPlayer->curr_weapon, FireDelay)));
}
static bool g_horizRecenter;
static float g_horizAngleAdjust;
static fix16_t g_horizSkew;
void P_GetInput(int const playerNum)
{
auto const pPlayer = g_player[playerNum].ps;
@ -2909,8 +2913,7 @@ void P_GetInput(int const playerNum)
if (!(pPlayer->gm&MODE_MENU))
CONTROL_GetInput(&info);
Bmemset(&localInput, 0, sizeof(input_t));
localInput = {};
localInput.bits = (((int32_t)g_gameQuit) << SK_GAMEQUIT);
localInput.extbits |= (1<<7);
@ -2934,9 +2937,9 @@ void P_GetInput(int const playerNum)
// JBF: Run key behaviour is selectable
int const playerRunning = G_CheckAutorun(buttonMap.ButtonDown(gamefunc_Run));
int const turnAmount = playerRunning ? (NORMALTURN << 1) : NORMALTURN;
constexpr int const analogTurnAmount = (NORMALTURN << 1);
constexpr int analogTurnAmount = (NORMALTURN << 1);
int const keyMove = playerRunning ? (NORMALKEYMOVE << 1) : NORMALKEYMOVE;
constexpr int const analogExtent = 32767; // KEEPINSYNC sdlayer.cpp
constexpr int analogExtent = 32767; // KEEPINSYNC sdlayer.cpp
input_t input {};
@ -2951,12 +2954,12 @@ void P_GetInput(int const playerNum)
}
else
{
input.q16avel = fix16_div(fix16_from_int(info.mousex), F16(32));
input.q16avel += fix16_div(fix16_from_int(info.mousex), F16(32));
input.q16avel += fix16_from_int(info.dyaw) / analogExtent * (analogTurnAmount << 1);
}
if (mouseaim)
input.q16horz = fix16_div(fix16_from_int(info.mousey), F16(64));
input.q16horz += fix16_div(fix16_from_int(info.mousey), F16(64));
else
input.fvel = -(info.mousey >> 3);
@ -2966,18 +2969,29 @@ void P_GetInput(int const playerNum)
input.svel -= info.dx * keyMove / analogExtent;
input.fvel -= info.dz * keyMove / analogExtent;
static double lastInputTicks;
auto const currentHiTicks = timerGetHiTicks();
double const elapsedInputTicks = currentHiTicks - lastInputTicks;
lastInputTicks = currentHiTicks;
auto scaleAdjustmentToInterval = [=](double x) { return x * REALGAMETICSPERSEC / (1000.0 / elapsedInputTicks); };
if (buttonMap.ButtonDown(gamefunc_Strafe))
{
if (buttonMap.ButtonDown(gamefunc_Turn_Left) && !(pPlayer->movement_lock&4))
input.svel -= -keyMove;
if (!localInput.svel)
{
if (buttonMap.ButtonDown(gamefunc_Turn_Left) && !(pPlayer->movement_lock & 4) && !localInput.svel)
input.svel = -keyMove;
if (buttonMap.ButtonDown(gamefunc_Turn_Right) && !(pPlayer->movement_lock&8))
input.svel -= keyMove;
if (buttonMap.ButtonDown(gamefunc_Turn_Right) && !(pPlayer->movement_lock & 8) && !localInput.svel)
input.svel = keyMove;
}
}
else
{
static int32_t turnHeldTime = 0;
static int32_t lastInputClock = 0; // MED
static int32_t turnHeldTime;
static int32_t lastInputClock; // MED
int32_t const elapsedTics = (int32_t)totalclock - lastInputClock;
lastInputClock = (int32_t) totalclock;
@ -2985,34 +2999,34 @@ void P_GetInput(int const playerNum)
if (buttonMap.ButtonDown(gamefunc_Turn_Left))
{
turnHeldTime += elapsedTics;
input.q16avel -= fix16_from_int((turnHeldTime >= TURBOTURNTIME) ? (turnAmount << 1) : (PREAMBLETURN << 1));
input.q16avel -= fix16_from_float(scaleAdjustmentToInterval((turnHeldTime >= TURBOTURNTIME) ? (turnAmount << 1) : (PREAMBLETURN << 1)));
}
else if (buttonMap.ButtonDown(gamefunc_Turn_Right))
{
turnHeldTime += elapsedTics;
input.q16avel += fix16_from_int((turnHeldTime >= TURBOTURNTIME) ? (turnAmount << 1) : (PREAMBLETURN << 1));
input.q16avel += fix16_from_float(scaleAdjustmentToInterval((turnHeldTime >= TURBOTURNTIME) ? (turnAmount << 1) : (PREAMBLETURN << 1)));
}
else
turnHeldTime = 0;
}
if (localInput.svel < keyMove && localInput.svel > -keyMove)
{
if (buttonMap.ButtonDown(gamefunc_Strafe_Left) && !(pPlayer->movement_lock & 4))
input.svel += keyMove;
if (buttonMap.ButtonDown(gamefunc_Strafe_Right) && !(pPlayer->movement_lock & 8))
input.svel += -keyMove;
}
if (localInput.fvel < keyMove && localInput.fvel > -keyMove)
{
if (buttonMap.ButtonDown(gamefunc_Move_Forward) && !(pPlayer->movement_lock & 1))
input.fvel += keyMove;
if (buttonMap.ButtonDown(gamefunc_Move_Backward) && !(pPlayer->movement_lock & 2))
input.fvel += -keyMove;
input.fvel = clamp(input.fvel, -MAXVEL, MAXVEL);
input.svel = clamp(input.svel, -MAXSVEL, MAXSVEL);
input.q16avel = fix16_clamp(input.q16avel, F16(-MAXANGVEL), F16(MAXANGVEL));
input.q16horz = fix16_clamp(input.q16horz, F16(-MAXHORIZ), F16(MAXHORIZ));
}
// Ion Fury does not use the tenth slot and misbehaves if it gets selected.
int weaponSelection = FURY? gamefunc_Weapon_9 : gamefunc_Weapon_10;
@ -3037,7 +3051,10 @@ void P_GetInput(int const playerNum)
else if (weaponSelection == gamefunc_Weapon_1-1)
weaponSelection = 0;
localInput.bits = (weaponSelection << SK_WEAPON_BITS) | (buttonMap.ButtonDown(gamefunc_Fire) << SK_FIRE);
if ((localInput.bits & 0xf00) == 0)
localInput.bits |= (weaponSelection << SK_WEAPON_BITS);
localInput.bits |= (buttonMap.ButtonDown(gamefunc_Fire) << SK_FIRE);
localInput.bits |= (buttonMap.ButtonDown(gamefunc_Open) << SK_OPEN);
int const sectorLotag = pPlayer->cursectnum != -1 ? sector[pPlayer->cursectnum].lotag : 0;
@ -3099,7 +3116,7 @@ void P_GetInput(int const playerNum)
if (PWEAPON(playerNum, pPlayer->curr_weapon, Flags) & WEAPON_SEMIAUTO && buttonMap.ButtonDown(gamefunc_Fire))
buttonMap.ClearButton(gamefunc_Fire);
localInput.extbits = (buttonMap.ButtonDown(gamefunc_Move_Forward) || (input.fvel > 0));
localInput.extbits |= (buttonMap.ButtonDown(gamefunc_Move_Forward) || (input.fvel > 0));
localInput.extbits |= (buttonMap.ButtonDown(gamefunc_Move_Backward) || (input.fvel < 0)) << 1;
localInput.extbits |= (buttonMap.ButtonDown(gamefunc_Strafe_Left) || (input.svel > 0)) << 2;
localInput.extbits |= (buttonMap.ButtonDown(gamefunc_Strafe_Right) || (input.svel < 0)) << 3;
@ -3107,32 +3124,89 @@ void P_GetInput(int const playerNum)
localInput.extbits |= buttonMap.ButtonDown(gamefunc_Turn_Right)<<5;
localInput.extbits |= buttonMap.ButtonDown(gamefunc_Alt_Fire)<<6;
if ((ud.scrollmode && ud.overhead_on) || P_CheckLockedMovement(playerNum))
{
if (ud.scrollmode && ud.overhead_on)
{
ud.folfvel = input.fvel;
ud.folavel = fix16_to_int(input.q16avel);
}
localInput.fvel = 0;
localInput.svel = 0;
localInput.q16avel = 0;
localInput.q16horz = 0;
}
else
{
localInput.q16avel = fix16_add(localInput.q16avel, input.q16avel);
localInput.q16horz = fix16_clamp(fix16_add(localInput.q16horz, input.q16horz), F16(-MAXHORIZVEL), F16(MAXHORIZVEL));
localInput.fvel = clamp(localInput.fvel + input.fvel, -MAXVEL, MAXVEL);
localInput.svel = clamp(localInput.svel + input.svel, -MAXSVEL, MAXSVEL);
return;
pPlayer->q16ang = fix16_add(pPlayer->q16ang, input.q16avel);
pPlayer->q16ang &= 0x7FFFFFF;
pPlayer->q16horiz = fix16_clamp(fix16_add(pPlayer->q16horiz, input.q16horz), F16(HORIZ_MIN), F16(HORIZ_MAX));
}
int16_t const q16ang = fix16_to_int(pPlayer->q16ang);
// A horiz diff of 128 equal 45 degrees,
// so we convert horiz to 1024 angle units
localInput.fvel = mulscale9(input.fvel, sintable[(q16ang + 2560) & 2047]) +
mulscale9(input.svel, sintable[(q16ang + 2048) & 2047]) +
(FURY ? 0 : pPlayer->fric.x);
if (g_horizAngleAdjust)
{
float const horizAngle
= atan2f(pPlayer->q16horiz - F16(100), F16(128)) * (512.f / fPI) + scaleAdjustmentToInterval(g_horizAngleAdjust);
pPlayer->q16horiz = F16(100) + Blrintf(F16(128) * tanf(horizAngle * (fPI / 512.f)));
}
else if (pPlayer->return_to_center > 0 || g_horizRecenter)
{
pPlayer->q16horiz += fix16_from_float(scaleAdjustmentToInterval(fix16_to_float(F16(33) - fix16_div(pPlayer->q16horiz, F16(3)))));
localInput.svel = mulscale9(input.fvel, sintable[(q16ang + 2048) & 2047]) +
mulscale9(input.svel, sintable[(q16ang + 1536) & 2047]) +
(FURY ? 0 : pPlayer->fric.y);
if (pPlayer->q16horiz >= F16(99.9) && pPlayer->q16horiz <= F16(100.1))
{
pPlayer->q16horiz = F16(100);
g_horizRecenter = 0;
}
localInput.q16avel = input.q16avel;
localInput.q16horz = input.q16horz;
if (pPlayer->q16horizoff >= F16(-0.1) && pPlayer->q16horizoff <= F16(0.1))
pPlayer->q16horizoff = 0;
}
// calculates automatic view angle for playing without a mouse
if (!pPlayer->aim_mode && pPlayer->on_ground && sectorLotag != ST_2_UNDERWATER && (sector[pPlayer->cursectnum].floorstat & 2))
{
// this is some kind of horse shit approximation of where the player is looking, I guess?
vec2_t const adjustedPosition = { pPlayer->pos.x + (sintable[(fix16_to_int(pPlayer->q16ang) + 512) & 2047] >> 5),
pPlayer->pos.y + (sintable[fix16_to_int(pPlayer->q16ang) & 2047] >> 5) };
int16_t currentSector = pPlayer->cursectnum;
updatesector(adjustedPosition.x, adjustedPosition.y, &currentSector);
if (currentSector >= 0)
{
int const slopeZ = getflorzofslope(pPlayer->cursectnum, adjustedPosition.x, adjustedPosition.y);
if ((pPlayer->cursectnum == currentSector) || (klabs(getflorzofslope(currentSector, adjustedPosition.x, adjustedPosition.y) - slopeZ) <= ZOFFSET6))
pPlayer->q16horizoff += fix16_from_float(scaleAdjustmentToInterval(mulscale16(pPlayer->truefz - slopeZ, 160)));
}
}
if (pPlayer->q16horizoff > 0)
{
pPlayer->q16horizoff -= fix16_from_float(scaleAdjustmentToInterval(fix16_to_float((pPlayer->q16horizoff >> 3) + fix16_one)));
pPlayer->q16horizoff = fix16_max(pPlayer->q16horizoff, 0);
}
else if (pPlayer->q16horizoff < 0)
{
pPlayer->q16horizoff += fix16_from_float(scaleAdjustmentToInterval(fix16_to_float((-pPlayer->q16horizoff >> 3) + fix16_one)));
pPlayer->q16horizoff = fix16_min(pPlayer->q16horizoff, 0);
}
if (g_horizSkew)
pPlayer->q16horiz += fix16_from_float(scaleAdjustmentToInterval(fix16_to_float(g_horizSkew)));
pPlayer->q16horiz = fix16_clamp(pPlayer->q16horiz, F16(HORIZ_MIN), F16(HORIZ_MAX));
}
static int32_t P_DoCounters(int playerNum)
@ -4639,6 +4713,9 @@ static void P_ClampZ(DukePlayer_t* const pPlayer, int const sectorLotag, int32_t
void P_ProcessInput(int playerNum)
{
g_horizAngleAdjust = 0;
g_horizSkew = 0;
if (g_player[playerNum].playerquitflag == 0)
return;
@ -4726,36 +4803,6 @@ void P_ProcessInput(int playerNum)
pPlayer->oq16horiz = pPlayer->q16horiz;
pPlayer->oq16horizoff = pPlayer->q16horizoff;
// calculates automatic view angle for playing without a mouse
if (pPlayer->aim_mode == 0 && pPlayer->on_ground && sectorLotag != ST_2_UNDERWATER
&& (sector[pPlayer->cursectnum].floorstat & 2))
{
vec2_t const adjustedPlayer = { pPlayer->pos.x + (sintable[(fix16_to_int(pPlayer->q16ang) + 512) & 2047] >> 5),
pPlayer->pos.y + (sintable[fix16_to_int(pPlayer->q16ang) & 2047] >> 5) };
int16_t curSectNum = pPlayer->cursectnum;
updatesector(adjustedPlayer.x, adjustedPlayer.y, &curSectNum);
if (curSectNum >= 0)
{
int const slopeZ = getflorzofslope(pPlayer->cursectnum, adjustedPlayer.x, adjustedPlayer.y);
if ((pPlayer->cursectnum == curSectNum) ||
(klabs(getflorzofslope(curSectNum, adjustedPlayer.x, adjustedPlayer.y) - slopeZ) <= ZOFFSET6))
pPlayer->q16horizoff += fix16_from_int(mulscale16(trueFloorZ - slopeZ, 160));
}
}
if (pPlayer->q16horizoff > 0)
{
pPlayer->q16horizoff -= ((pPlayer->q16horizoff >> 3) + fix16_one);
pPlayer->q16horizoff = max(pPlayer->q16horizoff, 0);
}
else if (pPlayer->q16horizoff < 0)
{
pPlayer->q16horizoff += (((-pPlayer->q16horizoff) >> 3) + fix16_one);
pPlayer->q16horizoff = min(pPlayer->q16horizoff, 0);
}
if ((highZhit & 49152) == 49152)
{
int const spriteNum = highZhit & (MAXSPRITES-1);
@ -5086,7 +5133,9 @@ void P_ProcessInput(int playerNum)
if ((sectorLotag != ST_1_ABOVE_WATER && sectorLotag != ST_2_UNDERWATER) &&
(pPlayer->on_ground == 0 && pPlayer->vel.z > (ACTOR_MAXFALLINGZVEL >> 1)))
{
pPlayer->hard_landing = pPlayer->vel.z >> 10;
}
pPlayer->on_ground = 1;
@ -5199,16 +5248,8 @@ void P_ProcessInput(int playerNum)
pPlayer->vel.x = 0;
pPlayer->vel.y = 0;
}
else if (g_player[playerNum].input->q16avel) //p->ang += syncangvel * constant
{
fix16_t const inputAng = g_player[playerNum].input->q16avel;
pPlayer->q16angvel = (sectorLotag == ST_2_UNDERWATER) ? fix16_mul(inputAng - (inputAng >> 3), fix16_from_int(ksgn(velocityModifier)))
: fix16_mul(inputAng, fix16_from_int(ksgn(velocityModifier)));
pPlayer->q16ang += pPlayer->q16angvel;
pPlayer->q16ang &= 0x7FFFFFF;
else if (g_player[playerNum].input->q16avel)
pPlayer->crack_time = PCRACKTIME;
}
if (pPlayer->spritebridge == 0)
{
@ -5475,24 +5516,22 @@ RECHECK:
G_ActivateBySector(pPlayer->cursectnum, pPlayer->i);
}
int centerHoriz = 0;
if (pPlayer->return_to_center > 0)
{
pPlayer->return_to_center--;
g_horizRecenter = true;
}
if (TEST_SYNC_KEY(playerBits, SK_CENTER_VIEW) || pPlayer->hard_landing)
if (VM_OnEvent(EVENT_RETURNTOCENTER, pPlayer->i,playerNum) == 0)
pPlayer->return_to_center = 9;
// A horiz diff of 128 equal 45 degrees,
// so we convert horiz to 1024 angle units
float horizAngle = atan2f(pPlayer->q16horiz - F16(100), F16(128)) * (512.f / fPI) + fix16_to_float(g_player[playerNum].input->q16horz);
if (TEST_SYNC_KEY(playerBits, SK_LOOK_UP))
{
if (VM_OnEvent(EVENT_LOOKUP,pPlayer->i,playerNum) == 0)
{
pPlayer->return_to_center = 9;
horizAngle += float(12<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN)));
centerHoriz++;
g_horizAngleAdjust = float(12<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN)));
}
}
@ -5501,8 +5540,7 @@ RECHECK:
if (VM_OnEvent(EVENT_LOOKDOWN,pPlayer->i,playerNum) == 0)
{
pPlayer->return_to_center = 9;
horizAngle -= float(12<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN)));
centerHoriz++;
g_horizAngleAdjust = -float(12<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN)));
}
}
@ -5510,8 +5548,8 @@ RECHECK:
{
if (VM_OnEvent(EVENT_AIMUP,pPlayer->i,playerNum) == 0)
{
horizAngle += float(6<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN)));
centerHoriz++;
g_horizAngleAdjust = float(6 << (int)(TEST_SYNC_KEY(playerBits, SK_RUN)));
g_horizRecenter = false;
}
}
@ -5519,35 +5557,17 @@ RECHECK:
{
if (VM_OnEvent(EVENT_AIMDOWN,pPlayer->i,playerNum) == 0)
{
horizAngle -= float(6<<(int)(TEST_SYNC_KEY(playerBits, SK_RUN)));
centerHoriz++;
g_horizAngleAdjust = -float(6 << (int)(TEST_SYNC_KEY(playerBits, SK_RUN)));
g_horizRecenter = false;
}
}
horizAngle = clamp(horizAngle, -255.f, 255.f); // keep the angle within ]-90°..90°[
pPlayer->q16horiz = F16(100) + Blrintf(F16(128) * tanf(horizAngle * (fPI / 512.f)));
if (pPlayer->return_to_center > 0 && !TEST_SYNC_KEY(playerBits, SK_LOOK_UP) && !TEST_SYNC_KEY(playerBits, SK_LOOK_DOWN))
{
pPlayer->return_to_center--;
pPlayer->q16horiz += F16(33)-fix16_div(pPlayer->q16horiz, F16(3));
centerHoriz++;
}
if (pPlayer->hard_landing > 0)
{
g_horizSkew = fix16_from_int(-(pPlayer->hard_landing << 4));
pPlayer->hard_landing--;
pPlayer->q16horiz -= fix16_from_int(pPlayer->hard_landing<<4);
}
if (centerHoriz)
{
if (pPlayer->q16horiz > F16(95) && pPlayer->q16horiz < F16(105)) pPlayer->q16horiz = F16(100);
if (pPlayer->q16horizoff > F16(-5) && pPlayer->q16horizoff < F16(5)) pPlayer->q16horizoff = 0;
}
pPlayer->q16horiz = fix16_clamp(pPlayer->q16horiz, F16(HORIZ_MIN), F16(HORIZ_MAX));
//Shooting code/changes
if (pPlayer->show_empty_weapon > 0)
@ -5573,7 +5593,7 @@ RECHECK:
#ifndef EDUKE32_STANDALONE
if (!FURY && pPlayer->knee_incs > 0)
{
pPlayer->q16horiz -= F16(48);
g_horizSkew = F16(-48);
pPlayer->return_to_center = 9;
if (++pPlayer->knee_incs > 15)