gzdoom/code/G_level.c
1999-02-17 00:00:00 +00:00

2330 lines
42 KiB
C

#include "d_main.h"
#include "m_alloc.h"
#include "g_level.h"
#include "g_game.h"
#include "s_sound.h"
#include "d_event.h"
#include "m_random.h"
#include "doomstat.h"
#include "wi_stuff.h"
#include "r_data.h"
#include "w_wad.h"
#include "am_map.h"
#include "c_dispch.h"
#include "z_zone.h"
#include "i_system.h"
#include "p_setup.h"
#include "p_local.h"
#include "r_sky.h"
#include "c_consol.h"
#include "f_finale.h"
#include "dstrings.h"
#include "v_video.h"
#include "st_stuff.h"
#include "hu_stuff.h"
#include "p_saveg.h"
#include "p_acs.h"
#include "d_proto.h"
#include "v_text.h"
#include "s_sndseq.h"
#include "minilzo.h"
// [RH] Output buffer size for LZO compression.
// Extra space in case uncompressable.
#define OUT_LEN(a) ((a) + (a) / 64 + 16 + 3)
static level_info_t *FindDefLevelInfo (char *mapname);
static cluster_info_t *FindDefClusterInfo (int cluster);
extern int timingdemo;
// Start time for timing demos
int starttime;
// ACS variables with world scope
int WorldVars[NUM_WORLDVARS];
extern BOOL netdemo;
BOOL savegamerestore;
extern int mousex, mousey, joyxmove, joyymove, Impulse;
extern BOOL sendpause, sendsave, sendcenterview;
void *statcopy; // for statistics driver
level_locals_t level; // info about current level
static level_pwad_info_t *wadlevelinfos;
static cluster_info_t *wadclusterinfos;
static int numwadlevelinfos = 0;
static int numwadclusterinfos = 0;
BOOL HexenHack;
static int FindWadLevelInfo (char *name)
{
int i;
for (i = 0; i < numwadlevelinfos; i++)
if (!strnicmp (name, wadlevelinfos[i].mapname, 8))
return i;
return -1;
}
static int FindWadClusterInfo (int cluster)
{
int i;
for (i = 0; i < numwadclusterinfos; i++)
if (wadclusterinfos[i].cluster == cluster)
return i;
return -1;
}
static void SetLevelDefaults (level_pwad_info_t *levelinfo)
{
memset (levelinfo, 0, sizeof(*levelinfo));
levelinfo->snapshot = NULL;
levelinfo->outsidefog = 0xff000000;
strncpy (levelinfo->fadetable, "COLORMAP", 8);
}
//
// G_ParseMapInfo
// Parses the MAPINFO lumps of all loaded WADs and generates
// data for wadlevelinfos and wadclusterinfos.
//
void G_ParseMapInfo (void)
{
int lastlump, lump;
char *mapinfo, *data;
int levelindex = -1, clusterindex = -1;
level_pwad_info_t *levelinfo;
level_pwad_info_t defaultinfo;
cluster_info_t *clusterinfo;
unsigned levelflags;
lastlump = 0;
while ((lump = W_FindLump ("MAPINFO", &lastlump)) != -1) {
mapinfo = W_CacheLumpNum (lump, PU_CACHE);
SetLevelDefaults (&defaultinfo);
while ( (data = COM_Parse (mapinfo)) ) {
if (com_token[0] == ';') {
// Handle comments from Hexen MAPINFO lumps
while (*mapinfo && *mapinfo != ';')
mapinfo++;
while (*mapinfo && *mapinfo != '\n')
mapinfo++;
continue;
}
if (!stricmp (com_token, "defaultmap")) {
// defaultmap
if (levelindex > -1) {
// Store previous level's flags
levelinfo->flags = levelflags;
}
levelflags = 0;
clusterindex = -1;
SetLevelDefaults (&defaultinfo);
levelindex = 0;
levelinfo = &defaultinfo;
mapinfo = data;
} else if (!stricmp (com_token, "map")) {
// map <MAPNAME> <Nice Name>
if (levelindex > -1) {
// Store previous level's flags
levelinfo->flags = levelflags;
}
levelflags = defaultinfo.flags;
clusterindex = -1;
data = COM_Parse (data);
if (IsNum (com_token)) {
// Handle Hexen MAPINFO map commands
int map = atoi (com_token);
sprintf (com_token, "MAP%02u", map);
SKYFLATNAME[5] = 0;
// Hexen levels are automatically nointermission
// and even lighting and no auto sound sequences
levelflags = LEVEL_NOINTERMISSION
| LEVEL_EVENLIGHTING
| LEVEL_SNDSEQTOTALCTRL;
HexenHack = true;
}
if ((levelindex = FindWadLevelInfo (com_token)) == -1)
{
levelindex = numwadlevelinfos++;
wadlevelinfos = Realloc (wadlevelinfos, sizeof(level_pwad_info_t)*numwadlevelinfos);
}
levelinfo = wadlevelinfos + levelindex;
memcpy (levelinfo, &defaultinfo, sizeof(*levelinfo));
strncpy (levelinfo->mapname, com_token, 8);
data = COM_Parse (data);
ReplaceString (&levelinfo->level_name, com_token);
mapinfo = data;
// Set up levelnum now so that the Teleport_NewMap specials
// in hexen.wad work without modification.
if (!strnicmp (levelinfo->mapname, "MAP", 3) && levelinfo->mapname[5] == 0) {
int mapnum = atoi (levelinfo->mapname + 3);
if (mapnum >= 1 && mapnum <= 99)
levelinfo->levelnum = mapnum;
}
} else if (levelindex > -1) {
if (!stricmp (com_token, "levelnum")) {
// levelnum <levelnum>
mapinfo = COM_Parse (data);
levelinfo->levelnum = atoi (com_token);
} else if (!stricmp (com_token, "next")) {
// next <MAPNAME>
mapinfo = COM_Parse (data);
if (IsNum (com_token)) {
// Handle Hexen MAPINFO next entries
int map = atoi (com_token);
sprintf (com_token, "MAP%02u", map);
}
strncpy (levelinfo->nextmap, com_token, 8);
} else if (!stricmp (com_token, "secretnext")) {
// secretnext <MAPNAME>
mapinfo = COM_Parse (data);
strncpy (levelinfo->secretmap, com_token, 8);
} else if (!stricmp (com_token, "cluster")) {
// cluster <clusternum>
mapinfo = COM_Parse (data);
levelinfo->cluster = atoi (com_token);
if (HexenHack) {
cluster_info_t *cluster = FindClusterInfo (levelinfo->cluster);
if (cluster)
cluster->flags |= CLUSTER_HUB;
}
} else if (!stricmp (com_token, "sky1")) {
// sky1 <sky1texture> <sky1scrollspeed>
mapinfo = COM_Parse (data);
strncpy (levelinfo->skypic1, com_token, 8);
mapinfo = COM_Parse (mapinfo);
if (HexenHack)
levelinfo->skyspeed1 = atoi (com_token) << 8;
else
levelinfo->skyspeed1 = (int)(atof (com_token) * 65536.0);
} else if (!stricmp (com_token, "sky2")) {
// sky2 <sky2texture> <sky2scrollspeed>
mapinfo = COM_Parse (data);
strncpy (levelinfo->skypic2, com_token, 8);
mapinfo = COM_Parse (mapinfo);
if (HexenHack)
levelinfo->skyspeed2 = atoi (com_token) << 8;
else
levelinfo->skyspeed2 = (int)(atof (com_token) * 65536.0);
} else if (!stricmp (com_token, "fade")) {
// fade <colorname> OR fade <colordescriptor>
char *colorstring;
mapinfo = COM_Parse (data);
if ( (colorstring = V_GetColorStringByName (com_token)) ) {
levelinfo->fadeto = V_GetColorFromString (NULL, colorstring);
free (colorstring);
} else {
levelinfo->fadeto = V_GetColorFromString (NULL, com_token);
}
} else if (!stricmp (com_token, "outsidefog")) {
// outsidefog <colorname> OR fade <colordescriptor>
char *colorstring;
mapinfo = COM_Parse (data);
if ( (colorstring = V_GetColorStringByName (com_token)) ) {
levelinfo->outsidefog = V_GetColorFromString (NULL, colorstring);
free (colorstring);
} else {
levelinfo->outsidefog = V_GetColorFromString (NULL, com_token);
}
} else if (!stricmp (com_token, "titlepatch")) {
// titlepatch <patchname>
mapinfo = COM_Parse (data);
strncpy (levelinfo->pname, com_token, 8);
} else if (!stricmp (com_token, "par")) {
// par <partime (in seconds)>
mapinfo = COM_Parse (data);
levelinfo->partime = atoi (com_token);
} else if (!stricmp (com_token, "music")) {
// music <musiclump>
mapinfo = COM_Parse (data);
strncpy (levelinfo->music, com_token, 8);
} else if (!stricmp (com_token, "nointermission")) {
// nointermission
mapinfo = data;
levelflags |= LEVEL_NOINTERMISSION;
} else if (!stricmp (com_token, "doublesky")) {
// doublesky
mapinfo = data;
levelflags |= LEVEL_DOUBLESKY;
} else if (!stricmp (com_token, "nosoundclipping")) {
// nosoundclipping
mapinfo = data;
levelflags |= LEVEL_NOSOUNDCLIPPING;
} else if (!stricmp (com_token, "allowmonstertelefrags")) {
// allowmonstertelefrags
mapinfo = data;
levelflags |= LEVEL_MONSTERSTELEFRAG;
} else if (!stricmp (com_token, "map07special")) {
// map07special
mapinfo = data;
levelflags |= LEVEL_MAP07SPECIAL;
} else if (!stricmp (com_token, "baronspecial")) {
// baronspecial
mapinfo = data;
levelflags |= LEVEL_BRUISERSPECIAL;
} else if (!stricmp (com_token, "cyberdemonspecial")) {
// cyberdemonspecial
mapinfo = data;
levelflags |= LEVEL_CYBORGSPECIAL;
} else if (!stricmp (com_token, "spidermastermindspecial")) {
// spidermastermindspecial
mapinfo = data;
levelflags |= LEVEL_SPIDERSPECIAL;
} else if (!stricmp (com_token, "specialaction_exitlevel")) {
// specialaction_exitlevel
mapinfo = data;
levelflags = (levelflags & (~LEVEL_SPECACTIONSMASK));
} else if (!stricmp (com_token, "specialaction_opendoor")) {
// specialaction_opendoor
mapinfo = data;
levelflags = (levelflags & (~LEVEL_SPECACTIONSMASK)) | LEVEL_SPECOPENDOOR;
} else if (!stricmp (com_token, "specialaction_lowerfloor")) {
// specialaction_lowerfloor
mapinfo = data;
levelflags = (levelflags & (~LEVEL_SPECACTIONSMASK)) | LEVEL_SPECLOWERFLOOR;
} else if (!stricmp (com_token, "lightning")) {
// lightning
mapinfo = data;
} else if (!stricmp (com_token, "fadetable")) {
// fadetable
mapinfo = COM_Parse (data);
uppercopy (levelinfo->fadetable, com_token);
} else if (!stricmp (com_token, "evenlighting")) {
// evenlighting
mapinfo = data;
levelflags |= LEVEL_EVENLIGHTING;
} else if (!stricmp (com_token, "noautosequences")) {
// noautosequences
mapinfo = data;
levelflags |= LEVEL_SNDSEQTOTALCTRL;
} else if (!stricmp (com_token, "forcenoskystretch")) {
// forcenoskystretch
mapinfo = data;
levelflags |= LEVEL_FORCENOSKYSTRETCH;
} else if (!stricmp (com_token, "cdtrack") ||
!stricmp (com_token, "cd_start_track") ||
!stricmp (com_token, "cd_end1_track") ||
!stricmp (com_token, "cd_end2_track") ||
!stricmp (com_token, "cd_end3_track") ||
!stricmp (com_token, "cd_intermission_track") ||
!stricmp (com_token, "cd_title_track") ||
!stricmp (com_token, "warptrans")) {
mapinfo = COM_Parse (data);
}
}
if (!stricmp (com_token, "clusterdef")) {
// clusterdef <clusternum>
if (levelindex > -1) {
// Store previous level's flags
levelinfo->flags = levelflags;
levelindex = -1;
}
mapinfo = COM_Parse (data);
if ((clusterindex = FindWadClusterInfo (atoi (com_token))) == -1)
{
clusterindex = numwadclusterinfos++;
wadclusterinfos = Realloc (wadclusterinfos, sizeof(cluster_info_t)*numwadclusterinfos);
memset (wadclusterinfos + clusterindex, 0, sizeof(cluster_info_t));
}
clusterinfo = wadclusterinfos + clusterindex;
clusterinfo->cluster = atoi (com_token);
} else if (clusterindex > -1) {
if (!stricmp (com_token, "entertext")) {
// entertext <"text to display when entering cluster">
mapinfo = COM_Parse (data);
ReplaceString (&clusterinfo->entertext, com_token);
} else if (!stricmp (com_token, "exittext")) {
// exittext <"text to display when leaving cluster">
mapinfo = COM_Parse (data);
ReplaceString (&clusterinfo->exittext, com_token);
} else if (!stricmp (com_token, "music")) {
// music <musiclump>
mapinfo = COM_Parse (data);
strncpy (clusterinfo->messagemusic, com_token, 8);
clusterinfo->messagemusic[8] = 0;
} else if (!stricmp (com_token, "flat")) {
// flat <flatname>
mapinfo = COM_Parse (data);
strncpy (clusterinfo->finaleflat, com_token, 8);
} else if (!stricmp (com_token, "hub")) {
// hub
mapinfo = data;
clusterinfo->flags |= CLUSTER_HUB;
}
}
}
if (levelindex > -1) {
// Store previous level's flags
levelinfo->flags = levelflags;
}
}
}
static void zapDefereds (acsdefered_t *def)
{
while (def) {
acsdefered_t *next = def->next;
Z_Free (def);
def = next;
}
}
void P_RemoveDefereds (void)
{
int i;
// Remove any existing defereds
for (i = 0; i < numwadlevelinfos; i++)
if (wadlevelinfos[i].defered) {
zapDefereds (wadlevelinfos[i].defered);
wadlevelinfos[i].defered = NULL;
}
for (i = 0; LevelInfos[i].level_name; i++)
if (LevelInfos[i].defered) {
zapDefereds (LevelInfos[i].defered);
LevelInfos[i].defered = NULL;
}
}
//
// G_InitNew
// Can be called by the startup code or the menu task,
// consoleplayer, displayplayer, playeringame[] should be set.
//
static char d_mapname[9];
void G_DeferedInitNew (char *mapname)
{
strncpy (d_mapname, mapname, 8);
gameaction = ga_newgame;
}
void Cmd_Map (void *plyr, int argc, char **argv)
{
if (argc > 1) {
if (W_CheckNumForName (argv[1]) == -1)
Printf (PRINT_HIGH, "No map %s\n", argv[1]);
else
G_DeferedInitNew (argv[1]);
}
}
void G_DoNewGame (void)
{
if (demoplayback) {
C_RestoreCVars ();
demoplayback = false;
D_SetupUserInfo ();
}
netdemo = false;
netgame = false;
// deathmatch = 0;
{
int i;
for (i = 1; i < MAXPLAYERS; i++)
playeringame[i] = 0;
}
// respawnparm = false;
// fastparm = 0;
// nomonsters = false;
consoleplayer = 0;
G_InitNew (d_mapname);
gameaction = ga_nothing;
}
void G_InitNew (char *mapname)
{
static BOOL isFast;
BOOL wantFast;
int i;
// [RH] Remove all particles
R_ClearParticles ();
if (!savegamerestore)
G_ClearSnapshots ();
// [RH] Mark all levels as not visited
if (!savegamerestore) {
for (i = 0; i < numwadlevelinfos; i++)
wadlevelinfos[i].flags &= ~LEVEL_VISITED;
for (i = 0; LevelInfos[i].mapname[0]; i++)
LevelInfos[i].flags &= ~LEVEL_VISITED;
}
UnlatchCVars ();
if (gameskill->value > sk_nightmare)
SetCVarFloat (gameskill, sk_nightmare);
else if (gameskill->value < sk_baby)
SetCVarFloat (gameskill, sk_baby);
UnlatchCVars ();
if (paused)
{
paused = false;
S_ResumeSound ();
}
// [RH] If this map doesn't exist, bomb out
if (W_CheckNumForName (mapname) == -1) {
I_Error ("Could not find map %s\n", mapname);
}
if ((gameskill->value == sk_nightmare) || (dmflags & DF_MONSTERS_RESPAWN) )
respawnmonsters = true;
else
respawnmonsters = false;
wantFast = (dmflags & DF_FAST_MONSTERS) || (gameskill->value == sk_nightmare);
if (wantFast != isFast) {
if (wantFast) {
for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
states[i].tics >>= 1;
mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT;
mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT;
mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT;
}
else
{
for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
states[i].tics <<= 1;
mobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT;
mobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT;
mobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT;
}
isFast = wantFast;
}
if (!savegamerestore)
M_ClearRandom ();
// [RH] Clear ACS world variables and time
// (Savegame restores them after calling G_InitNew)
memset (WorldVars, 0, sizeof(WorldVars));
level.time = 0;
// force players to be initialized upon first level load
if (!savegamerestore)
for (i=0 ; i<MAXPLAYERS ; i++)
players[i].playerstate = PST_REBORN;
usergame = true; // will be set false if a demo
paused = false;
demoplayback = false;
automapactive = false;
viewactive = true;
BorderNeedRefresh = true;
strncpy (level.mapname, mapname, 8);
G_DoLoadLevel (0);
}
//
// G_DoCompleted
//
BOOL secretexit;
static int startpos; // [RH] Support for multiple starts per level
extern char* pagename;
extern BOOL NoWipe; // [RH] Don't wipe when travelling in hubs
// [RH] The position parameter to these next two functions should
// match the first parameter of the single player start spots
// that should appear in the next map.
static void goOn (int position)
{
cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
cluster_info_t *nextcluster = FindClusterInfo (FindLevelInfo (level.nextmap)->cluster);
startpos = position;
gameaction = ga_completed;
if (thiscluster && (thiscluster->flags & CLUSTER_HUB)) {
if ((level.flags & LEVEL_NOINTERMISSION) || (nextcluster == thiscluster))
NoWipe = 4;
D_DrawIcon = "TELEICON";
}
}
void G_ExitLevel (int position)
{
secretexit = false;
goOn (position);
}
// Here's for the german edition.
void G_SecretExitLevel (int position)
{
// [RH] Check for secret levels is done in
// G_DoCompleted()
secretexit = true;
goOn (position);
}
void G_DoCompleted (void)
{
int i;
gameaction = ga_nothing;
// [RH] Mark this level as having been visited
if (!(level.flags & LEVEL_CHANGEMAPCHEAT))
FindLevelInfo (level.mapname)->flags |= LEVEL_VISITED;
if (automapactive)
AM_Stop ();
wminfo.epsd = level.cluster - 1; // Only used for DOOM I.
strncpy (wminfo.lname0, level.info->pname, 8);
strncpy (wminfo.current, level.mapname, 8);
if (deathmatch->value &&
(dmflags & DF_SAME_LEVEL) &&
!(level.flags & LEVEL_CHANGEMAPCHEAT)) {
strncpy (wminfo.next, level.mapname, 8);
strncpy (wminfo.lname1, level.info->pname, 8);
} else {
wminfo.next[0] = 0;
if (secretexit) {
if (W_CheckNumForName (level.secretmap) != -1) {
strncpy (wminfo.next, level.secretmap, 8);
strncpy (wminfo.lname1, FindLevelInfo (level.secretmap)->pname, 8);
} else {
secretexit = false;
}
}
if (!wminfo.next[0]) {
strncpy (wminfo.next, level.nextmap, 8);
strncpy (wminfo.lname1, FindLevelInfo (level.nextmap)->pname, 8);
}
}
wminfo.maxkills = level.total_monsters;
wminfo.maxitems = level.total_items;
wminfo.maxsecret = level.total_secrets;
wminfo.maxfrags = 0;
wminfo.partime = TICRATE * level.partime;
wminfo.pnum = consoleplayer;
for (i=0 ; i<MAXPLAYERS ; i++)
{
wminfo.plyr[i].in = playeringame[i];
wminfo.plyr[i].skills = players[i].killcount;
wminfo.plyr[i].sitems = players[i].itemcount;
wminfo.plyr[i].ssecret = players[i].secretcount;
wminfo.plyr[i].stime = level.time;
memcpy (wminfo.plyr[i].frags, players[i].frags
, sizeof(wminfo.plyr[i].frags));
wminfo.plyr[i].fragcount = players[i].fragcount;
}
// [RH] If we're in a hub and staying within that hub, take a snapshot
// of the level. If we're traveling to a new hub, take stuff from
// the player and clear the world vars. If this is just an
// ordinary cluster (not a hub), take stuff from the player, but
// leave the world vars alone.
{
cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
cluster_info_t *nextcluster = FindClusterInfo (FindLevelInfo (level.nextmap)->cluster);
if (thiscluster != nextcluster ||
deathmatch->value ||
!(thiscluster->flags & CLUSTER_HUB)) {
for (i=0 ; i<MAXPLAYERS ; i++)
if (playeringame[i])
G_PlayerFinishLevel (i); // take away cards and stuff
if (nextcluster->flags & CLUSTER_HUB) {
memset (WorldVars, 0, sizeof(WorldVars));
P_RemoveDefereds ();
G_ClearSnapshots ();
}
} else {
G_SnapshotLevel ();
}
if (!(nextcluster->flags & CLUSTER_HUB) || !(thiscluster->flags & CLUSTER_HUB))
level.time = 0; // Reset time to zero if not entering/staying in a hub
if (!deathmatch->value &&
((level.flags & LEVEL_NOINTERMISSION) ||
((nextcluster == thiscluster) && (thiscluster->flags & CLUSTER_HUB)))) {
G_WorldDone ();
return;
}
}
gamestate = GS_INTERMISSION;
viewactive = false;
automapactive = false;
// [RH] If you ever get a statistics driver operational, adapt this.
// if (statcopy)
// memcpy (statcopy, &wminfo, sizeof(wminfo));
WI_Start (&wminfo);
}
//
// G_DoLoadLevel
//
extern gamestate_t wipegamestate;
extern float BaseBlendA;
void G_DoLoadLevel (int position)
{
static int lastposition = 0;
gamestate_t oldgs = gamestate;
int i;
if (position == -1)
position = lastposition;
else
lastposition = position;
G_InitLevelLocals ();
Printf (PRINT_HIGH,
"\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
"\36\36\36\36\36\36\36\36\36\36\36\36\37\n"
TEXTCOLOR_BOLD "%s\n\n",
level.level_name);
if (wipegamestate == GS_LEVEL)
wipegamestate = -1; // force a wipe
gamestate = GS_LEVEL;
if (demoplayback || oldgs == GS_STARTUP)
C_HideConsole ();
C_FlushDisplay ();
// Set the sky map.
// First thing, we have a dummy sky texture name,
// a flat. The data is in the WAD only because
// we look for an actual index, instead of simply
// setting one.
skyflatnum = R_FlatNumForName ( SKYFLATNAME );
// DOOM determines the sky texture to be used
// depending on the current episode, and the game version.
// [RH] Fetch sky parameters from level_locals_t.
sky1texture = R_TextureNumForName (level.skypic1);
sky2texture = R_TextureNumForName (level.skypic2);
// [RH] Set up details about sky rendering
R_InitSkyMap (r_stretchsky);
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i] && players[i].playerstate == PST_DEAD)
players[i].playerstate = PST_REBORN;
memset (players[i].frags,0,sizeof(players[i].frags));
players[i].fragcount = 0;
}
// initialize the msecnode_t freelist. phares 3/25/98
// any nodes in the freelist are gone by now, cleared
// by Z_FreeTags() when the previous level ended or player
// died.
{
extern msecnode_t *headsecnode; // phares 3/25/98
headsecnode = NULL;
}
SN_StopAllSequences ();
P_SetupLevel (level.mapname, position);
displayplayer = consoleplayer; // view the guy you are playing
ST_Start(); // [RH] Make sure status bar knows who we are
gameaction = ga_nothing;
Z_CheckHeap ();
// clear cmd building stuff
Impulse = 0;
Actions &= ACTION_MLOOK | ACTION_KLOOK;
joyxmove = joyymove = 0;
mousex = mousey = 0;
sendpause = sendsave = paused = sendcenterview = false;
level.starttime = I_GetTime ();
G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level.
P_DoDeferedScripts (); // [RH] Do script actions that were triggerd on another map.
if (timingdemo) {
static BOOL firstTime = true;
if (firstTime) {
starttime = I_GetTimeReally ();
firstTime = false;
}
}
}
//
// G_WorldDone
//
void G_WorldDone (void)
{
cluster_info_t *nextcluster;
cluster_info_t *thiscluster;
gameaction = ga_worlddone;
if (level.flags & LEVEL_CHANGEMAPCHEAT)
return;
thiscluster = FindClusterInfo (level.cluster);
if (!strncmp (level.nextmap, "EndGame", 7)) {
F_StartFinale (thiscluster->messagemusic, thiscluster->finaleflat, thiscluster->exittext);
} else {
if (!secretexit)
nextcluster = FindClusterInfo (FindLevelInfo (level.nextmap)->cluster);
else
nextcluster = FindClusterInfo (FindLevelInfo (level.secretmap)->cluster);
if (nextcluster->cluster != level.cluster && !deathmatch->value) {
// Only start the finale if the next level's cluster is different
// than the current one and we're not in deathmatch.
if (nextcluster->entertext) {
F_StartFinale (nextcluster->messagemusic, nextcluster->finaleflat, nextcluster->entertext);
} else if (thiscluster->exittext) {
F_StartFinale (thiscluster->messagemusic, thiscluster->finaleflat, thiscluster->exittext);
}
}
}
}
void G_DoWorldDone (void)
{
gamestate = GS_LEVEL;
if (wminfo.next[0] == 0) {
// Don't die if no next map is given,
// just repeat the current one.
Printf (PRINT_HIGH, "No next map specified.\n");
} else {
strncpy (level.mapname, wminfo.next, 8);
}
G_DoLoadLevel (startpos);
startpos = 0;
gameaction = ga_nothing;
viewactive = true;
}
extern dyncolormap_t NormalLight;
void G_InitLevelLocals ()
{
int oldfade = level.fadeto;
level_info_t *info;
int i;
BaseBlendA = 0.0f; // Remove underwater blend effect, if any
NormalLight.maps = realcolormaps;
if ((i = FindWadLevelInfo (level.mapname)) > -1) {
level_pwad_info_t *pinfo = wadlevelinfos + i;
level.info = (level_info_t *)pinfo;
level.skyspeed1 = pinfo->skyspeed1;
level.skyspeed2 = pinfo->skyspeed2;
info = (level_info_t *)pinfo;
strncpy (level.skypic2, pinfo->skypic2, 8);
level.fadeto = pinfo->fadeto;
if (level.fadeto) {
NormalLight.maps = DefaultPalette->maps.colormaps;
} else {
R_SetDefaultColormap (pinfo->fadetable);
}
level.outsidefog = pinfo->outsidefog;
level.flags |= LEVEL_DEFINEDINMAPINFO;
} else {
info = FindDefLevelInfo (level.mapname);
level.info = info;
level.skyspeed1 = level.skyspeed2 = 0;
level.fadeto = 0;
level.outsidefog = 0xff000000; // 0xff000000 signals not to handle it special
level.skypic2[0] = 0;
R_SetDefaultColormap ("COLORMAP");
}
if (info->level_name) {
level.partime = info->partime;
level.cluster = info->cluster;
level.flags = info->flags;
level.levelnum = info->levelnum;
strncpy (level.level_name, info->level_name, 63);
strncpy (level.nextmap, info->nextmap, 8);
strncpy (level.secretmap, info->secretmap, 8);
strncpy (level.music, info->music, 8);
strncpy (level.skypic1, info->skypic1, 8);
if (!level.skypic2[0])
strncpy (level.skypic2, level.skypic1, 8);
} else {
level.partime = level.cluster = 0;
strcpy (level.level_name, "Unnamed");
level.nextmap[0] =
level.secretmap[0] =
level.music[0] = 0;
strncpy (level.skypic1, "SKY1", 8);
strncpy (level.skypic2, "SKY1", 8);
level.flags = 0;
level.levelnum = 1;
}
memset (level.vars, 0, sizeof(level.vars));
if (oldfade != level.fadeto)
RefreshPalettes ();
}
char *CalcMapName (int episode, int level)
{
static char lumpname[9];
if (gamemode == commercial) {
sprintf (lumpname, "MAP%02d", level);
} else {
lumpname[0] = 'E';
lumpname[1] = '0' + episode;
lumpname[2] = 'M';
lumpname[3] = '0' + level;
lumpname[4] = 0;
}
return lumpname;
}
static level_info_t *FindDefLevelInfo (char *mapname)
{
level_info_t *i;
i = LevelInfos;
while (i->level_name) {
if (!strnicmp (i->mapname, mapname, 8))
break;
i++;
}
return i;
}
level_info_t *FindLevelInfo (char *mapname)
{
int i;
if ((i = FindWadLevelInfo (mapname)) > -1)
return (level_info_t *)(wadlevelinfos + i);
else
return FindDefLevelInfo (mapname);
}
level_info_t *FindLevelByNum (int num)
{
{
int i;
for (i = 0; i < numwadlevelinfos; i++)
if (wadlevelinfos[i].levelnum == num)
return (level_info_t *)(wadlevelinfos + i);
}
{
level_info_t *i = LevelInfos;
while (i->level_name) {
if (i->levelnum == num)
return i;
i++;
}
return NULL;
}
}
static cluster_info_t *FindDefClusterInfo (int cluster)
{
cluster_info_t *i;
i = ClusterInfos;
while (i->cluster && i->cluster != cluster)
i++;
return i;
}
cluster_info_t *FindClusterInfo (int cluster)
{
int i;
if ((i = FindWadClusterInfo (cluster)) > -1)
return wadclusterinfos + i;
else
return FindDefClusterInfo (cluster);
}
void G_SetLevelStrings (void)
{
char temp[8];
char *namepart;
int i, start;
temp[0] = '0';
temp[1] = ':';
temp[2] = 0;
for (i = 65; i < 101; i++) { // HUSTR_E1M1 .. HUSTR_E4M9
if (temp[0] < '9')
temp[0]++;
else
temp[0] = '1';
if ( (namepart = strstr (Strings[i].string, temp)) ) {
namepart += 2;
while (*namepart && *namepart <= ' ')
namepart++;
} else {
namepart = Strings[i].string;
}
ReplaceString (&LevelInfos[i-65].level_name, namepart);
}
for (i = 0; i < 4; i++)
ReplaceString (&ClusterInfos[i].exittext, Strings[221+i].string);
if (gamemission == pack_plut)
start = 133; // PHUSTR_1
else if (gamemission == pack_tnt)
start = 165; // THUSTR_1
else
start = 101; // HUSTR_1
for (i = 0; i < 32; i++) {
sprintf (temp, "%d:", i + 1);
if ( (namepart = strstr (Strings[i+start].string, temp)) ) {
namepart += strlen (temp);
while (*namepart && *namepart <= ' ')
namepart++;
} else {
namepart = Strings[i+start].string;
}
ReplaceString (&LevelInfos[36+i].level_name, namepart);
}
if (gamemission == pack_plut)
start = 231; // P1TEXT
else if (gamemission == pack_tnt)
start = 237; // T1TEXT
else
start = 225; // C1TEXT
for (i = 0; i < 4; i++)
ReplaceString (&ClusterInfos[4 + i].exittext, Strings[start+i].string);
for (; i < 6; i++)
ReplaceString (&ClusterInfos[4 + i].entertext, Strings[start+i].string);
if (level.info)
strncpy (level.level_name, level.info->level_name, 63);
}
// Archives the current level
void G_SnapshotLevel (void)
{
save_p = savebuffer = Malloc (savegamesize);
if (level.info->snapshot)
Z_Free (level.info->snapshot);
WriteLong (level.flags, &save_p);
WriteLong (level.fadeto, &save_p);
WriteLong (level.found_secrets, &save_p);
WriteLong (level.found_items, &save_p);
WriteLong (level.killed_monsters, &save_p);
memcpy (save_p, level.vars, sizeof(level.vars));
save_p += sizeof(level.vars);
P_ArchiveWorld ();
P_ArchivePolyobjs ();
P_ArchiveThinkers ();
P_ArchiveSpecials ();
P_ArchiveScripts ();
P_ArchiveSounds ();
// Now compress it. This seems to shrink the data down to
// about 20% of its original size fairly consistantly.
{
int outlen;
int len = save_p - savebuffer;
lzo_byte *compressed;
lzo_byte *wrkmem;
int r;
compressed = Malloc (OUT_LEN(len));
wrkmem = Malloc (LZO1X_1_MEM_COMPRESS);
r = lzo1x_1_compress (savebuffer, len, compressed, &outlen, wrkmem);
free (wrkmem);
// If the data could not be compressed, store it as-is.
if (r != LZO_E_OK || outlen > len) {
DPrintf ("Snapshot not compressable\n");
outlen = 0;
} else {
DPrintf ("Snapshot: %d .. %d bytes\n", len, outlen);
}
level.info->snapshot = Z_Malloc (((outlen == 0) ? len : outlen) + sizeof(int)*2, PU_STATIC, 0);
((int *)(level.info->snapshot))[0] = outlen;
((int *)(level.info->snapshot))[1] = len;
if (outlen == 0)
memcpy (level.info->snapshot + sizeof(int)*2, savebuffer, len);
else
memcpy (level.info->snapshot + sizeof(int)*2, compressed, outlen);
free (compressed);
}
free (savebuffer);
savebuffer = save_p = NULL;
}
// Unarchives the current level based on its snapshot
// The level should have already been loaded and setup.
void G_UnSnapshotLevel (BOOL keepPlayers)
{
int expandsize, cprlen;
byte *expand;
if (!level.info->snapshot)
return;
cprlen = ((int *)(level.info->snapshot))[0];
expandsize = ((int *)(level.info->snapshot))[1];
if (cprlen) {
int r, newlen;
expand = Z_Malloc (expandsize, PU_STATIC, 0);
r = lzo1x_decompress (level.info->snapshot + sizeof(int)*2, cprlen, expand, &newlen, NULL);
if (r != LZO_E_OK || newlen != expandsize) {
Printf (PRINT_HIGH, "Could not decompress snapshot");
Z_Free (expand);
return;
}
save_p = expand;
} else {
save_p = level.info->snapshot + sizeof(int)*2;
expand = NULL;
}
level.flags = ReadLong (&save_p);
level.fadeto = ReadLong (&save_p);
level.found_secrets = ReadLong (&save_p);
level.found_items = ReadLong (&save_p);
level.killed_monsters = ReadLong (&save_p);
memcpy (level.vars, save_p, sizeof(level.vars));
save_p += sizeof(level.vars);
P_UnArchiveWorld ();
P_UnArchivePolyobjs ();
P_UnArchiveThinkers (keepPlayers);
P_UnArchiveSpecials ();
P_UnArchiveScripts ();
P_UnArchiveSounds ();
if (expand)
Z_Free (expand);
// No reason to keep the snapshot around once the level's been entered.
Z_Free (level.info->snapshot);
level.info->snapshot = NULL;
save_p = NULL;
}
void G_ClearSnapshots (void)
{
int i;
for (i = 0; i < numwadlevelinfos; i++)
if (wadlevelinfos[i].snapshot) {
Z_Free (wadlevelinfos[i].snapshot);
wadlevelinfos[i].snapshot = NULL;
}
for (i = 0; LevelInfos[i].level_name; i++)
if (LevelInfos[i].snapshot) {
Z_Free (LevelInfos[i].snapshot);
LevelInfos[i].snapshot = NULL;
}
}
static void writeSnapShot (level_info_t *i)
{
int len;
len = ((int *)(i->snapshot))[0];
if (len == 0)
len = ((int *)(i->snapshot))[1];
CheckSaveGame (len + 8 + sizeof(int)*2 + 4);
memcpy (save_p, i->mapname, 8);
save_p += 8;
PADSAVEP();
memcpy (save_p, i->snapshot, len + sizeof(int)*2);
save_p += len + sizeof(int)*2;
}
void G_ArchiveSnapshots (void)
{
int i;
for (i = 0; i < numwadlevelinfos; i++)
if (wadlevelinfos[i].snapshot)
writeSnapShot ((level_info_t *)&wadlevelinfos[i]);
for (i = 0; LevelInfos[i].level_name; i++)
if (LevelInfos[i].snapshot)
writeSnapShot (&LevelInfos[i]);
// Signal end of snapshots
CheckSaveGame (1);
*save_p++ = 0;
}
void G_UnArchiveSnapshots (void)
{
level_info_t *i;
int shortsize, fullsize, savesize;
G_ClearSnapshots ();
while (*save_p) {
i = FindLevelInfo (save_p);
save_p += 8;
PADSAVEP();
shortsize = ((int *)save_p)[0];
fullsize = ((int *)save_p)[1];
savesize = (shortsize ? shortsize : fullsize) + sizeof(int)*2;
if (i) {
i->snapshot = Z_Malloc (savesize, PU_STATIC, 0);
memcpy (i->snapshot, save_p, savesize);
}
save_p += savesize;
}
save_p++;
}
static void writeDefereds (level_info_t *i)
{
acsdefered_t *def = i->defered;
memcpy (save_p, i->mapname, 8);
save_p += 8;
while (def) {
CheckSaveGame (sizeof (*def));
memcpy (save_p, def, sizeof(*def));
save_p += sizeof(*def);
def = def->next;
}
}
void P_ArchiveACSDefereds (void)
{
int i;
for (i = 0; i < numwadlevelinfos; i++)
if (wadlevelinfos[i].defered)
writeDefereds ((level_info_t *)&wadlevelinfos[i]);
for (i = 0; LevelInfos[i].level_name; i++)
if (LevelInfos[i].defered)
writeDefereds (&LevelInfos[i]);
// Signal end of defereds
CheckSaveGame (1);
*save_p++ = 0;
}
void P_UnArchiveACSDefereds (void)
{
level_info_t *i;
acsdefered_t *def, **prev;
P_RemoveDefereds ();
while (*save_p) {
i = FindLevelInfo (save_p);
if (!i) {
char name[9];
strncpy (name, save_p, 8);
name[8] = 0;
I_Error ("Unknown map %s in savegame", name);
}
save_p += 8;
prev = &i->defered;
do {
def = Z_Malloc (sizeof(*def), PU_STATIC, 0);
memcpy (def, save_p, sizeof(*def));
save_p += sizeof(*def);
*prev = def;
prev = &def->next;
} while (*prev);
}
save_p++;
}
// Static level info from original game
// The level names and cluster messages now get filled in
// by G_SetLevelStrings().
level_info_t LevelInfos[] = {
// Registered/Retail Episode 1
{
"E1M1",
1,
NULL,
"WILV00",
"E1M2",
"E1M9",
30,
"SKY1",
"D_E1M1",
0,
1
},
{
"E1M2",
2,
NULL,
"WILV01",
"E1M3",
"E1M9",
75,
"SKY1",
"D_E1M2",
0,
1
},
{
"E1M3",
3,
NULL,
"WILV02",
"E1M4",
"E1M9",
120,
"SKY1",
"D_E1M3",
0,
1
},
{
"E1M4",
4,
NULL,
"WILV03",
"E1M5",
"E1M9",
90,
"SKY1",
"D_E1M4",
0,
1
},
{
"E1M5",
5,
NULL,
"WILV04",
"E1M6",
"E1M9",
165,
"SKY1",
"D_E1M5",
0,
1
},
{
"E1M6",
6,
NULL,
"WILV05",
"E1M7",
"E1M9",
180,
"SKY1",
"D_E1M6",
0,
1
},
{
"E1M7",
7,
NULL,
"WILV06",
"E1M8",
"E1M9",
180,
"SKY1",
"D_E1M7",
0,
1
},
{
"E1M8",
8,
NULL,
"WILV07",
"EndGame1",
"E1M9",
30,
"SKY1",
"D_E1M8",
LEVEL_NOINTERMISSION|LEVEL_NOSOUNDCLIPPING|LEVEL_BRUISERSPECIAL|LEVEL_SPECLOWERFLOOR,
1
},
{
"E1M9",
9,
NULL,
"WILV08",
"E1M4",
"E1M4",
165,
"SKY1",
"D_E1M9",
0,
1
},
// Registered/Retail Episode 2
{
"E2M1",
11,
NULL,
"WILV10",
"E2M2",
"E2M9",
90,
"SKY2",
"D_E2M1",
0,
2
},
{
"E2M2",
12,
NULL,
"WILV11",
"E2M3",
"E2M9",
90,
"SKY2",
"D_E2M2",
0,
2
},
{
"E2M3",
13,
NULL,
"WILV12",
"E2M4",
"E2M9",
90,
"SKY2",
"D_E2M3",
0,
2
},
{
"E2M4",
14,
NULL,
"WILV13",
"E2M5",
"E2M9",
120,
"SKY2",
"D_E2M4",
0,
2
},
{
"E2M5",
15,
NULL,
"WILV14",
"E2M6",
"E2M9",
90,
"SKY2",
"D_E2M5",
0,
2
},
{
"E2M6",
16,
NULL,
"WILV15",
"E2M7",
"E2M9",
360,
"SKY2",
"D_E2M6",
0,
2
},
{
"E2M7",
17,
NULL,
"WILV16",
"E2M8",
"E2M9",
240,
"SKY2",
"D_E2M7",
0,
2
},
{
"E2M8",
18,
NULL,
"WILV17",
"EndGame2",
"E2M9",
30,
"SKY2",
"D_E2M8",
LEVEL_NOINTERMISSION|LEVEL_NOSOUNDCLIPPING|LEVEL_CYBORGSPECIAL,
2
},
{
"E2M9",
19,
NULL,
"WILV18",
"E2M6",
"E2M6",
170,
"SKY2",
"D_E2M9",
0,
2
},
// Registered/Retail Episode 3
{
"E3M1",
21,
NULL,
"WILV20",
"E3M2",
"E3M9",
90,
"SKY3",
"D_E3M1",
0,
3
},
{
"E3M2",
22,
NULL,
"WILV21",
"E3M3",
"E3M9",
45,
"SKY3",
"D_E3M2",
0,
3
},
{
"E3M3",
23,
NULL,
"WILV22",
"E3M4",
"E3M9",
90,
"SKY3",
"D_E3M3",
0,
3
},
{
"E3M4",
24,
NULL,
"WILV23",
"E3M5",
"E3M9",
150,
"SKY3",
"D_E3M4",
0,
3
},
{
"E3M5",
25,
NULL,
"WILV24",
"E3M6",
"E3M9",
90,
"SKY3",
"D_E3M5",
0,
3
},
{
"E3M6",
26,
NULL,
"WILV25",
"E3M7",
"E3M9",
90,
"SKY3",
"D_E3M6",
0,
3
},
{
"E3M7",
27,
NULL,
"WILV26",
"E3M8",
"E3M9",
165,
"SKY3",
"D_E3M7",
0,
3
},
{
"E3M8",
28,
NULL,
"WILV27",
"EndGame3",
"E3M9",
30,
"SKY3",
"D_E3M8",
LEVEL_NOINTERMISSION|LEVEL_NOSOUNDCLIPPING|LEVEL_SPIDERSPECIAL,
3
},
{
"E3M9",
29,
NULL,
"WILV28",
"E3M7",
"E3M7",
135,
"SKY3",
"D_E3M9",
0,
3
},
// Retail Episode 4
{
"E4M1",
31,
NULL,
"WILV30",
"E4M2",
"E4M9",
0,
"SKY4",
"D_E3M4",
0,
4
},
{
"E4M2",
32,
NULL,
"WILV31",
"E4M3",
"E4M9",
0,
"SKY4",
"D_E3M2",
0,
4
},
{
"E4M3",
33,
NULL,
"WILV32",
"E4M4",
"E4M9",
0,
"SKY4",
"D_E3M3",
0,
4
},
{
"E4M4",
34,
NULL,
"WILV33",
"E4M5",
"E4M9",
0,
"SKY4",
"D_E1M5",
0,
4
},
{
"E4M5",
35,
NULL,
"WILV34",
"E4M6",
"E4M9",
0,
"SKY4",
"D_E2M7",
0,
4
},
{
"E4M6",
36,
NULL,
"WILV35",
"E4M7",
"E4M9",
0,
"SKY4",
"D_E2M4",
LEVEL_CYBORGSPECIAL|LEVEL_SPECOPENDOOR,
4
},
{
"E4M7",
37,
NULL,
"WILV36",
"E4M8",
"E4M9",
0,
"SKY4",
"D_E2M6",
0,
4
},
{
"E4M8",
38,
NULL,
"WILV37",
"EndGame4",
"E4M9",
0,
"SKY4",
"D_E2M5",
LEVEL_NOINTERMISSION|LEVEL_NOSOUNDCLIPPING|LEVEL_SPIDERSPECIAL|LEVEL_SPECLOWERFLOOR,
4
},
{
"E4M9",
39,
NULL,
"WILV38",
"E4M3",
"E4M3",
0,
"SKY4",
"D_E1M9",
0,
4
},
// DOOM 2 Levels
{
"MAP01",
1,
NULL,
"CWILV00",
"MAP02",
"MAP02",
30,
"SKY1",
"D_RUNNIN",
0,
5
},
{
"MAP02",
2,
NULL,
"CWILV01",
"MAP03",
"MAP03",
90,
"SKY1",
"D_STALKS",
0,
5
},
{
"MAP03",
3,
NULL,
"CWILV02",
"MAP04",
"MAP04",
120,
"SKY1",
"D_COUNTD",
0,
5
},
{
"MAP04",
4,
NULL,
"CWILV03",
"MAP05",
"MAP05",
120,
"SKY1",
"D_BETWEE",
0,
5
},
{
"MAP05",
5,
NULL,
"CWILV04",
"MAP06",
"MAP06",
90,
"SKY1",
"D_DOOM",
0,
5
},
{
"MAP06",
6,
NULL,
"CWILV05",
"MAP07",
"MAP07",
150,
"SKY1",
"D_THE_DA",
0,
5
},
{
"MAP07",
7,
NULL,
"CWILV06",
"MAP08",
"MAP08",
120,
"SKY1",
"D_SHAWN",
LEVEL_MAP07SPECIAL,
6
},
{
"MAP08",
8,
NULL,
"CWILV07",
"MAP09",
"MAP09",
120,
"SKY1",
"D_DDTBLU",
0,
6
},
{
"MAP09",
9,
NULL,
"CWILV08",
"MAP10",
"MAP10",
270,
"SKY1",
"D_IN_CIT",
0,
6
},
{
"MAP10",
10,
NULL,
"CWILV09",
"MAP11",
"MAP11",
90,
"SKY1",
"D_DEAD",
0,
6
},
{
"MAP11",
11,
NULL,
"CWILV10",
"MAP12",
"MAP12",
210,
"SKY1",
"D_STLKS2",
0,
6
},
{
"MAP12",
12,
NULL,
"CWILV11",
"MAP13",
"MAP13",
150,
"SKY2",
"D_THEDA2",
0,
7
},
{
"MAP13",
13,
NULL,
"CWILV12",
"MAP14",
"MAP14",
150,
"SKY2",
"D_DOOM2",
0,
7
},
{
"MAP14",
14,
NULL,
"CWILV13",
"MAP15",
"MAP15",
150,
"SKY2",
"D_DDTBL2",
0,
7
},
{
"MAP15",
15,
NULL,
"CWILV14",
"MAP16",
"MAP31",
210,
"SKY2",
"D_RUNNI2",
0,
7
},
{
"MAP16",
16,
NULL,
"CWILV15",
"MAP17",
"MAP17",
150,
"SKY2",
"D_DEAD2",
0,
7
},
{
"MAP17",
17,
NULL,
"CWILV16",
"MAP18",
"MAP18",
420,
"SKY2",
"D_STLKS3",
0,
7
},
{
"MAP18",
18,
NULL,
"CWILV17",
"MAP19",
"MAP19",
150,
"SKY2",
"D_ROMERO",
0,
7
},
{
"MAP19",
19,
NULL,
"CWILV18",
"MAP20",
"MAP20",
210,
"SKY2",
"D_SHAWN2",
0,
7
},
{
"MAP20",
20,
NULL,
"CWILV19",
"MAP21",
"MAP21",
150,
"SKY2",
"D_MESSAG",
0,
7
},
{
"MAP21",
21,
NULL,
"CWILV20",
"MAP22",
"MAP22",
240,
"SKY3",
"D_COUNT2",
0,
8
},
{
"MAP22",
22,
NULL,
"CWILV21",
"MAP23",
"MAP23",
150,
"SKY3",
"D_DDTBL3",
0,
8
},
{
"MAP23",
23,
NULL,
"CWILV22",
"MAP24",
"MAP24",
180,
"SKY3",
"D_AMPIE",
0,
8
},
{
"MAP24",
24,
NULL,
"CWILV23",
"MAP25",
"MAP25",
150,
"SKY3",
"D_THEDA3",
0,
8
},
{
"MAP25",
25,
NULL,
"CWILV24",
"MAP26",
"MAP26",
150,
"SKY3",
"D_ADRIAN",
0,
8
},
{
"MAP26",
26,
NULL,
"CWILV25",
"MAP27",
"MAP27",
300,
"SKY3",
"D_MESSG2",
0,
8
},
{
"MAP27",
27,
NULL,
"CWILV26",
"MAP28",
"MAP28",
330,
"SKY3",
"D_ROMER2",
0,
8
},
{
"MAP28",
28,
NULL,
"CWILV27",
"MAP29",
"MAP29",
420,
"SKY3",
"D_TENSE",
0,
8
},
{
"MAP29",
29,
NULL,
"CWILV28",
"MAP30",
"MAP30",
300,
"SKY3",
"D_SHAWN3",
0,
8
},
{
"MAP30",
30,
NULL,
"CWILV29",
"EndGameC",
"EndGameC",
180,
"SKY3",
"D_OPENIN",
LEVEL_MONSTERSTELEFRAG,
8
},
{
"MAP31",
31,
NULL,
"CWILV30",
"MAP16",
"MAP32",
120,
"SKY3",
"D_EVIL",
0,
9
},
{
"MAP32",
32,
NULL,
"CWILV31",
"MAP16",
"MAP16",
30,
"SKY3",
"D_ULTIMA",
0,
10
},
{
"",
0,
NULL,
}
};
// Episode/Cluster information
cluster_info_t ClusterInfos[] = {
{
1, // DOOM Episode 1
"D_VICTOR",
"FLOOR4_8",
NULL,
NULL
},
{
2, // DOOM Episode 2
"D_VICTOR",
"SFLR6_1",
NULL,
NULL
},
{
3, // DOOM Episode 3
"D_VICTOR",
"MFLR8_4",
NULL,
NULL
},
{
4, // DOOM Episode 4
"D_VICTOR",
"MFLR8_3",
NULL,
NULL
},
{
5, // DOOM II first cluster (up thru level 6)
"D_READ_M",
"SLIME16",
NULL,
NULL
},
{
6, // DOOM II second cluster (up thru level 11)
"D_READ_M",
"RROCK14",
NULL,
NULL
},
{
7, // DOOM II third cluster (up thru level 20)
"D_READ_M",
"RROCK07",
NULL,
NULL
},
{
8, // DOOM II fourth cluster (up thru level 30)
"D_READ_M",
"RROCK17",
NULL,
NULL
},
{
9, // DOOM II fifth cluster (level 31)
"D_READ_M",
"RROCK13",
NULL,
NULL
},
{
10, // DOOM II sixth cluster (level 32)
"D_READ_M",
"RROCK19",
NULL,
NULL
},
{
0 // End-of-clusters marker
}
};