Merge branch 'refactor-build-ticcmd' into 'master'

Refactor G_BuildTiccmd

See merge request KartKrew/Kart!1397
This commit is contained in:
James R 2023-08-16 08:00:27 +00:00
commit 420d9a402f
5 changed files with 412 additions and 443 deletions

View file

@ -16,6 +16,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
z_zone.c
f_finale.c
f_wipe.c
g_build_ticcmd.cpp
g_demo.c
g_game.c
g_input.c

411
src/g_build_ticcmd.cpp Normal file
View file

@ -0,0 +1,411 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
// Copyright (C) 2023 by Kart Krew.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
#include <algorithm>
#include <cmath>
#include "console.h"
#include "d_player.h"
#include "d_ticcmd.h"
#include "doomstat.h"
#include "doomtype.h"
#include "g_demo.h"
#include "g_game.h"
#include "g_input.h"
#include "g_state.h"
#include "g_party.h"
#include "hu_stuff.h"
#include "i_joy.h"
#include "i_system.h"
#include "k_bot.h"
#include "k_director.h"
#include "k_kart.h"
#include "k_menu.h"
#include "lua_hook.h"
#include "m_cheat.h"
#include "m_fixed.h"
#include "p_local.h"
#include "p_mobj.h"
#include "p_tick.h"
#include "tables.h"
namespace
{
// Take a magnitude of two axes, and adjust it to take out the deadzone
// Will return a value between 0 and JOYAXISRANGE
INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone)
{
const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT;
INT32 adjustedMagnitude = std::abs(magnitude);
if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%...
{
return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0
}
if (adjustedMagnitude <= jdeadzone)
{
return 0; // Magnitude is within deadzone, so do nothing
}
// Calculate how much the magnitude exceeds the deadzone
adjustedMagnitude = std::min(adjustedMagnitude, JOYAXISRANGE) - jdeadzone;
return (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone);
}
class TiccmdBuilder
{
struct JoyStickVector2
{
INT32 xaxis;
INT32 yaxis;
};
ticcmd_t* cmd;
INT32 realtics;
UINT8 ssplayer;
UINT8 viewnum;
JoyStickVector2 joystickvector;
UINT8 forplayer() const { return ssplayer - 1; }
player_t* player() const { return &players[g_localplayers[forplayer()]]; }
// Get the actual sensible radial value for a joystick axis when accounting for a deadzone
void handle_axis_deadzone()
{
INT32 gamepadStyle = Joystick[forplayer()].bGamepadStyle;
fixed_t deadZone = cv_deadzone[forplayer()].value;
// When gamepadstyle is "true" the values are just -1, 0, or 1. This is done in the interface code.
if (gamepadStyle)
{
return;
}
// Get the total magnitude of the 2 axes
INT32 magnitude = std::sqrt(static_cast<double>(
(joystickvector.xaxis * joystickvector.xaxis) + (joystickvector.yaxis * joystickvector.yaxis)
));
// Get the normalised xy values from the magnitude
INT32 normalisedXAxis = (joystickvector.xaxis * magnitude) / JOYAXISRANGE;
INT32 normalisedYAxis = (joystickvector.yaxis * magnitude) / JOYAXISRANGE;
// Apply the deadzone to the magnitude to give a correct value between 0 and JOYAXISRANGE
INT32 normalisedMagnitude = G_BasicDeadZoneCalculation(magnitude, deadZone);
// Apply the deadzone to the xy axes
joystickvector.xaxis = (normalisedXAxis * normalisedMagnitude) / JOYAXISRANGE;
joystickvector.yaxis = (normalisedYAxis * normalisedMagnitude) / JOYAXISRANGE;
// Cap the values so they don't go above the correct maximum
joystickvector.xaxis = std::min(joystickvector.xaxis, JOYAXISRANGE);
joystickvector.xaxis = std::max(joystickvector.xaxis, -JOYAXISRANGE);
joystickvector.yaxis = std::min(joystickvector.yaxis, JOYAXISRANGE);
joystickvector.yaxis = std::max(joystickvector.yaxis, -JOYAXISRANGE);
}
void hook()
{
/*
Lua: Allow this hook to overwrite ticcmd.
We check if we're actually in a level because for some reason this Hook would run in menus and on the titlescreen otherwise.
Be aware that within this hook, nothing but this player's cmd can be edited (otherwise we'd run in some pretty bad synching problems since this is clientsided, or something)
Possible usages for this are:
-Forcing the player to perform an action, which could otherwise require terrible, terrible hacking to replicate.
-Preventing the player to perform an action, which would ALSO require some weirdo hacks.
-Making some galaxy brain autopilot Lua if you're a masochist
-Making a Mario Kart 8 Deluxe tier baby mode that steers you away from walls and whatnot. You know what, do what you want!
*/
if (!addedtogame || gamestate != GS_LEVEL)
{
return;
}
LUA_HookTiccmd(player(), cmd, HOOK(PlayerCmd));
auto clamp = [](auto val, int range) { return std::clamp(static_cast<int>(val), -(range), range); };
cmd->forwardmove = clamp(cmd->forwardmove, MAXPLMOVE);
cmd->turning = clamp(cmd->turning, KART_FULLTURN);
cmd->throwdir = clamp(cmd->throwdir, KART_FULLTURN);
// Send leveltime when this tic was generated to the server for control lag calculations.
// Only do this when in a level. Also do this after the hook, so that it can't overwrite this.
cmd->latency = (leveltime & TICCMD_LATENCYMASK);
}
// Turning was removed from G_BuildTiccmd to prevent easy client hacking.
// This brings back the camera prediction that was lost.
void angle_prediction()
{
// Chasecam stops in these situations, so local cam should stop too.
// Otherwise it'll jerk when it resumes.
if (player()->playerstate == PST_DEAD)
{
return;
}
if (player()->mo != NULL && !P_MobjWasRemoved(player()->mo) && player()->mo->hitlag > 0)
{
return;
}
angle_t angleChange = 0;
while (realtics > 0)
{
INT32& steering = localsteering[forplayer()];
steering = K_UpdateSteeringValue(steering, cmd->turning);
angleChange = K_GetKartTurnValue(player(), steering) << TICCMD_REDUCE;
realtics--;
}
#if 0
// Left here in case it needs unsealing later. This tried to replicate an old localcam function, but this behavior was unpopular in tests.
//if (player()->pflags & PF_DRIFTEND)
{
localangle[forplayer()] = player()->mo->angle;
}
else
#endif
{
localangle[viewnum] += angleChange;
}
}
bool typing_input()
{
if (!menuactive && !chat_on && !CON_Ready())
{
return false;
}
cmd->flags |= TICCMD_TYPING;
if (hu_keystrokes)
{
cmd->flags |= TICCMD_KEYSTROKE;
}
return true;
}
bool director_input()
{
if (G_IsPartyLocal(displayplayers[forplayer()]) == true)
{
return false;
}
if (M_MenuButtonPressed(forplayer(), MBT_A))
{
G_AdjustView(ssplayer, 1, true);
K_ToggleDirector(false);
}
if (M_MenuButtonPressed(forplayer(), MBT_X))
{
G_AdjustView(ssplayer, -1, true);
K_ToggleDirector(false);
}
if (player()->spectator == true)
{
// duplication of fire
if (G_PlayerInputDown(forplayer(), gc_item, 0))
{
cmd->buttons |= BT_ATTACK;
}
if (M_MenuButtonPressed(forplayer(), MBT_R))
{
K_ToggleDirector(true);
}
}
return true;
}
bool spectator_analog_input()
{
if (!player()->spectator && !objectplacing)
{
return false;
}
if (G_PlayerInputDown(forplayer(), gc_accel, 0))
{
cmd->buttons |= BT_ACCELERATE;
}
if (G_PlayerInputDown(forplayer(), gc_brake, 0))
{
cmd->buttons |= BT_BRAKE;
}
if (G_PlayerInputDown(forplayer(), gc_lookback, 0))
{
cmd->aiming -= joystickvector.yaxis;
}
else
{
if (joystickvector.yaxis < 0)
{
cmd->forwardmove += MAXPLMOVE;
}
if (joystickvector.yaxis > 0)
{
cmd->forwardmove -= MAXPLMOVE;
}
}
return true;
}
void kart_analog_input()
{
// forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward.
INT32 value = G_PlayerInputAnalog(forplayer(), gc_accel, 0);
if (value != 0)
{
cmd->buttons |= BT_ACCELERATE;
cmd->forwardmove += ((value * MAXPLMOVE) / JOYAXISRANGE);
}
value = G_PlayerInputAnalog(forplayer(), gc_brake, 0);
if (value != 0)
{
cmd->buttons |= BT_BRAKE;
cmd->forwardmove -= ((value * MAXPLMOVE) / JOYAXISRANGE);
}
// But forward/backward IS used for aiming.
if (joystickvector.yaxis != 0)
{
cmd->throwdir -= (joystickvector.yaxis * KART_FULLTURN) / JOYAXISRANGE;
}
}
void analog_input()
{
joystickvector.xaxis = G_PlayerInputAnalog(forplayer(), gc_right, 0) - G_PlayerInputAnalog(forplayer(), gc_left, 0);
joystickvector.yaxis = 0;
handle_axis_deadzone();
// For kart, I've turned the aim axis into a digital axis because we only
// use it for aiming to throw items forward/backward and the vote screen
// This mean that the turn axis will still be gradient but up/down will be 0
// until the stick is pushed far enough
joystickvector.yaxis = G_PlayerInputAnalog(forplayer(), gc_down, 0) - G_PlayerInputAnalog(forplayer(), gc_up, 0);
if (encoremode)
{
joystickvector.xaxis = -joystickvector.xaxis;
}
if (joystickvector.xaxis != 0)
{
cmd->turning -= (joystickvector.xaxis * KART_FULLTURN) / JOYAXISRANGE;
}
if (spectator_analog_input())
{
return;
}
kart_analog_input();
}
void common_button_input()
{
auto map = [this](INT32 gamecontrol, UINT32 button)
{
if (G_PlayerInputDown(forplayer(), gamecontrol, 0))
{
cmd->buttons |= button;
}
};
map(gc_drift, BT_DRIFT); // drift
map(gc_spindash, BT_SPINDASHMASK); // C
map(gc_item, BT_ATTACK); // fire
map(gc_lookback, BT_LOOKBACK); // rear view
map(gc_respawn, BT_RESPAWN | BT_EBRAKEMASK); // respawn
map(gc_vote, BT_VOTE); // mp general function button
// lua buttons a thru c
map(gc_luaa, BT_LUAA);
map(gc_luab, BT_LUAB);
map(gc_luac, BT_LUAC);
}
public:
explicit TiccmdBuilder(ticcmd_t* cmd_, INT32 realtics_, UINT8 ssplayer_) :
cmd(cmd_), realtics(realtics_), ssplayer(ssplayer_), viewnum(G_PartyPosition(g_localplayers[forplayer()]))
{
*cmd = {}; // blank ticcmd
if (demo.playback)
{
return;
}
if (paused || P_AutoPause())
{
return;
}
if (gamestate == GS_LEVEL && player()->playerstate == PST_REBORN)
{
return;
}
// A human player can turn into a bot at the end of
// a race, so the director controls have higher
// priority.
bool overlay = typing_input() || director_input();
if (K_PlayerUsesBotMovement(player()))
{
// Bot ticcmd is generated by K_BuildBotTiccmd
return;
}
if (!overlay)
{
analog_input();
common_button_input();
}
cmd->angle = localangle[viewnum] >> TICCMD_REDUCE;
hook();
angle_prediction();
}
};
}; // namespace
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
{
TiccmdBuilder(cmd, realtics, ssplayer);
}

View file

@ -313,12 +313,6 @@ boolean comebackshowninfo; // Have you already seen the "ATTACK OR PROTECT" mess
tic_t curlap; // Current lap time
tic_t bestlap; // Best lap time
typedef struct joystickvector2_s
{
INT32 xaxis;
INT32 yaxis;
} joystickvector2_t;
boolean precache = true; // if true, load all graphics at start
UINT16 prevmap, nextmap;
@ -1047,64 +1041,6 @@ boolean G_PlayerInputDown(UINT8 p, INT32 gc, UINT8 menuPlayers)
return (G_PlayerInputAnalog(p, gc, menuPlayers) != 0);
}
// Take a magnitude of two axes, and adjust it to take out the deadzone
// Will return a value between 0 and JOYAXISRANGE
static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone)
{
const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT;
INT32 deadzoneAppliedValue = 0;
INT32 adjustedMagnitude = abs(magnitude);
if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%...
return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0
else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone
{
adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE);
adjustedMagnitude -= jdeadzone;
deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone);
}
return deadzoneAppliedValue;
}
// Get the actual sensible radial value for a joystick axis when accounting for a deadzone
static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvector)
{
INT32 gamepadStyle = Joystick[splitnum].bGamepadStyle;
fixed_t deadZone = cv_deadzone[splitnum].value;
// When gamepadstyle is "true" the values are just -1, 0, or 1. This is done in the interface code.
if (!gamepadStyle)
{
// Get the total magnitude of the 2 axes
INT32 magnitude = (joystickvector->xaxis * joystickvector->xaxis) + (joystickvector->yaxis * joystickvector->yaxis);
INT32 normalisedXAxis;
INT32 normalisedYAxis;
INT32 normalisedMagnitude;
double dMagnitude = sqrt((double)magnitude);
magnitude = (INT32)dMagnitude;
// Get the normalised xy values from the magnitude
normalisedXAxis = (joystickvector->xaxis * magnitude) / JOYAXISRANGE;
normalisedYAxis = (joystickvector->yaxis * magnitude) / JOYAXISRANGE;
// Apply the deadzone to the magnitude to give a correct value between 0 and JOYAXISRANGE
normalisedMagnitude = G_BasicDeadZoneCalculation(magnitude, deadZone);
// Apply the deadzone to the xy axes
joystickvector->xaxis = (normalisedXAxis * normalisedMagnitude) / JOYAXISRANGE;
joystickvector->yaxis = (normalisedYAxis * normalisedMagnitude) / JOYAXISRANGE;
// Cap the values so they don't go above the correct maximum
joystickvector->xaxis = min(joystickvector->xaxis, JOYAXISRANGE);
joystickvector->xaxis = max(joystickvector->xaxis, -JOYAXISRANGE);
joystickvector->yaxis = min(joystickvector->yaxis, JOYAXISRANGE);
joystickvector->yaxis = max(joystickvector->yaxis, -JOYAXISRANGE);
}
}
//
// G_BuildTiccmd
// Builds a ticcmd from all of the available inputs
@ -1118,330 +1054,6 @@ angle_t localangle[MAXSPLITSCREENPLAYERS];
INT32 localsteering[MAXSPLITSCREENPLAYERS];
// Turning was removed from G_BuildTiccmd to prevent easy client hacking.
// This brings back the camera prediction that was lost.
static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, UINT8 viewnum, player_t *player)
{
angle_t angleChange = 0;
// Chasecam stops in these situations, so local cam should stop too.
// Otherwise it'll jerk when it resumes.
if (player->playerstate == PST_DEAD)
return;
if (player->mo != NULL && !P_MobjWasRemoved(player->mo) && player->mo->hitlag > 0)
return;
while (realtics > 0)
{
localsteering[ssplayer - 1] = K_UpdateSteeringValue(localsteering[ssplayer - 1], cmd->turning);
angleChange = K_GetKartTurnValue(player, localsteering[ssplayer - 1]) << TICCMD_REDUCE;
realtics--;
}
#if 0
// Left here in case it needs unsealing later. This tried to replicate an old localcam function, but this behavior was unpopular in tests.
//if (player->pflags & PF_DRIFTEND)
{
localangle[ssplayer - 1] = player->mo->angle;
}
else
#endif
{
localangle[viewnum] += angleChange;
}
}
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
{
const UINT8 forplayer = ssplayer-1;
const UINT8 viewnum = G_PartyPosition(g_localplayers[forplayer]);
INT32 forward;
joystickvector2_t joystickvector;
player_t *player = &players[g_localplayers[forplayer]];
//camera_t *thiscam = &camera[forplayer];
//boolean *kbl = &keyboard_look[forplayer];
//boolean *rd = &resetdown[forplayer];
//const boolean mouseaiming = player->spectator;
if (demo.playback) return;
// Is there any reason this can't just be I_BaseTiccmd?
switch (ssplayer)
{
case 2:
G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1);
break;
case 3:
G_CopyTiccmd(cmd, I_BaseTiccmd3(), 1);
break;
case 4:
G_CopyTiccmd(cmd, I_BaseTiccmd4(), 1);
break;
case 1:
default:
G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
break;
}
cmd->angle = localangle[viewnum] >> TICCMD_REDUCE;
// why build a ticcmd if we're paused?
// Or, for that matter, if we're being reborn.
if (paused || P_AutoPause() || (gamestate == GS_LEVEL && player->playerstate == PST_REBORN))
{
return;
}
cmd->flags = 0;
if (menuactive || chat_on || CON_Ready())
{
cmd->flags |= TICCMD_TYPING;
if (hu_keystrokes)
{
cmd->flags |= TICCMD_KEYSTROKE;
}
goto aftercmdinput;
}
if (G_IsPartyLocal(displayplayers[forplayer]) == false)
{
if (M_MenuButtonPressed(forplayer, MBT_A))
{
G_AdjustView(ssplayer, 1, true);
K_ToggleDirector(false);
}
if (M_MenuButtonPressed(forplayer, MBT_X))
{
G_AdjustView(ssplayer, -1, true);
K_ToggleDirector(false);
}
if (player->spectator == true)
{
// duplication of fire
if (G_PlayerInputDown(forplayer, gc_item, 0))
{
cmd->buttons |= BT_ATTACK;
}
if (M_MenuButtonPressed(forplayer, MBT_R))
{
K_ToggleDirector(true);
}
}
goto aftercmdinput;
}
if (K_PlayerUsesBotMovement(player))
{
// Bot ticcmd is generated by K_BuildBotTiccmd
return;
}
joystickvector.xaxis = G_PlayerInputAnalog(forplayer, gc_right, 0) - G_PlayerInputAnalog(forplayer, gc_left, 0);
joystickvector.yaxis = 0;
G_HandleAxisDeadZone(forplayer, &joystickvector);
// For kart, I've turned the aim axis into a digital axis because we only
// use it for aiming to throw items forward/backward and the vote screen
// This mean that the turn axis will still be gradient but up/down will be 0
// until the stick is pushed far enough
joystickvector.yaxis = G_PlayerInputAnalog(forplayer, gc_down, 0) - G_PlayerInputAnalog(forplayer, gc_up, 0);
if (encoremode)
{
joystickvector.xaxis = -joystickvector.xaxis;
}
forward = 0;
cmd->turning = 0;
cmd->aiming = 0;
if (joystickvector.xaxis != 0)
{
cmd->turning -= (joystickvector.xaxis * KART_FULLTURN) / JOYAXISRANGE;
}
if (player->spectator || objectplacing) // SRB2Kart: spectators need special controls
{
if (G_PlayerInputDown(forplayer, gc_accel, 0))
{
cmd->buttons |= BT_ACCELERATE;
}
if (G_PlayerInputDown(forplayer, gc_brake, 0))
{
cmd->buttons |= BT_BRAKE;
}
if (G_PlayerInputDown(forplayer, gc_lookback, 0))
{
cmd->aiming -= joystickvector.yaxis;
}
else
{
if (joystickvector.yaxis < 0)
{
forward += MAXPLMOVE;
}
if (joystickvector.yaxis > 0)
{
forward -= MAXPLMOVE;
}
}
}
else
{
// forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward.
INT32 value = G_PlayerInputAnalog(forplayer, gc_accel, 0);
if (value != 0)
{
cmd->buttons |= BT_ACCELERATE;
forward += ((value * MAXPLMOVE) / JOYAXISRANGE);
}
value = G_PlayerInputAnalog(forplayer, gc_brake, 0);
if (value != 0)
{
cmd->buttons |= BT_BRAKE;
forward -= ((value * MAXPLMOVE) / JOYAXISRANGE);
}
// But forward/backward IS used for aiming.
if (joystickvector.yaxis != 0)
{
cmd->throwdir -= (joystickvector.yaxis * KART_FULLTURN) / JOYAXISRANGE;
}
}
// drift
if (G_PlayerInputDown(forplayer, gc_drift, 0))
{
cmd->buttons |= BT_DRIFT;
}
// C
if (G_PlayerInputDown(forplayer, gc_spindash, 0))
{
cmd->buttons |= BT_SPINDASHMASK;
}
// fire
if (G_PlayerInputDown(forplayer, gc_item, 0))
{
cmd->buttons |= BT_ATTACK;
}
// rear view
if (G_PlayerInputDown(forplayer, gc_lookback, 0))
{
cmd->buttons |= BT_LOOKBACK;
}
// respawn
if (G_PlayerInputDown(forplayer, gc_respawn, 0))
{
cmd->buttons |= (BT_RESPAWN | BT_EBRAKEMASK);
}
// mp general function button
if (G_PlayerInputDown(forplayer, gc_vote, 0))
{
cmd->buttons |= BT_VOTE;
}
// lua buttons a thru c
if (G_PlayerInputDown(forplayer, gc_luaa, 0)) { cmd->buttons |= BT_LUAA; }
if (G_PlayerInputDown(forplayer, gc_luab, 0)) { cmd->buttons |= BT_LUAB; }
if (G_PlayerInputDown(forplayer, gc_luac, 0)) { cmd->buttons |= BT_LUAC; }
// spectator aiming shit, ahhhh...
/*
{
INT32 player_invert = invertmouse ? -1 : 1;
INT32 screen_invert =
(player->mo && (player->mo->eflags & MFE_VERTICALFLIP)
&& (!thiscam->chase)) //because chasecam's not inverted
? -1 : 1; // set to -1 or 1 to multiply
axis = PlayerJoyAxis(ssplayer, AXISLOOK);
if (analogjoystickmove && axis != 0 && lookaxis && player->spectator)
cmd->aiming += (axis<<16) * screen_invert;
// spring back if not using keyboard neither mouselookin'
if (*kbl == false && !lookaxis && !mouseaiming)
cmd->aiming = 0;
if (player->spectator)
{
if (PlayerInputDown(ssplayer, gc_lookup) || (gamepadjoystickmove && axis < 0))
{
cmd->aiming += KB_LOOKSPEED * screen_invert;
*kbl = true;
}
else if (PlayerInputDown(ssplayer, gc_lookdown) || (gamepadjoystickmove && axis > 0))
{
cmd->aiming -= KB_LOOKSPEED * screen_invert;
*kbl = true;
}
}
if (PlayerInputDown(ssplayer, gc_centerview)) // No need to put a spectator limit on this one though :V
cmd->aiming = 0;
}
*/
cmd->forwardmove += (SINT8)forward;
aftercmdinput:
/* Lua: Allow this hook to overwrite ticcmd.
We check if we're actually in a level because for some reason this Hook would run in menus and on the titlescreen otherwise.
Be aware that within this hook, nothing but this player's cmd can be edited (otherwise we'd run in some pretty bad synching problems since this is clientsided, or something)
Possible usages for this are:
-Forcing the player to perform an action, which could otherwise require terrible, terrible hacking to replicate.
-Preventing the player to perform an action, which would ALSO require some weirdo hacks.
-Making some galaxy brain autopilot Lua if you're a masochist
-Making a Mario Kart 8 Deluxe tier baby mode that steers you away from walls and whatnot. You know what, do what you want!
*/
if (addedtogame && gamestate == GS_LEVEL)
{
LUA_HookTiccmd(player, cmd, HOOK(PlayerCmd));
// Send leveltime when this tic was generated to the server for control lag calculations.
// Only do this when in a level. Also do this after the hook, so that it can't overwrite this.
cmd->latency = (leveltime & TICCMD_LATENCYMASK);
}
if (cmd->forwardmove > MAXPLMOVE)
cmd->forwardmove = MAXPLMOVE;
else if (cmd->forwardmove < -MAXPLMOVE)
cmd->forwardmove = -MAXPLMOVE;
if (cmd->turning > KART_FULLTURN)
cmd->turning = KART_FULLTURN;
else if (cmd->turning < -KART_FULLTURN)
cmd->turning = -KART_FULLTURN;
if (cmd->throwdir > KART_FULLTURN)
cmd->throwdir = KART_FULLTURN;
else if (cmd->throwdir < -KART_FULLTURN)
cmd->throwdir = -KART_FULLTURN;
G_DoAnglePrediction(cmd, realtics, ssplayer, viewnum, player);
}
ticcmd_t *G_CopyTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
{
return M_Memcpy(dest, src, n*sizeof(*src));

View file

@ -90,27 +90,6 @@ void I_GetEvent(void);
*/
void I_OsPolling(void);
// Either returns a null ticcmd,
// or calls a loadable driver to build it.
// This ticcmd will then be modified by the gameloop
// for normal input.
/** \brief Input for the first player
*/
ticcmd_t *I_BaseTiccmd(void);
/** \brief Input for the second player
*/
ticcmd_t *I_BaseTiccmd2(void);
/** \brief Input for the third player
*/
ticcmd_t *I_BaseTiccmd3(void);
/** \brief Input for the fourth player
*/
ticcmd_t *I_BaseTiccmd4(void);
/** \brief Called by M_Responder when quit is selected, return exit code 0
*/
void I_Quit(void) FUNCNORETURN;

View file

@ -1416,40 +1416,6 @@ void I_Tactile4(FFType pFFType, const JoyFF_t *FFEffect)
(void)FFEffect;
}
static ticcmd_t emptycmd[MAXSPLITSCREENPLAYERS];
/** \brief empty ticcmd for player 1
*/
ticcmd_t *I_BaseTiccmd(void)
{
return &emptycmd[0];
}
/** \brief empty ticcmd for player 2
*/
ticcmd_t *I_BaseTiccmd2(void)
{
return &emptycmd[1];
}
/** \brief empty ticcmd for player 3
*/
ticcmd_t *I_BaseTiccmd3(void)
{
return &emptycmd[2];
}
/** \brief empty ticcmd for player 4
*/
ticcmd_t *I_BaseTiccmd4(void)
{
return &emptycmd[3];
}
//
// I_GetTime
// returns time in 1/TICRATE second tics