From 368ffb79b8831644858f37a2b27c0cf7f388df0b Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 13 Aug 2023 14:31:45 -0700 Subject: [PATCH 1/2] Move ticcmd generation code from g_game.c to g_build_ticcmd.cpp --- src/CMakeLists.txt | 1 + src/g_build_ticcmd.cpp | 432 +++++++++++++++++++++++++++++++++++++++++ src/g_game.c | 388 ------------------------------------ 3 files changed, 433 insertions(+), 388 deletions(-) create mode 100644 src/g_build_ticcmd.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1b3b169e0..9b3bc7715 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/g_build_ticcmd.cpp b/src/g_build_ticcmd.cpp new file mode 100644 index 000000000..50c307618 --- /dev/null +++ b/src/g_build_ticcmd.cpp @@ -0,0 +1,432 @@ +// 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 +#include + +#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 +{ + +struct joystickvector2_t +{ + INT32 xaxis; + INT32 yaxis; +}; + +// 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 deadzoneAppliedValue = 0; + 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 + else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone + { + adjustedMagnitude = std::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 +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 = std::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 = 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); + } +} + +// Turning was removed from G_BuildTiccmd to prevent easy client hacking. +// This brings back the camera prediction that was lost. +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; + } +} + +}; // namespace + +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); +} diff --git a/src/g_game.c b/src/g_game.c index 1e193b205..550324341 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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)); From c6db63463535c7838d5211cdcf8ba37a045b9b95 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 13 Aug 2023 16:00:23 -0700 Subject: [PATCH 2/2] Completely refactor G_BuildTiccmd into many smaller pieces --- src/g_build_ticcmd.cpp | 501 ++++++++++++++++++++--------------------- src/i_system.h | 21 -- src/sdl/i_system.c | 34 --- 3 files changed, 240 insertions(+), 316 deletions(-) diff --git a/src/g_build_ticcmd.cpp b/src/g_build_ticcmd.cpp index 50c307618..035bdc08a 100644 --- a/src/g_build_ticcmd.cpp +++ b/src/g_build_ticcmd.cpp @@ -41,154 +41,161 @@ namespace { -struct joystickvector2_t -{ - INT32 xaxis; - INT32 yaxis; -}; - // 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 deadzoneAppliedValue = 0; + 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 - else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone { - adjustedMagnitude = std::min(adjustedMagnitude, JOYAXISRANGE); - - adjustedMagnitude -= jdeadzone; - - deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); + return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0 } - return deadzoneAppliedValue; + 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); } -// Get the actual sensible radial value for a joystick axis when accounting for a deadzone -void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvector) +class TiccmdBuilder { - 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) + 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 = (joystickvector->xaxis * joystickvector->xaxis) + (joystickvector->yaxis * joystickvector->yaxis); - INT32 normalisedXAxis; - INT32 normalisedYAxis; - INT32 normalisedMagnitude; - double dMagnitude = std::sqrt((double)magnitude); - magnitude = (INT32)dMagnitude; + INT32 magnitude = std::sqrt(static_cast( + (joystickvector.xaxis * joystickvector.xaxis) + (joystickvector.yaxis * joystickvector.yaxis) + )); // Get the normalised xy values from the magnitude - normalisedXAxis = (joystickvector->xaxis * magnitude) / JOYAXISRANGE; - normalisedYAxis = (joystickvector->yaxis * magnitude) / JOYAXISRANGE; + 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 - normalisedMagnitude = G_BasicDeadZoneCalculation(magnitude, deadZone); + INT32 normalisedMagnitude = G_BasicDeadZoneCalculation(magnitude, deadZone); // Apply the deadzone to the xy axes - joystickvector->xaxis = (normalisedXAxis * normalisedMagnitude) / JOYAXISRANGE; - joystickvector->yaxis = (normalisedYAxis * normalisedMagnitude) / JOYAXISRANGE; + 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); + 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); } -} -// Turning was removed from G_BuildTiccmd to prevent easy client hacking. -// This brings back the camera prediction that was lost. -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) + void hook() { - localsteering[ssplayer - 1] = K_UpdateSteeringValue(localsteering[ssplayer - 1], cmd->turning); - angleChange = K_GetKartTurnValue(player, localsteering[ssplayer - 1]) << TICCMD_REDUCE; + /* + 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) - realtics--; + 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(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[ssplayer - 1] = player->mo->angle; - } - else + // 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; - } -} - -}; // namespace - -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; + { + localangle[viewnum] += angleChange; + } } - 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)) + bool typing_input() { - return; - } + if (!menuactive && !chat_on && !CON_Ready()) + { + return false; + } - cmd->flags = 0; - - if (menuactive || chat_on || CON_Ready()) - { cmd->flags |= TICCMD_TYPING; if (hu_keystrokes) @@ -196,83 +203,63 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->flags |= TICCMD_KEYSTROKE; } - goto aftercmdinput; + return true; } - if (G_IsPartyLocal(displayplayers[forplayer]) == false) + bool director_input() { - if (M_MenuButtonPressed(forplayer, MBT_A)) + 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)) + if (M_MenuButtonPressed(forplayer(), MBT_X)) { G_AdjustView(ssplayer, -1, true); K_ToggleDirector(false); } - if (player->spectator == true) + if (player()->spectator == true) { // duplication of fire - if (G_PlayerInputDown(forplayer, gc_item, 0)) + if (G_PlayerInputDown(forplayer(), gc_item, 0)) { cmd->buttons |= BT_ATTACK; } - if (M_MenuButtonPressed(forplayer, MBT_R)) + if (M_MenuButtonPressed(forplayer(), MBT_R)) { K_ToggleDirector(true); } } - goto aftercmdinput; + return true; } - if (K_PlayerUsesBotMovement(player)) + bool spectator_analog_input() { - // Bot ticcmd is generated by K_BuildBotTiccmd - return; - } + if (!player()->spectator && !objectplacing) + { + return false; + } - 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)) + if (G_PlayerInputDown(forplayer(), gc_accel, 0)) { cmd->buttons |= BT_ACCELERATE; } - if (G_PlayerInputDown(forplayer, gc_brake, 0)) + if (G_PlayerInputDown(forplayer(), gc_brake, 0)) { cmd->buttons |= BT_BRAKE; } - if (G_PlayerInputDown(forplayer, gc_lookback, 0)) + if (G_PlayerInputDown(forplayer(), gc_lookback, 0)) { cmd->aiming -= joystickvector.yaxis; } @@ -280,30 +267,33 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { if (joystickvector.yaxis < 0) { - forward += MAXPLMOVE; + cmd->forwardmove += MAXPLMOVE; } if (joystickvector.yaxis > 0) { - forward -= MAXPLMOVE; + cmd->forwardmove -= MAXPLMOVE; } } + + return true; } - else + + 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); + INT32 value = G_PlayerInputAnalog(forplayer(), gc_accel, 0); if (value != 0) { cmd->buttons |= BT_ACCELERATE; - forward += ((value * MAXPLMOVE) / JOYAXISRANGE); + cmd->forwardmove += ((value * MAXPLMOVE) / JOYAXISRANGE); } - value = G_PlayerInputAnalog(forplayer, gc_brake, 0); + value = G_PlayerInputAnalog(forplayer(), gc_brake, 0); if (value != 0) { cmd->buttons |= BT_BRAKE; - forward -= ((value * MAXPLMOVE) / JOYAXISRANGE); + cmd->forwardmove -= ((value * MAXPLMOVE) / JOYAXISRANGE); } // But forward/backward IS used for aiming. @@ -313,120 +303,109 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } } - // drift - if (G_PlayerInputDown(forplayer, gc_drift, 0)) + void analog_input() { - cmd->buttons |= BT_DRIFT; - } + joystickvector.xaxis = G_PlayerInputAnalog(forplayer(), gc_right, 0) - G_PlayerInputAnalog(forplayer(), gc_left, 0); + joystickvector.yaxis = 0; + handle_axis_deadzone(); - // C - if (G_PlayerInputDown(forplayer, gc_spindash, 0)) - { - cmd->buttons |= BT_SPINDASHMASK; - } + // 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); - // 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 (encoremode) { - 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; - } + joystickvector.xaxis = -joystickvector.xaxis; } - if (PlayerInputDown(ssplayer, gc_centerview)) // No need to put a spectator limit on this one though :V - cmd->aiming = 0; + if (joystickvector.xaxis != 0) + { + cmd->turning -= (joystickvector.xaxis * KART_FULLTURN) / JOYAXISRANGE; + } + + if (spectator_analog_input()) + { + return; + } + + kart_analog_input(); } - */ - 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) + void common_button_input() { - LUA_HookTiccmd(player, cmd, HOOK(PlayerCmd)); + auto map = [this](INT32 gamecontrol, UINT32 button) + { + if (G_PlayerInputDown(forplayer(), gamecontrol, 0)) + { + cmd->buttons |= button; + } + }; - // 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); + 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); } - if (cmd->forwardmove > MAXPLMOVE) - cmd->forwardmove = MAXPLMOVE; - else if (cmd->forwardmove < -MAXPLMOVE) - cmd->forwardmove = -MAXPLMOVE; +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 (cmd->turning > KART_FULLTURN) - cmd->turning = KART_FULLTURN; - else if (cmd->turning < -KART_FULLTURN) - cmd->turning = -KART_FULLTURN; + if (demo.playback) + { + return; + } - if (cmd->throwdir > KART_FULLTURN) - cmd->throwdir = KART_FULLTURN; - else if (cmd->throwdir < -KART_FULLTURN) - cmd->throwdir = -KART_FULLTURN; + if (paused || P_AutoPause()) + { + return; + } - G_DoAnglePrediction(cmd, realtics, ssplayer, viewnum, player); + 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); } diff --git a/src/i_system.h b/src/i_system.h index 23ee3bec0..9b1fd6c15 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -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; diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 2f7be946f..aed47915d 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -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