diff --git a/src/d_netcmd.c b/src/d_netcmd.c index bb4b7ad8c..f5f17199a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -233,6 +233,11 @@ static void Command_Teamchange2_f(void); static void Command_Teamchange3_f(void); static void Command_Teamchange4_f(void); +static void Command_Restat(void); +static void Command_Restat2(void); +static void Command_Restat3(void); +static void Command_Restat4(void); + static void Command_ServerTeamChange_f(void); static void Command_Clearscores_f(void); @@ -1440,6 +1445,117 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_connectawaittime); CV_RegisterVar(&cv_serverinfoscreen); + + // player restat + CV_RegisterVar(&cv_allowrestat); + CV_RegisterVar(&cv_notifyrestat); + COM_AddCommand("restat", Command_Restat); + COM_AddCommand("restat2", Command_Restat2); + COM_AddCommand("restat3", Command_Restat3); + COM_AddCommand("restat4", Command_Restat4); +} + +static void RestatForPlayer(UINT32 ssplayer) +{ + player_t* player = &players[g_localplayers[ssplayer]]; + int speed; + int weight; + + if (!cv_allowrestat.value) + { + CONS_Printf("This command has been disabled by the server host.\n"); + return; + } + + if (COM_Argc() > 2) + { + if (sscanf(COM_Argv(1), " %d", &speed) == 0 || + sscanf(COM_Argv(2), " %d", &weight) == 0) + { + CONS_Printf("Expected two numbers ( ) in the range of 1-9.\n"); + return; + } + // range checking + if (speed < 1 || speed > 9 || + weight < 1 || weight > 9) + { + CONS_Printf("Expected two numbers ( ) in the range of 1-9.\n"); + return; + } + + // should be good now 🥲 + player->kartspeedrestat = speed; + player->kartweightrestat = weight; + player->randomrestat = false; + + CONS_Printf("You will be %d speed, %d weight for the next race.\n", speed, weight); + CONS_Printf("Use \"restat off\" to return to your skin's default stats.\n"); + + WeaponPref_Send(ssplayer); + return; + } + else if (COM_Argc() > 1) + { + if (fasticmp(COM_Argv(1), "random")) + { + player->randomrestat = !player->randomrestat; + if (player->randomrestat) + { + CONS_Printf("Random restat is now enabled.\n"); + } + else + { + CONS_Printf("Random restat is now disabled.\n"); + } + + WeaponPref_Send(ssplayer); + return; + } + else if (fasticmp(COM_Argv(1), "off")) + { + player->kartspeedrestat = 0; + player->kartweightrestat = 0; + player->randomrestat = false; + WeaponPref_Send(ssplayer); + + CONS_Printf("Now using skin default stats.\n"); + return; + } + else + { + CONS_Printf( + "Usage: \"restat \"\n" + "Alternatively: \"restat random\" to toggle the use of random stats each round.\n" + "or \"restat off\" to use your skin's default stats.\n"); + } + } + else + { + CONS_Printf( + "Usage: \"restat \"\n" + "Alternatively: \"restat random\" to use random stats each round.\n" + "or \"restat off\" to use your skin's default stats.\n"); + } +} + +static void Command_Restat(void) +{ + RestatForPlayer(0); +} + +static void Command_Restat2(void) +{ + RestatForPlayer(1); +} + +static void Command_Restat3(void) +{ + RestatForPlayer(2); +} + +static void Command_Restat4(void) +{ + RestatForPlayer(3); } /** @@ -2230,11 +2346,13 @@ enum { WP_KICKSTARTACCEL = 1<<0, WP_SHRINKME = 1<<1, WP_FLIPCAM = 1<<2, - WP_LEGACYJITTER = 1<<3 + WP_LEGACYJITTER = 1<<3, + WP_RANDOMRESTAT = 1<<4 }; void WeaponPref_Send(UINT8 ssplayer) { + player_t* player = &players[g_localplayers[ssplayer]]; UINT8 prefs = 0; if (cv_kickstartaccel[ssplayer].value) @@ -2248,10 +2366,15 @@ void WeaponPref_Send(UINT8 ssplayer) if (!(cv_jitterlegacy[ssplayer].value)) prefs |= WP_LEGACYJITTER; + + if (player->randomrestat) + prefs |= WP_RANDOMRESTAT; - UINT8 buf[2]; + UINT8 buf[4]; buf[0] = prefs; buf[1] = cv_mindelay.value; + buf[2] = player->kartspeedrestat; + buf[3] = player->kartweightrestat; SendNetXCmdForPlayer(ssplayer, XD_WEAPONPREF, buf, sizeof buf); } @@ -2274,6 +2397,9 @@ void WeaponPref_Save(UINT8 **cp, INT32 playernum) if (player->jitterlegacy) prefs |= WP_LEGACYJITTER; + if (player->randomrestat) + prefs |= WP_RANDOMRESTAT; + WRITEUINT8(*cp, prefs); } @@ -2285,6 +2411,7 @@ void WeaponPref_Parse(UINT8 **cp, INT32 playernum) player->pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME|PF_FLIPCAM); player->jitterlegacy = false; + player->randomrestat = false; if (prefs & WP_KICKSTARTACCEL) player->pflags |= PF_KICKSTARTACCEL; @@ -2298,6 +2425,9 @@ void WeaponPref_Parse(UINT8 **cp, INT32 playernum) if (prefs & WP_LEGACYJITTER) player->jitterlegacy = true; + if (prefs & WP_RANDOMRESTAT) + player->randomrestat = true; + if (leveltime < 2) { // BAD HACK: No other place I tried to slot this in @@ -2309,14 +2439,28 @@ void WeaponPref_Parse(UINT8 **cp, INT32 playernum) static void Got_WeaponPref(UINT8 **cp,INT32 playernum) { + player_t *player = &players[playernum]; + WeaponPref_Parse(cp, playernum); UINT8 mindelay = READUINT8(*cp); if (server) { for (UINT8 i = 0; i < G_LocalSplitscreenPartySize(playernum); ++i) - playerdelaytable[G_LocalSplitscreenPartyMember(playernum, i)] = mindelay; + playerdelaytable[G_LocalSplitscreenPartyMember(playernum, i)] = mindelay; } + UINT8 kartspeedrestat = READUINT8(*cp); + UINT8 kartweightrestat = READUINT8(*cp); + if (kartspeedrestat < 0 || kartspeedrestat > 9 || + kartweightrestat < 0 || kartweightrestat > 9) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal restat values received from %s\n"), player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL); + return; + } + player->kartspeedrestat = kartspeedrestat; + player->kartweightrestat = kartweightrestat; // SEE ALSO g_demo.c demo_extradata[playernum] |= DXD_WEAPONPREF; diff --git a/src/d_player.h b/src/d_player.h index 5120717b4..543bce1b7 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -504,6 +504,10 @@ struct player_t UINT8 kartspeed; // Kart speed stat between 1 and 9 UINT8 kartweight; // Kart weight stat between 1 and 9 + UINT8 kartspeedrestat; // Player's wanted kart speed from restat + UINT8 kartweightrestat; // Player's wanted kart weight from restat + boolean randomrestat; // Randomly set the restat values of the player every game + INT32 followerskin; // Kart: This player's follower "skin" boolean followerready; // Kart: Used to know when we can have a follower or not. (This is set on the first NameAndColor follower update) UINT16 followercolor; // Kart: Used to store the follower colour the player wishes to use diff --git a/src/g_game.c b/src/g_game.c index 880e555ce..b09253d3c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -493,6 +493,10 @@ consvar_t cv_deadzonestyle[MAXSPLITSCREENPLAYERS] = { CVAR_INIT ("deadzonestyle4", "Kart", CV_SAVE, deadzonestyle_cons_t, NULL) }; +// allows players to use restat (server toggle) +consvar_t cv_allowrestat = CVAR_INIT ("allowrestat", "Yes", CV_NETVAR, CV_YesNo, NULL); +consvar_t cv_notifyrestat = CVAR_INIT ("notifyrestat", "Yes", CV_NETVAR, CV_YesNo, NULL); + // now automatically allocated in D_RegisterClientCommands // so that it doesn't have to be updated depending on the value of MAXPLAYERS char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; @@ -2736,6 +2740,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT8 kartspeed; UINT8 kartweight; + UINT8 kartspeedrestat; + UINT8 kartweightrestat; + boolean randomrestat; + boolean notifyrestat; boolean followerready; INT32 followerskin; @@ -2833,6 +2841,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) kartspeed = players[player].kartspeed; kartweight = players[player].kartweight; + kartspeedrestat = players[player].kartspeedrestat; + kartweightrestat = players[player].kartweightrestat; + randomrestat = players[player].randomrestat; + followerready = players[player].followerready; followercolor = players[player].followercolor; followerskin = players[player].followerskin; @@ -2858,7 +2870,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_FLIPCAM)); // SRB2kart - if (betweenmaps || leveltime <= starttime || spectator == true) + if (betweenmaps || leveltime <= starttime || spectator == true || players[player].jointime == 0) { itemroulette = 0; previtemroulette = 0; @@ -2884,7 +2896,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) exiting = 0; khudfinish = 0; khudcardanimation = 0; - starpostx =0; + starpostx = 0; starposty = 0; starpostz = 0; starpostangle = 0; @@ -2898,6 +2910,42 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) { laptime[i] = 0; } + + // might cause issues with weponpref sync? + if (!cv_allowrestat.value) + { + kartspeedrestat = 0; + kartweightrestat = 0; + randomrestat = false; + } + + if (randomrestat) + { + kartspeedrestat = P_RandomRange(1, 9); + kartweightrestat = P_RandomRange(1, 9); + } + + if (kartspeedrestat != 0 && kartweightrestat != 0) + { + kartspeed = kartspeedrestat; + kartweight = kartweightrestat; + + notifyrestat = ( + randomrestat || + kartspeedrestat != players[player].kartspeed || + kartweightrestat != players[player].kartweight + ) && cv_notifyrestat.value; + } + else + { + kartspeed = skins[players[player].skin].kartspeed; + kartweight = skins[players[player].skin].kartweight; + + notifyrestat = ( + players[player].kartspeed != skins[players[player].skin].kartspeed || + players[player].kartweight != skins[players[player].skin].kartweight + ) && cv_notifyrestat.value; + } } else { @@ -2990,6 +3038,38 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) else { follower = NULL; + + if (players[player].jointime > 0) + { + if (kartspeedrestat != 0 && kartweightrestat != 0) + { + if (playeringame[player] && notifyrestat) + { + if ((splitscreen && player == consoleplayer) || player != consoleplayer) + { + HU_AddChatText(va("%s is now %d speed, %d weight.", player_names[player], kartspeed, kartweight), true); + } + else + { + HU_AddChatText(va("You are now %d speed, %d weight.", kartspeed, kartweight), true); + } + } + } + else + { + if (playeringame[player] && notifyrestat) + { + if ((splitscreen && player == consoleplayer) || player != consoleplayer) + { + HU_AddChatText(va("%s is now using their skin's default stats.", player_names[player]), true); + } + else + { + HU_AddChatText(va("You are now using your skin's default stats."), true); + } + } + } + } } spectatorreentry = (betweenmaps ? 0 : players[player].spectatorreentry); @@ -3021,6 +3101,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->voice_id = voice; p->kartspeed = kartspeed; p->kartweight = kartweight; + + p->kartspeedrestat = kartspeedrestat; + p->kartweightrestat = kartweightrestat; + p->randomrestat = randomrestat; // p->charflags = charflags; memcpy(players[player].availabilities, availabilities, sizeof(availabilities)); diff --git a/src/g_game.h b/src/g_game.h index b1d9a28d8..64c04f360 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -96,6 +96,8 @@ extern consvar_t cv_resetspecialmusic; extern consvar_t cv_resume; +extern consvar_t cv_allowrestat, cv_notifyrestat; + void weaponPrefChange(void); void weaponPrefChange2(void); void weaponPrefChange3(void); diff --git a/src/p_saveg.c b/src/p_saveg.c index 57f7463b6..422446d38 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -555,6 +555,10 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(players[i].kartspeed); SYNC(players[i].kartweight); + SYNC(players[i].kartspeedrestat); + SYNC(players[i].kartweightrestat); + SYNC(players[i].randomrestat); + for (j = 0; j < MAXPREDICTTICS; j++) { SYNC(players[i].lturn_max[j]);