Port basics of roundqueue
This commit is contained in:
parent
266453f15a
commit
24373da780
18 changed files with 598 additions and 282 deletions
|
|
@ -6171,7 +6171,7 @@ boolean TryRunTics(tic_t realtics)
|
|||
{
|
||||
COM_BufTicker();
|
||||
if (mapchangepending)
|
||||
D_MapChange(-1, 0, encoremode, false, 2, false, fromlevelselect); // finish the map change
|
||||
D_MapChange(-1, 0, encoremode, false, 2, false, forcespecialstage); // finish the map change
|
||||
|
||||
// fun fact: this used to be located in NetUpdate!
|
||||
// as it turns out, reloading the level in rendering code is a very bad idea!
|
||||
|
|
|
|||
|
|
@ -1200,8 +1200,9 @@ void D_StartTitle(void)
|
|||
modeattacking = ATTACKING_NONE;
|
||||
marathonmode = static_cast<marathonmode_t>(0);
|
||||
|
||||
// Reset GP
|
||||
// Reset GP and roundqueue
|
||||
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
|
||||
// Reset Server mods
|
||||
numcustomservermods = 0;
|
||||
|
|
@ -1901,14 +1902,13 @@ void D_SRB2Main(void)
|
|||
|
||||
// Start up a "minor" grand prix session
|
||||
memset(&grandprixinfo, 0, sizeof(struct grandprixinfo));
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
|
||||
grandprixinfo.gamespeed = KARTSPEED_HARD;
|
||||
grandprixinfo.encore = false;
|
||||
grandprixinfo.masterbots = false;
|
||||
grandprixinfo.lunaticmode = false;
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.roundnum = 0;
|
||||
grandprixinfo.cup = NULL;
|
||||
grandprixinfo.wonround = false;
|
||||
|
||||
|
|
|
|||
201
src/d_netcmd.c
201
src/d_netcmd.c
|
|
@ -874,6 +874,7 @@ INT16 numgametypes = GT_FIRSTFREESLOT;
|
|||
|
||||
boolean forceresetplayers = false;
|
||||
boolean deferencoremode = false;
|
||||
boolean forcespecialstage = false;
|
||||
UINT8 splitscreen = 0;
|
||||
boolean circuitmap = false;
|
||||
INT32 adminplayers[MAXPLAYERS] = {};
|
||||
|
|
@ -893,7 +894,9 @@ const char *automate_names[AEV__MAX] =
|
|||
{
|
||||
"RoundStart", // AEV_ROUNDSTART
|
||||
"IntermissionStart", // AEV_INTERMISSIONSTART
|
||||
"VoteStart" // AEV_VOTESTART
|
||||
"VoteStart", // AEV_VOTESTART
|
||||
"QueueStart", // AEV_QUEUESTART
|
||||
"QueueEnd", // AEV_QUEUEEND
|
||||
};
|
||||
|
||||
/// \warning Keep this up-to-date if you add/remove/rename net text commands
|
||||
|
|
@ -3317,21 +3320,21 @@ INT32 mapchangepending = 0;
|
|||
* ESC to abort", which calls I_GetKey(), which adds an event. In other words,
|
||||
* 63 old events will get reexecuted, with ridiculous results. Just don't do
|
||||
* it (without setting delay to 1, which is the current solution).
|
||||
*
|
||||
* \param mapnum Map number to change to.
|
||||
* \param gametype Gametype to switch to.
|
||||
* \param pencoremode Is this 'Encore Mode'?
|
||||
* \param resetplayers 1 to reset player scores and lives and such, 0 not to.
|
||||
* \param delay Determines how the function will be executed: 0 to do
|
||||
* it all right now (must not be done from a menu), 1 to
|
||||
* do step one and prepare step two, 2 to do step two.
|
||||
* \param skipprecutscene To skip the precutscence or not?
|
||||
* \param mapnum Map number to change to.
|
||||
* \param gametype Gametype to switch to.
|
||||
* \param pencoremode Is this 'Encore Mode'?
|
||||
* \param presetplayers 1 to reset player scores and lives and such, 0 not to.
|
||||
* \param delay Determines how the function will be executed: 0 to do
|
||||
* it all right now (must not be done from a menu), 1 to
|
||||
* do step one and prepare step two, 2 to do step two.
|
||||
* \param skipprecutscene To skip the precutscence or not?
|
||||
* \param pforcespecialstage For certain contexts, forces a special stage.
|
||||
* \sa D_GameTypeChanged, Command_Map_f
|
||||
* \author Graue <graue@oceanbase.org>
|
||||
*/
|
||||
void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean resetplayers, INT32 delay, boolean skipprecutscene, boolean FLS)
|
||||
void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean presetplayers, INT32 delay, boolean skipprecutscene, boolean pforcespecialstage)
|
||||
{
|
||||
static char buf[2+MAX_WADPATH+1+4];
|
||||
static char buf[1+1+1+1+1+2+4];
|
||||
static char *buf_p = buf;
|
||||
// The supplied data are assumed to be good.
|
||||
I_Assert(delay >= 0 && delay <= 2);
|
||||
|
|
@ -3341,25 +3344,8 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
|
|||
CV_SetValue(&cv_nextmap, mapnum);
|
||||
}
|
||||
|
||||
CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d pencoremode=%d resetplayers=%d delay=%d skipprecutscene=%d\n",
|
||||
mapnum, newgametype, pencoremode, resetplayers, delay, skipprecutscene);
|
||||
|
||||
if ((netgame || multiplayer) && (grandprixinfo.gp != false))
|
||||
FLS = false;
|
||||
|
||||
// Too lazy to change the input value for every instance of this function.......
|
||||
if (bossinfo.boss == true)
|
||||
{
|
||||
pencoremode = bossinfo.encore;
|
||||
}
|
||||
else if (specialStage.active == true)
|
||||
{
|
||||
pencoremode = specialStage.encore;
|
||||
}
|
||||
else if (grandprixinfo.gp == true)
|
||||
{
|
||||
pencoremode = grandprixinfo.encore;
|
||||
}
|
||||
CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d pencoremode=%d presetplayers=%d delay=%d skipprecutscene=%d pforcespecialstage = %d\n",
|
||||
mapnum, newgametype, pencoremode, presetplayers, delay, skipprecutscene, pforcespecialstage);
|
||||
|
||||
if (delay != 2)
|
||||
{
|
||||
|
|
@ -3368,14 +3354,25 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
|
|||
buf_p = buf;
|
||||
if (pencoremode)
|
||||
flags |= 1;
|
||||
if (!resetplayers)
|
||||
if (presetplayers)
|
||||
flags |= 1<<1;
|
||||
if (skipprecutscene)
|
||||
flags |= 1<<2;
|
||||
if (FLS)
|
||||
if (pforcespecialstage)
|
||||
flags |= 1<<3;
|
||||
if (roundqueue.netcommunicate)
|
||||
flags |= 1<<4;
|
||||
WRITEUINT8(buf_p, flags);
|
||||
|
||||
if (roundqueue.netcommunicate)
|
||||
{
|
||||
// roundqueue state
|
||||
WRITEUINT8(buf_p, roundqueue.position);
|
||||
WRITEUINT8(buf_p, roundqueue.size);
|
||||
WRITEUINT8(buf_p, roundqueue.roundnum);
|
||||
roundqueue.netcommunicate = false;
|
||||
}
|
||||
|
||||
// new gametype value
|
||||
WRITEUINT8(buf_p, newgametype);
|
||||
|
||||
|
|
@ -3574,6 +3571,7 @@ static void Command_Map_f(void)
|
|||
size_t option_skill;
|
||||
const char *gametypename;
|
||||
boolean newresetplayers;
|
||||
boolean newforcespecialstage;
|
||||
|
||||
boolean mustmodifygame;
|
||||
|
||||
|
|
@ -3598,6 +3596,7 @@ static void Command_Map_f(void)
|
|||
option_encore = COM_CheckPartialParm("-e");
|
||||
option_skill = COM_CheckPartialParm("-s");
|
||||
newresetplayers = ! COM_CheckParm("-noresetplayers");
|
||||
newforcespecialstage = COM_CheckParm("-forcespecialstage");
|
||||
|
||||
mustmodifygame = !(netgame || multiplayer) && !majormods;
|
||||
|
||||
|
|
@ -3727,7 +3726,7 @@ static void Command_Map_f(void)
|
|||
// don't use a gametype the map doesn't support
|
||||
if (cht_debug || option_force || cv_skipmapcheck.value)
|
||||
{
|
||||
fromlevelselect = false; // The player wants us to trek on anyway. Do so.
|
||||
// The player wants us to trek on anyway. Do so.
|
||||
}
|
||||
// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
|
||||
else
|
||||
|
|
@ -3751,12 +3750,6 @@ static void Command_Map_f(void)
|
|||
Z_Free(mapname);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
fromlevelselect =
|
||||
( netgame || multiplayer ) &&
|
||||
grandprixinfo.gp != false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(netgame || multiplayer))
|
||||
|
|
@ -3845,36 +3838,41 @@ static void Command_Map_f(void)
|
|||
}
|
||||
}
|
||||
|
||||
grandprixinfo.encore = newencoremode;
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.roundnum = 0;
|
||||
grandprixinfo.cup = NULL;
|
||||
grandprixinfo.wonround = false;
|
||||
|
||||
bossinfo.boss = false;
|
||||
specialStage.active = false;
|
||||
|
||||
grandprixinfo.initalize = true;
|
||||
if (!Playing())
|
||||
{
|
||||
UINT8 ssplayers = cv_splitplayers.value-1;
|
||||
|
||||
grandprixinfo.cup = NULL;
|
||||
grandprixinfo.initalize = true;
|
||||
|
||||
multiplayer = true;
|
||||
|
||||
CopyCaretColors(connectedservername, cv_servername.string, MAXSERVERNAME);
|
||||
CopyCaretColors(connectedservercontact, cv_server_contact.string, MAXSERVERCONTACT);
|
||||
strncpy(connectedserverdescription, DEFAULTDESCSTRING, MAXSERVERDESCRIPTION);
|
||||
|
||||
if (cv_maxplayers.value < ssplayers+1)
|
||||
CV_SetValue(&cv_maxplayers, ssplayers+1);
|
||||
|
||||
if (splitscreen != ssplayers)
|
||||
{
|
||||
splitscreen = ssplayers;
|
||||
SplitScreen_OnChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent warping to locked levels
|
||||
// ... unless you're in a dedicated server. Yes, technically this means you can view any level by
|
||||
// running a dedicated server and joining it yourself, but that's better than making dedicated server's
|
||||
// lives hell.
|
||||
if (!dedicated && M_MapLocked(newmapnum))
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
|
||||
Z_Free(realmapname);
|
||||
Z_Free(mapname);
|
||||
return;
|
||||
}
|
||||
|
||||
tutorialmode = false; // warping takes us out of tutorial mode
|
||||
|
||||
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, fromlevelselect);
|
||||
D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, newforcespecialstage);
|
||||
|
||||
Z_Free(realmapname);
|
||||
}
|
||||
|
|
@ -3889,9 +3887,9 @@ static void Command_Map_f(void)
|
|||
static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
|
||||
{
|
||||
UINT8 flags;
|
||||
INT32 resetplayer = 1, lastgametype;
|
||||
UINT8 skipprecutscene, FLS;
|
||||
boolean pencoremode;
|
||||
INT32 presetplayer = 1, lastgametype;
|
||||
UINT8 skipprecutscene, pforcespecialstage;
|
||||
boolean pencoremode, hasroundqueuedata;
|
||||
INT16 mapnumber;
|
||||
|
||||
forceresetplayers = deferencoremode = false;
|
||||
|
|
@ -3904,14 +3902,53 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
|
|||
return;
|
||||
}
|
||||
|
||||
if (chmappending)
|
||||
chmappending--;
|
||||
|
||||
flags = READUINT8(*cp);
|
||||
|
||||
pencoremode = ((flags & 1) != 0);
|
||||
|
||||
resetplayer = ((flags & (1<<1)) == 0);
|
||||
presetplayer = ((flags & (1<<1)) != 0);
|
||||
|
||||
skipprecutscene = ((flags & (1<<2)) != 0);
|
||||
|
||||
pforcespecialstage = ((flags & (1<<3)) != 0);
|
||||
|
||||
hasroundqueuedata = ((flags & (1<<4)) != 0);
|
||||
|
||||
if (hasroundqueuedata)
|
||||
{
|
||||
UINT8 position = READUINT8(*cp);
|
||||
UINT8 size = READUINT8(*cp);
|
||||
UINT8 roundnum = READUINT8(*cp);
|
||||
|
||||
if (playernum != serverplayer // Clients, even admin clients, don't have full roundqueue data
|
||||
|| position > size // Sanity check A (intentionally not a >= comparison)
|
||||
|| size > ROUNDQUEUE_MAX) // Sanity Check B (ditto)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal round-queue data received from %s\n"), player_names[playernum]);
|
||||
if (server && playernum != serverplayer)
|
||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
roundqueue.position = position;
|
||||
if (size < roundqueue.size)
|
||||
{
|
||||
// Bafooliganism afoot - set to zero if the size is zero! ~toast 150523
|
||||
roundqueue.size = size;
|
||||
}
|
||||
else while (roundqueue.size < size)
|
||||
{
|
||||
// We wipe rather than provide full data to prevent bloating the packet,
|
||||
// and because only this data is necessary for sync. ~toast 100423
|
||||
memset(&roundqueue.entries[roundqueue.size], 0, sizeof(roundentry_t));
|
||||
roundqueue.size++;
|
||||
}
|
||||
roundqueue.roundnum = roundnum; // no sanity checking required, server is authoriative
|
||||
}
|
||||
|
||||
// No more kicks below this line, we can now start modifying state beyond this function.
|
||||
if (chmappending)
|
||||
chmappending--;
|
||||
|
||||
lastgametype = gametype;
|
||||
gametype = READUINT8(*cp);
|
||||
|
|
@ -3922,20 +3959,32 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
|
|||
else if (gametype != lastgametype)
|
||||
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
|
||||
|
||||
if (hasroundqueuedata && roundqueue.position > 0 && roundqueue.size > 0)
|
||||
{
|
||||
// ...we can evaluate CURRENT specifics for roundqueue data, though.
|
||||
roundqueue.entries[roundqueue.position-1].gametype = gametype;
|
||||
roundqueue.entries[roundqueue.position-1].encore = pencoremode;
|
||||
}
|
||||
|
||||
if (!(gametypes[gametype]->rules & GTR_ENCORE) && !bossinfo.boss)
|
||||
pencoremode = false;
|
||||
|
||||
skipprecutscene = ((flags & (1<<2)) != 0);
|
||||
|
||||
FLS = ((flags & (1<<3)) != 0);
|
||||
|
||||
mapnumber = READINT16(*cp);
|
||||
|
||||
// Handle some Grand Prix state.
|
||||
if (grandprixinfo.gp)
|
||||
{
|
||||
if (gametype != GT_RACE)
|
||||
boolean caughtretry = (gametype == lastgametype
|
||||
&& mapnumber == gamemap);
|
||||
if (pforcespecialstage // Forced.
|
||||
|| (caughtretry && grandprixinfo.eventmode == GPEVENT_SPECIAL) // Catch retries of forced.
|
||||
/*|| (roundqueue.size == 0 && (gametyperules & (GTR_BOSS|GTR_CATCHER)))*/) // Force convention for the (queue)map command.
|
||||
{
|
||||
grandprixinfo.eventmode = GPEVENT_CHALLENGE;
|
||||
grandprixinfo.eventmode = GPEVENT_SPECIAL;
|
||||
}
|
||||
else if (gametype != GT_RACE)
|
||||
{
|
||||
grandprixinfo.eventmode = GPEVENT_BONUS;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -3949,16 +3998,10 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
|
|||
if (!skipprecutscene)
|
||||
{
|
||||
DEBFILE(va("Warping to %s [resetplayer=%d lastgametype=%d gametype=%d cpnd=%d]\n",
|
||||
G_BuildMapName(mapnumber), resetplayer, lastgametype, gametype, chmappending));
|
||||
G_BuildMapName(mapnumber), presetplayer, lastgametype, gametype, chmappending));
|
||||
CON_LogMessage(M_GetText("Speeding off to level...\n"));
|
||||
}
|
||||
|
||||
if (resetplayer && !FLS)
|
||||
{
|
||||
emeralds = 0;
|
||||
memset(&luabanks, 0, sizeof(luabanks));
|
||||
}
|
||||
|
||||
demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING;
|
||||
demo.savebutton = 0;
|
||||
|
||||
|
|
@ -3966,7 +4009,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
|
|||
if (demo.recording && !modeattacking)
|
||||
G_CheckDemoStatus();
|
||||
|
||||
G_InitNew(pencoremode, mapnumber, resetplayer, skipprecutscene, FLS);
|
||||
G_InitNew(pencoremode, mapnumber, presetplayer, skipprecutscene);
|
||||
|
||||
if (demo.timing)
|
||||
G_DoneLevelLoad();
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ void D_SendPlayerConfig(UINT8 n);
|
|||
void Command_ExitGame_f(void);
|
||||
void Command_Retry_f(void);
|
||||
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
|
||||
void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect);
|
||||
void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage);
|
||||
void D_SetupVote(void);
|
||||
void D_ModifyClientVote(UINT8 player, SINT8 voted);
|
||||
void D_PickVote(void);
|
||||
|
|
@ -438,6 +438,8 @@ typedef enum
|
|||
AEV_ROUNDSTART,
|
||||
AEV_INTERMISSIONSTART,
|
||||
AEV_VOTESTART,
|
||||
AEV_QUEUESTART,
|
||||
AEV_QUEUEEND,
|
||||
AEV__MAX
|
||||
} automateEvents_t;
|
||||
|
||||
|
|
|
|||
|
|
@ -3722,6 +3722,41 @@ void readmaincfg(MYFILE *f)
|
|||
Z_Free(s);
|
||||
}
|
||||
|
||||
//
|
||||
// SRB2KART
|
||||
//
|
||||
|
||||
static void invalidateacrosscups(UINT16 map)
|
||||
{
|
||||
cupheader_t *cup = kartcupheaders;
|
||||
UINT8 i;
|
||||
|
||||
if (map >= nummapheaders)
|
||||
return;
|
||||
|
||||
while (cup)
|
||||
{
|
||||
for (i = 0; i < CUPCACHE_MAX; i++)
|
||||
{
|
||||
if (cup->cachedlevels[i] != map)
|
||||
continue;
|
||||
cup->cachedlevels[i] = NEXTMAP_INVALID;
|
||||
}
|
||||
cup = cup->next;
|
||||
}
|
||||
|
||||
mapheaderinfo[map]->cup = NULL;
|
||||
}
|
||||
|
||||
static char *MapNameOrRemoval(char *name)
|
||||
{
|
||||
if (name[0] == '\0'
|
||||
|| (name[0] == '/' && name[1] == '\0'))
|
||||
return NULL;
|
||||
|
||||
return Z_StrDup(name);
|
||||
}
|
||||
|
||||
void readwipes(MYFILE *f)
|
||||
{
|
||||
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
|
|
@ -3931,7 +3966,7 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
|
|||
cup->levellist[cup->numlevels] = NULL;
|
||||
if (cup->cachedlevels[cup->numlevels] == NEXTMAP_INVALID)
|
||||
continue;
|
||||
mapheaderinfo[cup->cachedlevels[cup->numlevels]]->cup = NULL;
|
||||
invalidateacrosscups(cup->cachedlevels[cup->numlevels]);
|
||||
}
|
||||
|
||||
tmp = strtok(word2,",");
|
||||
|
|
@ -3949,9 +3984,32 @@ void readcupheader(MYFILE *f, cupheader_t *cup)
|
|||
}
|
||||
else if (fastcmp(word, "BONUSGAME"))
|
||||
{
|
||||
Z_Free(cup->levellist[CUPCACHE_BONUS]);
|
||||
cup->levellist[CUPCACHE_BONUS] = Z_StrDup(word2);
|
||||
cup->cachedlevels[CUPCACHE_BONUS] = NEXTMAP_INVALID;
|
||||
while (cup->numbonus > 0)
|
||||
{
|
||||
cup->numbonus--;
|
||||
Z_Free(cup->levellist[CUPCACHE_BONUS + cup->numbonus]);
|
||||
cup->levellist[CUPCACHE_BONUS + cup->numbonus] = NULL;
|
||||
if (cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus] == NEXTMAP_INVALID)
|
||||
continue;
|
||||
invalidateacrosscups(cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus]);
|
||||
}
|
||||
|
||||
tmp = strtok(word2,",");
|
||||
do {
|
||||
if (cup->numbonus >= MAXBONUSLIST)
|
||||
{
|
||||
deh_warning("%s Cup: reached max bonus list (%d)\n", cup->name, MAXBONUSLIST);
|
||||
break;
|
||||
}
|
||||
|
||||
cup->levellist[CUPCACHE_BONUS + cup->numbonus] = MapNameOrRemoval(tmp);
|
||||
cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus] = NEXTMAP_INVALID;
|
||||
|
||||
if (cup->levellist[CUPCACHE_BONUS + cup->numbonus] == NULL)
|
||||
break;
|
||||
|
||||
cup->numbonus++;
|
||||
} while((tmp = strtok(NULL,",")) != NULL);
|
||||
}
|
||||
else if (fastcmp(word, "SPECIALSTAGE"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -152,8 +152,7 @@ extern UINT8 splitscreen;
|
|||
extern int r_splitscreen;
|
||||
|
||||
extern boolean circuitmap; // Does this level have 'circuit mode'?
|
||||
extern boolean fromlevelselect;
|
||||
extern boolean forceresetplayers, deferencoremode;
|
||||
extern boolean forceresetplayers, deferencoremode, forcespecialstage;
|
||||
|
||||
// ========================================
|
||||
// Internal parameters for sound rendering.
|
||||
|
|
@ -330,8 +329,10 @@ struct mapheader_lighting_t
|
|||
// Keep in mind that it may encourage people making overly long cups just because they "can", and would be a waste of memory.
|
||||
#define MAXLEVELLIST 5
|
||||
#define CUPCACHE_BONUS MAXLEVELLIST
|
||||
#define CUPCACHE_SPECIAL MAXLEVELLIST+1
|
||||
#define CUPCACHE_MAX CUPCACHE_SPECIAL+1
|
||||
#define MAXBONUSLIST 2
|
||||
#define CUPCACHE_SPECIAL (CUPCACHE_BONUS+MAXBONUSLIST)
|
||||
#define CUPCACHE_PODIUM (CUPCACHE_SPECIAL+1)
|
||||
#define CUPCACHE_MAX (CUPCACHE_PODIUM+1)
|
||||
|
||||
#define MAXCUPNAME 16 // includes \0, for cleaner savedata
|
||||
|
||||
|
|
@ -348,6 +349,7 @@ struct cupheader_t
|
|||
char *levellist[CUPCACHE_MAX]; ///< List of levels that belong to this cup
|
||||
INT16 cachedlevels[CUPCACHE_MAX]; ///< IDs in levellist, bonusgame, and specialstage
|
||||
UINT8 numlevels; ///< Number of levels defined in levellist
|
||||
UINT8 numbonus; ///< Number of bonus stages defined
|
||||
UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald)
|
||||
SINT8 unlockrequired; ///< An unlockable is required to select this cup. -1 for no unlocking required.
|
||||
cupheader_t *next; ///< Next cup in linked list
|
||||
|
|
|
|||
|
|
@ -4001,7 +4001,7 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
R_ExecuteSetViewSize();
|
||||
|
||||
P_SetRandSeed(header.randseed);
|
||||
G_InitNew(demoflags & DF_ENCORE, gamemap, true, true, false); // Doesn't matter whether you reset or not here, given changes to resetplayer.
|
||||
G_InitNew(demoflags & DF_ENCORE, gamemap, true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer.
|
||||
|
||||
// Setup server name, contact and description.
|
||||
CopyCaretColors(connectedservername, header.servername, MAXSERVERNAME);
|
||||
|
|
|
|||
406
src/g_game.c
406
src/g_game.c
|
|
@ -2135,6 +2135,11 @@ void G_DoLoadLevel(boolean resetplayer)
|
|||
|
||||
if (doAutomate == true)
|
||||
{
|
||||
if (roundqueue.size > 0 && roundqueue.position == 1)
|
||||
{
|
||||
Automate_Run(AEV_QUEUESTART);
|
||||
}
|
||||
|
||||
Automate_Run(AEV_ROUNDSTART);
|
||||
}
|
||||
}
|
||||
|
|
@ -4197,6 +4202,25 @@ INT32 G_GetGametypeByName(const char *gametypestr)
|
|||
return -1; // unknown gametype
|
||||
}
|
||||
|
||||
//
|
||||
// G_GuessGametypeByTOL
|
||||
//
|
||||
// Returns the first valid number for the given typeoflevel, or -1 if not valid.
|
||||
//
|
||||
INT32 G_GuessGametypeByTOL(UINT32 tol)
|
||||
{
|
||||
INT32 i = 0;
|
||||
|
||||
while (gametypes[i] != NULL)
|
||||
{
|
||||
if (tol & gametypes[i]->tol)
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
|
||||
return -1; // unknown gametype
|
||||
}
|
||||
|
||||
//
|
||||
// G_SetGametype
|
||||
//
|
||||
|
|
@ -4857,10 +4881,126 @@ static void G_HandleSaveLevel(void)
|
|||
G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages
|
||||
}
|
||||
|
||||
static INT16 G_GetNextMap(boolean advancemap)
|
||||
// Next map apparatus
|
||||
struct roundqueue roundqueue;
|
||||
|
||||
void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted)
|
||||
{
|
||||
I_Assert(position < ROUNDQUEUE_MAX);
|
||||
|
||||
roundqueue.entries[position].mapnum = map;
|
||||
roundqueue.entries[position].gametype = setgametype;
|
||||
roundqueue.entries[position].encore = setencore;
|
||||
roundqueue.entries[position].rankrestricted = rankrestricted;
|
||||
}
|
||||
|
||||
void G_MapIntoRoundQueue(UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted)
|
||||
{
|
||||
if (roundqueue.size >= ROUNDQUEUE_MAX)
|
||||
{
|
||||
CONS_Alert(CONS_ERROR, "G_MapIntoRoundQueue: Unable to add map beyond %u\n", roundqueue.size);
|
||||
return;
|
||||
}
|
||||
|
||||
G_MapSlipIntoRoundQueue(roundqueue.size, map, setgametype, setencore, rankrestricted);
|
||||
|
||||
roundqueue.size++;
|
||||
}
|
||||
|
||||
void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencore)
|
||||
{
|
||||
UINT8 i, levelindex = 0, bonusindex = 0;
|
||||
UINT8 bonusmodulo = max(1, (cup->numlevels+1)/(cup->numbonus+1));
|
||||
UINT16 cupLevelNum;
|
||||
|
||||
// Levels are added to the queue in the following pattern.
|
||||
// For 5 Race rounds and 2 Bonus rounds, the most common case:
|
||||
// race - race - BONUS - race - race - BONUS - race
|
||||
// The system is flexible enough to permit other arrangements.
|
||||
// However, we just want to keep the pacing even & consistent.
|
||||
while (levelindex < cup->numlevels)
|
||||
{
|
||||
// Fill like two or three Race maps.
|
||||
for (i = 0; i < bonusmodulo; i++)
|
||||
{
|
||||
cupLevelNum = cup->cachedlevels[levelindex];
|
||||
|
||||
if (cupLevelNum >= nummapheaders)
|
||||
{
|
||||
// For invalid Race maps, we keep the pacing by going to TEST RUN.
|
||||
// It transparently lets the user know something is wrong.
|
||||
cupLevelNum = 0;
|
||||
}
|
||||
|
||||
G_MapIntoRoundQueue(
|
||||
cupLevelNum,
|
||||
setgametype,
|
||||
setencore, // *probably* correct
|
||||
false
|
||||
);
|
||||
|
||||
levelindex++;
|
||||
if (levelindex >= cup->numlevels)
|
||||
break;
|
||||
}
|
||||
|
||||
// Attempt to add an interstitial Bonus round.
|
||||
if (levelindex < cup->numlevels
|
||||
&& bonusindex < cup->numbonus)
|
||||
{
|
||||
cupLevelNum = cup->cachedlevels[CUPCACHE_BONUS + bonusindex];
|
||||
|
||||
if (cupLevelNum < nummapheaders)
|
||||
{
|
||||
// In the case of Bonus rounds, we simply skip invalid maps.
|
||||
G_MapIntoRoundQueue(
|
||||
cupLevelNum,
|
||||
G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel),
|
||||
setencore, // if this isn't correct, Got_Mapcmd will fix it
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
bonusindex++;
|
||||
}
|
||||
}
|
||||
|
||||
// ...but there's one last trick up our sleeves.
|
||||
// At the end of the Cup is a Rank-restricted treat.
|
||||
// So we append it to the end of the roundqueue.
|
||||
// (as long as it exists, of course!)
|
||||
cupLevelNum = cup->cachedlevels[CUPCACHE_SPECIAL];
|
||||
if (cupLevelNum < nummapheaders)
|
||||
{
|
||||
G_MapIntoRoundQueue(
|
||||
cupLevelNum,
|
||||
G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel),
|
||||
setencore, // if this isn't correct, Got_Mapcmd will fix it
|
||||
true // Rank-restricted!
|
||||
);
|
||||
}
|
||||
|
||||
if (roundqueue.size == 0)
|
||||
{
|
||||
I_Error("G_CupToRoundQueue: roundqueue size is 0 after population!?");
|
||||
}
|
||||
}
|
||||
|
||||
void G_GetNextMap(void)
|
||||
{
|
||||
boolean spec = G_IsSpecialStage(prevmap+1);
|
||||
INT32 i;
|
||||
boolean setalready = false;
|
||||
|
||||
if (!server)
|
||||
{
|
||||
// Server is authoriative, not you
|
||||
return;
|
||||
}
|
||||
|
||||
deferencoremode = (cv_kartencore.value == 1);
|
||||
forceresetplayers = forcespecialstage = false;
|
||||
|
||||
INT16 newmap, curmap = gamestate == GS_LEVEL ? gamemap-1 : prevmap;
|
||||
|
||||
// go to next level
|
||||
|
|
@ -4868,131 +5008,97 @@ static INT16 G_GetNextMap(boolean advancemap)
|
|||
if (nextmapoverride != 0)
|
||||
{
|
||||
newmap = (INT16)(nextmapoverride-1);
|
||||
setalready = true;
|
||||
}
|
||||
else if (grandprixinfo.gp == true)
|
||||
else if (roundqueue.size > 0)
|
||||
{
|
||||
// G: oh dear, this whole GP block has loads of side effects...
|
||||
// for now, just repeat the same map for "map +"
|
||||
// you're not supposed to use that in GP anyways
|
||||
if (!advancemap || // ...right?
|
||||
grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session
|
||||
boolean permitrank = false;
|
||||
/*if (grandprixinfo.gp == true
|
||||
&& grandprixinfo.gamespeed >= KARTSPEED_NORMAL)
|
||||
{
|
||||
newmap = curmap; // Same map
|
||||
// On A rank pace? Then you get a chance for S rank!
|
||||
permitrank = (K_CalculateGPPercent(&grandprixinfo.rank) >= K_SealedStarEntryRequirement(&grandprixinfo.rank));
|
||||
|
||||
// If you're on Master, a win floats you to rank-restricted levels for free.
|
||||
// (This is a different class of challenge!)
|
||||
if (grandprixinfo.masterbots && grandprixinfo.rank.position <= 1)
|
||||
permitrank = true;
|
||||
}*/
|
||||
|
||||
while (roundqueue.position < roundqueue.size
|
||||
&& (roundqueue.entries[roundqueue.position].mapnum >= nummapheaders
|
||||
|| (permitrank == false && roundqueue.entries[roundqueue.position].rankrestricted == true)))
|
||||
{
|
||||
// Skip all restricted queue entries.
|
||||
roundqueue.position++;
|
||||
}
|
||||
|
||||
if (roundqueue.position < roundqueue.size)
|
||||
{
|
||||
// The next entry in the queue is valid; set it as nextmap!
|
||||
nextmap = roundqueue.entries[roundqueue.position].mapnum;
|
||||
deferencoremode = roundqueue.entries[roundqueue.position].encore;
|
||||
|
||||
// And we handle gametype changes, too.
|
||||
if (roundqueue.entries[roundqueue.position].gametype != gametype)
|
||||
{
|
||||
INT32 lastgametype = gametype;
|
||||
G_SetGametype(roundqueue.entries[roundqueue.position].gametype);
|
||||
D_GameTypeChanged(lastgametype);
|
||||
}
|
||||
|
||||
// On entering roundqueue mode, kill the non-PWR between-round scores.
|
||||
// This makes it viable as a future tournament mode base.
|
||||
if (roundqueue.position == 0)
|
||||
{
|
||||
forceresetplayers = true;
|
||||
}
|
||||
|
||||
// Handle primary queue position update.
|
||||
roundqueue.position++;
|
||||
if (grandprixinfo.gp == false || gametype == roundqueue.entries[0].gametype)
|
||||
{
|
||||
roundqueue.roundnum++;
|
||||
}
|
||||
|
||||
setalready = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
INT32 lastgametype = gametype;
|
||||
// Wipe the queue info.
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
|
||||
// If we're in a GP event, don't immediately follow it up with another.
|
||||
// I also suspect this will not work with online GP so I'm gonna prevent it right now.
|
||||
// The server might have to communicate eventmode (alongside other GP data) in XD_MAP later.
|
||||
if (netgame || grandprixinfo.eventmode != GPEVENT_NONE)
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
grandprixinfo.eventmode = GPEVENT_NONE;
|
||||
|
||||
G_SetGametype(GT_RACE);
|
||||
if (gametype != lastgametype)
|
||||
D_GameTypeChanged(lastgametype);
|
||||
|
||||
specialStage.active = false;
|
||||
bossinfo.boss = false;
|
||||
}
|
||||
// Special stage
|
||||
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels)
|
||||
{
|
||||
INT16 totaltotalring = 0;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
if (players[i].spectator)
|
||||
continue;
|
||||
if (players[i].bot)
|
||||
continue;
|
||||
totaltotalring += players[i].totalring;
|
||||
}
|
||||
|
||||
if (totaltotalring >= 50)
|
||||
{
|
||||
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_SPECIAL];
|
||||
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
|
||||
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_SPECIAL|TOL_BOSS|TOL_BATTLE))
|
||||
{
|
||||
grandprixinfo.eventmode = GPEVENT_SPECIAL;
|
||||
newmap = cupLevelNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (grandprixinfo.roundnum == (grandprixinfo.cup->numlevels+1)/2) // 3 for a 5-map cup
|
||||
{
|
||||
// todo any other condition?
|
||||
{
|
||||
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[CUPCACHE_BONUS];
|
||||
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum]
|
||||
&& mapheaderinfo[cupLevelNum]->typeoflevel & (TOL_BOSS|TOL_BATTLE))
|
||||
{
|
||||
grandprixinfo.eventmode = GPEVENT_CHALLENGE;
|
||||
newmap = cupLevelNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (grandprixinfo.eventmode != GPEVENT_NONE)
|
||||
{
|
||||
// nextmap is set above
|
||||
const INT32 newtol = mapheaderinfo[newmap]->typeoflevel;
|
||||
|
||||
if (newtol & TOL_SPECIAL)
|
||||
{
|
||||
specialStage.active = true;
|
||||
specialStage.encore = grandprixinfo.encore;
|
||||
}
|
||||
else //(if newtol & (TOL_BATTLE|TOL_BOSS)) -- safe to assume??
|
||||
{
|
||||
G_SetGametype(GT_BATTLE);
|
||||
if (gametype != lastgametype)
|
||||
D_GameTypeChanged(lastgametype);
|
||||
if (newtol & TOL_BOSS)
|
||||
{
|
||||
K_ResetBossInfo();
|
||||
bossinfo.boss = true;
|
||||
bossinfo.encore = grandprixinfo.encore;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
|
||||
{
|
||||
newmap = NEXTMAP_CEREMONY; // ceremonymap
|
||||
// In GP, we're now ready to go to the ceremony.
|
||||
nextmap = NEXTMAP_CEREMONY;
|
||||
setalready = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Proceed to next map
|
||||
const INT32 cupLevelNum = grandprixinfo.cup->cachedlevels[grandprixinfo.roundnum];
|
||||
|
||||
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
|
||||
{
|
||||
newmap = cupLevelNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
newmap = curmap; // Prevent uninitialised use
|
||||
}
|
||||
|
||||
grandprixinfo.roundnum++;
|
||||
// On exiting roundqueue mode, kill the non-PWR between-round scores.
|
||||
// This prevents future tournament winners from carrying their wins out.
|
||||
forceresetplayers = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the next D_MapChange sends updated roundqueue state.
|
||||
roundqueue.netcommunicate = true;
|
||||
}
|
||||
else if (bossinfo.boss == true)
|
||||
else if (grandprixinfo.gp == true)
|
||||
{
|
||||
newmap = NEXTMAP_TITLE; // temporary
|
||||
// Fast And Rapid Testing
|
||||
// this codepath is exclusively accessible through console/command line
|
||||
nextmap = prevmap;
|
||||
setalready = true;
|
||||
}
|
||||
else
|
||||
|
||||
if (setalready == false)
|
||||
{
|
||||
UINT32 tolflag = G_TOLFlag(gametype);
|
||||
register INT16 cm;
|
||||
|
||||
if (grandprixinfo.gp == true)
|
||||
if (true/*!(gametyperules & GTR_NOCUPSELECT)*/)
|
||||
{
|
||||
cupheader_t *cup = mapheaderinfo[gamemap-1]->cup;
|
||||
UINT8 gettingresult = 0;
|
||||
|
|
@ -5000,12 +5106,12 @@ static INT16 G_GetNextMap(boolean advancemap)
|
|||
while (cup)
|
||||
{
|
||||
// Not unlocked? Grab the next result afterwards
|
||||
if (!marathonmode && cup->unlockrequired != -1 && !unlockables[cup->unlockrequired].unlocked)
|
||||
/*if (!marathonmode && M_CupLocked(cup))
|
||||
{
|
||||
cup = cup->next;
|
||||
gettingresult = 1;
|
||||
continue;
|
||||
}
|
||||
}*/
|
||||
|
||||
for (i = 0; i < cup->numlevels; i++)
|
||||
{
|
||||
|
|
@ -5019,16 +5125,22 @@ static INT16 G_GetNextMap(boolean advancemap)
|
|||
|| (!marathonmode && M_MapLocked(cm+1)))
|
||||
continue;
|
||||
|
||||
// If the map is in multiple cups, only consider the first one valid.
|
||||
if (mapheaderinfo[cm]->cup != cup)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Grab the first valid after the map you're on
|
||||
if (gettingresult)
|
||||
{
|
||||
newmap = cm;
|
||||
nextmap = cm;
|
||||
gettingresult = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// Not the map you're on?
|
||||
if (cm != curmap)
|
||||
if (cm != prevmap)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -5050,16 +5162,16 @@ static INT16 G_GetNextMap(boolean advancemap)
|
|||
// Didn't get a nextmap before reaching the end?
|
||||
if (gettingresult != 2)
|
||||
{
|
||||
newmap = NEXTMAP_CEREMONY; // ceremonymap
|
||||
nextmap = NEXTMAP_CEREMONY; // ceremonymap
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cm = curmap;
|
||||
cm = prevmap;
|
||||
if (++cm >= nummapheaders)
|
||||
cm = 0;
|
||||
|
||||
while (cm != curmap)
|
||||
while (cm != prevmap)
|
||||
{
|
||||
if (!mapheaderinfo[cm]
|
||||
|| mapheaderinfo[cm]->lumpnum == LUMPERROR
|
||||
|
|
@ -5075,40 +5187,42 @@ static INT16 G_GetNextMap(boolean advancemap)
|
|||
break;
|
||||
}
|
||||
|
||||
newmap = cm;
|
||||
nextmap = cm;
|
||||
}
|
||||
|
||||
if (advancemap && K_CanChangeRules(true))
|
||||
if (K_CanChangeRules(true))
|
||||
{
|
||||
switch (cv_advancemap.value)
|
||||
if (!netgame) // Match Race.
|
||||
nextmap = NEXTMAP_TITLE;
|
||||
else switch (cv_advancemap.value)
|
||||
{
|
||||
case 0: // Stay on same map.
|
||||
newmap = curmap;
|
||||
nextmap = prevmap;
|
||||
break;
|
||||
case 3: // Voting screen.
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
if (players[i].spectator)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (i != MAXPLAYERS)
|
||||
{
|
||||
newmap = NEXTMAP_VOTING;
|
||||
break;
|
||||
}
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
if (players[i].spectator)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
/* FALLTHRU */
|
||||
if (i != MAXPLAYERS)
|
||||
{
|
||||
nextmap = NEXTMAP_VOTING;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* FALLTHRU */
|
||||
case 2: // Go to random map.
|
||||
newmap = G_RandMap(G_TOLFlag(gametype), curmap, 0, 0, NULL);
|
||||
nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, false, false, NULL);
|
||||
break;
|
||||
default:
|
||||
if (newmap >= NEXTMAP_SPECIAL) // Loop back around
|
||||
if (nextmap >= NEXTMAP_SPECIAL) // Loop back around
|
||||
{
|
||||
newmap = G_GetFirstMapOfGametype(gametype);
|
||||
nextmap = G_GetFirstMapOfGametype(gametype);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -5119,10 +5233,10 @@ static INT16 G_GetNextMap(boolean advancemap)
|
|||
if (newmap == NEXTMAP_INVALID || (newmap < NEXTMAP_SPECIAL && (newmap >= nummapheaders || !mapheaderinfo[newmap] || mapheaderinfo[newmap]->lumpnum == LUMPERROR)))
|
||||
I_Error("G_GetNextMap: Internal map ID %d not found (nummapheaders = %d)\n", newmap, nummapheaders);
|
||||
|
||||
#if 0 // This is a surprise tool that will help us later.
|
||||
if (!spec)
|
||||
#endif //#if 0
|
||||
lastmap = newmap;
|
||||
|
||||
return newmap;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -5244,7 +5358,7 @@ void G_AfterIntermission(void)
|
|||
|
||||
if (gamestate != GS_VOTING)
|
||||
{
|
||||
nextmap = G_GetNextMap(true);
|
||||
G_GetNextMap();
|
||||
G_HandleSaveLevel();
|
||||
}
|
||||
|
||||
|
|
@ -5270,10 +5384,6 @@ void G_NextLevel(void)
|
|||
return;
|
||||
}
|
||||
|
||||
forceresetplayers = false;
|
||||
if (cv_kartencore.value == 1)
|
||||
deferencoremode = true;
|
||||
|
||||
gameaction = ga_worlddone;
|
||||
}
|
||||
|
||||
|
|
@ -5288,7 +5398,7 @@ static void G_DoWorldDone(void)
|
|||
forceresetplayers,
|
||||
0,
|
||||
false,
|
||||
false);
|
||||
forcespecialstage);
|
||||
}
|
||||
|
||||
gameaction = ga_nothing;
|
||||
|
|
@ -6125,14 +6235,12 @@ void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ss
|
|||
// This is the map command interpretation something like Command_Map_f
|
||||
//
|
||||
// called at: map cmd execution, doloadgame, doplaydemo
|
||||
void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skipprecutscene, boolean FLS)
|
||||
void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skipprecutscene)
|
||||
{
|
||||
const char * mapname = G_BuildMapName(map);
|
||||
|
||||
INT32 i;
|
||||
|
||||
(void)FLS;
|
||||
|
||||
Y_CleanupScreenBuffer();
|
||||
|
||||
if (paused)
|
||||
|
|
@ -6160,7 +6268,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
|
|||
|
||||
players[i].roundscore = 0;
|
||||
|
||||
if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart
|
||||
if (resetplayer && !demo.playback) // SRB2Kart
|
||||
{
|
||||
players[i].lives = 3;
|
||||
players[i].xtralife = 0;
|
||||
|
|
@ -6465,7 +6573,11 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
|
|||
if (mapname[0] == '*') // current map
|
||||
return gamemap;
|
||||
else if (mapname[0] == '+') // next map
|
||||
return G_GetNextMap(false)+1;
|
||||
{
|
||||
G_GetNextMap();
|
||||
return nextmap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Now detect map number in base 10, which no one asked for. */
|
||||
|
|
|
|||
31
src/g_game.h
31
src/g_game.h
|
|
@ -53,6 +53,29 @@ typedef enum
|
|||
NEXTMAP_SPECIAL = NEXTMAP_INVALID
|
||||
} nextmapspecial_t;
|
||||
|
||||
#define ROUNDQUEUE_MAX 10 // sane max? maybe make dynamically allocated later
|
||||
|
||||
struct roundentry_t
|
||||
{
|
||||
UINT16 mapnum; // Map number at this position
|
||||
UINT8 gametype; // Gametype we want to play this in
|
||||
boolean encore; // Whether this will be flipped
|
||||
boolean rankrestricted; // For grand prix progression
|
||||
};
|
||||
|
||||
extern struct roundqueue
|
||||
{
|
||||
UINT8 roundnum; // Visible number on HUD
|
||||
UINT8 position; // Head position in the round queue
|
||||
UINT8 size; // Number of entries in the round queue
|
||||
boolean netcommunicate; // As server, should we net-communicate this in XD_MAP?
|
||||
roundentry_t entries[ROUNDQUEUE_MAX]; // Entries in the round queue
|
||||
} roundqueue;
|
||||
|
||||
void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted);
|
||||
void G_MapIntoRoundQueue(UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted);
|
||||
void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencore);
|
||||
|
||||
extern INT16 kartmap2native[NEXTMAP_SPECIAL], nativemap2kart[NEXTMAP_SPECIAL];
|
||||
extern INT16 nextexnum;
|
||||
|
||||
|
|
@ -166,7 +189,7 @@ void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo);
|
|||
void G_DoReborn(INT32 playernum);
|
||||
void G_PlayerReborn(INT32 player, boolean betweenmaps);
|
||||
void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer,
|
||||
boolean skipprecutscene, boolean FLS);
|
||||
boolean skipprecutscene);
|
||||
char *G_BuildMapTitle(INT32 mapnum);
|
||||
|
||||
struct searchdim
|
||||
|
|
@ -221,10 +244,11 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives);
|
|||
|
||||
void G_SetGametype(INT16 gametype);
|
||||
char *G_PrepareGametypeConstant(const char *newgtconst);
|
||||
void G_UpdateGametypeSelections(void);
|
||||
void G_SetGametypeColor(INT16 gtype,INT32 color);
|
||||
void G_AddTOL(UINT32 newtol, const char *tolname);
|
||||
void G_UpdateGametypeSelections(void);
|
||||
INT32 G_GetGametypeByName(const char *gametypestr);
|
||||
INT32 G_GuessGametypeByTOL(UINT32 tol);
|
||||
|
||||
boolean G_IsSpecialStage(INT32 mapnum);
|
||||
boolean G_GametypeUsesLives(void);
|
||||
boolean G_GametypeHasTeams(void);
|
||||
|
|
@ -235,6 +259,7 @@ UINT8 G_GetGametypeColor(INT16 gt);
|
|||
void G_BeginLevelExit(void);
|
||||
void G_FinishExitLevel(void);
|
||||
void G_NextLevel(void);
|
||||
void G_GetNextMap(void);
|
||||
void G_Continue(void);
|
||||
void G_UseContinue(void);
|
||||
void G_AfterIntermission(void);
|
||||
|
|
|
|||
|
|
@ -294,10 +294,10 @@ static INT16 K_RivalScore(player_t *bot)
|
|||
UINT8 lowestdifficulty = MAXBOTDIFFICULTY;
|
||||
UINT8 i;
|
||||
|
||||
if (grandprixinfo.cup != NULL)
|
||||
if (grandprixinfo.cup != NULL && roundqueue.size > 0)
|
||||
{
|
||||
roundnum = grandprixinfo.roundnum;
|
||||
roundsleft = grandprixinfo.cup->numlevels - grandprixinfo.roundnum;
|
||||
roundnum = roundqueue.roundnum;
|
||||
roundsleft = grandprixinfo.cup->numlevels - roundnum;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXGPPLAYERS; i++)
|
||||
|
|
@ -539,8 +539,10 @@ void K_RetireBots(void)
|
|||
UINT16 i;
|
||||
|
||||
if (grandprixinfo.gp == true
|
||||
&& (((grandprixinfo.cup != NULL) && (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels))
|
||||
|| grandprixinfo.eventmode != GPEVENT_NONE))
|
||||
&& (grandprixinfo.eventmode != GPEVENT_NONE
|
||||
|| (grandprixinfo.cup != NULL
|
||||
&& roundqueue.size > 0
|
||||
&& roundqueue.roundnum >= grandprixinfo.cup->numlevels)))
|
||||
{
|
||||
// No replacement.
|
||||
return;
|
||||
|
|
@ -580,6 +582,10 @@ void K_RetireBots(void)
|
|||
{
|
||||
const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed);
|
||||
newDifficulty = startingdifficulty - 4;
|
||||
if (roundqueue.size > 0)
|
||||
{
|
||||
newDifficulty += roundqueue.roundnum;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -738,7 +744,7 @@ void K_PlayerLoseLife(player_t *player)
|
|||
--------------------------------------------------*/
|
||||
boolean K_CanChangeRules(boolean allowdemos)
|
||||
{
|
||||
if (grandprixinfo.gp == true && grandprixinfo.roundnum > 0)
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
// Don't cheat the rules of the GP!
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -20,14 +20,16 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define GPEVENT_NONE 0
|
||||
#define GPEVENT_CHALLENGE 1
|
||||
#define GPEVENT_SPECIAL 2
|
||||
typedef enum
|
||||
{
|
||||
GPEVENT_NONE = 0,
|
||||
GPEVENT_BONUS,
|
||||
GPEVENT_SPECIAL,
|
||||
} gpEvent_e;
|
||||
|
||||
extern struct grandprixinfo
|
||||
{
|
||||
boolean gp; ///< If true, then we are in a Grand Prix.
|
||||
UINT8 roundnum; ///< Round number. If 0, this is a single session from the warp command.
|
||||
cupheader_t *cup; ///< Which cup are we playing?
|
||||
UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars
|
||||
boolean encore; ///< Ditto, but for encore mode
|
||||
|
|
|
|||
|
|
@ -39,23 +39,35 @@ SINT8 encorescramble = -1;
|
|||
|
||||
SINT8 K_UsingPowerLevels(void)
|
||||
{
|
||||
SINT8 pt = PWRLV_DISABLED;
|
||||
|
||||
if (!cv_kartusepwrlv.value || !(netgame || (demo.playback && demo.netgame)) || grandprixinfo.gp == true || bossinfo.boss == true)
|
||||
if (!cv_kartusepwrlv.value)
|
||||
{
|
||||
// Explicitly forbidden.
|
||||
return PWRLV_DISABLED;
|
||||
}
|
||||
|
||||
if (!(netgame || (demo.playback && demo.netgame)))
|
||||
{
|
||||
// Servers only.
|
||||
return PWRLV_DISABLED;
|
||||
}
|
||||
|
||||
if (roundqueue.size > 0 && roundqueue.position > 0)
|
||||
{
|
||||
// When explicit progression is in place, we're going by different rules.
|
||||
return PWRLV_DISABLED;
|
||||
}
|
||||
|
||||
if (gametype == GT_RACE)
|
||||
{
|
||||
pt = PWRLV_RACE;
|
||||
return PWRLV_RACE;
|
||||
}
|
||||
else if (gametype == GT_BATTLE)
|
||||
{
|
||||
pt = PWRLV_BATTLE;
|
||||
return PWRLV_BATTLE;
|
||||
}
|
||||
|
||||
return pt;
|
||||
// We do not support PWR for custom gametypes at this moment in time.
|
||||
return PWRLV_DISABLED;
|
||||
}
|
||||
|
||||
void K_ClearClientPowerLevels(void)
|
||||
|
|
|
|||
27
src/m_menu.c
27
src/m_menu.c
|
|
@ -145,8 +145,6 @@ M_waiting_mode_t m_waiting_mode = M_NOT_WAITING;
|
|||
|
||||
const char *quitmsg[NUM_QUITMESSAGES];
|
||||
|
||||
boolean fromlevelselect = false;
|
||||
|
||||
static char menu_text_input_buf[MAXSTRINGLENGTH];
|
||||
static textinput_t menuinput;
|
||||
|
||||
|
|
@ -6525,8 +6523,6 @@ INT32 MR_StartGrandPrix(INT32 choice)
|
|||
break;
|
||||
}
|
||||
|
||||
grandprixinfo.encore = (boolean)(cv_dummygpencore.value);
|
||||
|
||||
for (gpcup = kartcupheaders; gpcup != NULL; gpcup = gpcup->next)
|
||||
{
|
||||
if (gpcup->id == cv_nextcup.value-1)
|
||||
|
|
@ -6534,23 +6530,28 @@ INT32 MR_StartGrandPrix(INT32 choice)
|
|||
}
|
||||
|
||||
I_Assert(gpcup != NULL);
|
||||
grandprixinfo.cup = gpcup;
|
||||
|
||||
grandprixinfo.gp = true;
|
||||
grandprixinfo.roundnum = 1;
|
||||
grandprixinfo.wonround = false;
|
||||
|
||||
grandprixinfo.initalize = true;
|
||||
grandprixinfo.cup = gpcup;
|
||||
|
||||
levelNum = G_MapNumber(grandprixinfo.cup->levellist[0]);
|
||||
// Populate the roundqueue
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
G_GPCupIntoRoundQueue(gpcup, GT_RACE/*levellist.newgametype*/, (boolean)cv_dummygpencore.value);
|
||||
roundqueue.position = roundqueue.roundnum = 1;
|
||||
roundqueue.netcommunicate = true; // relevant for future Online GP
|
||||
|
||||
G_DeferedInitNew(
|
||||
D_MapChange(
|
||||
roundqueue.entries[0].mapnum + 1,
|
||||
roundqueue.entries[0].gametype,
|
||||
roundqueue.entries[0].encore,
|
||||
true,
|
||||
1,
|
||||
false,
|
||||
levelNum + 1,
|
||||
cv_chooseskin.value,
|
||||
(UINT8)(cv_splitplayers.value - 1),
|
||||
false
|
||||
roundqueue.entries[0].rankrestricted
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -420,14 +420,15 @@ static void P_RelinkWaypoint(savebuffer_t *save, waypoint_t **ptr)
|
|||
|
||||
// Block UINT32s to attempt to ensure that the correct data is
|
||||
// being sent and received
|
||||
#define ARCHIVEBLOCK_MISC 0x7FEEDEED
|
||||
#define ARCHIVEBLOCK_PLAYERS 0x7F448008
|
||||
#define ARCHIVEBLOCK_PARTIES 0x7F87AF0C
|
||||
#define ARCHIVEBLOCK_WORLD 0x7F8C08C0
|
||||
#define ARCHIVEBLOCK_POBJS 0x7F928546
|
||||
#define ARCHIVEBLOCK_THINKERS 0x7F37037C
|
||||
#define ARCHIVEBLOCK_SPECIALS 0x7F228378
|
||||
#define ARCHIVEBLOCK_WAYPOINTS 0x7F46498F
|
||||
#define ARCHIVEBLOCK_MISC 0x7FEEDEED
|
||||
#define ARCHIVEBLOCK_PLAYERS 0x7F448008
|
||||
#define ARCHIVEBLOCK_PARTIES 0x7F87AF0C
|
||||
#define ARCHIVEBLOCK_ROUNDQUEUE 0x7F721331
|
||||
#define ARCHIVEBLOCK_WORLD 0x7F8C08C0
|
||||
#define ARCHIVEBLOCK_POBJS 0x7F928546
|
||||
#define ARCHIVEBLOCK_THINKERS 0x7F37037C
|
||||
#define ARCHIVEBLOCK_SPECIALS 0x7F228378
|
||||
#define ARCHIVEBLOCK_WAYPOINTS 0x7F46498F
|
||||
|
||||
// Specialized netsynch markers
|
||||
#define PLYRSYNC_ITEMLIST (1)
|
||||
|
|
@ -920,6 +921,48 @@ static void P_NetSyncParties(savebuffer_t *save)
|
|||
TracyCZoneEnd(__zone);
|
||||
}
|
||||
|
||||
static void P_NetArchiveRoundQueue(savebuffer_t *save)
|
||||
{
|
||||
UINT8 i;
|
||||
WRITEUINT32(save->p, ARCHIVEBLOCK_ROUNDQUEUE);
|
||||
|
||||
WRITEUINT8(save->p, roundqueue.position);
|
||||
WRITEUINT8(save->p, roundqueue.size);
|
||||
WRITEUINT8(save->p, roundqueue.roundnum);
|
||||
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
{
|
||||
//WRITEUINT16(save->p, roundqueue.entries[i].mapnum);
|
||||
/* NOPE! Clients do not need to know what is in the roundqueue.
|
||||
* This disincentivises cheaty clients in future tournament environments.
|
||||
* ~toast 080423 */
|
||||
WRITEUINT8(save->p, roundqueue.entries[i].gametype);
|
||||
WRITEUINT8(save->p, (UINT8)roundqueue.entries[i].encore);
|
||||
WRITEUINT8(save->p, (UINT8)roundqueue.entries[i].rankrestricted);
|
||||
}
|
||||
}
|
||||
|
||||
static void P_NetUnArchiveRoundQueue(savebuffer_t *save)
|
||||
{
|
||||
UINT8 i;
|
||||
if (READUINT32(save->p) != ARCHIVEBLOCK_ROUNDQUEUE)
|
||||
I_Error("Bad $$$.sav at archive block RoundQueue");
|
||||
|
||||
memset(&roundqueue, 0, sizeof(struct roundqueue));
|
||||
|
||||
roundqueue.position = READUINT8(save->p);
|
||||
roundqueue.size = READUINT8(save->p);
|
||||
roundqueue.roundnum = READUINT8(save->p);
|
||||
|
||||
for (i = 0; i < roundqueue.size; i++)
|
||||
{
|
||||
roundqueue.entries[i].mapnum = 0; // TEST RUN -- dummy, has to be < nummapheaders
|
||||
roundqueue.entries[i].gametype = READUINT8(save->p);
|
||||
roundqueue.entries[i].encore = (boolean)READUINT8(save->p);
|
||||
roundqueue.entries[i].rankrestricted = (boolean)READUINT8(save->p);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Colormaps
|
||||
///
|
||||
|
|
@ -4562,6 +4605,7 @@ void P_SaveNetGame(savebuffer_t *save, boolean resending)
|
|||
|
||||
P_NetSyncPlayers(save);
|
||||
P_NetSyncParties(save);
|
||||
P_NetArchiveRoundQueue(save);
|
||||
|
||||
if (gamestate == GS_LEVEL)
|
||||
{
|
||||
|
|
@ -4614,6 +4658,7 @@ boolean P_LoadNetGame(savebuffer_t *save, boolean reloading)
|
|||
return false;
|
||||
P_NetSyncPlayers(save);
|
||||
P_NetSyncParties(save);
|
||||
P_NetUnArchiveRoundQueue(save);
|
||||
|
||||
if (gamestate == GS_LEVEL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -867,7 +867,8 @@ void P_Ticker(boolean run)
|
|||
}
|
||||
|
||||
if (leveltime < startingtime) // SRB2Kart
|
||||
S_ChangeMusicInternal(((grandprixinfo.roundnum > 0) ? "gpstrt" : (encoremode ? "estart" : "kstart")), false); // yes this will be spammed otherwise encore and some stuff WILL overwrite it
|
||||
S_ChangeMusicInternal(((grandprixinfo.gp == true
|
||||
) ? "gpstrt" : (encoremode ? "estart" : "kstart")), false); // yes this will be spammed otherwise encore and some stuff WILL overwrite it
|
||||
else if (leveltime == startingtime) // The GO! sound stops the level start ambience
|
||||
S_StopMusic();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2679,7 +2679,8 @@ void S_InitLevelMusic(boolean fromnetsave)
|
|||
|
||||
S_StopMusic(); // Starting ambience should always be restarted, if playing.
|
||||
|
||||
S_ChangeMusicEx(((grandprixinfo.roundnum > 0) ? "gpstrt" : (encoremode ? "estart" : "kstart")), 0, false, mapmusposition, 0, 0);
|
||||
S_ChangeMusicEx(((grandprixinfo.gp == true
|
||||
) ? "gpstrt" : (encoremode ? "estart" : "kstart")), 0, false, mapmusposition, 0, 0);
|
||||
|
||||
S_ResetMusicStack();
|
||||
music_stack_noposition = false;
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ TYPEDEF (menudemo_t);
|
|||
TYPEDEF (demoghost);
|
||||
|
||||
// g_game.h
|
||||
TYPEDEF (roundentry_t);
|
||||
TYPEDEF (mapsearchfreq_t);
|
||||
|
||||
// hu_stuff.h
|
||||
|
|
|
|||
|
|
@ -1201,6 +1201,11 @@ void Y_StartIntermission(void)
|
|||
K_CashInPowerLevels();
|
||||
}
|
||||
|
||||
if (roundqueue.size > 0 && roundqueue.position == roundqueue.size)
|
||||
{
|
||||
Automate_Run(AEV_QUEUEEND);
|
||||
}
|
||||
|
||||
bgtile = W_CachePatchName("SRB2BACK", PU_STATIC);
|
||||
useinterpic = false;
|
||||
Automate_Run(AEV_INTERMISSIONSTART);
|
||||
|
|
|
|||
Loading…
Reference in a new issue