raze/source/games/duke/src/input.cpp
Mitchell Richters 466bc84697 - Duke/RR: Completely revamped vehicle mouse/controller input.
* Original algorithm now used purely for keyboard input only.
* Mouse and controller input is no longer a scaled double version of the keyboard input.
* Mouse input is the square root of the base angle adjustment (20) multiplied by (mouse input divided by the input scale factor). As such, mouse input is completely consistent between synchronised and unsynchronised input.
* Controller input is base angle adjustment (20) multiplied by itself.
* Renamed `turnl`/`turnr` variables to `kbdLeft`/`kbdRight` respectively.
* Fixed issue where `p->TiltStatus` wasn't being backed up in `boatApplyTurn()`.
2020-11-07 09:56:57 +11:00

954 lines
26 KiB
C++

//-------------------------------------------------------------------------
/*
Copyright (C) 1996, 2003 - 3D Realms Entertainment
Copyright (C) 2000, 2003 - Matt Saettler (EDuke Enhancements)
Copyright (C) 2020 - Christoph Oelckers
This file is part of Enhanced Duke Nukem 3D version 1.5 - Atomic Edition
Duke Nukem 3D is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Original Source: 1996 - Todd Replogle
Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms
EDuke enhancements integrated: 04/13/2003 - Matt Saettler
Note: EDuke source was in transition. Changes are in-progress in the
source as it is released.
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "global.h"
#include "gamecontrol.h"
#include "v_video.h"
#include "dukeactor.h"
BEGIN_DUKE_NS
// State timer counters.
static int turnheldtime;
static int lastcontroltime;
static InputPacket loc; // input accumulation buffer.
//---------------------------------------------------------------------------
//
// handles all HUD related input, i.e. inventory item selection and activation plus weapon selection.
//
// Note: This doesn't restrict the events to WW2GI - since the other games do
// not define any by default there is no harm done keeping the code clean.
//
//---------------------------------------------------------------------------
void hud_input(int plnum)
{
int i, k;
uint8_t dainv;
struct player_struct* p;
short unk;
unk = 0;
p = &ps[plnum];
auto pact = p->GetActor();
i = p->aim_mode;
p->aim_mode = !PlayerInput(plnum, SB_AIMMODE);
if (p->aim_mode < i)
p->sync.actions |= SB_CENTERVIEW;
// Backup weapon here as hud_input() is the first function where any one of the weapon variables can change.
backupweapon(p);
if (isRR())
{
if (PlayerInput(plnum, SB_QUICK_KICK) && p->last_pissed_time == 0)
{
if (!isRRRA() || p->GetActor()->s.extra > 0)
{
p->last_pissed_time = 4000;
S_PlayActorSound(437, pact);
if (p->GetActor()->s.extra <= max_player_health - max_player_health / 10)
{
p->GetActor()->s.extra += 2;
p->last_extra = p->GetActor()->s.extra;
}
else if (p->GetActor()->s.extra < max_player_health)
p->GetActor()->s.extra = max_player_health;
}
}
}
else
{
if (PlayerInput(plnum, SB_QUICK_KICK) && p->quick_kick == 0 && (p->curr_weapon != KNEE_WEAPON || p->kickback_pic == 0))
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_QUICKKICK, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
{
p->quick_kick = 14;
if (!p->quick_kick_msg && plnum == screenpeek) FTA(QUOTE_MIGHTY_FOOT, p);
p->quick_kick_msg = true;
}
}
}
if (!PlayerInput(plnum, SB_QUICK_KICK)) p->quick_kick_msg = false;
if (!PlayerInputBits(plnum, SB_INTERFACE_BITS))
p->interface_toggle_flag = 0;
else if (p->interface_toggle_flag == 0)
{
p->interface_toggle_flag = 1;
// Don't go on if paused or dead.
if (paused) return;
if (p->GetActor()->s.extra <= 0) return;
// Activate an inventory item. This just forwards to the other inventory bits. If the inventory selector was taken out of the playsim this could be removed.
if (PlayerInput(plnum, SB_INVUSE) && p->newOwner == nullptr)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_INVENTORY, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
{
if (p->inven_icon > ICON_NONE && p->inven_icon <= ICON_HEATS) PlayerSetItemUsed(plnum, p->inven_icon);
}
}
if (!isRR() && PlayerUseItem(plnum, ICON_HEATS))
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USENIGHTVISION, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0 && p->heat_amount > 0)
{
p->heat_on = !p->heat_on;
p->inven_icon = 5;
S_PlayActorSound(NITEVISION_ONOFF, pact);
FTA(106 + (!p->heat_on), p);
}
}
if (PlayerUseItem(plnum, ICON_STEROIDS))
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USESTEROIDS, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
{
if (p->steroids_amount == 400)
{
p->steroids_amount--;
S_PlayActorSound(DUKE_TAKEPILLS, pact);
p->inven_icon = ICON_STEROIDS;
FTA(12, p);
}
}
return;
}
if (PlayerInput(plnum, SB_INVPREV) || PlayerInput(plnum, SB_INVNEXT))
{
p->invdisptime = 26 * 2;
if (PlayerInput(plnum, SB_INVNEXT)) k = 1;
else k = 0;
dainv = p->inven_icon;
i = 0;
CHECKINV1:
if (i < 9)
{
i++;
switch (dainv)
{
case 4:
if (p->jetpack_amount > 0 && i > 1)
break;
if (k) dainv = 5;
else dainv = 3;
goto CHECKINV1;
case 6:
if (p->scuba_amount > 0 && i > 1)
break;
if (k) dainv = 7;
else dainv = 5;
goto CHECKINV1;
case 2:
if (p->steroids_amount > 0 && i > 1)
break;
if (k) dainv = 3;
else dainv = 1;
goto CHECKINV1;
case 3:
if (p->holoduke_amount > 0 && i > 1)
break;
if (k) dainv = 4;
else dainv = 2;
goto CHECKINV1;
case 0:
case 1:
if (p->firstaid_amount > 0 && i > 1)
break;
if (k) dainv = 2;
else dainv = 7;
goto CHECKINV1;
case 5:
if (p->heat_amount > 0 && i > 1)
break;
if (k) dainv = 6;
else dainv = 4;
goto CHECKINV1;
case 7:
if (p->boot_amount > 0 && i > 1)
break;
if (k) dainv = 1;
else dainv = 6;
goto CHECKINV1;
}
}
else dainv = 0;
// These events force us to keep the inventory selector in the playsim as opposed to the UI where it really belongs.
if (PlayerInput(plnum, SB_INVPREV))
{
SetGameVarID(g_iReturnVarID, dainv, nullptr, plnum);
OnEvent(EVENT_INVENTORYLEFT, plnum, nullptr, -1);
dainv = GetGameVarID(g_iReturnVarID, nullptr, plnum);
}
if (PlayerInput(plnum, SB_INVNEXT))
{
SetGameVarID(g_iReturnVarID, dainv, nullptr, plnum);
OnEvent(EVENT_INVENTORYRIGHT, plnum, nullptr, -1);
dainv = GetGameVarID(g_iReturnVarID, nullptr, plnum);
}
p->inven_icon = dainv;
// Someone must have really hated constant data, doing this with a switch/case (and of course also with literal numbers...)
static const uint8_t invquotes[] = { QUOTE_MEDKIT, QUOTE_STEROIDS, QUOTE_HOLODUKE, QUOTE_JETPACK, QUOTE_NVG, QUOTE_SCUBA, QUOTE_BOOTS };
if (dainv >= 1 && dainv < 8) FTA(invquotes[dainv - 1], p);
}
int weap = PlayerNewWeapon(plnum);
if (weap > 1 && p->kickback_pic > 0)
p->wantweaponfire = weap - 1;
// Here we have to be extra careful that the weapons do not get mixed up, so let's keep the code for Duke and RR completely separate.
fi.selectweapon(plnum, weap);
if (PlayerInput(plnum, SB_HOLSTER))
{
if (p->curr_weapon > KNEE_WEAPON)
{
if (p->holster_weapon == 0 && p->weapon_pos == 0)
{
p->holster_weapon = 1;
p->weapon_pos = -1;
FTA(QUOTE_WEAPON_LOWERED, p);
}
else if (p->holster_weapon == 1 && p->weapon_pos == -9)
{
p->holster_weapon = 0;
p->weapon_pos = 10;
FTA(QUOTE_WEAPON_RAISED, p);
}
}
}
if (PlayerUseItem(plnum, ICON_HOLODUKE) && (isRR() || p->newOwner == nullptr))
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_HOLODUKEON, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
{
if (!isRR())
{
if (p->holoduke_on == nullptr)
{
if (p->holoduke_amount > 0)
{
p->inven_icon = 3;
auto pactor =
EGS(p->cursectnum,
p->posx,
p->posy,
p->posz + (30 << 8), TILE_APLAYER, -64, 0, 0, p->angle.ang.asbuild(), 0, 0, nullptr, 10);
pactor->temp_data[3] = pactor->temp_data[4] = 0;
p->holoduke_on = pactor;
pactor->s.yvel = plnum;
pactor->s.extra = 0;
FTA(QUOTE_HOLODUKE_ON, p);
S_PlayActorSound(TELEPORTER, p->holoduke_on);
}
else FTA(QUOTE_HOLODUKE_NOT_FOUND, p);
}
else
{
S_PlayActorSound(TELEPORTER, p->holoduke_on);
p->holoduke_on = nullptr;
FTA(QUOTE_HOLODUKE_OFF, p);
}
}
else // In RR this means drinking whiskey.
{
if (p->holoduke_amount > 0 && p->GetActor()->s.extra < max_player_health)
{
p->holoduke_amount -= 400;
p->GetActor()->s.extra += 5;
if (p->GetActor()->s.extra > max_player_health)
p->GetActor()->s.extra = max_player_health;
p->drink_amt += 5;
p->inven_icon = 3;
if (p->holoduke_amount == 0)
checkavailinven(p);
if (p->drink_amt < 99 && !S_CheckActorSoundPlaying(pact, 425))
S_PlayActorSound(425, pact);
}
}
}
}
if (isRR() && PlayerUseItem(plnum, ICON_HEATS) && p->newOwner == nullptr)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USENIGHTVISION, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
{
if (p->yehaa_timer == 0)
{
p->yehaa_timer = 126;
S_PlayActorSound(390, pact);
p->noise_radius = 16384;
madenoise(plnum);
if (sector[p->cursectnum].lotag == 857)
{
if (p->GetActor()->s.extra <= max_player_health)
{
p->GetActor()->s.extra += 10;
if (p->GetActor()->s.extra >= max_player_health)
p->GetActor()->s.extra = max_player_health;
}
}
else
{
if (p->GetActor()->s.extra + 1 <= max_player_health)
{
p->GetActor()->s.extra++;
}
}
}
}
}
if (PlayerUseItem(plnum, ICON_FIRSTAID))
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USEMEDKIT, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
{
if (p->firstaid_amount > 0 && p->GetActor()->s.extra < max_player_health)
{
if (!isRR())
{
int j = max_player_health - p->GetActor()->s.extra;
if ((unsigned int)p->firstaid_amount > j)
{
p->firstaid_amount -= j;
p->GetActor()->s.extra = max_player_health;
p->inven_icon = 1;
}
else
{
p->GetActor()->s.extra += p->firstaid_amount;
p->firstaid_amount = 0;
checkavailinven(p);
}
S_PlayActorSound(DUKE_USEMEDKIT, pact);
}
else
{
int j = 10;
if (p->firstaid_amount > j)
{
p->firstaid_amount -= j;
p->GetActor()->s.extra += j;
if (p->GetActor()->s.extra > max_player_health)
p->GetActor()->s.extra = max_player_health;
p->inven_icon = 1;
}
else
{
p->GetActor()->s.extra += p->firstaid_amount;
p->firstaid_amount = 0;
checkavailinven(p);
}
if (p->GetActor()->s.extra > max_player_health)
p->GetActor()->s.extra = max_player_health;
p->drink_amt += 10;
if (p->drink_amt <= 100 && !S_CheckActorSoundPlaying(pact, DUKE_USEMEDKIT))
S_PlayActorSound(DUKE_USEMEDKIT, pact);
}
}
}
}
if (PlayerUseItem(plnum, ICON_JETPACK) && (isRR() || p->newOwner == nullptr))
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_USEJETPACK, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) == 0)
{
if (!isRR())
{
if (p->jetpack_amount > 0)
{
p->jetpack_on = !p->jetpack_on;
if (p->jetpack_on)
{
p->inven_icon = 4;
S_StopSound(-1, pact, CHAN_VOICE); // this will stop the falling scream
S_PlayActorSound(DUKE_JETPACK_ON, pact);
FTA(QUOTE_JETPACK_ON, p);
}
else
{
p->hard_landing = 0;
p->poszv = 0;
S_PlayActorSound(DUKE_JETPACK_OFF, pact);
S_StopSound(DUKE_JETPACK_IDLE, pact);
S_StopSound(DUKE_JETPACK_ON, pact);
FTA(QUOTE_JETPACK_OFF, p);
}
}
else FTA(QUOTE_JETPACK_NOT_FOUND, p);
}
else
{
// eat cow pie
if (p->jetpack_amount > 0 && p->GetActor()->s.extra < max_player_health)
{
if (!S_CheckActorSoundPlaying(pact, 429))
S_PlayActorSound(429, pact);
p->jetpack_amount -= 100;
if (p->drink_amt > 0)
{
p->drink_amt -= 5;
if (p->drink_amt < 0)
p->drink_amt = 0;
}
if (p->eat < 100)
{
p->eat += 5;
if (p->eat > 100)
p->eat = 100;
}
p->GetActor()->s.extra += 5;
p->inven_icon = 4;
if (p->GetActor()->s.extra > max_player_health)
p->GetActor()->s.extra = max_player_health;
if (p->jetpack_amount <= 0)
checkavailinven(p);
}
}
}
}
if (PlayerInput(plnum, SB_TURNAROUND) && p->angle.spin.asbam() == 0 && p->on_crane == nullptr)
{
SetGameVarID(g_iReturnVarID, 0, nullptr, plnum);
OnEvent(EVENT_TURNAROUND, plnum, nullptr, -1);
if (GetGameVarID(g_iReturnVarID, nullptr, plnum) != 0)
{
p->sync.actions &= ~SB_TURNAROUND;
}
}
}
}
//---------------------------------------------------------------------------
//
// Main input routine.
// This includes several input improvements from EDuke32, but this code
// has been mostly rewritten completely to make it clearer and reduce redundancy.
//
//---------------------------------------------------------------------------
enum
{
TURBOTURNTIME = (TICRATE/8), // 7
NORMALTURN = 15,
PREAMBLETURN = 5,
NORMALKEYMOVE = 40,
MAXVEL = ((NORMALKEYMOVE*2)+10),
MAXSVEL = ((NORMALKEYMOVE*2)+10),
MAXANGVEL = 1024, // 127
MAXHORIZVEL = 256, // 127
MAXVELMOTO = 120,
VEHICLETURN = 20
};
//---------------------------------------------------------------------------
//
// handles the input bits
//
//---------------------------------------------------------------------------
static void processInputBits(player_struct *p, ControlInfo* const hidInput)
{
ApplyGlobalInput(loc, hidInput);
if (isRR() && (loc.actions & SB_CROUCH)) loc.actions &= ~SB_JUMP;
if (p->OnMotorcycle || p->OnBoat)
{
// mask out all actions not compatible with vehicles.
loc.actions &= ~(SB_WEAPONMASK_BITS | SB_TURNAROUND | SB_CENTERVIEW | SB_HOLSTER | SB_JUMP | SB_CROUCH | SB_RUN |
SB_AIM_UP | SB_AIM_DOWN | SB_AIMMODE | SB_LOOK_UP | SB_LOOK_DOWN | SB_LOOK_LEFT | SB_LOOK_RIGHT);
}
else
{
if (buttonMap.ButtonDown(gamefunc_Quick_Kick)) // this shares a bit with another function so cannot be in the common code.
loc.actions |= SB_QUICK_KICK;
if (buttonMap.ButtonDown(gamefunc_Toggle_Crouch) || p->crouch_toggle)
{
loc.actions |= SB_CROUCH;
}
if ((isRR() && p->drink_amt > 88)) loc.actions |= SB_LOOK_LEFT;
if ((isRR() && p->drink_amt > 99)) loc.actions |= SB_LOOK_DOWN;
}
}
//---------------------------------------------------------------------------
//
// split off so that it can later be integrated into the other games more easily.
//
//---------------------------------------------------------------------------
static void checkCrouchToggle(player_struct* p)
{
int const sectorLotag = p->cursectnum != -1 ? sector[p->cursectnum].lotag : 0;
int const crouchable = sectorLotag != ST_2_UNDERWATER && (sectorLotag != ST_1_ABOVE_WATER || p->spritebridge);
if (buttonMap.ButtonDown(gamefunc_Toggle_Crouch))
{
p->crouch_toggle = !p->crouch_toggle && crouchable;
if (crouchable)
buttonMap.ClearButton(gamefunc_Toggle_Crouch);
}
if (buttonMap.ButtonDown(gamefunc_Crouch) || buttonMap.ButtonDown(gamefunc_Jump) || p->jetpack_on || (!crouchable && p->on_ground))
p->crouch_toggle = 0;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int getticssincelastupdate()
{
int tics = lastcontroltime == 0 || ud.levelclock < lastcontroltime ? 0 : ud.levelclock - lastcontroltime;
lastcontroltime = ud.levelclock;
return tics;
}
//---------------------------------------------------------------------------
//
// split out for readability
//
//---------------------------------------------------------------------------
static double motoApplyTurn(player_struct* p, ControlInfo* const hidInput, bool const kbdLeft, bool const kbdRight, double const factor)
{
double turnvel = 0;
p->oTiltStatus = p->TiltStatus;
if (p->MotoSpeed == 0 || !p->on_ground)
{
turnheldtime = 0;
lastcontroltime = 0;
if (kbdLeft || hidInput->mouseturnx < 0 || hidInput->dyaw < 0)
{
p->TiltStatus -= factor;
if (p->TiltStatus < -10)
p->TiltStatus = -10;
}
else if (kbdRight || hidInput->mouseturnx > 0 || hidInput->dyaw > 0)
{
p->TiltStatus += factor;
if (p->TiltStatus > 10)
p->TiltStatus = 10;
}
}
else
{
if (kbdLeft || kbdRight || p->moto_drink || hidInput->mouseturnx || hidInput->dyaw)
{
auto const baseVel = (buttonMap.ButtonDown(gamefunc_Move_Backward) || hidInput->dz < 0) && p->MotoSpeed <= 0 ? -VEHICLETURN : VEHICLETURN;
int tics = getticssincelastupdate();
if (kbdLeft || p->moto_drink < 0 || hidInput->mouseturnx < 0 || hidInput->dyaw < 0)
{
turnheldtime += tics;
p->TiltStatus -= factor;
if (p->TiltStatus < -10)
p->TiltStatus = -10;
if (kbdLeft)
turnvel += turnheldtime >= TURBOTURNTIME && p->MotoSpeed > 0 ? -baseVel : -baseVel * (3. / 10.);
if (hidInput->mouseturnx < 0)
turnvel -= sqrt((p->MotoSpeed > 0 ? baseVel : baseVel * (3. / 10.)) * -(hidInput->mouseturnx / factor));
if (hidInput->dyaw < 0)
turnvel += (p->MotoSpeed > 0 ? baseVel : baseVel * (3. / 10.)) * hidInput->dyaw;
}
if (kbdRight || p->moto_drink > 0 || hidInput->mouseturnx > 0 || hidInput->dyaw > 0)
{
turnheldtime += tics;
p->TiltStatus += factor;
if (p->TiltStatus > 10)
p->TiltStatus = 10;
if (kbdRight)
turnvel += turnheldtime >= TURBOTURNTIME && p->MotoSpeed > 0 ? baseVel : baseVel * (3. / 10.);
if (hidInput->mouseturnx > 0)
turnvel += sqrt((p->MotoSpeed > 0 ? baseVel : baseVel * (3. / 10.)) * (hidInput->mouseturnx / factor));
if (hidInput->dyaw > 0)
turnvel += (p->MotoSpeed > 0 ? baseVel : baseVel * (3. / 10.)) * hidInput->dyaw;
}
}
else
{
turnheldtime = 0;
lastcontroltime = 0;
if (p->TiltStatus > 0)
p->TiltStatus -= factor;
else if (p->TiltStatus < 0)
p->TiltStatus += factor;
}
}
if (fabs(p->TiltStatus) < factor)
p->TiltStatus = 0;
return turnvel * factor;
}
//---------------------------------------------------------------------------
//
// same for the boat
//
//---------------------------------------------------------------------------
static double boatApplyTurn(player_struct *p, ControlInfo* const hidInput, bool const kbdLeft, bool const kbdRight, double const factor)
{
double turnvel = 0;
p->oTiltStatus = p->TiltStatus;
if (p->MotoSpeed)
{
if (kbdLeft || kbdRight || p->moto_drink || hidInput->mouseturnx || hidInput->dyaw)
{
double const velScale = 6. / 19.;
auto const baseVel = !p->NotOnWater ? VEHICLETURN : VEHICLETURN * velScale;
int tics = getticssincelastupdate();
if (kbdLeft || p->moto_drink < 0 || hidInput->mouseturnx < 0 || hidInput->dyaw < 0)
{
turnheldtime += tics;
if (!p->NotOnWater)
{
p->TiltStatus -= factor;
if (p->TiltStatus < -10)
p->TiltStatus = -10;
}
if (kbdRight)
turnvel -= turnheldtime >= TURBOTURNTIME ? baseVel : baseVel * velScale;
if (hidInput->mouseturnx < 0)
turnvel -= sqrt(baseVel * -(hidInput->mouseturnx / factor));
if (hidInput->dyaw < 0)
turnvel += baseVel * hidInput->dyaw;
}
if (kbdRight || p->moto_drink > 0 || hidInput->mouseturnx > 0 || hidInput->dyaw > 0)
{
turnheldtime += tics;
if (!p->NotOnWater)
{
p->TiltStatus += factor;
if (p->TiltStatus > 10)
p->TiltStatus = 10;
}
if (kbdRight)
turnvel += turnheldtime >= TURBOTURNTIME ? baseVel : baseVel * velScale;
if (hidInput->mouseturnx > 0)
turnvel += sqrt(baseVel * (hidInput->mouseturnx / factor));
if (hidInput->dyaw > 0)
turnvel += baseVel * hidInput->dyaw;
}
}
else if (!p->NotOnWater)
{
turnheldtime = 0;
lastcontroltime = 0;
if (p->TiltStatus > 0)
p->TiltStatus -= factor;
else if (p->TiltStatus < 0)
p->TiltStatus += factor;
}
}
else if (!p->NotOnWater)
{
turnheldtime = 0;
lastcontroltime = 0;
if (p->TiltStatus > 0)
p->TiltStatus -= factor;
else if (p->TiltStatus < 0)
p->TiltStatus += factor;
}
if (fabs(p->TiltStatus) < factor)
p->TiltStatus = 0;
return turnvel * factor;
}
//---------------------------------------------------------------------------
//
// much of this was rewritten from scratch to make the logic easier to follow.
//
//---------------------------------------------------------------------------
static void processVehicleInput(player_struct *p, ControlInfo* const hidInput, InputPacket& input, double const scaleAdjust)
{
bool const kbdLeft = buttonMap.ButtonDown(gamefunc_Turn_Left) || buttonMap.ButtonDown(gamefunc_Strafe_Left);
bool const kbdRight = buttonMap.ButtonDown(gamefunc_Turn_Right) || buttonMap.ButtonDown(gamefunc_Strafe_Right);
p->vehTurnLeft = kbdLeft || hidInput->mouseturnx < 0 || hidInput->dyaw < 0;
p->vehTurnRight = kbdRight || hidInput->mouseturnx > 0 || hidInput->dyaw > 0;
if (p->OnBoat || !p->moto_underwater)
{
p->vehForwardScale = (buttonMap.ButtonDown(gamefunc_Move_Forward) || buttonMap.ButtonDown(gamefunc_Strafe)) + hidInput->dz;
p->vehReverseScale = buttonMap.ButtonDown(gamefunc_Move_Backward) + -hidInput->dz;
if (loc.actions & SB_RUN)
loc.actions |= SB_CROUCH;
}
if (p->OnMotorcycle)
{
input.avel = motoApplyTurn(p, hidInput, kbdLeft, kbdRight, scaleAdjust);
if (p->moto_underwater) p->MotoSpeed = 0;
}
else
{
input.avel = boatApplyTurn(p, hidInput, kbdLeft, kbdRight, scaleAdjust);
}
input.fvel = xs_CRoundToInt(p->MotoSpeed);
input.avel *= (45. / 256.);
loc.avel += input.avel;
}
//---------------------------------------------------------------------------
//
// finalizes the input and passes it to the global input buffer
//
//---------------------------------------------------------------------------
static void FinalizeInput(int playerNum, InputPacket& input, bool vehicle)
{
auto p = &ps[playerNum];
bool blocked = movementBlocked(playerNum) || p->GetActor()->s.extra <= 0 || (p->dead_flag && !ud.god);
if (blocked && ps[playerNum].newOwner == nullptr)
{
// neutralize all movement when blocked or in automap follow mode
loc.fvel = loc.svel = 0;
loc.avel = loc.horz = 0;
input.avel = input.horz = 0;
}
else
{
if (p->on_crane == nullptr)
{
if (!vehicle)
{
loc.fvel = clamp(loc.fvel + input.fvel, -MAXVEL, MAXVEL);
loc.svel = clamp(loc.svel + input.svel, -MAXSVEL, MAXSVEL);
}
else
loc.fvel = clamp(input.fvel, -(MAXVELMOTO >> 3), MAXVELMOTO);
}
else
{
loc.fvel = input.fvel = 0;
loc.svel = input.svel = 0;
}
if (p->on_crane == nullptr && p->newOwner == nullptr)
{
// input.avel already added to loc in processMovement()
loc.avel = clamp(loc.avel, -MAXANGVEL, MAXANGVEL);
if (!cl_syncinput && input.avel)
{
p->angle.spin = bamlook(0);
}
}
else
{
loc.avel = input.avel = 0;
}
if (p->newOwner == nullptr && !(p->sync.actions & SB_CENTERVIEW))
{
// input.horz already added to loc in processMovement()
loc.horz = clamp(loc.horz, -MAXHORIZVEL, MAXHORIZVEL);
}
else
{
loc.horz = input.horz = 0;
}
}
}
//---------------------------------------------------------------------------
//
// External entry point
//
//---------------------------------------------------------------------------
void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput)
{
if (paused)
{
loc = {};
return;
}
auto const p = &ps[myconnectindex];
if (numplayers == 1)
{
setlocalplayerinput(p);
}
double const scaleAdjust = InputScale();
InputPacket input{};
if (isRRRA() && (p->OnMotorcycle || p->OnBoat))
{
p->crouch_toggle = 0;
processInputBits(p, hidInput);
processVehicleInput(p, hidInput, input, scaleAdjust);
FinalizeInput(myconnectindex, input, true);
if (!cl_syncinput && p->GetActor()->s.extra > 0)
{
apply_seasick(p, scaleAdjust);
}
}
else
{
processInputBits(p, hidInput);
processMovement(&input, &loc, hidInput, scaleAdjust, p->drink_amt);
checkCrouchToggle(p);
FinalizeInput(myconnectindex, input, false);
}
if (!cl_syncinput)
{
if (p->GetActor()->s.extra > 0)
{
// Do these in the same order as the old code.
calcviewpitch(p, scaleAdjust);
processavel(p, &input.avel);
applylook(&p->angle, input.avel, &p->sync.actions, scaleAdjust, p->crouch_toggle || p->sync.actions & SB_CROUCH);
sethorizon(&p->horizon.horiz, input.horz, &p->sync.actions, scaleAdjust);
}
p->angle.processhelpers(scaleAdjust);
p->horizon.processhelpers(scaleAdjust);
}
if (packet)
{
auto const pPlayer = &ps[myconnectindex];
auto const ang = pPlayer->angle.ang.asbuild();
*packet = loc;
auto fvel = loc.fvel;
auto svel = loc.svel;
packet->fvel = mulscale9(fvel, sintable[(ang + 2560) & 2047]) +
mulscale9(svel, sintable[(ang + 2048) & 2047]) +
pPlayer->fric.x;
packet->svel = mulscale9(fvel, sintable[(ang + 2048) & 2047]) +
mulscale9(svel, sintable[(ang + 1536) & 2047]) +
pPlayer->fric.y;
loc = {};
}
}
//---------------------------------------------------------------------------
//
// This is called from InputState::ClearAllInput and resets all static state being used here.
//
//---------------------------------------------------------------------------
void GameInterface::clearlocalinputstate()
{
loc = {};
turnheldtime = 0;
lastcontroltime = 0;
}
END_DUKE_NS