New kartgametypepreference cvar.

* A "canon" adaptation of the community-created server option `lessbattlevotes`.
* If set to "None", voting behaves as before.
* If set to "Race" or "Battle". that gametype is considered the preference.
    * The voting screen is always operated from the perspective of the preferred gametype.
    * If you're in an un-preferred gametype, the third vote option will always allow you to continue the gametype.
    * If the preferred gametype is Race and you've just exited a Battle map, Encore may now appear on the second vote option.
* A number of bugs with voting have been corrected.
    * If `kartencore` is on, the third vote option will now correctly have Encore applied.
    * If a custom EXE or malformed packet sends an Encore flag alongside a Battle gametype ID, actively strip it.
        * Just to note, clients do not enter Battle Encore with or without this change - this just prevents a promise the rest of the game couldn't fulfill.
This commit is contained in:
toaster 2022-05-27 23:16:02 +01:00
parent 7bf672a399
commit 7e7bd7dbb2
6 changed files with 72 additions and 32 deletions

View file

@ -370,6 +370,8 @@ consvar_t cv_kartcomeback = {"kartcomeback", "On", CV_NETVAR|CV_CHEAT|CV_CALL|CV
consvar_t cv_kartencore = {"kartencore", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartencore = {"kartencore", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}};
consvar_t cv_kartvoterulechanges = {"kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_kartvoterulechanges = {"kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartgametypepreference_cons_t[] = {{-1, "None"}, {GT_RACE, "Race"}, {GT_MATCH, "Battle"}, {0, NULL}};
consvar_t cv_kartgametypepreference = {"kartgametypepreference", "None", CV_NETVAR, kartgametypepreference_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Kilometers"}, {2, "Miles"}, {3, "Fracunits"}, {0, NULL}}; static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Kilometers"}, {2, "Miles"}, {3, "Fracunits"}, {0, NULL}};
consvar_t cv_kartspeedometer = {"kartdisplayspeed", "Off", CV_SAVE, kartspeedometer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display consvar_t cv_kartspeedometer = {"kartdisplayspeed", "Off", CV_SAVE, kartspeedometer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display
static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}};
@ -2397,13 +2399,14 @@ void D_SetupVote(void)
UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes
UINT8 *p = buf; UINT8 *p = buf;
INT32 i; INT32 i;
UINT8 secondgt = G_SometimesGetDifferentGametype(); UINT8 gt = (cv_kartgametypepreference.value == -1) ? gametype : cv_kartgametypepreference.value;
UINT8 secondgt = G_SometimesGetDifferentGametype(gt);
INT16 votebuffer[4] = {-1,-1,-1,0}; INT16 votebuffer[4] = {-1,-1,-1,0};
if (cv_kartencore.value && G_RaceGametype()) if (cv_kartencore.value && gt == GT_RACE)
WRITEUINT8(p, (gametype|0x80)); WRITEUINT8(p, (gt|0x80));
else else
WRITEUINT8(p, gametype); WRITEUINT8(p, gt);
WRITEUINT8(p, secondgt); WRITEUINT8(p, secondgt);
secondgt &= ~0x80; secondgt &= ~0x80;
@ -2413,9 +2416,9 @@ void D_SetupVote(void)
if (i == 2) // sometimes a different gametype if (i == 2) // sometimes a different gametype
m = G_RandMap(G_TOLFlag(secondgt), prevmap, false, 0, true, votebuffer); m = G_RandMap(G_TOLFlag(secondgt), prevmap, false, 0, true, votebuffer);
else if (i >= 3) // unknown-random and force-unknown MAP HELL else if (i >= 3) // unknown-random and force-unknown MAP HELL
m = G_RandMap(G_TOLFlag(gametype), prevmap, false, (i-2), (i < 4), votebuffer); m = G_RandMap(G_TOLFlag(gt), prevmap, false, (i-2), (i < 4), votebuffer);
else else
m = G_RandMap(G_TOLFlag(gametype), prevmap, false, 0, true, votebuffer); m = G_RandMap(G_TOLFlag(gt), prevmap, false, 0, true, votebuffer);
if (i < 3) if (i < 3)
votebuffer[min(i, 2)] = m; // min() is a dumb workaround for gcc 4.4 array-bounds error votebuffer[min(i, 2)] = m; // min() is a dumb workaround for gcc 4.4 array-bounds error
WRITEUINT16(p, m); WRITEUINT16(p, m);
@ -5136,9 +5139,17 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
return; return;
} }
// Get gametype data.
gt = (UINT8)READUINT8(*cp); gt = (UINT8)READUINT8(*cp);
secondgt = (UINT8)READUINT8(*cp); secondgt = (UINT8)READUINT8(*cp);
// Strip illegal Encore flag.
if (gt == (GT_MATCH|0x80))
{
gt &= ~0x80;
}
// Apply most data.
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
{ {
votelevels[i][0] = (UINT16)READUINT16(*cp); votelevels[i][0] = (UINT16)READUINT16(*cp);
@ -5147,8 +5158,20 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum)
P_AllocMapHeader(votelevels[i][0]); P_AllocMapHeader(votelevels[i][0]);
} }
// Correct third entry's gametype/Encore status.
votelevels[2][1] = secondgt; votelevels[2][1] = secondgt;
// If third entry has an illelegal Encore flag...
if (secondgt == (GT_MATCH|0x80))
{
votelevels[2][1] &= ~0x80;
// Apply it to the second entry instead, gametype permitting!
if (gt != GT_MATCH)
{
votelevels[1][1] |= 0x80;
}
}
G_SetGamestate(GS_VOTING); G_SetGamestate(GS_VOTING);
Y_StartVote(); Y_StartVote();
} }

View file

@ -118,6 +118,7 @@ extern consvar_t cv_kartfrantic;
extern consvar_t cv_kartcomeback; extern consvar_t cv_kartcomeback;
extern consvar_t cv_kartencore; extern consvar_t cv_kartencore;
extern consvar_t cv_kartvoterulechanges; extern consvar_t cv_kartvoterulechanges;
extern consvar_t cv_kartgametypepreference;
extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartspeedometer;
extern consvar_t cv_kartvoices; extern consvar_t cv_kartvoices;

View file

@ -3401,35 +3401,47 @@ boolean G_BattleGametype(void)
// //
// Oh, yeah, and we sometimes flip encore mode on here too. // Oh, yeah, and we sometimes flip encore mode on here too.
// //
INT16 G_SometimesGetDifferentGametype(void) UINT8 G_SometimesGetDifferentGametype(UINT8 prefgametype)
{ {
boolean encorepossible = (M_SecretUnlocked(SECRET_ENCORE) && G_RaceGametype()); // Most of the gametype references in this condition are intentionally not prefgametype.
// This is so a server CAN continue playing a gametype if they like the taste of it.
// The encore check needs prefgametype so can't use G_RaceGametype...
boolean encorepossible = (M_SecretUnlocked(SECRET_ENCORE)
&& (gametype == GT_RACE || prefgametype == GT_RACE));
boolean encoreactual = false;
UINT8 encoremodifier = 0;
if (!cv_kartvoterulechanges.value) // never
return gametype;
if (randmapbuffer[NUMMAPS] > 0 && (encorepossible || cv_kartvoterulechanges.value != 3))
{
randmapbuffer[NUMMAPS]--;
if (encorepossible) if (encorepossible)
{ {
switch (cv_kartvoterulechanges.value) switch (cv_kartvoterulechanges.value)
{ {
case 3: // always case 3: // always
randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set encoreactual = true;
break; break;
case 2: // frequent case 2: // frequent
encorepossible = M_RandomChance(FRACUNIT>>1); encoreactual = M_RandomChance(FRACUNIT>>1);
break; break;
case 1: // sometimes case 1: // sometimes
encoreactual = M_RandomChance(FRACUNIT>>2);
break;
default: default:
encorepossible = M_RandomChance(FRACUNIT>>2);
break; break;
} }
if (encorepossible != (boolean)cv_kartencore.value) if (encoreactual != (boolean)cv_kartencore.value)
return (gametype|0x80); encoremodifier = 0x80;
} }
return gametype;
if (!cv_kartvoterulechanges.value) // never
return (gametype|encoremodifier);
if (randmapbuffer[NUMMAPS] > 0 && (encorepossible || cv_kartvoterulechanges.value != 3))
{
randmapbuffer[NUMMAPS]--;
if (cv_kartvoterulechanges.value == 3) // always
{
randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set
}
return (gametype|encoremodifier);
} }
switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv? switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv?
@ -3447,9 +3459,11 @@ INT16 G_SometimesGetDifferentGametype(void)
break; break;
} }
if (gametype == GT_MATCH) // Only this response is prefgametype-based.
return GT_RACE; // Also intentionally does not use encoremodifier!
return GT_MATCH; if (prefgametype == GT_MATCH)
return (GT_RACE);
return (GT_MATCH);
} }
// //

View file

@ -290,7 +290,7 @@ boolean G_GametypeUsesLives(void);
boolean G_GametypeHasTeams(void); boolean G_GametypeHasTeams(void);
boolean G_GametypeHasSpectators(void); boolean G_GametypeHasSpectators(void);
boolean G_BattleGametype(void); boolean G_BattleGametype(void);
INT16 G_SometimesGetDifferentGametype(void); UINT8 G_SometimesGetDifferentGametype(UINT8 prefgametype);
UINT8 G_GetGametypeColor(INT16 gt); UINT8 G_GetGametypeColor(INT16 gt);
boolean G_RaceGametype(void); boolean G_RaceGametype(void);
boolean G_TagGametype(void); boolean G_TagGametype(void);

View file

@ -575,6 +575,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartcomeback); CV_RegisterVar(&cv_kartcomeback);
CV_RegisterVar(&cv_kartencore); CV_RegisterVar(&cv_kartencore);
CV_RegisterVar(&cv_kartvoterulechanges); CV_RegisterVar(&cv_kartvoterulechanges);
CV_RegisterVar(&cv_kartgametypepreference);
CV_RegisterVar(&cv_kartspeedometer); CV_RegisterVar(&cv_kartspeedometer);
CV_RegisterVar(&cv_kartvoices); CV_RegisterVar(&cv_kartvoices);
CV_RegisterVar(&cv_karteliminatelast); CV_RegisterVar(&cv_karteliminatelast);

View file

@ -1445,6 +1445,7 @@ void Y_VoteTicker(void)
void Y_StartVote(void) void Y_StartVote(void)
{ {
INT32 i = 0; INT32 i = 0;
UINT8 prefgametype = (votelevels[0][1] & ~0x80);
votetic = -1; votetic = -1;
@ -1453,8 +1454,8 @@ void Y_StartVote(void)
I_Error("voteendtic is dirty"); I_Error("voteendtic is dirty");
#endif #endif
widebgpatch = W_CachePatchName(((gametype == GT_MATCH) ? "BATTLSCW" : "INTERSCW"), PU_STATIC); widebgpatch = W_CachePatchName(((prefgametype == GT_MATCH) ? "BATTLSCW" : "INTERSCW"), PU_STATIC);
bgpatch = W_CachePatchName(((gametype == GT_MATCH) ? "BATTLSCR" : "INTERSCR"), PU_STATIC); bgpatch = W_CachePatchName(((prefgametype == GT_MATCH) ? "BATTLSCR" : "INTERSCR"), PU_STATIC);
cursor = W_CachePatchName("M_CURSOR", PU_STATIC); cursor = W_CachePatchName("M_CURSOR", PU_STATIC);
cursor1 = W_CachePatchName("P1CURSOR", PU_STATIC); cursor1 = W_CachePatchName("P1CURSOR", PU_STATIC);
cursor2 = W_CachePatchName("P2CURSOR", PU_STATIC); cursor2 = W_CachePatchName("P2CURSOR", PU_STATIC);