diff --git a/docs/rh-log.txt b/docs/rh-log.txt index c44ec99f5b..8f44049c1f 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,17 @@ +January 18, 2009 - February 3, 2009 (Changes by Graf Zahl) +- moved all code related to global ACS variables to p_acs.cpp where it belongs. +- fixed: The nextmap and nextsecret CCMDs need to call G_DeferedInitNew instead of G_InitNew. +- rewrote the MAPINFO parser: + * split level_info_t::flags into 2 DWORDS so that I don't have to deal with 64 bit values later. + * split off skill code into its own file + * created a parser class for MAPINFO + * replaced all uses of ReplaceString in level_info_t with FStrings and made the specialaction + data a TArray so that levelinfos can be handled without error prone maintenance functions. + * split of parser code from g_level.cpp + * const-ified parameters to F_StartFinale. + * Changed how G_MaybeLookupLevelName works and made it return an FString. + * removed 64 character limit on level names. + February 2, 2009 - Changed DECORATE replacements so that they aren't overridden by Dehacked. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9e2bfa66a3..a4d00925c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -478,6 +478,8 @@ add_executable( zdoom WIN32 g_game.cpp g_hub.cpp g_level.cpp + g_mapinfo.cpp + g_skill.cpp gameconfigfile.cpp gi.cpp hu_scores.cpp diff --git a/src/am_map.cpp b/src/am_map.cpp index 36c10d638b..541d3a5d0b 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -1784,7 +1784,7 @@ void AM_Drawer () if (!automapactive) return; - bool allmap = (level.flags & LEVEL_ALLMAP) != 0; + bool allmap = (level.flags2 & LEVEL2_ALLMAP) != 0; bool allthings = allmap && players[consoleplayer].mo->FindInventory() != NULL; AM_initColors (viewactive); diff --git a/src/autosegs.h b/src/autosegs.h index 01069c51df..b873602ed2 100644 --- a/src/autosegs.h +++ b/src/autosegs.h @@ -41,11 +41,13 @@ #define CREG_SECTION "__DATA,creg" #define GREG_SECTION "__DATA,greg" #define MREG_SECTION "__DATA,mreg" +#define MREG_SECTION "__DATA,yreg" #else #define AREG_SECTION "areg" #define CREG_SECTION "creg" #define GREG_SECTION "greg" #define MREG_SECTION "mreg" +#define GREG_SECTION "yreg" #endif #endif @@ -68,6 +70,10 @@ extern REGINFO GRegTail; extern REGINFO MRegHead; extern REGINFO MRegTail; +// List of MAPINFO map options +extern REGINFO YRegHead; +extern REGINFO YRegTail; + template class TAutoSegIteratorNoArrow { diff --git a/src/autostart.cpp b/src/autostart.cpp index 666f5b9d16..5bdfa3c6eb 100644 --- a/src/autostart.cpp +++ b/src/autostart.cpp @@ -46,7 +46,7 @@ #if defined(_MSC_VER) -#pragma comment(linker, "/merge:.areg=.data /merge:.creg=.data /merge:.greg=.data /merge:.mreg=.data") +#pragma comment(linker, "/merge:.areg=.data /merge:.creg=.data /merge:.greg=.data /merge:.mreg=.data /merge:.yreg=.data") #pragma data_seg(".areg$a") void *ARegHead = 0; @@ -60,6 +60,9 @@ void *GRegHead = 0; #pragma data_seg(".mreg$a") void *MRegHead = 0; +#pragma data_seg(".yreg$a") +void *YRegHead = 0; + #pragma data_seg() // We want visual styles support under XP @@ -87,6 +90,7 @@ void *ARegHead __attribute__((section(AREG_SECTION))) = 0; void *CRegHead __attribute__((section(CREG_SECTION))) = 0; void *GRegHead __attribute__((section(GREG_SECTION))) = 0; void *MRegHead __attribute__((section(MREG_SECTION))) = 0; +void *YRegHead __attribute__((section(YREG_SECTION))) = 0; #elif diff --git a/src/autozend.cpp b/src/autozend.cpp index 0e7c2e5dac..84903eee66 100644 --- a/src/autozend.cpp +++ b/src/autozend.cpp @@ -49,6 +49,9 @@ void *GRegTail = 0; #pragma data_seg(".mreg$z") void *MRegTail = 0; +#pragma data_seg(".yreg$z") +void *YRegTail = 0; + #pragma data_seg() @@ -59,6 +62,7 @@ void *ARegTail __attribute__((section(AREG_SECTION))) = 0; void *CRegTail __attribute__((section(CREG_SECTION))) = 0; void *GRegTail __attribute__((section(GREG_SECTION))) = 0; void *MRegTail __attribute__((section(MREG_SECTION))) = 0; +void *YRegTail __attribute__((section(YREG_SECTION))) = 0; #elif diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 48c86104ba..a65541deff 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -858,7 +858,7 @@ CCMD(nextmap) if (next != NULL && strncmp(next, "enDSeQ", 6)) { - G_InitNew(next, false); + G_DeferedInitNew(next, false); } else { @@ -879,7 +879,7 @@ CCMD(nextsecret) if (next != NULL && strncmp(next, "enDSeQ", 6)) { - G_InitNew(next, false); + G_DeferedInitNew(next, false); } else { diff --git a/src/c_console.cpp b/src/c_console.cpp index 76d1357434..d6ec09c0b6 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1317,7 +1317,7 @@ void C_FullConsole () if (gamestate != GS_STARTUP) { gamestate = GS_FULLCONSOLE; - level.music = NULL; + level.Music = ""; S_Start (); P_FreeLevelData (); V_SetBlend (0,0,0,0); diff --git a/src/doomstat.cpp b/src/doomstat.cpp index 0fe988a92f..6fe88955ed 100644 --- a/src/doomstat.cpp +++ b/src/doomstat.cpp @@ -60,7 +60,7 @@ CUSTOM_CVAR (String, language, "auto", CVAR_ARCHIVE) { SetLanguageIDs (); GStrings.LoadStrings (false); - G_MaybeLookupLevelName (NULL); + if (level.info != NULL) level.LevelName = level.info->LookupLevelName(); } // [RH] Network arbitrator diff --git a/src/f_finale.cpp b/src/f_finale.cpp index bbdd7d4352..11d06b55a3 100644 --- a/src/f_finale.cpp +++ b/src/f_finale.cpp @@ -86,8 +86,8 @@ void F_AdvanceSlideshow (); // // F_StartFinale // -void F_StartFinale (char *music, int musicorder, int cdtrack, unsigned int cdid, char *flat, char *text, - INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, bool ending) +void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, + const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, bool ending) { bool loopmusic = ending ? !(gameinfo.flags & GI_NOLOOPFINALEMUSIC) : true; gameaction = ga_nothing; @@ -1026,7 +1026,7 @@ void F_StartSlideshow () V_SetBlend (0,0,0,0); // The slideshow is determined solely by the map you're on. - if (!multiplayer && level.flags & LEVEL_DEATHSLIDESHOW) + if (!multiplayer && level.flags2 & LEVEL2_DEATHSLIDESHOW) { FinalePart = 14; } @@ -1062,7 +1062,7 @@ void F_AdvanceSlideshow () { case -99: if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid)) - S_ChangeMusic (level.music, level.musicorder); + S_ChangeMusic (level.Music, level.musicorder); gamestate = GS_LEVEL; wipegamestate = GS_LEVEL; P_ResumeConversation (); diff --git a/src/f_finale.h b/src/f_finale.h index 701a756e3d..8673e3b2a0 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -42,8 +42,8 @@ void F_Ticker (); void F_Drawer (); -void F_StartFinale (char *music, int musicorder, int cdtrack, unsigned int cdid, char *flat, char *text, - INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, bool ending); +void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, + const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, bool ending); void F_StartSlideshow (); diff --git a/src/g_game.cpp b/src/g_game.cpp index 241263af19..491852ffbd 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -73,6 +73,7 @@ #include "cmdlib.h" #include "d_net.h" #include "d_event.h" +#include "p_acs.h" #include @@ -1129,7 +1130,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, bool resetinventory } } - if (mode == FINISH_NoHub && !(level.flags & LEVEL_KEEPFULLINVENTORY)) + if (mode == FINISH_NoHub && !(level.flags2 & LEVEL2_KEEPFULLINVENTORY)) { // Reduce all owned (visible) inventory to 1 item each for (item = p->mo->Inventory; item != NULL; item = item->Inventory) { @@ -1429,7 +1430,7 @@ static void G_QueueBody (AActor *body) // void G_DoReborn (int playernum, bool freshbot) { - if (!multiplayer && !(level.flags & LEVEL_ALLOWRESPAWN)) + if (!multiplayer && !(level.flags2 & LEVEL2_ALLOWRESPAWN)) { if (BackupSaveName.Len() > 0 && FileExists (BackupSaveName.GetChars())) { // Load game from the last point it was saved @@ -1579,128 +1580,6 @@ bool G_CheckSaveGameWads (PNGHandle *png, bool printwarn) return true; } -static void WriteVars (FILE *file, SDWORD *vars, size_t count, DWORD id) -{ - size_t i, j; - - for (i = 0; i < count; ++i) - { - if (vars[i] != 0) - break; - } - if (i < count) - { - // Find last non-zero var. Anything beyond the last stored variable - // will be zeroed at load time. - for (j = count-1; j > i; --j) - { - if (vars[j] != 0) - break; - } - FPNGChunkArchive arc (file, id); - for (i = 0; i <= j; ++i) - { - DWORD var = vars[i]; - arc << var; - } - } -} - -static void ReadVars (PNGHandle *png, SDWORD *vars, size_t count, DWORD id) -{ - size_t len = M_FindPNGChunk (png, id); - size_t used = 0; - - if (len != 0) - { - DWORD var; - size_t i; - FPNGChunkArchive arc (png->File->GetFile(), id, len); - used = len / 4; - - for (i = 0; i < used; ++i) - { - arc << var; - vars[i] = var; - } - png->File->ResetFilePtr(); - } - if (used < count) - { - memset (&vars[used], 0, (count-used)*4); - } -} - -static void WriteArrayVars (FILE *file, FWorldGlobalArray *vars, unsigned int count, DWORD id) -{ - unsigned int i, j; - - // Find the first non-empty array. - for (i = 0; i < count; ++i) - { - if (vars[i].CountUsed() != 0) - break; - } - if (i < count) - { - // Find last non-empty array. Anything beyond the last stored array - // will be emptied at load time. - for (j = count-1; j > i; --j) - { - if (vars[j].CountUsed() != 0) - break; - } - FPNGChunkArchive arc (file, id); - arc.WriteCount (i); - arc.WriteCount (j); - for (; i <= j; ++i) - { - arc.WriteCount (vars[i].CountUsed()); - - FWorldGlobalArray::ConstIterator it(vars[i]); - const FWorldGlobalArray::Pair *pair; - - while (it.NextPair (pair)) - { - arc.WriteCount (pair->Key); - arc.WriteCount (pair->Value); - } - } - } -} - -static void ReadArrayVars (PNGHandle *png, FWorldGlobalArray *vars, size_t count, DWORD id) -{ - size_t len = M_FindPNGChunk (png, id); - unsigned int i, k; - - for (i = 0; i < count; ++i) - { - vars[i].Clear (); - } - - if (len != 0) - { - DWORD max, size; - FPNGChunkArchive arc (png->File->GetFile(), id, len); - - i = arc.ReadCount (); - max = arc.ReadCount (); - - for (; i <= max; ++i) - { - size = arc.ReadCount (); - for (k = 0; k < size; ++k) - { - SDWORD key, val; - key = arc.ReadCount(); - val = arc.ReadCount(); - vars[i].Insert (key, val); - } - } - png->File->ResetFilePtr(); - } -} void G_DoLoadGame () { @@ -1820,10 +1699,7 @@ void G_DoLoadGame () delete[] map; savegamerestore = false; - ReadVars (png, ACS_WorldVars, NUM_WORLDVARS, MAKE_ID('w','v','A','r')); - ReadVars (png, ACS_GlobalVars, NUM_GLOBALVARS, MAKE_ID('g','v','A','r')); - ReadArrayVars (png, ACS_WorldArrays, NUM_WORLDVARS, MAKE_ID('w','a','R','r')); - ReadArrayVars (png, ACS_GlobalArrays, NUM_GLOBALVARS, MAKE_ID('g','a','R','r')); + P_ReadACSVars(png); NextSkill = -1; if (M_FindPNGChunk (png, MAKE_ID('s','n','X','t')) == 1) @@ -1985,7 +1861,7 @@ static void PutSaveComment (FILE *file) // Get level name //strcpy (comment, level.level_name); - mysnprintf(comment, countof(comment), "%s - %s", level.mapname, level.level_name); + mysnprintf(comment, countof(comment), "%s - %s", level.mapname, level.LevelName.GetChars()); len = (WORD)strlen (comment); comment[len] = '\n'; @@ -2069,10 +1945,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio FRandom::StaticWriteRNGState (stdfile); P_WriteACSDefereds (stdfile); - WriteVars (stdfile, ACS_WorldVars, NUM_WORLDVARS, MAKE_ID('w','v','A','r')); - WriteVars (stdfile, ACS_GlobalVars, NUM_GLOBALVARS, MAKE_ID('g','v','A','r')); - WriteArrayVars (stdfile, ACS_WorldArrays, NUM_WORLDVARS, MAKE_ID('w','a','R','r')); - WriteArrayVars (stdfile, ACS_GlobalArrays, NUM_GLOBALVARS, MAKE_ID('g','a','R','r')); + P_WriteACSVars(stdfile); if (NextSkill != -1) { diff --git a/src/g_hexen/a_hexenspecialdecs.cpp b/src/g_hexen/a_hexenspecialdecs.cpp index c8c64b20b1..d9a704a049 100644 --- a/src/g_hexen/a_hexenspecialdecs.cpp +++ b/src/g_hexen/a_hexenspecialdecs.cpp @@ -72,7 +72,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_PotteryExplode) S_Sound (mo, CHAN_BODY, "PotteryExplode", 1, ATTN_NORM); if (self->args[0]>=0 && self->args[0]<=255 && SpawnableThings[self->args[0]]) { // Spawn an item - if (!((level.flags & LEVEL_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) + if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) || !(GetDefaultByType (SpawnableThings[self->args[0]])->flags3 & MF3_ISMONSTER)) { // Only spawn monsters if not -nomonsters Spawn (SpawnableThings[self->args[0]], @@ -290,7 +290,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_SoAExplode) } if (self->args[0]>=0 && self->args[0]<=255 && SpawnableThings[self->args[0]]) { // Spawn an item - if (!((level.flags & LEVEL_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) + if (!((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) || !(GetDefaultByType (SpawnableThings[self->args[0]])->flags3 & MF3_ISMONSTER)) { // Only spawn monsters if not -nomonsters Spawn (SpawnableThings[self->args[0]], diff --git a/src/g_hub.cpp b/src/g_hub.cpp index f533825019..162aacd164 100644 --- a/src/g_hub.cpp +++ b/src/g_hub.cpp @@ -127,17 +127,16 @@ void G_LeavingHub(int mode, cluster_info_t * cluster, wbstartstruct_t * wbs) wbs->plyr[j].ssecret += hubdata[i].plyr[j].ssecret; } } - if (cluster->clustername) + if (cluster->ClusterName.IsNotEmpty()) { if (cluster->flags & CLUSTER_LOOKUPNAME) { - strncpy(level.level_name, GStrings(cluster->clustername), 64); + level.LevelName = GStrings(cluster->ClusterName); } else { - strncpy(level.level_name, cluster->clustername, 64); + level.LevelName = cluster->ClusterName; } - level.level_name[63]=0; } } } diff --git a/src/g_level.cpp b/src/g_level.cpp index 445fcec684..64c173ec5d 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1,6 +1,6 @@ /* ** g_level.cpp -** Parses MAPINFO and controls movement between levels +** controls movement between levels ** **--------------------------------------------------------------------------- ** Copyright 1998-2006 Randy Heit @@ -83,14 +83,11 @@ #include "g_hub.h" + EXTERN_CVAR (Float, sv_gravity) EXTERN_CVAR (Float, sv_aircontrol) EXTERN_CVAR (Int, disableautosave) -// Hey, GCC, these macros better be safe! -#define lioffset(x) ((size_t)&((level_info_t*)1)->x - 1) -#define cioffset(x) ((size_t)&((cluster_info_t*)1)->x - 1) - #define SNAP_ID MAKE_ID('s','n','A','p') #define DSNP_ID MAKE_ID('d','s','N','p') #define VIST_ID MAKE_ID('v','i','S','t') @@ -98,58 +95,8 @@ EXTERN_CVAR (Int, disableautosave) #define RCLS_ID MAKE_ID('r','c','L','s') #define PCLS_ID MAKE_ID('p','c','L','s') -static int FindEndSequence (int type, const char *picname); static void SetEndSequence (char *nextmap, int type); -static void InitPlayerClasses (); -static void ParseEpisodeInfo (FScanner &sc); -static void G_DoParseMapInfo (int lump); -static void SetLevelNum (level_info_t *info, int num); -static void ClearEpisodes (); -static void ClearLevelInfoStrings (level_info_t *linfo); -static void ClearClusterInfoStrings (cluster_info_t *cinfo); -static void ParseSkill (FScanner &sc); -static void G_VerifySkill(); - -struct FOptionalMapinfoParser -{ - const char *keyword; - MIParseFunc parsefunc; -}; - -static TArray optmapinf(TArray::NoInit); - - -void AddOptionalMapinfoParser(const char *keyword, MIParseFunc parsefunc) -{ - FOptionalMapinfoParser mi; - - mi.keyword = keyword; - mi.parsefunc = parsefunc; - optmapinf.Push(mi); -} - -static void ParseOptionalBlock(const char *keyword, FScanner &sc, level_info_t *info) -{ - for(unsigned i=0;i wadclusterinfos; -TArray wadlevelinfos; -TArray AllSkills; - -// MAPINFO is parsed slightly differently when the map name is just a number. -static bool HexenHack; - -static char unnamed[] = "Unnamed"; -static level_info_t TheDefaultLevelInfo = -{ - "", // mapname - 0, // levelnum - "", // pname, - "", // nextmap - "", // secretmap - "SKY1", // skypic1 - 0, // cluster - 0, // partime - 0, // sucktime - 0, // flags - NULL, // music - unnamed, // level_name - "COLORMAP", // fadetable - +8, // WallVertLight - -8, // WallHorizLight - "", // [RC] F1 -}; - -static cluster_info_t TheDefaultClusterInfo = { 0 }; - - - -static const char *MapInfoTopLevel[] = -{ - "map", - "defaultmap", - "clusterdef", - "episode", - "clearepisodes", - "skill", - "clearskills", - "adddefaultmap", - "gamedefaults", - NULL -}; - -enum -{ - MITL_MAP, - MITL_DEFAULTMAP, - MITL_CLUSTERDEF, - MITL_EPISODE, - MITL_CLEAREPISODES, - MITL_SKILL, - MITL_CLEARSKILLS, - MITL_ADDDEFAULTMAP, - MITL_GAMEDEFAULTS, -}; - -static const char *MapInfoMapLevel[] = -{ - "levelnum", - "next", - "secretnext", - "cluster", - "sky1", - "sky2", - "fade", - "outsidefog", - "titlepatch", - "par", - "sucktime", - "music", - "nointermission", - "intermission", - "doublesky", - "nosoundclipping", - "allowmonstertelefrags", - "map07special", - "baronspecial", - "cyberdemonspecial", - "spidermastermindspecial", - "minotaurspecial", - "dsparilspecial", - "ironlichspecial", - "specialaction_exitlevel", - "specialaction_opendoor", - "specialaction_lowerfloor", - "specialaction_killmonsters", - "lightning", - "fadetable", - "evenlighting", - "smoothlighting", - "noautosequences", - "autosequences", - "forcenoskystretch", - "skystretch", - "allowfreelook", - "nofreelook", - "allowjump", - "nojump", - "fallingdamage", // Hexen falling damage - "oldfallingdamage", // Lesser ZDoom falling damage - "forcefallingdamage", // Skull Tag compatibility name for oldfallingdamage - "strifefallingdamage", // Strife's falling damage is really unforgiving - "nofallingdamage", - "noallies", - "cdtrack", - "cdid", - "cd_start_track", - "cd_end1_track", - "cd_end2_track", - "cd_end3_track", - "cd_intermission_track", - "cd_title_track", - "warptrans", - "vertwallshade", - "horizwallshade", - "gravity", - "aircontrol", - "filterstarts", - "activateowndeathspecials", - "killeractivatesdeathspecials", - "missilesactivateimpactlines", - "missileshootersactivetimpactlines", - "noinventorybar", - "deathslideshow", - "redirect", - "strictmonsteractivation", - "laxmonsteractivation", - "additive_scrollers", - "interpic", - "exitpic", - "enterpic", - "intermusic", - "airsupply", - "specialaction", - "keepfullinventory", - "monsterfallingdamage", - "nomonsterfallingdamage", - "sndseq", - "sndinfo", - "soundinfo", - "clipmidtextures", - "wrapmidtextures", - "allowcrouch", - "nocrouch", - "pausemusicinmenus", - "compat_shorttex", - "compat_stairs", - "compat_limitpain", - "compat_nopassover", - "compat_notossdrops", - "compat_useblocking", - "compat_nodoorlight", - "compat_ravenscroll", - "compat_soundtarget", - "compat_dehhealth", - "compat_trace", - "compat_dropoff", - "compat_boomscroll", - "compat_invisibility", - "compat_silent_instant_floors", - "compat_sectorsounds", - "compat_missileclip", - "bordertexture", - "f1", // [RC] F1 help - "noinfighting", - "normalinfighting", - "totalinfighting", - "infiniteflightpowerup", - "noinfiniteflightpowerup", - "allowrespawn", - "teamdamage", - "teamplayon", - "teamplayoff", - "checkswitchrange", - "nocheckswitchrange", - "translator", - "unfreezesingleplayerconversations", - "nobotnodes", - NULL -}; - -enum EMIType -{ - MITYPE_EATNEXT, - MITYPE_IGNORE, - MITYPE_INT, - MITYPE_FLOAT, - MITYPE_HEX, - MITYPE_COLOR, - MITYPE_MAPNAME, - MITYPE_LUMPNAME, - MITYPE_SKY, - MITYPE_SETFLAG, - MITYPE_CLRFLAG, - MITYPE_SCFLAGS, - MITYPE_CLUSTER, - MITYPE_STRING, - MITYPE_MUSIC, - MITYPE_RELLIGHT, - MITYPE_CLRBYTES, - MITYPE_REDIRECT, - MITYPE_SPECIALACTION, - MITYPE_COMPATFLAG, - MITYPE_STRINGT, -}; - -struct MapInfoHandler -{ - EMIType type; - QWORD data1, data2; -} -MapHandlers[] = -{ - { MITYPE_INT, lioffset(levelnum), 0 }, - { MITYPE_MAPNAME, lioffset(nextmap), 0 }, - { MITYPE_MAPNAME, lioffset(secretmap), 0 }, - { MITYPE_CLUSTER, lioffset(cluster), 0 }, - { MITYPE_SKY, lioffset(skypic1), lioffset(skyspeed1) }, - { MITYPE_SKY, lioffset(skypic2), lioffset(skyspeed2) }, - { MITYPE_COLOR, lioffset(fadeto), 0 }, - { MITYPE_COLOR, lioffset(outsidefog), 0 }, - { MITYPE_LUMPNAME, lioffset(pname), 0 }, - { MITYPE_INT, lioffset(partime), 0 }, - { MITYPE_INT, lioffset(sucktime), 0 }, - { MITYPE_MUSIC, lioffset(music), lioffset(musicorder) }, - { MITYPE_SETFLAG, LEVEL_NOINTERMISSION, 0 }, - { MITYPE_CLRFLAG, LEVEL_NOINTERMISSION, 0 }, - { MITYPE_SETFLAG, LEVEL_DOUBLESKY, 0 }, - { MITYPE_IGNORE, 0, 0 }, // was nosoundclipping - { MITYPE_SETFLAG, LEVEL_MONSTERSTELEFRAG, 0 }, - { MITYPE_SETFLAG, LEVEL_MAP07SPECIAL, 0 }, - { MITYPE_SETFLAG, LEVEL_BRUISERSPECIAL, 0 }, - { MITYPE_SETFLAG, LEVEL_CYBORGSPECIAL, 0 }, - { MITYPE_SETFLAG, LEVEL_SPIDERSPECIAL, 0 }, - { MITYPE_SETFLAG, LEVEL_MINOTAURSPECIAL, 0 }, - { MITYPE_SETFLAG, LEVEL_SORCERER2SPECIAL, 0 }, - { MITYPE_SETFLAG, LEVEL_HEADSPECIAL, 0 }, - { MITYPE_SCFLAGS, 0, ~LEVEL_SPECACTIONSMASK }, - { MITYPE_SCFLAGS, LEVEL_SPECOPENDOOR, ~LEVEL_SPECACTIONSMASK }, - { MITYPE_SCFLAGS, LEVEL_SPECLOWERFLOOR, ~LEVEL_SPECACTIONSMASK }, - { MITYPE_SETFLAG, LEVEL_SPECKILLMONSTERS, 0 }, - { MITYPE_SETFLAG, LEVEL_STARTLIGHTNING, 0 }, - { MITYPE_LUMPNAME, lioffset(fadetable), 0 }, - { MITYPE_CLRBYTES, lioffset(WallVertLight), lioffset(WallHorizLight) }, - { MITYPE_SETFLAG, LEVEL_SMOOTHLIGHTING, 0 }, - { MITYPE_SETFLAG, LEVEL_SNDSEQTOTALCTRL, 0 }, - { MITYPE_CLRFLAG, LEVEL_SNDSEQTOTALCTRL, 0 }, - { MITYPE_SETFLAG, LEVEL_FORCENOSKYSTRETCH, 0 }, - { MITYPE_CLRFLAG, LEVEL_FORCENOSKYSTRETCH, 0 }, - { MITYPE_SCFLAGS, LEVEL_FREELOOK_YES, ~LEVEL_FREELOOK_NO }, - { MITYPE_SCFLAGS, LEVEL_FREELOOK_NO, ~LEVEL_FREELOOK_YES }, - { MITYPE_CLRFLAG, LEVEL_JUMP_NO, 0 }, - { MITYPE_SETFLAG, LEVEL_JUMP_NO, 0 }, - { MITYPE_SCFLAGS, LEVEL_FALLDMG_HX, ~LEVEL_FALLDMG_ZD }, - { MITYPE_SCFLAGS, LEVEL_FALLDMG_ZD, ~LEVEL_FALLDMG_HX }, - { MITYPE_SCFLAGS, LEVEL_FALLDMG_ZD, ~LEVEL_FALLDMG_HX }, - { MITYPE_SETFLAG, LEVEL_FALLDMG_ZD|LEVEL_FALLDMG_HX, 0 }, - { MITYPE_SCFLAGS, 0, ~(LEVEL_FALLDMG_ZD|LEVEL_FALLDMG_HX) }, - { MITYPE_SETFLAG, LEVEL_NOALLIES, 0 }, - { MITYPE_INT, lioffset(cdtrack), 0 }, - { MITYPE_HEX, lioffset(cdid), 0 }, - { MITYPE_EATNEXT, 0, 0 }, - { MITYPE_EATNEXT, 0, 0 }, - { MITYPE_EATNEXT, 0, 0 }, - { MITYPE_EATNEXT, 0, 0 }, - { MITYPE_EATNEXT, 0, 0 }, - { MITYPE_EATNEXT, 0, 0 }, - { MITYPE_INT, lioffset(WarpTrans), 0 }, - { MITYPE_RELLIGHT, lioffset(WallVertLight), 0 }, - { MITYPE_RELLIGHT, lioffset(WallHorizLight), 0 }, - { MITYPE_FLOAT, lioffset(gravity), 0 }, - { MITYPE_FLOAT, lioffset(aircontrol), 0 }, - { MITYPE_SETFLAG, LEVEL_FILTERSTARTS, 0 }, - { MITYPE_SETFLAG, LEVEL_ACTOWNSPECIAL, 0 }, - { MITYPE_CLRFLAG, LEVEL_ACTOWNSPECIAL, 0 }, - { MITYPE_SETFLAG, LEVEL_MISSILESACTIVATEIMPACT, 0 }, - { MITYPE_CLRFLAG, LEVEL_MISSILESACTIVATEIMPACT, 0 }, - { MITYPE_SETFLAG, LEVEL_NOINVENTORYBAR, 0 }, - { MITYPE_SETFLAG, LEVEL_DEATHSLIDESHOW, 0 }, - { MITYPE_REDIRECT, lioffset(RedirectMap), 0 }, - { MITYPE_CLRFLAG, LEVEL_LAXMONSTERACTIVATION, LEVEL_LAXACTIVATIONMAPINFO }, - { MITYPE_SETFLAG, LEVEL_LAXMONSTERACTIVATION, LEVEL_LAXACTIVATIONMAPINFO }, - { MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL}, - { MITYPE_STRING, lioffset(exitpic), 0 }, - { MITYPE_STRING, lioffset(exitpic), 0 }, - { MITYPE_STRING, lioffset(enterpic), 0 }, - { MITYPE_MUSIC, lioffset(intermusic), lioffset(intermusicorder) }, - { MITYPE_INT, lioffset(airsupply), 0 }, - { MITYPE_SPECIALACTION, lioffset(specialactions), 0 }, - { MITYPE_SETFLAG, LEVEL_KEEPFULLINVENTORY, 0 }, - { MITYPE_SETFLAG, LEVEL_MONSTERFALLINGDAMAGE, 0 }, - { MITYPE_CLRFLAG, LEVEL_MONSTERFALLINGDAMAGE, 0 }, - { MITYPE_STRING, lioffset(sndseq), 0 }, - { MITYPE_STRING, lioffset(soundinfo), 0 }, - { MITYPE_STRING, lioffset(soundinfo), 0 }, - { MITYPE_SETFLAG, LEVEL_CLIPMIDTEX, 0 }, - { MITYPE_SETFLAG, LEVEL_WRAPMIDTEX, 0 }, - { MITYPE_CLRFLAG, LEVEL_CROUCH_NO, 0 }, - { MITYPE_SETFLAG, LEVEL_CROUCH_NO, 0 }, - { MITYPE_SCFLAGS, LEVEL_PAUSE_MUSIC_IN_MENUS, 0 }, - { MITYPE_COMPATFLAG, COMPATF_SHORTTEX}, - { MITYPE_COMPATFLAG, COMPATF_STAIRINDEX}, - { MITYPE_COMPATFLAG, COMPATF_LIMITPAIN}, - { MITYPE_COMPATFLAG, COMPATF_NO_PASSMOBJ}, - { MITYPE_COMPATFLAG, COMPATF_NOTOSSDROPS}, - { MITYPE_COMPATFLAG, COMPATF_USEBLOCKING}, - { MITYPE_COMPATFLAG, COMPATF_NODOORLIGHT}, - { MITYPE_COMPATFLAG, COMPATF_RAVENSCROLL}, - { MITYPE_COMPATFLAG, COMPATF_SOUNDTARGET}, - { MITYPE_COMPATFLAG, COMPATF_DEHHEALTH}, - { MITYPE_COMPATFLAG, COMPATF_TRACE}, - { MITYPE_COMPATFLAG, COMPATF_DROPOFF}, - { MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL}, - { MITYPE_COMPATFLAG, COMPATF_INVISIBILITY}, - { MITYPE_COMPATFLAG, COMPATF_SILENT_INSTANT_FLOORS}, - { MITYPE_COMPATFLAG, COMPATF_SECTORSOUNDS}, - { MITYPE_COMPATFLAG, COMPATF_MISSILECLIP}, - { MITYPE_LUMPNAME, lioffset(bordertexture), 0 }, - { MITYPE_LUMPNAME, lioffset(f1), 0, }, - { MITYPE_SCFLAGS, LEVEL_NOINFIGHTING, ~LEVEL_TOTALINFIGHTING }, - { MITYPE_SCFLAGS, 0, ~(LEVEL_NOINFIGHTING|LEVEL_TOTALINFIGHTING)}, - { MITYPE_SCFLAGS, LEVEL_TOTALINFIGHTING, ~LEVEL_NOINFIGHTING }, - { MITYPE_SETFLAG, LEVEL_INFINITE_FLIGHT, 0 }, - { MITYPE_CLRFLAG, LEVEL_INFINITE_FLIGHT, 0 }, - { MITYPE_SETFLAG, LEVEL_ALLOWRESPAWN, 0 }, - { MITYPE_FLOAT, lioffset(teamdamage), 0 }, - { MITYPE_SCFLAGS, LEVEL_FORCETEAMPLAYON, ~LEVEL_FORCETEAMPLAYOFF }, - { MITYPE_SCFLAGS, LEVEL_FORCETEAMPLAYOFF, ~LEVEL_FORCETEAMPLAYON }, - { MITYPE_SETFLAG, LEVEL_CHECKSWITCHRANGE, 0 }, - { MITYPE_CLRFLAG, LEVEL_CHECKSWITCHRANGE, 0 }, - { MITYPE_STRING, lioffset(translator), 0 }, - { MITYPE_SETFLAG, LEVEL_CONV_SINGLE_UNFREEZE, 0 }, - { MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes -}; - -static const char *MapInfoClusterLevel[] = -{ - "entertext", - "exittext", - "music", - "flat", - "pic", - "hub", - "cdtrack", - "cdid", - "entertextislump", - "exittextislump", - "name", - NULL -}; - -MapInfoHandler ClusterHandlers[] = -{ - { MITYPE_STRINGT, cioffset(entertext), CLUSTER_LOOKUPENTERTEXT }, - { MITYPE_STRINGT, cioffset(exittext), CLUSTER_LOOKUPEXITTEXT }, - { MITYPE_MUSIC, cioffset(messagemusic), cioffset(musicorder) }, - { MITYPE_LUMPNAME, cioffset(finaleflat), 0 }, - { MITYPE_LUMPNAME, cioffset(finaleflat), CLUSTER_FINALEPIC }, - { MITYPE_SETFLAG, CLUSTER_HUB, 0 }, - { MITYPE_INT, cioffset(cdtrack), 0 }, - { MITYPE_HEX, cioffset(cdid), 0 }, - { MITYPE_SETFLAG, CLUSTER_ENTERTEXTINLUMP, 0 }, - { MITYPE_SETFLAG, CLUSTER_EXITTEXTINLUMP, 0 }, - { MITYPE_STRINGT, cioffset(clustername), CLUSTER_LOOKUPNAME }, -}; - -static void ParseMapInfoLower (FScanner &sc, - MapInfoHandler *handlers, - const char *strings[], - level_info_t *levelinfo, - cluster_info_t *clusterinfo, - QWORD levelflags); - -static int FindWadLevelInfo (const char *name) -{ - for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) - if (!strnicmp (name, wadlevelinfos[i].mapname, 8)) - return i; - - return -1; -} - -static int FindWadClusterInfo (int cluster) -{ - for (unsigned int i = 0; i < wadclusterinfos.Size(); i++) - if (wadclusterinfos[i].cluster == cluster) - return i; - - return -1; -} - -static void SetLevelDefaults (level_info_t *levelinfo) -{ - memset (levelinfo, 0, sizeof(*levelinfo)); - levelinfo->snapshot = NULL; - levelinfo->outsidefog = 0xff000000; - levelinfo->WallHorizLight = -8; - levelinfo->WallVertLight = +8; - strncpy (levelinfo->fadetable, "COLORMAP", 8); - strcpy (levelinfo->skypic1, "-NOFLAT-"); - strcpy (levelinfo->skypic2, "-NOFLAT-"); - strcpy (levelinfo->bordertexture, gameinfo.borderFlat); - if (gameinfo.gametype != GAME_Hexen) - { - // For maps without a BEHAVIOR, this will be cleared. - levelinfo->flags |= LEVEL_LAXMONSTERACTIVATION; - } - levelinfo->airsupply = 10; -} - +//========================================================================== // -// G_ParseMapInfo -// Parses the MAPINFO lumps of all loaded WADs and generates -// data for wadlevelinfos and wadclusterinfos. // -void G_ParseMapInfo () -{ - int lump, lastlump = 0; +//========================================================================== - atterm (G_UnloadMapInfo); - - SetLevelDefaults (&gamedefaults); - - // Parse the default MAPINFO for the current game. - for(int i=0; i<2; i++) - { - if (gameinfo.mapinfo[i] != NULL) - { - G_DoParseMapInfo(Wads.GetNumForFullName(gameinfo.mapinfo[i])); - } - } - - // Parse any extra MAPINFOs. - while ((lump = Wads.FindLump ("MAPINFO", &lastlump)) != -1) - { - G_DoParseMapInfo (lump); - } - EndSequences.ShrinkToFit (); - - if (EpiDef.numitems == 0) - { - I_FatalError ("You cannot use clearepisodes in a MAPINFO if you do not define any new episodes after it."); - } - if (AllSkills.Size()==0) - { - I_FatalError ("You cannot use clearskills in a MAPINFO if you do not define any new skills after it."); - } - ClearLevelInfoStrings (&gamedefaults); -} - -static FSpecialAction *CopySpecialActions(FSpecialAction *spec) -{ - FSpecialAction **pSpec = &spec; - - while (*pSpec) - { - FSpecialAction *newspec = new FSpecialAction; - *newspec = **pSpec; - *pSpec = newspec; - pSpec = &newspec->Next; - } - return spec; -} - -static FOptionalMapinfoData *CopyOptData(FOptionalMapinfoData *opdata) -{ - FOptionalMapinfoData **opt = &opdata; - - while (*opt) - { - FOptionalMapinfoData *newop = (*opt)->Clone(); - *opt = newop; - opt = &newop->Next; - } - return opdata; -} - -static void CopyString (char *& string) -{ - if (string != NULL) - string = copystring(string); -} - -static void SafeDelete(char *&string) -{ - if (string != NULL) - { - delete[] string; - string = NULL; - } -} - -static void ClearLevelInfoStrings(level_info_t *linfo) -{ - SafeDelete(linfo->music); - SafeDelete(linfo->intermusic); - SafeDelete(linfo->level_name); - SafeDelete(linfo->translator); - SafeDelete(linfo->enterpic); - SafeDelete(linfo->exitpic); - SafeDelete(linfo->soundinfo); - SafeDelete(linfo->sndseq); - for (FSpecialAction *spac = linfo->specialactions; spac != NULL; ) - { - FSpecialAction *next = spac->Next; - delete spac; - spac = next; - } - for (FOptionalMapinfoData *spac = linfo->opdata; spac != NULL; ) - { - FOptionalMapinfoData *next = spac->Next; - delete spac; - spac = next; - } -} - -static void ClearClusterInfoStrings(cluster_info_t *cinfo) -{ - SafeDelete(cinfo->exittext); - SafeDelete(cinfo->entertext); - SafeDelete(cinfo->messagemusic); - SafeDelete(cinfo->clustername); -} - -static void CopyLevelInfo(level_info_t *levelinfo, level_info_t *from) -{ - memcpy (levelinfo, from, sizeof(*levelinfo)); - CopyString(levelinfo->music); - CopyString(levelinfo->intermusic); - CopyString(levelinfo->translator); - CopyString(levelinfo->enterpic); - CopyString(levelinfo->exitpic); - CopyString(levelinfo->soundinfo); - CopyString(levelinfo->sndseq); - levelinfo->specialactions = CopySpecialActions(levelinfo->specialactions); - levelinfo->opdata = CopyOptData(levelinfo->opdata); -} - - -static void G_DoParseMapInfo (int lump) -{ - level_info_t defaultinfo; - level_info_t *levelinfo; - int levelindex; - cluster_info_t *clusterinfo; - int clusterindex; - QWORD levelflags; - - FScanner sc(lump); - - CopyLevelInfo(&defaultinfo, &gamedefaults); - HexenHack = false; - - while (sc.GetString ()) - { - switch (sc.MustMatchString (MapInfoTopLevel)) - { - case MITL_GAMEDEFAULTS: - ClearLevelInfoStrings(&gamedefaults); - SetLevelDefaults (&gamedefaults); - ParseMapInfoLower (sc, MapHandlers, MapInfoMapLevel, &gamedefaults, NULL, defaultinfo.flags); - ClearLevelInfoStrings(&defaultinfo); - CopyLevelInfo(&defaultinfo, &gamedefaults); - break; - - case MITL_DEFAULTMAP: - ClearLevelInfoStrings(&defaultinfo); - SetLevelDefaults (&defaultinfo); - ParseMapInfoLower (sc, MapHandlers, MapInfoMapLevel, &defaultinfo, NULL, defaultinfo.flags); - break; - - case MITL_ADDDEFAULTMAP: - // Same as above but adds to the existing definitions instead of replacing them completely - ParseMapInfoLower (sc, MapHandlers, MapInfoMapLevel, &defaultinfo, NULL, defaultinfo.flags); - break; - - case MITL_MAP: // map - { - char maptemp[8]; - const char *mapname; - - levelflags = defaultinfo.flags; - sc.MustGetString (); - mapname = sc.String; - if (IsNum (mapname)) - { // MAPNAME is a number; assume a Hexen wad - int mapnum = atoi (mapname); - mysnprintf (maptemp, countof(maptemp), "MAP%02d", mapnum); - mapname = maptemp; - HexenHack = true; - // Hexen levels are automatically nointermission, - // no auto sound sequences, falling damage, - // monsters activate their own specials, and missiles - // are always the activators of impact lines. - levelflags |= LEVEL_NOINTERMISSION - | LEVEL_SNDSEQTOTALCTRL - | LEVEL_FALLDMG_HX - | LEVEL_ACTOWNSPECIAL - | LEVEL_MISSILESACTIVATEIMPACT - | LEVEL_INFINITE_FLIGHT - | LEVEL_MONSTERFALLINGDAMAGE - | LEVEL_HEXENHACK; - } - levelindex = FindWadLevelInfo (mapname); - if (levelindex == -1) - { - levelindex = wadlevelinfos.Reserve(1); - } - else - { - ClearLevelInfoStrings (&wadlevelinfos[levelindex]); - } - levelinfo = &wadlevelinfos[levelindex]; - CopyLevelInfo(levelinfo, &defaultinfo); - if (HexenHack) - { - levelinfo->WallHorizLight = levelinfo->WallVertLight = 0; - } - uppercopy (levelinfo->mapname, mapname); - sc.MustGetString (); - if (sc.String[0] == '$') - { - // For consistency with other definitions allow $Stringtablename here, too. - levelflags |= LEVEL_LOOKUPLEVELNAME; - ReplaceString (&levelinfo->level_name, sc.String + 1); - } - else - { - if (sc.Compare ("lookup")) - { - sc.MustGetString (); - levelflags |= LEVEL_LOOKUPLEVELNAME; - } - ReplaceString (&levelinfo->level_name, sc.String); - } - // Set up levelnum now so that you can use Teleport_NewMap specials - // to teleport to maps with standard names without needing a levelnum. - 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 (levelinfo->mapname[0] == 'E' && - levelinfo->mapname[1] >= '0' && levelinfo->mapname[1] <= '9' && - levelinfo->mapname[2] == 'M' && - levelinfo->mapname[3] >= '0' && levelinfo->mapname[3] <= '9') - { - int epinum = levelinfo->mapname[1] - '1'; - int mapnum = levelinfo->mapname[3] - '0'; - levelinfo->levelnum = epinum*10 + mapnum; - } - ParseMapInfoLower (sc, MapHandlers, MapInfoMapLevel, levelinfo, NULL, levelflags); - // When the second sky is -NOFLAT-, make it a copy of the first sky - if (strcmp (levelinfo->skypic2, "-NOFLAT-") == 0) - { - strcpy (levelinfo->skypic2, levelinfo->skypic1); - } - SetLevelNum (levelinfo, levelinfo->levelnum); // Wipe out matching levelnums from other maps. - /* can't do this here. - if (levelinfo->pname[0] != 0) - { - if (!TexMan.CheckForTexture(levelinfo->pname, FTexture::TEX_MiscPatch).Exists()) - { - levelinfo->pname[0] = 0; - } - } - */ - break; - } - - case MITL_CLUSTERDEF: // clusterdef - sc.MustGetNumber (); - clusterindex = FindWadClusterInfo (sc.Number); - if (clusterindex == -1) - { - clusterindex = wadclusterinfos.Reserve(1); - clusterinfo = &wadclusterinfos[clusterindex]; - } - else - { - clusterinfo = &wadclusterinfos[clusterindex]; - ClearClusterInfoStrings(clusterinfo); - } - memset (clusterinfo, 0, sizeof(cluster_info_t)); - clusterinfo->cluster = sc.Number; - ParseMapInfoLower (sc, ClusterHandlers, MapInfoClusterLevel, NULL, clusterinfo, 0); - break; - - case MITL_EPISODE: - ParseEpisodeInfo(sc); - break; - - case MITL_CLEAREPISODES: - ClearEpisodes(); - break; - - case MITL_SKILL: - ParseSkill(sc); - break; - - case MITL_CLEARSKILLS: - AllSkills.Clear(); - break; - - } - } - ClearLevelInfoStrings(&defaultinfo); -} - -static void ClearEpisodes() -{ - for (int i = 0; i < EpiDef.numitems; ++i) - { - delete[] const_cast(EpisodeMenu[i].name); - EpisodeMenu[i].name = NULL; - } - EpiDef.numitems = 0; -} - -static void ParseMapInfoLower (FScanner &sc, - MapInfoHandler *handlers, - const char *strings[], - level_info_t *levelinfo, - cluster_info_t *clusterinfo, - QWORD flags) -{ - int entry; - MapInfoHandler *handler; - BYTE *info; - - info = levelinfo ? (BYTE *)levelinfo : (BYTE *)clusterinfo; - - while (sc.GetString ()) - { - if (sc.MatchString (MapInfoTopLevel) != -1) - { - sc.UnGet (); - break; - } - entry = sc.MatchString (strings); - - if (entry == -1) - { - FString keyword = sc.String; - sc.MustGetString(); - if (levelinfo != NULL) - { - if (sc.Compare("{")) - { - ParseOptionalBlock(keyword, sc, levelinfo); - continue; - } - } - sc.ScriptError("Unknown keyword '%s'", keyword.GetChars()); - } - - handler = handlers + entry; - switch (handler->type) - { - case MITYPE_EATNEXT: - sc.MustGetString (); - break; - - case MITYPE_IGNORE: - break; - - case MITYPE_INT: - sc.MustGetNumber (); - *((int *)(info + handler->data1)) = sc.Number; - break; - - case MITYPE_FLOAT: - sc.MustGetFloat (); - *((float *)(info + handler->data1)) = sc.Float; - break; - - case MITYPE_HEX: - sc.MustGetString (); - *((int *)(info + handler->data1)) = strtoul (sc.String, NULL, 16); - break; - - case MITYPE_COLOR: - sc.MustGetString (); - *((DWORD *)(info + handler->data1)) = V_GetColor (NULL, sc.String); - break; - - case MITYPE_REDIRECT: - sc.MustGetString (); - levelinfo->RedirectType = sc.String; - // Intentional fall-through - - case MITYPE_MAPNAME: { - EndSequence newSeq; - bool useseq = false; - char maptemp[8]; - const char *mapname; - - sc.MustGetString (); - mapname = sc.String; - if (IsNum (mapname)) - { - int mapnum = atoi (mapname); - - if (HexenHack) - { - mysnprintf (maptemp, countof(maptemp), "&wt@%02d", mapnum); - } - else - { - mysnprintf (maptemp, countof(maptemp), "MAP%02d", mapnum); - } - mapname = maptemp; - } - if (stricmp (mapname, "endgame") == 0) - { - newSeq.Advanced = true; - newSeq.EndType = END_Pic1; - newSeq.PlayTheEnd = false; - newSeq.MusicLooping = true; - sc.MustGetStringName("{"); - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("pic")) - { - sc.MustGetString(); - newSeq.EndType = END_Pic; - newSeq.PicName = sc.String; - } - else if (sc.Compare("hscroll")) - { - newSeq.EndType = END_Bunny; - sc.MustGetString(); - newSeq.PicName = sc.String; - sc.MustGetString(); - newSeq.PicName2 = sc.String; - if (sc.CheckNumber()) - newSeq.PlayTheEnd = !!sc.Number; - } - else if (sc.Compare("vscroll")) - { - newSeq.EndType = END_Demon; - sc.MustGetString(); - newSeq.PicName = sc.String; - sc.MustGetString(); - newSeq.PicName2 = sc.String; - } - else if (sc.Compare("cast")) - { - newSeq.EndType = END_Cast; - } - else if (sc.Compare("music")) - { - sc.MustGetString(); - newSeq.Music = sc.String; - if (sc.CheckNumber()) - { - newSeq.MusicLooping = !!sc.Number; - } - } - } - useseq = true; - } - else if (strnicmp (mapname, "EndGame", 7) == 0) - { - int type; - switch (sc.String[7]) - { - case '1': type = END_Pic1; break; - case '2': type = END_Pic2; break; - case '3': type = END_Bunny; break; - case 'C': type = END_Cast; break; - case 'W': type = END_Underwater; break; - case 'S': type = END_Strife; break; - default: type = END_Pic3; break; - } - newSeq.EndType = type; - useseq = true; - } - else if (stricmp (mapname, "endpic") == 0) - { - sc.MustGetString (); - newSeq.EndType = END_Pic; - newSeq.PicName = sc.String; - useseq = true; - } - else if (stricmp (mapname, "endbunny") == 0) - { - newSeq.EndType = END_Bunny; - useseq = true; - } - else if (stricmp (mapname, "endcast") == 0) - { - newSeq.EndType = END_Cast; - useseq = true; - } - else if (stricmp (mapname, "enddemon") == 0) - { - newSeq.EndType = END_Demon; - useseq = true; - } - else if (stricmp (mapname, "endchess") == 0) - { - newSeq.EndType = END_Chess; - useseq = true; - } - else if (stricmp (mapname, "endunderwater") == 0) - { - newSeq.EndType = END_Underwater; - useseq = true; - } - else if (stricmp (mapname, "endbuystrife") == 0) - { - newSeq.EndType = END_BuyStrife; - useseq = true; - } - else - { - strncpy ((char *)(info + handler->data1), mapname, 8); - } - if (useseq) - { - int seqnum = -1; - - if (!newSeq.Advanced) - { - seqnum = FindEndSequence (newSeq.EndType, newSeq.PicName); - } - - if (seqnum == -1) - { - seqnum = (int)EndSequences.Push (newSeq); - } - strcpy ((char *)(info + handler->data1), "enDSeQ"); - *((WORD *)(info + handler->data1 + 6)) = (WORD)seqnum; - } - break; - } - - case MITYPE_LUMPNAME: - sc.MustGetString (); - uppercopy ((char *)(info + handler->data1), sc.String); - flags |= handler->data2; - break; - - case MITYPE_SKY: - sc.MustGetString (); // get texture name; - uppercopy ((char *)(info + handler->data1), sc.String); - sc.MustGetFloat (); // get scroll speed - if (HexenHack) - { - sc.Float /= 256; - } - // Sky scroll speed is specified as pixels per tic, but we - // want pixels per millisecond. - *((float *)(info + handler->data2)) = sc.Float * 35 / 1000; - break; - - case MITYPE_SETFLAG: - flags |= handler->data1; - flags |= handler->data2; - break; - - case MITYPE_CLRFLAG: - flags &= ~handler->data1; - flags |= handler->data2; - break; - - case MITYPE_SCFLAGS: - flags = (flags & handler->data2) | handler->data1; - break; - - case MITYPE_CLUSTER: - sc.MustGetNumber (); - *((int *)(info + handler->data1)) = sc.Number; - // If this cluster hasn't been defined yet, add it. This is especially needed - // for Hexen, because it doesn't have clusterdefs. If we don't do this, every - // level on Hexen will sometimes be considered as being on the same hub, - // depending on the check done. - if (FindWadClusterInfo (sc.Number) == -1) - { - unsigned int clusterindex = wadclusterinfos.Reserve(1); - clusterinfo = &wadclusterinfos[clusterindex]; - memset (clusterinfo, 0, sizeof(cluster_info_t)); - clusterinfo->cluster = sc.Number; - if (HexenHack) - { - clusterinfo->flags |= CLUSTER_HUB; - } - } - break; - - case MITYPE_STRINGT: - sc.MustGetString (); - if (sc.String[0] == '$') - { - // For consistency with other definitions allow $Stringtablename here, too. - flags |= handler->data2; - ReplaceString ((char **)(info + handler->data1), sc.String+1); - } - else - { - if (sc.Compare ("lookup")) - { - flags |= handler->data2; - sc.MustGetString (); - } - ReplaceString ((char **)(info + handler->data1), sc.String); - } - break; - - case MITYPE_STRING: - sc.MustGetString(); - ReplaceString ((char **)(info + handler->data1), sc.String); - break; - - case MITYPE_MUSIC: - sc.MustGetString (); - { - char *colon = strchr (sc.String, ':'); - if (colon) - { - *colon = 0; - } - ReplaceString ((char **)(info + handler->data1), sc.String); - *((int *)(info + handler->data2)) = colon ? atoi (colon + 1) : 0; - if (levelinfo != NULL) - { - // Flag the level so that the $MAP command doesn't override this. - flags|=LEVEL_MUSICDEFINED; - } - } - break; - - case MITYPE_RELLIGHT: - sc.MustGetNumber (); - *((SBYTE *)(info + handler->data1)) = (SBYTE)clamp (sc.Number / 2, -128, 127); - break; - - case MITYPE_CLRBYTES: - *((BYTE *)(info + handler->data1)) = 0; - *((BYTE *)(info + handler->data2)) = 0; - break; - - case MITYPE_SPECIALACTION: - { - FSpecialAction **so = (FSpecialAction**)(info + handler->data1); - FSpecialAction *sa = new FSpecialAction; - int min_arg, max_arg; - sa->Next = *so; - *so = sa; - sc.SetCMode(true); - sc.MustGetString(); - sa->Type = FName(sc.String); - sc.CheckString(","); - sc.MustGetString(); - sa->Action = P_FindLineSpecial(sc.String, &min_arg, &max_arg); - if (sa->Action == 0 || min_arg < 0) - { - sc.ScriptError("Unknown specialaction '%s'"); - } - int j = 0; - while (j < 5 && sc.CheckString(",")) - { - sc.MustGetNumber(); - sa->Args[j++] = sc.Number; - } - /* - if (jmax) - { - // Should be an error but can't for compatibility. - } - */ - sc.SetCMode(false); - } - break; - - case MITYPE_COMPATFLAG: - if (!sc.CheckNumber()) sc.Number = 1; - - if (levelinfo != NULL) - { - if (sc.Number) levelinfo->compatflags |= (DWORD)handler->data1; - else levelinfo->compatflags &= ~ (DWORD)handler->data1; - levelinfo->compatmask |= (DWORD)handler->data1; - } - break; - } - } - if (levelinfo) - { - levelinfo->flags = flags; - } - else - { - clusterinfo->flags = flags; - } -} - -// Episode definitions start with the header "episode " -// and then can be followed by any of the following: -// -// name "Episode name as text" -// picname "Picture to display the episode name" -// key "Shortcut key for the menu" -// noskillmenu -// remove - -static void ParseEpisodeInfo (FScanner &sc) -{ - int i; - char map[9]; - char *pic = NULL; - bool picisgfx = false; // Shut up, GCC!!!! - bool remove = false; - char key = 0; - bool noskill = false; - bool optional = false; - bool extended = false; - - // Get map name - sc.MustGetString (); - uppercopy (map, sc.String); - map[8] = 0; - - sc.MustGetString (); - if (sc.Compare ("teaser")) - { - sc.MustGetString (); - if (gameinfo.flags & GI_SHAREWARE) - { - uppercopy (map, sc.String); - } - sc.MustGetString (); - } - do - { - if (sc.Compare ("optional")) - { - // For M4 in Doom - optional = true; - } - else if (sc.Compare ("extended")) - { - // For M4 and M5 in Heretic - extended = true; - } - else if (sc.Compare ("name")) - { - sc.MustGetString (); - ReplaceString (&pic, sc.String); - picisgfx = false; - } - else if (sc.Compare ("picname")) - { - sc.MustGetString (); - ReplaceString (&pic, sc.String); - picisgfx = true; - } - else if (sc.Compare ("remove")) - { - remove = true; - } - else if (sc.Compare ("key")) - { - sc.MustGetString (); - key = sc.String[0]; - } - else if (sc.Compare("noskillmenu")) - { - noskill = true; - } - else - { - sc.UnGet (); - break; - } - } - while (sc.GetString ()); - - if (extended && !(gameinfo.flags & GI_MENUHACK_EXTENDED)) - { // If the episode is for the extended Heretic, but this is - // not the extended Heretic, ignore it. - return; - } - - if (optional && !remove) - { - if (!P_CheckMapData(map)) - { - // If the episode is optional and the map does not exist - // just ignore this episode definition. - return; - } - } - - - for (i = 0; i < EpiDef.numitems; ++i) - { - if (strncmp (EpisodeMaps[i], map, 8) == 0) - { - break; - } - } - - if (remove) - { - // If the remove property is given for an episode, remove it. - if (i < EpiDef.numitems) - { - if (i+1 < EpiDef.numitems) - { - memmove (&EpisodeMaps[i], &EpisodeMaps[i+1], - sizeof(EpisodeMaps[0])*(EpiDef.numitems - i - 1)); - memmove (&EpisodeMenu[i], &EpisodeMenu[i+1], - sizeof(EpisodeMenu[0])*(EpiDef.numitems - i - 1)); - memmove (&EpisodeNoSkill[i], &EpisodeNoSkill[i+1], - sizeof(EpisodeNoSkill[0])*(EpiDef.numitems - i - 1)); - } - EpiDef.numitems--; - } - } - else - { - if (pic == NULL) - { - pic = copystring (map); - picisgfx = false; - } - - if (i == EpiDef.numitems) - { - if (EpiDef.numitems == MAX_EPISODES) - { - i = EpiDef.numitems - 1; - } - else - { - i = EpiDef.numitems++; - } - } - else - { - delete[] const_cast(EpisodeMenu[i].name); - } - - EpisodeMenu[i].name = pic; - EpisodeMenu[i].alphaKey = tolower(key); - EpisodeMenu[i].fulltext = !picisgfx; - EpisodeNoSkill[i] = noskill; - strncpy (EpisodeMaps[i], map, 8); - } -} - -static int FindEndSequence (int type, const char *picname) +int FindEndSequence (int type, const char *picname) { unsigned int i, num; @@ -1467,6 +152,11 @@ static int FindEndSequence (int type, const char *picname) return -1; } +//========================================================================== +// +// +//========================================================================== + static void SetEndSequence (char *nextmap, int type) { int seqnum; @@ -1482,6 +172,11 @@ static void SetEndSequence (char *nextmap, int type) *((WORD *)(nextmap + 6)) = (WORD)seqnum; } +//========================================================================== +// +// +//========================================================================== + void G_SetForEndGame (char *nextmap) { if (!strncmp(nextmap, "enDSeQ",6)) return; // If there is already an end sequence please leave it alone!!! @@ -1505,85 +200,14 @@ void G_SetForEndGame (char *nextmap) } } -void G_UnloadMapInfo () -{ - unsigned int i; - - G_ClearSnapshots (); - - for (i = 0; i < wadlevelinfos.Size(); ++i) - { - ClearLevelInfoStrings (&wadlevelinfos[i]); - } - wadlevelinfos.Clear(); - - for (i = 0; i < wadclusterinfos.Size(); ++i) - { - ClearClusterInfoStrings (&wadclusterinfos[i]); - } - wadclusterinfos.Clear(); - - ClearEpisodes(); -} - -level_info_t *FindLevelByWarpTrans (int num) -{ - for (unsigned i = wadlevelinfos.Size(); i-- != 0; ) - if (wadlevelinfos[i].WarpTrans == num) - return &wadlevelinfos[i]; - - return NULL; -} - -static void zapDefereds (acsdefered_t *def) -{ - while (def) - { - acsdefered_t *next = def->next; - delete def; - def = next; - } -} - -void P_RemoveDefereds (void) -{ - // Remove any existing defereds - for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) - { - if (wadlevelinfos[i].defered) - { - zapDefereds (wadlevelinfos[i].defered); - wadlevelinfos[i].defered = NULL; - } - } -} - -bool CheckWarpTransMap (FString &mapname, bool substitute) -{ - if (mapname[0] == '&' && (mapname[1] & 0xDF) == 'W' && - (mapname[2] & 0xDF) == 'T' && mapname[3] == '@') - { - level_info_t *lev = FindLevelByWarpTrans (atoi (&mapname[4])); - if (lev != NULL) - { - mapname = lev->mapname; - return true; - } - else if (substitute) - { - char a = mapname[4], b = mapname[5]; - mapname = "MAP"; - mapname << a << b; - } - } - return false; -} - +//========================================================================== // // G_InitNew // Can be called by the startup code or the menu task, // consoleplayer, playeringame[] should be set. // +//========================================================================== + static FString d_mapname; static int d_skill=-1; @@ -1595,6 +219,11 @@ void G_DeferedInitNew (const char *mapname, int newskill) gameaction = ga_newgame2; } +//========================================================================== +// +// +//========================================================================== + CCMD (map) { if (netgame) @@ -1620,6 +249,11 @@ CCMD (map) } } +//========================================================================== +// +// +//========================================================================== + CCMD (open) { if (netgame) @@ -1648,6 +282,11 @@ CCMD (open) } +//========================================================================== +// +// +//========================================================================== + void G_NewInit () { int i; @@ -1680,6 +319,11 @@ void G_NewInit () NextSkill = -1; } +//========================================================================== +// +// +//========================================================================== + void G_DoNewGame (void) { G_NewInit (); @@ -1692,6 +336,38 @@ void G_DoNewGame (void) gameaction = ga_nothing; } +//========================================================================== +// +// Initializes player classes in case they are random. +// This gets called at the start of a new game, and the classes +// chosen here are used for the remainder of a single-player +// or coop game. These are ignored for deathmatch. +// +//========================================================================== + + +static void InitPlayerClasses () +{ + if (!savegamerestore) + { + for (int i = 0; i < MAXPLAYERS; ++i) + { + SinglePlayerClass[i] = players[i].userinfo.PlayerClass; + if (SinglePlayerClass[i] < 0 || !playeringame[i]) + { + SinglePlayerClass[i] = (pr_classchoice()) % PlayerClasses.Size (); + } + players[i].cls = NULL; + players[i].CurrentPlayerClass = SinglePlayerClass[i]; + } + } +} + +//========================================================================== +// +// +//========================================================================== + void G_InitNew (const char *mapname, bool bTitleLevel) { EGameSpeed oldSpeed; @@ -1807,16 +483,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel) rngseed = rngseed*3/2; } FRandom::StaticClearRandom (); - memset (ACS_WorldVars, 0, sizeof(ACS_WorldVars)); - memset (ACS_GlobalVars, 0, sizeof(ACS_GlobalVars)); - for (i = 0; i < NUM_WORLDVARS; ++i) - { - ACS_WorldArrays[i].Clear (); - } - for (i = 0; i < NUM_GLOBALVARS; ++i) - { - ACS_GlobalArrays[i].Clear (); - } + P_ClearACSVars(true); level.time = 0; level.maptime = 0; level.totaltime = 0; @@ -1870,9 +537,14 @@ static bool resetinventory; // Reset the inventory to the player's default for static bool unloading; static bool g_nomonsters; +//========================================================================== +// // [RH] The position parameter to these next three functions should // match the first parameter of the single player start spots // that should appear in the next map. +// +//========================================================================== + void G_ChangeLevel(const char *levelname, int position, bool keepFacing, int nextSkill, bool nointermission, bool resetinv, bool nomonsters) @@ -1887,7 +559,7 @@ void G_ChangeLevel(const char *levelname, int position, bool keepFacing, int nex if (strncmp(levelname, "enDSeQ", 6)) { - level_info_t *nextinfo = CheckLevelRedirect (FindLevelInfo (nextlevel)); + level_info_t *nextinfo = FindLevelInfo (nextlevel)->CheckLevelRedirect (); if (nextinfo) { nextlevel = nextinfo->mapname; @@ -1949,6 +621,11 @@ void G_ChangeLevel(const char *levelname, int position, bool keepFacing, int nex } } +//========================================================================== +// +// +//========================================================================== + const char *G_GetExitMap() { return level.nextmap; @@ -1968,6 +645,11 @@ const char *G_GetSecretExitMap() return nextmap; } +//========================================================================== +// +// +//========================================================================== + void G_ExitLevel (int position, bool keepFacing) { G_ChangeLevel(G_GetExitMap(), position, keepFacing); @@ -1978,6 +660,11 @@ void G_SecretExitLevel (int position) G_ChangeLevel(G_GetSecretExitMap(), position, false); } +//========================================================================== +// +// +//========================================================================== + void G_DoCompleted (void) { int i; @@ -2098,11 +785,7 @@ void G_DoCompleted (void) if (mode == FINISH_NextHub) { // Reset world variables for the new hub. - memset (ACS_WorldVars, 0, sizeof(ACS_WorldVars)); - for (i = 0; i < NUM_WORLDVARS; ++i) - { - ACS_WorldArrays[i].Clear (); - } + P_ClearACSVars(false); } // With hub statistics the time should be per hub. // Additionally there is a global time counter now so nothing is missed by changing it @@ -2132,6 +815,11 @@ void G_DoCompleted (void) WI_Start (&wminfo); } +//========================================================================== +// +// +//========================================================================== + class DAutosaver : public DThinker { DECLARE_CLASS (DAutosaver, DThinker) @@ -2147,9 +835,12 @@ void DAutosaver::Tick () Destroy (); } +//========================================================================== // // G_DoLoadLevel // +//========================================================================== + extern gamestate_t wipegamestate; void G_DoLoadLevel (int position, bool autosave) @@ -2175,18 +866,18 @@ void G_DoLoadLevel (int position, bool autosave) StatusBar->DetachAllMessages (); // Force 'teamplay' to 'true' if need be. - if (level.flags & LEVEL_FORCETEAMPLAYON) + if (level.flags2 & LEVEL2_FORCETEAMPLAYON) teamplay = true; // Force 'teamplay' to 'false' if need be. - if (level.flags & LEVEL_FORCETEAMPLAYOFF) + if (level.flags2 & LEVEL2_FORCETEAMPLAYOFF) teamplay = false; Printf ( "\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\n" TEXTCOLOR_BOLD "%s - %s\n\n", - level.mapname, level.level_name); + level.mapname, level.LevelName.GetChars()); if (wipegamestate == GS_LEVEL) wipegamestate = GS_FORCEWIPE; @@ -2223,11 +914,11 @@ void G_DoLoadLevel (int position, bool autosave) if (g_nomonsters) { - level.flags |= LEVEL_NOMONSTERS; + level.flags2 |= LEVEL2_NOMONSTERS; } else { - level.flags &= ~LEVEL_NOMONSTERS; + level.flags2 &= ~LEVEL2_NOMONSTERS; } P_SetupLevel (level.mapname, position); @@ -2294,9 +985,12 @@ void G_DoLoadLevel (int position, bool autosave) } +//========================================================================== // // G_WorldDone // +//========================================================================== + void G_WorldDone (void) { cluster_info_t *nextcluster; @@ -2311,9 +1005,9 @@ void G_WorldDone (void) if (strncmp (nextlevel, "enDSeQ", 6) == 0) { - F_StartFinale (thiscluster->messagemusic, thiscluster->musicorder, + F_StartFinale (thiscluster->MessageMusic, thiscluster->musicorder, thiscluster->cdtrack, thiscluster->cdid, - thiscluster->finaleflat, thiscluster->exittext, + thiscluster->finaleflat, thiscluster->ExitText, thiscluster->flags & CLUSTER_EXITTEXTINLUMP, thiscluster->flags & CLUSTER_FINALEPIC, thiscluster->flags & CLUSTER_LOOKUPEXITTEXT, @@ -2327,21 +1021,21 @@ void G_WorldDone (void) { // 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) + if (nextcluster->EnterText.IsNotEmpty()) { - F_StartFinale (nextcluster->messagemusic, nextcluster->musicorder, + F_StartFinale (nextcluster->MessageMusic, nextcluster->musicorder, nextcluster->cdtrack, nextcluster->cdid, - nextcluster->finaleflat, nextcluster->entertext, + nextcluster->finaleflat, nextcluster->EnterText, nextcluster->flags & CLUSTER_ENTERTEXTINLUMP, nextcluster->flags & CLUSTER_FINALEPIC, nextcluster->flags & CLUSTER_LOOKUPENTERTEXT, false); } - else if (thiscluster->exittext) + else if (thiscluster->ExitText.IsNotEmpty()) { - F_StartFinale (thiscluster->messagemusic, thiscluster->musicorder, + F_StartFinale (thiscluster->MessageMusic, thiscluster->musicorder, thiscluster->cdtrack, nextcluster->cdid, - thiscluster->finaleflat, thiscluster->exittext, + thiscluster->finaleflat, thiscluster->ExitText, thiscluster->flags & CLUSTER_EXITTEXTINLUMP, thiscluster->flags & CLUSTER_FINALEPIC, thiscluster->flags & CLUSTER_LOOKUPEXITTEXT, @@ -2351,6 +1045,11 @@ void G_WorldDone (void) } } +//========================================================================== +// +// +//========================================================================== + void G_DoWorldDone (void) { gamestate = GS_LEVEL; @@ -2494,6 +1193,11 @@ void G_FinishTravel () } } +//========================================================================== +// +// +//========================================================================== + void G_InitLevelLocals () { level_info_t *info; @@ -2514,7 +1218,6 @@ void G_InitLevelLocals () level.info = info; level.skyspeed1 = info->skyspeed1; level.skyspeed2 = info->skyspeed2; - info = (level_info_t *)info; strncpy (level.skypic2, info->skypic2, 8); level.fadeto = info->fadeto; level.cdtrack = info->cdtrack; @@ -2553,7 +1256,7 @@ void G_InitLevelLocals () G_AirControlChanged (); - if (info->level_name) + if (!info->LevelName.IsEmpty()) { cluster_info_t *clus = FindClusterInfo (info->cluster); @@ -2563,11 +1266,10 @@ void G_InitLevelLocals () level.clusterflags = clus ? clus->flags : 0; level.flags |= info->flags; level.levelnum = info->levelnum; - level.music = info->music; + level.Music = info->Music; level.musicorder = info->musicorder; - strncpy (level.level_name, info->level_name, 63); - G_MaybeLookupLevelName (NULL); + level.LevelName = level.info->LookupLevelName(); strncpy (level.nextmap, info->nextmap, 8); level.nextmap[8] = 0; strncpy (level.secretmap, info->secretmap, 8); @@ -2582,10 +1284,10 @@ void G_InitLevelLocals () { level.partime = level.cluster = 0; level.sucktime = 0; - strcpy (level.level_name, "Unnamed"); + level.LevelName = "Unnamed"; level.nextmap[0] = level.secretmap[0] = 0; - level.music = NULL; + level.Music = ""; strcpy (level.skypic1, "SKY1"); strcpy (level.skypic2, "SKY1"); level.flags = 0; @@ -2597,6 +1299,11 @@ void G_InitLevelLocals () NormalLight.ChangeFade (level.fadeto); } +//========================================================================== +// +// +//========================================================================== + bool FLevelLocals::IsJumpingAllowed() const { if (dmflags & DF_NO_JUMP) @@ -2606,6 +1313,11 @@ bool FLevelLocals::IsJumpingAllowed() const return !(level.flags & LEVEL_JUMP_NO); } +//========================================================================== +// +// +//========================================================================== + bool FLevelLocals::IsCrouchingAllowed() const { if (dmflags & DF_NO_CROUCH) @@ -2615,6 +1327,11 @@ bool FLevelLocals::IsCrouchingAllowed() const return !(level.flags & LEVEL_CROUCH_NO); } +//========================================================================== +// +// +//========================================================================== + bool FLevelLocals::IsFreelookAllowed() const { if (level.flags & LEVEL_FREELOOK_NO) @@ -2624,6 +1341,11 @@ bool FLevelLocals::IsFreelookAllowed() const return !(dmflags & DF_NO_FREELOOK); } +//========================================================================== +// +// +//========================================================================== + FString CalcMapName (int episode, int level) { FString lumpname; @@ -2640,124 +1362,10 @@ FString CalcMapName (int episode, int level) return lumpname; } -level_info_t *FindLevelInfo (const char *mapname) -{ - int i; - - if ((i = FindWadLevelInfo (mapname)) > -1) - return &wadlevelinfos[i]; - else - return &TheDefaultLevelInfo; -} - -level_info_t *FindLevelByNum (int num) -{ - for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) - if (wadlevelinfos[i].levelnum == num) - return &wadlevelinfos[i]; - - return NULL; -} - -level_info_t *CheckLevelRedirect (level_info_t *info) -{ - if (info->RedirectType != NAME_None) - { - const PClass *type = PClass::FindClass(info->RedirectType); - if (type != NULL) - { - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && players[i].mo->FindInventory (type)) - { - // check for actual presence of the map. - if (P_CheckMapData(info->RedirectMap)) - { - return FindLevelInfo(info->RedirectMap); - } - break; - } - } - } - } - return NULL; -} - -static void SetLevelNum (level_info_t *info, int num) -{ - // Avoid duplicate levelnums. The level being set always has precedence. - for (unsigned int i = 0; i < wadlevelinfos.Size(); ++i) - { - if (wadlevelinfos[i].levelnum == num) - wadlevelinfos[i].levelnum = 0; - } - info->levelnum = num; -} - -cluster_info_t *FindClusterInfo (int cluster) -{ - int i; - - if ((i = FindWadClusterInfo (cluster)) > -1) - return &wadclusterinfos[i]; - else - return &TheDefaultClusterInfo; -} - -const char *G_MaybeLookupLevelName (level_info_t *ininfo) -{ - level_info_t *info; - - if (ininfo == NULL) - { - info = level.info; - } - else - { - info = ininfo; - } - - if (info != NULL && info->flags & LEVEL_LOOKUPLEVELNAME) - { - const char *thename; - const char *lookedup; - - lookedup = GStrings[info->level_name]; - if (lookedup == NULL) - { - thename = info->level_name; - } - else - { - char checkstring[32]; - - // Strip out the header from the localized string - if (info->mapname[0] == 'E' && info->mapname[2] == 'M') - { - mysnprintf (checkstring, countof(checkstring), "%s: ", info->mapname); - } - else if (info->mapname[0] == 'M' && info->mapname[1] == 'A' && info->mapname[2] == 'P') - { - mysnprintf (checkstring, countof(checkstring), "%d: ", atoi(info->mapname + 3)); - } - thename = strstr (lookedup, checkstring); - if (thename == NULL) - { - thename = lookedup; - } - else - { - thename += strlen (checkstring); - } - } - if (ininfo == NULL) - { - strncpy (level.level_name, thename, 63); - } - return thename; - } - return info != NULL ? info->level_name : NULL; -} +//========================================================================== +// +// +//========================================================================== void G_AirControlChanged () { @@ -2773,6 +1381,11 @@ void G_AirControlChanged () } } +//========================================================================== +// +// +//========================================================================== + void G_SerializeLevel (FArchive &arc, bool hubLoad) { int i = level.totaltime; @@ -2885,13 +1498,18 @@ void G_SerializeLevel (FArchive &arc, bool hubLoad) } } +//========================================================================== +// // Archives the current level +// +//========================================================================== + void G_SnapshotLevel () { if (level.info->snapshot) delete level.info->snapshot; - if (level.info->mapname[0] != 0 || level.info == &TheDefaultLevelInfo) + if (level.info->isValid()) { level.info->snapshotVer = SAVEVER; level.info->snapshot = new FCompressedMemFile; @@ -2904,14 +1522,19 @@ void G_SnapshotLevel () } } +//========================================================================== +// // Unarchives the current level based on its snapshot // The level should have already been loaded and setup. +// +//========================================================================== + void G_UnSnapshotLevel (bool hubLoad) { if (level.info->snapshot == NULL) return; - if (level.info->mapname[0] != 0 || level.info == &TheDefaultLevelInfo) + if (level.info->isValid()) { SaveVersion = level.info->snapshotVer; level.info->snapshot->Reopen (); @@ -2949,21 +1572,13 @@ void G_UnSnapshotLevel (bool hubLoad) } } // No reason to keep the snapshot around once the level's been entered. - delete level.info->snapshot; - level.info->snapshot = NULL; + level.info->ClearSnapshot(); } -void G_ClearSnapshots (void) -{ - for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) - { - if (wadlevelinfos[i].snapshot) - { - delete wadlevelinfos[i].snapshot; - wadlevelinfos[i].snapshot = NULL; - } - } -} +//========================================================================== +// +// +//========================================================================== static void writeMapName (FArchive &arc, const char *name) { @@ -2980,6 +1595,11 @@ static void writeMapName (FArchive &arc, const char *name) arc.Write (name, size); } +//========================================================================== +// +// +//========================================================================== + static void writeSnapShot (FArchive &arc, level_info_t *i) { arc << i->snapshotVer; @@ -2987,6 +1607,11 @@ static void writeSnapShot (FArchive &arc, level_info_t *i) i->snapshot->Serialize (arc); } +//========================================================================== +// +// +//========================================================================== + void G_WriteSnapshots (FILE *file) { unsigned int i; @@ -3054,6 +1679,11 @@ void G_WriteSnapshots (FILE *file) } } +//========================================================================== +// +// +//========================================================================== + void G_ReadSnapshots (PNGHandle *png) { DWORD chunkLen; @@ -3140,12 +1770,22 @@ void G_ReadSnapshots (PNGHandle *png) } +//========================================================================== +// +// +//========================================================================== + static void writeDefereds (FArchive &arc, level_info_t *i) { writeMapName (arc, i->mapname); arc << i->defered; } +//========================================================================== +// +// +//========================================================================== + void P_WriteACSDefereds (FILE *file) { FPNGChunkArchive *arc = NULL; @@ -3171,6 +1811,11 @@ void P_WriteACSDefereds (FILE *file) } } +//========================================================================== +// +// +//========================================================================== + void P_ReadACSDefereds (PNGHandle *png) { BYTE namelen; @@ -3201,6 +1846,11 @@ void P_ReadACSDefereds (PNGHandle *png) } +//========================================================================== +// +// +//========================================================================== + void FLevelLocals::Tick () { // Reset carry sectors @@ -3210,6 +1860,11 @@ void FLevelLocals::Tick () } } +//========================================================================== +// +// +//========================================================================== + void FLevelLocals::AddScroller (DScroller *scroller, int secnum) { if (secnum < 0) @@ -3223,285 +1878,11 @@ void FLevelLocals::AddScroller (DScroller *scroller, int secnum) } } -// Initializes player classes in case they are random. -// This gets called at the start of a new game, and the classes -// chosen here are used for the remainder of a single-player -// or coop game. These are ignored for deathmatch. - -static void InitPlayerClasses () -{ - if (!savegamerestore) - { - for (int i = 0; i < MAXPLAYERS; ++i) - { - SinglePlayerClass[i] = players[i].userinfo.PlayerClass; - if (SinglePlayerClass[i] < 0 || !playeringame[i]) - { - SinglePlayerClass[i] = (pr_classchoice()) % PlayerClasses.Size (); - } - players[i].cls = NULL; - players[i].CurrentPlayerClass = SinglePlayerClass[i]; - } - } -} - - -static void ParseSkill (FScanner &sc) -{ - FSkillInfo skill; - - skill.AmmoFactor = FRACUNIT; - skill.DoubleAmmoFactor = 2*FRACUNIT; - skill.DropAmmoFactor = -1; - skill.DamageFactor = FRACUNIT; - skill.FastMonsters = false; - skill.DisableCheats = false; - skill.EasyBossBrain = false; - skill.AutoUseHealth = false; - skill.RespawnCounter = 0; - skill.RespawnLimit = 0; - skill.Aggressiveness = FRACUNIT; - skill.SpawnFilter = 0; - skill.ACSReturn = AllSkills.Size(); - skill.MenuNameIsLump = false; - skill.MustConfirm = false; - skill.Shortcut = 0; - skill.TextColor = ""; - - sc.MustGetString(); - skill.Name = sc.String; - - while (sc.GetString ()) - { - if (sc.Compare ("ammofactor")) - { - sc.MustGetFloat (); - skill.AmmoFactor = FLOAT2FIXED(sc.Float); - } - else if (sc.Compare ("doubleammofactor")) - { - sc.MustGetFloat (); - skill.DoubleAmmoFactor = FLOAT2FIXED(sc.Float); - } - else if (sc.Compare ("dropammofactor")) - { - sc.MustGetFloat (); - skill.DropAmmoFactor = FLOAT2FIXED(sc.Float); - } - else if (sc.Compare ("damagefactor")) - { - sc.MustGetFloat (); - skill.DamageFactor = FLOAT2FIXED(sc.Float); - } - else if (sc.Compare ("fastmonsters")) - { - skill.FastMonsters = true; - } - else if (sc.Compare ("disablecheats")) - { - skill.DisableCheats = true; - } - else if (sc.Compare ("easybossbrain")) - { - skill.EasyBossBrain = true; - } - else if (sc.Compare("autousehealth")) - { - skill.AutoUseHealth = true; - } - else if (sc.Compare("respawntime")) - { - sc.MustGetFloat (); - skill.RespawnCounter = int(sc.Float*TICRATE); - } - else if (sc.Compare("respawnlimit")) - { - sc.MustGetNumber (); - skill.RespawnLimit = sc.Number; - } - else if (sc.Compare("Aggressiveness")) - { - sc.MustGetFloat (); - skill.Aggressiveness = FRACUNIT - FLOAT2FIXED(clamp(sc.Float, 0,1)); - } - else if (sc.Compare("SpawnFilter")) - { - if (sc.CheckNumber()) - { - if (sc.Number > 0) skill.SpawnFilter |= (1<<(sc.Number-1)); - } - else - { - sc.MustGetString (); - if (sc.Compare("baby")) skill.SpawnFilter |= 1; - else if (sc.Compare("easy")) skill.SpawnFilter |= 2; - else if (sc.Compare("normal")) skill.SpawnFilter |= 4; - else if (sc.Compare("hard")) skill.SpawnFilter |= 8; - else if (sc.Compare("nightmare")) skill.SpawnFilter |= 16; - } - } - else if (sc.Compare("ACSReturn")) - { - sc.MustGetNumber (); - skill.ACSReturn = sc.Number; - } - else if (sc.Compare("Name")) - { - sc.MustGetString (); - skill.MenuName = sc.String; - skill.MenuNameIsLump = false; - } - else if (sc.Compare("PlayerClassName")) - { - sc.MustGetString (); - FName pc = sc.String; - sc.MustGetString (); - skill.MenuNamesForPlayerClass[pc]=sc.String; - } - else if (sc.Compare("PicName")) - { - sc.MustGetString (); - skill.MenuName = sc.String; - skill.MenuNameIsLump = true; - } - else if (sc.Compare("MustConfirm")) - { - skill.MustConfirm = true; - if (sc.CheckToken(TK_StringConst)) - { - skill.MustConfirmText = sc.String; - } - } - else if (sc.Compare("Key")) - { - sc.MustGetString(); - skill.Shortcut = tolower(sc.String[0]); - } - else if (sc.Compare("TextColor")) - { - sc.MustGetString(); - skill.TextColor = '['; - skill.TextColor << sc.String << ']'; - } - else - { - sc.UnGet (); - break; - } - } - for(unsigned int i = 0; i < AllSkills.Size(); i++) - { - if (AllSkills[i].Name == skill.Name) - { - AllSkills[i] = skill; - return; - } - } - AllSkills.Push(skill); -} - -int G_SkillProperty(ESkillProperty prop) -{ - if (AllSkills.Size() > 0) - { - switch(prop) - { - case SKILLP_AmmoFactor: - if (dmflags2 & DF2_YES_DOUBLEAMMO) - { - return AllSkills[gameskill].DoubleAmmoFactor; - } - return AllSkills[gameskill].AmmoFactor; - - case SKILLP_DropAmmoFactor: - return AllSkills[gameskill].DropAmmoFactor; - - case SKILLP_DamageFactor: - return AllSkills[gameskill].DamageFactor; - - case SKILLP_FastMonsters: - return AllSkills[gameskill].FastMonsters || (dmflags & DF_FAST_MONSTERS); - - case SKILLP_Respawn: - if (dmflags & DF_MONSTERS_RESPAWN && AllSkills[gameskill].RespawnCounter==0) - return TICRATE * (gameinfo.gametype != GAME_Strife ? 12 : 16); - return AllSkills[gameskill].RespawnCounter; - - case SKILLP_RespawnLimit: - return AllSkills[gameskill].RespawnLimit; - - case SKILLP_Aggressiveness: - return AllSkills[gameskill].Aggressiveness; - - case SKILLP_DisableCheats: - return AllSkills[gameskill].DisableCheats; - - case SKILLP_AutoUseHealth: - return AllSkills[gameskill].AutoUseHealth; - - case SKILLP_EasyBossBrain: - return AllSkills[gameskill].EasyBossBrain; - - case SKILLP_SpawnFilter: - return AllSkills[gameskill].SpawnFilter; - - case SKILLP_ACSReturn: - return AllSkills[gameskill].ACSReturn; - } - } - return 0; -} - - -void G_VerifySkill() -{ - if (gameskill >= (int)AllSkills.Size()) - gameskill = AllSkills.Size()-1; - else if (gameskill < 0) - gameskill = 0; -} - -FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other) -{ - Name = other.Name; - AmmoFactor = other.AmmoFactor; - DoubleAmmoFactor = other.DoubleAmmoFactor; - DropAmmoFactor = other.DropAmmoFactor; - DamageFactor = other.DamageFactor; - FastMonsters = other.FastMonsters; - DisableCheats = other.DisableCheats; - AutoUseHealth = other.AutoUseHealth; - EasyBossBrain = other.EasyBossBrain; - RespawnCounter= other.RespawnCounter; - RespawnLimit= other.RespawnLimit; - Aggressiveness= other.Aggressiveness; - SpawnFilter = other.SpawnFilter; - ACSReturn = other.ACSReturn; - MenuName = other.MenuName; - MenuNamesForPlayerClass = other.MenuNamesForPlayerClass; - MenuNameIsLump = other.MenuNameIsLump; - MustConfirm = other.MustConfirm; - MustConfirmText = other.MustConfirmText; - Shortcut = other.Shortcut; - TextColor = other.TextColor; - return *this; -} - -int FSkillInfo::GetTextColor() const -{ - if (TextColor.IsEmpty()) - { - return CR_UNTRANSLATED; - } - const BYTE *cp = (const BYTE *)TextColor.GetChars(); - int color = V_ParseFontColor(cp, 0, 0); - if (color == CR_UNDEFINED) - { - Printf("Undefined color '%s' in definition of skill %s\n", TextColor.GetChars(), Name.GetChars()); - color = CR_UNTRANSLATED; - } - return color; -} +//========================================================================== +// +// Lists all currently defined maps +// +//========================================================================== CCMD(listmaps) { @@ -3511,7 +1892,11 @@ CCMD(listmaps) if (P_CheckMapData(info->mapname)) { - Printf("%s: '%s'\n", info->mapname, G_MaybeLookupLevelName(info)); + Printf("%s: '%s'\n", info->mapname, info->LookupLevelName().GetChars()); } } } + + + + diff --git a/src/g_level.h b/src/g_level.h index b288cc06e9..593bd34238 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -36,90 +36,166 @@ #include "doomtype.h" #include "doomdef.h" +#include "autosegs.h" +#include "sc_man.h" -#define NUM_WORLDVARS 256 -#define NUM_GLOBALVARS 64 +struct level_info_t; +struct cluster_info_t; +class FScanner; -#define LEVEL_NOINTERMISSION UCONST64(0x00000001) -#define LEVEL_NOINVENTORYBAR UCONST64(0x00000002) // This effects Doom only, since it's the only one without a standard inventory bar. -#define LEVEL_DOUBLESKY UCONST64(0x00000004) -#define LEVEL_HASFADETABLE UCONST64(0x00000008) // Level uses Hexen's fadetable mapinfo to get fog +#if defined(_MSC_VER) +#pragma data_seg(".yreg$u") +#pragma data_seg() -#define LEVEL_MAP07SPECIAL UCONST64(0x00000010) -#define LEVEL_BRUISERSPECIAL UCONST64(0x00000020) -#define LEVEL_CYBORGSPECIAL UCONST64(0x00000040) -#define LEVEL_SPIDERSPECIAL UCONST64(0x00000080) +#define MSVC_YSEG __declspec(allocate(".yreg$u")) +#define GCC_YSEG +#else +#define MSVC_YSEG +#define GCC_YSEG __attribute__((section(YREG_SECTION))) +#endif -#define LEVEL_SPECLOWERFLOOR UCONST64(0x00000100) -#define LEVEL_SPECOPENDOOR UCONST64(0x00000200) -#define LEVEL_SPECACTIONSMASK UCONST64(0x00000300) -#define LEVEL_MONSTERSTELEFRAG UCONST64(0x00000400) -#define LEVEL_ACTOWNSPECIAL UCONST64(0x00000800) -#define LEVEL_SNDSEQTOTALCTRL UCONST64(0x00001000) -#define LEVEL_FORCENOSKYSTRETCH UCONST64(0x00002000) +struct FMapInfoParser +{ + enum EFormatType + { + FMT_Unknown, + FMT_Old, + FMT_New + }; -#define LEVEL_CROUCH_NO UCONST64(0x00004000) -#define LEVEL_JUMP_NO UCONST64(0x00008000) -#define LEVEL_FREELOOK_NO UCONST64(0x00010000) -#define LEVEL_FREELOOK_YES UCONST64(0x00020000) + FScanner sc; + int format_type; + bool HexenHack; -// The absence of both of the following bits means that this level does not -// use falling damage (though damage can be forced with dmflags). -#define LEVEL_FALLDMG_ZD UCONST64(0x00040000) // Level uses ZDoom's falling damage -#define LEVEL_FALLDMG_HX UCONST64(0x00080000) // Level uses Hexen's falling damage + FMapInfoParser() + { + format_type = FMT_Unknown; + HexenHack = false; + } -#define LEVEL_HEADSPECIAL UCONST64(0x00100000) // Heretic episode 1/4 -#define LEVEL_MINOTAURSPECIAL UCONST64(0x00200000) // Heretic episode 2/5 -#define LEVEL_SORCERER2SPECIAL UCONST64(0x00400000) // Heretic episode 3 -#define LEVEL_SPECKILLMONSTERS UCONST64(0x00800000) + bool ParseLookupName(FString &dest); + void ParseMusic(FString &name, int &order); + void ParseLumpOrTextureName(char *name); -#define LEVEL_STARTLIGHTNING UCONST64(0x01000000) // Automatically start lightning -#define LEVEL_FILTERSTARTS UCONST64(0x02000000) // Apply mapthing filtering to player starts -#define LEVEL_LOOKUPLEVELNAME UCONST64(0x04000000) // Level name is the name of a language string -#define LEVEL_HEXENFORMAT UCONST64(0x08000000) // Level uses the Hexen map format + void ParseCluster(); + void ParseNextMap(char *mapname); + level_info_t *ParseMapHeader(level_info_t &defaultinfo); + void ParseMapDefinition(level_info_t &leveldef); + void ParseEpisodeInfo (); + void ParseSkill (); + void ParseMapInfo (int lump, level_info_t &gamedefaults); -#define LEVEL_SWAPSKIES UCONST64(0x10000000) // Used by lightning -#define LEVEL_NOALLIES UCONST64(0x20000000) // i.e. Inside Strife's front base -#define LEVEL_CHANGEMAPCHEAT UCONST64(0x40000000) // Don't display cluster messages -#define LEVEL_VISITED UCONST64(0x80000000) // Used for intermission map + void ParseOpenBrace(); + bool ParseCloseBrace(); + bool CheckAssign(); + void ParseAssign(); + void MustParseAssign(); + void ParseComma(); + bool CheckNumber(); + bool CheckFloat(); + void SkipToNext(); +}; -#define LEVEL_DEATHSLIDESHOW UCONST64(0x100000000) // Slideshow on death -#define LEVEL_ALLMAP UCONST64(0x200000000) // The player picked up a map on this level +#define DEFINE_MAP_OPTION(name, old) \ + static void MapOptHandler_##name(FMapInfoParser &parse, level_info_t *info); \ + static FMapOptInfo MapOpt_##name = \ + { #name, MapOptHandler_##name, old }; \ + MSVC_YSEG FMapOptInfo *mapopt_##name GCC_YSEG = &MapOpt_##name; \ + static void MapOptHandler_##name(FMapInfoParser &parse, level_info_t *info) -#define LEVEL_LAXMONSTERACTIVATION UCONST64(0x400000000) // Monsters can open doors depending on the door speed -#define LEVEL_LAXACTIVATIONMAPINFO UCONST64(0x800000000) // LEVEL_LAXMONSTERACTIVATION is not a default. -#define LEVEL_MISSILESACTIVATEIMPACT UCONST64(0x1000000000) // Missiles are the activators of SPAC_IMPACT events, not their shooters -#define LEVEL_FROZEN UCONST64(0x2000000000) // Game is frozen by a TimeFreezer +struct FMapOptInfo +{ + const char *name; + void (*handler) (FMapInfoParser &parse, level_info_t *levelinfo); + bool old; +}; -#define LEVEL_KEEPFULLINVENTORY UCONST64(0x4000000000) // doesn't reduce the amount of inventory items to 1 +enum ELevelFlags +{ + LEVEL_NOINTERMISSION = 0x00000001, + LEVEL_NOINVENTORYBAR = 0x00000002, // This effects Doom only, since it's the only one without a standard inventory bar. + LEVEL_DOUBLESKY = 0x00000004, + LEVEL_HASFADETABLE = 0x00000008, // Level uses Hexen's fadetable mapinfo to get fog -#define LEVEL_MUSICDEFINED UCONST64(0x8000000000) // a marker to disable the $map command in SNDINFO for this map -#define LEVEL_MONSTERFALLINGDAMAGE UCONST64(0x10000000000) -#define LEVEL_CLIPMIDTEX UCONST64(0x20000000000) -#define LEVEL_WRAPMIDTEX UCONST64(0x40000000000) + LEVEL_MAP07SPECIAL = 0x00000010, + LEVEL_BRUISERSPECIAL = 0x00000020, + LEVEL_CYBORGSPECIAL = 0x00000040, + LEVEL_SPIDERSPECIAL = 0x00000080, -#define LEVEL_CHECKSWITCHRANGE UCONST64(0x80000000000) + LEVEL_SPECLOWERFLOOR = 0x00000100, + LEVEL_SPECOPENDOOR = 0x00000200, + LEVEL_SPECACTIONSMASK = 0x00000300, -#define LEVEL_PAUSE_MUSIC_IN_MENUS UCONST64(0x100000000000) -#define LEVEL_TOTALINFIGHTING UCONST64(0x200000000000) -#define LEVEL_NOINFIGHTING UCONST64(0x400000000000) + LEVEL_MONSTERSTELEFRAG = 0x00000400, + LEVEL_ACTOWNSPECIAL = 0x00000800, + LEVEL_SNDSEQTOTALCTRL = 0x00001000, + LEVEL_FORCENOSKYSTRETCH = 0x00002000, -#define LEVEL_NOMONSTERS UCONST64(0x800000000000) -#define LEVEL_INFINITE_FLIGHT UCONST64(0x1000000000000) + LEVEL_CROUCH_NO = 0x00004000, + LEVEL_JUMP_NO = 0x00008000, + LEVEL_FREELOOK_NO = 0x00010000, + LEVEL_FREELOOK_YES = 0x00020000, -#define LEVEL_ALLOWRESPAWN UCONST64(0x2000000000000) + // The absence of both of the following bits means that this level does not + // use falling damage (though damage can be forced with dmflags,. + LEVEL_FALLDMG_ZD = 0x00040000, // Level uses ZDoom's falling damage + LEVEL_FALLDMG_HX = 0x00080000, // Level uses Hexen's falling damage -#define LEVEL_FORCETEAMPLAYON UCONST64(0x4000000000000) -#define LEVEL_FORCETEAMPLAYOFF UCONST64(0x8000000000000) + LEVEL_HEADSPECIAL = 0x00100000, // Heretic episode 1/4 + LEVEL_MINOTAURSPECIAL = 0x00200000, // Heretic episode 2/5 + LEVEL_SORCERER2SPECIAL = 0x00400000, // Heretic episode 3 + LEVEL_SPECKILLMONSTERS = 0x00800000, -#define LEVEL_CONV_SINGLE_UNFREEZE UCONST64(0x10000000000000) -#define LEVEL_RAILINGHACK UCONST64(0x20000000000000) // but UDMF requires them to be separate to have more control -#define LEVEL_DUMMYSWITCHES UCONST64(0x40000000000000) -#define LEVEL_HEXENHACK UCONST64(0x80000000000000) // Level was defined in a Hexen style MAPINFO + LEVEL_STARTLIGHTNING = 0x01000000, // Automatically start lightning + LEVEL_FILTERSTARTS = 0x02000000, // Apply mapthing filtering to player starts + LEVEL_LOOKUPLEVELNAME = 0x04000000, // Level name is the name of a language string + LEVEL_HEXENFORMAT = 0x08000000, // Level uses the Hexen map format -#define LEVEL_SMOOTHLIGHTING UCONST64(0x100000000000000) // Level uses the smooth lighting feature. + LEVEL_SWAPSKIES = 0x10000000, // Used by lightning + LEVEL_NOALLIES = 0x20000000, // i.e. Inside Strife's front base + LEVEL_CHANGEMAPCHEAT = 0x40000000, // Don't display cluster messages + LEVEL_VISITED = 0x80000000, // Used for intermission map + + // The flags QWORD is now split into 2 DWORDs + LEVEL2_DEATHSLIDESHOW = 0x00000001, // Slideshow on death + LEVEL2_ALLMAP = 0x00000002, // The player picked up a map on this level + + LEVEL2_LAXMONSTERACTIVATION = 0x00000004, // Monsters can open doors depending on the door speed + LEVEL2_LAXACTIVATIONMAPINFO = 0x00000008, // LEVEL_LAXMONSTERACTIVATION is not a default. + + LEVEL2_MISSILESACTIVATEIMPACT = 0x00000010, // Missiles are the activators of SPAC_IMPACT events, not their shooters + LEVEL2_FROZEN = 0x00000020, // Game is frozen by a TimeFreezer + + LEVEL2_KEEPFULLINVENTORY = 0x00000040, // doesn't reduce the amount of inventory items to 1 + + LEVEL2_MUSICDEFINED = 0x00000080, // a marker to disable the $map command in SNDINFO for this map + LEVEL2_MONSTERFALLINGDAMAGE = 0x00000100, + LEVEL2_CLIPMIDTEX = 0x00000200, + LEVEL2_WRAPMIDTEX = 0x00000400, + + LEVEL2_CHECKSWITCHRANGE = 0x00000800, + + LEVEL2_PAUSE_MUSIC_IN_MENUS = 0x00001000, + LEVEL2_TOTALINFIGHTING = 0x00002000, + LEVEL2_NOINFIGHTING = 0x00004000, + + LEVEL2_NOMONSTERS = 0x00008000, + LEVEL2_INFINITE_FLIGHT = 0x00010000, + + LEVEL2_ALLOWRESPAWN = 0x00020000, + + LEVEL2_FORCETEAMPLAYON = 0x00040000, + LEVEL2_FORCETEAMPLAYOFF = 0x00080000, + + LEVEL2_CONV_SINGLE_UNFREEZE = 0x00100000, + LEVEL2_RAILINGHACK = 0x00200000, // but UDMF requires them to be separate to have more control + LEVEL2_DUMMYSWITCHES = 0x00400000, + LEVEL2_HEXENHACK = 0x00800000, // Level was defined in a Hexen style MAPINFO + + LEVEL2_SMOOTHLIGHTING = 0x01000000, // Level uses the smooth lighting feature. +}; struct acsdefered_t; @@ -129,7 +205,6 @@ struct FSpecialAction FName Type; // this is initialized before the actors... BYTE Action; int Args[5]; // must allow 16 bit tags for 666 & 667! - FSpecialAction *Next; }; class FCompressedMemFile; @@ -161,13 +236,13 @@ struct level_info_t int cluster; int partime; int sucktime; - QWORD flags; - char *music; - char *level_name; + DWORD flags; + DWORD flags2; + FString Music; + FString LevelName; char fadetable[9]; SBYTE WallVertLight, WallHorizLight; char f1[9]; - // TheDefaultLevelInfo initializes everything above this line. int musicorder; FCompressedMemFile *snapshot; DWORD snapshotVer; @@ -185,26 +260,41 @@ struct level_info_t int airsupply; DWORD compatflags; DWORD compatmask; - char *translator; // for converting Doom-format linedef and sector types. + FString Translator; // for converting Doom-format linedef and sector types. // Redirection: If any player is carrying the specified item, then // you go to the RedirectMap instead of this one. FName RedirectType; char RedirectMap[9]; - char *enterpic; - char *exitpic; - char *intermusic; + FString EnterPic; + FString ExitPic; + FString InterMusic; int intermusicorder; - char *soundinfo; - char *sndseq; + FString SoundInfo; + FString SndSeq; char bordertexture[9]; float teamdamage; - FSpecialAction * specialactions; - FOptionalMapinfoData *opdata; + TArray specialactions; + + level_info_t() + { + Reset(); + } + ~level_info_t() + { + ClearSnapshot(); + ClearDefered(); + } + void Reset(); + bool isValid(); + FString LookupLevelName (); + void ClearSnapshot(); + void ClearDefered(); + level_info_t *CheckLevelRedirect (); }; @@ -231,17 +321,18 @@ struct FLevelLocals int clusterflags; int levelnum; int lumpnum; - char level_name[64]; // the descriptive name (Outer Base, etc) + FString LevelName; char mapname[256]; // the server name (base1, etc) char nextmap[9]; // go here when fraglimit is hit char secretmap[9]; // map to go to when used secret exit - QWORD flags; + DWORD flags; + DWORD flags2; DWORD fadeto; // The color the palette fades to (usually black) DWORD outsidefog; // The fog for sectors with sky ceilings - char *music; + FString Music; int musicorder; int cdtrack; unsigned int cdid; @@ -313,14 +404,17 @@ struct cluster_info_t { int cluster; char finaleflat[9]; - char *exittext; - char *entertext; - char *messagemusic; + FString ExitText; + FString EnterText; + FString MessageMusic; int musicorder; int flags; int cdtrack; - char *clustername; + FString ClusterName; unsigned int cdid; + + void Reset(); + }; // Cluster flags @@ -331,25 +425,12 @@ struct cluster_info_t #define CLUSTER_LOOKUPEXITTEXT 0x00000010 // Exit text is the name of a language string #define CLUSTER_LOOKUPENTERTEXT 0x00000020 // Enter text is the name of a language string #define CLUSTER_LOOKUPNAME 0x00000040 // Name is the name of a language string +#define CLUSTER_LOOKUPCLUSTERNAME 0x00000080 // Cluster name is the name of a language string extern FLevelLocals level; extern TArray wadlevelinfos; - -extern SDWORD ACS_WorldVars[NUM_WORLDVARS]; -extern SDWORD ACS_GlobalVars[NUM_GLOBALVARS]; - -struct InitIntToZero -{ - void Init(int &v) - { - v = 0; - } -}; -typedef TMap, InitIntToZero> FWorldGlobalArray; - -extern FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS]; -extern FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS]; +extern TArray wadclusterinfos; extern bool savegamerestore; @@ -382,8 +463,6 @@ void G_InitLevelLocals (void); void G_AirControlChanged (); -const char *G_MaybeLookupLevelName (level_info_t *level); - cluster_info_t *FindClusterInfo (int cluster); level_info_t *FindLevelInfo (const char *mapname); level_info_t *FindLevelByNum (int num); @@ -392,9 +471,9 @@ level_info_t *CheckLevelRedirect (level_info_t *info); FString CalcMapName (int episode, int level); void G_ParseMapInfo (void); -void G_UnloadMapInfo (); void G_ClearSnapshots (void); +void P_RemoveDefereds (); void G_SnapshotLevel (void); void G_UnSnapshotLevel (bool keepPlayers); struct PNGHandle; diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp new file mode 100644 index 0000000000..c7bd8ec1c1 --- /dev/null +++ b/src/g_mapinfo.cpp @@ -0,0 +1,1849 @@ +/* +** g_level.cpp +** Parses MAPINFO +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** Copyright 2009 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include "templates.h" +#include "g_level.h" +#include "sc_man.h" +#include "w_wad.h" +#include "m_menu.h" +#include "cmdlib.h" +#include "v_video.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "i_system.h" +#include "gi.h" +#include "gstrings.h" +#include "farchive.h" +#include "p_acs.h" +#include "doomstat.h" +#include "d_player.h" + +int FindEndSequence (int type, const char *picname); + + +TArray wadclusterinfos; +TArray wadlevelinfos; + +level_info_t TheDefaultLevelInfo; +static cluster_info_t TheDefaultClusterInfo; + +//========================================================================== +// +// +//========================================================================== + +static int FindWadLevelInfo (const char *name) +{ + for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) + if (!strnicmp (name, wadlevelinfos[i].mapname, 8)) + return i; + + return -1; +} + +//========================================================================== +// +// +//========================================================================== + +level_info_t *FindLevelInfo (const char *mapname) +{ + int i; + + if ((i = FindWadLevelInfo (mapname)) > -1) + return &wadlevelinfos[i]; + else + { + if (TheDefaultLevelInfo.LevelName.IsEmpty()) + { + uppercopy(TheDefaultLevelInfo.skypic1, "SKY1"); + uppercopy(TheDefaultLevelInfo.skypic2, "SKY1"); + TheDefaultLevelInfo.LevelName = "Unnamed"; + } + return &TheDefaultLevelInfo; + } +} + +//========================================================================== +// +// +//========================================================================== + +level_info_t *FindLevelByNum (int num) +{ + for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) + if (wadlevelinfos[i].levelnum == num) + return &wadlevelinfos[i]; + + return NULL; +} + + +//========================================================================== +// +// +//========================================================================== + +static level_info_t *FindLevelByWarpTrans (int num) +{ + for (unsigned i = wadlevelinfos.Size(); i-- != 0; ) + if (wadlevelinfos[i].WarpTrans == num) + return &wadlevelinfos[i]; + + return NULL; +} + +//========================================================================== +// +// +//========================================================================== + +bool CheckWarpTransMap (FString &mapname, bool substitute) +{ + if (mapname[0] == '&' && (mapname[1] & 0xDF) == 'W' && + (mapname[2] & 0xDF) == 'T' && mapname[3] == '@') + { + level_info_t *lev = FindLevelByWarpTrans (atoi (&mapname[4])); + if (lev != NULL) + { + mapname = lev->mapname; + return true; + } + else if (substitute) + { + char a = mapname[4], b = mapname[5]; + mapname = "MAP"; + mapname << a << b; + } + } + return false; +} + +//========================================================================== +// +// +//========================================================================== + +static int FindWadClusterInfo (int cluster) +{ + for (unsigned int i = 0; i < wadclusterinfos.Size(); i++) + if (wadclusterinfos[i].cluster == cluster) + return i; + + return -1; +} + +//========================================================================== +// +// +//========================================================================== + +cluster_info_t *FindClusterInfo (int cluster) +{ + int i; + + if ((i = FindWadClusterInfo (cluster)) > -1) + return &wadclusterinfos[i]; + else + return &TheDefaultClusterInfo; +} + +//========================================================================== +// +// +//========================================================================== + +void G_ClearSnapshots (void) +{ + for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) + { + wadlevelinfos[i].ClearSnapshot(); + } +} + +//========================================================================== +// +// Remove any existing defereds +// +//========================================================================== + +void P_RemoveDefereds (void) +{ + for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) + { + wadlevelinfos[i].ClearDefered(); + } +} + + +//========================================================================== +// +// +//========================================================================== + +void level_info_t::Reset() +{ + mapname[0] = 0; + levelnum = 0; + pname[0] = 0; + nextmap[0] = 0; + secretmap[0] = 0; + strcpy (skypic1, "-NOFLAT-"); + strcpy (skypic2, "-NOFLAT-"); + cluster = 0; + partime = 0; + sucktime = 0; + flags = 0; + flags2 = gameinfo.gametype == GAME_Hexen? 0 : LEVEL2_LAXMONSTERACTIVATION; + Music = ""; + LevelName = ""; + strcpy (fadetable, "COLORMAP"); + WallHorizLight = -8; + WallVertLight = +8; + f1[0] = 0; + musicorder = 0; + snapshot = NULL; + snapshotVer = 0; + defered = 0; + skyspeed1 = skyspeed2 = 0.f; + fadeto = 0; + outsidefog = 0xff000000; + cdtrack = 0; + cdid = 0; + gravity = 0.f; + aircontrol = 0.f; + WarpTrans = 0; + airsupply = 20; + compatflags = 0; + compatmask = 0; + Translator = ""; + RedirectType = 0; + RedirectMap[0] = 0; + EnterPic = ""; + ExitPic = ""; + InterMusic = ""; + intermusicorder = 0; + SoundInfo = ""; + SndSeq = ""; + strcpy (bordertexture, gameinfo.borderFlat); + teamdamage = 0.f; + specialactions.Clear(); +} + + +//========================================================================== +// +// +//========================================================================== + +FString level_info_t::LookupLevelName() +{ + if (flags & LEVEL_LOOKUPLEVELNAME) + { + const char *thename; + const char *lookedup; + + lookedup = GStrings[LevelName]; + if (lookedup == NULL) + { + thename = LevelName; + } + else + { + char checkstring[32]; + + // Strip out the header from the localized string + if (mapname[0] == 'E' && mapname[2] == 'M') + { + mysnprintf (checkstring, countof(checkstring), "%s: ", mapname); + } + else if (mapname[0] == 'M' && mapname[1] == 'A' && mapname[2] == 'P') + { + mysnprintf (checkstring, countof(checkstring), "%d: ", atoi(mapname + 3)); + } + thename = strstr (lookedup, checkstring); + if (thename == NULL) + { + thename = lookedup; + } + else + { + thename += strlen (checkstring); + } + } + return thename; + } + else return LevelName; +} + + +//========================================================================== +// +// +//========================================================================== + +void level_info_t::ClearSnapshot() +{ + if (snapshot != NULL) delete snapshot; + snapshot = NULL; +} + +//========================================================================== +// +// +//========================================================================== + +void level_info_t::ClearDefered() +{ + acsdefered_t *def = defered; + while (def) + { + acsdefered_t *next = def->next; + delete def; + def = next; + } + defered = NULL; +} + +//========================================================================== +// +// +//========================================================================== + +level_info_t *level_info_t::CheckLevelRedirect () +{ + if (RedirectType != NAME_None) + { + const PClass *type = PClass::FindClass(RedirectType); + if (type != NULL) + { + for (int i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i] && players[i].mo->FindInventory (type)) + { + // check for actual presence of the map. + if (P_CheckMapData(RedirectMap)) + { + return FindLevelInfo(RedirectMap); + } + break; + } + } + } + } + return NULL; +} + +//========================================================================== +// +// +//========================================================================== + +bool level_info_t::isValid() +{ + return mapname[0] != 0 || this == &TheDefaultLevelInfo; +} + +//========================================================================== +// +// +//========================================================================== + +void cluster_info_t::Reset() +{ + cluster = 0; + finaleflat[0] = 0; + ExitText = ""; + EnterText = ""; + MessageMusic = ""; + musicorder = 0; + flags = 0; + cdtrack = 0; + ClusterName = ""; + cdid = 0; +} + + + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseOpenBrace() +{ + switch(format_type) + { + default: + format_type = sc.CheckString("{")? FMT_New : FMT_Old; + if (format_type == FMT_New) + sc.SetCMode(true); + break; + + case FMT_Old: + break; + + case FMT_New: + sc.MustGetStringName("{"); + sc.SetCMode(true); + break; + } +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::ParseCloseBrace() +{ + if (format_type == FMT_New) + { + return sc.Compare("}"); + } + else + { + // We have to assume that the next keyword + // starts a new top level block + sc.UnGet(); + return true; + } +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckAssign() +{ + if (format_type == FMT_New) return sc.CheckString("="); + else return false; // force explicit handling +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseAssign() +{ + if (format_type == FMT_New) sc.MustGetStringName("="); +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::MustParseAssign() +{ + if (format_type == FMT_New) sc.MustGetStringName("="); + else sc.ScriptError(NULL); +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseComma() +{ + if (format_type == FMT_New) sc.MustGetStringName(","); +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckNumber() +{ + if (format_type == FMT_New) + { + if (sc.CheckString(",")) + { + sc.MustGetNumber(); + return true; + } + return false; + } + else return sc.CheckNumber(); +} + +//========================================================================== +// +// +//========================================================================== + +bool FMapInfoParser::CheckFloat() +{ + if (format_type == FMT_New) + { + if (sc.CheckString(",")) + { + sc.MustGetFloat(); + return true; + } + return false; + } + else return sc.CheckFloat(); +} + +//========================================================================== +// +// skips an entire parameter list that's separated by commas +// +//========================================================================== + +void FMapInfoParser::SkipToNext() +{ + if (sc.CheckString("=")) + { + do + { + sc.MustGetString(); + } + while (sc.CheckString(",")); + } +} + +//========================================================================== +// +// ParseLookupname +// Parses a string that may reference the string table +// +//========================================================================== + +bool FMapInfoParser::ParseLookupName(FString &dest) +{ + sc.MustGetString(); + if (sc.Compare("lookup")) + { + ParseComma(); + sc.MustGetString(); + dest = sc.String; + return true; + } + else if (sc.String[0] == '$') + { + dest = sc.String+1; + return true; + } + else if (format_type == FMT_Old) + { + dest = sc.String; + return false; + } + else + { + sc.UnGet(); + dest = ""; + do + { + sc.MustGetString(); + dest << sc.String << '\n'; + } + while (sc.CheckString(",")); + // strip off the last newline + dest.Truncate(long(dest.Len()-1)); + return false; + } +} + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseLumpOrTextureName(char *name) +{ + sc.MustGetString(); + uppercopy(name, sc.String); + name[8]=0; +} + + +//========================================================================== +// +// +//========================================================================== + +void FMapInfoParser::ParseMusic(FString &name, int &order) +{ + sc.MustGetString(); + + order = 0; + char *colon = strchr (sc.String, ':'); + if (colon) + { + order = atoi(colon+1); + *colon = 0; + } + name = sc.String; + if (!colon && CheckNumber()) + { + order = sc.Number; + } +} + +//========================================================================== +// +// ParseCluster +// Parses a cluster definition +// +//========================================================================== + +void FMapInfoParser::ParseCluster() +{ + sc.MustGetNumber (); + int clusterindex = FindWadClusterInfo (sc.Number); + if (clusterindex == -1) + { + clusterindex = wadclusterinfos.Reserve(1); + } + + cluster_info_t *clusterinfo = &wadclusterinfos[clusterindex]; + clusterinfo->Reset(); + clusterinfo->cluster = sc.Number; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if (sc.Compare("name")) + { + ParseAssign(); + if (ParseLookupName(clusterinfo->ClusterName)) + clusterinfo->flags |= CLUSTER_LOOKUPCLUSTERNAME; + } + else if (sc.Compare("entertext")) + { + ParseAssign(); + if (ParseLookupName(clusterinfo->EnterText)) + clusterinfo->flags |= CLUSTER_LOOKUPENTERTEXT; + } + else if (sc.Compare("exittext")) + { + ParseAssign(); + if (ParseLookupName(clusterinfo->ExitText)) + clusterinfo->flags |= CLUSTER_LOOKUPEXITTEXT; + } + else if (sc.Compare("music")) + { + int order = 0; + + ParseAssign(); + ParseMusic(clusterinfo->MessageMusic, clusterinfo->musicorder); + } + else if (sc.Compare("flat")) + { + ParseAssign(); + ParseLumpOrTextureName(clusterinfo->finaleflat); + } + else if (sc.Compare("pic")) + { + ParseAssign(); + ParseLumpOrTextureName(clusterinfo->finaleflat); + clusterinfo->flags |= CLUSTER_FINALEPIC; + } + else if (sc.Compare("hub")) + { + clusterinfo->flags |= CLUSTER_HUB; + } + else if (sc.Compare("cdtrack")) + { + ParseAssign(); + sc.MustGetNumber(); + clusterinfo->cdtrack = sc.Number; + } + else if (sc.Compare("cdid")) + { + ParseAssign(); + sc.MustGetString(); + clusterinfo->cdid = strtoul (sc.String, NULL, 16); + } + else if (sc.Compare("entertextislump")) + { + clusterinfo->flags |= CLUSTER_ENTERTEXTINLUMP; + } + else if (sc.Compare("exittextislump")) + { + clusterinfo->flags |= CLUSTER_EXITTEXTINLUMP; + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in map definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } +} + + +//========================================================================== +// +// ParseNextMap +// Parses a next map field +// +//========================================================================== + +void FMapInfoParser::ParseNextMap(char *mapname) +{ + EndSequence newSeq; + bool useseq = false; + + if (sc.CheckNumber()) + { + if (HexenHack) + { + mysnprintf (mapname, 9, "&wt@%02d", sc.Number); + } + else + { + mysnprintf (mapname, 9, "MAP%02d", sc.Number); + } + } + else + { + sc.MustGetString(); + + if (sc.Compare("endgame")) + { + newSeq.Advanced = true; + newSeq.EndType = END_Pic1; + newSeq.PlayTheEnd = false; + newSeq.MusicLooping = true; + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("pic")) + { + ParseAssign(); + sc.MustGetString(); + newSeq.EndType = END_Pic; + newSeq.PicName = sc.String; + } + else if (sc.Compare("hscroll")) + { + ParseAssign(); + newSeq.EndType = END_Bunny; + sc.MustGetString(); + newSeq.PicName = sc.String; + ParseComma(); + sc.MustGetString(); + newSeq.PicName2 = sc.String; + if (CheckNumber()) + newSeq.PlayTheEnd = !!sc.Number; + } + else if (sc.Compare("vscroll")) + { + ParseAssign(); + newSeq.EndType = END_Demon; + sc.MustGetString(); + newSeq.PicName = sc.String; + ParseComma(); + sc.MustGetString(); + newSeq.PicName2 = sc.String; + } + else if (sc.Compare("cast")) + { + newSeq.EndType = END_Cast; + } + else if (sc.Compare("music")) + { + ParseAssign(); + sc.MustGetString(); + newSeq.Music = sc.String; + if (CheckNumber()) + { + newSeq.MusicLooping = !!sc.Number; + } + } + else + { + if (format_type == FMT_New) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in endgame definition\n", sc.String); + SkipToNext(); + } + else + { + sc.ScriptError("Unknown property '%s' found in endgame definition\n", sc.String); + } + + } + } + useseq = true; + } + else if (strnicmp (sc.String, "EndGame", 7) == 0) + { + int type; + switch (sc.String[7]) + { + case '1': type = END_Pic1; break; + case '2': type = END_Pic2; break; + case '3': type = END_Bunny; break; + case 'C': type = END_Cast; break; + case 'W': type = END_Underwater; break; + case 'S': type = END_Strife; break; + default: type = END_Pic3; break; + } + newSeq.EndType = type; + useseq = true; + } + else if (sc.Compare("endpic")) + { + ParseComma(); + sc.MustGetString (); + newSeq.EndType = END_Pic; + newSeq.PicName = sc.String; + useseq = true; + } + else if (sc.Compare("endbunny")) + { + newSeq.EndType = END_Bunny; + useseq = true; + } + else if (sc.Compare("endcast")) + { + newSeq.EndType = END_Cast; + useseq = true; + } + else if (sc.Compare("enddemon")) + { + newSeq.EndType = END_Demon; + useseq = true; + } + else if (sc.Compare("endchess")) + { + newSeq.EndType = END_Chess; + useseq = true; + } + else if (sc.Compare("endunderwater")) + { + newSeq.EndType = END_Underwater; + useseq = true; + } + else if (sc.Compare("endbuystrife")) + { + newSeq.EndType = END_BuyStrife; + useseq = true; + } + else + { + strncpy (mapname, sc.String, 8); + } + if (useseq) + { + int seqnum = -1; + + if (!newSeq.Advanced) + { + seqnum = FindEndSequence (newSeq.EndType, newSeq.PicName); + } + + if (seqnum == -1) + { + seqnum = (int)EndSequences.Push (newSeq); + } + strcpy (mapname, "enDSeQ"); + *((WORD *)(mapname + 6)) = (WORD)seqnum; + } + } +} + +//========================================================================== +// +// Map options +// +//========================================================================== + +DEFINE_MAP_OPTION(levelnum, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->levelnum = parse.sc.Number; +} + +DEFINE_MAP_OPTION(next, true) +{ + parse.ParseAssign(); + parse.ParseNextMap(info->nextmap); +} + +DEFINE_MAP_OPTION(secretnext, true) +{ + parse.ParseAssign(); + parse.ParseNextMap(info->secretmap); +} + +DEFINE_MAP_OPTION(cluster, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->cluster = parse.sc.Number; + + // If this cluster hasn't been defined yet, add it. This is especially needed + // for Hexen, because it doesn't have clusterdefs. If we don't do this, every + // level on Hexen will sometimes be considered as being on the same hub, + // depending on the check done. + if (FindWadClusterInfo (parse.sc.Number) == -1) + { + unsigned int clusterindex = wadclusterinfos.Reserve(1); + cluster_info_t *clusterinfo = &wadclusterinfos[clusterindex]; + clusterinfo->cluster = parse.sc.Number; + if (parse.HexenHack) + { + clusterinfo->flags |= CLUSTER_HUB; + } + } +} + +DEFINE_MAP_OPTION(sky1, true) +{ + parse.ParseAssign(); + parse.ParseLumpOrTextureName(info->skypic1); + if (parse.CheckFloat()) + { + if (parse.HexenHack) + { + parse.sc.Float /= 256; + } + info->skyspeed1 = parse.sc.Float * (35.f / 1000.f); + } +} + +DEFINE_MAP_OPTION(sky2, true) +{ + parse.ParseAssign(); + parse.ParseLumpOrTextureName(info->skypic2); + if (parse.CheckFloat()) + { + if (parse.HexenHack) + { + parse.sc.Float /= 256; + } + info->skyspeed2 = parse.sc.Float * (35.f / 1000.f); + } +} + +DEFINE_MAP_OPTION(fade, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->fadeto = V_GetColor(NULL, parse.sc.String); +} + +DEFINE_MAP_OPTION(outsidefog, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->outsidefog = V_GetColor(NULL, parse.sc.String); +} + +DEFINE_MAP_OPTION(titlepatch, true) +{ + parse.ParseAssign(); + parse.ParseLumpOrTextureName(info->pname); +} + +DEFINE_MAP_OPTION(partime, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->partime = parse.sc.Number; +} + +DEFINE_MAP_OPTION(par, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->partime = parse.sc.Number; +} + +DEFINE_MAP_OPTION(sucktime, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->sucktime = parse.sc.Number; +} + +DEFINE_MAP_OPTION(music, true) +{ + parse.ParseAssign(); + parse.ParseMusic(info->Music, info->musicorder); + // Flag the level so that the $MAP command doesn't override this. + info->flags2 |= LEVEL2_MUSICDEFINED; +} + +DEFINE_MAP_OPTION(intermusic, true) +{ + parse.ParseAssign(); + parse.ParseMusic(info->InterMusic, info->intermusicorder); +} + +DEFINE_MAP_OPTION(fadetable, true) +{ + parse.ParseAssign(); + parse.ParseLumpOrTextureName(info->fadetable); +} + +DEFINE_MAP_OPTION(evenlighting, true) +{ + info->WallVertLight = info->WallHorizLight = 0; +} + +DEFINE_MAP_OPTION(cdtrack, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->cdtrack = parse.sc.Number; +} + +DEFINE_MAP_OPTION(cdid, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->cdid = strtoul (parse.sc.String, NULL, 16); +} + +DEFINE_MAP_OPTION(warptrans, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->WarpTrans = parse.sc.Number; +} + +DEFINE_MAP_OPTION(vertwallshade, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->WallVertLight = (SBYTE)clamp (parse.sc.Number / 2, -128, 127); +} + +DEFINE_MAP_OPTION(horizwallshade, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->WallHorizLight = (SBYTE)clamp (parse.sc.Number / 2, -128, 127); +} + +DEFINE_MAP_OPTION(gravity, true) +{ + parse.ParseAssign(); + parse.sc.MustGetFloat(); + info->gravity = parse.sc.Float; +} + +DEFINE_MAP_OPTION(aircontrol, true) +{ + parse.ParseAssign(); + parse.sc.MustGetFloat(); + info->aircontrol = parse.sc.Float; +} + +DEFINE_MAP_OPTION(airsupply, true) +{ + parse.ParseAssign(); + parse.sc.MustGetNumber(); + info->airsupply = parse.sc.Number; +} + +DEFINE_MAP_OPTION(interpic, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->ExitPic = parse.sc.String; +} + +DEFINE_MAP_OPTION(exitpic, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->ExitPic = parse.sc.String; +} + +DEFINE_MAP_OPTION(enterpic, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->EnterPic = parse.sc.String; +} + +DEFINE_MAP_OPTION(specialaction, true) +{ + parse.ParseAssign(); + + FSpecialAction *sa = &info->specialactions[info->specialactions.Reserve(1)]; + int min_arg, max_arg; + if (parse.format_type == parse.FMT_Old) parse.sc.SetCMode(true); + parse.sc.MustGetString(); + sa->Type = FName(parse.sc.String); + parse.sc.CheckString(","); + parse.sc.MustGetString(); + sa->Action = P_FindLineSpecial(parse.sc.String, &min_arg, &max_arg); + if (sa->Action == 0 || min_arg < 0) + { + parse.sc.ScriptError("Unknown specialaction '%s'"); + } + int j = 0; + while (j < 5 && parse.sc.CheckString(",")) + { + parse.sc.MustGetNumber(); + sa->Args[j++] = parse.sc.Number; + } + if (parse.format_type == parse.FMT_Old) parse.sc.SetCMode(false); +} + +DEFINE_MAP_OPTION(redirect, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->RedirectType = parse.sc.String; + parse.ParseComma(); + parse.ParseLumpOrTextureName(info->RedirectMap); +} + +DEFINE_MAP_OPTION(sndseq, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->SndSeq = parse.sc.String; +} + +DEFINE_MAP_OPTION(sndinfo, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->SoundInfo = parse.sc.String; +} + +DEFINE_MAP_OPTION(soundinfo, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->SoundInfo = parse.sc.String; +} + +DEFINE_MAP_OPTION(translator, true) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->Translator = parse.sc.String; +} + +DEFINE_MAP_OPTION(bordertexture, true) +{ + parse.ParseAssign(); + parse.ParseLumpOrTextureName(info->bordertexture); +} + +DEFINE_MAP_OPTION(f1, true) +{ + parse.ParseAssign(); + parse.ParseLumpOrTextureName(info->f1); +} + +DEFINE_MAP_OPTION(teamdamage, true) +{ + parse.ParseAssign(); + parse.sc.MustGetFloat(); + info->teamdamage = parse.sc.Float; +} + +//========================================================================== +// +// All flag based map options +// +//========================================================================== + +enum EMIType +{ + MITYPE_IGNORE, + MITYPE_EATNEXT, + MITYPE_SETFLAG, + MITYPE_CLRFLAG, + MITYPE_SCFLAGS, + MITYPE_SETFLAG2, + MITYPE_CLRFLAG2, + MITYPE_SCFLAGS2, + MITYPE_COMPATFLAG, +}; + +struct MapInfoFlagHandler +{ + const char *name; + EMIType type; + DWORD data1, data2; +} +MapFlagHandlers[] = +{ + { "nointermission", MITYPE_SETFLAG, LEVEL_NOINTERMISSION, 0 }, + { "intermission", MITYPE_CLRFLAG, LEVEL_NOINTERMISSION, 0 }, + { "doublesky", MITYPE_SETFLAG, LEVEL_DOUBLESKY, 0 }, + { "nosoundclipping", MITYPE_IGNORE, 0, 0 }, // was nosoundclipping + { "allowmonstertelefrags", MITYPE_SETFLAG, LEVEL_MONSTERSTELEFRAG, 0 }, + { "map07special", MITYPE_SETFLAG, LEVEL_MAP07SPECIAL, 0 }, + { "baronspecial", MITYPE_SETFLAG, LEVEL_BRUISERSPECIAL, 0 }, + { "cyberdemonspecial", MITYPE_SETFLAG, LEVEL_CYBORGSPECIAL, 0 }, + { "spidermastermindspecial", MITYPE_SETFLAG, LEVEL_SPIDERSPECIAL, 0 }, + { "minotaurspecial", MITYPE_SETFLAG, LEVEL_MINOTAURSPECIAL, 0 }, + { "dsparilspecial", MITYPE_SETFLAG, LEVEL_SORCERER2SPECIAL, 0 }, + { "ironlichspecial", MITYPE_SETFLAG, LEVEL_HEADSPECIAL, 0 }, + { "specialaction_exitlevel", MITYPE_SCFLAGS, 0, ~LEVEL_SPECACTIONSMASK }, + { "specialaction_opendoor", MITYPE_SCFLAGS, LEVEL_SPECOPENDOOR, ~LEVEL_SPECACTIONSMASK }, + { "specialaction_lowerfloor", MITYPE_SCFLAGS, LEVEL_SPECLOWERFLOOR, ~LEVEL_SPECACTIONSMASK }, + { "specialaction_killmonsters", MITYPE_SETFLAG, LEVEL_SPECKILLMONSTERS, 0 }, + { "lightning", MITYPE_SETFLAG, LEVEL_STARTLIGHTNING, 0 }, + { "smoothlighting", MITYPE_SETFLAG2, LEVEL2_SMOOTHLIGHTING, 0 }, + { "noautosequences", MITYPE_SETFLAG, LEVEL_SNDSEQTOTALCTRL, 0 }, + { "autosequences", MITYPE_CLRFLAG, LEVEL_SNDSEQTOTALCTRL, 0 }, + { "forcenoskystretch", MITYPE_SETFLAG, LEVEL_FORCENOSKYSTRETCH, 0 }, + { "skystretch", MITYPE_CLRFLAG, LEVEL_FORCENOSKYSTRETCH, 0 }, + { "allowfreelook", MITYPE_SCFLAGS, LEVEL_FREELOOK_YES, ~LEVEL_FREELOOK_NO }, + { "nofreelook", MITYPE_SCFLAGS, LEVEL_FREELOOK_NO, ~LEVEL_FREELOOK_YES }, + { "allowjump", MITYPE_CLRFLAG, LEVEL_JUMP_NO, 0 }, + { "nojump", MITYPE_SETFLAG, LEVEL_JUMP_NO, 0 }, + { "fallingdamage", MITYPE_SCFLAGS, LEVEL_FALLDMG_HX, ~LEVEL_FALLDMG_ZD }, + { "oldfallingdamage", MITYPE_SCFLAGS, LEVEL_FALLDMG_ZD, ~LEVEL_FALLDMG_HX }, + { "forcefallingdamage", MITYPE_SCFLAGS, LEVEL_FALLDMG_ZD, ~LEVEL_FALLDMG_HX }, + { "strifefallingdamage", MITYPE_SETFLAG, LEVEL_FALLDMG_ZD|LEVEL_FALLDMG_HX, 0 }, + { "nofallingdamage", MITYPE_SCFLAGS, 0, ~(LEVEL_FALLDMG_ZD|LEVEL_FALLDMG_HX) }, + { "noallies", MITYPE_SETFLAG, LEVEL_NOALLIES, 0 }, + { "filterstarts", MITYPE_SETFLAG, LEVEL_FILTERSTARTS, 0 }, + { "activateowndeathspecials", MITYPE_SETFLAG, LEVEL_ACTOWNSPECIAL, 0 }, + { "killeractivatesdeathspecials", MITYPE_CLRFLAG, LEVEL_ACTOWNSPECIAL, 0 }, + { "missilesactivateimpactlines", MITYPE_SETFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, + { "missileshootersactivetimpactlines",MITYPE_CLRFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, + { "noinventorybar", MITYPE_SETFLAG, LEVEL_NOINVENTORYBAR, 0 }, + { "deathslideshow", MITYPE_SETFLAG2, LEVEL2_DEATHSLIDESHOW, 0 }, + { "strictmonsteractivation", MITYPE_CLRFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, + { "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, + { "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL}, + { "keepfullinventory", MITYPE_SETFLAG2, LEVEL2_KEEPFULLINVENTORY, 0 }, + { "monsterfallingdamage", MITYPE_SETFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 }, + { "nomonsterfallingdamage", MITYPE_CLRFLAG2, LEVEL2_MONSTERFALLINGDAMAGE, 0 }, + { "clipmidtextures", MITYPE_SETFLAG2, LEVEL2_CLIPMIDTEX, 0 }, + { "wrapmidtextures", MITYPE_SETFLAG2, LEVEL2_WRAPMIDTEX, 0 }, + { "allowcrouch", MITYPE_CLRFLAG, LEVEL_CROUCH_NO, 0 }, + { "nocrouch", MITYPE_SETFLAG, LEVEL_CROUCH_NO, 0 }, + { "pausemusicinmenus", MITYPE_SCFLAGS2, LEVEL2_PAUSE_MUSIC_IN_MENUS, 0 }, + { "noinfighting", MITYPE_SCFLAGS2, LEVEL2_NOINFIGHTING, ~LEVEL2_TOTALINFIGHTING }, + { "normalinfighting", MITYPE_SCFLAGS2, 0, ~(LEVEL2_NOINFIGHTING|LEVEL2_TOTALINFIGHTING)}, + { "totalinfighting", MITYPE_SCFLAGS2, LEVEL2_TOTALINFIGHTING, ~LEVEL2_NOINFIGHTING }, + { "infiniteflightpowerup", MITYPE_SETFLAG2, LEVEL2_INFINITE_FLIGHT, 0 }, + { "noinfiniteflightpowerup", MITYPE_CLRFLAG2, LEVEL2_INFINITE_FLIGHT, 0 }, + { "allowrespawn", MITYPE_SETFLAG2, LEVEL2_ALLOWRESPAWN, 0 }, + { "teamplayon", MITYPE_SCFLAGS2, LEVEL2_FORCETEAMPLAYON, ~LEVEL2_FORCETEAMPLAYOFF }, + { "teamplayoff", MITYPE_SCFLAGS2, LEVEL2_FORCETEAMPLAYOFF, ~LEVEL2_FORCETEAMPLAYON }, + { "checkswitchrange", MITYPE_SETFLAG2, LEVEL2_CHECKSWITCHRANGE, 0 }, + { "nocheckswitchrange", MITYPE_CLRFLAG2, LEVEL2_CHECKSWITCHRANGE, 0 }, + { "unfreezesingleplayerconversations",MITYPE_SETFLAG2, LEVEL2_CONV_SINGLE_UNFREEZE, 0 }, + { "nobotnodes", MITYPE_IGNORE, 0, 0 }, // Skulltag option: nobotnodes + { "compat_shorttex", MITYPE_COMPATFLAG, COMPATF_SHORTTEX}, + { "compat_stairs", MITYPE_COMPATFLAG, COMPATF_STAIRINDEX}, + { "compat_limitpain", MITYPE_COMPATFLAG, COMPATF_LIMITPAIN}, + { "compat_nopassover", MITYPE_COMPATFLAG, COMPATF_NO_PASSMOBJ}, + { "compat_notossdrops", MITYPE_COMPATFLAG, COMPATF_NOTOSSDROPS}, + { "compat_useblocking", MITYPE_COMPATFLAG, COMPATF_USEBLOCKING}, + { "compat_nodoorlight", MITYPE_COMPATFLAG, COMPATF_NODOORLIGHT}, + { "compat_ravenscroll", MITYPE_COMPATFLAG, COMPATF_RAVENSCROLL}, + { "compat_soundtarget", MITYPE_COMPATFLAG, COMPATF_SOUNDTARGET}, + { "compat_dehhealth", MITYPE_COMPATFLAG, COMPATF_DEHHEALTH}, + { "compat_trace", MITYPE_COMPATFLAG, COMPATF_TRACE}, + { "compat_dropoff", MITYPE_COMPATFLAG, COMPATF_DROPOFF}, + { "compat_boomscroll", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL}, + { "compat_invisibility", MITYPE_COMPATFLAG, COMPATF_INVISIBILITY}, + { "compat_silent_instant_floors", MITYPE_COMPATFLAG, COMPATF_SILENT_INSTANT_FLOORS}, + { "compat_sectorsounds", MITYPE_COMPATFLAG, COMPATF_SECTORSOUNDS}, + { "compat_missileclip", MITYPE_COMPATFLAG, COMPATF_MISSILECLIP}, + { "compat_crossdropoff", MITYPE_COMPATFLAG, COMPATF_CROSSDROPOFF}, + { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, + { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, + { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, + { "cd_end3_track", MITYPE_EATNEXT, 0, 0 }, + { "cd_intermission_track", MITYPE_EATNEXT, 0, 0 }, + { "cd_title_track", MITYPE_EATNEXT, 0, 0 }, + { NULL, MITYPE_IGNORE, 0} +}; + +//========================================================================== +// +// ParseMapDefinition +// Parses the body of a map definition, including defaultmap etc. +// +//========================================================================== + +void FMapInfoParser::ParseMapDefinition(level_info_t &info) +{ + int index; + + ParseOpenBrace(); + + while (sc.GetString()) + { + if ((index = sc.MatchString(&MapFlagHandlers->name, sizeof(*MapFlagHandlers))) >= 0) + { + MapInfoFlagHandler *handler = &MapFlagHandlers[index]; + switch (handler->type) + { + case MITYPE_EATNEXT: + ParseAssign(); + sc.MustGetString(); + break; + + case MITYPE_IGNORE: + break; + + case MITYPE_SETFLAG: + info.flags |= handler->data1; + info.flags |= handler->data2; + break; + + case MITYPE_CLRFLAG: + info.flags &= ~handler->data1; + info.flags |= handler->data2; + break; + + case MITYPE_SCFLAGS: + info.flags = (info.flags & handler->data2) | handler->data1; + break; + + case MITYPE_SETFLAG2: + info.flags2 |= handler->data1; + info.flags2 |= handler->data2; + break; + + case MITYPE_CLRFLAG2: + info.flags2 &= ~handler->data1; + info.flags2 |= handler->data2; + break; + + case MITYPE_SCFLAGS2: + info.flags2 = (info.flags2 & handler->data2) | handler->data1; + break; + + case MITYPE_COMPATFLAG: + { + int set = 1; + if (format_type == FMT_New) + { + if (CheckAssign()) + { + sc.MustGetNumber(); + set = sc.Number; + } + } + else + { + if (sc.CheckNumber()) set = sc.Number; + } + + if (set) info.compatflags |= handler->data1; + else info.compatflags &= ~handler->data1; + info.compatmask |= handler->data1; + } + break; + + default: + // should never happen + assert(false); + break; + } + } + else + { + TAutoSegIterator probe; + bool success = false; + + while (++probe != NULL) + { + if (sc.Compare(probe->name)) + { + probe->handler(*this, &info); + success = true; + break; + } + } + + if (!success) + { + if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in map definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + } + } +} + + +//========================================================================== +// +// GetDefaultLevelNum +// Gets a default level num from a map name. +// +//========================================================================== + +static int GetDefaultLevelNum(const char *mapname) +{ + if (!strnicmp (mapname, "MAP", 3) && strlen(mapname) <= 5) + { + int mapnum = atoi (mapname + 3); + + if (mapnum >= 1 && mapnum <= 99) + return mapnum; + } + else if (mapname[0] == 'E' && + mapname[1] >= '0' && mapname[1] <= '9' && + mapname[2] == 'M' && + mapname[3] >= '0' && mapname[3] <= '9') + { + int epinum = mapname[1] - '1'; + int mapnum = mapname[3] - '0'; + return epinum*10 + mapnum; + } + return 0; +} + +//========================================================================== +// +// ParseMapHeader +// Parses the header of a map definition ('map mapxx mapname') +// +//========================================================================== + +level_info_t *FMapInfoParser::ParseMapHeader(level_info_t &defaultinfo) +{ + FName mapname; + + if (sc.CheckNumber()) + { // MAPNAME is a number; assume a Hexen wad + char maptemp[8]; + mysnprintf (maptemp, countof(maptemp), "MAP%02d", sc.Number); + mapname = maptemp; + HexenHack = true; + } + else + { + sc.MustGetString(); + mapname = sc.String; + } + int levelindex = FindWadLevelInfo (mapname); + if (levelindex == -1) + { + levelindex = wadlevelinfos.Reserve(1); + } + level_info_t *levelinfo = &wadlevelinfos[levelindex]; + *levelinfo = defaultinfo; + if (HexenHack) + { + levelinfo->WallHorizLight = levelinfo->WallVertLight = 0; + + // Hexen levels are automatically nointermission, + // no auto sound sequences, falling damage, + // monsters activate their own specials, and missiles + // are always the activators of impact lines. + levelinfo->flags |= LEVEL_NOINTERMISSION + | LEVEL_SNDSEQTOTALCTRL + | LEVEL_FALLDMG_HX + | LEVEL_ACTOWNSPECIAL; + levelinfo->flags2|= LEVEL2_HEXENHACK + | LEVEL2_INFINITE_FLIGHT + | LEVEL2_MISSILESACTIVATEIMPACT + | LEVEL2_MONSTERFALLINGDAMAGE; + + } + + uppercopy (levelinfo->mapname, mapname); + levelinfo->mapname[8] = 0; + sc.MustGetString (); + if (sc.String[0] == '$') + { + // For consistency with other definitions allow $Stringtablename here, too. + levelinfo->flags |= LEVEL_LOOKUPLEVELNAME; + levelinfo->LevelName = sc.String + 1; + } + else + { + if (sc.Compare ("lookup")) + { + sc.MustGetString (); + levelinfo->flags |= LEVEL_LOOKUPLEVELNAME; + } + levelinfo->LevelName = sc.String; + } + + // Set up levelnum now so that you can use Teleport_NewMap specials + // to teleport to maps with standard names without needing a levelnum. + levelinfo->levelnum = GetDefaultLevelNum(levelinfo->mapname); + + return levelinfo; +} + + +//========================================================================== +// +// Episode definitions start with the header "episode " +// and then can be followed by any of the following: +// +// name "Episode name as text" +// picname "Picture to display the episode name" +// key "Shortcut key for the menu" +// noskillmenu +// remove +// +//========================================================================== + +void FMapInfoParser::ParseEpisodeInfo () +{ + int i; + char map[9]; + char *pic = NULL; + bool picisgfx = false; // Shut up, GCC!!!! + bool remove = false; + char key = 0; + bool noskill = false; + bool optional = false; + bool extended = false; + + // Get map name + sc.MustGetString (); + uppercopy (map, sc.String); + map[8] = 0; + + if (sc.CheckString ("teaser")) + { + sc.MustGetString (); + if (gameinfo.flags & GI_SHAREWARE) + { + uppercopy (map, sc.String); + } + } + + ParseOpenBrace(); + + while (sc.GetString()) + { + if (sc.Compare ("optional")) + { + // For M4 in Doom + optional = true; + } + else if (sc.Compare ("extended")) + { + // For M4 and M5 in Heretic + extended = true; + } + else if (sc.Compare ("name")) + { + ParseAssign(); + sc.MustGetString (); + ReplaceString (&pic, sc.String); + picisgfx = false; + } + else if (sc.Compare ("picname")) + { + ParseAssign(); + sc.MustGetString (); + ReplaceString (&pic, sc.String); + picisgfx = true; + } + else if (sc.Compare ("remove")) + { + remove = true; + } + else if (sc.Compare ("key")) + { + ParseAssign(); + sc.MustGetString (); + key = sc.String[0]; + } + else if (sc.Compare("noskillmenu")) + { + noskill = true; + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in episode definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + + if (extended && !(gameinfo.flags & GI_MENUHACK_EXTENDED)) + { // If the episode is for the extended Heretic, but this is + // not the extended Heretic, ignore it. + return; + } + + if (optional && !remove) + { + if (!P_CheckMapData(map)) + { + // If the episode is optional and the map does not exist + // just ignore this episode definition. + return; + } + } + + + for (i = 0; i < EpiDef.numitems; ++i) + { + if (strncmp (EpisodeMaps[i], map, 8) == 0) + { + break; + } + } + + if (remove) + { + // If the remove property is given for an episode, remove it. + if (i < EpiDef.numitems) + { + if (i+1 < EpiDef.numitems) + { + memmove (&EpisodeMaps[i], &EpisodeMaps[i+1], + sizeof(EpisodeMaps[0])*(EpiDef.numitems - i - 1)); + memmove (&EpisodeMenu[i], &EpisodeMenu[i+1], + sizeof(EpisodeMenu[0])*(EpiDef.numitems - i - 1)); + memmove (&EpisodeNoSkill[i], &EpisodeNoSkill[i+1], + sizeof(EpisodeNoSkill[0])*(EpiDef.numitems - i - 1)); + } + EpiDef.numitems--; + } + } + else + { + if (pic == NULL) + { + pic = copystring (map); + picisgfx = false; + } + + if (i == EpiDef.numitems) + { + if (EpiDef.numitems == MAX_EPISODES) + { + i = EpiDef.numitems - 1; + } + else + { + i = EpiDef.numitems++; + } + } + else + { + delete[] const_cast(EpisodeMenu[i].name); + } + + EpisodeMenu[i].name = pic; + EpisodeMenu[i].alphaKey = tolower(key); + EpisodeMenu[i].fulltext = !picisgfx; + EpisodeNoSkill[i] = noskill; + strncpy (EpisodeMaps[i], map, 8); + } +} + + +//========================================================================== +// +// Clears episode definitions +// +//========================================================================== + +void ClearEpisodes() +{ + for (int i = 0; i < EpiDef.numitems; ++i) + { + delete[] const_cast(EpisodeMenu[i].name); + EpisodeMenu[i].name = NULL; + } + EpiDef.numitems = 0; +} + +//========================================================================== +// +// SetLevelNum +// Avoid duplicate levelnums. The level being set always has precedence. +// +//========================================================================== + +static void SetLevelNum (level_info_t *info, int num) +{ + for (unsigned int i = 0; i < wadlevelinfos.Size(); ++i) + { + if (wadlevelinfos[i].levelnum == num) + wadlevelinfos[i].levelnum = 0; + } + info->levelnum = num; +} + +//========================================================================== +// +// G_DoParseMapInfo +// Parses a single MAPINFO lump +// data for wadlevelinfos and wadclusterinfos. +// +//========================================================================== + +void FMapInfoParser::ParseMapInfo (int lump, level_info_t &gamedefaults) +{ + level_info_t defaultinfo; + + sc.OpenLumpNum(lump); + + defaultinfo = gamedefaults; + HexenHack = false; + + while (sc.GetString ()) + { + if (sc.Compare("gamedefaults")) + { + gamedefaults.Reset(); + ParseMapDefinition(gamedefaults); + defaultinfo = gamedefaults; + } + else if (sc.Compare("defaultmap")) + { + defaultinfo = gamedefaults; + ParseMapDefinition(defaultinfo); + } + else if (sc.Compare("adddefaultmap")) + { + // Same as above but adds to the existing definitions instead of replacing them completely + ParseMapDefinition(defaultinfo); + } + else if (sc.Compare("map")) + { + level_info_t *levelinfo = ParseMapHeader(defaultinfo); + + ParseMapDefinition(*levelinfo); + + // When the second sky is -NOFLAT-, make it a copy of the first sky + if (strcmp (levelinfo->skypic2, "-NOFLAT-") == 0) + { + strcpy (levelinfo->skypic2, levelinfo->skypic1); + } + SetLevelNum (levelinfo, levelinfo->levelnum); // Wipe out matching levelnums from other maps. + } + else if (sc.Compare("clusterdef")) + { + ParseCluster(); + } + else if (sc.Compare("episode")) + { + ParseEpisodeInfo(); + } + else if (sc.Compare("clearepisodes")) + { + ClearEpisodes(); + } + else if (sc.Compare("skill")) + { + ParseSkill(); + } + else if (sc.Compare("clearskills")) + { + AllSkills.Clear(); + } + else + { + sc.ScriptError("%s: Unknown top level keyword", sc.String); + } + } +} + + +//========================================================================== +// +// G_ParseMapInfo +// Parses the MAPINFO lumps of all loaded WADs and generates +// data for wadlevelinfos and wadclusterinfos. +// +//========================================================================== + +void G_ParseMapInfo () +{ + int lump, lastlump = 0; + level_info_t gamedefaults; + + atterm(ClearEpisodes); + + // Parse the default MAPINFO for the current game. + for(int i=0; i<2; i++) + { + if (gameinfo.mapinfo[i] != NULL) + { + FMapInfoParser parse; + parse.ParseMapInfo(Wads.GetNumForFullName(gameinfo.mapinfo[i]), gamedefaults); + } + } + + // Parse any extra MAPINFOs. + while ((lump = Wads.FindLump ("MAPINFO", &lastlump)) != -1) + { + FMapInfoParser parse; + parse.ParseMapInfo(lump, gamedefaults); + } + EndSequences.ShrinkToFit (); + + if (EpiDef.numitems == 0) + { + I_FatalError ("You cannot use clearepisodes in a MAPINFO if you do not define any new episodes after it."); + } + if (AllSkills.Size()==0) + { + I_FatalError ("You cannot use clearskills in a MAPINFO if you do not define any new skills after it."); + } +} + diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 6ee93f4f75..37bf53123b 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -951,7 +951,7 @@ void APowerFlight::InitEffect () void APowerFlight::Tick () { // The Wings of Wrath only expire in multiplayer and non-hub games - if (!multiplayer && (level.flags & LEVEL_INFINITE_FLIGHT)) + if (!multiplayer && (level.flags2 & LEVEL2_INFINITE_FLIGHT)) { assert(EffectTics < INT_MAX); // I can't see a game lasting nearly two years, but... EffectTics++; @@ -1364,7 +1364,7 @@ void APowerTimeFreezer::InitEffect( ) // Make sure the effect starts and ends on an even tic. if ((level.time & 1) == 0) { - level.flags |= LEVEL_FROZEN; + level.flags2 |= LEVEL2_FROZEN; } else { @@ -1395,9 +1395,9 @@ void APowerTimeFreezer::DoEffect( ) || (( EffectTics > 2*32 && EffectTics <= 3*32 ) && ((EffectTics + 1) & 7) != 0 ) || (( EffectTics > 32 && EffectTics <= 2*32 ) && ((EffectTics + 1) & 3) != 0 ) || (( EffectTics > 0 && EffectTics <= 1*32 ) && ((EffectTics + 1) & 1) != 0 )) - level.flags |= LEVEL_FROZEN; + level.flags2 |= LEVEL2_FROZEN; else - level.flags &= ~LEVEL_FROZEN; + level.flags2 &= ~LEVEL2_FROZEN; } //=========================================================================== @@ -1411,7 +1411,7 @@ void APowerTimeFreezer::EndEffect( ) int ulIdx; // Allow other actors to move about freely once again. - level.flags &= ~LEVEL_FROZEN; + level.flags2 &= ~LEVEL2_FROZEN; // Also, turn the music back on. S_ResumeSound( ); diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 67635acb40..e466d1cd09 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1707,7 +1707,7 @@ IMPLEMENT_CLASS (AMapRevealer) bool AMapRevealer::TryPickup (AActor *&toucher) { - level.flags |= LEVEL_ALLMAP; + level.flags2 |= LEVEL2_ALLMAP; GoAwayAndDie (); return true; } diff --git a/src/g_shared/sbarinfo_display.cpp b/src/g_shared/sbarinfo_display.cpp index 790a9a49c9..205c820576 100644 --- a/src/g_shared/sbarinfo_display.cpp +++ b/src/g_shared/sbarinfo_display.cpp @@ -56,6 +56,7 @@ #include "a_strifeglobal.h" #include "g_level.h" #include "v_palette.h" +#include "p_acs.h" static FRandom pr_chainwiggle; //use the same method of chain wiggling as heretic. diff --git a/src/g_shared/sbarinfo_parser.cpp b/src/g_shared/sbarinfo_parser.cpp index a502cb99ce..e9e1f21871 100644 --- a/src/g_shared/sbarinfo_parser.cpp +++ b/src/g_shared/sbarinfo_parser.cpp @@ -46,6 +46,7 @@ #include "gi.h" #include "i_system.h" #include "g_level.h" +#include "p_acs.h" SBarInfo *SBarInfoScript[2] = {NULL,NULL}; diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index cb77e40d19..43b0600231 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -875,7 +875,7 @@ void DrawHUD() } } - mysnprintf(printstr, countof(printstr), "%s: %s", level.mapname, level.level_name); + mysnprintf(printstr, countof(printstr), "%s: %s", level.mapname, level.LevelName.GetChars()); screen->DrawText(SmallFont, hudcolor_titl, 1, hudheight-fonth-1, printstr, DTA_KeepRatio, true, DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, TAG_DONE); diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index cc25a3b185..644e250853 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -1242,11 +1242,11 @@ void DBaseStatusBar::Draw (EHudState state) { i = mysnprintf (line, countof(line), "%s: ", level.mapname); } - line[i] = TEXTCOLOR_ESCAPE; - line[i+1] = CR_GREY + 'A'; - strcpy (&line[i+2], level.level_name); + FString mapname; + + mapname.Format("%c%c%s", TEXTCOLOR_ESCAPE, CR_GREY + 'A', level.LevelName.GetChars()); screen->DrawText (SmallFont, highlight, - (SCREENWIDTH - SmallFont->StringWidth (line)*CleanXfac)/2, y, line, + (SCREENWIDTH - SmallFont->StringWidth (mapname)*CleanXfac)/2, y, mapname, DTA_CleanNoMove, true, TAG_DONE); if (!deathmatch) diff --git a/src/g_skill.cpp b/src/g_skill.cpp new file mode 100644 index 0000000000..a664dc7ab9 --- /dev/null +++ b/src/g_skill.cpp @@ -0,0 +1,354 @@ +/* +** g_skill.cpp +** Skill level handling +** +**--------------------------------------------------------------------------- +** Copyright 2008-2009 Christoph Oelckers +** Copyright 2008-2009 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include "doomstat.h" +#include "g_level.h" +#include "g_game.h" +#include "gi.h" +#include "templates.h" +#include "v_font.h" + +TArray AllSkills; + +//========================================================================== +// +// ParseSkill +// +//========================================================================== + +void FMapInfoParser::ParseSkill () +{ + FSkillInfo skill; + + skill.AmmoFactor = FRACUNIT; + skill.DoubleAmmoFactor = 2*FRACUNIT; + skill.DropAmmoFactor = -1; + skill.DamageFactor = FRACUNIT; + skill.FastMonsters = false; + skill.DisableCheats = false; + skill.EasyBossBrain = false; + skill.AutoUseHealth = false; + skill.RespawnCounter = 0; + skill.RespawnLimit = 0; + skill.Aggressiveness = FRACUNIT; + skill.SpawnFilter = 0; + skill.ACSReturn = AllSkills.Size(); + skill.MenuNameIsLump = false; + skill.MustConfirm = false; + skill.Shortcut = 0; + skill.TextColor = ""; + + sc.MustGetString(); + skill.Name = sc.String; + + ParseOpenBrace(); + + while (sc.GetString ()) + { + if (sc.Compare ("ammofactor")) + { + ParseAssign(); + sc.MustGetFloat (); + skill.AmmoFactor = FLOAT2FIXED(sc.Float); + } + else if (sc.Compare ("doubleammofactor")) + { + ParseAssign(); + sc.MustGetFloat (); + skill.DoubleAmmoFactor = FLOAT2FIXED(sc.Float); + } + else if (sc.Compare ("dropammofactor")) + { + ParseAssign(); + sc.MustGetFloat (); + skill.DropAmmoFactor = FLOAT2FIXED(sc.Float); + } + else if (sc.Compare ("damagefactor")) + { + ParseAssign(); + sc.MustGetFloat (); + skill.DamageFactor = FLOAT2FIXED(sc.Float); + } + else if (sc.Compare ("fastmonsters")) + { + skill.FastMonsters = true; + } + else if (sc.Compare ("disablecheats")) + { + skill.DisableCheats = true; + } + else if (sc.Compare ("easybossbrain")) + { + skill.EasyBossBrain = true; + } + else if (sc.Compare("autousehealth")) + { + skill.AutoUseHealth = true; + } + else if (sc.Compare("respawntime")) + { + ParseAssign(); + sc.MustGetFloat (); + skill.RespawnCounter = int(sc.Float*TICRATE); + } + else if (sc.Compare("respawnlimit")) + { + ParseAssign(); + sc.MustGetNumber (); + skill.RespawnLimit = sc.Number; + } + else if (sc.Compare("Aggressiveness")) + { + ParseAssign(); + sc.MustGetFloat (); + skill.Aggressiveness = FRACUNIT - FLOAT2FIXED(clamp(sc.Float, 0.,1.)); + } + else if (sc.Compare("SpawnFilter")) + { + ParseAssign(); + if (sc.CheckNumber()) + { + if (sc.Number > 0) skill.SpawnFilter |= (1<<(sc.Number-1)); + } + else + { + sc.MustGetString (); + if (sc.Compare("baby")) skill.SpawnFilter |= 1; + else if (sc.Compare("easy")) skill.SpawnFilter |= 2; + else if (sc.Compare("normal")) skill.SpawnFilter |= 4; + else if (sc.Compare("hard")) skill.SpawnFilter |= 8; + else if (sc.Compare("nightmare")) skill.SpawnFilter |= 16; + } + } + else if (sc.Compare("ACSReturn")) + { + ParseAssign(); + sc.MustGetNumber (); + skill.ACSReturn = sc.Number; + } + else if (sc.Compare("Name")) + { + ParseAssign(); + sc.MustGetString (); + skill.MenuName = sc.String; + skill.MenuNameIsLump = false; + } + else if (sc.Compare("PlayerClassName")) + { + ParseAssign(); + sc.MustGetString (); + FName pc = sc.String; + ParseComma(); + sc.MustGetString (); + skill.MenuNamesForPlayerClass[pc]=sc.String; + } + else if (sc.Compare("PicName")) + { + ParseAssign(); + sc.MustGetString (); + skill.MenuName = sc.String; + skill.MenuNameIsLump = true; + } + else if (sc.Compare("MustConfirm")) + { + ParseAssign(); + skill.MustConfirm = true; + if (sc.CheckToken(TK_StringConst)) + { + skill.MustConfirmText = sc.String; + } + } + else if (sc.Compare("Key")) + { + ParseAssign(); + sc.MustGetString(); + skill.Shortcut = tolower(sc.String[0]); + } + else if (sc.Compare("TextColor")) + { + ParseAssign(); + sc.MustGetString(); + skill.TextColor.Format("[%s]", sc.String); + } + else if (!ParseCloseBrace()) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in skill definition\n", sc.String); + SkipToNext(); + } + else + { + break; + } + } + for(unsigned int i = 0; i < AllSkills.Size(); i++) + { + if (AllSkills[i].Name == skill.Name) + { + AllSkills[i] = skill; + return; + } + } + AllSkills.Push(skill); +} + +//========================================================================== +// +// +// +//========================================================================== + +int G_SkillProperty(ESkillProperty prop) +{ + if (AllSkills.Size() > 0) + { + switch(prop) + { + case SKILLP_AmmoFactor: + if (dmflags2 & DF2_YES_DOUBLEAMMO) + { + return AllSkills[gameskill].DoubleAmmoFactor; + } + return AllSkills[gameskill].AmmoFactor; + + case SKILLP_DropAmmoFactor: + return AllSkills[gameskill].DropAmmoFactor; + + case SKILLP_DamageFactor: + return AllSkills[gameskill].DamageFactor; + + case SKILLP_FastMonsters: + return AllSkills[gameskill].FastMonsters || (dmflags & DF_FAST_MONSTERS); + + case SKILLP_Respawn: + if (dmflags & DF_MONSTERS_RESPAWN && AllSkills[gameskill].RespawnCounter==0) + return TICRATE * (gameinfo.gametype != GAME_Strife ? 12 : 16); + return AllSkills[gameskill].RespawnCounter; + + case SKILLP_RespawnLimit: + return AllSkills[gameskill].RespawnLimit; + + case SKILLP_Aggressiveness: + return AllSkills[gameskill].Aggressiveness; + + case SKILLP_DisableCheats: + return AllSkills[gameskill].DisableCheats; + + case SKILLP_AutoUseHealth: + return AllSkills[gameskill].AutoUseHealth; + + case SKILLP_EasyBossBrain: + return AllSkills[gameskill].EasyBossBrain; + + case SKILLP_SpawnFilter: + return AllSkills[gameskill].SpawnFilter; + + case SKILLP_ACSReturn: + return AllSkills[gameskill].ACSReturn; + } + } + return 0; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void G_VerifySkill() +{ + if (gameskill >= (int)AllSkills.Size()) + gameskill = AllSkills.Size()-1; + else if (gameskill < 0) + gameskill = 0; +} + +//========================================================================== +// +// +// +//========================================================================== + +FSkillInfo &FSkillInfo::operator=(const FSkillInfo &other) +{ + Name = other.Name; + AmmoFactor = other.AmmoFactor; + DoubleAmmoFactor = other.DoubleAmmoFactor; + DropAmmoFactor = other.DropAmmoFactor; + DamageFactor = other.DamageFactor; + FastMonsters = other.FastMonsters; + DisableCheats = other.DisableCheats; + AutoUseHealth = other.AutoUseHealth; + EasyBossBrain = other.EasyBossBrain; + RespawnCounter= other.RespawnCounter; + RespawnLimit= other.RespawnLimit; + Aggressiveness= other.Aggressiveness; + SpawnFilter = other.SpawnFilter; + ACSReturn = other.ACSReturn; + MenuName = other.MenuName; + MenuNamesForPlayerClass = other.MenuNamesForPlayerClass; + MenuNameIsLump = other.MenuNameIsLump; + MustConfirm = other.MustConfirm; + MustConfirmText = other.MustConfirmText; + Shortcut = other.Shortcut; + TextColor = other.TextColor; + return *this; +} + +//========================================================================== +// +// +// +//========================================================================== + +int FSkillInfo::GetTextColor() const +{ + if (TextColor.IsEmpty()) + { + return CR_UNTRANSLATED; + } + const BYTE *cp = (const BYTE *)TextColor.GetChars(); + int color = V_ParseFontColor(cp, 0, 0); + if (color == CR_UNDEFINED) + { + Printf("Undefined color '%s' in definition of skill %s\n", TextColor.GetChars(), Name.GetChars()); + color = CR_UNTRANSLATED; + } + return color; +} + diff --git a/src/g_strife/a_strifeitems.cpp b/src/g_strife/a_strifeitems.cpp index c1028ccfab..15bb4fbd24 100644 --- a/src/g_strife/a_strifeitems.cpp +++ b/src/g_strife/a_strifeitems.cpp @@ -106,7 +106,7 @@ IMPLEMENT_CLASS (AScanner) bool AScanner::Use (bool pickup) { - if (!(level.flags & LEVEL_ALLMAP)) + if (!(level.flags2 & LEVEL2_ALLMAP)) { if (Owner->CheckLocalView (consoleplayer)) { diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 12356e4002..b8b73aa6dc 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -236,7 +236,7 @@ void cht_DoCheat (player_t *player, int cheat) if (i == 4) { - level.flags ^= LEVEL_ALLMAP; + level.flags2 ^= LEVEL2_ALLMAP; } else if (player->mo != NULL && player->health >= 0) { diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 8d56ebda5b..2c192e91e2 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -69,6 +69,7 @@ #include "r_translate.h" #include "sbarinfo.h" #include "cmdlib.h" +#include "m_png.h" extern FILE *Logfile; @@ -118,6 +119,222 @@ struct FBehavior::ArrayInfo TArray FBehavior::StaticModules; + +//============================================================================ +// +// Global and world variables +// +//============================================================================ + +// ACS variables with world scope +SDWORD ACS_WorldVars[NUM_WORLDVARS]; +FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS]; + +// ACS variables with global scope +SDWORD ACS_GlobalVars[NUM_GLOBALVARS]; +FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS]; + + +//============================================================================ +// +// +// +//============================================================================ + +void P_ClearACSVars(bool alsoglobal) +{ + int i; + + memset (ACS_WorldVars, 0, sizeof(ACS_WorldVars)); + for (i = 0; i < NUM_WORLDVARS; ++i) + { + ACS_WorldArrays[i].Clear (); + } + if (alsoglobal) + { + memset (ACS_GlobalVars, 0, sizeof(ACS_GlobalVars)); + for (i = 0; i < NUM_GLOBALVARS; ++i) + { + ACS_GlobalArrays[i].Clear (); + } + } +} + +//============================================================================ +// +// +// +//============================================================================ + +static void WriteVars (FILE *file, SDWORD *vars, size_t count, DWORD id) +{ + size_t i, j; + + for (i = 0; i < count; ++i) + { + if (vars[i] != 0) + break; + } + if (i < count) + { + // Find last non-zero var. Anything beyond the last stored variable + // will be zeroed at load time. + for (j = count-1; j > i; --j) + { + if (vars[j] != 0) + break; + } + FPNGChunkArchive arc (file, id); + for (i = 0; i <= j; ++i) + { + DWORD var = vars[i]; + arc << var; + } + } +} + +//============================================================================ +// +// +// +//============================================================================ + +static void ReadVars (PNGHandle *png, SDWORD *vars, size_t count, DWORD id) +{ + size_t len = M_FindPNGChunk (png, id); + size_t used = 0; + + if (len != 0) + { + DWORD var; + size_t i; + FPNGChunkArchive arc (png->File->GetFile(), id, len); + used = len / 4; + + for (i = 0; i < used; ++i) + { + arc << var; + vars[i] = var; + } + png->File->ResetFilePtr(); + } + if (used < count) + { + memset (&vars[used], 0, (count-used)*4); + } +} + +//============================================================================ +// +// +// +//============================================================================ + +static void WriteArrayVars (FILE *file, FWorldGlobalArray *vars, unsigned int count, DWORD id) +{ + unsigned int i, j; + + // Find the first non-empty array. + for (i = 0; i < count; ++i) + { + if (vars[i].CountUsed() != 0) + break; + } + if (i < count) + { + // Find last non-empty array. Anything beyond the last stored array + // will be emptied at load time. + for (j = count-1; j > i; --j) + { + if (vars[j].CountUsed() != 0) + break; + } + FPNGChunkArchive arc (file, id); + arc.WriteCount (i); + arc.WriteCount (j); + for (; i <= j; ++i) + { + arc.WriteCount (vars[i].CountUsed()); + + FWorldGlobalArray::ConstIterator it(vars[i]); + const FWorldGlobalArray::Pair *pair; + + while (it.NextPair (pair)) + { + arc.WriteCount (pair->Key); + arc.WriteCount (pair->Value); + } + } + } +} + +//============================================================================ +// +// +// +//============================================================================ + +static void ReadArrayVars (PNGHandle *png, FWorldGlobalArray *vars, size_t count, DWORD id) +{ + size_t len = M_FindPNGChunk (png, id); + unsigned int i, k; + + for (i = 0; i < count; ++i) + { + vars[i].Clear (); + } + + if (len != 0) + { + DWORD max, size; + FPNGChunkArchive arc (png->File->GetFile(), id, len); + + i = arc.ReadCount (); + max = arc.ReadCount (); + + for (; i <= max; ++i) + { + size = arc.ReadCount (); + for (k = 0; k < size; ++k) + { + SDWORD key, val; + key = arc.ReadCount(); + val = arc.ReadCount(); + vars[i].Insert (key, val); + } + } + png->File->ResetFilePtr(); + } +} + +//============================================================================ +// +// +// +//============================================================================ + +void P_ReadACSVars(PNGHandle *png) +{ + ReadVars (png, ACS_WorldVars, NUM_WORLDVARS, MAKE_ID('w','v','A','r')); + ReadVars (png, ACS_GlobalVars, NUM_GLOBALVARS, MAKE_ID('g','v','A','r')); + ReadArrayVars (png, ACS_WorldArrays, NUM_WORLDVARS, MAKE_ID('w','a','R','r')); + ReadArrayVars (png, ACS_GlobalArrays, NUM_GLOBALVARS, MAKE_ID('g','a','R','r')); +} + +//============================================================================ +// +// +// +//============================================================================ + +void P_WriteACSVars(FILE *stdfile) +{ + WriteVars (stdfile, ACS_WorldVars, NUM_WORLDVARS, MAKE_ID('w','v','A','r')); + WriteVars (stdfile, ACS_GlobalVars, NUM_GLOBALVARS, MAKE_ID('g','v','A','r')); + WriteArrayVars (stdfile, ACS_WorldArrays, NUM_WORLDVARS, MAKE_ID('w','a','R','r')); + WriteArrayVars (stdfile, ACS_GlobalArrays, NUM_GLOBALVARS, MAKE_ID('g','a','R','r')); +} + //---- Inventory functions --------------------------------------// // diff --git a/src/p_acs.h b/src/p_acs.h index a92b0d0cfd..59ff1274e1 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -36,6 +36,7 @@ #define __P_ACS_H__ #include "dobject.h" +#include "dthinker.h" #include "doomtype.h" #define LOCAL_SIZE 20 @@ -44,6 +45,34 @@ class FFont; class FileReader; + +enum +{ + NUM_WORLDVARS = 256, + NUM_GLOBALVARS = 64 +}; + +struct InitIntToZero +{ + void Init(int &v) + { + v = 0; + } +}; +typedef TMap, InitIntToZero> FWorldGlobalArray; + +// ACS variables with world scope +extern SDWORD ACS_WorldVars[NUM_WORLDVARS]; +extern FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS]; + +// ACS variables with global scope +extern SDWORD ACS_GlobalVars[NUM_GLOBALVARS]; +extern FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS]; + +void P_ReadACSVars(PNGHandle *); +void P_WriteACSVars(FILE*); +void P_ClearACSVars(bool); + // The in-memory version struct ScriptPtr { diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index bcbb0b4214..45bb6ff469 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -848,7 +848,7 @@ static void DrawConversationMenu () } // [CW] Freeze the game depending on MAPINFO options. - if (ConversationPauseTic < gametic && !multiplayer && !(level.flags & LEVEL_CONV_SINGLE_UNFREEZE)) + if (ConversationPauseTic < gametic && !multiplayer && !(level.flags2 & LEVEL2_CONV_SINGLE_UNFREEZE)) { menuactive = MENU_On; } diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 6b18d4cce5..851634c3a8 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -2652,9 +2652,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_BossDeath) // Do generic special death actions first bool checked = false; - FSpecialAction *sa = level.info->specialactions; - while (sa) + for(unsigned i=0; ispecialactions.Size(); i++) { + FSpecialAction *sa = &level.info->specialactions[i]; + if (type == sa->Type || mytype == sa->Type) { if (!checked && !CheckBossDeath(self)) @@ -2666,7 +2667,6 @@ DEFINE_ACTION_FUNCTION(AActor, A_BossDeath) LineSpecials[sa->Action](NULL, self, false, sa->Args[0], sa->Args[1], sa->Args[2], sa->Args[3], sa->Args[4]); } - sa = sa->Next; } // [RH] These all depend on the presence of level flags now diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 47f80e63de..372318e5f7 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1296,8 +1296,8 @@ bool AActor::OkayToSwitchTarget (AActor *other) int infight; if (flags5 & MF5_NOINFIGHTING) infight=-1; - else if (level.flags & LEVEL_TOTALINFIGHTING) infight=1; - else if (level.flags & LEVEL_NOINFIGHTING) infight=-1; + else if (level.flags2 & LEVEL2_TOTALINFIGHTING) infight=1; + else if (level.flags2 & LEVEL2_NOINFIGHTING) infight=-1; else infight = infighting; if (infight < 0 && other->player == NULL && !IsHostile (other)) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 1120534355..a26e71be06 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -2489,7 +2489,7 @@ FUNC(LS_SetPlayerProperty) } else if (it->player - players == consoleplayer) { - level.flags |= LEVEL_ALLMAP; + level.flags2 |= LEVEL2_ALLMAP; } } else @@ -2504,7 +2504,7 @@ FUNC(LS_SetPlayerProperty) } else if (it->player - players == consoleplayer) { - level.flags &= ~LEVEL_ALLMAP; + level.flags2 &= ~LEVEL2_ALLMAP; } } } @@ -2525,7 +2525,7 @@ FUNC(LS_SetPlayerProperty) } else if (i == consoleplayer) { - level.flags |= LEVEL_ALLMAP; + level.flags2 |= LEVEL2_ALLMAP; } } else @@ -2540,7 +2540,7 @@ FUNC(LS_SetPlayerProperty) } else if (i == consoleplayer) { - level.flags &= ~LEVEL_ALLMAP; + level.flags2 &= ~LEVEL2_ALLMAP; } } } diff --git a/src/p_map.cpp b/src/p_map.cpp index 3307c3d6dc..3e93882b77 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -672,7 +672,7 @@ bool PIT_CheckLine (line_t *ld, const FBoundingBox &box, FCheckPosition &tm) // better than Strife's handling of rails, which lets you jump into rails // from either side. How long until somebody reports this as a bug and I'm // forced to say, "It's not a bug. It's a feature?" Ugh. - (!(level.flags & LEVEL_RAILINGHACK) || + (!(level.flags2 & LEVEL2_RAILINGHACK) || open.bottom == tm.thing->Sector->floorplane.ZatPoint (sx, sy))) { open.bottom += 32*FRACUNIT; @@ -870,8 +870,8 @@ bool PIT_CheckThing (AActor *thing, FCheckPosition &tm) if (!thing->player && !tm.thing->target->player) { int infight; - if (level.flags & LEVEL_TOTALINFIGHTING) infight=1; - else if (level.flags & LEVEL_NOINFIGHTING) infight=-1; + if (level.flags2 & LEVEL2_TOTALINFIGHTING) infight=1; + else if (level.flags2 & LEVEL2_NOINFIGHTING) infight=-1; else infight = infighting; if (infight < 0) @@ -1412,7 +1412,7 @@ static void CheckForPushSpecial (line_t *line, int side, AActor *mobj) } else if (mobj->flags2 & MF2_IMPACT) { - if ((level.flags & LEVEL_MISSILESACTIVATEIMPACT) || + if ((level.flags2 & LEVEL2_MISSILESACTIVATEIMPACT) || !(mobj->flags & MF_MISSILE) || (mobj->target == NULL)) { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 2b6c597989..0c67306f15 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1828,7 +1828,7 @@ void P_MonsterFallingDamage (AActor *mo) int damage; int mom; - if (!(level.flags&LEVEL_MONSTERFALLINGDAMAGE)) + if (!(level.flags2 & LEVEL2_MONSTERFALLINGDAMAGE)) return; if (mo->floorsector->Flags & SECF_NOFALLINGDAMAGE) return; @@ -2533,7 +2533,7 @@ void AActor::Tick () if (!(flags5 & MF5_NOTIMEFREEZE)) { //Added by MC: Freeze mode. - if (bglobal.freeze || level.flags & LEVEL_FROZEN) + if (bglobal.freeze || level.flags2 & LEVEL2_FROZEN) { return; } @@ -2572,7 +2572,7 @@ void AActor::Tick () } // Apply freeze mode. - if (( level.flags & LEVEL_FROZEN ) && ( player == NULL || !( player->cheats & CF_TIMEFREEZE ))) + if (( level.flags2 & LEVEL2_FROZEN ) && ( player == NULL || !( player->cheats & CF_TIMEFREEZE ))) { return; } @@ -3697,7 +3697,7 @@ APlayerPawn *P_SpawnPlayer (FMapThing *mthing, bool tempplayer) { // Give all cards in death match mode. p->mo->GiveDeathmatchInventory (); } - else if ((multiplayer || (level.flags & LEVEL_ALLOWRESPAWN)) && state == PST_REBORN && oldactor != NULL) + else if ((multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) && state == PST_REBORN && oldactor != NULL) { // Special inventory handling for respawning in coop p->mo->FilterCoopRespawnInventory (oldactor); } @@ -4007,7 +4007,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) } // don't spawn any monsters if -nomonsters - if (((level.flags & LEVEL_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) && info->flags3 & MF3_ISMONSTER ) + if (((level.flags2 & LEVEL2_NOMONSTERS) || (dmflags & DF_NO_MONSTERS)) && info->flags3 & MF3_ISMONSTER ) { return NULL; } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 112cd2f843..b4168e729d 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -466,7 +466,7 @@ void P_SerializeSounds (FArchive &arc) { if (!S_ChangeMusic (name, order)) if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid)) - S_ChangeMusic (level.music, level.musicorder); + S_ChangeMusic (level.Music, level.musicorder); } delete[] name; } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 6ae1739d1c..0b6e6d2478 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1561,7 +1561,7 @@ void P_SetLineID (line_t *ld) switch (ld->special) { case Line_SetIdentification: - if (!(level.flags & LEVEL_HEXENHACK)) + if (!(level.flags2 & LEVEL2_HEXENHACK)) { ld->id = ld->args[0] + 256 * ld->args[4]; ld->flags |= ld->args[1]<<16; @@ -1772,9 +1772,9 @@ void P_LoadLineDefs (MapData * map) P_AdjustLine (ld); P_SaveLineSpecial (ld); - if (level.flags & LEVEL_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; - if (level.flags & LEVEL_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; - if (level.flags & LEVEL_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; + if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; + if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; + if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; } delete[] mldf; } @@ -1850,9 +1850,9 @@ void P_LoadLineDefs2 (MapData * map) P_AdjustLine (ld); P_SetLineID(ld); P_SaveLineSpecial (ld); - if (level.flags & LEVEL_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; - if (level.flags & LEVEL_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; - if (level.flags & LEVEL_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; + if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; + if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; + if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; // convert the activation type ld->activation = 1 << GET_SPAC(ld->flags); @@ -3312,16 +3312,16 @@ void P_SetupLevel (char *lumpname, int position) { // We need translators only for Doom format maps. // If none has been defined in a map use the game's default. - P_LoadTranslator(level.info->translator != NULL? (const char *)level.info->translator : gameinfo.translator); + P_LoadTranslator(!level.info->Translator.IsEmpty()? level.info->Translator.GetChars() : gameinfo.translator); } if (!map->HasBehavior || map->isText) { // Doom format and UDMF text maps get strict monster activation unless the mapinfo // specifies differently. - if (!(level.flags & LEVEL_LAXACTIVATIONMAPINFO)) + if (!(level.flags2 & LEVEL2_LAXACTIVATIONMAPINFO)) { - level.flags &= ~LEVEL_LAXMONSTERACTIVATION; + level.flags2 &= ~LEVEL2_LAXMONSTERACTIVATION; } } @@ -3330,9 +3330,9 @@ void P_SetupLevel (char *lumpname, int position) // set compatibility flags if (gameinfo.gametype == GAME_Strife) { - level.flags |= LEVEL_RAILINGHACK; + level.flags2 |= LEVEL2_RAILINGHACK; } - level.flags |= LEVEL_DUMMYSWITCHES; + level.flags2 |= LEVEL2_DUMMYSWITCHES; } FBehavior::StaticLoadDefaultModules (); diff --git a/src/p_spec.cpp b/src/p_spec.cpp index f128e812f0..185719b285 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -239,7 +239,7 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType) } // some old WADs use this method to create walls that change the texture when shot. else if (activationType == SPAC_Impact && // only for shootable triggers - (level.flags & LEVEL_DUMMYSWITCHES) && // this is only a compatibility setting for an old hack! + (level.flags2 & LEVEL2_DUMMYSWITCHES) && // this is only a compatibility setting for an old hack! !repeat && // only non-repeatable triggers (specialGeneric_Crusher) && // not for Boom's generalized linedefs special && // not for lines without a special @@ -322,7 +322,7 @@ bool P_TestActivateLine (line_t *line, AActor *mo, int side, int activationType) // lax activation checks, monsters can also activate certain lines // even without them being marked as monster activate-able. This is // the default for non-Hexen maps in Hexen format. - if (!(level.flags & LEVEL_LAXMONSTERACTIVATION)) + if (!(level.flags2 & LEVEL2_LAXMONSTERACTIVATION)) { return false; } diff --git a/src/p_things.cpp b/src/p_things.cpp index b8273db570..0404ac37b9 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -68,7 +68,7 @@ bool P_Thing_Spawn (int tid, AActor *source, int type, angle_t angle, bool fog, kind = kind->ActorInfo->GetReplacement()->Class; if ((GetDefaultByType (kind)->flags3 & MF3_ISMONSTER) && - ((dmflags & DF_NO_MONSTERS) || (level.flags & LEVEL_NOMONSTERS))) + ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) @@ -204,7 +204,7 @@ bool P_Thing_Projectile (int tid, AActor *source, int type, const char *type_nam defflags3 = GetDefaultByType (kind)->flags3; if ((defflags3 & MF3_ISMONSTER) && - ((dmflags & DF_NO_MONSTERS) || (level.flags & LEVEL_NOMONSTERS))) + ((dmflags & DF_NO_MONSTERS) || (level.flags2 & LEVEL2_NOMONSTERS))) return false; if (tid == 0) diff --git a/src/p_tick.cpp b/src/p_tick.cpp index e072375625..b4a97dd841 100644 --- a/src/p_tick.cpp +++ b/src/p_tick.cpp @@ -57,7 +57,7 @@ bool P_CheckTickerPaused () && players[consoleplayer].viewz != 1 && wipegamestate == gamestate) { - S_PauseSound (!(level.flags & LEVEL_PAUSE_MUSIC_IN_MENUS)); + S_PauseSound (!(level.flags2 & LEVEL2_PAUSE_MUSIC_IN_MENUS)); return true; } return false; @@ -111,7 +111,7 @@ void P_Ticker (void) // Since things will be moving, it's okay to interpolate them in the renderer. r_NoInterpolate = false; - if (!bglobal.freeze && !(level.flags & LEVEL_FROZEN)) + if (!bglobal.freeze && !(level.flags2 & LEVEL2_FROZEN)) { P_ThinkParticles (); // [RH] make the particles think } @@ -126,7 +126,7 @@ void P_Ticker (void) DThinker::RunThinkers (); //if added by MC: Freeze mode. - if (!bglobal.freeze && !(level.flags & LEVEL_FROZEN)) + if (!bglobal.freeze && !(level.flags2 & LEVEL2_FROZEN)) { P_UpdateSpecials (); P_RunEffects (); // [RH] Run particle effects diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 94bfdc77f3..6bd5b26c92 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -419,9 +419,9 @@ struct UDMFParser ld->Alpha = FRACUNIT; ld->id = -1; ld->sidenum[0] = ld->sidenum[1] = NO_SIDE; - if (level.flags & LEVEL_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; - if (level.flags & LEVEL_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; - if (level.flags & LEVEL_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; + if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; + if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; + if (level.flags2 & LEVEL2_CHECKSWITCHRANGE) ld->flags |= ML_CHECKSWITCHRANGE; sc.MustGetToken('{'); while (!sc.CheckToken('}')) @@ -1124,19 +1124,19 @@ struct UDMFParser case NAME_Doom: namespace_bits = Dm; P_LoadTranslator("xlat/doom_base.txt"); - level.flags |= LEVEL_DUMMYSWITCHES; + level.flags2 |= LEVEL2_DUMMYSWITCHES; floordrop = true; break; case NAME_Heretic: namespace_bits = Ht; P_LoadTranslator("xlat/heretic_base.txt"); - level.flags |= LEVEL_DUMMYSWITCHES; + level.flags2 |= LEVEL2_DUMMYSWITCHES; floordrop = true; break; case NAME_Strife: namespace_bits = St; P_LoadTranslator("xlat/strife_base.txt"); - level.flags |= LEVEL_DUMMYSWITCHES|LEVEL_RAILINGHACK; + level.flags2 |= LEVEL2_DUMMYSWITCHES|LEVEL2_RAILINGHACK; floordrop = true; break; default: diff --git a/src/p_user.cpp b/src/p_user.cpp index 449fc76d23..d1ef70fc91 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1148,7 +1148,7 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor) } } } - if (!multiplayer && (level.flags & LEVEL_DEATHSLIDESHOW)) + if (!multiplayer && (level.flags2 & LEVEL2_DEATHSLIDESHOW)) { F_StartSlideshow (); } @@ -1876,7 +1876,7 @@ void P_DeathThink (player_t *player) if (level.time >= player->respawn_time || ((player->cmd.ucmd.buttons & BT_USE) && !player->isbot)) { player->cls = NULL; // Force a new class if the player is using a random class - player->playerstate = (multiplayer || (level.flags & LEVEL_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER; + player->playerstate = (multiplayer || (level.flags2 & LEVEL2_ALLOWRESPAWN)) ? PST_REBORN : PST_ENTER; if (player->mo->special1 > 2) { player->mo->special1 = 0; diff --git a/src/r_segs.cpp b/src/r_segs.cpp index 3087e0c96c..673a585a44 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -1441,7 +1441,7 @@ int side_t::GetLightLevel (bool foggy, int baselight) const { if (!(Flags & WALLF_NOFAKECONTRAST)) { - if (((level.flags & LEVEL_SMOOTHLIGHTING) || (Flags & WALLF_SMOOTHLIGHTING) || r_smoothlighting) && + if (((level.flags2 & LEVEL2_SMOOTHLIGHTING) || (Flags & WALLF_SMOOTHLIGHTING) || r_smoothlighting) && lines[linenum].dx != 0) { baselight += int // OMG LEE KILLOUGH LIVES! :/ diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index 4117930b8e..0736c80c52 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -1071,9 +1071,9 @@ static void S_AddSNDINFO (int lump) mysnprintf (temp, countof(temp), "MAP%02d", sc.Number); info = FindLevelInfo (temp); sc.MustGetString (); - if (info->mapname[0] && (!(info->flags & LEVEL_MUSICDEFINED))) + if (info->mapname[0] && (!(info->flags2 & LEVEL2_MUSICDEFINED))) { - ReplaceString (&info->music, sc.String); + info->Music = sc.String; } } break; diff --git a/src/s_sound.cpp b/src/s_sound.cpp index a978e51634..effa092522 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -370,26 +370,18 @@ void S_Start () // Check for local sound definitions. Only reload if they differ // from the previous ones. - const char *LocalSndInfo; - const char *LocalSndSeq; + FString LocalSndInfo; + FString LocalSndSeq; // To be certain better check whether level is valid! - if (level.info && level.info->soundinfo) + if (level.info) { - LocalSndInfo = level.info->soundinfo; - } - else - { - LocalSndInfo = ""; + LocalSndInfo = level.info->SoundInfo; } - if (level.info && level.info->sndseq) + if (level.info) { - LocalSndSeq = level.info->sndseq; - } - else - { - LocalSndSeq = ""; + LocalSndSeq = level.info->SndSeq; } bool parse_ss = false; @@ -420,11 +412,11 @@ void S_Start () { parse_ss = true; } + if (parse_ss) { S_ParseSndSeq(*LocalSndSeq? Wads.CheckNumForFullName(LocalSndSeq, true) : -1); } - else LastLocalSndInfo = LocalSndInfo; LastLocalSndSeq = LocalSndSeq; @@ -442,7 +434,7 @@ void S_Start () if (!savegamerestore) { if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid)) - S_ChangeMusic (level.music, level.musicorder); + S_ChangeMusic (level.Music, level.musicorder); } } @@ -2170,7 +2162,7 @@ bool S_ChangeMusic (const char *musicname, int order, bool looping, bool force) { if (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL) { - musicname = level.music; + musicname = level.Music; order = level.musicorder; } else @@ -2433,9 +2425,9 @@ CCMD (idmus) if ( (info = FindLevelInfo (map)) ) { - if (info->music) + if (info->Music.IsNotEmpty()) { - S_ChangeMusic (info->music, info->musicorder); + S_ChangeMusic (info->Music, info->musicorder); Printf ("%s\n", GStrings("STSTR_MUS")); } } diff --git a/src/sc_man.cpp b/src/sc_man.cpp index 9de966fd13..c474439b6c 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -22,6 +22,7 @@ #include "m_misc.h" #include "templates.h" #include "doomstat.h" +#include "v_text.h" // MACROS ------------------------------------------------------------------ @@ -752,16 +753,21 @@ void FScanner::UnGet () // //========================================================================== -int FScanner::MatchString (const char **strings) +int FScanner::MatchString (const char **strings, size_t stride) { int i; + assert(stride % sizeof(const char*) == 0); + + stride /= sizeof(const char*); + for (i = 0; *strings != NULL; i++) { - if (Compare (*strings++)) + if (Compare (*strings)) { return i; } + strings += stride; } return -1; } @@ -772,11 +778,11 @@ int FScanner::MatchString (const char **strings) // //========================================================================== -int FScanner::MustMatchString (const char **strings) +int FScanner::MustMatchString (const char **strings, size_t stride) { int i; - i = MatchString (strings); + i = MatchString (strings, stride); if (i == -1) { ScriptError (NULL); @@ -1007,7 +1013,7 @@ void STACK_ARGS FScanner::ScriptMessage (const char *message, ...) va_end (arglist); } - Printf ("Script error, \"%s\" line %d:\n%s\n", ScriptName.GetChars(), + Printf (TEXTCOLOR_RED"Script error, \"%s\" line %d:\n%s\n", ScriptName.GetChars(), AlreadyGot? AlreadyGotLine : Line, composed.GetChars()); } diff --git a/src/sc_man.h b/src/sc_man.h index c74fca9428..dcfb9254eb 100644 --- a/src/sc_man.h +++ b/src/sc_man.h @@ -54,8 +54,8 @@ public: void UnGet(); bool Compare(const char *text); - int MatchString(const char **strings); - int MustMatchString(const char **strings); + int MatchString(const char **strings, size_t stride = sizeof(char*)); + int MustMatchString(const char **strings, size_t stride = sizeof(char*)); int GetMessageLine(); void ScriptError(const char *message, ...); diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index bf3ed803b4..540d6554eb 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -228,7 +228,6 @@ void I_Quit (void) if (demorecording) G_CheckDemoStatus(); - G_ClearSnapshots (); } diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 5eccd87d8e..6475495085 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -230,7 +230,7 @@ static FTexture* p; // Player graphic static FTexture* lnames[2]; // Name graphics of each level (centered) // [RH] Info to dynamically generate the level name graphics -static const char *lnametexts[2]; +static FString lnametexts[2]; static FTexture *background; @@ -297,11 +297,11 @@ void WI_LoadBackground(bool isenterpic) if (isenterpic) { level_info_t * li = FindLevelInfo(wbs->next); - if (li != NULL) lumpname = li->enterpic; + if (li != NULL) lumpname = li->EnterPic; } else { - lumpname = level.info->exitpic; + lumpname = level.info->ExitPic; } // Try to get a default if nothing specified @@ -329,7 +329,7 @@ void WI_LoadBackground(bool isenterpic) // If going from E1-E3 to E4 the default should be used, not the exit pic. // Not if the exit pic is user defined! - if (level.info->exitpic != NULL && level.info->exitpic[0]!=0) return; + if (level.info->ExitPic.IsNotEmpty()) return; // E1-E3 need special treatment when playing Doom 1. if (gamemode!=commercial) @@ -712,6 +712,7 @@ int WI_DrawName(int y, const char *levelname) lumph = BigFont->GetHeight() * CleanYfac; p = levelname; + if (!p) return 0; l = strlen(p); if (!l) return 0; @@ -1885,8 +1886,8 @@ void WI_Ticker(void) if (bcnt == 1) { // intermission music - use the defaults if none specified - if (level.info->intermusic != NULL) - S_ChangeMusic(level.info->intermusic, level.info->intermusicorder); + if (level.info->InterMusic.IsNotEmpty()) + S_ChangeMusic(level.info->InterMusic, level.info->intermusicorder); else if (gameinfo.gametype == GAME_Heretic) S_ChangeMusic ("mus_intr"); else if (gameinfo.gametype == GAME_Hexen) @@ -1966,11 +1967,12 @@ void WI_loadData(void) bstar = star; } - // Use the local level structure which can be overridden by hubs if they eventually get names! - lnametexts[0] = level.level_name; + // Use the local level structure which can be overridden by hubs + lnametexts[0] = level.LevelName; level_info_t *li = FindLevelInfo(wbs->next); - lnametexts[1] = li ? G_MaybeLookupLevelName(li) : NULL; + if (li) lnametexts[1] = li->LookupLevelName(); + else lnametexts[1] = ""; WI_LoadBackground(false); } diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 4318d95dd8..9da20744c4 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -449,7 +449,6 @@ void I_Quit (void) if (demorecording) G_CheckDemoStatus(); - G_ClearSnapshots (); } diff --git a/src/zstring.cpp b/src/zstring.cpp index 9f39264389..d126d607b3 100644 --- a/src/zstring.cpp +++ b/src/zstring.cpp @@ -1110,6 +1110,7 @@ void FString::ReallocBuffer (size_t newlen) #include static HANDLE StringHeap; +const SIZE_T STRING_HEAP_SIZE = 64*1024; #endif FStringData *FStringData::Alloc (size_t strlen) @@ -1120,7 +1121,7 @@ FStringData *FStringData::Alloc (size_t strlen) #ifdef _WIN32 if (StringHeap == NULL) { - StringHeap = HeapCreate (0, 64*1024, 0); + StringHeap = HeapCreate (0, STRING_HEAP_SIZE, 0); if (StringHeap == NULL) { throw std::bad_alloc(); diff --git a/zdoom.vcproj b/zdoom.vcproj index d6aced38eb..ca4a46e42a 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -624,6 +624,14 @@ RelativePath=".\src\g_level.cpp" > + + + +