Merge branch 'input-display' into 'master'

Input Display

See merge request KartKrew/Kart!1951
This commit is contained in:
Oni 2024-02-25 07:19:36 +00:00
commit 0a9b143053
19 changed files with 196 additions and 177 deletions

View file

@ -1235,7 +1235,7 @@ enum {
WP_KICKSTARTACCEL = 1<<0,
WP_SHRINKME = 1<<1,
WP_AUTOROULETTE = 1<<2,
WP_LITESTEER = 1<<3,
WP_ANALOGSTICK = 1<<3,
};
void WeaponPref_Send(UINT8 ssplayer)
@ -1248,12 +1248,12 @@ void WeaponPref_Send(UINT8 ssplayer)
if (cv_autoroulette[ssplayer].value)
prefs |= WP_AUTOROULETTE;
if (cv_litesteer[ssplayer].value)
prefs |= WP_LITESTEER;
if (cv_shrinkme[ssplayer].value)
prefs |= WP_SHRINKME;
if (gamecontrolflags[ssplayer] & GCF_ANALOGSTICK)
prefs |= WP_ANALOGSTICK;
SendNetXCmdForPlayer(ssplayer, XD_WEAPONPREF, &prefs, 1);
}
@ -1269,12 +1269,12 @@ void WeaponPref_Save(UINT8 **cp, INT32 playernum)
if (player->pflags & PF_AUTOROULETTE)
prefs |= WP_AUTOROULETTE;
if (player->pflags & PF_LITESTEER)
prefs |= WP_LITESTEER;
if (player->pflags & PF_SHRINKME)
prefs |= WP_SHRINKME;
if (player->pflags & PF_ANALOGSTICK)
prefs |= WP_ANALOGSTICK;
WRITEUINT8(*cp, prefs);
}
@ -1296,6 +1296,11 @@ size_t WeaponPref_Parse(const UINT8 *bufstart, INT32 playernum)
if (prefs & WP_SHRINKME)
player->pflags |= PF_SHRINKME;
if (prefs & WP_ANALOGSTICK)
player->pflags |= PF_ANALOGSTICK;
else
player->pflags &= ~PF_ANALOGSTICK;
if (leveltime < 2)
{
// BAD HACK: No other place I tried to slot this in

View file

@ -103,7 +103,7 @@ typedef enum
PF_RINGLOCK = 1<<13, // Prevent picking up rings while SPB is locked on
PF_LITESTEER = 1<<14, // Hold Down to shallow turn (digital only)
PF_ANALOGSTICK = 1<<14, // This player is using an analog joystick
//15-17 free, was previously itemflags stuff

View file

@ -3994,7 +3994,7 @@ const char *const PLAYERFLAG_LIST[] = {
"RINGLOCK", // Prevent picking up rings while SPB is locked on
"LITESTEER", // Shallow digital turn with DOWN
"ANALOGSTICK", // This player is using an analog joystick
"\x01", // Free
"\x01", // Free
"\x01", // Free

View file

@ -125,7 +125,6 @@ demoghost *ghosts = NULL;
#define DEMO_SHRINKME 0x04
#define DEMO_BOT 0x08
#define DEMO_AUTOROULETTE 0x10
#define DEMO_LITESTEER 0x20
// For demos
#define ZT_FWD 0x0001
@ -2218,8 +2217,6 @@ void G_BeginRecording(void)
i |= DEMO_KICKSTART;
if (player->pflags & PF_AUTOROULETTE)
i |= DEMO_AUTOROULETTE;
if (player->pflags & PF_LITESTEER)
i |= DEMO_LITESTEER;
if (player->pflags & PF_SHRINKME)
i |= DEMO_SHRINKME;
if (player->bot == true)
@ -3172,11 +3169,6 @@ void G_DoPlayDemo(const char *defdemoname)
else
players[p].pflags &= ~PF_AUTOROULETTE;
if (flags & DEMO_LITESTEER)
players[p].pflags |= PF_LITESTEER;
else
players[p].pflags &= ~PF_LITESTEER;
if (flags & DEMO_SHRINKME)
players[p].pflags |= PF_SHRINKME;
else

View file

@ -2189,7 +2189,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
totalring = players[player].totalring;
xtralife = players[player].xtralife;
pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_LITESTEER));
pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_ANALOGSTICK));
// SRB2kart
memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette));

View file

@ -31,6 +31,7 @@ INT32 gamekeydown[MAXDEVICES][NUMINPUTS];
// two key codes (or virtual key) per game control
INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING];
UINT8 gamecontrolflags[MAXSPLITSCREENPLAYERS];
INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage
INT32 menucontrolreserved[num_gamecontrols][MAXINPUTMAPPING];
@ -963,19 +964,37 @@ void G_DefineDefaultControls(void)
menucontrolreserved[gc_start][0] = KEY_ESCAPE; // Handled special
}
void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen)
static boolean G_ControlUsesAxis(INT32 map[MAXINPUTMAPPING])
{
INT32 i, j, gc;
for (i = 0; i < (gclist && gclen ? gclen : num_gamecontrols); i++)
for (INT32 i = 0; i < MAXINPUTMAPPING; i++)
{
gc = (gclist && gclen) ? gclist[i] : i;
for (j = 0; j < MAXINPUTMAPPING; j++)
INT32 key = map[i];
if (key >= KEY_AXIS1 && key < JOYINPUTEND)
{
setupcontrols[gc][j] = fromcontrols[gc][j];
return true;
}
}
return false;
}
void G_ApplyControlScheme(UINT8 splitplayer, INT32 (*fromcontrols)[MAXINPUTMAPPING])
{
UINT8 flags = 0;
if (G_ControlUsesAxis(fromcontrols[gc_up]) ||
G_ControlUsesAxis(fromcontrols[gc_down]) ||
G_ControlUsesAxis(fromcontrols[gc_left]) ||
G_ControlUsesAxis(fromcontrols[gc_right]))
{
flags |= GCF_ANALOGSTICK;
}
memcpy(gamecontrol[splitplayer], fromcontrols, sizeof gamecontrol[splitplayer]);
gamecontrolflags[splitplayer] = flags;
if (Playing())
WeaponPref_Send(splitplayer); // update PF_ANALOGSTICK
}
void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[MAXINPUTMAPPING], INT32 (*fromcontrolsb)[MAXINPUTMAPPING], INT32 (*fromcontrolsc)[MAXINPUTMAPPING], INT32 (*fromcontrolsd)[MAXINPUTMAPPING])

View file

@ -100,6 +100,11 @@ typedef enum
gc_drift = gc_r,
} gamecontrols_e;
typedef enum
{
GCF_ANALOGSTICK = 1 << 0,
} gamecontrol_flags_e;
// mouse values are used once
extern consvar_t cv_controlperkey;
@ -113,6 +118,7 @@ extern INT32 gamekeydown[MAXDEVICES][NUMINPUTS];
// several key codes (or virtual key) per game control
extern INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING];
extern UINT8 gamecontrolflags[MAXSPLITSCREENPLAYERS];
extern INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage
extern INT32 menucontrolreserved[num_gamecontrols][MAXINPUTMAPPING];
@ -194,7 +200,7 @@ void Command_Setcontrol3_f(void);
void Command_Setcontrol4_f(void);
void G_DefineDefaultControls(void);
INT32 G_GetControlScheme(INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen);
void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen);
void G_ApplyControlScheme(UINT8 splitplayer, INT32 (*fromcontrols)[MAXINPUTMAPPING]);
void G_SaveKeySetting(FILE *f, INT32 (*fromcontrolsa)[MAXINPUTMAPPING], INT32 (*fromcontrolsb)[MAXINPUTMAPPING], INT32 (*fromcontrolsc)[MAXINPUTMAPPING], INT32 (*fromcontrolsd)[MAXINPUTMAPPING]);
INT32 G_CheckDoubleUsage(INT32 keynum, INT32 playernum, boolean modify);

View file

@ -316,3 +316,10 @@ UINT8 G_PartyPosition(UINT8 player)
return party.find(player) - party.begin();
}
UINT8 G_LocalSplitscreenPartyPosition(UINT8 player)
{
const Party& party = local_party[player];
return party.find(player) - party.begin();
}

View file

@ -62,6 +62,9 @@ const UINT8 *G_PartyArray(UINT8 player);
// Suitable index to G_PartyMember and G_PartyArray.
UINT8 G_PartyPosition(UINT8 player);
//
UINT8 G_LocalSplitscreenPartyPosition(UINT8 player);
//
// Globals
//

View file

@ -3,4 +3,5 @@ target_sources(SRB2SDL2 PRIVATE
spectator.cpp
timer.cpp
emerald-win.cpp
input-display.cpp
)

105
src/hud/input-display.cpp Normal file
View file

@ -0,0 +1,105 @@
// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2024 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 <string>
#include <fmt/format.h>
#include "../math/vec.hpp"
#include "../g_input.h"
#include "../g_game.h"
#include "../i_joy.h"
#include "../k_hud.h"
#include "../k_kart.h"
#include "../v_draw.hpp"
using srb2::Draw;
using srb2::math::Vec2;
namespace
{
const char* dpad_suffix(const Vec2<float>& v)
{
if (v.y > 0)
{
if (v.x < 0)
return "UL";
else if (v.x > 0)
return "UR";
else
return "U";
}
else if (v.y < 0)
{
if (v.x < 0)
return "DL";
else if (v.x > 0)
return "DR";
else
return "D";
}
else
{
if (v.x < 0)
return "L";
else if (v.x > 0)
return "R";
else
return "N";
}
}
}; // namespace
void K_DrawInputDisplay(INT32 x, INT32 y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent)
{
const ticcmd_t& cmd = players[displayplayers[pid]].cmd;
const std::string prefix = fmt::format("PR{}", mode);
auto gfx = [&](auto format, auto&&... args) { return prefix + fmt::format(format, args...); };
auto but = [&](char key, INT32 gc, UINT32 bt)
{
bool press = local ? G_PlayerInputAnalog(pid, gc, 0) : ((cmd.buttons & bt) == bt);
return gfx(press ? "BT{}B" : "BT{}", key);
};
Draw box = Draw(x, y).flags(flags);
box.flags(transparent ? V_TRANSLUCENT : 0).patch(gfx("CONT"));
Vec2<float> dpad = local ?
Vec2<float> {
(G_PlayerInputAnalog(pid, gc_right, 0) - G_PlayerInputAnalog(pid, gc_left, 0)) / (float)JOYAXISRANGE,
(G_PlayerInputAnalog(pid, gc_up, 0) - G_PlayerInputAnalog(pid, gc_down, 0)) / (float)JOYAXISRANGE,
} :
Vec2<float> {
-cmd.turning / (float)KART_FULLTURN,
(float)cmd.throwdir,
};
box.patch(gfx("PAD{}", dpad_suffix(dpad)));
box.patch(but('A', gc_a, BT_ACCELERATE));
box.patch(but('B', gc_b, BT_LOOKBACK));
box.patch(but('C', gc_c, BT_SPINDASHMASK));
box.patch(but('X', gc_x, BT_BRAKE));
box.patch(but('Y', gc_y, BT_RESPAWN));
box.patch(but('Z', gc_z, BT_VOTE));
box.patch(but('L', gc_l, BT_ATTACK));
box.patch(but('R', gc_r, BT_DRIFT));
box.patch(but('S', gc_start, 0xFFFFFFFF));
if (mode == '4' || mode == '5') // Saturn 3D
{
float dist = (mode == '4') ? 3.f : 2.f;
box.patch(gfx("JOY1"));
box.xy(dpad.x * dist, -dpad.y * dist).patch(gfx("JOY2"));
}
}

View file

@ -50,6 +50,7 @@
#include "k_rank.h"
#include "g_party.h"
#include "k_hitlag.h"
#include "g_input.h"
//{ Patch Definitions
static patch_t *kp_nodraw;
@ -5160,85 +5161,27 @@ static void K_drawKartFirstPerson(void)
}
}
// doesn't need to ever support 4p
static void K_drawInput(void)
{
static INT32 pn = 0;
INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT);
INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col;
const INT32 accent1 = splitflags | skincolors[stplyr->skincolor].ramp[5];
const INT32 accent2 = splitflags | skincolors[stplyr->skincolor].ramp[9];
ticcmd_t *cmd = &stplyr->cmd;
#define BUTTW 8
#define BUTTH 11
#define drawbutt(xoffs, butt, symb)\
if (!stplyr->exiting && (cmd->buttons & butt))\
{\
offs = 2;\
col = accent1;\
}\
else\
{\
offs = 0;\
col = accent2;\
V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\
}\
V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\
V_DrawFixedPatch((x+1+(xoffs))<<FRACBITS, (y+offs+1)<<FRACBITS, FRACUNIT, splitflags, fontv[TINY_FONT].font[symb-HU_FONTSTART], NULL)
drawbutt(-2*BUTTW, BT_ACCELERATE, 'A');
drawbutt( -BUTTW, BT_BRAKE, 'B');
drawbutt( 0, BT_DRIFT, 'D');
drawbutt( BUTTW, BT_ATTACK, 'I');
#undef drawbutt
#undef BUTTW
#undef BUTTH
y -= 1;
if (stplyr->exiting || !stplyr->steering) // no turn
target = 0;
else // turning of multiple strengths!
{
target = ((abs(stplyr->steering) - 1)/125)+1;
if (target > 4)
target = 4;
if (stplyr->steering < 0)
target = -target;
}
if (pn != target)
{
if (abs(pn - target) == 1)
pn = target;
else if (pn < target)
pn += 2;
else //if (pn > target)
pn -= 2;
}
if (pn < 0)
{
splitflags |= V_FLIP; // right turn
x--;
}
target = abs(pn);
if (target > 4)
target = 4;
if (!stplyr->skincolor)
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, splitflags, kp_inputwheel[target], NULL);
else
{
UINT8 *colormap;
colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(stplyr->skincolor), GTC_CACHE);
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, splitflags, kp_inputwheel[target], colormap);
}
INT32 def[4][3] = {
{247, 156, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 1p
{247, 56, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 2p
{6, 52, V_SNAPTOBOTTOM | V_SNAPTOLEFT}, // 4p left
{282 - BASEVIDWIDTH/2, 52, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 4p right
};
INT32 k = r_splitscreen <= 1 ? r_splitscreen : 2 + (R_GetViewNumber() & 1);
INT32 flags = def[k][2] | V_SPLITSCREEN | V_SLIDEIN;
char mode = ((stplyr->pflags & PF_ANALOGSTICK) ? '4' : '2') + (r_splitscreen > 1);
bool local = !demo.playback && P_IsMachineLocalPlayer(stplyr);
K_DrawInputDisplay(
def[k][0],
def[k][1],
flags,
mode,
(local ? G_LocalSplitscreenPartyPosition : G_PartyPosition)(stplyr - players),
local,
stplyr->speed > 0
);
}
static void K_drawChallengerScreen(void)

View file

@ -63,6 +63,8 @@ void K_DrawKartPositionNumXY(
boolean exit, boolean lastLap, boolean losing
);
void K_DrawInputDisplay(INT32 x, INT32 y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent);
extern patch_t *kp_capsuletarget_arrow[2][2];
extern patch_t *kp_capsuletarget_icon[2];
extern patch_t *kp_capsuletarget_far[2][2];

View file

@ -4742,52 +4742,6 @@ INT16 controlleroffsets[][2] = {
{149, 187}, // gc_start
};
// Controller patches for button presses.
// reminder that lumpnames can only be 8 chars at most. (+1 for \0)
static const char *controllerpresspatch[9][2] = {
{"PR_BTA", "PR_BTAB"}, // MBT_A
{"PR_BTB", "PR_BTBB"}, // MBT_B
{"PR_BTC", "PR_BTCB"}, // MBT_C
{"PR_BTX", "PR_BTXB"}, // MBT_X
{"PR_BTY", "PR_BTYB"}, // MBT_Y
{"PR_BTZ", "PR_BTZB"}, // MBT_Z
{"PR_BTL", "PR_BTLB"}, // MBT_L
{"PR_BTR", "PR_BTRB"}, // MBT_R
{"PR_BTS", "PR_BTSB"}, // MBT_START
};
static const char *M_GetDPadPatchName(SINT8 ud, SINT8 lr)
{
if (ud < 0)
{
if (lr < 0)
return "PR_PADUL";
else if (lr > 0)
return "PR_PADUR";
else
return "PR_PADU";
}
else if (ud > 0)
{
if (lr < 0)
return "PR_PADDL";
else if (lr > 0)
return "PR_PADDR";
else
return "PR_PADD";
}
else
{
if (lr < 0)
return "PR_PADL";
else if (lr > 0)
return "PR_PADR";
else
return "PR_PADN";
}
}
static void M_DrawBindBen(INT32 x, INT32 y, INT32 scroll_remaining)
{
// optionsmenu.bindben_swallow
@ -4896,26 +4850,7 @@ void M_DrawProfileControls(void)
patch_t *hint = W_CachePatchName("MENUHINT", PU_CACHE);
INT32 hintofs = 3;
V_DrawScaledPatch(BASEVIDWIDTH*2/3 - optionsmenu.contx, BASEVIDHEIGHT/2 -optionsmenu.conty, 0, W_CachePatchName("PR_CONT", PU_CACHE));
// Draw button presses...
V_DrawScaledPatch(
BASEVIDWIDTH*2/3 - optionsmenu.contx,
BASEVIDHEIGHT/2 - optionsmenu.conty,
0,
W_CachePatchName(M_GetDPadPatchName(menucmd[pid].dpad_ud, menucmd[pid].dpad_lr), PU_CACHE)
);
for (i = 0; i < 9; i++)
{
INT32 bt = 1<<i;
V_DrawScaledPatch(
BASEVIDWIDTH*2/3 - optionsmenu.contx,
BASEVIDHEIGHT/2 - optionsmenu.conty,
0,
W_CachePatchName(controllerpresspatch[i][M_MenuButtonHeld(pid, bt) != 0], PU_CACHE)
);
}
K_DrawInputDisplay(BASEVIDWIDTH*2/3 - optionsmenu.contx, BASEVIDHEIGHT/2 - optionsmenu.conty, 0, '_', pid, true, false);
if (optionsmenu.trycontroller)
{

View file

@ -497,7 +497,7 @@ static void PR_ApplyProfile_Settings(profile_t *p, UINT8 playernum)
CV_StealthSetValue(&cv_rumble[playernum], p->rumble);
// set controls...
memcpy(&gamecontrol[playernum], p->controls, sizeof(gamecontroldefault));
G_ApplyControlScheme(playernum, p->controls);
}
static void PR_ApplyProfile_Memory(UINT8 profilenum, UINT8 playernum)

View file

@ -577,7 +577,7 @@ void Command_LoadConfig_f(void)
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
G_CopyControls(gamecontrol[i], gamecontroldefault, NULL, 0);
G_ApplyControlScheme(i, gamecontroldefault);
}
// temporarily reset execversion to default
@ -631,7 +631,7 @@ void M_FirstLoadConfig(void)
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
G_CopyControls(gamecontrol[i], gamecontroldefault, NULL, 0);
G_ApplyControlScheme(i, gamecontroldefault);
}
// register execversion here before we load any configs

View file

@ -219,7 +219,7 @@ void M_ProfileTryController(INT32 choice)
optionsmenu.trycontroller = TICRATE*5;
// Apply these controls right now on P1's end.
memcpy(&gamecontrol[0], optionsmenu.tempcontrols, sizeof(gamecontroldefault));
G_ApplyControlScheme(0, optionsmenu.tempcontrols);
}
static void M_ProfileControlSaveResponse(INT32 choice)
@ -234,7 +234,7 @@ static void M_ProfileControlSaveResponse(INT32 choice)
// Don't apply the profile itself as that would lead to issues mid-game.
if (belongsto > -1 && belongsto < MAXSPLITSCREENPLAYERS)
{
memcpy(&gamecontrol[belongsto], optionsmenu.tempcontrols, sizeof(gamecontroldefault));
G_ApplyControlScheme(belongsto, optionsmenu.tempcontrols);
}
}
else
@ -325,7 +325,7 @@ boolean M_ProfileControlsInputs(INT32 ch)
profile_t *cpr = PR_GetProfile(cv_currprofile.value);
if (cpr == NULL)
cpr = PR_GetProfile(0); // Creating a profile at boot, revert to guest profile
memcpy(&gamecontrol[0], cpr->controls, sizeof(gamecontroldefault));
G_ApplyControlScheme(0, cpr->controls);
}
return true;

View file

@ -502,7 +502,7 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
else if (num)
{
// For any player past player 1, set controls to default profile controls, otherwise it's generally awful to do any menuing...
memcpy(&gamecontrol[num], gamecontroldefault, sizeof(gamecontroldefault));
G_ApplyControlScheme(num, gamecontroldefault);
}
G_SetDeviceForPlayer(num, device);

View file

@ -180,6 +180,7 @@ public:
void patch(patch_t* patch) const;
void patch(const char* name) const { patch(Draw::cache_patch(name)); }
void patch(const std::string& name) const { patch(name.c_str()); }
void thumbnail(UINT16 mapnum) const;