diff --git a/extras/udmf-spec.txt b/extras/udmf-spec.txt index e501c20be..111aaba62 100644 --- a/extras/udmf-spec.txt +++ b/extras/udmf-spec.txt @@ -42,7 +42,7 @@ additional lumps: BEHAVIOR = Compiled ACS code. ZNODES = Compiled extended / GL friendly nodes. These are required. - PICTURE = A Doom graphic lump, expected to be 160x100. Intended to be a + PICTURE = A Doom graphic lump, expected to be 320x240 or 160x100. Intended to be a screenshot of the map itself. This is used by the game for level select menus. MINIMAP = A Doom graphic lump, expected to be 100x100. Intended to be a diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index b84b3126b..8dff2e811 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -3054,7 +3054,7 @@ bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wor const char *levelName = NULL; size_t levelLen = 0; - UINT16 nextmap = NUMMAPS; + UINT16 nextmap = NEXTMAP_INVALID; (void)argC; @@ -3075,13 +3075,10 @@ bool CallFunc_MapWarp(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wor { CONS_Alert(CONS_WARNING, "MapWarp level name was not provided.\n"); } - - if (levelName[0] == 'M' && levelName[1] == 'A' && levelName[2] == 'P' && levelName[5]=='\0') - { - levelName = va("MAP%d",M_MapNumber(levelName[3], levelName[4])); - } - if (nextmap >= NUMMAPS) + nextmap = G_MapNumber(levelName); + + if (nextmap == NEXTMAP_INVALID) { CONS_Alert(CONS_WARNING, "MapWarp level %s is not valid or loaded.\n", levelName); return false; diff --git a/src/command.c b/src/command.c index 29039d574..9a29966f9 100644 --- a/src/command.c +++ b/src/command.c @@ -1992,42 +1992,9 @@ void CV_AddValue(consvar_t *var, INT32 increment) if (var->PossibleValue) { - if (var == &cv_nextmap) - { - // Special case for the nextmap variable, used only directly from the menu - INT32 oldvalue = var->value - 1, gt; - gt = cv_newgametype.value; - { - newvalue = var->value - 1; - do - { - if(increment > 0) // Going up! - { - if (++newvalue == NUMMAPS) - newvalue = -1; - } - else // Going down! - { - if (--newvalue == -2) - newvalue = NUMMAPS-1; - } - - if (newvalue == oldvalue) - break; // don't loop forever if there's none of a certain gametype - - if(!mapheaderinfo[newvalue]) - continue; // Don't allocate the header. That just makes memory usage skyrocket. - - } while (!M_CanShowLevelInList(newvalue, gt)); - - var->value = newvalue + 1; - var->func(); - return; - } - } #define MINVAL 0 #define MAXVAL 1 - else if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN")) + if (var->PossibleValue[MINVAL].strvalue && !strcmp(var->PossibleValue[MINVAL].strvalue, "MIN")) { #ifdef PARANOIA if (!var->PossibleValue[MAXVAL].strvalue) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b435d29a9..da5453605 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -909,6 +909,9 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) UINT8 *p; size_t mirror_length; const char *httpurl = cv_httpsource.string; + UINT8 prefgametype = (cv_kartgametypepreference.value == -1) + ? gametype + : cv_kartgametypepreference.value; netbuffer->packettype = PT_SERVERINFO; netbuffer->u.serverinfo._255 = 255; @@ -933,7 +936,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) else netbuffer->u.serverinfo.refusereason = 0; - strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], + strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[prefgametype], sizeof netbuffer->u.serverinfo.gametypename); netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); @@ -945,7 +948,6 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) CopyCaretColors(netbuffer->u.serverinfo.servername, cv_servername.string, MAXSERVERNAME); - strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); @@ -1158,35 +1160,32 @@ static boolean SV_ResendingSavegameToAnyone(void) static void SV_SendSaveGame(INT32 node, boolean resending) { size_t length, compressedlen; - savebuffer_t save; + savebuffer_t save = {0}; UINT8 *compressedsave; UINT8 *buffertosend; // first save it in a malloced buffer - save.size = NETSAVEGAMESIZE; - save.buffer = (UINT8 *)malloc(save.size); - if (!save.buffer) + if (P_SaveBufferAlloc(&save, NETSAVEGAMESIZE) == false) { CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); return; } // Leave room for the uncompressed length. - save.p = save.buffer + sizeof(UINT32); - save.end = save.buffer + save.size; + save.p += sizeof(UINT32); P_SaveNetGame(&save, resending); length = save.p - save.buffer; if (length > NETSAVEGAMESIZE) { - free(save.buffer); + P_SaveBufferFree(&save); I_Error("Savegame buffer overrun"); } // Allocate space for compressed save: one byte fewer than for the // uncompressed data to ensure that the compression is worthwhile. - compressedsave = malloc(length - 1); + compressedsave = Z_Malloc(length - 1, PU_STATIC, NULL); if (!compressedsave) { CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); @@ -1197,7 +1196,7 @@ static void SV_SendSaveGame(INT32 node, boolean resending) if ((compressedlen = lzf_compress(save.buffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) { // Compressing succeeded; send compressed data - free(save.buffer); + P_SaveBufferFree(&save); // State that we're compressed. buffertosend = compressedsave; @@ -1207,14 +1206,14 @@ static void SV_SendSaveGame(INT32 node, boolean resending) else { // Compression failed to make it smaller; send original - free(compressedsave); + Z_Free(compressedsave); // State that we're not compressed buffertosend = save.buffer; WRITEUINT32(save.buffer, 0); } - AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); + AddRamToSendQueue(node, buffertosend, length, SF_Z_RAM, 0); // Remember when we started sending the savegame so we can handle timeouts sendingsavegame[node] = true; @@ -1228,7 +1227,7 @@ static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SA static void SV_SavedGame(void) { size_t length; - savebuffer_t save; + savebuffer_t save = {0}; char tmpsave[256]; if (!cv_dumpconsistency.value) @@ -1237,22 +1236,18 @@ static void SV_SavedGame(void) sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); // first save it in a malloced buffer - save.size = NETSAVEGAMESIZE; - save.p = save.buffer = (UINT8 *)malloc(save.size); - if (!save.p) + if (P_SaveBufferAlloc(&save, NETSAVEGAMESIZE) == false) { CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); return; } - save.end = save.buffer + save.size; - P_SaveNetGame(&save, false); length = save.p - save.buffer; if (length > NETSAVEGAMESIZE) { - free(save.buffer); + P_SaveBufferFree(&save); I_Error("Savegame buffer overrun"); } @@ -1260,7 +1255,7 @@ static void SV_SavedGame(void) if (!FIL_WriteFile(tmpsave, save.buffer, length)) CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); - free(save.buffer); + P_SaveBufferFree(&save); } #undef TMPSAVENAME @@ -1270,37 +1265,31 @@ static void SV_SavedGame(void) static void CL_LoadReceivedSavegame(boolean reloading) { - savebuffer_t save; + savebuffer_t save = {0}; size_t length, decompressedlen; char tmpsave[256]; sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - length = FIL_ReadFile(tmpsave, &save.buffer); - - CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); - if (!length) + if (P_SaveBufferFromFile(&save, tmpsave) == false) { I_Error("Can't read savegame sent"); return; } - save.p = save.buffer; - save.size = length; - save.end = save.buffer + save.size; + length = save.size; + CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); // Decompress saved game if necessary. decompressedlen = READUINT32(save.p); - if(decompressedlen > 0) + if (decompressedlen > 0) { UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); lzf_decompress(save.p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); - Z_Free(save.buffer); - save.p = save.buffer = decompressedbuffer; - save.size = decompressedlen; - save.end = save.buffer + decompressedlen; + P_SaveBufferFree(&save); + P_SaveBufferFromExisting(&save, decompressedbuffer, decompressedlen); } paused = false; @@ -1332,10 +1321,13 @@ static void CL_LoadReceivedSavegame(boolean reloading) } // done - Z_Free(save.buffer); - save.p = NULL; + P_SaveBufferFree(&save); + if (unlink(tmpsave) == -1) + { CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); + } + consistancy[gametic%BACKUPTICS] = Consistancy(); CON_ToggleOff(); @@ -6134,7 +6126,7 @@ void CL_ClearRewinds(void) rewind_t *CL_SaveRewindPoint(size_t demopos) { - savebuffer_t save; + savebuffer_t save = {0}; rewind_t *rewind; if (rewindhead && rewindhead->leveltime + REWIND_POINT_INTERVAL > leveltime) @@ -6144,10 +6136,7 @@ rewind_t *CL_SaveRewindPoint(size_t demopos) if (!rewind) return NULL; - save.buffer = save.p = rewind->savebuffer; - save.size = NETSAVEGAMESIZE; - save.end = save.buffer + save.size; - + P_SaveBufferFromExisting(&save, rewind->savebuffer, NETSAVEGAMESIZE); P_SaveNetGame(&save, false); rewind->leveltime = leveltime; @@ -6160,7 +6149,7 @@ rewind_t *CL_SaveRewindPoint(size_t demopos) rewind_t *CL_RewindToTime(tic_t time) { - savebuffer_t save; + savebuffer_t save = {0}; rewind_t *rewind; while (rewindhead && rewindhead->leveltime > time) @@ -6173,10 +6162,7 @@ rewind_t *CL_RewindToTime(tic_t time) if (!rewindhead) return NULL; - save.buffer = save.p = rewindhead->savebuffer; - save.size = NETSAVEGAMESIZE; - save.end = save.buffer + save.size; - + P_SaveBufferFromExisting(&save, rewindhead->savebuffer, NETSAVEGAMESIZE); P_LoadNetGame(&save, false); wipegamestate = gamestate; // No fading back in! diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 25899b77c..2bf1148de 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -284,7 +284,6 @@ struct serverinfo_pak tic_t time; tic_t leveltime; char servername[MAXSERVERNAME]; - char mapname[8]; char maptitle[33]; unsigned char mapmd5[16]; UINT8 actnum; diff --git a/src/d_main.cpp b/src/d_main.cpp index 6d67785fa..ede869228 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -347,7 +347,7 @@ static bool D_Display(void) if (gamestate != GS_LEVEL && rendermode != render_none) { V_SetPaletteLump("PLAYPAL"); // Reset the palette - R_ReInitColormaps(0, LUMPERROR); + R_ReInitColormaps(0, NULL, 0, false); } F_WipeStartScreen(); @@ -986,13 +986,12 @@ void D_StartTitle(void) if (server) { - char mapname[6]; + i = G_GetFirstMapOfGametype(gametype)+1; - strlcpy(mapname, G_BuildMapName(spstage_start), sizeof (mapname)); - strlwr(mapname); - mapname[5] = '\0'; + if (i > nummapheaders) + I_Error("D_StartTitle: No valid map ID found!?"); - COM_BufAddText(va("map %s\n", mapname)); + COM_BufAddText(va("map %s\n", G_BuildMapName(i))); } return; @@ -1247,11 +1246,6 @@ D_ConvertVersionNumbers (void) // void D_SRB2Main(void) { - INT32 i; - UINT16 wadnum; - lumpinfo_t *lumpinfo; - char *name; - INT32 p; INT32 pstartmap = 0; @@ -1512,31 +1506,7 @@ void D_SRB2Main(void) // conversion sometimes needs the palette V_ReloadPalette(); - // - // search for maps - // - for (wadnum = 0; wadnum <= mainwads; wadnum++) - { - lumpinfo = wadfiles[wadnum]->lumpinfo; - for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++) - { - name = lumpinfo->name; - - if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers - { - INT16 num; - if (name[5] != '\0') - continue; - num = (INT16)M_MapNumber(name[3], name[4]); - - // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant - if (num <= NUMMAPS && mapheaderinfo[num - 1]) - { - mapheaderinfo[num - 1]->alreadyExists = true; - } - } - } - } + P_InitMapData(false); CON_SetLoadingProgress(LOADED_IWAD); @@ -1595,39 +1565,7 @@ void D_SRB2Main(void) // // search for pwad maps // - - // - // search for maps... again. - // - for (wadnum = mainwads+1; wadnum < numwadfiles; wadnum++) - { - lumpinfo = wadfiles[wadnum]->lumpinfo; - for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++) - { - name = lumpinfo->name; - - if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers - { - INT16 num; - if (name[5] != '\0') - continue; - num = (INT16)M_MapNumber(name[3], name[4]); - - // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant - if (num <= NUMMAPS && mapheaderinfo[num - 1]) - { - if (mapheaderinfo[num - 1]->alreadyExists != false) - { - G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you - } - - mapheaderinfo[num - 1]->alreadyExists = true; - } - - CONS_Printf("%s\n", name); - } - } - } + P_InitMapData(true); HU_LoadGraphics(); } @@ -1730,33 +1668,41 @@ void D_SRB2Main(void) { const char *word = M_GetNextParm(); - pstartmap = G_FindMapByNameOrCode(word, 0); - - if (! pstartmap) - I_Error("Cannot find a map remotely named '%s'\n", word); + if (WADNAMECHECK(word)) + { + if (!(pstartmap = wadnamemap)) + I_Error("Bad '%s' level warp.\n" +#if defined (_WIN32) + "Are you using MSDOS 8.3 filenames in Zone Builder?\n" +#endif + , word); + } else { - if (!M_CheckParm("-server") && !M_CheckParm("-dedicated") && !M_CheckParm("-nograndprix")) - { - G_SetGameModified(true, true); - - // Start up a "minor" grand prix session - memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); - - grandprixinfo.gamespeed = KARTSPEED_NORMAL; - grandprixinfo.encore = false; - grandprixinfo.masterbots = false; - - grandprixinfo.gp = true; - grandprixinfo.roundnum = 0; - grandprixinfo.cup = NULL; - grandprixinfo.wonround = false; - - grandprixinfo.initalize = true; - } - - autostart = true; + if (!(pstartmap = G_FindMapByNameOrCode(word, 0))) + I_Error("Cannot find a map remotely named '%s'\n", word); } + + if (!M_CheckParm("-server") && !M_CheckParm("-dedicated") && !M_CheckParm("-nograndprix")) + { + G_SetGameModified(true, true); + + // Start up a "minor" grand prix session + memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); + + grandprixinfo.gamespeed = KARTSPEED_NORMAL; + grandprixinfo.encore = false; + grandprixinfo.masterbots = false; + + grandprixinfo.gp = true; + grandprixinfo.roundnum = 0; + grandprixinfo.cup = NULL; + grandprixinfo.wonround = false; + + grandprixinfo.initalize = true; + } + + autostart = true; } // Set up splitscreen players before joining! @@ -1847,14 +1793,14 @@ void D_SRB2Main(void) // rei/miru: bootmap (Idea: starts the game on a predefined map) if (bootmap && !(M_CheckParm("-warp") && M_IsNextParm())) { - pstartmap = bootmap; + pstartmap = G_MapNumber(bootmap)+1; - if (pstartmap < 1 || pstartmap > NUMMAPS) - I_Error("Cannot warp to map %d (out of range)\n", pstartmap); - else + if (pstartmap > nummapheaders) { - autostart = true; + I_Error("Cannot warp to map %s (not found)\n", bootmap); } + + autostart = true; } if (autostart || netgame) @@ -1947,14 +1893,11 @@ void D_SRB2Main(void) if (server && !M_CheckParm("+map")) { - // Prevent warping to nonexistent levels - if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR) - I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap)); // 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. - else if (!dedicated && M_MapLocked(pstartmap)) + if (!dedicated && M_MapLocked(pstartmap)) I_Error("You need to unlock this level before you can warp to it!\n"); else { diff --git a/src/d_net.c b/src/d_net.c index d2c481d87..aab76b8fe 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -924,9 +924,9 @@ static void DebugPrintpacket(const char *header) netbuffer->u.servercfg.modifiedgame); break; case PT_SERVERINFO: - fprintf(debugfile, " '%s' player %d/%d, map %s, filenum %d, time %u \n", + fprintf(debugfile, " '%s' player %d/%d, filenum %d, time %u \n", netbuffer->u.serverinfo.servername, netbuffer->u.serverinfo.numberofplayer, - netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname, + netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.fileneedednum, (UINT32)LONG(netbuffer->u.serverinfo.time)); fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded, diff --git a/src/d_netcmd.c b/src/d_netcmd.c index d468cab45..2cc507cb8 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -399,6 +399,8 @@ static CV_PossibleValue_t kartencore_cons_t[] = {{-1, "Auto"}, {0, "Off"}, {1, " consvar_t cv_kartencore = CVAR_INIT ("kartencore", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartencore_cons_t, KartEncore_OnChange); static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}}; consvar_t cv_kartvoterulechanges = CVAR_INIT ("kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL); +static CV_PossibleValue_t kartgametypepreference_cons_t[] = {{-1, "None"}, {GT_RACE, "Race"}, {GT_BATTLE, "Battle"}, {0, NULL}}; +consvar_t cv_kartgametypepreference = CVAR_INIT ("kartgametypepreference", "None", CV_NETVAR, kartgametypepreference_cons_t, NULL); static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Kilometers"}, {2, "Miles"}, {3, "Fracunits"}, {4, "Percentage"}, {0, NULL}}; consvar_t cv_kartspeedometer = CVAR_INIT ("kartdisplayspeed", "Percentage", CV_SAVE, kartspeedometer_cons_t, NULL); // use tics in display static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}}; @@ -840,6 +842,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_recordmultiplayerdemos); CV_RegisterVar(&cv_netdemosyncquality); + CV_RegisterVar(&cv_netdemosize); CV_RegisterVar(&cv_shoutname); CV_RegisterVar(&cv_shoutcolor); @@ -1425,7 +1428,7 @@ UINT8 CanChangeSkin(INT32 playernum) return true; // Force skin in effect. - if ((cv_forceskin.value != -1) || (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')) + if (cv_forceskin.value != -1) return false; // Can change skin in intermission and whatnot. @@ -2603,8 +2606,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r if (delay != 2) { UINT8 flags = 0; - const char *mapname = G_BuildMapName(mapnum); - I_Assert(W_CheckNumForName(mapname) != LUMPERROR); + //I_Assert(W_CheckNumForName(mapname) != LUMPERROR); buf_p = buf; if (pencoremode) flags |= 1; @@ -2619,7 +2621,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r // new gametype value WRITEUINT8(buf_p, newgametype); - WRITESTRINGN(buf_p, mapname, MAX_WADPATH); + WRITEINT16(buf_p, mapnum); } if (delay == 1) @@ -2650,27 +2652,28 @@ void D_SetupVote(void) UINT8 buf[5*2]; // four UINT16 maps (at twice the width of a UINT8), and two gametypes UINT8 *p = buf; 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}; - if ((cv_kartencore.value == 1) && (gametyperules & GTR_CIRCUIT)) - WRITEUINT8(p, (gametype|0x80)); + if ((cv_kartencore.value == 1) && (gametypedefaultrules[gt] & GTR_CIRCUIT)) + WRITEUINT8(p, (gt|VOTEMODIFIER_ENCORE)); else - WRITEUINT8(p, gametype); + WRITEUINT8(p, gt); WRITEUINT8(p, secondgt); - secondgt &= ~0x80; + secondgt &= ~VOTEMODIFIER_ENCORE; for (i = 0; i < 4; i++) { UINT16 m; if (i == 2) // sometimes a different gametype m = G_RandMap(G_TOLFlag(secondgt), prevmap, ((secondgt != gametype) ? 2 : 0), 0, true, votebuffer); - else if (i >= 3) // unknown-random and force-unknown MAP HELL - m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, (i-2), (i < 4), votebuffer); + else if (i >= 3) // unknown-random and formerly force-unknown MAP HELL + m = G_RandMap(G_TOLFlag(gt), prevmap, 0, (i-2), (i < 4), votebuffer); else - m = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, true, votebuffer); + m = G_RandMap(G_TOLFlag(gt), prevmap, 0, 0, true, votebuffer); if (i < 3) - votebuffer[i] = m; // min() is a dumb workaround for gcc 4.4 array-bounds error + votebuffer[i] = m; WRITEUINT16(p, m); } @@ -3075,11 +3078,11 @@ static void Command_Map_f(void) */ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) { - char mapname[MAX_WADPATH+1]; UINT8 flags; INT32 resetplayer = 1, lastgametype; UINT8 skipprecutscene, FLS; boolean pencoremode; + INT16 mapnumber; forceresetplayers = deferencoremode = false; @@ -3116,7 +3119,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) FLS = ((flags & (1<<3)) != 0); - READSTRINGN(*cp, mapname, MAX_WADPATH); + mapnumber = READINT16(*cp); if (netgame) P_SetRandSeed(READUINT32(*cp)); @@ -3124,7 +3127,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) if (!skipprecutscene) { DEBFILE(va("Warping to %s [resetplayer=%d lastgametype=%d gametype=%d cpnd=%d]\n", - mapname, resetplayer, lastgametype, gametype, chmappending)); + G_BuildMapName(mapnumber), resetplayer, lastgametype, gametype, chmappending)); CON_LogMessage(M_GetText("Speeding off to level...\n")); } @@ -3140,7 +3143,11 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING; demo.savebutton = 0; - G_InitNew(pencoremode, mapname, resetplayer, skipprecutscene, FLS); + // clear this demo before recording a new one + if (demo.recording && !modeattacking) + G_CheckDemoStatus(); + + G_InitNew(pencoremode, mapnumber, resetplayer, skipprecutscene, FLS); if (demo.playback && !demo.timing) precache = true; if (demo.timing) @@ -5380,8 +5387,9 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) { INT32 i; UINT8 gt, secondgt; + INT16 tempvotelevels[4][2]; - if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + if (playernum != serverplayer) // admin shouldn't be able to set up vote... { CONS_Alert(CONS_WARNING, M_GetText("Illegal vote setup received from %s\n"), player_names[playernum]); if (server) @@ -5392,14 +5400,43 @@ static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) gt = (UINT8)READUINT8(*cp); secondgt = (UINT8)READUINT8(*cp); - for (i = 0; i < 4; i++) + // Strip illegal Encore flag. + if ((gt & VOTEMODIFIER_ENCORE) + && !(gametypedefaultrules[(gt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT)) { - votelevels[i][0] = (UINT16)READUINT16(*cp); - votelevels[i][1] = gt; - if (!mapheaderinfo[votelevels[i][0]]) - P_AllocMapHeader(votelevels[i][0]); + gt &= ~VOTEMODIFIER_ENCORE; } + for (i = 0; i < 4; i++) + { + tempvotelevels[i][0] = (UINT16)READUINT16(*cp); + tempvotelevels[i][1] = gt; + if (tempvotelevels[i][0] < nummapheaders && mapheaderinfo[tempvotelevels[i][0]]) + continue; + + if (server) + I_Error("Got_SetupVotecmd: Internal map ID %d not found (nummapheaders = %d)", tempvotelevels[i][0], nummapheaders); + CONS_Alert(CONS_WARNING, M_GetText("Vote setup with bad map ID %d received from %s\n"), tempvotelevels[i][0], player_names[playernum]); + return; + } + + tempvotelevels[2][1] = secondgt; + + memcpy(votelevels, tempvotelevels, sizeof(votelevels)); + + // If third entry has an illelegal Encore flag... (illelegal!?) + if ((secondgt & VOTEMODIFIER_ENCORE) + && !(gametypedefaultrules[(secondgt & ~VOTEMODIFIER_ENCORE)] & GTR_CIRCUIT)) + { + secondgt &= ~VOTEMODIFIER_ENCORE; + // Apply it to the second entry instead, gametype permitting! + if (gametypedefaultrules[gt] & GTR_CIRCUIT) + { + votelevels[1][1] |= VOTEMODIFIER_ENCORE; + } + } + + // Finally, set third entry's gametype/Encore status. votelevels[2][1] = secondgt; G_SetGamestate(GS_VOTING); @@ -5817,7 +5854,7 @@ static void Command_Togglemodified_f(void) static void Command_Archivetest_f(void) { - savebuffer_t save; + savebuffer_t save = {0}; UINT32 i, wrote; thinker_t *th; if (gamestate != GS_LEVEL) @@ -5833,9 +5870,11 @@ static void Command_Archivetest_f(void) ((mobj_t *)th)->mobjnum = i++; // allocate buffer - save.size = 1024; - save.buffer = save.p = ZZ_Alloc(save.size); - save.end = save.buffer + save.size; + if (P_SaveBufferAlloc(&save, 1024) == false) + { + CONS_Printf("Unable to allocate buffer.\n"); + return; + } // test archive CONS_Printf("LUA_Archive...\n"); @@ -5853,10 +5892,12 @@ static void Command_Archivetest_f(void) LUA_UnArchive(&save, true); i = READUINT8(save.p); if (i != 0x7F || wrote != (UINT32)(save.p - save.buffer)) + { CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(save.p - save.buffer)); + } // free buffer - Z_Free(save.buffer); + P_SaveBufferFree(&save); CONS_Printf("Done. No crash.\n"); } #endif diff --git a/src/d_netcmd.h b/src/d_netcmd.h index b624b9d6e..72f0a8890 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -84,6 +84,7 @@ extern consvar_t cv_kartfrantic; extern consvar_t cv_kartcomeback; extern consvar_t cv_kartencore; extern consvar_t cv_kartvoterulechanges; +extern consvar_t cv_kartgametypepreference; extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartvoices; extern consvar_t cv_kartbot; diff --git a/src/deh_lua.c b/src/deh_lua.c index 0f9132935..01ea87625 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -621,18 +621,6 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word) } return luaL_error(L, "skincolor '%s' could not be found.\n", word); } - else if (fastncmp("GRADE_",word,6)) - { - p = word+6; - for (i = 0; NIGHTSGRADE_LIST[i]; i++) - if (*p == NIGHTSGRADE_LIST[i]) - { - CacheAndPushConstant(L, word, i); - return 1; - } - if (mathlib) return luaL_error(L, "NiGHTS grade '%s' could not be found.\n", word); - return 0; - } else if (fastncmp("MN_",word,3)) { p = word+3; for (i = 0; i < NUMMENUTYPES; i++) diff --git a/src/deh_soc.c b/src/deh_soc.c index 6509e2f62..8843cb099 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -142,28 +142,53 @@ void clear_conditionsets(void) void clear_levels(void) { - INT16 i; - // This is potentially dangerous but if we're resetting these headers, // we may as well try to save some memory, right? - for (i = 0; i < NUMMAPS; ++i) + while (nummapheaders > 0) { - if (!mapheaderinfo[i] || i == (tutorialmap-1)) + nummapheaders--; + + if (!mapheaderinfo[nummapheaders]) + continue; + + if (strcmp(mapheaderinfo[nummapheaders]->lumpname, tutorialmap) == 0) // Sal: Is this needed...? continue; // Custom map header info // (no need to set num to 0, we're freeing the entire header shortly) - Z_Free(mapheaderinfo[i]->customopts); + Z_Free(mapheaderinfo[nummapheaders]->customopts); - P_DeleteFlickies(i); - P_DeleteGrades(i); + P_DeleteFlickies(nummapheaders); - Z_Free(mapheaderinfo[i]); - mapheaderinfo[i] = NULL; + Z_Free(mapheaderinfo[nummapheaders]->mainrecord); + + Patch_Free(mapheaderinfo[nummapheaders]->thumbnailPic); + Patch_Free(mapheaderinfo[nummapheaders]->minimapPic); + + Z_Free(mapheaderinfo[nummapheaders]->lumpname); + + Z_Free(mapheaderinfo[nummapheaders]); + mapheaderinfo[nummapheaders] = NULL; } - // Realloc the one for the current gamemap as a safeguard - P_AllocMapHeader(gamemap-1); + // Clear out the cache + { + cupheader_t *cup = kartcupheaders; + UINT8 i; + + while (cup) + { + for (i = 0; i < CUPCACHE_MAX; i++) + { + cup->cachedlevels[i] = NEXTMAP_INVALID; + } + cup = cup->next; + } + } + + // Exit the current gamemap as a safeguard + if (Playing()) + COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed } static boolean findFreeSlot(INT32 *num) @@ -1048,27 +1073,44 @@ static mapheader_lighting_t *usemaplighting(INT32 mapnum, const char *word) { if (fastncmp(word, "ENCORE", 6)) { - mapheaderinfo[mapnum-1]->use_encore_lighting = true; + mapheaderinfo[mapnum]->use_encore_lighting = true; - return &mapheaderinfo[mapnum-1]->lighting_encore; + return &mapheaderinfo[mapnum]->lighting_encore; } else { - return &mapheaderinfo[mapnum-1]->lighting; + return &mapheaderinfo[mapnum]->lighting; } } -void readlevelheader(MYFILE *f, INT32 num) +void readlevelheader(MYFILE *f, char * name) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; char *word2; //char *word3; // Non-uppercase version of word2 + char *tmp; INT32 i; - // Reset all previous map header information - P_AllocMapHeader((INT16)(num-1)); + INT32 num = G_MapNumber(name); + + if (num >= nummapheaders) + { + P_AllocMapHeader((INT16)(num = nummapheaders)); + } + else if (f->wad > mainwads) + { + // only mark as a major mod if it replaces an already-existing mapheaderinfo + G_SetGameModified(multiplayer, true); + } + + if (mapheaderinfo[num]->lumpname == NULL) + { + mapheaderinfo[num]->lumpname = Z_StrDup(name); + mapheaderinfo[num]->lumpnamehash = quickncasehash(mapheaderinfo[num]->lumpname, MAXMAPLUMPNAME); + } do { @@ -1106,16 +1148,15 @@ void readlevelheader(MYFILE *f, INT32 num) if (fastcmp(word, "LEVELNAME")) { - deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2, - sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); - strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once + deh_strlcpy(mapheaderinfo[num]->lvlttl, word2, + sizeof(mapheaderinfo[num]->lvlttl), va("Level header %d: levelname", num)); continue; } // CHEAP HACK: move this over here for lowercase subtitles if (fastcmp(word, "SUBTITLE")) { - deh_strlcpy(mapheaderinfo[num-1]->subttl, word2, - sizeof(mapheaderinfo[num-1]->subttl), va("Level header %d: subtitle", num)); + deh_strlcpy(mapheaderinfo[num]->subttl, word2, + sizeof(mapheaderinfo[num]->subttl), va("Level header %d: subtitle", num)); continue; } @@ -1137,19 +1178,19 @@ void readlevelheader(MYFILE *f, INT32 num) } // Sanity limit of 128 params - if (mapheaderinfo[num-1]->numCustomOptions == 128) + if (mapheaderinfo[num]->numCustomOptions == 128) { deh_warning("Level header %d: too many custom parameters", num); continue; } - j = mapheaderinfo[num-1]->numCustomOptions++; + j = mapheaderinfo[num]->numCustomOptions++; - mapheaderinfo[num-1]->customopts = - Z_Realloc(mapheaderinfo[num-1]->customopts, - sizeof(customoption_t) * mapheaderinfo[num-1]->numCustomOptions, PU_STATIC, NULL); + mapheaderinfo[num]->customopts = + Z_Realloc(mapheaderinfo[num]->customopts, + sizeof(customoption_t) * mapheaderinfo[num]->numCustomOptions, PU_STATIC, NULL); // Newly allocated - modoption = &mapheaderinfo[num-1]->customopts[j]; + modoption = &mapheaderinfo[num]->customopts[j]; strncpy(modoption->option, word, 31); modoption->option[31] = '\0'; @@ -1165,33 +1206,33 @@ void readlevelheader(MYFILE *f, INT32 num) if (fastcmp(word, "FLICKYLIST") || fastcmp(word, "ANIMALLIST")) { if (fastcmp(word2, "NONE")) - P_DeleteFlickies(num-1); + P_DeleteFlickies(num); else if (fastcmp(word2, "DEMO")) - P_SetDemoFlickies(num-1); + P_SetDemoFlickies(num); else if (fastcmp(word2, "ALL")) { mobjtype_t tmpflickies[MAXFLICKIES]; - for (mapheaderinfo[num-1]->numFlickies = 0; - ((mapheaderinfo[num-1]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type); - mapheaderinfo[num-1]->numFlickies++) - tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type; + for (mapheaderinfo[num]->numFlickies = 0; + ((mapheaderinfo[num]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num]->numFlickies].type); + mapheaderinfo[num]->numFlickies++) + tmpflickies[mapheaderinfo[num]->numFlickies] = FLICKYTYPES[mapheaderinfo[num]->numFlickies].type; - if (mapheaderinfo[num-1]->numFlickies) // just in case... + if (mapheaderinfo[num]->numFlickies) // just in case... { - size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies; - mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL); - M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize); + size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num]->numFlickies; + mapheaderinfo[num]->flickies = Z_Realloc(mapheaderinfo[num]->flickies, newsize, PU_STATIC, NULL); + M_Memcpy(mapheaderinfo[num]->flickies, tmpflickies, newsize); } } else { mobjtype_t tmpflickies[MAXFLICKIES]; - mapheaderinfo[num-1]->numFlickies = 0; + mapheaderinfo[num]->numFlickies = 0; tmp = strtok(word2,","); // get up to the first MAXFLICKIES flickies do { - if (mapheaderinfo[num-1]->numFlickies == MAXFLICKIES) // never going to get above that number + if (mapheaderinfo[num]->numFlickies == MAXFLICKIES) // never going to get above that number { deh_warning("Level header %d: too many flickies\n", num); break; @@ -1205,7 +1246,7 @@ void readlevelheader(MYFILE *f, INT32 num) //deh_warning("Level header %d: unknown flicky mobj type %s\n", num, tmp); -- no need for this line as get_mobjtype complains too continue; } - tmpflickies[mapheaderinfo[num-1]->numFlickies] = i; + tmpflickies[mapheaderinfo[num]->numFlickies] = i; } else // ...or a quick, limited selection of default flickies! { @@ -1218,17 +1259,17 @@ void readlevelheader(MYFILE *f, INT32 num) deh_warning("Level header %d: unknown flicky selection %s\n", num, tmp); continue; } - tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[i].type; + tmpflickies[mapheaderinfo[num]->numFlickies] = FLICKYTYPES[i].type; } - mapheaderinfo[num-1]->numFlickies++; + mapheaderinfo[num]->numFlickies++; } while ((tmp = strtok(NULL,",")) != NULL); - if (mapheaderinfo[num-1]->numFlickies) + if (mapheaderinfo[num]->numFlickies) { - size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies; - mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL); + size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num]->numFlickies; + mapheaderinfo[num]->flickies = Z_Realloc(mapheaderinfo[num]->flickies, newsize, PU_STATIC, NULL); // now we add them to the list! - M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize); + M_Memcpy(mapheaderinfo[num]->flickies, tmpflickies, newsize); } else deh_warning("Level header %d: no valid flicky types found\n", num); @@ -1238,64 +1279,32 @@ void readlevelheader(MYFILE *f, INT32 num) // Strings that can be truncated else if (fastcmp(word, "ZONETITLE")) { - deh_strlcpy(mapheaderinfo[num-1]->zonttl, word2, - sizeof(mapheaderinfo[num-1]->zonttl), va("Level header %d: zonetitle", num)); + deh_strlcpy(mapheaderinfo[num]->zonttl, word2, + sizeof(mapheaderinfo[num]->zonttl), va("Level header %d: zonetitle", num)); } else if (fastcmp(word, "SCRIPTNAME")) { - deh_strlcpy(mapheaderinfo[num-1]->scriptname, word2, - sizeof(mapheaderinfo[num-1]->scriptname), va("Level header %d: scriptname", num)); + deh_strlcpy(mapheaderinfo[num]->scriptname, word2, + sizeof(mapheaderinfo[num]->scriptname), va("Level header %d: scriptname", num)); } else if (fastcmp(word, "RUNSOC")) { - deh_strlcpy(mapheaderinfo[num-1]->runsoc, word2, - sizeof(mapheaderinfo[num-1]->runsoc), va("Level header %d: runsoc", num)); + deh_strlcpy(mapheaderinfo[num]->runsoc, word2, + sizeof(mapheaderinfo[num]->runsoc), va("Level header %d: runsoc", num)); } else if (fastcmp(word, "ACT")) { /*if (i >= 0 && i < 20) // 0 for no act number, TTL1 through TTL19 - mapheaderinfo[num-1]->actnum = (UINT8)i; + mapheaderinfo[num]->actnum = (UINT8)i; else deh_warning("Level header %d: invalid act number %d", num, i);*/ - deh_strlcpy(mapheaderinfo[num-1]->actnum, word2, - sizeof(mapheaderinfo[num-1]->actnum), va("Level header %d: actnum", num)); - } - else if (fastcmp(word, "NEXTLEVEL")) - { - if (fastcmp(word2, "TITLE")) i = 1100; - else if (fastcmp(word2, "EVALUATION")) i = 1101; - else if (fastcmp(word2, "CREDITS")) i = 1102; - else if (fastcmp(word2, "ENDING")) i = 1103; - else - // Support using the actual map name, - // i.e., Nextlevel = AB, Nextlevel = FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - - mapheaderinfo[num-1]->nextlevel = (INT16)i; - } - else if (fastcmp(word, "MARATHONNEXT")) - { - if (fastcmp(word2, "TITLE")) i = 1100; - else if (fastcmp(word2, "EVALUATION")) i = 1101; - else if (fastcmp(word2, "CREDITS")) i = 1102; - else if (fastcmp(word2, "ENDING")) i = 1103; - else - // Support using the actual map name, - // i.e., MarathonNext = AB, MarathonNext = FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - - mapheaderinfo[num-1]->marathonnext = (INT16)i; + deh_strlcpy(mapheaderinfo[num]->actnum, word2, + sizeof(mapheaderinfo[num]->actnum), va("Level header %d: actnum", num)); } else if (fastcmp(word, "TYPEOFLEVEL")) { if (i) // it's just a number - mapheaderinfo[num-1]->typeoflevel = (UINT32)i; + mapheaderinfo[num]->typeoflevel = (UINT32)i; else { UINT32 tol = 0; @@ -1308,20 +1317,20 @@ void readlevelheader(MYFILE *f, INT32 num) deh_warning("Level header %d: unknown typeoflevel flag %s\n", num, tmp); tol |= TYPEOFLEVEL[i].flag; } while((tmp = strtok(NULL,",")) != NULL); - mapheaderinfo[num-1]->typeoflevel = tol; + mapheaderinfo[num]->typeoflevel = tol; } } else if (fastcmp(word, "KEYWORDS")) { - deh_strlcpy(mapheaderinfo[num-1]->keywords, word2, - sizeof(mapheaderinfo[num-1]->keywords), va("Level header %d: keywords", num)); + deh_strlcpy(mapheaderinfo[num]->keywords, word2, + sizeof(mapheaderinfo[num]->keywords), va("Level header %d: keywords", num)); } else if (fastcmp(word, "MUSIC")) { if (fastcmp(word2, "NONE")) { - mapheaderinfo[num-1]->musname[0][0] = 0; // becomes empty string - mapheaderinfo[num-1]->musname_size = 0; + mapheaderinfo[num]->musname[0][0] = 0; // becomes empty string + mapheaderinfo[num]->musname_size = 0; } else { @@ -1330,71 +1339,66 @@ void readlevelheader(MYFILE *f, INT32 num) do { if (j >= MAXMUSNAMES) break; - deh_strlcpy(mapheaderinfo[num-1]->musname[j], tmp, - sizeof(mapheaderinfo[num-1]->musname[j]), va("Level header %d: music", num)); + deh_strlcpy(mapheaderinfo[num]->musname[j], tmp, + sizeof(mapheaderinfo[num]->musname[j]), va("Level header %d: music", num)); j++; } while ((tmp = strtok(NULL,",")) != NULL); if (tmp != NULL) deh_warning("Level header %d: additional music slots past %d discarded", num, MAXMUSNAMES); - mapheaderinfo[num-1]->musname_size = j; + mapheaderinfo[num]->musname_size = j; } } else if (fastcmp(word, "MUSICSLOT")) deh_warning("Level header %d: MusicSlot parameter is deprecated and will be removed.\nUse \"Music\" instead.", num); else if (fastcmp(word, "MUSICTRACK")) - mapheaderinfo[num-1]->mustrack = ((UINT16)i - 1); + mapheaderinfo[num]->mustrack = ((UINT16)i - 1); else if (fastcmp(word, "MUSICPOS")) - mapheaderinfo[num-1]->muspos = (UINT32)get_number(word2); - else if (fastcmp(word, "FORCECHARACTER")) - { - strlcpy(mapheaderinfo[num-1]->forcecharacter, word2, SKINNAMESIZE+1); - strlwr(mapheaderinfo[num-1]->forcecharacter); // skin names are lowercase - } + mapheaderinfo[num]->muspos = (UINT32)get_number(word2); else if (fastcmp(word, "WEATHER")) - mapheaderinfo[num-1]->weather = get_precip(word2); + mapheaderinfo[num]->weather = get_precip(word2); else if (fastcmp(word, "SKYTEXTURE")) - deh_strlcpy(mapheaderinfo[num-1]->skytexture, word2, - sizeof(mapheaderinfo[num-1]->skytexture), va("Level header %d: sky texture", num)); + deh_strlcpy(mapheaderinfo[num]->skytexture, word2, + sizeof(mapheaderinfo[num]->skytexture), va("Level header %d: sky texture", num)); else if (fastcmp(word, "SKYNUM")) - deh_strlcpy(mapheaderinfo[num-1]->skytexture, va("SKY%s", word2), - sizeof(mapheaderinfo[num-1]->skytexture), va("Level header %d: sky texture", num)); + deh_strlcpy(mapheaderinfo[num]->skytexture, va("SKY%s", word2), + sizeof(mapheaderinfo[num]->skytexture), va("Level header %d: sky texture", num)); else if (fastcmp(word, "PRECUTSCENENUM")) - mapheaderinfo[num-1]->precutscenenum = (UINT8)i; + mapheaderinfo[num]->precutscenenum = (UINT8)i; else if (fastcmp(word, "CUTSCENENUM")) - mapheaderinfo[num-1]->cutscenenum = (UINT8)i; + mapheaderinfo[num]->cutscenenum = (UINT8)i; else if (fastcmp(word, "PALETTE")) - mapheaderinfo[num-1]->palette = (UINT16)i; + mapheaderinfo[num]->palette = (UINT16)i; else if (fastcmp(word, "ENCOREPAL")) - mapheaderinfo[num-1]->encorepal = (UINT16)i; + mapheaderinfo[num]->encorepal = (UINT16)i; else if (fastcmp(word, "NUMLAPS")) - mapheaderinfo[num-1]->numlaps = (UINT8)i; + mapheaderinfo[num]->numlaps = (UINT8)i; else if (fastcmp(word, "UNLOCKABLE")) { if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something - mapheaderinfo[num-1]->unlockrequired = (SINT8)i - 1; + mapheaderinfo[num]->unlockrequired = (SINT8)i - 1; else deh_warning("Level header %d: invalid unlockable number %d", num, i); } else if (fastcmp(word, "LEVELSELECT")) - mapheaderinfo[num-1]->levelselect = (UINT8)i; + mapheaderinfo[num]->levelselect = (UINT8)i; else if (fastcmp(word, "SKYBOXSCALE")) - mapheaderinfo[num-1]->skybox_scalex = mapheaderinfo[num-1]->skybox_scaley = mapheaderinfo[num-1]->skybox_scalez = (INT16)i; + mapheaderinfo[num]->skybox_scalex = mapheaderinfo[num]->skybox_scaley = mapheaderinfo[num]->skybox_scalez = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEX")) - mapheaderinfo[num-1]->skybox_scalex = (INT16)i; + mapheaderinfo[num]->skybox_scalex = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEY")) - mapheaderinfo[num-1]->skybox_scaley = (INT16)i; + mapheaderinfo[num]->skybox_scaley = (INT16)i; else if (fastcmp(word, "SKYBOXSCALEZ")) - mapheaderinfo[num-1]->skybox_scalez = (INT16)i; + mapheaderinfo[num]->skybox_scalez = (INT16)i; else if (fastcmp(word, "LEVELFLAGS")) - mapheaderinfo[num-1]->levelflags = get_number(word2); + mapheaderinfo[num]->levelflags = get_number(word2); else if (fastcmp(word, "MENUFLAGS")) - mapheaderinfo[num-1]->menuflags = get_number(word2); + mapheaderinfo[num]->menuflags = get_number(word2); // SRB2Kart else if (fastcmp(word, "MOBJSCALE")) - mapheaderinfo[num-1]->mobj_scale = get_number(word2); + mapheaderinfo[num]->mobj_scale = get_number(word2); else if (fastcmp(word, "DEFAULTWAYPOINTRADIUS")) - mapheaderinfo[num-1]->default_waypoint_radius = get_number(word2); + mapheaderinfo[num]->default_waypoint_radius = get_number(word2); else if (fastcmp(word, "LIGHTCONTRAST")) { usemaplighting(num, word)->light_contrast = (UINT8)i; @@ -1422,77 +1426,80 @@ void readlevelheader(MYFILE *f, INT32 num) else if (fastcmp(word, "SCRIPTISFILE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SCRIPTISFILE; + mapheaderinfo[num]->levelflags |= LF_SCRIPTISFILE; else - mapheaderinfo[num-1]->levelflags &= ~LF_SCRIPTISFILE; + mapheaderinfo[num]->levelflags &= ~LF_SCRIPTISFILE; } else if (fastcmp(word, "NOZONE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NOZONE; + mapheaderinfo[num]->levelflags |= LF_NOZONE; else - mapheaderinfo[num-1]->levelflags &= ~LF_NOZONE; + mapheaderinfo[num]->levelflags &= ~LF_NOZONE; } else if (fastcmp(word, "SECTIONRACE")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SECTIONRACE; + mapheaderinfo[num]->levelflags |= LF_SECTIONRACE; else - mapheaderinfo[num-1]->levelflags &= ~LF_SECTIONRACE; + mapheaderinfo[num]->levelflags &= ~LF_SECTIONRACE; } else if (fastcmp(word, "SUBTRACTNUM")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SUBTRACTNUM; + mapheaderinfo[num]->levelflags |= LF_SUBTRACTNUM; else - mapheaderinfo[num-1]->levelflags &= ~LF_SUBTRACTNUM; + mapheaderinfo[num]->levelflags &= ~LF_SUBTRACTNUM; } // Individual triggers for menu flags else if (fastcmp(word, "HIDDEN")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_HIDEINMENU; + mapheaderinfo[num]->menuflags |= LF2_HIDEINMENU; else - mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINMENU; + mapheaderinfo[num]->menuflags &= ~LF2_HIDEINMENU; } else if (fastcmp(word, "HIDEINSTATS")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_HIDEINSTATS; + mapheaderinfo[num]->menuflags |= LF2_HIDEINSTATS; else - mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINSTATS; + mapheaderinfo[num]->menuflags &= ~LF2_HIDEINSTATS; } - else if (fastcmp(word, "TIMEATTACK") || fastcmp(word, "RECORDATTACK")) + else if (fastcmp(word, "NOTIMEATTACK") || fastcmp(word, "NORECORDATTACK")) { // RECORDATTACK is an accepted alias if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags &= ~LF2_NOTIMEATTACK; + mapheaderinfo[num]->menuflags |= LF2_NOTIMEATTACK; else - mapheaderinfo[num-1]->menuflags |= LF2_NOTIMEATTACK; + mapheaderinfo[num]->menuflags &= ~LF2_NOTIMEATTACK; } else if (fastcmp(word, "VISITNEEDED")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_VISITNEEDED; + mapheaderinfo[num]->menuflags |= LF2_VISITNEEDED; else - mapheaderinfo[num-1]->menuflags &= ~LF2_VISITNEEDED; + mapheaderinfo[num]->menuflags &= ~LF2_VISITNEEDED; } else if (fastcmp(word, "NOVISITNEEDED")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags &= ~LF2_VISITNEEDED; + mapheaderinfo[num]->menuflags &= ~LF2_VISITNEEDED; else - mapheaderinfo[num-1]->menuflags |= LF2_VISITNEEDED; + mapheaderinfo[num]->menuflags |= LF2_VISITNEEDED; } else if (fastcmp(word, "GRAVITY")) - mapheaderinfo[num-1]->gravity = FLOAT_TO_FIXED(atof(word2)); + mapheaderinfo[num]->gravity = FLOAT_TO_FIXED(atof(word2)); else if (fastcmp(word, "WALLTRANSFER") || fastcmp(word, "WALLRUNNING")) { if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->use_walltransfer = true; + mapheaderinfo[num]->use_walltransfer = true; else - mapheaderinfo[num-1]->use_walltransfer = false; + mapheaderinfo[num]->use_walltransfer = false; } + // ignored for compatibility + else if (fastcmp(word, "NEXTLEVEL") || fastcmp(word, "TIMEATTACK") || fastcmp(word, "RECORDATTACK")) + continue; else deh_warning("Level header %d: unknown word '%s'", num, word); } @@ -2555,16 +2562,9 @@ void reademblemdata(MYFILE *f, INT32 num) } else if (fastcmp(word, "TAG")) emblemlocations[num-1].tag = (INT16)value; - else if (fastcmp(word, "MAPNUM")) + else if (fastcmp(word, "MAPNAME")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - - emblemlocations[num-1].level = (INT16)value; + emblemlocations[num-1].level = Z_StrDup(word2); } else if (fastcmp(word, "SPRITE")) { @@ -2785,14 +2785,8 @@ void readunlockable(MYFILE *f, INT32 num) } else if (fastcmp(word, "VAR")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - i = M_MapNumber(word2[0], word2[1]); - - unlockables[num].variable = (INT16)i; + // TODO: different field for level name string + unlockables[num].variable = (INT16)G_MapNumber(word2); } else deh_warning("Unlockable %d: unknown word '%s'", num+1, word); @@ -2830,7 +2824,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (!params[0]) { - deh_warning("condition line is empty"); + deh_warning("condition line is empty for condition ID %d", id); return; } @@ -2850,7 +2844,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (x1 < 0 || x1 >= PWRLV_NUMTYPES) { - deh_warning("Power level type %d out of range (0 - %d)", x1, PWRLV_NUMTYPES-1); + deh_warning("Power level type %d out of range (0 - %d) for condition ID %d", x1, PWRLV_NUMTYPES-1, id); return; } } @@ -2870,16 +2864,11 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) { PARAMCHECK(1); ty = UC_MAPVISITED + offset; + re = G_MapNumber(params[1]); - // Convert to map number if it appears to be one - if (params[1][0] >= 'A' && params[1][0] <= 'Z') - re = M_MapNumber(params[1][0], params[1][1]); - else - re = atoi(params[1]); - - if (re < 0 || re >= NUMMAPS) + if (re >= nummapheaders) { - deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); + deh_warning("Invalid level %s for condition ID %d", params[1], id); return; } } @@ -2888,16 +2877,11 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) PARAMCHECK(2); ty = UC_MAPTIME; re = atoi(params[2]); + x1 = G_MapNumber(params[1]); - // Convert to map number if it appears to be one - if (params[1][0] >= 'A' && params[1][0] <= 'Z') - x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); - else - x1 = (INT16)atoi(params[1]); - - if (x1 < 0 || x1 >= NUMMAPS) + if (x1 >= nummapheaders) { - deh_warning("Level number %d out of range (1 - %d)", x1, NUMMAPS); + deh_warning("Invalid level %s for condition ID %d", params[1], id); return; } } @@ -2910,7 +2894,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) // constrained by 32 bits if (re < 0 || re > 31) { - deh_warning("Trigger ID %d out of range (0 - 31)", re); + deh_warning("Trigger ID %d out of range (0 - 31) for condition ID %d", re, id); return; } } @@ -2928,7 +2912,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (re <= 0 || re > MAXEMBLEMS) { - deh_warning("Emblem %d out of range (1 - %d)", re, MAXEMBLEMS); + deh_warning("Emblem %d out of range (1 - %d) for condition ID %d", re, MAXEMBLEMS, id); return; } } @@ -2940,7 +2924,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (re <= 0 || re > MAXEXTRAEMBLEMS) { - deh_warning("Extra emblem %d out of range (1 - %d)", re, MAXEXTRAEMBLEMS); + deh_warning("Extra emblem %d out of range (1 - %d) for condition ID %d", re, MAXEXTRAEMBLEMS, id); return; } } @@ -2952,13 +2936,13 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) if (re <= 0 || re > MAXCONDITIONSETS) { - deh_warning("Condition set %d out of range (1 - %d)", re, MAXCONDITIONSETS); + deh_warning("Condition set %d out of range (1 - %d) for condition ID %d", re, MAXCONDITIONSETS, id); return; } } else { - deh_warning("Invalid condition name %s", params[0]); + deh_warning("Invalid condition name %s for condition ID %d", params[0], id); return; } @@ -3093,61 +3077,6 @@ void readmaincfg(MYFILE *f) COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); } } - - else if (fastcmp(word, "SPSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - spstage_start = spmarathon_start = (INT16)value; - } - else if (fastcmp(word, "SPMARATHON_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - spmarathon_start = (INT16)value; - } - else if (fastcmp(word, "SSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - sstage_start = (INT16)value; - sstage_end = (INT16)(sstage_start+7); // 7 special stages total plus one weirdo - } - else if (fastcmp(word, "SMPSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - smpstage_start = (INT16)value; - smpstage_end = (INT16)(smpstage_start+6); // 7 special stages total - } else if (fastcmp(word, "REDTEAM")) { skincolor_redteam = (UINT16)get_number(word2); @@ -3230,16 +3159,7 @@ void readmaincfg(MYFILE *f) } else if (fastcmp(word, "TITLEMAP")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - titlemap = (INT16)value; + titlemap = Z_StrDup(word2); titlechanged = true; } else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE")) @@ -3365,30 +3285,12 @@ void readmaincfg(MYFILE *f) } else if (fastcmp(word, "BOOTMAP")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - bootmap = (INT16)value; + bootmap = Z_StrDup(word2); //titlechanged = true; } else if (fastcmp(word, "TUTORIALMAP")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - tutorialmap = (INT16)value; + tutorialmap = Z_StrDup(word2); } else deh_warning("Maincfg: unknown word '%s'", word); @@ -3600,7 +3502,16 @@ void readcupheader(MYFILE *f, cupheader_t *cup) i = atoi(word2); // used for numerical settings strupr(word2); - if (fastcmp(word, "ICON")) + if (fastcmp(word, "MONITOR")) + { + if (i > 0 && i < 10) + cup->monitor = i; + else if (!word2[0] || word2[1] != '\0' || word2[0] == '0') + deh_warning("%s Cup: Invalid monitor type \"%s\" (should be 1-9 or A-Z)\n", cup->name, word2); + else + cup->monitor = (word2[0] - 'A') + 10; + } + else if (fastcmp(word, "ICON")) { deh_strlcpy(cup->icon, word2, sizeof(cup->icon), va("%s Cup: icon", cup->name)); @@ -3611,37 +3522,26 @@ void readcupheader(MYFILE *f, cupheader_t *cup) tmp = strtok(word2,","); do { - INT32 map = atoi(tmp); - - if (tmp[0] >= 'A' && tmp[0] <= 'Z' && tmp[2] == '\0') - map = M_MapNumber(tmp[0], tmp[1]); - - if (!map) - break; - if (cup->numlevels >= MAXLEVELLIST) { deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST); break; } - cup->levellist[cup->numlevels] = map - 1; + cup->levellist[cup->numlevels] = Z_StrDup(tmp); + cup->cachedlevels[cup->numlevels] = NEXTMAP_INVALID; cup->numlevels++; } while((tmp = strtok(NULL,",")) != NULL); } else if (fastcmp(word, "BONUSGAME")) { - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - cup->bonusgame = (INT16)i - 1; + cup->levellist[CUPCACHE_BONUS] = Z_StrDup(word2); + cup->cachedlevels[CUPCACHE_BONUS] = NEXTMAP_INVALID; } else if (fastcmp(word, "SPECIALSTAGE")) { - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - cup->specialstage = (INT16)i - 1; + cup->levellist[CUPCACHE_SPECIAL] = Z_StrDup(word2); + cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID; } else if (fastcmp(word, "EMERALDNUM")) { @@ -4374,19 +4274,6 @@ static fixed_t find_const(const char **rword) free(word); return 0; } - else if (fastncmp("GRADE_",word,6)) - { - char *p = word+6; - for (i = 0; NIGHTSGRADE_LIST[i]; i++) - if (*p == NIGHTSGRADE_LIST[i]) - { - free(word); - return i; - } - const_warning("NiGHTS grade",word); - free(word); - return 0; - } for (i = 0; INT_CONST[i].n; i++) if (fastcmp(word,INT_CONST[i].n)) { free(word); diff --git a/src/deh_soc.h b/src/deh_soc.h index d8a652579..cb24db595 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -73,7 +73,7 @@ void readhuditem(MYFILE *f, INT32 num); void readmenu(MYFILE *f, INT32 num); void readtextprompt(MYFILE *f, INT32 num); void readcutscene(MYFILE *f, INT32 num); -void readlevelheader(MYFILE *f, INT32 num); +void readlevelheader(MYFILE *f, char * name); void readgametype(MYFILE *f, char *gtname); void readsprite2(MYFILE *f, INT32 num); #ifdef HWRENDER diff --git a/src/deh_tables.c b/src/deh_tables.c index e901efd31..a0e920249 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -35,17 +35,6 @@ char *FREE_MOBJS[NUMMOBJFREESLOTS]; char *FREE_SKINCOLORS[NUMCOLORFREESLOTS]; UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway. -const char NIGHTSGRADE_LIST[] = { - 'F', // GRADE_F - 'E', // GRADE_E - 'D', // GRADE_D - 'C', // GRADE_C - 'B', // GRADE_B - 'A', // GRADE_A - 'S', // GRADE_S - '\0' -}; - struct flickytypes_s FLICKYTYPES[] = { {"BLUEBIRD", MT_FLICKY_01}, // Flicky (Flicky) {"RABBIT", MT_FLICKY_02}, // Pocky (1) diff --git a/src/deh_tables.h b/src/deh_tables.h index 629d0ce87..484cb281b 100644 --- a/src/deh_tables.h +++ b/src/deh_tables.h @@ -57,7 +57,6 @@ struct int_const_s { lua_Integer v; }; -extern const char NIGHTSGRADE_LIST[]; extern struct flickytypes_s FLICKYTYPES[]; extern actionpointer_t actionpointers[]; // Array mapping action names to action functions. extern const char *const STATE_LIST[]; diff --git a/src/dehacked.c b/src/dehacked.c index 211223ec0..9ce541cb4 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -368,25 +368,19 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) #endif else if (fastcmp(word, "LEVEL")) { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - i = M_MapNumber(word2[0], word2[1]); - - if (i > 0 && i <= NUMMAPS) + size_t len = strlen(word2); + if (len <= MAXMAPLUMPNAME-1) { - if (mapheaderinfo[i]) - { - G_SetGameModified(multiplayer, true); // Only a major mod if editing stuff that isn't your own! - } - - readlevelheader(f, i); + if (len == 1) + readlevelheader(f, va("MAP0%s",word2)); + else if (len == 2) + readlevelheader(f, va("MAP%s",word2)); + else + readlevelheader(f, word2); } else { - deh_warning("Level number %d out of range (1 - %d)", i, NUMMAPS); + deh_warning("Map header's lumpname %s is too long (%s characters VS %d max)", word2, sizeu1(len), (MAXMAPLUMPNAME-1)); ignorelines(f); } } @@ -519,38 +513,51 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) // else if (fastcmp(word, "CUP")) { - cupheader_t *cup = kartcupheaders; - cupheader_t *prev = NULL; - - while (cup) + size_t len = strlen(word2); + if (len <= MAXCUPNAME-1) { - if (fastcmp(cup->name, word2)) + cupheader_t *cup = kartcupheaders; + cupheader_t *prev = NULL; + UINT32 hash = quickncasehash(word2, MAXCUPNAME); + + while (cup) { - // Only a major mod if editing stuff that isn't your own! - G_SetGameModified(multiplayer, true); - break; + if (hash == cup->namehash && fastcmp(cup->name, word2)) + { + // Only a major mod if editing stuff that isn't your own! + G_SetGameModified(multiplayer, true); + break; + } + + prev = cup; + cup = cup->next; } - prev = cup; - cup = cup->next; - } + // Nothing found, add to the end. + if (!cup) + { + cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL); + cup->id = numkartcupheaders; + cup->monitor = 1; + deh_strlcpy(cup->name, word2, + sizeof(cup->name), va("Cup header %s: name", word2)); + cup->namehash = hash; + if (prev != NULL) + prev->next = cup; + if (kartcupheaders == NULL) + kartcupheaders = cup; + numkartcupheaders++; + CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name); + } - // Nothing found, add to the end. - if (!cup) + readcupheader(f, cup); + + } + else { - cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL); - cup->id = numkartcupheaders; - deh_strlcpy(cup->name, word2, - sizeof(cup->name), va("Cup header %s: name", word2)); - if (prev != NULL) - prev->next = cup; - if (kartcupheaders == NULL) - kartcupheaders = cup; - numkartcupheaders++; - CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name); + deh_warning("Cup header's name %s is too long (%s characters VS %d max)", word2, sizeu1(len), (MAXCUPNAME-1)); + ignorelines(f); } - - readcupheader(f, cup); } else if (fastcmp(word, "WEATHER") || fastcmp(word, "PRECIP") || fastcmp(word, "PRECIPITATION")) { diff --git a/src/discord.c b/src/discord.c index 4a361a3f0..4e8af70eb 100644 --- a/src/discord.c +++ b/src/discord.c @@ -442,7 +442,6 @@ void DRPC_UpdatePresence(void) char detailstr[48+1]; - char mapimg[8+1]; char mapname[5+21+21+2+1]; char charimg[4+SKINNAMESIZE+1]; @@ -545,14 +544,7 @@ void DRPC_UpdatePresence(void) if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info && !(demo.playback && demo.title)) { - if ((gamemap >= 1 && gamemap <= 60) // supported race maps - || (gamemap >= 136 && gamemap <= 164)) // supported battle maps - { - snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap)); - strlwr(mapimg); - discordPresence.largeImageKey = mapimg; // Map image - } - else if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) + if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU) { // Hell map, use the method that got you here :P discordPresence.largeImageKey = "miscdice"; diff --git a/src/doomdata.h b/src/doomdata.h index bb4a5b27a..03686a537 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -289,8 +289,6 @@ struct mapthing_t #define ZSHIFT 4 -#define NUMMAPS 1035 - /* slope thing types */ enum { diff --git a/src/doomdef.h b/src/doomdef.h index a2c0bec26..75487e48a 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -344,6 +344,7 @@ extern char liveeventbackup[256]; void M_StartupLocale(void); void *M_Memcpy(void* dest, const void* src, size_t n); char *va(const char *format, ...) FUNCPRINTF; +char *xva(const char *format, ...) FUNCPRINTF; char *M_GetToken(const char *inputString); void M_UnGetToken(void); @@ -386,6 +387,7 @@ typedef enum DBG_SETUP = 0x00000400, DBG_LUA = 0x00000800, DBG_RNG = 0x00001000, + DBG_DEMO = 0x00002000, } debugFlags_t; struct debugFlagNames_s diff --git a/src/doomstat.h b/src/doomstat.h index 9ae5fdeb6..7c1ce5491 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -107,6 +107,23 @@ extern preciptype_t precip_freeslot; extern preciptype_t globalweather; extern preciptype_t curWeather; +/** Time attack information, currently a very small structure. + */ +struct recorddata_t +{ + tic_t time; ///< Time in which the level was finished. + tic_t lap; ///< Best lap time for this level. + //UINT32 score; ///< Score when the level was finished. + //UINT16 rings; ///< Rings when the level was finished. +}; + +// mapvisited is now a set of flags that says what we've done in the map. +#define MV_VISITED (1) +#define MV_BEATEN (1<<1) +#define MV_ENCORE (1<<2) +#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE) +#define MV_MP ((MV_MAX+1)<<1) + // Set if homebrew PWAD stuff has been added. extern boolean modifiedgame; extern boolean majormods; @@ -190,15 +207,11 @@ extern INT32 splitscreen_party[MAXPLAYERS][MAXSPLITSCREENPLAYERS]; /* the only local one */ extern boolean splitscreen_partied[MAXPLAYERS]; -// Maps of special importance -extern INT16 spstage_start, spmarathon_start; -extern INT16 sstage_start, sstage_end, smpstage_start, smpstage_end; - -extern INT16 titlemap; +extern char * titlemap; extern boolean hidetitlepics; -extern INT16 bootmap; //bootmap for loading a map on startup +extern char * bootmap; //bootmap for loading a map on startup -extern INT16 tutorialmap; // map to load for tutorial +extern char * tutorialmap; // map to load for tutorial extern boolean tutorialmode; // are we in a tutorial right now? extern INT32 tutorialgcs; // which control scheme is loaded? @@ -207,8 +220,6 @@ extern boolean looptitle; // CTF colors. extern UINT16 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering; -extern tic_t countdowntimer; -extern boolean countdowntimeup; extern boolean exitfadestarted; struct scene_t @@ -299,8 +310,6 @@ extern textprompt_t *textprompts[MAX_PROMPTS]; extern INT16 nextmapoverride; extern UINT8 skipstats; -extern UINT32 ssspheres; // Total # of spheres in a level - // Fun extra stuff extern INT16 lastmap; // Last level you were at (returning from special stages). @@ -321,12 +330,6 @@ extern struct quake fixed_t radius, intensity; } quake; -// NiGHTS grades -typedef struct -{ - UINT32 grade[6]; // D, C, B, A, S, X (F: failed to reach any of these) -} nightsgrades_t; - // Custom Lua values struct customoption_t { @@ -344,96 +347,115 @@ struct mapheader_lighting_t #define MAXMUSNAMES 3 // maximum definable music tracks per level +// This could support more, but is that a good idea? +// 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 MAXCUPNAME 16 // includes \0, for cleaner savedata + +struct cupheader_t +{ + UINT16 id; ///< Cup ID + UINT8 monitor; ///< Monitor graphic 1-9 or A-Z + + char name[MAXCUPNAME]; ///< Cup title + UINT32 namehash; ///< Cup title hash + + char icon[9]; ///< Name of the icon patch + 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 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 +}; + +extern cupheader_t *kartcupheaders; // Start of cup linked list +extern UINT16 numkartcupheaders; + +#define MAXMAPLUMPNAME 64 // includes \0, for cleaner savedata + /** Map header information. */ struct mapheader_t { - // The original eight, plus one. - char lvlttl[22]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway) - char subttl[33]; ///< Subtitle for level - char zonttl[22]; ///< "ZONE" replacement name - char actnum[3]; ///< SRB2Kart: Now a 2 character long string. - UINT32 typeoflevel; ///< Combination of typeoflevel flags. - INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end. - INT16 marathonnext; ///< See nextlevel, but for Marathon mode. Necessary to support hub worlds ala SUGOI. - char keywords[33]; ///< Keywords separated by space to search for. 32 characters. - char forcecharacter[17]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable. - UINT8 weather; ///< 0 = sunny day, 1 = storm, 2 = snow, 3 = rain, 4 = blank, 5 = thunder w/o rain, 6 = rain w/o lightning, 7 = heat wave. - char skytexture[9]; ///< Sky texture to use. - INT16 skybox_scalex; ///< Skybox X axis scale. (0 = no movement, 1 = 1:1 movement, 16 = 16:1 slow movement, -4 = 1:4 fast movement, etc.) - INT16 skybox_scaley; ///< Skybox Y axis scale. - INT16 skybox_scalez; ///< Skybox Z axis scale. + // Core game information, not user-modifiable directly + char *lumpname; ///< Lump name can be really long + UINT32 lumpnamehash; ///< quickncasehash(->lumpname, MAXMAPLUMPNAME) + lumpnum_t lumpnum; ///< Lump number for the map, used by vres_GetMap - // Extra information. - char interscreen[8]; ///< 320x200 patch to display at intermission. - char runsoc[33]; ///< SOC to execute at start of level (32 character limit instead of 63) - char scriptname[33]; ///< Script to use when the map is switched to. (32 character limit instead of 191) - UINT8 precutscenenum; ///< Cutscene number to play BEFORE a level starts. - UINT8 cutscenenum; ///< Cutscene number to use, 0 for none. - INT16 countdown; ///< Countdown until level end? - UINT16 palette; ///< PAL lump to use on this map - UINT16 encorepal; ///< PAL for encore mode - UINT8 numlaps; ///< Number of laps in circuit mode, unless overridden. - SINT8 unlockrequired; ///< Is an unlockable required to play this level? -1 if no. - UINT8 levelselect; ///< Is this map available in the level select? If so, which map list is it available in? - SINT8 bonustype; ///< What type of bonus does this level have? (-1 for null.) - SINT8 maxbonuslives; ///< How many bonus lives to award at Intermission? (-1 for unlimited.) + void *thumbnailPic; ///< Lump data for the level select thumbnail. + void *minimapPic; ///< Lump data for the minimap graphic. - UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below - UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus + UINT8 mapvisited; ///< A set of flags that says what we've done in the map. + recorddata_t *mainrecord; ///< Stores best time attack data - char selectheading[22]; ///< Level select heading. Allows for controllable grouping. - UINT16 startrings; ///< Number of rings players start with. - INT32 sstimer; ///< Timer for special stages. - UINT32 ssspheres; ///< Sphere requirement in special stages. - fixed_t gravity; ///< Map-wide gravity. + cupheader_t *cup; ///< Cached cup - // Title card. - char ltzzpatch[8]; ///< Zig zag patch. - char ltzztext[8]; ///< Zig zag text. - char ltactdiamond[8]; ///< Act diamond. + // Titlecard information + char lvlttl[22]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway) + char subttl[33]; ///< Subtitle for level + char zonttl[22]; ///< "ZONE" replacement name + char actnum[3]; ///< SRB2Kart: Now a 2 character long string. - // Freed animals stuff. - UINT8 numFlickies; ///< Internal. For freed flicky support. - mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful. + // Selection metadata + char keywords[33]; ///< Keywords separated by space to search for. 32 characters. - // NiGHTS stuff. - UINT8 numGradedMares; ///< Internal. For grade support. - nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful. + SINT8 unlockrequired; ///< Is an unlockable required to play this level? -1 if no. + UINT8 levelselect; ///< Is this map available in the level select? If so, which map list is it available in? + UINT8 menuflags; ///< LF2_flags: options that affect record attack menus - // SRB2kart - fixed_t mobj_scale; ///< Replacement for TOL_ERZ3 - fixed_t default_waypoint_radius; ///< 0 is a special value for DEFAULT_WAYPOINT_RADIUS, but scaled with mobjscale + // Operational metadata + UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below + UINT32 typeoflevel; ///< Combination of typeoflevel flags. + UINT8 numlaps; ///< Number of laps in circuit mode, unless overridden. + fixed_t gravity; ///< Map-wide gravity. - mapheader_lighting_t lighting; ///< Wall and sprite lighting - mapheader_lighting_t lighting_encore; ///< Alternative lighting for Encore mode - boolean use_encore_lighting; ///< Whether to use separate Encore lighting - - boolean use_walltransfer; ///< Whether to use DRRR style wall transfering or not - - // Music stuff. - UINT32 musinterfadeout; ///< Fade out level music on intermission screen in milliseconds - char musintername[7]; ///< Intermission screen music. // Music information char musname[MAXMUSNAMES][7]; ///< Music tracks to play. First dimension is the track number, second is the music string. "" for no music. UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. UINT32 muspos; ///< Music position to jump to. UINT8 musname_size; ///< Number of music tracks defined - char muspostbossname[7]; ///< Post-bossdeath music. - UINT16 muspostbosstrack; ///< Post-bossdeath track. - UINT32 muspostbosspos; ///< Post-bossdeath position - UINT32 muspostbossfadein; ///< Post-bossdeath fade-in milliseconds. + // Sky information + UINT8 weather; ///< See preciptype_t + char skytexture[9]; ///< Sky texture to use. + INT16 skybox_scalex; ///< Skybox X axis scale. (0 = no movement, 1 = 1:1 movement, 16 = 16:1 slow movement, -4 = 1:4 fast movement, etc.) + INT16 skybox_scaley; ///< Skybox Y axis scale. + INT16 skybox_scalez; ///< Skybox Z axis scale. - SINT8 musforcereset; ///< Force resetmusic (-1 for default; 0 for force off; 1 for force on) + // Distance information + fixed_t mobj_scale; ///< Defines the size all object calculations are relative to + fixed_t default_waypoint_radius; ///< 0 is a special value for DEFAULT_WAYPOINT_RADIUS, but scaled with mobjscale - // SRB2Kart: Keeps track of if a map lump exists, so we can tell when a map is being replaced. - boolean alreadyExists; + // Visual information + UINT16 palette; ///< PAL lump to use on this map + UINT16 encorepal; ///< PAL for encore mode + mapheader_lighting_t lighting; ///< Wall and sprite lighting + mapheader_lighting_t lighting_encore; ///< Alternative lighting for Encore mode + boolean use_encore_lighting; ///< Whether to use separate Encore lighting - // Lua stuff. - // (This is not ifdeffed so the map header structure can stay identical, just in case.) - UINT8 numCustomOptions; ///< Internal. For Lua custom value support. - customoption_t *customopts; ///< Custom options. Allocated dynamically for space reasons. Be careful. + // Freed animal information + UINT8 numFlickies; ///< Internal. For freed flicky support. + mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful. + + // Script information + char runsoc[33]; ///< SOC to execute at start of level (32 character limit instead of 63) + char scriptname[33]; ///< Script to use when the map is switched to. (32 character limit instead of 191) + + // Cutscene information + UINT8 precutscenenum; ///< Cutscene number to play BEFORE a level starts. + UINT8 cutscenenum; ///< Cutscene number to use, 0 for none. + + // Lua information + UINT8 numCustomOptions; ///< Internal. For Lua custom value support. + customoption_t *customopts; ///< Custom options. Allocated dynamically for space reasons. Be careful. + + // BlanKart + boolean use_walltransfer; ///< Whether to use DRRR style wall transfering or not }; @@ -448,28 +470,8 @@ struct mapheader_t #define LF2_NOTIMEATTACK (1<<2) ///< Hide this map in Time Attack modes #define LF2_VISITNEEDED (1<<3) ///< Not available in Time Attack modes until you visit the level -extern mapheader_t* mapheaderinfo[NUMMAPS]; - -// This could support more, but is that a good idea? -// 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 - -struct cupheader_t -{ - UINT16 id; ///< Cup ID - char name[15]; ///< Cup title (14 chars) - char icon[9]; ///< Name of the icon patch - INT16 levellist[MAXLEVELLIST]; ///< List of levels that belong to this cup - UINT8 numlevels; ///< Number of levels defined in levellist - INT16 bonusgame; ///< Map number to use for bonus game - INT16 specialstage; ///< Map number to use for special stage - 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 -}; - -extern cupheader_t *kartcupheaders; // Start of cup linked list -extern UINT16 numkartcupheaders; +extern mapheader_t** mapheaderinfo; +extern INT32 nummapheaders, mapallocsize; // Gametypes #define NUMGAMETYPEFREESLOTS 128 @@ -577,52 +579,10 @@ extern INT32 luabanks[NUM_LUABANKS]; extern INT32 nummaprings; //keep track of spawned rings/coins -/** Time attack information, currently a very small structure. - */ -struct recorddata_t -{ - tic_t time; ///< Time in which the level was finished. - tic_t lap; ///< Best lap time for this level. - //UINT32 score; ///< Score when the level was finished. - //UINT16 rings; ///< Rings when the level was finished. -}; - -/** Setup for one NiGHTS map. - * These are dynamically allocated because I am insane - */ -#define GRADE_F 0 -#define GRADE_E 1 -#define GRADE_D 2 -#define GRADE_C 3 -#define GRADE_B 4 -#define GRADE_A 5 -#define GRADE_S 6 - -/*typedef struct -{ - // 8 mares, 1 overall (0) - UINT8 nummares; - UINT32 score[9]; - UINT8 grade[9]; - tic_t time[9]; -} nightsdata_t;*/ - -//extern nightsdata_t *nightsrecords[NUMMAPS]; -extern recorddata_t *mainrecords[NUMMAPS]; - -// mapvisited is now a set of flags that says what we've done in the map. -#define MV_VISITED (1) -#define MV_BEATEN (1<<1) -#define MV_ENCORE (1<<2) -#define MV_MAX (MV_VISITED|MV_BEATEN|MV_ENCORE) -#define MV_MP ((MV_MAX+1)<<1) -extern UINT8 mapvisited[NUMMAPS]; - extern UINT32 token; ///< Number of tokens collected in a level extern UINT32 tokenlist; ///< List of tokens collected extern boolean gottoken; ///< Did you get a token? Used for end of act extern INT32 tokenbits; ///< Used for setting token bits -extern INT32 sstimer; ///< Time allotted in the special stage extern UINT32 bluescore; ///< Blue Team Scores extern UINT32 redscore; ///< Red Team Scores diff --git a/src/f_finale.c b/src/f_finale.c index a3e925431..ff3690ea1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1118,6 +1118,8 @@ static void F_CacheTitleScreen(void) void F_StartTitleScreen(void) { + INT32 titleMapNum; + if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS) { ttuser_count = 0; @@ -1127,23 +1129,23 @@ void F_StartTitleScreen(void) else wipegamestate = GS_TITLESCREEN; - if (titlemap) + if (titlemap + && ((titleMapNum = G_MapNumber(titlemap)) < nummapheaders) + && mapheaderinfo[titleMapNum] + && mapheaderinfo[titleMapNum]->lumpnum != LUMPERROR) { mapthing_t *startpos; gamestate_t prevwipegamestate = wipegamestate; titlemapinaction = TITLEMAP_LOADING; titlemapcameraref = NULL; - gamemap = titlemap; + gamemap = titleMapNum+1; - if (!mapheaderinfo[gamemap-1]) - P_AllocMapHeader(gamemap-1); - - maptol = mapheaderinfo[gamemap-1]->typeoflevel; - globalweather = mapheaderinfo[gamemap-1]->weather; + maptol = mapheaderinfo[titleMapNum]->typeoflevel; + globalweather = mapheaderinfo[titleMapNum]->weather; G_DoLoadLevel(true); - if (!titlemap) + if (!titleMapNum) return; players[displayplayers[0]].playerstate = PST_DEAD; // Don't spawn the player in dummy (I'm still a filthy cheater) @@ -1418,20 +1420,17 @@ void F_TitleScreenTicker(boolean run) // is it time? if (!(--demoIdleLeft)) { - //static boolean use_netreplay = false; - - char dname[9]; - lumpnum_t l; - const char *mapname; + char dname[MAXMAPLUMPNAME+1+8+1]; + UINT16 mapnum; UINT8 numstaff; + static boolean use_netreplay = false; - //@TODO uncomment this when this goes into vanilla - /*if ((use_netreplay = !use_netreplay))*/ + if ((use_netreplay = !use_netreplay)) { - numstaff = 1; - while ((l = W_CheckNumForName(va("TDEMO%03u", numstaff))) != LUMPERROR) + lumpnum_t l = LUMPERROR; + numstaff = 0; + while (numstaff < 99 && (l = W_CheckNumForName(va("TDEMO%03u", numstaff))) != LUMPERROR) numstaff++; - numstaff--; if (numstaff) { @@ -1444,54 +1443,38 @@ void F_TitleScreenTicker(boolean run) // prevent console spam if failed demoIdleLeft = demoIdleTime; - if ((l = W_CheckNumForName("MAP01S01")) == LUMPERROR) // gotta have ONE + mapnum = G_RandMap(TOL_RACE, -2, 2, 0, false, NULL); + if (mapnum == 0) // gotta have ONE { - F_StartIntro(); return; } - // Replay intro when done cycling through demos - /* - if (curDemo == numDemos) -- uuuh... we have a LOT of maps AND a big devteam... probably not gonna see a repeat unless you're super unlucky :V - { - curDemo = 0; - F_StartIntro(); - return; - } - */ - - mapname = G_BuildMapName(G_RandMap(TOL_RACE, -2, 0, 0, false, NULL)+1); - - numstaff = 1; - while (numstaff < 99 && (l = W_CheckNumForName(va("%sS%02u",mapname,numstaff+1))) != LUMPERROR) - numstaff++; - -#if 0 // turns out this isn't how we're gonna organise 'em - if (numstaff > 1) - { - if (laststaff && laststaff <= numstaff) // don't do the same staff member twice in a row, even if they're on different maps - { - numstaff = M_RandomKey(numstaff-1)+1; - if (numstaff >= laststaff) - numstaff++; - } - else - numstaff = M_RandomKey(numstaff)+1; - } - laststaff = numstaff; -#else numstaff = M_RandomKey(numstaff)+1; -#endif // Setup demo name - snprintf(dname, 9, "%sS%02u", mapname, numstaff); + sprintf(dname, "%s/GHOST_%u", mapheaderinfo[mapnum]->lumpname, numstaff); - /*if ((l = W_CheckNumForName(dname)) == LUMPERROR) -- we KNOW it exists now { - CONS_Alert(CONS_ERROR, M_GetText("Demo lump \"%s\" doesn't exist\n"), dname); - F_StartIntro(); - return; - }*/ + // vres GHOST_%u + virtres_t *vRes; + virtlump_t *vLump; + + vRes = vres_GetMap(mapheaderinfo[mapnum]->lumpnum); + vLump = vres_Find(vRes, dname); + + if (vLump == NULL) + { + + if (((W_CheckNumForName(va("%sS%02u", mapheaderinfo[mapnum]->lumpname, numstaff)))) != LUMPERROR) + sprintf(dname, "%sS%02u", mapheaderinfo[mapnum]->lumpname, numstaff); + else + { + vres_Free(vRes); + return; + } + } + vres_Free(vRes); + } loadreplay: demo.title = demo.fromtitle = true; @@ -1628,7 +1611,7 @@ void F_EndCutScene(void) F_StartGameEvaluation(); else if (cutnum == introtoplay-1) D_StartTitle(); - else if (nextmap < 1100-1) + else if (nextmap < NEXTMAP_SPECIAL) G_NextLevel(); else G_EndGame(); diff --git a/src/filesrch.c b/src/filesrch.c index 053855b9b..a662cc160 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -41,7 +41,6 @@ #include #define SUFFIX "*" -#define SLASH "\\" #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #ifndef INVALID_FILE_ATTRIBUTES @@ -139,7 +138,7 @@ opendir (const CHAR *szPath) /* Allocate enough space to store DIR structure and the complete * directory path given. */ nd = (DIR *) malloc (sizeof (DIR) + (strlen(szFullPath) + strlen (SLASH) + - strlen(SUFFIX) + 1) * sizeof (CHAR)); + strlen(PATHSEP) + 1) * sizeof (CHAR)); if (!nd) { @@ -153,10 +152,9 @@ opendir (const CHAR *szPath) /* Add on a slash if the path does not end with one. */ if (nd->dd_name[0] != '\0' && - nd->dd_name[strlen (nd->dd_name) - 1] != '/' && - nd->dd_name[strlen (nd->dd_name) - 1] != '\\') + nd->dd_name[strlen (nd->dd_name) - 1] != PATHSEP[0]) { - strcat (nd->dd_name, SLASH); + strcat (nd->dd_name, PATHSEP); } /* Add on the search pattern */ @@ -473,9 +471,9 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want return FS_NOTFOUND; } - if (searchpath[searchpathindex[depthleft]-2] != '/') + if (searchpath[searchpathindex[depthleft]-2] != PATHSEP[0]) { - searchpath[searchpathindex[depthleft]-1] = '/'; + searchpath[searchpathindex[depthleft]-1] = PATHSEP[0]; searchpath[searchpathindex[depthleft]] = 0; } else @@ -517,8 +515,8 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want depthleft++; } - searchpath[searchpathindex[depthleft]-1]='/'; - searchpath[searchpathindex[depthleft]]=0; + searchpath[searchpathindex[depthleft]-1] = PATHSEP[0]; + searchpath[searchpathindex[depthleft]] = 0; } else if (!strcasecmp(searchname, dent->d_name)) { diff --git a/src/g_demo.c b/src/g_demo.c index 2e2a05149..b4467afa8 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -59,12 +59,14 @@ consvar_t cv_recordmultiplayerdemos = CVAR_INIT ("netdemo_record", "Manual Save" static CV_PossibleValue_t netdemosyncquality_cons_t[] = {{1, "MIN"}, {35, "MAX"}, {0, NULL}}; consvar_t cv_netdemosyncquality = CVAR_INIT ("netdemo_syncquality", "1", CV_SAVE, netdemosyncquality_cons_t, NULL); +consvar_t cv_netdemosize = CVAR_INIT ("netdemo_size", "6", CV_SAVE, CV_Natural, NULL); + boolean nodrawers; // for comparative timing purposes boolean noblit; // for comparative timing purposes tic_t demostarttime; // for comparative timing purposes static char demoname[MAX_WADPATH]; -static savebuffer_t demobuf; +static savebuffer_t demobuf = {0}; static UINT8 *demotime_p, *demoinfo_p; static UINT8 demoflags; boolean demosynced = true; // console warning message @@ -2000,22 +2002,13 @@ void G_RecordDemo(const char *name) strcpy(demoname, name); strcat(demoname, ".lmp"); - //@TODO make a maxdemosize cvar - maxsize = 1024*1024*2; + maxsize = 1024 * 1024 * cv_netdemosize.value; - if (M_CheckParm("-maxdemo") && M_IsNextParm()) - maxsize = atoi(M_GetNextParm()) * 1024; - -// if (demobuf.buffer) -// P_SaveBufferFree(&demobuf); - - demobuf.size = maxsize; - demobuf.buffer = (UINT8 *)malloc(maxsize); + P_SaveBufferAlloc(&demobuf, maxsize); demobuf.p = NULL; - demobuf.end = demobuf.buffer + demobuf.size; demo.recording = true; - //demo.buffer = &demobuf; + demo.buffer = &demobuf; /* FIXME: This whole file is in a wretched state. Take a look at G_WriteAllGhostTics and G_WriteDemoTiccmd, they @@ -2039,10 +2032,8 @@ void G_RecordMetal(void) if (M_CheckParm("-maxdemo") && M_IsNextParm()) maxsize = atoi(M_GetNextParm()) * 1024; - demobuf.size = maxsize; - demobuf.buffer = (UINT8 *)malloc(maxsize); + P_SaveBufferAlloc(&demobuf, maxsize); demobuf.p = NULL; - demobuf.end = demobuf.buffer + demobuf.size; metalrecording = true; } @@ -2088,7 +2079,7 @@ void G_BeginRecording(void) // game data M_Memcpy(demobuf.p, "PLAY", 4); demobuf.p += 4; - WRITEINT16(demobuf.p,gamemap); + WRITESTRINGN(demobuf.p, mapheaderinfo[gamemap-1]->lumpname, MAXMAPLUMPNAME); M_Memcpy(demobuf.p, mapmd5, 16); demobuf.p += 16; WRITEUINT8(demobuf.p, demoflags); @@ -2531,7 +2522,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) p += 16; // demo checksum I_Assert(!memcmp(p, "PLAY", 4)); p += 4; // PLAY - p += 2; // gamemap + SKIPSTRING(p); // gamemap p += 16; // map md5 flags = READUINT8(p); // demoflags p++; // gametype @@ -2589,7 +2580,7 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) Z_Free(buffer); return UINT8_MAX; } p += 4; // "PLAY" - p += 2; // gamemap + SKIPSTRING(p); // gamemap p += 16; // mapmd5 flags = READUINT8(p); p++; // gametype @@ -2635,6 +2626,7 @@ void G_LoadDemoInfo(menudemo_t *pdemo) UINT8 *infobuffer, *info_p, *extrainfo_p; UINT8 version, subversion, pdemoflags; UINT16 pdemoversion, count; + char mapname[MAXMAPLUMPNAME]; if (!FIL_ReadFile(pdemo->filepath, &infobuffer)) { @@ -2694,7 +2686,8 @@ void G_LoadDemoInfo(menudemo_t *pdemo) return; } info_p += 4; // "PLAY" - pdemo->map = READINT16(info_p); + READSTRINGN(info_p, mapname, sizeof(mapname)); + pdemo->map = G_MapNumber(mapname); info_p += 16; // mapmd5 pdemoflags = READUINT8(info_p); @@ -2808,7 +2801,7 @@ void G_DoPlayDemo(char *defdemoname) { UINT8 i, p; lumpnum_t l; - char skin[17],color[MAXCOLORNAME+1],follower[17],*n,*pdemoname; + char skin[17],color[MAXCOLORNAME+1],follower[17],mapname[MAXMAPLUMPNAME],*n,*pdemoname; UINT8 version,subversion; UINT32 randseed; char msg[1024]; @@ -2848,7 +2841,7 @@ void G_DoPlayDemo(char *defdemoname) if (FIL_CheckExtension(defdemoname)) { //FIL_DefaultExtension(defdemoname, ".lmp"); - if (!FIL_ReadFile(defdemoname, &demobuf.buffer)) + if (P_SaveBufferFromFile(&demobuf, defdemoname) == false) { snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); CONS_Alert(CONS_ERROR, "%s", msg); @@ -2856,22 +2849,72 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); return; } - demobuf.p = demobuf.buffer; } // load demo resource from WAD - else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) + else { - snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - else // it's an internal demo - { - demobuf.buffer = demobuf.p = W_CacheLumpNum(l, PU_STATIC); + if (n == defdemoname) + { + // Raw lump. + if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) + { + snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + Z_Free(pdemoname); + gameaction = ga_nothing; + M_StartMessage(msg, NULL, MM_NOTHING); + return; + } + + P_SaveBufferFromLump(&demobuf, l); + } + else + { + // vres GHOST_%u + virtres_t *vRes; + virtlump_t *vLump; + UINT16 mapnum; + size_t step = 0; + + step = 0; + while (defdemoname+step < n-1) + { + mapname[step] = defdemoname[step]; + step++; + } + mapname[step] = '\0'; + + mapnum = G_MapNumber(mapname); + if (mapnum >= nummapheaders || mapheaderinfo[mapnum]->lumpnum == LUMPERROR) + { + snprintf(msg, 1024, M_GetText("Failed to read lump '%s (couldn't find map %s)'.\n"), defdemoname, mapname); + CONS_Alert(CONS_ERROR, "%s", msg); + Z_Free(pdemoname); + gameaction = ga_nothing; + M_StartMessage(msg, NULL, MM_NOTHING); + return; + } + + vRes = vres_GetMap(mapheaderinfo[mapnum]->lumpnum); + vLump = vres_Find(vRes, pdemoname); + + if (vLump == NULL) + { + snprintf(msg, 1024, M_GetText("Failed to read lump '%s (couldn't find lump %s in %s)'.\n"), defdemoname, pdemoname, mapname); + CONS_Alert(CONS_ERROR, "%s", msg); + Z_Free(pdemoname); + gameaction = ga_nothing; + M_StartMessage(msg, NULL, MM_NOTHING); + return; + } + + P_SaveBufferAlloc(&demobuf, vLump->size); + memcpy(demobuf.buffer, vLump->data, vLump->size); + + vres_Free(vRes); + } #if defined(SKIPERRORS) && !defined(DEVELOP) - skiperrors = true; // SRB2Kart: Don't print warnings for staff ghosts, since they'll inevitably happen when we make bugfixes/changes... + skiperrors = true; // RR: Don't print warnings for staff ghosts, since they'll inevitably happen when we make bugfixes/changes... #endif } } @@ -2879,13 +2922,14 @@ void G_DoPlayDemo(char *defdemoname) // read demo header gameaction = ga_nothing; demo.playback = true; + demo.buffer = &demobuf; if (memcmp(demobuf.p, DEMOHEADER, 12)) { snprintf(msg, 1024, M_GetText("%s is not a SRB2Kart replay file.\n"), pdemoname); CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - Z_Free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.playback = false; demo.title = false; return; @@ -2905,7 +2949,7 @@ void G_DoPlayDemo(char *defdemoname) CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - Z_Free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.playback = false; demo.title = false; return; @@ -2923,14 +2967,15 @@ void G_DoPlayDemo(char *defdemoname) CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - Z_Free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.playback = false; demo.title = false; return; } demobuf.p += 4; // "PLAY" - gamemap = READINT16(demobuf.p); + READSTRINGN(demobuf.p, mapname, sizeof(mapname)); // gamemap + gamemap = G_MapNumber(mapname)+1; demobuf.p += 16; // mapmd5 demoflags = READUINT8(demobuf.p); @@ -2987,7 +3032,7 @@ void G_DoPlayDemo(char *defdemoname) if (!CON_Ready()) // In the console they'll just see the notice there! No point pulling them out. M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - Z_Free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.playback = false; demo.title = false; return; @@ -3024,13 +3069,13 @@ void G_DoPlayDemo(char *defdemoname) demobuf.p += 4; // Extrainfo location // ...*map* not loaded? - if (!gamemap || (gamemap > NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->alreadyExists == true)) + if (!gamemap || (gamemap > nummapheaders) || !mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->lumpnum == LUMPERROR) { snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname); CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - Z_Free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.playback = false; demo.title = false; return; @@ -3049,7 +3094,7 @@ void G_DoPlayDemo(char *defdemoname) CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - Z_Free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.playback = false; demo.title = false; return; @@ -3099,7 +3144,7 @@ void G_DoPlayDemo(char *defdemoname) CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - Z_Free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.playback = false; demo.title = false; return; @@ -3115,7 +3160,7 @@ void G_DoPlayDemo(char *defdemoname) CONS_Alert(CONS_ERROR, "%s", msg); M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); - Z_Free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.playback = false; demo.title = false; return; @@ -3225,7 +3270,7 @@ void G_DoPlayDemo(char *defdemoname) R_ExecuteSetViewSize(); P_SetRandSeed(randseed); - G_InitNew(demoflags & DF_ENCORE, G_BuildMapName(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, false); // Doesn't matter whether you reset or not here, given changes to resetplayer. for (i = 0; i < MAXPLAYERS; i++) { @@ -3340,7 +3385,7 @@ void G_AddGhost(char *defdemoname) } p += 4; // "PLAY" - p += 2; // gamemap + SKIPSTRING(p); // gamemap p += 16; // mapmd5 (possibly check for consistency?) flags = READUINT8(p); @@ -3468,7 +3513,7 @@ void G_AddGhost(char *defdemoname) ghosts = gh; gh->version = ghostversion; - mthing = playerstarts[0]; + mthing = playerstarts[0] ? playerstarts[0] : deathmatchstarts[0]; // todo not correct but out of scope I_Assert(mthing); { // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling. fixed_t z,f,c; @@ -3572,7 +3617,7 @@ void G_UpdateStaffGhostName(lumpnum_t l) } p += 4; // "PLAY" - p += 2; // gamemap + SKIPSTRING(p); // gamemap p += 16; // mapmd5 (possibly check for consistency?) flags = READUINT8(p); @@ -3650,6 +3695,7 @@ void G_DoPlayMetal(void) thinker_t *th; // it's an internal demo + // TODO: Use map header to determine lump name if ((l = W_CheckNumForName(va("%sMS",G_BuildMapName(gamemap)))) == LUMPERROR) { CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n")); @@ -3758,7 +3804,7 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill) WriteDemoChecksum(); saved = FIL_WriteFile(va("%sMS.LMP", G_BuildMapName(gamemap)), demobuf.buffer, demobuf.p - demobuf.buffer); // finally output the file. } - free(demobuf.buffer); + P_SaveBufferFree(&demobuf); metalrecording = false; if (saved) I_Error("Saved to %sMS.LMP", G_BuildMapName(gamemap)); @@ -3826,8 +3872,7 @@ static void G_StopTimingDemo(void) // called from stopdemo command, map command, and g_checkdemoStatus. void G_StopDemo(void) { - Z_Free(demobuf.buffer); - demobuf.buffer = NULL; + P_SaveBufferFree(&demobuf); demo.playback = false; if (demo.title) modeattacking = false; @@ -3893,6 +3938,8 @@ boolean G_CheckDemoStatus(void) return true; } + if (demo.recording) + P_SaveBufferFree(&demobuf); demo.recording = false; return false; } @@ -3966,7 +4013,7 @@ void G_SaveDemo(void) if (FIL_WriteFile(demoname, demobuf.buffer, demobuf.p - demobuf.buffer)) // finally output the file. demo.savemode = DSM_SAVED; - free(demobuf.buffer); + P_SaveBufferFree(&demobuf); demo.recording = false; if (!modeattacking) diff --git a/src/g_demo.h b/src/g_demo.h index a8e2c15d2..420809845 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -28,7 +28,7 @@ extern UINT8 *demo_p; // DEMO playback/recording related stuff. // ====================================== -extern consvar_t cv_recordmultiplayerdemos, cv_netdemosyncquality; +extern consvar_t cv_recordmultiplayerdemos, cv_netdemosyncquality, cv_netdemosize; extern tic_t demostarttime; @@ -58,6 +58,7 @@ struct demovars_s { boolean freecam; + const savebuffer_t *buffer; // debug, valid only if recording or playback }; extern struct demovars_s demo; diff --git a/src/g_game.c b/src/g_game.c index 3bb36a88a..0a203217c 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -147,18 +147,14 @@ INT32 g_localplayers[MAXSPLITSCREENPLAYERS]; tic_t gametic; tic_t levelstarttic; // gametic at level start -UINT32 ssspheres; // old special stage INT16 lastmap; // last level you were at (returning from special stages) tic_t timeinmap; // Ticker for time spent in level (used for levelcard display) -INT16 spstage_start, spmarathon_start; -INT16 sstage_start, sstage_end, smpstage_start, smpstage_end; - -INT16 titlemap = 0; +char * titlemap = NULL; boolean hidetitlepics = false; -INT16 bootmap; //bootmap for loading a map on startup +char * bootmap = NULL; //bootmap for loading a map on startup -INT16 tutorialmap = 0; // map to load for tutorial +char * tutorialmap = NULL; // map to load for tutorial boolean tutorialmode = false; // are we in a tutorial right now? INT32 tutorialgcs = gcs_custom; // which control scheme is loaded? @@ -169,8 +165,6 @@ UINT16 skincolor_blueteam = SKINCOLOR_BLUE; UINT16 skincolor_redring = SKINCOLOR_RASPBERRY; UINT16 skincolor_bluering = SKINCOLOR_PERIWINKLE; -tic_t countdowntimer = 0; -boolean countdowntimeup = false; boolean exitfadestarted = false; cutscene_t *cutscenes[128]; @@ -182,7 +176,8 @@ UINT8 skipstats; struct quake quake; // Map Header Information -mapheader_t* mapheaderinfo[NUMMAPS] = {NULL}; +mapheader_t** mapheaderinfo = {NULL}; +INT32 nummapheaders, mapallocsize = 0; // Kart cup definitions cupheader_t *kartcupheaders = NULL; @@ -201,19 +196,10 @@ UINT32 tokenlist; // List of tokens collected boolean gottoken; // Did you get a token? Used for end of act INT32 tokenbits; // Used for setting token bits -// Old Special Stage -INT32 sstimer; // Time allotted in the special stage - tic_t totalplaytime; UINT32 matchesplayed; // SRB2Kart boolean gamedataloaded = false; -// Time attack data for levels -// These are dynamically allocated for space reasons now -recorddata_t *mainrecords[NUMMAPS] = {NULL}; -//nightsdata_t *nightsrecords[NUMMAPS] = {NULL}; -UINT8 mapvisited[NUMMAPS]; - // Temporary holding place for nights data for the current map //nightsdata_t ntemprecords; @@ -323,7 +309,25 @@ boolean legitimateexit; // Did this client actually finish the match? boolean comebackshowninfo; // Have you already seen the "ATTACK OR PROTECT" message? tic_t curlap; // Current lap time tic_t bestlap; // Best lap time -static INT16 randmapbuffer[NUMMAPS+1]; // Buffer for maps RandMap is allowed to roll + +typedef struct +{ + INT16 *mapbuffer; // Pointer to zone memory + INT32 lastnummapheaders; // Reset if nummapheaders != this + UINT8 counttogametype; // Time to gametype change event +} randmaps_t; +static randmaps_t randmaps = {NULL, 0, 0}; + +static void G_ResetRandMapBuffer(void) +{ + INT32 i; + Z_Free(randmaps.mapbuffer); + randmaps.lastnummapheaders = nummapheaders; + randmaps.mapbuffer = Z_Malloc(randmaps.lastnummapheaders * sizeof(INT16), PU_STATIC, NULL); + for (i = 0; i < randmaps.lastnummapheaders; i++) + randmaps.mapbuffer[i] = -1; + //intentionally not resetting randmaps.counttogametype here +} // Grading UINT32 timesBeaten; @@ -519,21 +523,23 @@ INT32 player_name_changes[MAXPLAYERS]; // Allocation for time and nights data void G_AllocMainRecordData(INT16 i) { - if (!mainrecords[i]) - mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL); - memset(mainrecords[i], 0, sizeof(recorddata_t)); + if (i > nummapheaders || !mapheaderinfo[i]) + I_Error("G_AllocMainRecordData: Internal map ID %d not found (nummapheaders = %d)\n", i, nummapheaders); + if (!mapheaderinfo[i]->mainrecord) + mapheaderinfo[i]->mainrecord = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL); + memset(mapheaderinfo[i]->mainrecord, 0, sizeof(recorddata_t)); } // MAKE SURE YOU SAVE DATA BEFORE CALLING THIS void G_ClearRecords(void) { INT16 i; - for (i = 0; i < NUMMAPS; ++i) + for (i = 0; i < nummapheaders; ++i) { - if (mainrecords[i]) + if (mapheaderinfo[i]->mainrecord) { - Z_Free(mainrecords[i]); - mainrecords[i] = NULL; + Z_Free(mapheaderinfo[i]->mainrecord); + mapheaderinfo[i]->mainrecord = NULL; } /*if (nightsrecords[i]) { @@ -546,20 +552,22 @@ void G_ClearRecords(void) // For easy retrieval of records tic_t G_GetBestTime(INT16 map) { - if (!mainrecords[map-1] || mainrecords[map-1]->time <= 0) + if (!mapheaderinfo[map] || !mapheaderinfo[map]->mainrecord || mapheaderinfo[map]->mainrecord->time <= 0) return (tic_t)UINT32_MAX; - return mainrecords[map-1]->time; + return mapheaderinfo[map]->mainrecord->time; } +// BE RIGHT BACK + // Not needed /* tic_t G_GetBestLap(INT16 map) { - if (!mainrecords[map-1] || mainrecords[map-1]->lap <= 0) + if (!mapheaderinfo[map] || !mapheaderinfo[map]->mainrecord || mapheaderinfo[map]->mainrecord->lap <= 0) return (tic_t)UINT32_MAX; - return mainrecords[map-1]->lap; + return mapheaderinfo[map]->mainrecord->lap; } */ @@ -573,9 +581,10 @@ static void G_UpdateRecordReplays(void) char *gpath; char lastdemo[256], bestdemo[256]; UINT8 earnedEmblems; + int parts; // Record new best time - if (!mainrecords[gamemap-1]) + if (!mapheaderinfo[gamemap-1]->mainrecord) G_AllocMainRecordData(gamemap-1); if (players[consoleplayer].pflags & PF_NOCONTEST) @@ -583,20 +592,20 @@ static void G_UpdateRecordReplays(void) players[consoleplayer].realtime = UINT32_MAX; } - if (((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time)) + if (((mapheaderinfo[gamemap-1]->mainrecord->time == 0) || (players[consoleplayer].realtime < mapheaderinfo[gamemap-1]->mainrecord->time)) && (players[consoleplayer].realtime < UINT32_MAX)) // DNF { - mainrecords[gamemap-1]->time = players[consoleplayer].realtime; + mapheaderinfo[gamemap-1]->mainrecord->time = players[consoleplayer].realtime; } if (modeattacking == ATTACKING_TIME) { - if ((mainrecords[gamemap-1]->lap == 0) || (bestlap < mainrecords[gamemap-1]->lap)) - mainrecords[gamemap-1]->lap = bestlap; + if ((mapheaderinfo[gamemap-1]->mainrecord->lap == 0) || (bestlap < mapheaderinfo[gamemap-1]->mainrecord->lap)) + mapheaderinfo[gamemap-1]->mainrecord->lap = bestlap; } else { - mainrecords[gamemap-1]->lap = 0; + mapheaderinfo[gamemap-1]->mainrecord->lap = 0; } // Save demo! @@ -605,17 +614,13 @@ static void G_UpdateRecordReplays(void) G_SetDemoTime(players[consoleplayer].realtime, bestlap); G_CheckDemoStatus(); - gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s", - srb2home, timeattackfolder); - M_MkdirEach(gpath, M_PathParts(gpath) - 3, 0755); - - strcat(gpath, PATHSEP); - strcat(gpath, G_BuildMapName(gamemap)); + gpath = xva("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", + srb2home, timeattackfolder, G_BuildMapName(gamemap)); + parts = M_PathParts(gpath); + M_MkdirEachUntil(gpath, parts - 4, parts - 1, 0755); snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, cv_chooseskin.string); - gpath = Z_StrDup(gpath); - if (FIL_FileExists(lastdemo)) { UINT8 *buf; @@ -647,7 +652,7 @@ static void G_UpdateRecordReplays(void) Z_Free(buf); } - Z_Free(gpath); + free(gpath); // Check emblems when level data is updated if ((earnedEmblems = M_CheckLevelEmblems())) @@ -685,43 +690,64 @@ void G_SetGameModified(boolean silent, boolean major) } /** Builds an original game map name from a map number. - * The complexity is due to MAPA0-MAPZZ. * * \param map Map number. * \return Pointer to a static buffer containing the desired map name. - * \sa M_MapNumber + * \sa G_MapNumber */ const char *G_BuildMapName(INT32 map) { - static char mapname[10] = "MAPXX"; // internal map name (wad resource name) - - I_Assert(map >= 0); - I_Assert(map <= NUMMAPS); - - if (map == 0) // hack??? + if (map > 0 && map <= nummapheaders && mapheaderinfo[map - 1] != NULL) { - if (gamestate == GS_TITLESCREEN) - map = -1; - else if (gamestate == GS_LEVEL) - map = gamemap-1; - else - map = prevmap; - map = G_RandMap(G_TOLFlag(cv_newgametype.value), map, 0, 0, false, NULL)+1; + return mapheaderinfo[map - 1]->lumpname; } - - if (map < 100) - sprintf(&mapname[3], "%.2d", map); else { - mapname[3] = (char)('A' + (char)((map - 100) / 36)); - if ((map - 100) % 36 < 10) - mapname[4] = (char)('0' + (char)((map - 100) % 36)); - else - mapname[4] = (char)('A' + (char)((map - 100) % 36) - 10); - mapname[5] = '\0'; + return NULL; + } +} + +/** Returns the map number for map lump name. + * + * \param name Map name; + * \return Map number. + * \sa G_BuildMapName, nextmapspecial_t + */ +INT32 G_MapNumber(const char * name) +{ +#ifdef NEXTMAPINSOC + if (strncasecmp("NEXTMAP_", name, 8) != 0) +#endif + { + INT32 map; + UINT32 hash = quickncasehash(name, MAXMAPLUMPNAME); + + for (map = 0; map < nummapheaders; ++map) + { + if (hash != mapheaderinfo[map]->lumpnamehash) + continue; + + if (strcasecmp(mapheaderinfo[map]->lumpname, name) != 0) + continue; + + return map; + } + + return NEXTMAP_INVALID; } - return mapname; +#ifdef NEXTMAPINSOC + name += 8; + + if (strcasecmp("EVALUATION", name) == 0) + return NEXTMAP_EVALUATION; + if (strcasecmp("CREDITS", name) == 0) + return NEXTMAP_CREDITS; + //if (strcasecmp("CEREMONY", name) == 0) + //return NEXTMAP_CEREMONY; + //if (strcasecmp("TITLE", name) == 0) + return NEXTMAP_TITLE; +#endif } /** Clips the console player's mouse aiming to the current view. @@ -1325,7 +1351,7 @@ void G_DoLoadLevel(boolean resetplayer) // cleanup if (titlemapinaction == TITLEMAP_LOADING) { - if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR) + if (gamemap < 1 || gamemap > nummapheaders) { titlemap = 0; // let's not infinite recursion ok Command_ExitGame_f(); @@ -3272,18 +3298,15 @@ INT32 G_GetGametypeByName(const char *gametypestr) // boolean G_IsSpecialStage(INT32 mapnum) { -#if 1 - (void)mapnum; -#else - if (modeattacking == ATTACKING_TIME) - return false; - if (mapnum >= sstage_start && mapnum <= sstage_end) - return true; - if (mapnum >= smpstage_start && mapnum <= smpstage_end) - return true; -#endif + mapnum--; // gamemap-based to 0 indexed - return false; + if (mapnum > nummapheaders || !mapheaderinfo[mapnum]) + return false; + + if (!mapheaderinfo[mapnum]->cup || mapheaderinfo[mapnum]->cup->cachedlevels[CUPCACHE_SPECIAL] != mapnum) + return false; + + return true; } // @@ -3351,64 +3374,78 @@ boolean G_GametypeHasSpectators(void) // // Oh, yeah, and we sometimes flip encore mode on here too. // -INT16 G_SometimesGetDifferentGametype(void) +INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype) { - boolean encorepossible = ((M_SecretUnlocked(SECRET_ENCORE) || encorescramble == 1) && (gametyperules & GTR_CIRCUIT)); + // 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) || encorescramble == 1) + && ((gametyperules|gametypedefaultrules[prefgametype]) & GTR_CIRCUIT)); + UINT8 encoremodifier = 0; - if (!cv_kartvoterulechanges.value // never - && encorescramble != 1) // destroying the code for this one instance - return gametype; + // -- the below is only necessary if you want to use randmaps.mapbuffer here + //if (randmaps.lastnummapheaders != nummapheaders) + //G_ResetRandMapBuffer(); - if (randmapbuffer[NUMMAPS] > 0 && (encorepossible || cv_kartvoterulechanges.value != 3)) + if (encorepossible) { - randmapbuffer[NUMMAPS]--; - if (encorepossible) + if (encorescramble != -1) { - if (encorescramble != -1) - encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission - else - { - switch (cv_kartvoterulechanges.value) - { - case 3: // always - randmapbuffer[NUMMAPS] = 0; // gotta prep this in case it isn't already set - break; - case 2: // frequent - encorepossible = M_RandomChance(FRACUNIT>>1); - break; - case 1: // sometimes - default: - encorepossible = M_RandomChance(FRACUNIT>>2); - break; - } - } - if (encorepossible != (cv_kartencore.value == 1)) - return (gametype|0x80); + encorepossible = (boolean)encorescramble; // FORCE to what was scrambled on intermission } - return gametype; + else + { + switch (cv_kartvoterulechanges.value) + { + case 3: // always + encorepossible = true; + break; + case 2: // frequent + encorepossible = M_RandomChance(FRACUNIT>>1); + break; + case 1: // sometimes + encorepossible = M_RandomChance(FRACUNIT>>2); + break; + default: + break; + } + } + if (encorepossible != (cv_kartencore.value == 1)) + encoremodifier = VOTEMODIFIER_ENCORE; } - if (!cv_kartvoterulechanges.value) // never (again) - return gametype; + if (!cv_kartvoterulechanges.value) // never + return (gametype|encoremodifier); + + if (randmaps.counttogametype > 0 && (cv_kartvoterulechanges.value != 3)) + { + randmaps.counttogametype--; + return (gametype|encoremodifier); + } switch (cv_kartvoterulechanges.value) // okay, we're having a gametype change! when's the next one, luv? { - case 3: // always - randmapbuffer[NUMMAPS] = 1; // every other vote (or always if !encorepossible) - break; case 1: // sometimes - randmapbuffer[NUMMAPS] = 5; // per "cup" + randmaps.counttogametype = 5; // per "cup" break; default: // fallthrough - happens when clearing buffer, but needs a reasonable countdown if cvar is modified case 2: // frequent - randmapbuffer[NUMMAPS] = 2; // ...every 1/2th-ish cup? + randmaps.counttogametype = 2; // ...every 1/2th-ish cup? break; } - if (gametype == GT_BATTLE) - return GT_RACE; - return GT_BATTLE; + // Only this response is prefgametype-based. + // todo custom gametypes + if (prefgametype == GT_BATTLE) + { + // Intentionally does not use encoremodifier! + if (cv_kartencore.value == 1) + return (GT_RACE|VOTEMODIFIER_ENCORE); + return (GT_RACE); + } + // This might appear wrong HERE, but the game will display the Encore possibility on the second voting choice instead. + return (GT_BATTLE|encoremodifier); } // @@ -3442,20 +3479,56 @@ UINT32 G_TOLFlag(INT32 pgametype) return gametypetol[pgametype]; } -static UINT32 TOLMaps(UINT32 tolflags) +INT16 G_GetFirstMapOfGametype(UINT8 pgametype) { - UINT32 num = 0; - UINT32 i; + INT16 mapnum = NEXTMAP_INVALID; - // Find all the maps that are ok and and put them in an array. - for (i = 0; i < NUMMAPS; i++) + /* G: not sure what to do with this + if ((gametypedefaultrules[pgametype] & GTR_CAMPAIGN) && kartcupheaders) + { + mapnum = kartcupheaders->cachedlevels[0]; + } + */ + + if (mapnum >= nummapheaders) + { + UINT32 tolflag = G_TOLFlag(pgametype); + for (mapnum = 0; mapnum < nummapheaders; mapnum++) + { + if (!mapheaderinfo[mapnum]) + continue; + if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR) + continue; + if (!(mapheaderinfo[mapnum]->typeoflevel & tolflag)) + continue; + if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) + continue; + break; + } + } + + return mapnum; +} + +static INT32 TOLMaps(UINT8 pgametype) +{ + INT32 num = 0; + INT32 i; + + UINT32 tolflag = G_TOLFlag(pgametype); + + // Find all the maps that are ok + for (i = 0; i < nummapheaders; i++) { if (!mapheaderinfo[i]) continue; + if (mapheaderinfo[i]->lumpnum == LUMPERROR) + continue; + if (!(mapheaderinfo[i]->typeoflevel & tolflag)) + continue; if (mapheaderinfo[i]->menuflags & LF2_HIDEINMENU) // Don't include Map Hell continue; - if ((mapheaderinfo[i]->typeoflevel & tolflags) == tolflags) - num++; + num++; } return num; @@ -3477,10 +3550,13 @@ INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, UINT8 ignorebuffer, UINT8 maphe UINT16 extbufsize = 0; boolean usehellmaps; // Only consider Hell maps in this pick + if (randmaps.lastnummapheaders != nummapheaders) + G_ResetRandMapBuffer(); + if (!okmaps) { //CONS_Printf("(making okmaps)\n"); - okmaps = Z_Malloc(NUMMAPS * sizeof(INT16), PU_STATIC, NULL); + okmaps = Z_Malloc(nummapheaders * sizeof(INT16), PU_STATIC, NULL); } if (extbuffer != NULL) @@ -3497,11 +3573,11 @@ tryagain: usehellmaps = (maphell == 0 ? false : (maphell == 2 || M_RandomChance(FRACUNIT/100))); // 1% chance of Hell // Find all the maps that are ok and and put them in an array. - for (ix = 0; ix < NUMMAPS; ix++) + for (ix = 0; ix < nummapheaders; ix++) { boolean isokmap = true; - if (!mapheaderinfo[ix]) + if (!mapheaderinfo[ix] || mapheaderinfo[ix]->lumpnum == LUMPERROR) continue; if ((mapheaderinfo[ix]->typeoflevel & tolflags) != tolflags @@ -3510,6 +3586,32 @@ tryagain: || (usehellmaps != (mapheaderinfo[ix]->menuflags & LF2_HIDEINMENU))) // this is bad continue; //isokmap = false; + if (pprevmap == -2) // title demo hack + { + // vres GHOST_%u + virtres_t *vRes; + virtlump_t *vLump; + lumpnum_t l; + + vRes = vres_GetMap(mapheaderinfo[ix+1]->lumpnum); + + for (int i = 0; i < 10; i++) + { + vLump = vres_Find(vRes, va("%s/GHOST_%u",mapheaderinfo[ix+1]->lumpname,i)); + + if (vLump != NULL) + break; + } + + if (vLump == NULL && ((l = W_CheckNumForLongName(va("%sS01",mapheaderinfo[ix+1]->lumpname))) == LUMPERROR)) + { + vres_Free(vRes); + continue; + } + + vres_Free(vRes); + } + if (!ignorebuffer) { if (extbufsize > 0) @@ -3529,11 +3631,11 @@ tryagain: continue; } - for (bufx = 0; bufx < (maphell ? 3 : NUMMAPS); bufx++) + for (bufx = 0; bufx < (maphell ? 3 : randmaps.lastnummapheaders); bufx++) { - if (randmapbuffer[bufx] == -1) // Rest of buffer SHOULD be empty + if (randmaps.mapbuffer[bufx] == -1) // Rest of buffer SHOULD be empty break; - if (ix == randmapbuffer[bufx]) + if (ix == randmaps.mapbuffer[bufx]) { isokmap = false; break; @@ -3544,13 +3646,6 @@ tryagain: continue; } - if (pprevmap == -2) // title demo hack - { - lumpnum_t l; - if ((l = W_CheckNumForName(va("%sS01",G_BuildMapName(ix+1)))) == LUMPERROR) - continue; - } - okmaps[numokmaps++] = ix; } @@ -3558,15 +3653,15 @@ tryagain: { if (!ignorebuffer) { - if (randmapbuffer[3] == -1) // Is the buffer basically empty? + if (randmaps.mapbuffer[3] == -1) // Is the buffer basically empty? { ignorebuffer = 1; // This will probably only help in situations where there's very few maps, but it's folly not to at least try it //CONS_Printf("RANDMAP - ignoring buffer\n"); goto tryagain; } - for (bufx = 3; bufx < NUMMAPS; bufx++) // Let's clear all but the three most recent maps... - randmapbuffer[bufx] = -1; + for (bufx = 3; bufx < randmaps.lastnummapheaders; bufx++) // Let's clear all but the three most recent maps... + randmaps.mapbuffer[bufx] = -1; //CONS_Printf("RANDMAP - emptying randmapbuffer\n"); goto tryagain; } @@ -3583,8 +3678,8 @@ tryagain: if (ignorebuffer == 1) { //CONS_Printf("(emptying randmapbuffer entirely)\n"); - for (bufx = 0; bufx < NUMMAPS; bufx++) - randmapbuffer[bufx] = -1; // if we're having trouble finding a map we should probably clear it + for (bufx = 0; bufx < randmaps.lastnummapheaders; bufx++) + randmaps.mapbuffer[bufx] = -1; // if we're having trouble finding a map we should probably clear it } } else @@ -3605,19 +3700,30 @@ tryagain: void G_AddMapToBuffer(INT16 map) { - INT16 bufx, refreshnum = max(0, ((INT32)TOLMaps(G_TOLFlag(gametype)))-3); + INT16 bufx; + INT16 refreshnum = (TOLMaps(gametype))-3; - // Add the map to the buffer. - for (bufx = NUMMAPS-1; bufx > 0; bufx--) - randmapbuffer[bufx] = randmapbuffer[bufx-1]; - randmapbuffer[0] = map; + if (refreshnum < 0) + refreshnum = 3; + + if (nummapheaders != randmaps.lastnummapheaders) + { + G_ResetRandMapBuffer(); + } + else + { + for (bufx = randmaps.lastnummapheaders-1; bufx > 0; bufx--) + randmaps.mapbuffer[bufx] = randmaps.mapbuffer[bufx-1]; + } + + randmaps.mapbuffer[0] = map; // We're getting pretty full, so lets flush this for future usage. - if (randmapbuffer[refreshnum] != -1) + if (randmaps.mapbuffer[refreshnum] != -1) { // Clear all but the five most recent maps. - for (bufx = 5; bufx < NUMMAPS; bufx++) // bufx < refreshnum? Might not handle everything for gametype switches, though. - randmapbuffer[bufx] = -1; + for (bufx = 5; bufx < randmaps.lastnummapheaders; bufx++) + randmaps.mapbuffer[bufx] = -1; //CONS_Printf("Random map buffer has been flushed.\n"); } } @@ -3627,20 +3733,19 @@ void G_AddMapToBuffer(INT16 map) // static void G_UpdateVisited(void) { - boolean spec = G_IsSpecialStage(gamemap); // Update visitation flags? - if ((!modifiedgame || savemoddata) // Not modified - && !multiplayer && !demo.playback // SP/RA/NiGHTS mode - && !(spec && stagefailed)) // Not failed the special stage + if (/*(!majormods || savemoddata) // Not modified + &&*/ !multiplayer && !demo.playback // SP/RA/NiGHTS mode + && !(modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST))) // Not failed { UINT8 earnedEmblems; // Update visitation flags - mapvisited[gamemap-1] |= MV_BEATEN; + mapheaderinfo[gamemap-1]->mapvisited |= MV_BEATEN; if (encoremode == true) { - mapvisited[gamemap-1] |= MV_ENCORE; + mapheaderinfo[gamemap-1]->mapvisited |= MV_ENCORE; } if (modeattacking) @@ -3661,7 +3766,7 @@ static boolean CanSaveLevel(INT32 mapnum) static void G_HandleSaveLevel(void) { // do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c - if (nextmap >= 1100-1) + if (nextmap >= NEXTMAP_SPECIAL) { if (!gamecomplete) gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission @@ -3675,7 +3780,7 @@ static void G_HandleSaveLevel(void) cursaveslot = 0; } else if ((!modifiedgame || savemoddata) && !(netgame || multiplayer || ultimatemode || demo.recording || metalrecording || modeattacking)) - G_SaveGame((UINT32)cursaveslot, spstage_start); + G_SaveGame((UINT32)cursaveslot, 0); // TODO when we readd a campaign one day } } // and doing THIS here means you don't lose your progress if you close the game mid-intermission @@ -3684,6 +3789,163 @@ 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) +{ + INT32 i; + INT16 newmap, curmap = gamestate == GS_LEVEL ? gamemap-1 : prevmap; + + // go to next level + // nextmap is 0-based, unlike gamemap + if (nextmapoverride != 0) + { + newmap = (INT16)(nextmapoverride-1); + } + else if (grandprixinfo.gp == true) + { + if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session + { + newmap = curmap; // Same map + } + else + { + if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map + { + newmap = NEXTMAP_CEREMONY; // ceremonymap + } + 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++; + } + } + } + else if (bossinfo.boss == true) + { + newmap = NEXTMAP_TITLE; // temporary + } + else + { + UINT32 tolflag = G_TOLFlag(gametype); + + if (grandprixinfo.gp == true) + { + register INT16 cm; + cupheader_t *cup = mapheaderinfo[gamemap-1]->cup; + UINT8 gettingresult = 0; + + while (cup) + { + for (i = 0; i < cup->numlevels; i++) + { + cm = cup->cachedlevels[i]; + + // Not valid? + if (cm >= nummapheaders + || !mapheaderinfo[cm] + || mapheaderinfo[cm]->lumpnum == LUMPERROR + || !(mapheaderinfo[cm]->typeoflevel & tolflag)) + continue; + + // Grab the first valid after the map you're on + if (gettingresult) + { + newmap = cm; + gettingresult = 2; + break; + } + + // Not the map you're on? + if (cm != curmap) + { + continue; + } + + // Ok, this is the current map, time to get the next + gettingresult = 1; + } + + // We have a good nextmap? + if (gettingresult == 2) + { + break; + } + + // Ok, iterate to the next + cup = cup->next; + } + + // Didn't get a nextmap before reaching the end? + if (gettingresult != 2) + { + if (marathonmode) + { + newmap = NEXTMAP_CEREMONY; // ceremonymap + } + else + { + newmap = NEXTMAP_TITLE; + } + } + } + else + { + i = curmap; + if (++i >= nummapheaders) + i = 0; + + while (i != curmap) + { + if (!mapheaderinfo[i] + || mapheaderinfo[i]->lumpnum == LUMPERROR + || !(mapheaderinfo[i]->typeoflevel & tolflag) + || (mapheaderinfo[i]->menuflags & LF2_HIDEINMENU)) + { + if (++i >= nummapheaders) + i = 0; + continue; + } + + break; + } + + newmap = i; + } + + if (advancemap && !marathonmode) + { + if (cv_advancemap.value == 0) // Stay on same map. + { + newmap = curmap; + } + else if (cv_advancemap.value == 2) // Go to random map. + { + newmap = G_RandMap(G_TOLFlag(gametype), curmap, 0, 0, false, NULL); + } + else if (nextmap >= NEXTMAP_SPECIAL) // Loop back around + { + newmap = G_GetFirstMapOfGametype(gametype); + } + } + } + + // We are committed to this map now. + 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); + + return newmap; +} + // // G_DoCompleted // @@ -3745,134 +4007,24 @@ static void G_DoCompleted(void) prevmap = (INT16)(gamemap-1); - if (demo.playback) goto demointermission; - - // go to next level - // nextmap is 0-based, unlike gamemap - if (nextmapoverride != 0) + if (!demo.playback) { - nextmap = (INT16)(nextmapoverride-1); - } - else if (marathonmode && mapheaderinfo[gamemap-1]->marathonnext) - { - nextmap = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1); - } - else if (grandprixinfo.gp == true) - { - if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session - { - nextmap = prevmap; // Same map - } - else - { - if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map - { - nextmap = 1101; // ceremonymap - } - else - { - // Proceed to next map - nextmap = grandprixinfo.cup->levellist[grandprixinfo.roundnum]; - grandprixinfo.roundnum++; - } - } - } - else - { - nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); - if (marathonmode && nextmap == spmarathon_start-1) - nextmap = 1100-1; // No infinite loop for you - } - - // Remember last map for when you come out of the special stage. - if (!spec) - lastmap = nextmap; - - // If nextmap is actually going to get used, make sure it points to - // a map of the proper gametype -- skip levels that don't support - // the current gametype. (Helps avoid playing boss levels in Race, - // for instance). - if (!modeattacking && grandprixinfo.gp == false && bossinfo.boss == false) - { - if (nextmap >= 0 && nextmap < NUMMAPS) - { - register INT16 cm = nextmap; - UINT32 tolflag = G_TOLFlag(gametype); - UINT8 visitedmap[(NUMMAPS+7)/8]; - - memset(visitedmap, 0, sizeof (visitedmap)); - - while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag)) - { - visitedmap[cm/8] |= (1<<(cm&7)); - if (!mapheaderinfo[cm]) - cm = -1; // guarantee error execution - else if (marathonmode && mapheaderinfo[cm]->marathonnext) - cm = (INT16)(mapheaderinfo[cm]->marathonnext-1); - else - cm = (INT16)(mapheaderinfo[cm]->nextlevel-1); - - if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error) - { - cm = nextmap; //Start the loop again so that the error checking below is executed. - - //Make sure the map actually exists before you try to go to it! - if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR)) - { - //CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1); - cm = 0; - break; - } - } - - if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar - { - // We got stuck in a loop, came back to the map we started on - // without finding one supporting the current gametype. - // Thus, print a warning, and just use this map anyways. - CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1); - break; - } - } - nextmap = cm; - } - - if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1) - I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1); + nextmap = G_GetNextMap(true); + // Remember last map for when you come out of the special stage. if (!spec) - lastmap = nextmap; // Remember last map for when you come out of the special stage. + lastmap = nextmap; + + // Set up power level gametype scrambles + K_SetPowerLevelScrambles(powertype); } - automapactive = false; - - if (!(grandprixinfo.gp == true)) - { - if (cv_advancemap.value == 0) // Stay on same map. - { - nextmap = prevmap; - } - else if (cv_advancemap.value == 2) // Go to random map. - { - nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, false, NULL); - } - } - - // We are committed to this map now. - // We may as well allocate its header if it doesn't exist - // (That is, if it's a real map) - if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) - P_AllocMapHeader(nextmap); - - // Set up power level gametype scrambles - K_SetPowerLevelScrambles(powertype); - -demointermission: - // If the current gametype has no intermission screen set, then don't start it. Y_DetermineIntermissionType(); - if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed) || (intertype == int_none)) + if ((skipstats && !modeattacking) + || (modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST)) + || (intertype == int_none)) { G_UpdateVisited(); G_HandleSaveLevel(); @@ -3892,17 +4044,10 @@ void G_AfterIntermission(void) { Y_CleanupScreenBuffer(); - if (modeattacking) - { - M_EndModeAttackRun(); - return; - } - if (gamecomplete == 2) // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission gamecomplete = 1; HU_ClearCEcho(); - //G_NextLevel(); if (demo.playback) { @@ -3924,11 +4069,11 @@ void G_AfterIntermission(void) return; } - if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene. - F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); + if (grandprixinfo.gp == true && mapheaderinfo[prevmap]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene. + F_StartCustomCutscene(mapheaderinfo[prevmap]->cutscenenum-1, false, false); else { - if (nextmap < 1100-1) + if (nextmap < NEXTMAP_SPECIAL) G_NextLevel(); else G_EndGame(); @@ -4069,7 +4214,27 @@ void G_EndGame(void) if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) G_SaveDemo(); - // 1100 or competitive multiplayer, so go back to title screen. + // Only do evaluation and credits in singleplayer contexts + if (!netgame && grandprixinfo.gp == true) + { + if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony + { + D_StartTitle(); //F_StartEnding(); -- temporary + return; + } + if (nextmap == NEXTMAP_CREDITS) // end game with credits + { + F_StartCredits(); + return; + } + if (nextmap == NEXTMAP_EVALUATION) // end game with evaluation + { + F_StartGameEvaluation(); + return; + } + } + + // direct or competitive multiplayer, so go back to title screen. D_StartTitle(); } @@ -4079,31 +4244,23 @@ void G_EndGame(void) // Sets a tad of default info we need. void G_LoadGameSettings(void) { - // defaults - spstage_start = spmarathon_start = 1; - sstage_start = 50; - sstage_end = 56; // 7 special stages in vanilla SRB2 - sstage_end++; // plus one weirdo - smpstage_start = 60; - smpstage_end = 66; // 7 multiplayer special stages too - // initialize free sfx slots for skin sounds S_InitRuntimeSounds(); } +#define GD_VERSIONCHECK 0xBA5ED444 + // G_LoadGameData // Loads the main data file, which stores information such as emblems found, etc. void G_LoadGameData(void) { - size_t length; - INT32 i, j; + UINT32 i, j; UINT8 modded = false; UINT8 rtemp; - savebuffer_t save; + savebuffer_t save = {0}; //For records - tic_t rectime; - tic_t reclap; + UINT32 numgamedatamapheaders; // Clear things so previously read gamedata doesn't transfer // to new gamedata @@ -4129,23 +4286,19 @@ void G_LoadGameData(void) if (M_CheckParm("-resetdata")) return; // Don't load (essentially, reset). - - length = FIL_ReadFile(va(pandf, srb2home, gamedatafilename), &save.buffer); - if (!length) // Aw, no game data. Their loss! + + if (P_SaveBufferFromFile(&save, va(pandf, srb2home, gamedatafilename)) == false) return; - save.p = save.buffer; - // Version check - if (READUINT32(save.p) != 0xFCAFE211) + if (READUINT32(save.p) != GD_VERSIONCHECK) { const char *gdfolder = "the SRB2Kart folder"; if (strcmp(srb2home,".")) gdfolder = srb2home; - Z_Free(save.buffer); - save.p = NULL; - I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder); + P_SaveBufferFree(&save); + I_Error("Game data is not for SRB2Kart v2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder); } totalplaytime = READUINT32(save.p); @@ -4166,11 +4319,6 @@ void G_LoadGameData(void) else if (modded != true && modded != false) goto datacorrupt; - // TODO put another cipher on these things? meh, I don't care... - for (i = 0; i < NUMMAPS; i++) - if ((mapvisited[i] = READUINT8(save.p)) > MV_MAX) - goto datacorrupt; - // To save space, use one bit per collected/achieved/unlocked flag for (i = 0; i < MAXEMBLEMS;) { @@ -4204,22 +4352,49 @@ void G_LoadGameData(void) timesBeaten = READUINT32(save.p); // Main records - for (i = 0; i < NUMMAPS; ++i) + numgamedatamapheaders = READUINT32(save.p); + if (numgamedatamapheaders >= NEXTMAP_SPECIAL) + goto datacorrupt; + + for (i = 0; i < numgamedatamapheaders; i++) { + char mapname[MAXMAPLUMPNAME]; + INT16 mapnum; + tic_t rectime; + tic_t reclap; + + READSTRINGN(save.p, mapname, sizeof(mapname)); + mapnum = G_MapNumber(mapname); + + rtemp = READUINT8(save.p); rectime = (tic_t)READUINT32(save.p); reclap = (tic_t)READUINT32(save.p); - if (rectime || reclap) + if (mapnum < nummapheaders && mapheaderinfo[mapnum]) { - G_AllocMainRecordData((INT16)i); - mainrecords[i]->time = rectime; - mainrecords[i]->lap = reclap; + // Valid mapheader, time to populate with record data. + + if ((mapheaderinfo[mapnum]->mapvisited = rtemp) & ~MV_MAX) + goto datacorrupt; + + if (rectime || reclap) + { + G_AllocMainRecordData((INT16)i); + mapheaderinfo[i]->mainrecord->time = rectime; + mapheaderinfo[i]->mainrecord->lap = reclap; + CONS_Printf("ID %d, Time = %d, Lap = %d\n", i, rectime/35, reclap/35); + } + } + else + { + // Since it's not worth declaring the entire gamedata + // corrupt over extra maps, we report and move on. + //CONS_Alert(CONS_WARNING, "Map with lumpname %s does not exist, time record data will be discarded\n", mapname); } } // done - Z_Free(save.buffer); - save.p = NULL; + P_SaveBufferFree(&save); // Silent update unlockables in case they're out of sync with conditions M_SilentUpdateUnlockablesAndEmblems(); @@ -4233,8 +4408,7 @@ void G_LoadGameData(void) if (strcmp(srb2home,".")) gdfolder = srb2home; - Z_Free(save.buffer); - save.p = NULL; + P_SaveBufferFree(&save); I_Error("Corrupt game data file.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder); } @@ -4247,18 +4421,23 @@ void G_SaveGameData(void) size_t length; INT32 i, j; UINT8 btemp; - savebuffer_t save; + savebuffer_t save = {0}; if (!gamedataloaded) return; // If never loaded (-nodata), don't save - save.p = save.buffer = (UINT8 *)malloc(GAMEDATASIZE); - if (!save.p) + length = 4+4+4+(PWRLV_NUMTYPES*2)+1 + + BIT_ARRAY_SIZE(MAXEMBLEMS) + + BIT_ARRAY_SIZE(MAXEXTRAEMBLEMS) + + BIT_ARRAY_SIZE(MAXUNLOCKABLES) + + BIT_ARRAY_SIZE(MAXCONDITIONSETS) + + 4+4+(nummapheaders*(MAXMAPLUMPNAME+1+4+4)); + + if (P_SaveBufferAlloc(&save, length) == false) { CONS_Alert(CONS_ERROR, M_GetText("No more free memory for saving game data\n")); return; } - save.end = save.buffer + save.size; #if 0 // SRB2Kart: Let players unlock stuff with addons. @@ -4271,22 +4450,18 @@ void G_SaveGameData(void) #endif // Version test - WRITEUINT32(save.p, 0xFCAFE211); + WRITEUINT32(save.p, GD_VERSIONCHECK); // 4 - WRITEUINT32(save.p, totalplaytime); - WRITEUINT32(save.p, matchesplayed); + WRITEUINT32(save.p, totalplaytime); // 4 + WRITEUINT32(save.p, matchesplayed); // 4 for (i = 0; i < PWRLV_NUMTYPES; i++) WRITEUINT16(save.p, vspowerlevel[i]); - WRITEUINT8(save.p, (UINT8)savemoddata); - - // TODO put another cipher on these things? meh, I don't care... - for (i = 0; i < NUMMAPS; i++) - WRITEUINT8(save.p, (mapvisited[i] & MV_MAX)); + WRITEUINT8(save.p, (UINT8)savemoddata); // 1 // To save space, use one bit per collected/achieved/unlocked flag - for (i = 0; i < MAXEMBLEMS;) + for (i = 0; i < MAXEMBLEMS;) // BIT_ARRAY_SIZE(MAXEMBLEMS) { btemp = 0; for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j) @@ -4294,7 +4469,7 @@ void G_SaveGameData(void) WRITEUINT8(save.p, btemp); i += j; } - for (i = 0; i < MAXEXTRAEMBLEMS;) + for (i = 0; i < MAXEXTRAEMBLEMS;) // BIT_ARRAY_SIZE(MAXEXTRAEMBLEMS) { btemp = 0; for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j) @@ -4302,7 +4477,7 @@ void G_SaveGameData(void) WRITEUINT8(save.p, btemp); i += j; } - for (i = 0; i < MAXUNLOCKABLES;) + for (i = 0; i < MAXUNLOCKABLES;) // BIT_ARRAY_SIZE(MAXUNLOCKABLES) { btemp = 0; for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j) @@ -4310,7 +4485,7 @@ void G_SaveGameData(void) WRITEUINT8(save.p, btemp); i += j; } - for (i = 0; i < MAXCONDITIONSETS;) + for (i = 0; i < MAXCONDITIONSETS;) // BIT_ARRAY_SIZE(MAXCONDITIONSETS) { btemp = 0; for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j) @@ -4319,29 +4494,34 @@ void G_SaveGameData(void) i += j; } - WRITEUINT32(save.p, timesBeaten); + WRITEUINT32(save.p, timesBeaten); // 4 // Main records - for (i = 0; i < NUMMAPS; i++) + WRITEUINT32(save.p, nummapheaders); // 4 + + for (i = 0; i < nummapheaders; i++) // nummapheaders * (MAXMAPLUMPNAME+1+4+4) { - if (mainrecords[i]) + // For figuring out which header to assing it to on load + WRITESTRINGN(save.p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME); + + WRITEUINT8(save.p, (mapheaderinfo[i]->mapvisited & MV_MAX)); + + if (mapheaderinfo[i]->mainrecord) { - WRITEUINT32(save.p, mainrecords[i]->time); - WRITEUINT32(save.p, mainrecords[i]->lap); + WRITEUINT32(save.p, mapheaderinfo[i]->mainrecord->time); + WRITEUINT32(save.p, mapheaderinfo[i]->mainrecord->lap); } else { WRITEUINT32(save.p, 0); WRITEUINT32(save.p, 0); } - WRITEUINT8(save.p, 0); // compat } length = save.p - save.buffer; FIL_WriteFile(va(pandf, srb2home, gamedatafilename), save.buffer, length); - free(save.buffer); - save.p = save.buffer = NULL; + P_SaveBufferFree(&save); } #define VERSIONSIZE 16 @@ -4352,10 +4532,9 @@ void G_SaveGameData(void) // void G_LoadGame(UINT32 slot, INT16 mapoverride) { - size_t length; char vcheck[VERSIONSIZE]; char savename[255]; - savebuffer_t save; + savebuffer_t save = {0}; // memset savedata to all 0, fixes calling perfectly valid saves corrupt because of bots memset(&savedata, 0, sizeof(savedata)); @@ -4370,17 +4549,12 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) else sprintf(savename, savegamename, slot); - length = FIL_ReadFile(savename, &save.buffer); - if (!length) + if (P_SaveBufferFromFile(&save, savename) == false) { CONS_Printf(M_GetText("Couldn't read file %s\n"), savename); return; } - save.p = save.buffer; - save.size = length; - save.end = save.buffer + save.size; - memset(vcheck, 0, sizeof (vcheck)); sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION); if (strcmp((const char *)save.p, (const char *)vcheck)) @@ -4393,7 +4567,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) M_ClearMenus(true); // so ESC backs out to title M_StartMessage(M_GetText("Save game from different version\n\nPress ESC\n"), NULL, MM_NOTHING); Command_ExitGame_f(); - Z_Free(save.buffer); + P_SaveBufferFree(&save); // no cheating! memset(&savedata, 0, sizeof(savedata)); @@ -4428,7 +4602,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) } // done - Z_Free(save.buffer); + P_SaveBufferFree(&save); // gameaction = ga_nothing; // G_SetGamestate(GS_LEVEL); @@ -4454,7 +4628,7 @@ void G_SaveGame(UINT32 slot, INT16 mapnum) boolean saved; char savename[256] = ""; const char *backup; - savebuffer_t save; + savebuffer_t save = {0}; if (marathonmode) strcpy(savename, liveeventbackup); @@ -4467,14 +4641,11 @@ void G_SaveGame(UINT32 slot, INT16 mapnum) char name[VERSIONSIZE]; size_t length; - save.size = SAVEGAMESIZE; - save.p = save.buffer = (UINT8 *)malloc(save.size); - if (!save.p) + if (P_SaveBufferAlloc(&save, SAVEGAMESIZE) == false) { CONS_Alert(CONS_ERROR, M_GetText("No more free memory for saving game data\n")); return; } - save.end = save.buffer + save.size; memset(name, 0, sizeof (name)); sprintf(name, (marathonmode ? "back-up %d" : "version %d"), VERSION); @@ -4492,7 +4663,7 @@ void G_SaveGame(UINT32 slot, INT16 mapnum) length = save.p - save.buffer; saved = FIL_WriteFile(backup, save.buffer, length); - free(save.buffer); + P_SaveBufferFree(&save); } gameaction = ga_nothing; @@ -4512,7 +4683,7 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives) char vcheck[VERSIONSIZE]; char savename[255]; const char *backup; - savebuffer_t save; + savebuffer_t save = {0}; if (marathonmode) strcpy(savename, liveeventbackup); @@ -4520,22 +4691,19 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives) sprintf(savename, savegamename, slot); backup = va("%s",savename); - length = FIL_ReadFile(savename, &save.buffer); - if (!length) + if (P_SaveBufferFromFile(&save, savename) == false) { CONS_Printf(M_GetText("Couldn't read file %s\n"), savename); return; } + length = save.size; + { char temp[sizeof(timeattackfolder)]; UINT8 *lives_p; SINT8 pllives; - save.p = save.buffer; - save.size = length; - save.end = save.buffer + save.size; - // Version check memset(vcheck, 0, sizeof (vcheck)); sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION); @@ -4606,9 +4774,8 @@ cleanup: CONS_Printf(M_GetText("Game saved.\n")); else if (!saved) CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename)); - Z_Free(save.buffer); - save.p = save.buffer = NULL; + P_SaveBufferFree(&save); } #undef CHECKPOS #undef BADSAVE @@ -4618,9 +4785,8 @@ cleanup: // Can be called by the startup code or the menu task, // consoleplayer, displayplayers[], playeringame[] should be set. // -void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, UINT8 ssplayers, boolean FLS) +void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS) { - INT32 i; UINT16 color = SKINCOLOR_NONE; paused = false; @@ -4630,8 +4796,7 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar G_FreeGhosts(); // TODO: do we actually need to do this? - for (i = 0; i < NUMMAPS+1; i++) - randmapbuffer[i] = -1; + G_ResetRandMapBuffer(); // this leave the actual game if needed SV_StartSinglePlayerServer(); @@ -4650,18 +4815,17 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar CV_StealthSetValue(&cv_playercolor[0], color); } - if (mapname) - { - D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pencoremode, true, 1, false, FLS); - } + D_MapChange(map, gametype, pencoremode, true, 1, false, FLS); } // // This is the map command interpretation something like Command_Map_f // // called at: map cmd execution, doloadgame, doplaydemo -void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS) +void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skipprecutscene, boolean FLS) { + const char * mapname = G_BuildMapName(map); + INT32 i; (void)FLS; @@ -4674,7 +4838,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool S_ResumeAudio(); } - prevencoremode = ((gamestate == GS_TITLESCREEN) ? false : encoremode); + prevencoremode = ((!Playing()) ? false : encoremode); encoremode = pencoremode; legitimateexit = false; // SRB2Kart @@ -4712,19 +4876,20 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool // internal game map // well this check is useless because it is done before (d_netcmd.c::command_map_f) // but in case of for demos.... - if (W_CheckNumForName(mapname) == LUMPERROR) + if (!mapname) + { + I_Error("Internal game map with ID %d not found\n", map); + Command_ExitGame_f(); + return; + } + if (mapheaderinfo[map-1]->lumpnum == LUMPERROR) { I_Error("Internal game map '%s' not found\n", mapname); Command_ExitGame_f(); return; } - gamemap = (INT16)M_MapNumber(mapname[3], mapname[4]); // get xx out of MAPxx - - // gamemap changed; we assume that its map header is always valid, - // so make it so - if(!mapheaderinfo[gamemap-1]) - P_AllocMapHeader(gamemap-1); + gamemap = map; maptol = mapheaderinfo[gamemap-1]->typeoflevel; globalweather = mapheaderinfo[gamemap-1]->weather; @@ -4747,7 +4912,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool { char *title = G_BuildMapTitle(gamemap); - CON_LogMessage(va(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap))); + CON_LogMessage(va(M_GetText("Map is now \"%s"), mapname)); if (title) { CON_LogMessage(va(": %s", title)); @@ -4762,11 +4927,8 @@ char *G_BuildMapTitle(INT32 mapnum) { char *title = NULL; - if (mapnum == 0) - return Z_StrDup("Random"); - - if (!mapheaderinfo[mapnum-1]) - P_AllocMapHeader(mapnum-1); + if (!mapnum || mapnum > nummapheaders || !mapheaderinfo[mapnum-1]) + I_Error("G_BuildMapTitle: Internal map ID %d not found (nummapheaders = %d)", mapnum-1, nummapheaders); if (strcmp(mapheaderinfo[mapnum-1]->lvlttl, "")) { @@ -4862,21 +5024,17 @@ INT32 G_FindMap(const char *mapname, char **foundmapnamep, mapnamelen = strlen(mapname); - /* Count available maps; how ugly. */ - for (i = 0, freqc = 0; i < NUMMAPS; ++i) - { - if (mapheaderinfo[i]) - freqc++; - } - - freq = ZZ_Calloc(freqc * sizeof (mapsearchfreq_t)); + freq = ZZ_Calloc(nummapheaders * sizeof (mapsearchfreq_t)); wanttable = !!( freqp ); freqc = 0; - for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum) - if (mapheaderinfo[i]) + + for (i = 0, mapnum = 1; i < nummapheaders; ++i, ++mapnum) { + if (!mapheaderinfo[i] || mapheaderinfo[i]->lumpnum == LUMPERROR) + continue; + if (!( realmapname = G_BuildMapTitle(mapnum) )) continue; @@ -4985,8 +5143,6 @@ void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc) INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) { - boolean usemapcode = false; - INT32 newmapnum; size_t mapnamelen; @@ -4995,45 +5151,35 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep) mapnamelen = strlen(mapname); - if (mapnamelen == 2)/* maybe two digit code */ + if (mapnamelen == 1) { - if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) )) - usemapcode = true; - } - else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0) - { - if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) )) - usemapcode = true; + if (mapname[0] == '*') // current map + return gamemap; + else if (mapname[0] == '+') // next map + return G_GetNextMap(false)+1; } - if (!usemapcode) - { - /* Now detect map number in base 10, which no one asked for. */ - newmapnum = strtol(mapname, &p, 10); - if (*p == '\0')/* we got it */ - { - if (newmapnum < 1 || newmapnum > NUMMAPS) - { - CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum); - return 0; - } - usemapcode = true; - } - else - { - newmapnum = G_FindMap(mapname, realmapnamep, NULL, NULL); - } - } + /* Now detect map number in base 10, which no one asked for. */ + newmapnum = strtol(mapname, &p, 10); - if (usemapcode) + if (*p == '\0')/* we got it */ { - /* we can't check mapheaderinfo for this hahahaha */ - if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR) + if (newmapnum < 1 || newmapnum > nummapheaders) return 0; - if (realmapnamep) - (*realmapnamep) = G_BuildMapTitle(newmapnum); + if (!mapheaderinfo[newmapnum-1] || mapheaderinfo[newmapnum-1]->lumpnum == LUMPERROR) + return 0; } + else + { + newmapnum = G_MapNumber(mapname)+1; + + if (newmapnum > nummapheaders) + return G_FindMap(mapname, realmapnamep, NULL, NULL); + } + + if (realmapnamep) + (*realmapnamep) = G_BuildMapTitle(newmapnum); return newmapnum; } diff --git a/src/g_game.h b/src/g_game.h index 091e162ff..4ff12b60a 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -27,7 +27,6 @@ extern "C" { extern char gamedatafilename[64]; extern char timeattackfolder[64]; extern char customversionstring[32]; -#define GAMEDATASIZE (4*8192) extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; extern INT32 player_name_changes[MAXPLAYERS]; @@ -40,6 +39,19 @@ extern tic_t levelstarttic; // for modding? extern INT16 prevmap, nextmap; + +// see also G_MapNumber +typedef enum +{ + NEXTMAP_RESERVED = INT16_MAX, // so nextmap+1 doesn't roll over -- remove when gamemap is made 0-indexed + NEXTMAP_TITLE = INT16_MAX-1, + NEXTMAP_EVALUATION = INT16_MAX-2, + NEXTMAP_CREDITS = INT16_MAX-3, + NEXTMAP_CEREMONY = INT16_MAX-4, + NEXTMAP_INVALID = INT16_MAX-5, // Always last (swap with NEXTMAP_RESERVED when removing that) + NEXTMAP_SPECIAL = NEXTMAP_INVALID +} nextmapspecial_t; + extern INT32 gameovertics; extern UINT8 ammoremovaltics; extern tic_t timeinmap; // Ticker for time spent in level (used for levelcard display) @@ -93,8 +105,8 @@ void weaponPrefChange4(void); #define MAXPLMOVE (50) #define SLOWTURNTICS (cv_turnsmooth.value * 3) -// build an internal map name MAPxx from map number const char *G_BuildMapName(INT32 map); +INT32 G_MapNumber(const char *mapname); void G_ResetAnglePrediction(player_t *player); void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer); @@ -136,7 +148,7 @@ extern INT32 localaiming[MAXSPLITSCREENPLAYERS]; // should be an angle_t but sig 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, const char *mapname, boolean resetplayer, +void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skipprecutscene, boolean FLS); char *G_BuildMapTitle(INT32 mapnum); @@ -173,7 +185,7 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost); // Can be called by the startup code or M_Responder. // A normal game starts at map 1, but a warp test can start elsewhere -void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, +void G_DeferedInitNew(boolean pencoremode, INT32 map, INT32 pickedchar, UINT8 ssplayers, boolean FLS); void G_DoLoadLevel(boolean resetplayer); @@ -205,7 +217,8 @@ boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); -INT16 G_SometimesGetDifferentGametype(void); +#define VOTEMODIFIER_ENCORE 0x80 +INT16 G_SometimesGetDifferentGametype(UINT8 prefgametype); UINT8 G_GetGametypeColor(INT16 gt); void G_BeginLevelExit(void); void G_FinishExitLevel(void); @@ -267,6 +280,7 @@ FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics); // Don't split up TOL handling UINT32 G_TOLFlag(INT32 pgametype); +INT16 G_GetFirstMapOfGametype(UINT8 pgametype); INT16 G_RandMap(UINT32 tolflags, INT16 pprevmap, UINT8 ignorebuffer, UINT8 maphell, boolean callagainsoon, INT16 *extbuffer); void G_AddMapToBuffer(INT16 map); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 972faab56..e6cbcbda9 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -98,6 +98,9 @@ static char hu_tick; //------------------------------------------- patch_t *missingpat; +patch_t *blanklvl; +patch_t *randomlvl; +patch_t *nolvl; // song credits static patch_t *songcreditbg; @@ -186,6 +189,10 @@ void HU_LoadGraphics(void) Font_Load(); + HU_UpdatePatch(&blanklvl, "BLANKLVL"); + HU_UpdatePatch(&randomlvl, "RANDOMLV"); + HU_UpdatePatch(&nolvl, "M_NOLVL"); + HU_UpdatePatch(&songcreditbg, "K_SONGCR"); // cache ping gfx: diff --git a/src/k_battle.c b/src/k_battle.c index d554d0bdf..482bf3b74 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -241,7 +241,7 @@ void K_CheckBumpers(void) winnerscoreadd -= players[i].roundscore; } - if (bossinfo.boss) + if (K_CanChangeRules() == false) { if (nobumpers) { diff --git a/src/k_hud.c b/src/k_hud.c index dcfcccdf2..0e5af2687 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1454,7 +1454,7 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI else if ((drawtime/TICRATE) & 1) V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99")); - if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time! + if ((modeattacking || (mode == 1)) && !demo.playback) // emblem time! { INT32 workx = TX + 96, worky = TY+18; SINT8 curemb = 0; @@ -3300,7 +3300,6 @@ static void K_drawKartMinimapWaypoint(waypoint_t *wp, INT32 hudx, INT32 hudy, IN static void K_drawKartMinimap(void) { - INT32 lumpnum; patch_t *AutomapPic, *workingPic; INT32 i = 0; INT32 x, y; @@ -3323,12 +3322,12 @@ static void K_drawKartMinimap(void) if (stplyr != &players[displayplayers[0]]) return; - lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(gamemap))); + AutomapPic = mapheaderinfo[gamemap-1]->minimapPic; - if (lumpnum != -1) - AutomapPic = W_CachePatchName(va("%sR", G_BuildMapName(gamemap)), PU_HUDGFX); - else + if (!AutomapPic) + { return; // no pic, just get outta here + } if (r_splitscreen < 2) // 1/2P right aligned { diff --git a/src/k_kart.c b/src/k_kart.c index 57d9d83de..02789b821 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -222,6 +222,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartcomeback); CV_RegisterVar(&cv_kartencore); CV_RegisterVar(&cv_kartvoterulechanges); + CV_RegisterVar(&cv_kartgametypepreference); CV_RegisterVar(&cv_kartspeedometer); CV_RegisterVar(&cv_kartvoices); CV_RegisterVar(&cv_kartbot); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 020a6dcf8..dd1a8c04a 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -34,7 +34,6 @@ #include "k_hud.h" #include "d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff -#include "m_misc.h" // M_MapNumber #include "p_spec.h" // P_StartQuake #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision @@ -390,18 +389,18 @@ static int lib_pGetColorAfter(lua_State *L) // M_MISC ////////////// -static int lib_mMapNumber(lua_State *L) +static int lib_gMapNumber(lua_State *L) { const char *arg = luaL_checkstring(L, 1); - size_t len = strlen(arg); - if (len == 2 || len == 5) { - char first = arg[len-2]; - char second = arg[len-1]; - lua_pushinteger(L, M_MapNumber(first, second)); - } else { - lua_pushinteger(L, 0); - } - return 1; + INT32 map; + + map = G_MapNumber(arg); + + if (map == INT32_MAX) + return 0; + + + return map; } // M_RANDOM @@ -3166,12 +3165,12 @@ static int lib_gBuildMapTitle(lua_State *L) { INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapTitle"); char *name; - if (map < 1 || map > NUMMAPS) + if (map < 1 || map > nummapheaders) { return luaL_error(L, - "map number %d out of range (1 - %d)", + "map ID %d out of range (1 - %d)", map, - NUMMAPS + nummapheaders ); } name = G_BuildMapTitle(map); @@ -3968,9 +3967,6 @@ static luaL_Reg lib[] = { {"M_GetColorAfter",lib_pGetColorAfter}, {"M_GetColorBefore",lib_pGetColorBefore}, - // m_misc - {"M_MapNumber",lib_mMapNumber}, - // m_random {"P_RandomFixed",lib_pRandomFixed}, {"P_RandomByte",lib_pRandomByte}, @@ -4173,6 +4169,7 @@ static luaL_Reg lib[] = { // g_game {"G_AddGametype", lib_gAddGametype}, {"G_BuildMapName",lib_gBuildMapName}, + {"G_MapNumber",lib_gMapNumber}, {"G_BuildMapTitle",lib_gBuildMapTitle}, {"G_FindMap",lib_gFindMap}, {"G_FindMapByNameOrCode",lib_gFindMapByNameOrCode}, diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index d25628404..810dc2b24 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -22,6 +22,7 @@ #include "p_local.h" // camera_t #include "screen.h" // screen width/height #include "m_random.h" // m_random +#include "m_menu.h" // M_GetMapThumbnail #include "v_video.h" #include "w_wad.h" #include "z_zone.h" @@ -605,7 +606,6 @@ static int libd_drawOnMinimap(lua_State *L) huddrawlist_h list; // variables used to replicate k_kart's mmap drawer: - INT32 lumpnum; patch_t *AutomapPic; INT32 mx, my; INT32 splitflags, minimaptrans; @@ -691,12 +691,12 @@ static int libd_drawOnMinimap(lua_State *L) if (stplyr != &players[displayplayers[0]]) return 0; - lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(gamemap))); + AutomapPic = mapheaderinfo[gamemap-1]->minimapPic; - if (lumpnum != -1) - AutomapPic = W_CachePatchName(va("%sR", G_BuildMapName(gamemap)), PU_HUDGFX); - else + if (!AutomapPic) + { return 0; // no pic, just get outta here + } mx = MM_X - (AutomapPic->width/2); my = MM_Y - (AutomapPic->height/2); @@ -1091,6 +1091,41 @@ static int libd_getStringColormap(lua_State *L) return 0; } +static int libd_getMapThumbnail(lua_State *L) +{ + INT16 mapnum; + patch_t *patch = NULL; + HUDONLY + if (lua_type(L, 1) == LUA_TNUMBER) + mapnum = luaL_checkinteger(L, 1) - 1; + else + mapnum = G_MapNumber(luaL_checkstring(L, 1)); + + fixed_t scale = M_GetMapThumbnail(mapnum, &patch); + LUA_PushUserdata(L, patch, META_PATCH); + lua_pushfixed(L, scale); + return 2; +} + +static int libd_getMapMinimap(lua_State *L) +{ + INT16 mapnum; + patch_t *patch = NULL; + HUDONLY + if (lua_type(L, 1) == LUA_TNUMBER) + mapnum = luaL_checkinteger(L, 1) - 1; + else + mapnum = G_MapNumber(luaL_checkstring(L, 1)); + + if (mapnum >= 0 && mapnum < nummapheaders && mapheaderinfo[mapnum]) + patch = mapheaderinfo[mapnum]->minimapPic; + if (!patch) + patch = missingpat; + + LUA_PushUserdata(L, patch, META_PATCH); + return 1; +} + static int libd_width(lua_State *L) { HUDONLY @@ -1258,6 +1293,8 @@ static luaL_Reg lib_draw[] = { {"getSprite2Patch", libd_getSprite2Patch}, {"getColormap", libd_getColormap}, {"getStringColormap", libd_getStringColormap}, + {"getMapThumbnail", libd_getMapThumbnail}, + {"getMapMinimap", libd_getMapMinimap}, // drawing {"draw", libd_draw}, {"drawScaled", libd_drawScaled}, diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 0ca4167aa..77a3e6174 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -2516,8 +2516,8 @@ static int lib_getMapheaderinfo(lua_State *L) lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) { - size_t i = lua_tointeger(L, 1)-1; - if (i >= NUMMAPS) + INT32 i = lua_tointeger(L, 1)-1; + if (i < 0 || i >= nummapheaders) return 0; LUA_PushUserdata(L, mapheaderinfo[i], META_MAPHEADER); //CONS_Printf(mapheaderinfo[i]->lvlttl); @@ -2535,7 +2535,7 @@ static int lib_getMapheaderinfo(lua_State *L) static int lib_nummapheaders(lua_State *L) { - lua_pushinteger(L, NUMMAPS); + lua_pushinteger(L, nummapheaders); return 1; } @@ -2547,7 +2547,6 @@ static int mapheaderinfo_get(lua_State *L) { mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER)); const char *field = luaL_checkstring(L, 2); - INT16 i; if (fastcmp(field,"lvlttl")) lua_pushstring(L, header->lvlttl); else if (fastcmp(field,"subttl")) @@ -2558,10 +2557,6 @@ static int mapheaderinfo_get(lua_State *L) lua_pushstring(L, header->actnum); else if (fastcmp(field,"typeoflevel")) lua_pushinteger(L, header->typeoflevel); - else if (fastcmp(field,"nextlevel")) - lua_pushinteger(L, header->nextlevel); - else if (fastcmp(field,"marathonnext")) - lua_pushinteger(L, header->marathonnext); else if (fastcmp(field,"keywords")) lua_pushstring(L, header->keywords); else if (fastcmp(field,"musname")) // we create a table here because it saves us from a userdata nightmare @@ -2585,22 +2580,6 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->muspos); else if (fastcmp(field,"musname_size")) lua_pushinteger(L, header->musname_size); - else if (fastcmp(field,"musinterfadeout")) - lua_pushinteger(L, header->musinterfadeout); - else if (fastcmp(field,"musintername")) - lua_pushstring(L, header->musintername); - else if (fastcmp(field,"muspostbossname")) - lua_pushstring(L, header->muspostbossname); - else if (fastcmp(field,"muspostbosstrack")) - lua_pushinteger(L, header->muspostbosstrack); - else if (fastcmp(field,"muspostbosspos")) - lua_pushinteger(L, header->muspostbosspos); - else if (fastcmp(field,"muspostbossfadein")) - lua_pushinteger(L, header->muspostbossfadein); - else if (fastcmp(field,"musforcereset")) - lua_pushinteger(L, header->musforcereset); - else if (fastcmp(field,"forcecharacter")) - lua_pushstring(L, header->forcecharacter); else if (fastcmp(field,"weather")) lua_pushinteger(L, header->weather); else if (fastcmp(field,"skytexture")) @@ -2611,12 +2590,7 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->skybox_scaley); else if (fastcmp(field,"skybox_scalez")) lua_pushinteger(L, header->skybox_scalez); - else if (fastcmp(field,"interscreen")) { - for (i = 0; i < 8; i++) - if (!header->interscreen[i]) - break; - lua_pushlstring(L, header->interscreen, i); - } else if (fastcmp(field,"runsoc")) + else if (fastcmp(field,"runsoc")) lua_pushstring(L, header->runsoc); else if (fastcmp(field,"scriptname")) lua_pushstring(L, header->scriptname); @@ -2624,8 +2598,6 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->precutscenenum); else if (fastcmp(field,"cutscenenum")) lua_pushinteger(L, header->cutscenenum); - else if (fastcmp(field,"countdown")) - lua_pushinteger(L, header->countdown); else if (fastcmp(field,"palette")) lua_pushinteger(L, header->palette); else if (fastcmp(field,"numlaps")) @@ -2634,31 +2606,14 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->unlockrequired); else if (fastcmp(field,"levelselect")) lua_pushinteger(L, header->levelselect); - else if (fastcmp(field,"bonustype")) - lua_pushinteger(L, header->bonustype); - else if (fastcmp(field,"ltzzpatch")) - lua_pushstring(L, header->ltzzpatch); - else if (fastcmp(field,"ltzztext")) - lua_pushstring(L, header->ltzztext); - else if (fastcmp(field,"ltactdiamond")) - lua_pushstring(L, header->ltactdiamond); - else if (fastcmp(field,"maxbonuslives")) - lua_pushinteger(L, header->maxbonuslives); else if (fastcmp(field,"levelflags")) lua_pushinteger(L, header->levelflags); else if (fastcmp(field,"menuflags")) lua_pushinteger(L, header->menuflags); else if (fastcmp(field,"mobj_scale")) lua_pushfixed(L, header->mobj_scale); - else if (fastcmp(field,"startrings")) - lua_pushinteger(L, header->startrings); - else if (fastcmp(field, "sstimer")) - lua_pushinteger(L, header->sstimer); - else if (fastcmp(field, "ssspheres")) - lua_pushinteger(L, header->ssspheres); else if (fastcmp(field, "gravity")) lua_pushfixed(L, header->gravity); - // TODO add support for reading numGradedMares and grades else { // Read custom vars now // (note: don't include the "LUA." in your lua scripts!) diff --git a/src/lua_script.c b/src/lua_script.c index 0f40b6a7d..67c8b9b7e 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -213,35 +213,17 @@ int LUA_PushGlobals(lua_State *L, const char *word) lua_pushinteger(L, cv_pointlimit.value); return 1; // begin map vars - } else if (fastcmp(word,"spstage_start")) { - lua_pushinteger(L, spstage_start); - return 1; - } else if (fastcmp(word,"spmarathon_start")) { - lua_pushinteger(L, spmarathon_start); - return 1; - } else if (fastcmp(word,"sstage_start")) { - lua_pushinteger(L, sstage_start); - return 1; - } else if (fastcmp(word,"sstage_end")) { - lua_pushinteger(L, sstage_end); - return 1; - } else if (fastcmp(word,"smpstage_start")) { - lua_pushinteger(L, smpstage_start); - return 1; - } else if (fastcmp(word,"smpstage_end")) { - lua_pushinteger(L, smpstage_end); - return 1; } else if (fastcmp(word,"titlemap")) { - lua_pushinteger(L, titlemap); + lua_pushstring(L, titlemap); return 1; } else if (fastcmp(word,"titlemapinaction")) { lua_pushboolean(L, (titlemapinaction != TITLEMAP_OFF)); return 1; } else if (fastcmp(word,"bootmap")) { - lua_pushinteger(L, bootmap); + lua_pushstring(L, bootmap); return 1; } else if (fastcmp(word,"tutorialmap")) { - lua_pushinteger(L, tutorialmap); + lua_pushstring(L, tutorialmap); return 1; } else if (fastcmp(word,"tutorialmode")) { lua_pushboolean(L, tutorialmode); diff --git a/src/m_cheat.c b/src/m_cheat.c index af7a6b51e..61c7f3441 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -758,6 +758,8 @@ struct debugFlagNames_s const debug_flag_names[] = {"Lua", DBG_LUA}, {"RNG", DBG_RNG}, {"Randomizer", DBG_RNG}, // alt name + {"Demo", DBG_DEMO}, + {"Replay", DBG_DEMO}, // alt name {NULL, 0} }; diff --git a/src/m_cond.c b/src/m_cond.c index fdad03069..e9d2c71b2 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -30,124 +30,7 @@ UINT32 unlocktriggers; conditionset_t conditionSets[MAXCONDITIONSETS]; // Default Emblem locations -emblem_t emblemlocations[MAXEMBLEMS] = -{ - // GOLD DEV MEDALS - // These values are directly lifted from the Champion ghost, through the power of hex editing! - {ET_TIME, 0, 1, 'A', SKINCOLOR_GOLD, 3021, "", 0}, // Green Hills Zone - 1'26"31 - {ET_TIME, 0, 2, 'A', SKINCOLOR_GOLD, 4466, "", 0}, // Dark Race - 2'07"60 - {ET_TIME, 0, 3, 'A', SKINCOLOR_GOLD, 4337, "", 0}, // Northern District Zone - 2'03"91 - {ET_TIME, 0, 4, 'A', SKINCOLOR_GOLD, 3452, "", 0}, // Darkvile Garden Zone - 1'38"62 - {ET_TIME, 0, 5, 'A', SKINCOLOR_GOLD, 3525, "", 0}, // Daytona Speedway Zone - 1'40"71 - {ET_TIME, 0, 6, 'A', SKINCOLOR_GOLD, 4047, "", 0}, // Egg Zeppelin Zone - 1'55"62 - {ET_TIME, 0, 7, 'A', SKINCOLOR_GOLD, 4041, "", 0}, // Sonic Speedway Zone - 1'55"45 - {ET_TIME, 0, 8, 'A', SKINCOLOR_GOLD, 3281, "", 0}, // Hill Top Zone - 1'33"74 - {ET_TIME, 0, 9, 'A', SKINCOLOR_GOLD, 4764, "", 0}, // Misty Maze Zone - 2'16"11 - {ET_TIME, 0, 10, 'A', SKINCOLOR_GOLD, 4378, "", 0}, // Grand Metropolis - 2'05"08 - {ET_TIME, 0, 11, 'A', SKINCOLOR_GOLD, 3678, "", 0}, // Sunbeam Paradise Zone - 1'45"08 - {ET_TIME, 0, 12, 'A', SKINCOLOR_GOLD, 3928, "", 0}, // Diamond Square Zone - 1'52"22 - {ET_TIME, 0, 13, 'A', SKINCOLOR_GOLD, 3846, "", 0}, // Midnight Meadow Zone - 1'49"88 - {ET_TIME, 0, 14, 'A', SKINCOLOR_GOLD, 3278, "", 0}, // Twinkle Cart - 1'33"65 - {ET_TIME, 0, 15, 'A', SKINCOLOR_GOLD, 3591, "", 0}, // Pleasure Castle - 1'42"60 - {ET_TIME, 0, 16, 'A', SKINCOLOR_GOLD, 5187, "", 0}, // Paradise Hill Zone - 2'28"20 - {ET_TIME, 0, 17, 'A', SKINCOLOR_GOLD, 4976, "", 0}, // Sub-Zero Peak Zone - 2'22"17 - {ET_TIME, 0, 18, 'A', SKINCOLOR_GOLD, 3696, "", 0}, // Sapphire Coast Zone - 1'45"60 - {ET_TIME, 0, 19, 'A', SKINCOLOR_GOLD, 4931, "", 0}, // Sand Valley Zone - 2'20"88 - {ET_TIME, 0, 20, 'A', SKINCOLOR_GOLD, 4220, "", 0}, // Megablock Castle Zone - 2'00"57 - {ET_TIME, 0, 21, 'A', SKINCOLOR_GOLD, 4053, "", 0}, // Canyon Rush Zone - 1'55"80 - {ET_TIME, 0, 22, 'A', SKINCOLOR_GOLD, 3613, "", 0}, // Casino Resort Zone - 1'43"22 - {ET_TIME, 0, 23, 'A', SKINCOLOR_GOLD, 4385, "", 0}, // Silvercloud Island Zone - 2'05"28 - {ET_TIME, 0, 24, 'A', SKINCOLOR_GOLD, 5321, "", 0}, // Blue Mountain Zone - 2'32"02 - {ET_TIME, 0, 25, 'A', SKINCOLOR_GOLD, 4476, "", 0}, // Petroleum Refinery Zone - 2'07"88 - {ET_TIME, 0, 26, 'A', SKINCOLOR_GOLD, 3295, "", 0}, // Desert Palace Zone - 1'34"14 - {ET_TIME, 0, 27, 'A', SKINCOLOR_GOLD, 4765, "", 0}, // Aurora Atoll Zone - 2'16"14 - {ET_TIME, 0, 28, 'A', SKINCOLOR_GOLD, 4670, "", 0}, // Barren Badlands Zone - 2'13"42 - {ET_TIME, 0, 29, 'A', SKINCOLOR_GOLD, 5888, "", 0}, // Red Barrage Area - 2'48"22 - {ET_TIME, 0, 30, 'A', SKINCOLOR_GOLD, 4047, "", 0}, // Midnight Channel - 1'55"62 - {ET_TIME, 0, 31, 'A', SKINCOLOR_GOLD, 4944, "", 0}, // Vanilla Hotel Zone - 2'21"25 - {ET_TIME, 0, 32, 'A', SKINCOLOR_GOLD, 4638, "", 0}, // Toxic Palace Zone - 2'12"51 - {ET_TIME, 0, 33, 'A', SKINCOLOR_GOLD, 4036, "", 0}, // Ancient Tomb Zone - 1'55"31 - {ET_TIME, 0, 34, 'A', SKINCOLOR_GOLD, 3595, "", 0}, // Cloud Cradle Zone K - 1'42"71 - {ET_TIME, 0, 35, 'A', SKINCOLOR_GOLD, 4705, "", 0}, // Volcanic Valley Zone - 2'14"42 - {ET_TIME, 0, 36, 'A', SKINCOLOR_GOLD, 3314, "", 0}, // Kodachrome Void Zone - 1'34"68 - {ET_TIME, 0, 37, 'A', SKINCOLOR_GOLD, 3501, "", 0}, // Boiling Bedrock Zone - 1'40"02 - {ET_TIME, 0, 38, 'A', SKINCOLOR_GOLD, 4920, "", 0}, // Egg Quarters - 2'20"57 - {ET_TIME, 0, 39, 'A', SKINCOLOR_GOLD, 4318, "", 0}, // Virtual Highway Zone - 2'03"37 - {ET_TIME, 0, 40, 'A', SKINCOLOR_GOLD, 3550, "", 0}, // Eggman's Nightclub Zone - 1'41"42 - {ET_TIME, 0, 41, 'A', SKINCOLOR_GOLD, 2572, "", 0}, // KKR Ganbare Dochu 2 - 1'13"48 - {ET_TIME, 0, 42, 'A', SKINCOLOR_GOLD, 3091, "", 0}, // CK Chao Circuit 1 - 1'28"31 - {ET_TIME, 0, 43, 'A', SKINCOLOR_GOLD, 3454, "", 0}, // CK Chao Circuit 2 - 1'38"68 - {ET_TIME, 0, 44, 'A', SKINCOLOR_GOLD, 2958, "", 0}, // CK Cloud Tops 2 - 1'24"51 - {ET_TIME, 0, 45, 'A', SKINCOLOR_GOLD, 3693, "", 0}, // CK Regal Raceway - 1'45"51 - {ET_TIME, 0, 46, 'A', SKINCOLOR_GOLD, 3437, "", 0}, // SD2 Balloon Panic - 1'38"20 - {ET_TIME, 0, 47, 'A', SKINCOLOR_GOLD, 3238, "", 0}, // SM Dimension Heist - 1'32"51 - {ET_TIME, 0, 48, 'A', SKINCOLOR_GOLD, 3063, "", 0}, // MKSC Sky Garden - 1'27"51 - {ET_TIME, 0, 49, 'A', SKINCOLOR_GOLD, 2980, "", 0}, // MKDS Peach Gardens - 1'25"14 - {ET_TIME, 0, 50, 'A', SKINCOLOR_GOLD, 2914, "", 0}, // MKSC Rainbow Road - 1'23"25 - {ET_TIME, 0, 51, 'A', SKINCOLOR_GOLD, 3003, "", 0}, // SMK Donut Plains 1 - 1'25"80 - {ET_TIME, 0, 52, 'A', SKINCOLOR_GOLD, 2930, "", 0}, // SMK Mario Circuit 2 - 1'23"71 - {ET_TIME, 0, 53, 'A', SKINCOLOR_GOLD, 2389, "", 0}, // SMK Ghost Valley 2 - 1'08"25 - {ET_TIME, 0, 54, 'A', SKINCOLOR_GOLD, 4292, "", 0}, // SMK Bowser Castle 3 - 2'02"62 - {ET_TIME, 0, 55, 'A', SKINCOLOR_GOLD, 2346, "", 0}, // SMK Vanilla Lake 2 - 1'07"02 - - // SILVER NORMAL MEDALS - // The general "guideline" of how good we want a player to be at a map. - {ET_TIME, 0, 1, 'B', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Green Hills Zone - 1'50"00 - {ET_TIME, 0, 2, 'B', SKINCOLOR_GREY, 160*TICRATE, "", 0}, // Dark Race - 2'40"00 - {ET_TIME, 0, 3, 'B', SKINCOLOR_GREY, 160*TICRATE, "", 0}, // Northern District Zone - 2'40"00 - {ET_TIME, 0, 4, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Darkvile Garden Zone - 2'00"00 - {ET_TIME, 0, 5, 'B', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // Daytona Speedway Zone - 2'10"00 - {ET_TIME, 0, 6, 'B', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Egg Zeppelin Zone - 2'20"00 - {ET_TIME, 0, 7, 'B', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Sonic Speedway Zone - 2'20"00 - {ET_TIME, 0, 8, 'B', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Hill Top Zone - 1'50"00 - {ET_TIME, 0, 9, 'B', SKINCOLOR_GREY, 170*TICRATE, "", 0}, // Misty Maze Zone - 2'50"00 - {ET_TIME, 0, 10, 'B', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Grand Metropolis - 2'20"00 - {ET_TIME, 0, 11, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Sunbeam Paradise Zone - 2'00"00 - {ET_TIME, 0, 12, 'B', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // Diamond Square Zone - 2'10"00 - {ET_TIME, 0, 13, 'B', SKINCOLOR_GREY, 135*TICRATE, "", 0}, // Midnight Meadow Zone - 2'15"00 - {ET_TIME, 0, 14, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Twinkle Cart - 2'00"00 - {ET_TIME, 0, 15, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Pleasure Castle - 2'00"00 - {ET_TIME, 0, 16, 'B', SKINCOLOR_GREY, 160*TICRATE, "", 0}, // Paradise Hill Zone - 2'40"00 - {ET_TIME, 0, 17, 'B', SKINCOLOR_GREY, 170*TICRATE, "", 0}, // Sub-Zero Peak Zone - 2'50"00 - {ET_TIME, 0, 18, 'B', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // Sapphire Coast Zone - 2'10"00 - {ET_TIME, 0, 19, 'B', SKINCOLOR_GREY, 170*TICRATE, "", 0}, // Sand Valley Zone - 2'50"00 - {ET_TIME, 0, 20, 'B', SKINCOLOR_GREY, 145*TICRATE, "", 0}, // Megablock Castle Zone - 2'25"00 - {ET_TIME, 0, 21, 'B', SKINCOLOR_GREY, 140*TICRATE, "", 0}, // Canyon Rush Zone - 2'20"00 - {ET_TIME, 0, 22, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Casino Resort Zone - 2'00"00 - {ET_TIME, 0, 23, 'B', SKINCOLOR_GREY, 150*TICRATE, "", 0}, // Silvercloud Island Zone - 2'30"00 - {ET_TIME, 0, 24, 'B', SKINCOLOR_GREY, 170*TICRATE, "", 0}, // Blue Mountain Zone - 2'50"00 - {ET_TIME, 0, 25, 'B', SKINCOLOR_GREY, 150*TICRATE, "", 0}, // Petroleum Refinery Zone - 2'30"00 - {ET_TIME, 0, 26, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Desert Palace Zone - 2'00"00 - {ET_TIME, 0, 27, 'B', SKINCOLOR_GREY, 160*TICRATE, "", 0}, // Aurora Atoll Zone - 2'40"00 - {ET_TIME, 0, 28, 'B', SKINCOLOR_GREY, 150*TICRATE, "", 0}, // Barren Badlands Zone - 2'30"00 - {ET_TIME, 0, 29, 'B', SKINCOLOR_GREY, 170*TICRATE, "", 0}, // Red Barrage Area - 2'50"00 - {ET_TIME, 0, 30, 'B', SKINCOLOR_GREY, 135*TICRATE, "", 0}, // Midnight Channel - 2'15"00 - {ET_TIME, 0, 31, 'B', SKINCOLOR_GREY, 160*TICRATE, "", 0}, // Vanilla Hotel Zone - 2'40"00 - {ET_TIME, 0, 32, 'B', SKINCOLOR_GREY, 160*TICRATE, "", 0}, // Toxic Palace Zone - 2'40"00 - {ET_TIME, 0, 33, 'B', SKINCOLOR_GREY, 150*TICRATE, "", 0}, // Ancient Tomb Zone - 2'30"00 - {ET_TIME, 0, 34, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Cloud Cradle Zone K - 2'00"00 - {ET_TIME, 0, 35, 'B', SKINCOLOR_GREY, 165*TICRATE, "", 0}, // Volcanic Valley Zone - 2'45"00 - {ET_TIME, 0, 36, 'B', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // Kodachrome Void Zone - 1'50"00 - {ET_TIME, 0, 37, 'B', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // Boiling Bedrock Zone - 2'10"00 - {ET_TIME, 0, 38, 'B', SKINCOLOR_GREY, 165*TICRATE, "", 0}, // Egg Quarters - 2'45"00 - {ET_TIME, 0, 39, 'B', SKINCOLOR_GREY, 145*TICRATE, "", 0}, // Virtual Highway Zone - 2'25"00 - {ET_TIME, 0, 40, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // Eggman's Nightclub Zone - 2'00"00 - {ET_TIME, 0, 41, 'B', SKINCOLOR_GREY, 100*TICRATE, "", 0}, // KKR Ganbare Dochu 2 - 1'40"00 - {ET_TIME, 0, 42, 'B', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // CK Chao Circuit 1 - 1'50"00 - {ET_TIME, 0, 43, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // CK Chao Circuit 2 - 2'00"00 - {ET_TIME, 0, 44, 'B', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // CK Cloud Tops 2 - 1'50"00 - {ET_TIME, 0, 45, 'B', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // CK Regal Raceway - 2'10"00 - {ET_TIME, 0, 46, 'B', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // SD2 Balloon Panic - 1'50"00 - {ET_TIME, 0, 47, 'B', SKINCOLOR_GREY, 130*TICRATE, "", 0}, // SM Dimension Heist - 2'10"00 - {ET_TIME, 0, 48, 'B', SKINCOLOR_GREY, 110*TICRATE, "", 0}, // MKSC Sky Garden - 1'50"00 - {ET_TIME, 0, 49, 'B', SKINCOLOR_GREY, 105*TICRATE, "", 0}, // MKDS Peach Gardens - 1'45"00 - {ET_TIME, 0, 50, 'B', SKINCOLOR_GREY, 120*TICRATE, "", 0}, // MKSC Rainbow Road - 2'00"00 - {ET_TIME, 0, 51, 'B', SKINCOLOR_GREY, 100*TICRATE, "", 0}, // SMK Donut Plains 1 - 1'40"00 - {ET_TIME, 0, 52, 'B', SKINCOLOR_GREY, 105*TICRATE, "", 0}, // SMK Mario Circuit 2 - 1'45"00 - {ET_TIME, 0, 53, 'B', SKINCOLOR_GREY, 90*TICRATE, "", 0}, // SMK Ghost Valley 2 - 1'30"00 - {ET_TIME, 0, 54, 'B', SKINCOLOR_GREY, 150*TICRATE, "", 0}, // SMK Bowser Castle 3 - 2'30"00 - {ET_TIME, 0, 55, 'B', SKINCOLOR_GREY, 90*TICRATE, "", 0} // SMK Vanilla Lake 2 - 1'30"00 -}; +emblem_t emblemlocations[MAXEMBLEMS] = {0}; // Default Extra Emblems extraemblem_t extraemblems[MAXEXTRAEMBLEMS] = @@ -177,7 +60,7 @@ unlockable_t unlockables[MAXUNLOCKABLES] = }; // Number of emblems and extra emblems -INT32 numemblems = 110; +INT32 numemblems = 0; INT32 numextraemblems = 5; // DEFAULT CONDITION SETS FOR SRB2KART: @@ -263,7 +146,10 @@ void M_ClearSecrets(void) { INT32 i; - memset(mapvisited, 0, sizeof(mapvisited)); + for (i = 0; i < nummapheaders; ++i) + { + mapheaderinfo[i]->mapvisited = 0; + } for (i = 0; i < MAXEMBLEMS; ++i) emblemlocations[i].collected = false; @@ -298,11 +184,19 @@ UINT8 M_CheckCondition(condition_t *cn) case UC_OVERALLTIME: // Requires overall time <= x return (M_GotLowEnoughTime(cn->requirement)); case UC_MAPVISITED: // Requires map x to be visited - return ((mapvisited[cn->requirement - 1] & MV_VISITED) == MV_VISITED); case UC_MAPBEATEN: // Requires map x to be beaten - return ((mapvisited[cn->requirement - 1] & MV_BEATEN) == MV_BEATEN); case UC_MAPENCORE: // Requires map x to be beaten in encore - return ((mapvisited[cn->requirement - 1] & MV_ENCORE) == MV_ENCORE); + { + UINT8 mvtype = MV_VISITED; + if (cn->type == UC_MAPBEATEN) + mvtype = MV_BEATEN; + else if (cn->type == UC_MAPENCORE) + mvtype = MV_ENCORE; + + return ((cn->requirement < nummapheaders) + && (mapheaderinfo[cn->requirement]) + && ((mapheaderinfo[cn->requirement]->mapvisited & mvtype) == mvtype)); + } case UC_MAPTIME: // Requires time on map <= x return (G_GetBestTime(cn->extrainfo1) <= (unsigned)cn->requirement); case UC_TRIGGER: // requires map trigger set @@ -467,10 +361,17 @@ UINT8 M_CheckLevelEmblems(void) // Update Score, Time, Rings emblems for (i = 0; i < numemblems; ++i) { + INT32 checkLevel; + if (emblemlocations[i].type < ET_TIME || emblemlocations[i].collected) continue; - levelnum = emblemlocations[i].level; + checkLevel = G_MapNumber(emblemlocations[i].level); + + if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel]) + continue; + + levelnum = checkLevel; valToReach = emblemlocations[i].var; switch (emblemlocations[i].type) @@ -500,17 +401,24 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa for (i = 0; i < numemblems; ++i) { - if (emblemlocations[i].type != ET_MAP || emblemlocations[i].collected) + INT32 checkLevel; + + if (emblemlocations[i].type < ET_TIME || emblemlocations[i].collected) continue; - levelnum = emblemlocations[i].level; + checkLevel = G_MapNumber(emblemlocations[i].level); + + if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel]) + continue; + + levelnum = checkLevel; embtype = emblemlocations[i].var; flags = MV_BEATEN; if (embtype & ME_ENCORE) flags |= MV_ENCORE; - res = ((mapvisited[levelnum - 1] & flags) == flags); + res = ((mapheaderinfo[levelnum]->mapvisited & flags) == flags); emblemlocations[i].collected = res; if (res) @@ -622,14 +530,14 @@ UINT8 M_GotLowEnoughTime(INT32 tictime) INT32 curtics = 0; INT32 i; - for (i = 0; i < NUMMAPS; ++i) + for (i = 0; i < nummapheaders; ++i) { if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK)) continue; - if (!mainrecords[i] || !mainrecords[i]->time) + if (!mapheaderinfo[i]->mainrecord || !mapheaderinfo[i]->mainrecord->time) return false; - else if ((curtics += mainrecords[i]->time) > tictime) + else if ((curtics += mapheaderinfo[i]->mainrecord->time) > tictime) return false; } return true; @@ -648,7 +556,7 @@ emblem_t *M_GetLevelEmblems(INT32 mapnum) static INT32 map = -1; static INT32 i = -1; - if (mapnum > 0) + if (mapnum >= 0) { map = mapnum; i = numemblems; @@ -656,7 +564,12 @@ emblem_t *M_GetLevelEmblems(INT32 mapnum) while (--i >= 0) { - if (emblemlocations[i].level == map) + INT32 checkLevel = G_MapNumber(emblemlocations[i].level); + + if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel]) + continue; + + if (checkLevel == map) return &emblemlocations[i]; } return NULL; diff --git a/src/m_cond.h b/src/m_cond.h index c23d8ff72..dfa34dc8d 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -80,7 +80,7 @@ struct emblem_t { UINT8 type; ///< Emblem type INT16 tag; ///< Tag of emblem mapthing - INT16 level; ///< Level on which this emblem can be found. + char * level; ///< Level on which this emblem can be found. UINT8 sprite; ///< emblem sprite to use, 0 - 25 UINT16 color; ///< skincolor to use INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin) diff --git a/src/m_menu.c b/src/m_menu.c index 720a0fdb9..dbdf900fa 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -424,12 +424,14 @@ static void Dummystaff_OnChange(void); consvar_t cv_showfocuslost = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL); static CV_PossibleValue_t map_cons_t[] = { - {0,"MIN"}, - {NUMMAPS, "MAX"}, + {-1,"MIN"}, + {NEXTMAP_SPECIAL, "MAX"}, // TODO: kill nextmap (can't do that i'm afraid!) {0, NULL} }; consvar_t cv_nextmap = CVAR_INIT ("nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange); +static INT16 lastnextmap = 1; + static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange); @@ -1783,6 +1785,23 @@ INT32 HU_GetHighlightColor(void) return highlightflags; } +fixed_t M_GetMapThumbnail(INT16 mapnum, patch_t **out) +{ + patch_t *patch = NULL; + if (mapnum == -1) + patch = randomlvl; + else if (mapnum >= 0 && mapnum < nummapheaders && mapheaderinfo[mapnum]) + patch = mapheaderinfo[mapnum]->thumbnailPic; + + if (!patch) + patch = blanklvl; + + *out = patch; + + // check width instead of height because haha big winton + return patch->width >= 320 ? FRACUNIT : FRACUNIT*2; +} + // Sky Room menu_t SR_PandoraDef = { @@ -2110,26 +2129,45 @@ static INT32 M_GetFirstLevelInList(void); void Nextmap_OnChange(void) { char *leveltitle; - UINT8 active; - const char *gamemode = (levellistmode == LLM_ITEMBREAKER) ? "IB" : "RA"; + + // welp, we're stuck with nextmap for the time being. so just make the damn thing work + if (cv_nextmap.value != lastnextmap) + { + boolean increment = cv_nextmap.value > lastnextmap; + INT16 oldvalue = cv_nextmap.value - 1; + INT16 newvalue = oldvalue; + INT32 gt = cv_newgametype.value; + while (!M_CanShowLevelInList(newvalue, gt)) + { + if (increment) // Going up! + { + if (++newvalue == nummapheaders) + newvalue = -1; + } + else // Going down! + { + if (--newvalue == -2) + newvalue = nummapheaders-1; + } + + if (newvalue == oldvalue) + break; // don't loop forever if there's none of a certain gametype + } + cv_nextmap.value = lastnextmap = newvalue + 1; + } // Update the string in the consvar. Z_Free(cv_nextmap.zstring); - leveltitle = G_BuildMapTitle(cv_nextmap.value); - cv_nextmap.string = cv_nextmap.zstring = leveltitle ? leveltitle : Z_StrDup(G_BuildMapName(cv_nextmap.value)); - + leveltitle = cv_nextmap.value ? G_BuildMapTitle(cv_nextmap.value) : Z_StrDup("Random"); + cv_nextmap.string = cv_nextmap.zstring = leveltitle; if (currentMenu == &SP_TimeAttackDef) { // see also p_setup.c's P_LoadRecordGhosts - const size_t glen = strlen(srb2home)+1+strlen("media")+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; - char *gpath = malloc(glen); + const char *gamemode = (levellistmode == LLM_ITEMBREAKER) ? "IB" : "RA"; + char *gpath = xva("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); INT32 i; - - if (!gpath) - return; - - sprintf(gpath,"%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(cv_nextmap.value)); + UINT8 active = 0; CV_StealthSetValue(&cv_dummystaff, 0); @@ -2198,9 +2236,6 @@ void Nextmap_OnChange(void) itemOn = tastart; } - if (mapheaderinfo[cv_nextmap.value-1] && mapheaderinfo[cv_nextmap.value-1]->forcecharacter[0] != '\0') - CV_Set(&cv_chooseskin, mapheaderinfo[cv_nextmap.value-1]->forcecharacter); - free(gpath); } } @@ -2239,7 +2274,8 @@ static void Dummystaff_OnChange(void) dummystaffname[0] = '\0'; - if ((l = W_CheckNumForName(va("%sS01",G_BuildMapName(cv_nextmap.value)))) == LUMPERROR) + // TODO: Use map header to determine lump name + if ((l = W_CheckNumForLongName(va("%sS01",G_BuildMapName(cv_nextmap.value)))) == LUMPERROR) { CV_StealthSetValue(&cv_dummystaff, 0); return; @@ -2248,7 +2284,7 @@ static void Dummystaff_OnChange(void) { char *temp = dummystaffname; UINT8 numstaff = 1; - while (numstaff < 99 && (l = W_CheckNumForName(va("%sS%02u",G_BuildMapName(cv_nextmap.value),numstaff+1))) != LUMPERROR) + while (numstaff < 99 && (l = W_CheckNumForLongName(va("%sS%02u",G_BuildMapName(cv_nextmap.value),numstaff+1))) != LUMPERROR) numstaff++; if (cv_dummystaff.value < 1) @@ -2256,7 +2292,7 @@ static void Dummystaff_OnChange(void) else if (cv_dummystaff.value > numstaff) CV_StealthSetValue(&cv_dummystaff, 1); - if ((l = W_CheckNumForName(va("%sS%02u",G_BuildMapName(cv_nextmap.value), cv_dummystaff.value))) == LUMPERROR) + if ((l = W_CheckNumForLongName(va("%sS%02u",G_BuildMapName(cv_nextmap.value), cv_dummystaff.value))) == LUMPERROR) return; // shouldn't happen but might as well check... G_UpdateStaffGhostName(l); @@ -4458,18 +4494,24 @@ static void M_PrepareLevelSelect(void) // boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) { + UINT32 tolflag = G_TOLFlag(gt); + // Random map! if (mapnum == -1) return (levellistmode == LLM_CREATESERVER); // Does the map exist? - if (!mapheaderinfo[mapnum]) + if (mapnum < 0 || mapnum >= nummapheaders || !mapheaderinfo[mapnum]) return false; // Does the map have a name? if (!mapheaderinfo[mapnum]->lvlttl[0]) return false; + // Does the map have a LUMP? + if (mapheaderinfo[mapnum]->lumpnum == LUMPERROR) + return false; + switch (levellistmode) { case LLM_CREATESERVER: @@ -4480,10 +4522,11 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) if (M_MapLocked(mapnum+1)) return false; // not unlocked - if (gt >= 0 && gt < gametypecount && mapheaderinfo[mapnum]->typeoflevel & gametypetol[gt]) - return true; + // Check for TOL + if (!(mapheaderinfo[mapnum]->typeoflevel & tolflag)) + return false; - return false; + return true; /*case LLM_LEVELSELECT: if (mapheaderinfo[mapnum]->levelselect != maplistoption) @@ -4511,7 +4554,7 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) return false; // map hell - if ((mapheaderinfo[mapnum]->menuflags & LF2_VISITNEEDED) && !mapvisited[mapnum]) + if ((mapheaderinfo[mapnum]->menuflags & LF2_VISITNEEDED) && !mapheaderinfo[mapnum]->mapvisited) return false; return true; @@ -4534,7 +4577,7 @@ static INT32 M_CountLevelsToShowInList(void) { INT32 mapnum, count = 0; - for (mapnum = 0; mapnum < NUMMAPS; mapnum++) + for (mapnum = 0; mapnum < nummapheaders; mapnum++) if (M_CanShowLevelInList(mapnum, -1)) count++; @@ -4545,7 +4588,7 @@ static INT32 M_GetFirstLevelInList(void) { INT32 mapnum; - for (mapnum = 0; mapnum < NUMMAPS; mapnum++) + for (mapnum = 0; mapnum < nummapheaders; mapnum++) if (M_CanShowLevelInList(mapnum, -1)) return mapnum + 1; @@ -5563,7 +5606,6 @@ static void M_HandleReplayHutList(INT32 choice) #define SCALEDVIEWHEIGHT (vid.height/vid.dupy) static void DrawReplayHutReplayInfo(void) { - lumpnum_t lumpnum; patch_t *patch; UINT8 *colormap; INT32 x, y, w, h; @@ -5588,21 +5630,18 @@ static void DrawReplayHutReplayInfo(void) // Draw level stuff x = 15; y = 15; - // A 160x100 image of the level as entry MAPxxP //CONS_Printf("%d %s\n", demolist[dir_on[menudepthleft]].map, G_BuildMapName(demolist[dir_on[menudepthleft]].map)); - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(demolist[dir_on[menudepthleft]].map))); - if (lumpnum != LUMPERROR) - patch = W_CachePatchNum(lumpnum, PU_CACHE); - else - patch = W_CachePatchName("M_NOLVL", PU_CACHE); + fixed_t scale = M_GetMapThumbnail(demolist[dir_on[menudepthleft]].map, &patch)/4; + if (patch == blanklvl) + patch = nolvl; if (!(demolist[dir_on[menudepthleft]].kartspeed & DF_ENCORE)) - V_DrawSmallScaledPatch(x, y, V_SNAPTOTOP, patch); + V_DrawFixedPatch(x<width); - h = SHORT(patch->height); - V_DrawSmallScaledPatch(x+(w>>1), y, V_SNAPTOTOP|V_FLIP, patch); + w = FixedMul(SHORT(patch->width), scale); + h = FixedMul(SHORT(patch->height), scale); + V_DrawFixedPatch((x+(w>>1))<level != gamemap || emblem->type != ET_GLOBAL) + if (emblem->type != ET_GLOBAL) + continue; + + checkLevel = G_MapNumber(emblem->level); + + if (!mapheaderinfo[checkLevel] || gamemap != checkLevel) continue; if (emblem->collected) @@ -7544,7 +7585,8 @@ static void M_ChoosePlayer(INT32 choice) static INT32 statsLocation; static INT32 statsMax; -static INT16 statsMapList[NUMMAPS+1]; +static INT16 *statsMapList = NULL; +static INT16 statsMapListLen; static void M_Statistics(INT32 choice) { @@ -7552,9 +7594,13 @@ static void M_Statistics(INT32 choice) (void)choice; - memset(statsMapList, 0, sizeof(statsMapList)); + if (!statsMapList || nummapheaders != statsMapListLen) + { + statsMapList = Z_Realloc(statsMapList, nummapheaders * sizeof(*statsMapList), PU_LEVEL, NULL); + statsMapListLen = nummapheaders; + } - for (i = 0; i < NUMMAPS; i++) + for (i = 0; i < nummapheaders; i++) { if (!mapheaderinfo[i] || mapheaderinfo[i]->lvlttl[0] == '\0') continue; @@ -7696,18 +7742,18 @@ static void M_DrawLevelStats(void) V_DrawRightAlignedString(BASEVIDWIDTH-16, 52, 0, va("Race: %i", vspowerlevel[PWRLV_RACE])); V_DrawRightAlignedString(BASEVIDWIDTH-16, 60, 0, va("Battle: %i", vspowerlevel[PWRLV_BATTLE])); - for (i = 0; i < NUMMAPS; i++) + for (i = 0; i < nummapheaders; i++) { if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK)) continue; - if (!mainrecords[i] || mainrecords[i]->time <= 0) + if (!mapheaderinfo[i]->mainrecord || mapheaderinfo[i]->mainrecord->time <= 0) { mapsunfinished++; continue; } - besttime += mainrecords[i]->time; + besttime += mapheaderinfo[i]->mainrecord->time; } V_DrawString(20, 70, highlightflags, "Combined time records:"); @@ -7784,6 +7830,7 @@ static void M_GrandPrixTemp(INT32 choice) static void M_StartGrandPrix(INT32 choice) { cupheader_t *gpcup = kartcupheaders; + INT32 levelNum; (void)choice; @@ -7835,9 +7882,11 @@ static void M_StartGrandPrix(INT32 choice) grandprixinfo.initalize = true; + levelNum = G_MapNumber(grandprixinfo.cup->levellist[0]); + G_DeferedInitNew( false, - G_BuildMapName(grandprixinfo.cup->levellist[0] + 1), + levelNum + 1, (UINT8)(cv_chooseskin.value - 1), (UINT8)(cv_splitplayers.value - 1), false @@ -7943,10 +7992,10 @@ void M_DrawTimeAttackMenu(void) { INT32 dupadjust = (vid.width/vid.dupx); tic_t lap = 0, time = 0; - if (mainrecords[cv_nextmap.value-1]) + if (mapheaderinfo[cv_nextmap.value-1]->mainrecord) { - lap = mainrecords[cv_nextmap.value-1]->lap; - time = mainrecords[cv_nextmap.value-1]->time; + lap = mapheaderinfo[cv_nextmap.value-1]->mainrecord->lap; + time = mapheaderinfo[cv_nextmap.value-1]->mainrecord->time; } V_DrawFill((BASEVIDWIDTH - dupadjust)>>1, 78, dupadjust, 36, 159); @@ -7954,11 +8003,11 @@ void M_DrawTimeAttackMenu(void) if (levellistmode != LLM_ITEMBREAKER) { V_DrawRightAlignedString(149, 80, highlightflags, "BEST LAP:"); - K_drawKartTimestamp(lap, 19, 86, 0, 2); + K_drawKartTimestamp(lap, 19, 86, -1, 2); } V_DrawRightAlignedString(292, 80, highlightflags, "BEST TIME:"); - K_drawKartTimestamp(time, 162, 86, cv_nextmap.value, 1); + K_drawKartTimestamp(time, 162, 86, cv_nextmap.value-1, 1); } /*{ char beststr[40]; @@ -8134,7 +8183,7 @@ static void M_ChooseTimeAttack(INT32 choice) else G_RecordDemo(nameofdemo); - G_DeferedInitNew(false, G_BuildMapName(cv_nextmap.value), (UINT8)(cv_chooseskin.value-1), 0, false); + G_DeferedInitNew(false, cv_nextmap.value, (UINT8)(cv_chooseskin.value-1), 0, false); } static void M_HandleStaffReplay(INT32 choice) @@ -8877,7 +8926,7 @@ static INT32 M_FindFirstMap(INT32 gtype) if (mapheaderinfo[gamemap] && (mapheaderinfo[gamemap]->typeoflevel & gametypetol[gtype])) return gamemap; - for (i = 0; i < NUMMAPS; i++) + for (i = 0; i < nummapheaders; i++) { if (!mapheaderinfo[i]) continue; @@ -8942,24 +8991,12 @@ static void M_StartServer(INT32 choice) static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade) { - lumpnum_t lumpnum; patch_t *PictureOfLevel; INT32 x, y, w, i, oldval, trans, dupadjust = ((vid.width/vid.dupx) - BASEVIDWIDTH)>>1; + fixed_t scale = M_GetMapThumbnail(cv_nextmap.value-1, &PictureOfLevel)/4; - // A 160x100 image of the level as entry MAPxxP - if (cv_nextmap.value) - { - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value))); - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); - } - else - PictureOfLevel = W_CachePatchName("RANDOMLV", PU_CACHE); - - w = SHORT(PictureOfLevel->width)/2; - i = SHORT(PictureOfLevel->height)/2; + w = FixedMul(SHORT(PictureOfLevel->width), scale); + i = FixedMul(SHORT(PictureOfLevel->height), scale); x = BASEVIDWIDTH/2 - w/2; y = currentMenu->y + 130 + 8 - i; @@ -8971,14 +9008,14 @@ static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade) V_DrawFill(x-1, y-1, w+2, i+2, trans); // variable reuse... if ((cv_kartencore.value != 1) || gamestate == GS_TIMEATTACK || cv_newgametype.value != GT_RACE) - V_DrawSmallScaledPatch(x, y, 0, PictureOfLevel); + V_DrawFixedPatch(x< horizspac-dupadjust); x = (BASEVIDWIDTH + w)/2 + horizspac; @@ -9039,7 +9066,7 @@ static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade) do { i++; - if (i == NUMMAPS) + if (i == nummapheaders) i = -1; if (i == oldval) @@ -9050,19 +9077,9 @@ static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade) } while (!M_CanShowLevelInList(i, cv_newgametype.value)); - // A 160x100 image of the level as entry MAPxxP - if (i+1) - { - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(i+1))); - if (lumpnum != LUMPERROR) - PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE); - else - PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE); - } - else - PictureOfLevel = W_CachePatchName("RANDOMLV", PU_CACHE); + scale = M_GetMapThumbnail(i, &PictureOfLevel)/8; - V_DrawTinyScaledPatch(x, y, trans, PictureOfLevel); + V_DrawFixedPatch(x<(malloc((unsigned)size+1)); + if (!string) + I_Error("xva: out of memory"); + va_end(argptr); + + va_start(argptr, format); + vsprintf(string, format, argptr); + va_end(argptr); + + return string; +} + /** Creates a string in the first argument that is the second argument followed * by the third argument followed by the first argument. * Useful for making filenames with full path. s1 = s2+s3+s1 diff --git a/src/m_misc.h b/src/m_misc.h index f3a464fc7..a11a384c6 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -46,8 +46,6 @@ void M_StopMovie(void); // the file where game vars and settings are saved #define CONFIGFILENAME "kartconfig.cfg" -INT32 M_MapNumber(char first, char second); - boolean FIL_WriteFile(char const *name, const void *source, size_t length); size_t FIL_ReadFileTag(char const *name, UINT8 **buffer, INT32 tag); #define FIL_ReadFile(n, b) FIL_ReadFileTag(n, b, PU_STATIC) diff --git a/src/p_mobj.c b/src/p_mobj.c index e9fa2cd3b..15333e037 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -10512,7 +10512,7 @@ void P_SpawnPlayer(INT32 playernum) else if (p->bot) { /* - if (bonusgame || specialstage) + if (bonusgame || specialstage || boss) { // Bots should avoid p->spectator = true; diff --git a/src/p_saveg.c b/src/p_saveg.c index 377c62c4a..a1909aa6f 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4852,8 +4852,8 @@ static inline void P_UnArchiveSPGame(savebuffer_t *save, INT16 mapoverride) // gamemap changed; we assume that its map header is always valid, // so make it so - if(!mapheaderinfo[gamemap-1]) - P_AllocMapHeader(gamemap-1); + if (!gamemap || gamemap > nummapheaders || !mapheaderinfo[gamemap-1]) + I_Error("P_UnArchiveSPGame: Internal map ID %d not found (nummapheaders = %d)", gamemap-1, nummapheaders); //lastmapsaved = gamemap; lastmaploaded = gamemap; @@ -4908,7 +4908,6 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) WRITEUINT8(save->p, mapmusrng); WRITEUINT32(save->p, leveltime); - WRITEUINT32(save->p, ssspheres); WRITEINT16(save->p, lastmap); WRITEUINT16(save->p, bossdisabled); WRITEUINT8(save->p, ringsdisabled); @@ -4935,7 +4934,6 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) } WRITEUINT32(save->p, token); - WRITEINT32(save->p, sstimer); WRITEUINT32(save->p, bluescore); WRITEUINT32(save->p, redscore); @@ -4968,9 +4966,6 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) WRITEFIXED(save->p, gravity); WRITEFIXED(save->p, mapobjectscale); - WRITEUINT32(save->p, countdowntimer); - WRITEUINT8(save->p, countdowntimeup); - // SRB2kart WRITEINT32(save->p, numgotboxes); WRITEUINT8(save->p, numtargets); @@ -5041,8 +5036,8 @@ FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(savebuffer_t *save, bool // gamemap changed; we assume that its map header is always valid, // so make it so - if(!mapheaderinfo[gamemap-1]) - P_AllocMapHeader(gamemap-1); + if (!gamemap || gamemap > nummapheaders || !mapheaderinfo[gamemap-1]) + I_Error("P_NetUnArchiveMisc: Internal map ID %d not found (nummapheaders = %d)", gamemap-1, nummapheaders); // tell the sound code to reset the music since we're skipping what // normally sets this flag @@ -5078,7 +5073,6 @@ FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(savebuffer_t *save, bool // get the time leveltime = READUINT32(save->p); - ssspheres = READUINT32(save->p); lastmap = READINT16(save->p); bossdisabled = READUINT16(save->p); ringsdisabled = READUINT8(save->p); @@ -5102,7 +5096,6 @@ FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(savebuffer_t *save, bool } token = READUINT32(save->p); - sstimer = READINT32(save->p); bluescore = READUINT32(save->p); redscore = READUINT32(save->p); @@ -5135,9 +5128,6 @@ FUNCINLINE static ATTRINLINE boolean P_NetUnArchiveMisc(savebuffer_t *save, bool gravity = READFIXED(save->p); mapobjectscale = READFIXED(save->p); - countdowntimer = (tic_t)READUINT32(save->p); - countdowntimeup = (boolean)READUINT8(save->p); - // SRB2kart numgotboxes = READINT32(save->p); numtargets = READUINT8(save->p); @@ -5314,7 +5304,7 @@ boolean P_LoadGame(savebuffer_t *save, INT16 mapoverride) return false; // Only do this after confirming savegame is ok - G_DeferedInitNew(false, G_BuildMapName(gamemap), savedata.skin, 0, true); + G_DeferedInitNew(false, gamemap, savedata.skin, 0, true); COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this return true; @@ -5352,3 +5342,93 @@ boolean P_LoadNetGame(savebuffer_t *save, boolean reloading) return P_UnArchiveLuabanksAndConsistency(save); } + +boolean P_SaveBufferZAlloc(savebuffer_t *save, size_t alloc_size, INT32 tag, void *user) +{ + I_Assert(save->buffer == NULL); + save->buffer = (UINT8 *)Z_Malloc(alloc_size, tag, user); + + if (save->buffer == NULL) + { + return false; + } + + save->size = alloc_size; + save->p = save->buffer; + save->end = save->buffer + save->size; + + return true; +} + +boolean P_SaveBufferFromExisting(savebuffer_t *save, UINT8 *existing_buffer, size_t existing_size) +{ + I_Assert(save->buffer == NULL); + + if (existing_buffer == NULL || existing_size == 0) + { + return false; + } + + save->buffer = existing_buffer; + save->size = existing_size; + + save->p = save->buffer; + save->end = save->buffer + save->size; + + return true; +} + +boolean P_SaveBufferFromLump(savebuffer_t *save, lumpnum_t lump) +{ + I_Assert(save->buffer == NULL); + + if (lump == LUMPERROR) + { + return false; + } + + save->buffer = (UINT8 *)W_CacheLumpNum(lump, PU_STATIC); + + if (save->buffer == NULL) + { + return false; + } + + save->size = W_LumpLength(lump); + + save->p = save->buffer; + save->end = save->buffer + save->size; + + return true; +} + +size_t P_SaveBufferFromFile(savebuffer_t *save, char const *name) +{ + size_t len = 0; + + I_Assert(save->buffer == NULL); + len = FIL_ReadFile(name, &save->buffer); + + if (len != 0) + { + save->size = len; + + save->p = save->buffer; + save->end = save->buffer + save->size; + } + + return len; +} + +static void P_SaveBufferInvalidate(savebuffer_t *save) +{ + save->buffer = save->p = save->end = NULL; + save->size = 0; +} + +void P_SaveBufferFree(savebuffer_t *save) +{ + I_Assert(save->buffer != NULL); + Z_Free(save->buffer); + P_SaveBufferInvalidate(save); +} diff --git a/src/p_saveg.h b/src/p_saveg.h index 0da3b065d..a85916f69 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -53,6 +53,13 @@ struct savebuffer_t size_t size; }; +boolean P_SaveBufferZAlloc(savebuffer_t *save, size_t alloc_size, INT32 tag, void *user); +#define P_SaveBufferAlloc(a,b) P_SaveBufferZAlloc(a, b, PU_STATIC, NULL) +boolean P_SaveBufferFromExisting(savebuffer_t *save, UINT8 *existing_buffer, size_t existing_size); +boolean P_SaveBufferFromLump(savebuffer_t *save, lumpnum_t lump); +size_t P_SaveBufferFromFile(savebuffer_t *save, char const *name); +void P_SaveBufferFree(savebuffer_t *save); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/p_setup.c b/src/p_setup.c index 223aaa9f7..1d3a44ac4 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -334,11 +334,19 @@ boolean P_IsDegeneratedTubeWaypointSequence(UINT8 sequence) FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg) { // don't use va() because the calling function probably uses it - char mapnum[10]; + char mapname[MAXMAPLUMPNAME]; + + if (gamemap > 0 && gamemap <= nummapheaders && mapheaderinfo[gamemap-1]) + { + sprintf(mapname, "%s", mapheaderinfo[gamemap-1]->lumpname); + } + else + { + sprintf(mapname, "ID %d", gamemap-1); + } - sprintf(mapnum, "%hd", gamemap); CON_LogMessage("Map "); - CON_LogMessage(mapnum); + CON_LogMessage(mapname); CON_LogMessage(" is corrupt: "); CON_LogMessage(msg); CON_LogMessage("\n"); @@ -387,40 +395,20 @@ static void P_ClearMapHeaderLighting(mapheader_lighting_t *lighting) * \param i Map number to clear header for. * \sa P_ClearMapHeaderInfo */ -static void P_ClearSingleMapHeaderInfo(INT16 i) +static void P_ClearSingleMapHeaderInfo(INT16 num) { - const INT16 num = (INT16)(i-1); - boolean exists = (mapheaderinfo[gamemap-1]->alreadyExists == true); - mapheaderinfo[num]->lvlttl[0] = '\0'; - mapheaderinfo[num]->selectheading[0] = '\0'; mapheaderinfo[num]->subttl[0] = '\0'; mapheaderinfo[num]->zonttl[0] = '\0'; - mapheaderinfo[num]->ltzzpatch[0] = '\0'; - mapheaderinfo[num]->ltzztext[0] = '\0'; - mapheaderinfo[num]->ltactdiamond[0] = '\0'; mapheaderinfo[num]->actnum[0] = '\0'; mapheaderinfo[num]->typeoflevel = 0; - mapheaderinfo[num]->nextlevel = (INT16)(i + 1); - mapheaderinfo[num]->marathonnext = 0; - mapheaderinfo[num]->startrings = 0; - mapheaderinfo[num]->sstimer = 90; - mapheaderinfo[num]->ssspheres = 1; mapheaderinfo[num]->gravity = DEFAULT_GRAVITY; mapheaderinfo[num]->use_walltransfer = false; mapheaderinfo[num]->keywords[0] = '\0'; - for (i = 0; i < MAXMUSNAMES; i++) + for (int i = 0; i < MAXMUSNAMES; i++) mapheaderinfo[num]->musname[i][0] = 0; mapheaderinfo[num]->mustrack = 0; mapheaderinfo[num]->muspos = 0; - mapheaderinfo[num]->musinterfadeout = 0; - mapheaderinfo[num]->musintername[0] = 0; - mapheaderinfo[num]->muspostbossname[0] = 0; - mapheaderinfo[num]->muspostbosstrack = 0; - mapheaderinfo[num]->muspostbosspos = 0; - mapheaderinfo[num]->muspostbossfadein = 0; - mapheaderinfo[num]->musforcereset = -1; - mapheaderinfo[num]->forcecharacter[0] = '\0'; mapheaderinfo[num]->musname_size = 0; mapheaderinfo[num]->weather = PRECIP_NONE; snprintf(mapheaderinfo[num]->skytexture, 5, "SKY1"); @@ -428,19 +416,15 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->skybox_scalex = 16; mapheaderinfo[num]->skybox_scaley = 16; mapheaderinfo[num]->skybox_scalez = 16; - mapheaderinfo[num]->interscreen[0] = '#'; mapheaderinfo[num]->runsoc[0] = '#'; mapheaderinfo[num]->scriptname[0] = '#'; mapheaderinfo[num]->precutscenenum = 0; mapheaderinfo[num]->cutscenenum = 0; - mapheaderinfo[num]->countdown = 0; mapheaderinfo[num]->palette = UINT16_MAX; mapheaderinfo[num]->encorepal = UINT16_MAX; mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT; mapheaderinfo[num]->unlockrequired = -1; mapheaderinfo[num]->levelselect = 0; - mapheaderinfo[num]->bonustype = 0; - mapheaderinfo[num]->maxbonuslives = -1; mapheaderinfo[num]->levelflags = 0; mapheaderinfo[num]->menuflags = 0; mapheaderinfo[num]->mobj_scale = FRACUNIT; @@ -453,10 +437,10 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) #else // equivalent to "FlickyList = NONE" P_DeleteFlickies(num); #endif - P_DeleteGrades(num); - // see p_setup.c - prevents replacing maps in addons with different versions - mapheaderinfo[num]->alreadyExists = exists; + mapheaderinfo[num]->mapvisited = 0; + Z_Free(mapheaderinfo[num]->mainrecord); + mapheaderinfo[num]->mainrecord = NULL; mapheaderinfo[num]->customopts = NULL; mapheaderinfo[num]->numCustomOptions = 0; @@ -468,110 +452,53 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) */ void P_AllocMapHeader(INT16 i) { - if (!mapheaderinfo[i]) + if (i > nummapheaders) + I_Error("P_AllocMapHeader: Called on %d, should be %d", i, nummapheaders); + + if (i >= NEXTMAP_SPECIAL) { - mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL); - mapheaderinfo[i]->flickies = NULL; - mapheaderinfo[i]->grades = NULL; - } - P_ClearSingleMapHeaderInfo(i + 1); -} - -/** NiGHTS Grades are a special structure, - * we initialize them here. - * - * \param i Index of header to allocate grades for - * \param mare The mare we're adding grades for - * \param grades the string from DeHackEd, we work with it ourselves - */ -void P_AddGradesForMare(INT16 i, UINT8 mare, char *gtext) -{ - INT32 g; - char *spos = gtext; - - CONS_Debug(DBG_SETUP, "Map %d Mare %d: ", i+1, (UINT16)mare+1); - - if (mapheaderinfo[i]->numGradedMares < mare+1) - { - mapheaderinfo[i]->numGradedMares = mare+1; - mapheaderinfo[i]->grades = Z_Realloc(mapheaderinfo[i]->grades, sizeof(nightsgrades_t) * mapheaderinfo[i]->numGradedMares, PU_STATIC, NULL); + I_Error("P_AllocMapHeader: Too many maps!"); } - for (g = 0; g < 6; ++g) + if (i >= mapallocsize) { - // Allow "partial" grading systems - if (spos != NULL) + if (!mapallocsize) { - mapheaderinfo[i]->grades[mare].grade[g] = atoi(spos); - CONS_Debug(DBG_SETUP, "%u ", atoi(spos)); - // Grab next comma - spos = strchr(spos, ','); - if (spos) - ++spos; + mapallocsize = 16; } else { - // Grade not reachable - mapheaderinfo[i]->grades[mare].grade[g] = UINT32_MAX; + mapallocsize *= 2; } + + mapheaderinfo = Z_ReallocAlign( + (void*) mapheaderinfo, + sizeof(mapheader_t*) * mapallocsize, + PU_STATIC, + NULL, + sizeof(mapheader_t*) * 8 + ); + + if (!mapheaderinfo) + I_Error("P_AllocMapHeader: Not enough memory to realloc mapheaderinfo (size %d)", mapallocsize); } - CONS_Debug(DBG_SETUP, "\n"); -} - -/** And this removes the grades safely. - * - * \param i The header to remove grades from - */ -void P_DeleteGrades(INT16 i) -{ - if (mapheaderinfo[i]->grades) - Z_Free(mapheaderinfo[i]->grades); - - mapheaderinfo[i]->grades = NULL; - mapheaderinfo[i]->numGradedMares = 0; -} - -/** And this fetches the grades - * - * \param pscore The player's score. - * \param map The game map. - * \param mare The mare to test. - */ -UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare) -{ - INT32 i; - - // Determining the grade - if (mapheaderinfo[map-1] && mapheaderinfo[map-1]->grades && mapheaderinfo[map-1]->numGradedMares >= mare + 1) + if (!mapheaderinfo[i]) { - INT32 pgrade = 0; - for (i = 0; i < 6; ++i) - { - if (pscore >= mapheaderinfo[map-1]->grades[mare].grade[i]) - ++pgrade; - } - return (UINT8)pgrade; + mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL); + if (!mapheaderinfo[i]) + I_Error("P_AllocMapHeader: Not enough memory to allocate new mapheader (ID %d)", i); + + mapheaderinfo[i]->lumpnum = LUMPERROR; + mapheaderinfo[i]->lumpname = NULL; + mapheaderinfo[i]->thumbnailPic = NULL; + mapheaderinfo[i]->minimapPic = NULL; + mapheaderinfo[i]->cup = NULL; + mapheaderinfo[i]->mainrecord = NULL; + mapheaderinfo[i]->flickies = NULL; + nummapheaders++; } - return 0; -} - -UINT8 P_HasGrades(INT16 map, UINT8 mare) -{ - // Determining the grade - // Mare 0 is treated as overall and is true if ANY grades exist - if (mapheaderinfo[map-1] && mapheaderinfo[map-1]->grades - && (mare == 0 || mapheaderinfo[map-1]->numGradedMares >= mare)) - return true; - return false; -} - -UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade) -{ - // Get the score for the grade... if it exists - if (grade == GRADE_F || grade > GRADE_S || !P_HasGrades(map, mare)) return 0; - - return mapheaderinfo[map-1]->grades[mare].grade[grade-1]; + P_ClearSingleMapHeaderInfo(i); } // @@ -808,65 +735,6 @@ void P_ReloadRings(void) } } -#ifdef SCANTHINGS -void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum) -{ - size_t i, n; - UINT8 *data, *datastart; - UINT16 type, maprings; - INT16 tol; - UINT32 flags; - - tol = mapheaderinfo[mapnum-1]->typeoflevel; - flags = mapheaderinfo[mapnum-1]->levelflags; - - n = W_LumpLengthPwad(wadnum, lumpnum) / (5 * sizeof (INT16)); - //CONS_Printf("%u map things found!\n", n); - - maprings = 0; - data = datastart = W_CacheLumpNumPwad(wadnum, lumpnum, PU_STATIC); - for (i = 0; i < n; i++) - { - data += 3 * sizeof (INT16); // skip x y position, angle - type = READUINT16(data) & 4095; - data += sizeof (INT16); // skip options - - if (mt->type == mobjinfo[MT_RANDOMITEM].doomednum) - { - nummapboxes++; - } - else if (mt->type == mobjinfo[MT_RING].doomednum) - { - maprings++; - } - else - { - switch (type) - { - case 603: // 10 diagonal rings - maprings += 10; - break; - case 600: // 5 vertical rings - case 601: // 5 vertical rings - case 602: // 5 diagonal rings - maprings += 5; - break; - case 604: // 8 circle rings - maprings += 8; - break; - case 605: // 16 circle rings - maprings += 16; - break; - } - } - } - Z_Free(datastart); - - if (maprings) - CONS_Printf("%s has %u rings\n", G_BuildMapName(mapnum), maprings); -} -#endif - static int cmp_loopends(const void *a, const void *b) { const mapthing_t @@ -968,20 +836,15 @@ void P_WriteThings(void) { size_t i, length; mapthing_t *mt; - savebuffer_t save; + savebuffer_t save = {0}; INT16 temp; - save.size = nummapthings * sizeof (mapthing_t); - save.p = save.buffer = (UINT8 *)malloc(nummapthings * sizeof (mapthing_t)); - - if (!save.p) + if (P_SaveBufferAlloc(&save, nummapthings * sizeof (mapthing_t)) == false) { CONS_Alert(CONS_ERROR, M_GetText("No more free memory for thing writing!\n")); return; } - save.end = save.buffer + save.size; - mt = mapthings; for (i = 0; i < nummapthings; i++, mt++) { @@ -2389,7 +2252,7 @@ typedef struct static FILE *P_OpenTextmap(const char *mode, const char *error) { FILE *f; - char *filepath = va(pandf, srb2home, "TEXTMAP"); + char *filepath = va("%s" PATHSEP "TEXTMAP.%s.txt", srb2home, mapheaderinfo[gamemap-1]->lumpname); f = fopen(filepath, mode); if (!f) @@ -7879,19 +7742,9 @@ static void P_InitLevelSettings(boolean reloadinggamestate) // emerald hunt hunt1 = hunt2 = hunt3 = NULL; - // map time limit - if (mapheaderinfo[gamemap-1]->countdown) - { - countdowntimer = mapheaderinfo[gamemap-1]->countdown * TICRATE; - } - else - countdowntimer = 0; - countdowntimeup = false; - // circuit, race and competition stuff circuitmap = false; numstarposts = 0; - ssspheres = 0; numbosswaypoints = 0; if (!reloadinggamestate) timeinmap = 0; @@ -8047,43 +7900,6 @@ static void P_RunLevelScript(const char *scriptname) COM_BufExecute(); // Run it! } -static void P_ForceCharacter(const char *forcecharskin) -{ - UINT8 i; - - if (netgame) - { - char skincmd[33]; - - for (i = 0; i <= splitscreen; i++) - { - const char *num = ""; - - if (i > 0) - num = va("%d", i+1); - - sprintf(skincmd, "skin%s %s\n", num, forcecharskin); - CV_Set(&cv_skin[i], forcecharskin); - } - - COM_BufAddText(skincmd); - } - else - { - for (i = 0; i <= splitscreen; i++) - { - SetPlayerSkin(g_localplayers[i], forcecharskin); - - // normal player colors in single player - if ((unsigned)cv_playercolor[i].value != skins[players[g_localplayers[i]].skin].prefcolor && !modeattacking) - { - CV_StealthSetValue(&cv_playercolor[i], skins[players[g_localplayers[i]].skin].prefcolor); - players[g_localplayers[i]].skincolor = skins[players[g_localplayers[i]].skin].prefcolor; - } - } - } -} - static void P_ResetSpawnpoints(void) { UINT8 i; @@ -8112,7 +7928,7 @@ static void P_LoadRecordGhosts(void) INT32 i; const char *gamemode = (modeattacking & ATTACKING_ITEMBREAK) ? "IB" : "RA"; - gpath = Z_StrDup(va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap))); + gpath = xva("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap)); // Best Time ghost if (cv_ghost_besttime.value) @@ -8165,14 +7981,15 @@ static void P_LoadRecordGhosts(void) { lumpnum_t l; UINT8 j = 1; - while (j <= 99 && (l = W_CheckNumForName(va("%sS%02u",G_BuildMapName(gamemap),j))) != LUMPERROR) + // TODO: Use map header to determine lump name + while (j <= 99 && (l = W_CheckNumForLongName(va("%sS%02u",G_BuildMapName(gamemap),j))) != LUMPERROR) { G_AddGhost(va("%sS%02u",G_BuildMapName(gamemap),j)); j++; } } - Z_Free(gpath); + free(gpath); } static void P_SetupCamera(UINT8 pnum, camera_t *cam) @@ -8307,7 +8124,7 @@ static void P_InitGametype(void) #else strcpy(ver, VERSIONSTRING); #endif - sprintf(buf, "%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online"PATHSEP"%s"PATHSEP"%d-%s", + snprintf(buf, sizeof buf, "%s"PATHSEP"media"PATHSEP"replay"PATHSEP"online"PATHSEP"%s"PATHSEP"%d-%s", srb2home, ver, (int) (time(NULL)), G_BuildMapName(gamemap)); parts = M_PathParts(buf); @@ -8329,6 +8146,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // Map header should always be in place at this point INT32 i, ranspecialwipe = 0; sector_t *ss; + virtlump_t *encoreLump = NULL; + lumpnum_t oldEncore = LUMPERROR; levelloading = true; @@ -8366,9 +8185,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) for (i = 0; i <= r_splitscreen; i++) postimgtype[i] = postimg_none; - if (mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0') - P_ForceCharacter(mapheaderinfo[gamemap-1]->forcecharacter); - // Initial height of PointOfView // will be set by player think. players[consoleplayer].viewz = 1; @@ -8540,15 +8356,46 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } // internal game map - maplumpname = G_BuildMapName(gamemap); - lastloadedmaplumpnum = W_CheckNumForMap(maplumpname); + maplumpname = mapheaderinfo[gamemap-1]->lumpname; + lastloadedmaplumpnum = mapheaderinfo[gamemap-1]->lumpnum; if (lastloadedmaplumpnum == LUMPERROR) I_Error("Map %s not found.\n", maplumpname); curmapvirt = vres_GetMap(lastloadedmaplumpnum); - R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette, - W_CheckNumForName(va("%s%c", maplumpname, (encoremode ? 'E' : 'T')))); + if (mapheaderinfo[gamemap-1]) + { + if (encoremode) + { + encoreLump = vres_Find(curmapvirt, "ENCORE"); + if (!encoreLump) + oldEncore = W_CheckNumForName(va("%sE", maplumpname)); + } + else + { + encoreLump = vres_Find(curmapvirt, "TWEAKMAP"); + if (!encoreLump) + oldEncore = W_CheckNumForName(va("%sT", maplumpname)); + } + } + + if (encoreLump) + { + R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette, encoreLump->data, encoreLump->size, false); + } + else if (oldEncore != LUMPERROR) + { + // mildly annoying, but whatever + size_t size = W_LumpLength(oldEncore); + void *data = malloc(size); + W_ReadLump(oldEncore, data); + R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette, data, size, wadfiles[WADFILENUM(oldEncore)]->compatmode); + free(data); + } + else + { + R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette, NULL, 0, false); + } CON_SetupBackColormap(); // SRB2 determines the sky texture to be used depending on the map header. @@ -8662,9 +8509,9 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) R_PrecacheLevel(); if (!(netgame || multiplayer || demo.playback) && !majormods) - mapvisited[gamemap-1] |= MV_VISITED; + mapheaderinfo[gamemap-1]->mapvisited |= MV_VISITED; else if (!demo.playback) - mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently + mapheaderinfo[gamemap-1]->mapvisited |= MV_MP; // you want to record that you've been there this session, but not permanently G_AddMapToBuffer(gamemap-1); @@ -8901,6 +8748,143 @@ static lumpinfo_t* FindFolder(const char *folName, UINT16 *start, UINT16 *end, l return lumpinfo; } +lumpnum_t wadnamelump = LUMPERROR; +INT16 wadnamemap = 0; // gamemap based + +// Initialising map data... +UINT8 P_InitMapData(boolean existingmapheaders) +{ + UINT8 ret = 0; + INT32 i; + lumpnum_t maplump; + virtres_t *virtmap; + virtlump_t *minimap, *thumbnailPic; + patch_t *oldPic; + char *name; + + for (i = 0; i < nummapheaders; ++i) + { + name = mapheaderinfo[i]->lumpname; + maplump = W_CheckNumForMap(name); + + // Doesn't exist? + if (maplump == INT16_MAX) + { +#ifndef DEVELOP + if (!existingmapheaders) + { + I_Error("P_InitMapData: Base map %s has a header but no level\n", name); + } +#endif + continue; + } + + // Always check for cup cache reassociations. + // (The core assumption is that cups < headers.) + { + cupheader_t *cup = kartcupheaders; + INT32 j; + + mapheaderinfo[i]->cup = NULL; + + while (cup) + { + for (j = 0; j < CUPCACHE_MAX; j++) + { + // Already discovered? + if (cup->cachedlevels[j] != NEXTMAP_INVALID) + continue; + + if (!cup->levellist[j] || strcasecmp(cup->levellist[j], name) != 0) + continue; + + // Only panic about back-reference for non-bonus material. + if (j < MAXLEVELLIST) + { + if (mapheaderinfo[i]->cup) + I_Error("P_InitMapData: Map %s cannot appear in cups multiple times! (First in %s, now in %s)", name, mapheaderinfo[i]->cup->name, cup->name); + mapheaderinfo[i]->cup = cup; + } + + cup->cachedlevels[j] = i; + } + cup = cup->next; + } + } + + // No change? + if (mapheaderinfo[i]->lumpnum == maplump) + continue; + + // Okay, it does... + { + ret |= MAPRET_ADDED; + + if (existingmapheaders) + { + CONS_Printf("%s\n", name); + + if (mapheaderinfo[i]->lumpnum != LUMPERROR) + { + G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you + + //If you replaced the map you're on, end the level when done. + if (i == gamemap - 1) + ret |= MAPRET_CURRENTREPLACED; + } + } + + mapheaderinfo[i]->lumpnum = maplump; + if (maplump == wadnamelump) + wadnamemap = i+1; + + // Get map thumbnail and minimap + virtmap = vres_GetMap(mapheaderinfo[i]->lumpnum); + thumbnailPic = vres_Find(virtmap, "PICTURE"); + minimap = vres_Find(virtmap, "MINIMAP"); + + // Clear out existing graphics... + if (mapheaderinfo[i]->thumbnailPic) + { + Patch_Free(mapheaderinfo[i]->thumbnailPic); + } + + if (mapheaderinfo[i]->minimapPic) + { + Patch_Free(mapheaderinfo[i]->minimapPic); + } + + // Now apply the new ones! + if (thumbnailPic) + { + mapheaderinfo[i]->thumbnailPic = vres_GetPatch(thumbnailPic, PU_STATIC); + } + // okay... try finding them the old-fashioned way + else + { + oldPic = W_CachePatchName(va("%sP", name), PU_STATIC); + if (oldPic != missingpat) + mapheaderinfo[i]->thumbnailPic = oldPic; + } + + if (minimap) + { + mapheaderinfo[i]->minimapPic = vres_GetPatch(minimap, PU_STATIC); + } + else + { + oldPic = W_CachePatchName(va("%sR", name), PU_STATIC); + if (oldPic != missingpat) + mapheaderinfo[i]->minimapPic = oldPic; + } + + vres_Free(virtmap); + } + } + + return ret; +} + // // Add a wadfile to the active wad files, // replace sounds, musics, patches, textures, sprites and maps @@ -8926,7 +8910,6 @@ UINT16 P_PartialAddWadFile(const char *wadfilename, wadcompat_t compat) UINT16 numlumps, wadnum; char *name; lumpinfo_t *lumpinfo; - boolean mapsadded = false; // Vars to help us with the position start and amount of each resource type. // Useful for PK3s since they use folders. @@ -9082,41 +9065,6 @@ UINT16 P_PartialAddWadFile(const char *wadfilename, wadcompat_t compat) // R_LoadSpriteInfoLumps(wadnum, numlumps); - // - // search for maps - // - lumpinfo = wadfiles[wadnum]->lumpinfo; - for (i = 0; i < numlumps; i++, lumpinfo++) - { - name = lumpinfo->name; - if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers - { - INT16 num; - if (name[5]!='\0') - continue; - num = (INT16)M_MapNumber(name[3], name[4]); - - // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant - if (num <= NUMMAPS && mapheaderinfo[num-1]) - { - if (mapheaderinfo[num - 1]->alreadyExists != false) - { - G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you - } - mapheaderinfo[num - 1]->alreadyExists = true; - } - - if (num == gamemap) - partadd_replacescurrentmap = true; - - CONS_Printf("%s\n", name); - mapsadded = true; - } - } - - if (!mapsadded) - CONS_Printf(M_GetText("No maps added\n")); - refreshdirmenu &= ~REFRESHDIR_GAMEDATA; // Under usual circumstances we'd wait for REFRESHDIR_ flags to disappear the next frame, but this one's a bit too dangerous for that... partadd_stage = 0; return wadnum; @@ -9180,9 +9128,16 @@ boolean P_MultiSetupWadFiles(boolean fullsetup) if (partadd_stage == 2) { - if (partadd_replacescurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer)) + UINT8 mapsadded = P_InitMapData(true); + + if (!mapsadded) + CONS_Printf(M_GetText("No maps added\n")); + + if ((mapsadded & MAPRET_CURRENTREPLACED) + && (gamestate == GS_LEVEL) + && (netgame || multiplayer)) { - CONS_Printf(M_GetText("Current map %d replaced, ending the level to ensure consistency.\n"), gamemap); + CONS_Printf(M_GetText("Current map %s replaced by added file, ending the level to ensure consistency.\n"), mapheaderinfo[gamemap-1]->lumpname); if (server) SendNetXCmd(XD_EXITLEVEL, NULL, 0); } diff --git a/src/p_setup.h b/src/p_setup.h index 2e72c626a..70b93d532 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -103,9 +103,6 @@ extern size_t nummapthings; extern mapthing_t *mapthings; void P_SetupLevelSky(const char *skytexname, boolean global); -#ifdef SCANTHINGS -void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum); -#endif void P_RespawnThings(void); boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate); void P_PostLoadLevel(void); @@ -114,6 +111,13 @@ void HWR_LoadLevel(void); #endif boolean P_AddWadFile(const char *wadfilename, wadcompat_t compat); +#define MAPRET_ADDED (1) +#define MAPRET_CURRENTREPLACED (1<<1) +UINT8 P_InitMapData(boolean existingmapheaders); +extern lumpnum_t wadnamelump; +extern INT16 wadnamemap; +#define WADNAMECHECK(name) (!strncmp(name, "WADNAME", 7)) + // WARNING: The following functions should be grouped as follows: // any amount of PartialAdds followed by MultiSetups until returned true, // as soon as possible. @@ -150,11 +154,6 @@ void P_DeleteFlickies(INT16 i); // Needed for NiGHTS void P_ReloadRings(void); -void P_DeleteGrades(INT16 i); -void P_AddGradesForMare(INT16 i, UINT8 mare, char *gtext); -UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare); -UINT8 P_HasGrades(INT16 map, UINT8 mare); -UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade); #ifdef __cplusplus } // extern "C" diff --git a/src/p_spec.c b/src/p_spec.c index f477316d0..f80a23767 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -6714,10 +6714,6 @@ void P_InitSpecials(void) maplighting.directional = lighting->use_light_angle; maplighting.angle = lighting->light_angle; - // Defaults in case levels don't have them set. - sstimer = mapheaderinfo[gamemap-1]->sstimer*TICRATE + 6; - ssspheres = mapheaderinfo[gamemap-1]->ssspheres; - CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false; // Set weather @@ -6868,11 +6864,6 @@ void P_SpawnSpecials(boolean fromnetsave) // Process Section 2 switch(GETSECSPECIAL(sector->special, 2)) { - case 10: // Time for special stage - sstimer = (sector->floorheight>>FRACBITS) * TICRATE + 6; // Time to finish - ssspheres = sector->ceilingheight>>FRACBITS; // Ring count for special stage - break; - case 11: // Custom global gravity! if (udmf) break; diff --git a/src/p_user.c b/src/p_user.c index beab7153d..53a0da17d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -303,11 +303,15 @@ boolean P_PlayerMoving(INT32 pnum) // UINT8 P_GetNextEmerald(void) { - if (gamemap >= sstage_start && gamemap <= sstage_end) - return (UINT8)(gamemap - sstage_start); - if (gamemap >= smpstage_start || gamemap <= smpstage_end) - return (UINT8)(gamemap - smpstage_start); - return 0; + INT16 mapnum = gamemap-1; + + if (mapnum > nummapheaders || !mapheaderinfo[mapnum]) + return 0; + + if (!mapheaderinfo[mapnum]->cup || mapheaderinfo[mapnum]->cup->cachedlevels[CUPCACHE_SPECIAL] != mapnum) + return 0; + + return mapheaderinfo[mapnum]->cup->emeraldnum; } // @@ -2098,9 +2102,6 @@ void P_MovePlayer(player_t *player) fixed_t runspd; - if (countdowntimeup) - return; - cmd = &player->cmd; runspd = 14*player->mo->scale; //srb2kart @@ -4293,14 +4294,7 @@ void P_PlayerThink(player_t *player) if (!player->spectator) P_PlayerInSpecialSector(player); - else if ( -#else - if (player->spectator && #endif - (gametyperules & GTR_LIVES)) - { - /*P_ConsiderAllGone()*/; - } if (player->playerstate == PST_DEAD) { diff --git a/src/r_data.c b/src/r_data.c index ecac6920a..69f583914 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -287,7 +287,7 @@ void R_InitColormaps(void) #endif } -void R_ReInitColormaps(UINT16 num, lumpnum_t newencoremap) +void R_ReInitColormaps(UINT16 num, void *newencoremap, size_t encoremapsize, boolean compat) { char colormap[9] = "COLORMAP"; lumpnum_t lump; @@ -325,18 +325,17 @@ void R_ReInitColormaps(UINT16 num, lumpnum_t newencoremap) } // Encore mode. - if (newencoremap != LUMPERROR) + if (newencoremap) { lighttable_t *colormap_p, *colormap_p2; size_t p, i; encoremap = Z_MallocAlign(256 + 10, PU_LEVEL, NULL, 8); - W_ReadLump(newencoremap, encoremap); + M_Memcpy(encoremap, newencoremap, encoremapsize); colormap_p = colormap_p2 = colormaps; colormap_p += COLORMAP_REMAPOFFSET; - remap = wadfiles[WADFILENUM(newencoremap)]->compatmode; - if (remap) + if (compat) { UINT8 *copy = malloc(256); memcpy(copy, encoremap, 256); diff --git a/src/r_data.h b/src/r_data.h index b006ac862..a76978693 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -55,7 +55,7 @@ extern size_t flatmemory, spritememory, texturememory; //#define COLORMAPREVERSELIST void R_InitColormaps(void); -void R_ReInitColormaps(UINT16 num, lumpnum_t newencoremap); +void R_ReInitColormaps(UINT16 num, void *newencoremap, size_t encoremapsize, boolean compat); void R_ClearColormaps(void); extracolormap_t *R_CreateDefaultColormap(boolean lighttable); extracolormap_t *R_GetDefaultColormap(void); diff --git a/src/r_defs.h b/src/r_defs.h index b6ef0b223..ce833906d 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -913,6 +913,9 @@ struct patch_t }; extern patch_t *missingpat; +extern patch_t *blanklvl; +extern patch_t *randomlvl; +extern patch_t *nolvl; #if defined(_MSC_VER) #pragma pack(1) diff --git a/src/r_patch.c b/src/r_patch.c index 544c15ae8..8cf89fa3d 100644 --- a/src/r_patch.c +++ b/src/r_patch.c @@ -101,7 +101,7 @@ static void Patch_FreeData(patch_t *patch) void Patch_Free(patch_t *patch) { - if (patch == missingpat) + if (!patch || patch == missingpat) return; Patch_FreeData(patch); Z_Free(patch); diff --git a/src/r_skins.c b/src/r_skins.c index 867b205c1..96a21e9bc 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -198,12 +198,6 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum) return true; } - if (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) - { - // Being forced to play as this character by the level - return true; - } - if (netgame && (cv_forceskin.value == skinnum)) { // Being forced to play as this character by the server diff --git a/src/st_stuff.c b/src/st_stuff.c index 07aeded4e..6e5079962 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -442,6 +442,31 @@ static void ST_drawRenderDebug(INT32 *height) ST_pushDebugString(height, va("Skybox Portals: %4s", sizeu1(i->skybox_portals))); } +static void ST_drawDemoDebug(INT32 *height) +{ + if (!demo.recording && !demo.playback) + return; + + size_t needle = demo.buffer->p - demo.buffer->buffer; + size_t size = demo.buffer->size; + double percent = (double)needle / size * 100.0; + double avg = (double)needle / leveltime; + + ST_pushDebugString(height, va("%s/%s bytes", sizeu1(needle), sizeu2(size))); + ST_pushDebugString(height, va( + "%.2f/%.2f MB %5.2f%%", + needle / (1024.0 * 1024.0), + size / (1024.0 * 1024.0), + percent + )); + ST_pushDebugString(height, va( + "%.2f KB/s (ETA %.2f minutes)", + avg * TICRATE / 1024.0, + (size - needle) / (avg * TICRATE * 60.0) + )); + ST_pushDebugString(height, va("Demo (%s)", demo.recording ? "recording" : "playback")); +} + static void ST_drawDebugInfo(void) { INT32 height = 192; @@ -512,6 +537,11 @@ static void ST_drawDebugInfo(void) ST_drawRenderDebug(&height); } + if (cht_debug & DBG_DEMO) + { + ST_drawDemoDebug(&height); + } + if (cht_debug & DBG_MEMORY) V_DrawRightAlignedString(320, height, V_MONOSPACE, va("Heap used: %7sKB", sizeu1(Z_TagsUsage(0, INT32_MAX)>>10))); } diff --git a/src/w_wad.c b/src/w_wad.c index f251ee123..37cc1dea8 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -64,11 +64,7 @@ #include "i_system.h" #include "md5.h" #include "lua_script.h" -#ifdef SCANTHINGS -#include "p_setup.h" // P_ScanThings -#endif -#include "m_misc.h" // M_MapNumber -#include "g_game.h" // G_SetGameModified +#include "g_game.h" // G_MapNumber #include "k_terrain.h" @@ -94,10 +90,12 @@ typedef struct // Must be a power of two #define LUMPNUMCACHESIZE 64 +#define LUMPNUMCACHENAME 32 typedef struct lumpnum_cache_s { char lumpname[32]; + UINT32 lumphash; lumpnum_t lumpnum; } lumpnum_cache_t; @@ -307,22 +305,6 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile) DEH_LoadDehackedLumpPwad(wadnum, lump, mainfile); } } - -#ifdef SCANTHINGS - // Scan maps for emblems 'n shit - { - lumpinfo_t *lump_p = wadfiles[wadnum]->lumpinfo; - for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++) - { - const char *name = lump_p->name; - if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P' && name[5]=='\0') - { - INT16 mapnum = (INT16)M_MapNumber(name[3], name[4]); - P_ScanThings(mapnum, wadnum, lump + ML_THINGS); - } - } - } -#endif } static inline boolean CheckCompatFilename(const char *filename) @@ -545,19 +527,76 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen } else lump_p->compression = CM_NOCOMPRESSION; + memset(lump_p->name, 0x00, 9); strncpy(lump_p->name, fileinfo->name, 8); - lump_p->hash = quickncasehash(lump_p->name, 8); - // Allocate the lump's long name. - lump_p->longname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); - strncpy(lump_p->longname, fileinfo->name, 8); - lump_p->longname[8] = '\0'; + if (WADNAMECHECK(fileinfo->name)) + { + size_t namelen; + const char *trimname, *dotpos; - // Allocate the lump's full name. - lump_p->fullname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); - strncpy(lump_p->fullname, fileinfo->name, 8); - lump_p->fullname[8] = '\0'; + trimname = strrchr(filename, PATHSEP[0]); +#if defined (_WIN32) + // For Zone Builder support, work around temporary filenames. + // They're annoyingly randomised, BUT they follow \Temp\8\8.3... + // AND they're always guaranteed to follow the map file, which + // should already have a WADNAME in it for us to piggyback off. + // EXAMPLE: // \Temp\gj3l7w7n\4f926789.wad + + if (trimname != 0 + && wadnamelump != LUMPERROR + && strlen(trimname+1) == 8+1+3) + { + const char *temp = trimname-1; + while (temp >= filename+5 && *temp != PATHSEP[0]) + temp--; + + if (((trimname-1) - temp) == 8 + && temp >= filename+5 + && !strncmp(temp-5, PATHSEP"Temp", 5)) + { + filename = wadfiles[ + ((wadnamelump & ~UINT16_MAX) >> 16) + ]->filename; + trimname = strrchr(filename, PATHSEP[0]); + } + } +#endif + // Strip away file address + if (trimname != 0) + trimname++; + else + trimname = filename; // Care taken for root files. + + // First stop, not last, to permit RR_GREENHILLS.beta3.wad + if ((dotpos = strchr(trimname, '.')) != 0) + namelen = (dotpos + 1 - trimname); + else + namelen = strlen(trimname); + + // Allocate the lump's long and full name (save on memory). + lump_p->longname = lump_p->fullname = Z_Calloc(namelen * sizeof(char), PU_STATIC, NULL); + strncpy(lump_p->longname, trimname, namelen); + lump_p->longname[namelen-1] = '\0'; + + CONS_Debug(DBG_SETUP, "WADNAME handling:\n -- path %s\n -- interpreted lumpname %s\n", filename, lump_p->longname); + + // Grab the hash from the first part + lump_p->hash = quickncasehash(lump_p->longname, 8); + + wadnamelump = i | (numwadfiles << 16); + } + else + { + // Set up true hash + lump_p->hash = quickncasehash(lump_p->name, 8); + + // Allocate the lump's long and full name (save on memory). + lump_p->longname = lump_p->fullname = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL); + strncpy(lump_p->longname, fileinfo->name, 8); + lump_p->longname[8] = '\0'; + } } free(fileinfov); *nlmp = numlumps; @@ -1094,6 +1133,63 @@ UINT16 W_FindNextEmptyInPwad(UINT16 wad, UINT16 startlump) return INT16_MAX; } +// Get a map marker for WADs, and a standalone WAD file lump inside PK3s. +UINT16 W_CheckNumForMapPwad(const char *name, UINT32 hash, UINT16 wad, UINT16 startlump) +{ + UINT16 i, end; + + if (wadfiles[wad]->type == RET_WAD) + { + for (i = startlump; i < wadfiles[wad]->numlumps; i++) + { + // Not the hash? + if ((wadfiles[wad]->lumpinfo + i)->hash != hash) + continue; + + // Not the name? (always use longname, even in wads, to accomodate WADNAME) + if (strcasecmp(name, (wadfiles[wad]->lumpinfo + i)->longname)) + continue; + + // Not a header? + if (W_LumpLength(i | (wad << 16)) > 0) + continue; + + return i; + } + } + else if (wadfiles[wad]->type == RET_PK3) + { + i = W_CheckNumForFolderStartPK3("maps/", wad, startlump); + + if (i != INT16_MAX) + { + end = W_CheckNumForFolderEndPK3("maps/", wad, i); + + // Now look for the specified map. + for (; i < end; i++) + { + // Not the hash? + if ((wadfiles[wad]->lumpinfo + i)->hash != hash) + continue; + + // Not the name? + if (strcasecmp(name, (wadfiles[wad]->lumpinfo + i)->longname)) + continue; + +#if 0 + // Not a .wad? + if (!W_IsLumpWad(i | (wad << 16))) + continue; +#endif + + return i; + } + } + } + + return INT16_MAX; +} + // // Same as the original, but checks in one pwad only. // wadid is a wad number @@ -1141,12 +1237,14 @@ UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump) { UINT16 i; static char uname[256 + 1]; + UINT32 hash; if (!TestValidLump(wad,0)) return INT16_MAX; strlcpy(uname, name, sizeof uname); strupr(uname); + hash = quickncasehash(uname, 8); // Not a mistake, legacy system for short lumpnames // // scan forward @@ -1157,8 +1255,14 @@ UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump) { lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump; for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++) - if (!strcmp(lump_p->longname, uname)) - return i; + { + if (lump_p->hash != hash) + continue; + if (strcmp(lump_p->longname, uname)) + continue; + return i; + } + } // not found. @@ -1234,6 +1338,7 @@ UINT16 W_CheckNumForFullNamePK3(const char *name, UINT16 wad, UINT16 startlump) lumpnum_t W_CheckNumForName(const char *name) { INT32 i; + UINT32 hash = name ? quickncasehash(name, 8) : 0; lumpnum_t check = INT16_MAX; if (name == NULL) @@ -1247,6 +1352,7 @@ lumpnum_t W_CheckNumForName(const char *name) for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) { if (!lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname[8] + && lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumphash == hash && strncmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0) { lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); @@ -1284,6 +1390,7 @@ lumpnum_t W_CheckNumForName(const char *name) lumpnum_t W_CheckNumForLongName(const char *name) { INT32 i; + UINT32 hash = name ? quickncasehash(name, 8) : 0; lumpnum_t check = INT16_MAX; if (name == NULL) @@ -1321,6 +1428,7 @@ lumpnum_t W_CheckNumForLongName(const char *name) memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32); strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32); lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check; + lumpnumcache[lumpnumcacheindex].lumphash = hash; } return (i << 16) + check; @@ -1329,45 +1437,52 @@ lumpnum_t W_CheckNumForLongName(const char *name) // Look for valid map data through all added files in descendant order. // Get a map marker for WADs, and a standalone WAD file lump inside PK3s. -// TODO: Make it search through cache first, maybe...? lumpnum_t W_CheckNumForMap(const char *name) { - UINT32 hash = quickncasehash(name, 8); - UINT16 lumpNum, end; - UINT32 i; - lumpinfo_t *p; - for (i = numwadfiles - 1; i < numwadfiles; i--) + lumpnum_t check = INT16_MAX; + UINT32 uhash, hash = quickncasehash(name, LUMPNUMCACHENAME); + INT32 i; + + // Check the lumpnumcache first. Loop backwards so that we check + // most recent entries first + for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--) { - if (wadfiles[i]->type == RET_WAD) + if (lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumphash == hash + && strcasecmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0) { - for (lumpNum = 0; lumpNum < wadfiles[i]->numlumps; lumpNum++) - { - p = wadfiles[i]->lumpinfo + lumpNum; - if (p->hash == hash && !strncmp(name, p->name, 8)) - return (i<<16) + lumpNum; - } - } - else if (wadfiles[i]->type == RET_PK3) - { - lumpNum = W_CheckNumForFolderStartPK3("maps/", i, 0); - if (lumpNum != INT16_MAX) - end = W_CheckNumForFolderEndPK3("maps/", i, lumpNum); - else - continue; - // Now look for the specified map. - for (; lumpNum < end; lumpNum++) - { - p = wadfiles[i]->lumpinfo + lumpNum; - if (p->hash == hash && !strnicmp(name, p->name, 8)) - { - const char *extension = strrchr(p->fullname, '.'); - if (!(extension && stricmp(extension, ".wad"))) - return (i<<16) + lumpNum; - } - } + lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1); + return lumpnumcache[lumpnumcacheindex].lumpnum; } } - return LUMPERROR; + + uhash = quickncasehash(name, 8); // Not a mistake, legacy system for short lumpnames + + for (i = numwadfiles - 1; i >= 0; i--) + { + check = W_CheckNumForMapPwad(name, uhash, (UINT16)i, 0); + + if (check != INT16_MAX) + break; // found it + } + + if (check == INT16_MAX) + { + return LUMPERROR; + } + else + { + if (strlen(name) < LUMPNUMCACHENAME) + { + // Update the cache. + lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1); + memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', LUMPNUMCACHENAME); + strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, LUMPNUMCACHENAME); + lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check; + lumpnumcache[lumpnumcacheindex].lumphash = hash; + } + + return (i << 16) + check; + } } // @@ -1893,6 +2008,28 @@ void *W_CacheLumpName(const char *name, INT32 tag) // Cache a patch into heap memory, convert the patch format as necessary // +static void *MakePatch(void *lumpdata, size_t size, INT32 tag, void *cache, boolean remap) +{ + void *ptr, *dest; + size_t len = size; + + ptr = lumpdata; + +#ifndef NO_PNG_LUMPS + if (Picture_IsLumpPNG((UINT8 *)lumpdata, len)) + ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &len, 0); +#endif + + if (remap) + R_DoPaletteRemapPatch(ptr, len); + + dest = Z_Calloc(sizeof(patch_t), tag, cache); + + Patch_Create(ptr, len, dest); + + return dest; +} + void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) { lumpcache_t *lumpcache = NULL; @@ -1905,25 +2042,13 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) if (!lumpcache[lump]) { size_t len = W_LumpLengthPwad(wad, lump); - void *ptr, *dest, *lumpdata = Z_Malloc(len, PU_STATIC, NULL); + void *lumpdata = Z_Malloc(len, PU_STATIC, NULL); // read the lump in full W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0); - ptr = lumpdata; -#ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)lumpdata, len)) - ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &len, 0); -#endif - - // we already know this is a patch, do palette remapping here - if (wadfiles[wad]->compatmode) - R_DoPaletteRemapPatch(ptr, len); - - dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]); - Patch_Create(ptr, len, dest); - - Z_Free(ptr); + MakePatch(lumpdata, len, tag, &lumpcache[lump], wadfiles[wad]->compatmode); + Z_Free(lumpdata); } else Z_ChangeTag(lumpcache[lump], tag); @@ -2395,28 +2520,48 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) if (W_IsLumpWad(lumpnum)) { + UINT32 realentry; + size_t *vsizecache; + // Remember that we're assuming that the WAD will have a specific set of lumps in a specific order. UINT8 *wadData = W_CacheLumpNum(lumpnum, PU_LEVEL); filelump_t *fileinfo = (filelump_t *)(wadData + ((wadinfo_t *)wadData)->infotableofs); - numlumps = ((wadinfo_t *)wadData)->numlumps; - vlumps = Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL); - // Build the lumps. - for (i = 0; i < numlumps; i++) + i = ((wadinfo_t *)wadData)->numlumps; + vsizecache = Z_Malloc(sizeof(size_t)*i, PU_LEVEL, NULL); + + for (realentry = 0; realentry < i; realentry++) { - vlumps[i].size = (size_t)(((filelump_t *)(fileinfo + i))->size); - // Play it safe with the name in this case. - memcpy(vlumps[i].name, (fileinfo + i)->name, 8); - vlumps[i].name[8] = '\0'; - vlumps[i].data = Z_Malloc(vlumps[i].size, PU_LEVEL, NULL); // This is memory inefficient, sorry about that. - memcpy(vlumps[i].data, wadData + (fileinfo + i)->filepos, vlumps[i].size); + vsizecache[realentry] = (size_t)(((filelump_t *)(fileinfo + realentry))->size); + + if (!vsizecache[realentry]) + continue; + + numlumps++; } + vlumps = Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL); + + // Build the lumps, skipping over empty entries. + for (i = 0, realentry = 0; i < numlumps; realentry++) + { + if (vsizecache[realentry] == 0) + continue; + vlumps[i].size = vsizecache[realentry]; + // Play it safe with the name in this case. + memcpy(vlumps[i].name, (fileinfo + realentry)->name, 8); + vlumps[i].name[8] = '\0'; + vlumps[i].data = Z_Malloc(vlumps[i].size, PU_LEVEL, NULL); // This is memory inefficient, sorry about that. + memcpy(vlumps[i].data, wadData + (fileinfo + realentry)->filepos, vlumps[i].size); + i++; + } + + Z_Free(vsizecache); Z_Free(wadData); } else { - // Count number of lumps until the end of resource OR up until next "MAPXX" lump. + // Count number of lumps until the end of resource OR up until next 0-length lump. lumpnum_t lumppos = lumpnum + 1; lumpnum_t lastlump = wadfiles[WADFILENUM(lumpnum)]->numlumps; // or the end of directory, for PK3 files @@ -2430,8 +2575,12 @@ virtres_t* vres_GetMap(lumpnum_t lumpnum) free(dirname); } for (i = LUMPNUM(lumppos); i < lastlump; i++, lumppos++, numlumps++) - if (memcmp(W_CheckNameForNum(lumppos), "MAP", 3) == 0) - break; + { + if (W_LumpLength(lumppos) > 0) + continue; + + break; + } numlumps++; vlumps = Z_Malloc(sizeof(virtlump_t)*numlumps, PU_LEVEL, NULL); @@ -2463,7 +2612,12 @@ void vres_Free(virtres_t* vres) } while (vres->numlumps--) - Z_Free(vres->vlumps[vres->numlumps].data); + { + if (vres->vlumps[vres->numlumps].data) + { + Z_Free(vres->vlumps[vres->numlumps].data); + } + } Z_Free(vres->vlumps); Z_Free(vres); } @@ -2494,3 +2648,30 @@ virtlump_t* vres_Find(const virtres_t* vres, const char* name) return &vres->vlumps[i]; return NULL; } + +/** \brief Gets patch from given virtual lump + * + * \param Virtual lump + * \return Patch data + * + */ +void *vres_GetPatch(virtlump_t *vlump, INT32 tag) +{ + patch_t *patch; + + if (!vlump) + return NULL; + + patch = MakePatch(vlump->data, vlump->size, tag, NULL, false); + +#ifdef HWRENDER + // Software-only compile cache the data without conversion + if (rendermode == render_soft || rendermode == render_none) +#endif + return (void *)patch; + +#ifdef HWRENDER + Patch_CreateGL(patch); + return (void *)patch; +#endif +} diff --git a/src/w_wad.h b/src/w_wad.h index 3b5375ca4..8a088ad3c 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -92,6 +92,7 @@ struct virtres_t { virtres_t* vres_GetMap(lumpnum_t); void vres_Free(virtres_t*); virtlump_t* vres_Find(const virtres_t*, const char*); +void* vres_GetPatch(virtlump_t *vlump, INT32); // ========================================================================= // DYNAMIC WAD LOADING @@ -163,6 +164,7 @@ const char *W_CheckNameForNum(lumpnum_t lumpnum); UINT16 W_FindNextEmptyInPwad(UINT16 wad, UINT16 startlump); // checks only in one pwad +UINT16 W_CheckNumForMapPwad(const char *name, UINT32 hash, UINT16 wad, UINT16 startlump); UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump); // checks only in one pwad UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump); diff --git a/src/y_inter.c b/src/y_inter.c index f84d4dedb..7f0e4a762 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -142,7 +142,6 @@ typedef struct char str[62]; UINT8 gtc; const char *gts; - patch_t *pic; boolean encore; } y_votelvlinfo; @@ -175,7 +174,6 @@ static patch_t *cursor1 = NULL; static patch_t *cursor2 = NULL; static patch_t *cursor3 = NULL; static patch_t *cursor4 = NULL; -static patch_t *randomlvl = NULL; static patch_t *rubyicon = NULL; static void Y_UnloadVoteData(void); @@ -1036,8 +1034,8 @@ void Y_StartIntermission(void) //if (dedicated) return; // This should always exist, but just in case... - if (!mapheaderinfo[prevmap]) - P_AllocMapHeader(prevmap); + if (prevmap >= nummapheaders || !mapheaderinfo[prevmap]) + I_Error("Y_StartIntermission: Internal map ID %d not found (nummapheaders = %d)", prevmap, nummapheaders); switch (intertype) { @@ -1189,6 +1187,9 @@ void Y_VoteDrawer(void) INT32 i, x, y = 0, height = 0; UINT8 selected[4]; fixed_t rubyheight = 0; + fixed_t scale; + patch_t *pic; + INT16 mapnum; if (rendermode == render_none) return; @@ -1257,19 +1258,19 @@ void Y_VoteDrawer(void) for (i = 0; i < 4; i++) { const char *str; - patch_t *pic; UINT8 j, color; if (i == 3) { str = "RANDOM"; - pic = randomlvl; + mapnum = -1; } else { str = levelinfo[i].str; - pic = levelinfo[i].pic; + mapnum = votelevels[i][0]; } + scale = M_GetMapThumbnail(mapnum, &pic)/4; if (selected[i]) { @@ -1333,10 +1334,10 @@ void Y_VoteDrawer(void) } if (!levelinfo[i].encore) - V_DrawSmallScaledPatch(BASEVIDWIDTH-100, y, V_SNAPTORIGHT, pic); + V_DrawFixedPatch((BASEVIDWIDTH-100)<= 3 && (i != pickedvote || voteendtic == -1)) - pic = randomlvl; + mapnum = -1; // randomlvl else - pic = levelinfo[votes[i]].pic; + mapnum = votelevels[votes[i]][0]; + + scale = M_GetMapThumbnail(mapnum, &pic)/8; if (!timer && i == voteclient.ranim) { @@ -1402,10 +1404,10 @@ void Y_VoteDrawer(void) } if (!levelinfo[votes[i]].encore) - V_DrawTinyScaledPatch(x, y, V_SNAPTOLEFT, pic); + V_DrawFixedPatch(x<width, scale))<levelflags & LF_NOZONE || !mapheaderinfo[votelevels[i][0]]->zonttl[0]) @@ -1803,13 +1803,6 @@ void Y_StartVote(void) levelinfo[i].gts = gametype_cons_t[votelevels[i][1]].strvalue; else levelinfo[i].gts = NULL; - - // set up the pic - lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(votelevels[i][0]+1))); - if (lumpnum != LUMPERROR) - levelinfo[i].pic = W_CachePatchName(va("%sP", G_BuildMapName(votelevels[i][0]+1)), PU_STATIC); - else - levelinfo[i].pic = W_CachePatchName("BLANKLVL", PU_STATIC); } voteclient.loaded = true; @@ -1833,8 +1826,6 @@ void Y_EndVote(void) // static void Y_UnloadVoteData(void) { - UINT8 i; - voteclient.loaded = false; if (rendermode != render_soft) @@ -1847,30 +1838,7 @@ static void Y_UnloadVoteData(void) UNLOAD(cursor2); UNLOAD(cursor3); UNLOAD(cursor4); - UNLOAD(randomlvl); UNLOAD(rubyicon); - - // to prevent double frees... - for (i = 0; i < 4; i++) - { - // I went to all the trouble of doing this, - // but literally nowhere else frees level pics. -#if 0 - UINT8 j; - - if (!levelinfo[i].pic) - continue; - - for (j = i+1; j < 4; j++) - { - if (levelinfo[j].pic == levelinfo[i].pic) - levelinfo[j].pic = NULL; - } - UNLOAD(levelinfo[i].pic); -#else - CLEANUP(levelinfo[i].pic); -#endif - } } // diff --git a/src/z_zone.c b/src/z_zone.c index 19bef7f17..711d87443 100644 --- a/src/z_zone.c +++ b/src/z_zone.c @@ -47,7 +47,6 @@ #ifdef HAVE_VALGRIND #include "valgrind.h" static boolean Z_calloc = false; -#include "memcheck.h" #endif #define ZONEID 0xa441d13d @@ -151,7 +150,7 @@ void Z_Free2(void *ptr, const char *file, INT32 line) if (block->user != NULL) *block->user = NULL; -#ifdef VALGRIND_DESTROY_MEMPOOL +#ifdef HAVE_VALGRIND VALGRIND_DESTROY_MEMPOOL(block); #endif block->prev->next = block->next; @@ -217,10 +216,6 @@ void *Z_Malloc2(size_t size, INT32 tag, void *user, INT32 alignbits, ptr = MEMORY(block); I_Assert((intptr_t)ptr % alignof (max_align_t) == 0); -#ifdef HAVE_VALGRIND - Z_calloc = false; -#endif - block->next = head.next; block->prev = &head; head.next = block; @@ -233,8 +228,9 @@ void *Z_Malloc2(size_t size, INT32 tag, void *user, INT32 alignbits, block->size = sizeof (memblock_t) + size; block->realsize = size; -#ifdef VALGRIND_CREATE_MEMPOOL +#ifdef HAVE_VALGRIND VALGRIND_CREATE_MEMPOOL(block, size, Z_calloc); + Z_calloc = false; #endif block->id = ZONEID; @@ -266,7 +262,7 @@ void *Z_Malloc2(size_t size, INT32 tag, void *user, INT32 alignbits, */ void *Z_Calloc2(size_t size, INT32 tag, void *user, INT32 alignbits, const char *file, INT32 line) { -#ifdef VALGRIND_MEMPOOL_ALLOC +#ifdef HAVE_VALGRIND Z_calloc = true; #endif return memset(Z_Malloc2 (size, tag, user, alignbits, file, line), 0, size); @@ -435,13 +431,12 @@ void Z_CheckHeap(INT32 i) CONS_Debug(DBG_MEMORY, "block %u owned by %s:%d\n", blocknumon, block->ownerfile, block->ownerline); #endif -#ifdef VALGRIND_MEMPOOL_EXISTS - if (!VALGRIND_MEMPOOL_EXISTS(block)) +#ifdef HAVE_VALGRIND + if (RUNNING_ON_VALGRIND && !VALGRIND_MEMPOOL_EXISTS(block)) { I_Error("Z_CheckHeap %d: block %u" "(owned by %s:%d)" " should not exist", i, blocknumon, - " should not exist", i, blocknumon, block->ownerfile, block->ownerline ); } @@ -470,9 +465,6 @@ void Z_CheckHeap(INT32 i) block->ownerfile, block->ownerline ); } -#ifdef VALGRIND_MAKE_MEM_DEFINED - VALGRIND_MAKE_MEM_DEFINED(hdr, sizeof *hdr); -#endif if (block->id != ZONEID) { I_Error("Z_CheckHeap %d: block %u"