Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Hanicef/SRB2Classic
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Jisk/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
118 results
Show changes
Commits on Source (21)
......@@ -2158,14 +2158,14 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if(increment > 0) // Going up!
{
newvalue++;
if (newvalue == NUMMAPS)
if (newvalue == numgamemaps)
newvalue = 0;
}
else // Going down!
{
newvalue--;
if (newvalue == -1)
newvalue = NUMMAPS-1;
newvalue = numgamemaps-1;
}
if (newvalue == oldvalue)
......@@ -2173,7 +2173,6 @@ void CV_AddValue(consvar_t *var, INT32 increment)
if(!mapheaderinfo[newvalue])
continue; // Don't allocate the header. That just makes memory usage skyrocket.
} while (newvalue != oldvalue && !M_CanShowLevelInList(newvalue, gt));
var->value = newvalue + 1;
......
......@@ -952,13 +952,7 @@ void D_StartTitle(void)
if (server)
{
char mapname[6];
strlcpy(mapname, G_BuildMapName(spstage_start), sizeof (mapname));
strlwr(mapname);
mapname[5] = '\0';
COM_BufAddText(va("map %s\n", mapname));
COM_BufAddText(va("map %s\n", G_BuildMapName(spstage_start)));
}
}
......@@ -1377,6 +1371,8 @@ void D_SRB2Main(void)
CONS_Printf("Z_Init(): Init zone memory allocation daemon. \n");
Z_Init();
G_InitMaps();
clientGamedata = M_NewGameDataStruct();
serverGamedata = M_NewGameDataStruct();
......@@ -1690,7 +1686,7 @@ void D_SRB2Main(void)
{
pstartmap = bootmap;
if (pstartmap < 1 || pstartmap > NUMMAPS)
if (pstartmap < 1 || pstartmap > numgamemaps)
I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
else
{
......@@ -1737,7 +1733,7 @@ void D_SRB2Main(void)
if (server && !M_CheckParm("+map"))
{
// Prevent warping to nonexistent levels
if (W_CheckNumForName(G_BuildMapName(pstartmap)) == LUMPERROR)
if (!G_MapFileExists(G_BuildMapName(pstartmap)))
I_Error("Could not warp to %s (map not found)\n", G_BuildMapName(pstartmap));
else
{
......
......@@ -168,7 +168,7 @@ void clear_levels(void)
// 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)
for (i = 0; i < numgamemaps; ++i)
{
if (!mapheaderinfo[i] || i == (tutorialmap-1))
continue;
......@@ -1352,6 +1352,39 @@ void readgametype(MYFILE *f, char *gtname)
CONS_Printf("Added gametype %s\n", Gametype_Names[newgtidx]);
}
static INT32 ParseNextLevelName(const char *name)
{
if (fastcmp(name, "TITLE")) return NEXTMAP_TITLE;
else if (fastcmp(name, "EVALUATION")) return NEXTMAP_EVALUATION;
else if (fastcmp(name, "CREDITS")) return NEXTMAP_CREDITS;
else if (fastcmp(name, "ENDING")) return NEXTMAP_ENDING;
else
{
// Support using the actual map name,
// i.e., Nextlevel = AB, Nextlevel = FZ, etc.
// Convert to map number
return G_GetMapNumber(name);
}
}
static INT32 ConvertLevelHeaderMapNum(INT32 mapnum)
{
switch (mapnum)
{
case 1100:
return NEXTMAP_TITLE;
case 1101:
return NEXTMAP_EVALUATION;
case 1102:
return NEXTMAP_CREDITS;
case 1103:
return NEXTMAP_ENDING;
default:
return mapnum;
}
}
void readlevelheader(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
......@@ -1574,33 +1607,17 @@ void readlevelheader(MYFILE *f, INT32 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]);
i = ConvertLevelHeaderMapNum(i);
if (i == 0)
i = ParseNextLevelName(word2);
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]);
i = ConvertLevelHeaderMapNum(i);
if (i == 0)
i = ParseNextLevelName(word2);
mapheaderinfo[num-1]->marathonnext = (INT16)i;
}
......@@ -2015,8 +2032,7 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
}
else if (fastcmp(word, "MUSIC"))
{
strncpy(cutscenes[num]->scene[scenenum].musswitch, word2, 7);
cutscenes[num]->scene[scenenum].musswitch[6] = 0;
strlcpy(cutscenes[num]->scene[scenenum].musswitch, word2, MAX_MUSIC_NAME+1);
}
else if (fastcmp(word, "MUSICTRACK"))
{
......@@ -2277,8 +2293,7 @@ static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum)
}
else if (fastcmp(word, "MUSIC"))
{
strncpy(textprompts[num]->page[pagenum].musswitch, word2, 7);
textprompts[num]->page[pagenum].musswitch[6] = 0;
strlcpy(textprompts[num]->page[pagenum].musswitch, word2, MAX_MUSIC_NAME+1);
}
else if (fastcmp(word, "MUSICTRACK"))
{
......@@ -2601,8 +2616,7 @@ void readmenu(MYFILE *f, INT32 num)
}
else if (fastcmp(word, "MUSIC"))
{
strncpy(menupres[num].musname, word2, 7);
menupres[num].musname[6] = 0;
strlcpy(menupres[num].musname, word2, MAX_MUSIC_NAME+1);
titlechanged = true;
}
else if (fastcmp(word, "MUSICTRACK"))
......@@ -3016,12 +3030,10 @@ void reademblemdata(MYFILE *f, INT32 num)
emblemlocations[num-1].tag = (INT16)value;
else if (fastcmp(word, "MAPNUM"))
{
// 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]);
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
emblemlocations[num-1].level = (INT16)value;
}
......@@ -3263,6 +3275,12 @@ void readunlockable(MYFILE *f, INT32 num)
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
i = M_MapNumber(word2[0], word2[1]);
else if (unlockables[num].type == SECRET_WARP)
{
INT16 mapnum = G_GetMapNumber(word2);
if (mapnum)
i = mapnum;
}
unlockables[num].variable = (INT16)i;
}
......@@ -3336,15 +3354,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
PARAMCHECK(1);
ty = UC_MAPVISITED + offset;
// 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
// Convert to map number
re = G_GetMapNumber(params[1]);
if (!re)
re = atoi(params[1]);
if (re <= 0 || re > NUMMAPS)
if (re <= 0 || re > numgamemaps)
{
deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", re, numgamemaps);
return;
}
}
......@@ -3356,15 +3373,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
ty = UC_MAPSCORE + offset;
re = atoi(params[2]);
// 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
// Convert to map number
x1 = G_GetMapNumber(params[1]);
if (!x1)
x1 = (INT16)atoi(params[1]);
if (x1 <= 0 || x1 > NUMMAPS)
if (x1 <= 0 || x1 > numgamemaps)
{
deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", re, numgamemaps);
return;
}
}
......@@ -3391,15 +3407,14 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
else
re = atoi(params[i]);
// 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
// Convert to map number
x1 = G_GetMapNumber(params[1]);
if (!x1)
x1 = (INT16)atoi(params[1]);
if (x1 <= 0 || x1 > NUMMAPS)
if (x1 <= 0 || x1 > numgamemaps)
{
deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", re, numgamemaps);
return;
}
......@@ -3609,9 +3624,9 @@ void readmaincfg(MYFILE *f)
// 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
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
spstage_start = spmarathon_start = (INT16)value;
......@@ -3622,9 +3637,9 @@ void readmaincfg(MYFILE *f)
// 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
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
spmarathon_start = (INT16)value;
......@@ -3635,9 +3650,9 @@ void readmaincfg(MYFILE *f)
// 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
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
sstage_start = (INT16)value;
......@@ -3649,9 +3664,9 @@ void readmaincfg(MYFILE *f)
// 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
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
smpstage_start = (INT16)value;
......@@ -3743,9 +3758,9 @@ void readmaincfg(MYFILE *f)
// 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
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
titlemap = (INT16)value;
......@@ -3919,13 +3934,12 @@ void readmaincfg(MYFILE *f)
// 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
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
bootmap = (INT16)value;
//titlechanged = true;
}
else if (fastcmp(word, "STARTCHAR"))
{
......@@ -3938,9 +3952,9 @@ void readmaincfg(MYFILE *f)
// 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
if (G_IsValidMapName(word2))
value = G_GetMapNumber(word2);
if (!value)
value = get_number(word2);
tutorialmap = (INT16)value;
......
......@@ -372,18 +372,32 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
}
else if (fastcmp(word, "LEVEL"))
{
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.
boolean is_valid_level = false;
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
i = M_MapNumber(word2[0], word2[1]);
if (G_IsValidMapName(word2))
{
INT16 mapnum = G_GetMapNumber(word2);
if (mapnum != 0)
{
i = mapnum;
is_valid_level = true;
}
}
else
{
is_valid_level = true;
}
if (i > 0 && i <= NUMMAPS)
if (!is_valid_level)
{
deh_warning("Unknown level %s", word2);
ignorelines(f);
}
else if (i > 0 && i <= numgamemaps)
readlevelheader(f, i);
else
{
deh_warning("Level number %d out of range (1 - %d)", i, NUMMAPS);
deh_warning("Level number %d out of range (1 - %d)", i, numgamemaps);
ignorelines(f);
}
}
......
......@@ -32,23 +32,6 @@
// used in the lumps of the WAD files.
//
// Lump order in a map WAD: each map needs a couple of lumps
// to provide a complete scene geometry description.
enum
{
ML_LABEL, // A separator, name, MAPxx
ML_THINGS, // Enemies, rings, monitors, scenery, etc.
ML_LINEDEFS, // Linedefs, from editing
ML_SIDEDEFS, // Sidedefs, from editing
ML_VERTEXES, // Vertices, edited and BSP splits generated
ML_SEGS, // Linesegs, from linedefs split by BSP
ML_SSECTORS, // Subsectors, list of linesegs
ML_NODES, // BSP nodes
ML_SECTORS, // Sectors, from editing
ML_REJECT, // LUT, sector-sector visibility
ML_BLOCKMAP, // LUT, motion clipping, walls/grid element
};
// Extra flag for objects.
#define MTF_EXTRA 1
......@@ -223,6 +206,4 @@ typedef struct
#define ZSHIFT 4
#define NUMMAPS 1035
#endif // __DOOMDATA__
......@@ -253,6 +253,19 @@ extern char logfilename[1024];
#define MAXSKINS 255
#define MAXCHARACTERSLOTS (MAXSKINS * 3) // Should be higher than MAXSKINS.
#define MAXMAPS 16386
#define MAX_MAP_NAME_SIZE 256 // This is an arbitrary limit to prevent exceedingly long map names.
#define NEXTMAP_TITLE (MAXMAPS)
#define NEXTMAP_EVALUATION (MAXMAPS+2)
#define NEXTMAP_CREDITS (MAXMAPS+3)
#define NEXTMAP_ENDING (MAXMAPS+4)
#define NUM_NEXTMAPS 4
#define NUMBASEMAPS 1035 // MAP01 to MAPZZ
#define COLORRAMPSIZE 16
#define MAXCOLORNAME 32
#define NUMCOLORFREESLOTS 1024
......@@ -467,6 +480,8 @@ extern skincolor_t skincolors[MAXSKINCOLORS];
#define MUSICRATE 1000 // sound timing is calculated by milliseconds
#define MAX_MUSIC_NAME 64
#define RING_DIST 512*FRACUNIT // how close you need to be to a ring to attract it
#define PUSHACCEL (2*FRACUNIT) // Acceleration for MF2_SLIDEPUSH items.
......
......@@ -31,7 +31,7 @@
// Selected by user.
extern INT16 gamemap;
extern char mapmusname[7];
extern char mapmusname[MAX_MUSIC_NAME+1];
extern UINT16 mapmusflags;
extern UINT32 mapmusposition;
#define MUSIC_TRACKMASK 0x0FFF // ----************
......@@ -79,6 +79,7 @@ extern boolean usedCheats;
extern boolean disableSpeedAdjust; // Don't alter the duration of player states if true
extern boolean imcontinuing; // Temporary flag while continuing
extern boolean metalrecording;
extern boolean replacedcurrentmap;
#define ATTACKING_NONE 0
#define ATTACKING_RECORD 1
......@@ -175,7 +176,7 @@ typedef struct
UINT16 textxpos;
UINT16 textypos;
char musswitch[7];
char musswitch[MAX_MUSIC_NAME+1];
UINT16 musswitchflags;
UINT32 musswitchposition;
......@@ -215,7 +216,7 @@ typedef struct
UINT16 ycoord[MAX_PROMPT_PICS]; // gfx
UINT16 picduration[MAX_PROMPT_PICS];
char musswitch[7];
char musswitch[MAX_MUSIC_NAME+1];
UINT16 musswitchflags;
UINT8 musicloop;
......@@ -277,6 +278,23 @@ extern struct quake
fixed_t radius, intensity;
} quake;
typedef struct
{
UINT32 hash;
size_t length;
char *chars;
} mapname_t;
typedef struct
{
mapname_t name;
UINT32 lumpnum;
char *thumbnail;
char *thumbnail_wide;
char *music;
char *metal_replay;
} gamemap_t;
// NiGHTS grades
typedef struct
{
......@@ -296,22 +314,22 @@ typedef struct
typedef struct
{
// The original eight, plus one.
char lvlttl[21+1]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway)
char subttl[32+1]; ///< Subtitle for level
UINT8 actnum; ///< Act number or 0 for none.
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[32+1]; ///< Keywords separated by space to search for. 32 characters.
char musname[7]; ///< Music track to play. "" 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.
char forcecharacter[16+1]; ///< (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.
INT16 skynum; ///< Sky number 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.
char lvlttl[21+1]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway)
char subttl[32+1]; ///< Subtitle for level
UINT8 actnum; ///< Act number or 0 for none.
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[32+1]; ///< Keywords separated by space to search for. 32 characters.
char musname[MAX_MUSIC_NAME+1]; ///< Music track to play. "" 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.
char forcecharacter[16+1]; ///< (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.
INT16 skynum; ///< Sky number 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.
// Extra information.
char interscreen[8+1]; ///< 320x200 patch to display at intermission.
......@@ -351,10 +369,10 @@ typedef struct
nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful.
// Music stuff.
UINT32 musinterfadeout; ///< Fade out level music on intermission screen in milliseconds
char musintername[7]; ///< Intermission screen music.
UINT32 musinterfadeout; ///< Fade out level music on intermission screen in milliseconds
char musintername[MAX_MUSIC_NAME+1]; ///< Intermission screen music.
char muspostbossname[7]; ///< Post-bossdeath music.
char muspostbossname[MAX_MUSIC_NAME+1]; ///< Post-bossdeath music.
UINT16 muspostbosstrack; ///< Post-bossdeath track.
UINT32 muspostbosspos; ///< Post-bossdeath position
UINT32 muspostbossfadein; ///< Post-bossdeath fade-in milliseconds.
......@@ -389,7 +407,11 @@ typedef struct
#define LF2_NOVISITNEEDED 16 ///< Available in time attack/nights mode without visiting the level
#define LF2_WIDEICON 32 ///< If you're in a circumstance where it fits, use a wide map icon
extern mapheader_t* mapheaderinfo[NUMMAPS];
extern mapheader_t* mapheaderinfo[MAXMAPS];
extern gamemap_t gamemaps[MAXMAPS];
extern UINT16 numgamemaps;
// Gametypes
#define NUMGAMETYPEFREESLOTS 128
......
......@@ -3972,7 +3972,7 @@ void F_EndCutScene(void)
F_StartGameEvaluation();
else if (cutnum == introtoplay-1)
D_StartTitle();
else if (nextmap < 1100-1)
else if (!G_IsGameEndMap(nextmap+1))
G_NextLevel();
else
G_EndGame();
......
......@@ -858,6 +858,8 @@ lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders)
if (strstr(fullname, path))
fullname += strlen(path) + 1;
size_t fullnamelength = strlen(fullname);
// Get the 8-character long lump name.
trimname = strrchr(fullname, '/');
if (trimname)
......@@ -869,21 +871,30 @@ lumpinfo_t *getdirectoryfiles(const char *path, UINT16 *nlmp, UINT16 *nfolders)
{
char *dotpos = strrchr(trimname, '.');
if (dotpos == NULL)
dotpos = fullname + strlen(fullname);
dotpos = fullname + fullnamelength;
strncpy(lump_p->name, trimname, min(8, dotpos - trimname));
// The name of the file, without the extension.
lump_p->longname = Z_Calloc(dotpos - trimname + 1, PU_STATIC, NULL);
strlcpy(lump_p->longname, trimname, dotpos - trimname + 1);
lump_p->longnamelength = strlen(lump_p->longname);
}
else
else {
lump_p->longname = Z_Calloc(1, PU_STATIC, NULL);
lump_p->longnamelength = 0;
}
// The complete name of the file, with its extension,
// excluding the path of the directory where it resides.
lump_p->fullname = Z_StrDup(fullname);
lump_p->hash = quickncasehash(lump_p->name, 8);
lump_p->hash.name = W_HashLumpName(lump_p->name);
lump_p->hash.fullname = W_HashLumpName(lump_p->fullname);
lump_p->hash.longname = W_HashLumpName(lump_p->longname);
lump_p->namelength = strlen(lump_p->name);
lump_p->fullnamelength = fullnamelength;
lump_p++;
i++;
......
......@@ -46,7 +46,7 @@ boolean nodrawers; // for comparative timing purposes
boolean noblit; // for comparative timing purposes
tic_t demostarttime; // for comparative timing purposes
static char demoname[64];
static char demoname[512];
boolean demorecording;
boolean demoplayback;
boolean titledemo; // Title Screen demo can be cancelled by any key
......@@ -1417,15 +1417,14 @@ void G_WriteMetalTic(mobj_t *metal)
//
void G_RecordDemo(const char *name)
{
INT32 maxsize;
strlcpy(demoname, name, sizeof(demoname));
strcpy(demoname, name);
strcat(demoname, ".lmp");
maxsize = 1024*1024;
FIL_ForceExtension(demoname, ".lmp");
INT32 maxsize = 1024*1024;
if (M_CheckParm("-maxdemo") && M_IsNextParm())
maxsize = atoi(M_GetNextParm()) * 1024;
// if (demobuffer)
// free(demobuffer);
demo_p = NULL;
demobuffer = malloc(maxsize);
demoend = demobuffer + maxsize;
......@@ -2666,7 +2665,7 @@ void G_DoPlayMetal(void)
thinker_t *th;
// it's an internal demo
if ((l = W_CheckNumForName(va("%sMS",G_BuildMapName(gamemap)))) == LUMPERROR)
if ((l = W_CheckNumForName(G_GetMapMetalSonicReplay(gamemap))) == LUMPERROR)
{
CONS_Alert(CONS_WARNING, M_GetText("No bot recording for this map.\n"));
return;
......@@ -2792,7 +2791,7 @@ ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(boolean kill)
{
WRITEUINT8(demo_p, (kill) ? METALDEATH : DEMOMARKER); // add the demo end (or metal death) marker
WriteDemoChecksum();
sprintf(demoname, "%sMS.LMP", G_BuildMapName(gamemap));
snprintf(demoname, sizeof(demoname), "%s.lmp", G_GetMapMetalSonicReplay(gamemap));
saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
}
free(demobuffer);
......
This diff is collapsed.
......@@ -114,8 +114,18 @@ INT32 Joy2Axis(joyaxis_e axissel);
#define SLOWTURNTICS (6)
// build an internal map name MAPxx from map number
const char *G_BuildClassicMapName(INT32 map);
const char *G_BuildMapName(INT32 map);
void G_InitMaps(void);
UINT16 G_GetMapNumber(const char *name);
UINT16 G_GetMapNumberForNextMap(const char *name);
UINT16 G_AddMap(const char *name, UINT32 lumpnum);
lumpnum_t G_GetMapLumpnum(const char *name);
boolean G_MapFileExists(const char *name);
boolean G_IsValidMapName(const char *name);
extern INT16 ticcmd_oldangleturn[2];
extern boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player
extern mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object?
......@@ -142,6 +152,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps);
void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer,
boolean skipprecutscene, boolean FLS);
char *G_BuildMapTitle(INT32 mapnum);
const char *G_GetMapThumbnail(INT16 map);
const char *G_GetMapThumbnailWide(INT16 map);
const char *G_GetDefaultMapMusic(INT16 map);
const char *G_GetMapMetalSonicReplay(INT16 map);
boolean G_IsGameEndMap(INT16 mapnum);
struct searchdim
{
......
......@@ -1396,7 +1396,7 @@ static int lib_pPlayJingleMusic(lua_State *L)
{
player_t *player = NULL;
const char *musnamearg = luaL_checkstring(L, 2);
char musname[7], *p = musname;
char musname[MAX_MUSIC_NAME+1], *p = musname;
UINT16 musflags = luaL_optinteger(L, 3, 0);
boolean looping = lua_opttrueboolean(L, 4);
jingletype_t jingletype = luaL_optinteger(L, 5, JT_OTHER);
......@@ -1411,8 +1411,7 @@ static int lib_pPlayJingleMusic(lua_State *L)
if (jingletype >= NUMJINGLES)
return luaL_error(L, "jingletype %d out of range (0 - %d)", jingletype, NUMJINGLES-1);
musname[6] = '\0';
strncpy(musname, musnamearg, 6);
strlcpy(musname, musnamearg, MAX_MUSIC_NAME+1);
while (*p) {
*p = tolower(*p);
......@@ -3988,10 +3987,53 @@ static int lib_gSetUsedCheats(lua_State *L)
return 0;
}
static int GetMapNameOrNumber(lua_State *L, int idx)
{
if (lua_type(L, idx) == LUA_TSTRING)
{
const char *mapname = luaL_checkstring(L, idx);
INT16 mapnum = G_GetMapNumber(mapname);
if (mapnum == 0)
{
return luaL_error(L,
"%s is not a valid game map.",
mapname
);
}
return mapnum;
}
else
return luaL_checkinteger(L, idx);
}
static int GetNextMapNameOrNumber(lua_State *L, int idx)
{
if (lua_type(L, idx) == LUA_TSTRING)
{
const char *mapname = luaL_checkstring(L, idx);
INT16 mapnum = G_GetMapNumberForNextMap(mapname);
if (mapnum == 0)
{
return luaL_error(L,
"%s is not a valid game map.",
mapname
);
}
return mapnum;
}
else
return luaL_checkinteger(L, idx);
}
static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
{
if (ISINLEVEL)
return luaL_optinteger(L, idx, gamemap);
{
if (!lua_isnoneornil(L, idx))
return GetMapNameOrNumber(L, idx);
else
return gamemap;
}
else
{
if (lua_isnoneornil(L, idx))
......@@ -4002,14 +4044,21 @@ static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
);
}
else
return luaL_checkinteger(L, idx);
return GetMapNameOrNumber(L, idx);
}
}
static int lib_gBuildMapName(lua_State *L)
{
INT32 map = Lcheckmapnumber(L, 1, "G_BuildMapName");
//HUDSAFE
if (map < 1 || map > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
numgamemaps
);
}
lua_pushstring(L, G_BuildMapName(map));
return 1;
}
......@@ -4018,12 +4067,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 > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
NUMMAPS
numgamemaps
);
}
name = G_BuildMapTitle(map);
......@@ -4143,6 +4192,36 @@ static int lib_gFindMapByNameOrCode(lua_State *L)
return 1;
}
static int lib_gGetMapThumbnail(lua_State *L)
{
INT32 map = Lcheckmapnumber(L, 1, "G_GetMapThumbnail");
if (map < 1 || map > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
numgamemaps
);
}
lua_pushstring(L, G_GetMapThumbnail(map));
return 1;
}
static int lib_gGetMapThumbnailWide(lua_State *L)
{
INT32 map = Lcheckmapnumber(L, 1, "G_GetMapThumbnailWide");
if (map < 1 || map > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
map,
numgamemaps
);
}
lua_pushstring(L, G_GetMapThumbnailWide(map));
return 1;
}
static int lib_gDoReborn(lua_State *L)
{
INT32 playernum = luaL_checkinteger(L, 1);
......@@ -4176,7 +4255,19 @@ static int lib_gSetCustomExitVars(lua_State *L)
if (n >= 1)
{
nextmapoverride = (INT16)luaL_optinteger(L, 1, 0);
if (!lua_isnoneornil(L, 1))
{
INT16 mapnum = GetNextMapNameOrNumber(L, 1);
if (mapnum < 1 || (mapnum > numgamemaps && !G_IsGameEndMap(mapnum)))
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
mapnum,
numgamemaps
);
}
nextmapoverride = mapnum;
}
skipstats = (INT16)luaL_optinteger(L, 2, 0);
nextgametype = (INT16)luaL_optinteger(L, 3, -1);
}
......@@ -4204,9 +4295,22 @@ static int lib_gExitLevel(lua_State *L)
static int lib_gIsSpecialStage(lua_State *L)
{
INT32 mapnum = luaL_optinteger(L, 1, gamemap);
//HUDSAFE
INT32 mapnum;
INLEVEL
if (!lua_isnoneornil(L, 1))
{
mapnum = GetMapNameOrNumber(L, 1);
if (mapnum < 1 || mapnum > numgamemaps)
{
return luaL_error(L,
"map number %d out of range (1 - %d)",
mapnum,
numgamemaps
);
}
}
else
mapnum = gamemap;
lua_pushboolean(L, G_IsSpecialStage(mapnum));
return 1;
}
......@@ -4623,6 +4727,8 @@ static luaL_Reg lib[] = {
{"G_BuildMapTitle",lib_gBuildMapTitle},
{"G_FindMap",lib_gFindMap},
{"G_FindMapByNameOrCode",lib_gFindMapByNameOrCode},
{"G_GetMapThumbnail",lib_gGetMapThumbnail},
{"G_GetMapThumbnailWide",lib_gGetMapThumbnailWide},
{"G_DoReborn",lib_gDoReborn},
{"G_SetCustomExitVars",lib_gSetCustomExitVars},
{"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished},
......
......@@ -1128,16 +1128,16 @@ static void update_music_name(struct MusicChange *musicchange)
size_t length;
const char * new = lua_tolstring(gL, -6, &length);
if (length < 7)
if (length <= MAX_MUSIC_NAME)
{
strcpy(musicchange->newname, new);
lua_pushvalue(gL, -6);/* may as well keep it for next call */
}
else
{
memcpy(musicchange->newname, new, 6);
musicchange->newname[6] = '\0';
lua_pushlstring(gL, new, 6);
memcpy(musicchange->newname, new, MAX_MUSIC_NAME);
musicchange->newname[MAX_MUSIC_NAME] = '\0';
lua_pushlstring(gL, new, MAX_MUSIC_NAME);
}
lua_replace(gL, -7);
......
......@@ -2696,32 +2696,22 @@ static int vector3_get(lua_State *L)
static int lib_getMapheaderinfo(lua_State *L)
{
// i -> mapheaderinfo[i-1]
//int field;
lua_settop(L, 2);
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)
if (i >= numgamemaps)
return 0;
LUA_PushUserdata(L, mapheaderinfo[i], META_MAPHEADER);
//CONS_Printf(mapheaderinfo[i]->lvlttl);
return 1;
}/*
field = luaL_checkoption(L, 1, NULL, array_opt);
switch(field)
{
case 0: // iterate
lua_pushcfunction(L, lib_iterateSubsectors);
return 1;
}*/
}
return 0;
}
static int lib_nummapheaders(lua_State *L)
{
lua_pushinteger(L, NUMMAPS);
lua_pushinteger(L, numgamemaps);
return 1;
}
......
......@@ -78,7 +78,7 @@ void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
memcpy(dest->mapvisited, src->mapvisited, sizeof(dest->mapvisited));
// Main records
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < MAXMAPS; ++i)
{
if (!src->mainrecords[i])
continue;
......@@ -90,7 +90,7 @@ void M_CopyGameData(gamedata_t *dest, gamedata_t *src)
}
// Nights records
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < MAXMAPS; ++i)
{
if (!src->nightsrecords[i] || !src->nightsrecords[i]->nummares)
continue;
......@@ -577,7 +577,7 @@ UINT8 M_GotHighEnoughScore(INT32 tscore, gamedata_t *data)
INT32 mscore = 0;
INT32 i;
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < numgamemaps; ++i)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
......@@ -595,7 +595,7 @@ UINT8 M_GotLowEnoughTime(INT32 tictime, gamedata_t *data)
INT32 curtics = 0;
INT32 i;
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < numgamemaps; ++i)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
......@@ -613,7 +613,7 @@ UINT8 M_GotHighEnoughRings(INT32 trings, gamedata_t *data)
INT32 mrings = 0;
INT32 i;
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < numgamemaps; ++i)
{
if (!mapheaderinfo[i] || !(mapheaderinfo[i]->menuflags & LF2_RECORDATTACK))
continue;
......
......@@ -203,9 +203,9 @@ typedef struct
boolean unlocked[MAXUNLOCKABLES];
// TIME ATTACK DATA
recorddata_t *mainrecords[NUMMAPS];
nightsdata_t *nightsrecords[NUMMAPS];
UINT8 mapvisited[NUMMAPS];
recorddata_t *mainrecords[MAXMAPS];
nightsdata_t *nightsrecords[MAXMAPS];
UINT8 mapvisited[MAXMAPS];
// # OF TIMES THE GAME HAS BEEN BEATEN
UINT32 timesBeaten;
......
......@@ -419,7 +419,7 @@ consvar_t cv_showfocuslost = CVAR_INIT ("showfocuslost", "Yes", CV_SAVE, CV_YesN
static CV_PossibleValue_t map_cons_t[] = {
{1,"MIN"},
{NUMMAPS, "MAX"},
{MAXMAPS, "MAX"},
{0,NULL}
};
consvar_t cv_nextmap = CVAR_INIT ("nextmap", "1", CV_HIDEN|CV_CALL, map_cons_t, Nextmap_OnChange);
......@@ -2246,9 +2246,9 @@ void Nextmap_OnChange(void)
{
gamedata_t *data = clientGamedata;
char *leveltitle;
char tabase[256];
char tabase[4096];
#ifdef OLDNREPLAYNAME
char tabaseold[256];
char tabaseold[4096];
#endif
short i;
boolean active;
......@@ -2516,13 +2516,13 @@ void M_InitMenuPresTables(void)
menupres[i].muslooping = true;
}
if (i == MN_SP_TIMEATTACK)
strncpy(menupres[i].musname, "_recat", 7);
strlcpy(menupres[i].musname, "_recat", MAX_MUSIC_NAME+1);
else if (i == MN_SP_NIGHTSATTACK)
strncpy(menupres[i].musname, "_nitat", 7);
strlcpy(menupres[i].musname, "_nitat", MAX_MUSIC_NAME+1);
else if (i == MN_SP_MARATHON)
strncpy(menupres[i].musname, "spec8", 6);
strlcpy(menupres[i].musname, "spec8", MAX_MUSIC_NAME+1);
else if (i == MN_SP_PLAYER || i == MN_SR_PLAYER)
strncpy(menupres[i].musname, "_chsel", 7);
strlcpy(menupres[i].musname, "_chsel", MAX_MUSIC_NAME+1);
else if (i == MN_SR_SOUNDTEST)
{
*menupres[i].musname = '\0';
......@@ -2548,7 +2548,7 @@ typedef boolean (*menutree_iterator)(UINT32, INT32, INT32 *, void **, boolean fr
// a single input. Maybe someday use this struct program-wide.
typedef struct
{
char musname[7];
char musname[MAX_MUSIC_NAME+1];
UINT16 mustrack;
boolean muslooping;
} menupresmusic_t;
......@@ -2759,8 +2759,7 @@ void M_ChangeMenuMusic(const char *defaultmusname, boolean defaultmuslooping)
if (!defaultmusname)
defaultmusname = "";
strncpy(defaultmusic.musname, defaultmusname, 7);
defaultmusic.musname[6] = 0;
strlcpy(defaultmusic.musname, defaultmusname, MAX_MUSIC_NAME+1);
defaultmusic.mustrack = 0;
defaultmusic.muslooping = defaultmuslooping;
......@@ -5135,7 +5134,7 @@ static INT32 M_CountLevelsToShowOnPlatter(INT32 gt)
{
INT32 mapnum, count = 0;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelOnPlatter(mapnum, gt))
count++;
......@@ -5170,7 +5169,7 @@ static boolean M_GametypeHasLevels(INT32 gt)
{
INT32 mapnum;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelOnPlatter(mapnum, gt))
return true;
......@@ -5182,11 +5181,9 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
INT32 col = 0, rows = 0;
INT32 mapIterate = 0;
INT32 headingIterate = 0;
boolean mapAddedAlready[NUMMAPS];
boolean *mapAddedAlready = Z_Calloc(numgamemaps*sizeof(boolean), PU_STATIC, NULL);
memset(mapAddedAlready, 0, sizeof mapAddedAlready);
for (mapIterate = 0; mapIterate < NUMMAPS; mapIterate++)
for (mapIterate = 0; mapIterate < numgamemaps; mapIterate++)
{
boolean forceNewRow = true;
......@@ -5202,7 +5199,7 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
continue;
}
for (headingIterate = mapIterate; headingIterate < NUMMAPS; headingIterate++)
for (headingIterate = mapIterate; headingIterate < numgamemaps; headingIterate++)
{
boolean wide = false;
......@@ -5249,6 +5246,8 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
rows++;
}
Z_Free(mapAddedAlready);
return rows;
}
......@@ -5280,11 +5279,13 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
INT32 col = 0, row = 0, startrow = 0;
INT32 mapIterate = 0; // First level of map loop -- find starting points for select headings
INT32 headingIterate = 0; // Second level of map loop -- finding maps that match mapIterate's heading.
boolean mapAddedAlready[NUMMAPS];
boolean *mapAddedAlready;
if (!numrows)
return false;
mapAddedAlready = Z_Calloc(numgamemaps*sizeof(boolean), PU_STATIC, NULL);
if (levelselect.rows)
Z_Free(levelselect.rows);
levelselect.rows = NULL;
......@@ -5297,8 +5298,6 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
// done here so lsrow and lscol can be set if cv_nextmap is on the platter
lsrow = lscol = lshli = lsoffs[0] = lsoffs[1] = 0;
memset(mapAddedAlready, 0, sizeof mapAddedAlready);
if (levellistmode == LLM_CREATESERVER)
{
sprintf(levelselect.rows[0].header, "Gametype");
......@@ -5310,7 +5309,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
char_notes = NULL;
}
for (mapIterate = 0; mapIterate < NUMMAPS; mapIterate++)
for (mapIterate = 0; mapIterate < numgamemaps; mapIterate++)
{
INT32 headerRow = -1;
boolean anyAvailable = false;
......@@ -5328,7 +5327,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
continue;
}
for (headingIterate = mapIterate; headingIterate < NUMMAPS; headingIterate++)
for (headingIterate = mapIterate; headingIterate < numgamemaps; headingIterate++)
{
UINT8 actnum = 0;
boolean headingisname = false;
......@@ -5479,6 +5478,8 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
}
#endif
Z_Free(mapAddedAlready);
M_CacheLevelPlatter();
return true;
......@@ -5714,8 +5715,9 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo
}
else
{
if (W_CheckNumForName(va("%sW", G_BuildMapName(map))) != LUMPERROR)
patch = W_CachePatchName(va("%sW", G_BuildMapName(map)), PU_PATCH);
const char *thumbnail = G_GetMapThumbnailWide(map);
if (W_CheckNumForLongName(thumbnail) != LUMPERROR)
patch = W_CachePatchLongName(thumbnail, PU_PATCH);
else
patch = levselp[1][2]; // don't static to indicate that it's just a normal level
......@@ -5745,8 +5747,9 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea
}
else
{
if (W_CheckNumForName(va("%sP", G_BuildMapName(map))) != LUMPERROR)
patch = W_CachePatchName(va("%sP", G_BuildMapName(map)), PU_PATCH);
const char *thumbnail = G_GetMapThumbnail(map);
if (W_CheckNumForLongName(thumbnail) != LUMPERROR)
patch = W_CachePatchLongName(thumbnail, PU_PATCH);
else
patch = levselp[0][2]; // don't static to indicate that it's just a normal level
......@@ -6059,7 +6062,7 @@ static INT32 M_GetFirstLevelInList(INT32 gt)
{
INT32 mapnum;
for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
for (mapnum = 0; mapnum < numgamemaps; mapnum++)
if (M_CanShowLevelInList(mapnum, gt))
return mapnum + 1;
......@@ -6818,12 +6821,9 @@ static boolean M_ExitPandorasBox(void)
static void M_ChangeLevel(INT32 choice)
{
char mapname[6];
(void)choice;
const char *mapname = G_BuildMapName(cv_nextmap.value);
strlcpy(mapname, G_BuildMapName(cv_nextmap.value), sizeof (mapname));
strlwr(mapname);
mapname[5] = '\0';
(void)choice;
M_ClearMenus(true);
COM_BufAddText(va("map %s -gametype \"%s\"\n", mapname, cv_newgametype.string));
......@@ -6988,7 +6988,7 @@ static void M_LevelSelectWarp(INT32 choice)
{
(void)choice;
if (W_CheckNumForName(G_BuildMapName(cv_nextmap.value)) == LUMPERROR)
if (!G_MapFileExists(G_BuildMapName(cv_nextmap.value)))
{
CONS_Alert(CONS_WARNING, "Internal game map '%s' not found\n", G_BuildMapName(cv_nextmap.value));
return;
......@@ -8155,7 +8155,7 @@ static void M_SinglePlayerMenu(INT32 choice)
if (mapheaderinfo[spmarathon_start-1]
&& !mapheaderinfo[spmarathon_start-1]->marathonnext
&& (mapheaderinfo[spmarathon_start-1]->nextlevel == spmarathon_start
|| mapheaderinfo[spmarathon_start-1]->nextlevel >= 1100))
|| G_IsGameEndMap(mapheaderinfo[spmarathon_start-1]->nextlevel)))
{
SP_MainMenu[spmarathon].status = IT_NOTHING|IT_DISABLED; // Hide and disable the Marathon Run option...
// ...and lower the above options' display positions by 8 pixels to close the gap
......@@ -8358,7 +8358,7 @@ static void M_DrawLoadGameData(void)
#ifdef PERFECTSAVE // disabled on request
else if ((savegameinfo[savetodraw].skinnum == 1)
&& (savegameinfo[savetodraw].lives == 99)
&& (savegameinfo[savetodraw].gamemap & 8192)
&& (savegameinfo[savetodraw].flags & SAVE_GAME_COMPLETE_BIT)
&& (savegameinfo[savetodraw].numgameovers == 0)
&& (savegameinfo[savetodraw].numemeralds == ((1<<7) - 1))) // perfect save
{
......@@ -8414,11 +8414,11 @@ static void M_DrawLoadGameData(void)
else
{
patch_t *patch;
if (savegameinfo[savetodraw].gamemap & 8192)
if (savegameinfo[savetodraw].flags & SAVE_GAME_COMPLETE_BIT)
patch = savselp[3];
else
{
lumpnum_t lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName((savegameinfo[savetodraw].gamemap) & 8191)));
lumpnum_t lumpnum = W_CheckNumForLongName(G_GetMapThumbnail(savegameinfo[savetodraw].gamemap));
if (lumpnum != LUMPERROR)
patch = W_CachePatchNum(lumpnum, PU_PATCH);
else
......@@ -8433,7 +8433,7 @@ static void M_DrawLoadGameData(void)
V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME");
else if (savegameinfo[savetodraw].lives == -666)
V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "CAN'T LOAD!");
else if (savegameinfo[savetodraw].gamemap & 8192)
else if (savegameinfo[savetodraw].flags & SAVE_GAME_COMPLETE_BIT)
V_DrawRightAlignedThinString(x + 79, y, V_GREENMAP, "CLEAR!");
else
V_DrawRightAlignedThinString(x + 79, y, V_YELLOWMAP, savegameinfo[savetodraw].levelname);
......@@ -8651,7 +8651,7 @@ static void M_LoadSelect(INT32 choice)
// This slot is empty, so start a new game here.
M_NewGame();
}
else if (savegameinfo[saveSlotSelected-1].gamemap & 8192) // Completed
else if (savegameinfo[saveSlotSelected-1].flags & SAVE_GAME_COMPLETE_BIT) // Completed
{
M_LoadGameLevelSelect(0);
}
......@@ -8677,6 +8677,7 @@ static void M_ReadSavegameInfo(UINT32 slot)
UINT8 *end_p; // buffer end point, don't read past here
UINT8 *sav_p;
INT32 fake; // Dummy variable
INT16 mapnum;
char temp[sizeof(timeattackfolder)];
char vcheck[VERSIONSIZE];
#ifdef NEWSKINSAVES
......@@ -8711,19 +8712,44 @@ static void M_ReadSavegameInfo(UINT32 slot)
CHECKPOS
fake = READINT16(sav_p);
if (((fake-1) & 8191) >= NUMMAPS) BADSAVE
#ifdef NEWMAPSAVES
if (fake == NEWMAPSAVES)
{
char mapname[MAX_MAP_NAME_SIZE+1];
READSTRINGN(sav_p, mapname, MAX_MAP_NAME_SIZE);
savegameinfo[slot].flags = READUINT8(sav_p);
if(!mapheaderinfo[(fake-1) & 8191])
mapnum = G_GetMapNumber(mapname);
if (mapnum == 0)
BADSAVE
}
else
#endif
{
if (((fake-1) & 8191) >= NUMBASEMAPS) BADSAVE
mapnum = (fake-1) & 8191;
mapnum++;
if (fake & 8192)
savegameinfo[slot].flags = SAVE_GAME_COMPLETE_BIT;
else
savegameinfo[slot].flags = 0;
}
if(!mapheaderinfo[mapnum-1])
savegameinfo[slot].levelname[0] = '\0';
else if (V_ThinStringWidth(mapheaderinfo[(fake-1) & 8191]->lvlttl, 0) <= 78)
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl, 22);
else if (V_ThinStringWidth(mapheaderinfo[mapnum-1]->lvlttl, 0) <= 78)
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[mapnum-1]->lvlttl, 22);
else
{
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl, 15);
strlcpy(savegameinfo[slot].levelname, mapheaderinfo[mapnum-1]->lvlttl, 15);
strcat(savegameinfo[slot].levelname, "...");
}
savegameinfo[slot].gamemap = fake;
savegameinfo[slot].gamemap = mapnum;
CHECKPOS
savegameinfo[slot].numemeralds = READUINT16(sav_p)-357; // emeralds
......@@ -9538,7 +9564,8 @@ static void M_ChoosePlayer(INT32 choice)
static INT32 statsLocation;
static INT32 statsMax;
static INT16 statsMapList[NUMMAPS+1];
static INT16 *statsMapList = NULL;
static size_t statsMapLength = 0;
static void M_Statistics(INT32 choice)
{
......@@ -9546,9 +9573,17 @@ static void M_Statistics(INT32 choice)
(void)choice;
memset(statsMapList, 0, sizeof(statsMapList));
size_t num_maps = (size_t)(numgamemaps+1);
if (statsMapLength != num_maps)
{
statsMapLength = num_maps;
statsMapList = Z_Realloc(statsMapList, statsMapLength*sizeof(INT16), PU_STATIC, NULL);
}
memset(statsMapList, 0, statsMapLength*sizeof(INT16));
for (i = 0; i < NUMMAPS; i++)
for (i = 0; i < numgamemaps; i++)
{
if (!mapheaderinfo[i] || mapheaderinfo[i]->lvlttl[0] == '\0')
continue;
......@@ -9684,7 +9719,7 @@ static void M_DrawLevelStats(void)
G_TicsToMinutes(data->totalplaytime, false),
G_TicsToSeconds(data->totalplaytime)));
for (i = 0; i < NUMMAPS; i++)
for (i = 0; i < numgamemaps; i++)
{
boolean mapunfinished = false;
......@@ -9895,10 +9930,10 @@ void M_DrawTimeAttackMenu(void)
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
lumpnum = W_CheckNumForLongName(G_GetMapThumbnail(cv_nextmap.value));
if (lumpnum != LUMPERROR)
PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
PictureOfLevel = W_CachePatchLongName(G_GetMapThumbnail(cv_nextmap.value), PU_PATCH);
else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
......@@ -10160,10 +10195,10 @@ void M_DrawNightsAttackMenu(void)
M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
lumpnum = W_CheckNumForLongName(G_GetMapThumbnail(cv_nextmap.value));
if (lumpnum != LUMPERROR)
PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
PictureOfLevel = W_CachePatchLongName(G_GetMapThumbnail(cv_nextmap.value), PU_PATCH);
else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
......@@ -10328,7 +10363,8 @@ static void M_NightsAttack(INT32 choice)
static void M_ChooseNightsAttack(INT32 choice)
{
char *gpath;
const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
const char *mapname = G_BuildMapName(cv_nextmap.value);
const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen(mapname)+1;
char nameofdemo[256];
(void)choice;
emeralds = 0;
......@@ -10342,7 +10378,7 @@ static void M_ChooseNightsAttack(INT32 choice)
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, G_BuildMapName(cv_nextmap.value));
sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, mapname);
snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1]->name);
if (!cv_autorecord.value)
......@@ -10357,7 +10393,8 @@ static void M_ChooseNightsAttack(INT32 choice)
static void M_ChooseTimeAttack(INT32 choice)
{
char *gpath;
const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
const char *mapname = G_BuildMapName(cv_nextmap.value);
const size_t glen = strlen("replay")+1+strlen(timeattackfolder)+1+strlen(mapname)+1;
char nameofdemo[256];
(void)choice;
emeralds = 0;
......@@ -10371,7 +10408,7 @@ static void M_ChooseTimeAttack(INT32 choice)
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, G_BuildMapName(cv_nextmap.value));
sprintf(gpath,"replay"PATHSEP"%s"PATHSEP"%s", timeattackfolder, mapname);
snprintf(nameofdemo, sizeof nameofdemo, "%s-%s-last", gpath, skins[cv_chooseskin.value-1]->name);
if (!cv_autorecord.value)
......@@ -10704,7 +10741,7 @@ static void M_Marathon(INT32 choice)
SP_MarathonMenu[marathonplayer].status = (skinset == MAXCHARACTERSLOTS) ? IT_KEYHANDLER|IT_STRING : IT_NOTHING|IT_DISABLED;
while (mapnum < NUMMAPS)
while (mapnum < numgamemaps)
{
if (mapheaderinfo[mapnum])
{
......@@ -10714,7 +10751,7 @@ static void M_Marathon(INT32 choice)
mapnum++;
}
SP_MarathonMenu[marathoncutscenes].status = (mapnum < NUMMAPS) ? IT_CVAR|IT_STRING : IT_NOTHING|IT_DISABLED;
SP_MarathonMenu[marathoncutscenes].status = (mapnum < numgamemaps) ? IT_CVAR|IT_STRING : IT_NOTHING|IT_DISABLED;
M_ChangeMenuMusic("spec8", true);
......@@ -11616,10 +11653,10 @@ static void M_DrawServerMenu(void)
M_DrawLevelPlatterHeader(currentMenu->y + imgheight - 10 - lsheadingheight/2, (const char *)headerstr, true, false);
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
lumpnum = W_CheckNumForLongName(G_GetMapThumbnail(cv_nextmap.value));
if (lumpnum != LUMPERROR)
PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_PATCH);
PictureOfLevel = W_CachePatchLongName(G_GetMapThumbnail(cv_nextmap.value), PU_PATCH);
else
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH);
......
......@@ -159,7 +159,7 @@ typedef struct
INT16 ttloop; // # frame to loop; -1 means dont loop
UINT16 tttics; // # of tics per frame
char musname[7]; ///< Music track to play. "" for no music.
char musname[MAX_MUSIC_NAME+1]; ///< Music track to play. "" for no music.
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
boolean muslooping; ///< Loop the music
boolean musstop; ///< Don't play any music
......@@ -427,6 +427,7 @@ typedef struct
INT32 lives;
INT32 continuescore;
INT32 gamemap;
UINT8 flags;
} saveinfo_t;
extern description_t *description;
......
......@@ -2261,6 +2261,30 @@ boolean M_IsStringEmpty(const char *s)
return true;
}
const char *M_GetFilenameFromPath(const char *path)
{
const char *slash = strrchr(path, PATHSEP[0]);
if (slash)
return slash + 1;
return path;
}
const char *M_GetExtensionFromFilename(const char *filename)
{
const char *dot = strrchr(filename, '.');
if (dot)
return dot + 1;
return NULL;
}
const char *M_CheckFilenameExtension(const char *filename, const char *ext)
{
const char *dot = strrchr(filename, '.');
if (dot && (strstr(dot, ext) || strstr(dot + 1, ext)))
return dot + 1;
return NULL;
}
// Converts a string containing a whole number into an int. Returns false if the conversion failed.
boolean M_StringToNumber(const char *input, int *out)
{
......@@ -2312,3 +2336,48 @@ int M_RoundUp(double number)
return (int)number;
}
// Hashes some message using FNV-1a
#define FNV1A_OFFSET_BASIS 0x811C9DC5
#define FNV1A_PRIME 0x01000193
UINT32 FNV1a_Hash(const char *message, size_t size)
{
UINT32 hash = FNV1A_OFFSET_BASIS;
for (size_t i = 0; i < size; i++)
{
hash ^= message[i];
hash *= FNV1A_PRIME;
}
return hash;
}
UINT32 FNV1a_HashString(const char *message)
{
UINT32 hash = FNV1A_OFFSET_BASIS;
while (*message)
{
hash ^= *message;
hash *= FNV1A_PRIME;
message++;
}
return hash;
}
UINT32 FNV1a_HashLowercaseString(const char *message)
{
UINT32 hash = FNV1A_OFFSET_BASIS;
while (*message)
{
hash ^= tolower(*message);
hash *= FNV1A_PRIME;
message++;
}
return hash;
}