diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 92bdaf8e..98f102e1 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,114 @@ +February 5, 2009 (Changes by Graf Zahl) +- Made improvements so that the FOptionalMapinfoData class is easier to use. + +February 3, 2009 +- Moved the MF_INCHASE recursion check from A_Look() into A_Chase(). This + lets A_Look() always put the actor into its see state. This problem could + be heard by an Archvile's resurrectee playing its see sound but failing to + enter its see state because it was called from A_Chase(). +- Fixed: SBARINFO used different rounding modes for the background and + foreground of the DrawBar command. +- Bumped MINSAVEVER to coincide with the new MAPINFO merge. +- Added a fflush() call after the logfile write in I_FatalError so that the + error text is visible in the file while the error dialog is displayed. + +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. + +February 2, 2009 (Changes by Graf Zahl) +- Fixed: The damage factor for 'normal' damage is supposed to be applied to + all damage types that don't have a specific damage factor. + +January 31, 2009 +- Changed FMOD init() to allocate some virtual channels. +- Fixed clipping in D3DFB::DrawTextureV() for good by using a scissor test. + +January 30, 2009 +- Fixed: D3DFB::DrawTextureV() did not properly adjust the texture coordinate + for lclip and rclip. +- Added weapdrop ccmd. +- Centered the compatibility mode option in the comptibility options menu. +- Added button mappings for 8 mouse buttons on SDL. It works with my system, + but Linux being Linux, there are no guarantees that it's appropriate for + other systems. +- Fixed: SDL input code did not generate GUI events for the mousewheel, so it + could not be used to scroll the console buffer. + +January 30, 2009 (Changes by Graf Zahl) +- Added Blzut3's statusbar maintenance patch. + +January 29, 2009 (Changes by Graf Zahl) +- fixed sound origin of the Mage Wand's missile. + +January 28, 2009 (Changes by Graf Zahl) +- Added APROP_Dropped actor property. +- Fixed: The compatmode CVAR needs CVAR_NOINITCALL so that the compatibility + flags don't get reset each start. +- Fixed: compatmode Doom(strict) was missing COMPAT_CROSSDROPOFF + +January 27, 2009 +- More GCC warning removal, the most egregious of which was the security + vulnerability "format not a string literal and no format arguments". +- Changed the CMake script to search for fmod libraries by full name instead + of assuming a symbolic link has been placed for the latest version. It can + also find a non-installed copy of FMOD if it is placed local to the ZDoom + source tree. + +January 26, 2009 +- Fixed: Some OPL state needs to be restored before calculating rhythm. Also, + since only the rhythm section uses the RNG, it doesn't need to be advanced + for the normal voice processing. + +January 25, 2009 (Changes by Graf Zahl) +- fixed some issues with P_FindFloorCeiling's coordinate processing. +- added a new compatmode CVAR which allows setting some generic compatibility + flag combinations: + Doom: sets the options needed to make most Doom.exe compatible map play without + errors. + Doom (strict): Same as above but sets a few more options. Please note that this + does not mean full Doom.exe behavior emulation. + Boom: Sets all options that consider differences between ZDoom and Boom. + ZDoom 2.0.63: Sets only the COMPATF_SOUNDTARGET option which is needed for + many older ZDoom maps. +- added new COMPAT_CROSSDROPOFF option to block monsters from being pushed over + dropoffs by damage kickback. +- fixed: AFastProjectile::Tick must call Effect only 8 times per tic, regardless + of the amount of steps taken. +- fixed: momentum checks in AFastProjectile did not use absolute values. + +January 24, 2009 +- Restored the rhythm section to fmopl.cpp and made some slight updates from + version 0.72 of MAME's fmopl.c. Also refactored CalcVoice so that the + original MAME structure is more visible. +- Removed the SoundChans bitfield from AActor, since it seems there are race + conditions I don't fully understand where it simply doesn't work. +- Removed BaseTime initialization from sdl/i_system.cpp as per Chris's + recommendation. + +January 24, 2009 (Changes by Graf Zahl) +- Fixed: The sight checking code didn't initialize the myseethrough variable. +- Fixed: With COMPAT_TRACE switched on linedef actions on lines having + the same sector on both sides were not triggered. + +January 23, 2009 +- Set packet server as the default for four or more player games, and also the + default for three player games where the other players are not in the same + private IP range. + January 18, 2009 (Changes by Graf Zahl) - Added a CopyInfo function to FTexture that contains all code required to clone a texture. Used for creating warping textures. diff --git a/gzdoom.vcproj b/gzdoom.vcproj index f285653d..cd15b678 100644 --- a/gzdoom.vcproj +++ b/gzdoom.vcproj @@ -625,6 +625,14 @@ RelativePath=".\src\g_level.cpp" > + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 270cd969..d9f34f9c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -455,6 +455,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 36c10d63..541d3a5d 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 01069c51..b873602e 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 666f5b9d..5bdfa3c6 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 0e7c2e5d..84903eee 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/b_bot.cpp b/src/b_bot.cpp index be43f2b1..b03c067f 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -55,7 +55,7 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam) if (bot) { bot->inuse = false; - bot->lastteam = keepTeam ? players[i].userinfo.team : TEAM_None; + bot->lastteam = keepTeam ? players[i].userinfo.team : TEAM_NONE; } players[i].~player_t(); ::new(&players[i]) player_t; diff --git a/src/b_game.cpp b/src/b_game.cpp index 9a6b618f..f517f27e 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -332,7 +332,7 @@ bool FCajunMaster::SpawnBot (const char *name, int color) { strcat (concat, colors[bot_next_color]); } - if (TEAMINFO_IsValidTeam (thebot->lastteam)) + if (TeamLibrary.IsValidTeam (thebot->lastteam)) { // Keep the bot on the same team when switching levels mysnprintf (concat + strlen(concat), countof(concat) - strlen(concat), "\\team\\%d\n", thebot->lastteam); @@ -374,7 +374,7 @@ void FCajunMaster::DoAddBot (int bnum, char *info) botingame[bnum] = true; if (teamplay) - Printf ("%s joined the %s team\n", players[bnum].userinfo.netname, teams[players[bnum].userinfo.team].name.GetChars ()); + Printf ("%s joined the %s team\n", players[bnum].userinfo.netname,Teams[players[bnum].userinfo.team].GetName ()); else Printf ("%s joined the game\n", players[bnum].userinfo.netname); @@ -592,17 +592,17 @@ bool FCajunMaster::LoadBots () if (IsNum (sc.String)) { teamnum = atoi (sc.String); - if (!TEAMINFO_IsValidTeam (teamnum)) + if (!TeamLibrary.IsValidTeam (teamnum)) { - teamnum = TEAM_None; + teamnum = TEAM_NONE; } } else { - teamnum = TEAM_None; - for (int i = 0; i < int(teams.Size()); ++i) + teamnum = TEAM_NONE; + for (unsigned int i = 0; i < Teams.Size(); ++i) { - if (stricmp (teams[i].name, sc.String) == 0) + if (stricmp (Teams[i].GetName (), sc.String) == 0) { teamnum = i; break; @@ -638,7 +638,7 @@ bool FCajunMaster::LoadBots () appendinfo (newinfo->info, "255"); } newinfo->next = bglobal.botinfo; - newinfo->lastteam = TEAM_None; + newinfo->lastteam = TEAM_NONE; bglobal.botinfo = newinfo; bglobal.loaded_bots++; } diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index e4197ce2..a65541de 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -482,7 +482,7 @@ CCMD (error) if (argv.argc() > 1) { char *textcopy = copystring (argv[1]); - I_Error (textcopy); + I_Error ("%s", textcopy); } else { @@ -495,7 +495,7 @@ CCMD (error_fatal) if (argv.argc() > 1) { char *textcopy = copystring (argv[1]); - I_FatalError (textcopy); + I_FatalError ("%s", textcopy); } else { @@ -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 76d13574..d6ec09c0 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/d_dehacked.cpp b/src/d_dehacked.cpp index 6977179d..4194055a 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2585,7 +2585,7 @@ void FinishDehPatch () AActor *defaults2 = GetDefaultByType (subclass); memcpy (defaults2, defaults1, sizeof(AActor)); - // Make a copy the replaced class's state labels + // Make a copy of the replaced class's state labels FStateDefinitions statedef; statedef.MakeStateDefines(type); @@ -2598,8 +2598,16 @@ void FinishDehPatch () // Use the DECORATE replacement feature to redirect all spawns // of the original class to the new one. + FActorInfo *old_replacement = type->ActorInfo->Replacement; + type->ActorInfo->Replacement = subclass->ActorInfo; subclass->ActorInfo->Replacee = type->ActorInfo; + // If this actor was already replaced by another actor, copy that + // replacement over to this item. + if (old_replacement != NULL) + { + subclass->ActorInfo->Replacement = old_replacement; + } DPrintf ("%s replaces %s\n", subclass->TypeName.GetChars(), type->TypeName.GetChars()); } diff --git a/src/d_main.cpp b/src/d_main.cpp index cb9db695..8512252e 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -523,7 +523,7 @@ CUSTOM_CVAR (Int, compatflags, 0, CVAR_ARCHIVE|CVAR_SERVERINFO) i_compatflags = GetCompatibility(self); } -CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_SERVERINFO) +CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_SERVERINFO|CVAR_NOINITCALL) { int v; @@ -542,7 +542,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_SERVERINFO) case 2: // same as 1 but stricter (NO_PASSMOBJ and INVISIBILITY are also set) v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT| COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_NO_PASSMOBJ|COMPATF_LIMITPAIN| - COMPATF_DEHHEALTH|COMPATF_INVISIBILITY; + COMPATF_DEHHEALTH|COMPATF_INVISIBILITY|COMPATF_CROSSDROPOFF; break; case 3: @@ -2447,7 +2447,7 @@ void D_DoomMain (void) } if (devparm) { - Printf (GStrings("D_DEVSTR")); + Printf ("%s", GStrings("D_DEVSTR")); } #ifndef unix @@ -2456,7 +2456,7 @@ void D_DoomMain (void) // the user's home directory. if (Args->CheckParm("-cdrom")) { - Printf (GStrings("D_CDROM")); + Printf ("%s", GStrings("D_CDROM")); mkdir (CDROM_DIR, 0); } #endif @@ -2521,9 +2521,9 @@ void D_DoomMain (void) // Now that all textues have been loaded the crosshair can be initialized. crosshair.Callback (); - // [CW] Parse any TEAMINFO lumps - Printf ("TEAMINFO_Init: Load team definitions.\n"); - TEAMINFO_Init (); + // [CW] Parse any TEAMINFO lumps. + Printf ("ParseTeamInfo: Load team definitions.\n"); + TeamLibrary.ParseTeamInfo (); FActorInfo::StaticInit (); diff --git a/src/d_net.cpp b/src/d_net.cpp index d480ab61..ed98a1a6 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -1963,7 +1963,7 @@ void Net_DoCommand (int type, BYTE **stream, int player) BYTE who = ReadByte (stream); s = ReadString (stream); - if (((who & 1) == 0) || players[player].userinfo.team == TEAM_None) + if (((who & 1) == 0) || players[player].userinfo.team == TEAM_NONE) { // Said to everyone if (who & 2) { @@ -1997,7 +1997,7 @@ void Net_DoCommand (int type, BYTE **stream, int player) case DEM_PRINT: s = ReadString (stream); - Printf (s); + Printf ("%s", s); break; case DEM_CENTERPRINT: diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index cb097d59..5ed0a9ac 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -65,7 +65,7 @@ CVAR (Float, autoaim, 5000.f, CVAR_USERINFO | CVAR_ARCHIVE); CVAR (String, name, "Player", CVAR_USERINFO | CVAR_ARCHIVE); CVAR (Color, color, 0x40cf00, CVAR_USERINFO | CVAR_ARCHIVE); CVAR (String, skin, "base", CVAR_USERINFO | CVAR_ARCHIVE); -CVAR (Int, team, TEAM_None, CVAR_USERINFO | CVAR_ARCHIVE); +CVAR (Int, team, TEAM_NONE, CVAR_USERINFO | CVAR_ARCHIVE); CVAR (String, gender, "male", CVAR_USERINFO | CVAR_ARCHIVE); CVAR (Bool, neverswitchonpickup, false, CVAR_USERINFO | CVAR_ARCHIVE); CVAR (Float, movebob, 0.25f, CVAR_USERINFO | CVAR_ARCHIVE); @@ -191,13 +191,13 @@ void D_GetPlayerColor (int player, float *h, float *s, float *v) RGBtoHSV (RPART(color)/255.f, GPART(color)/255.f, BPART(color)/255.f, h, s, v); - if (teamplay && TEAMINFO_IsValidTeam(info->team)) + if (teamplay && TeamLibrary.IsValidTeam(info->team) && !Teams[info->team].GetAllowCustomPlayerColor ()) { // In team play, force the player to use the team's hue // and adjust the saturation and value so that the team // hue is visible in the final color. float ts, tv; - int tcolor = teams[info->team].playercolor; + int tcolor = Teams[info->team].GetPlayerColor (); RGBtoHSV (RPART(tcolor)/255.f, GPART(tcolor)/255.f, BPART(tcolor)/255.f, h, &ts, &tv); @@ -225,10 +225,10 @@ void D_PickRandomTeam (int player) int D_PickRandomTeam () { - for (int i = 0; i < (signed)teams.Size (); i++) + for (unsigned int i = 0; i < Teams.Size (); i++) { - teams[i].present = 0; - teams[i].ties = 0; + Teams[i].m_iPresent = 0; + Teams[i].m_iTies = 0; } int numTeams = 0; @@ -238,9 +238,9 @@ int D_PickRandomTeam () { if (playeringame[i]) { - if (TEAMINFO_IsValidTeam (players[i].userinfo.team)) + if (TeamLibrary.IsValidTeam (players[i].userinfo.team)) { - if (teams[players[i].userinfo.team].present++ == 0) + if (Teams[players[i].userinfo.team].m_iPresent++ == 0) { numTeams++; } @@ -252,36 +252,37 @@ int D_PickRandomTeam () { do { - team = pr_pickteam() % teams.Size (); - } while (teams[team].present != 0); + team = pr_pickteam() % Teams.Size (); + } while (Teams[team].m_iPresent != 0); } else { - int lowest = INT_MAX, lowestTie = 0, i; + int lowest = INT_MAX, lowestTie = 0; + unsigned int i; - for (i = 0; i < (signed)teams.Size (); ++i) + for (i = 0; i < Teams.Size (); ++i) { - if (teams[i].present > 0) + if (Teams[i].m_iPresent > 0) { - if (teams[i].present < lowest) + if (Teams[i].m_iPresent < lowest) { - lowest = teams[i].present; + lowest = Teams[i].m_iPresent; lowestTie = 0; - teams[0].ties = i; + Teams[0].m_iTies = i; } - else if (teams[i].present == lowest) + else if (Teams[i].m_iPresent == lowest) { - teams[++lowestTie].ties = i; + Teams[++lowestTie].m_iTies = i; } } } if (lowestTie == 0) { - team = teams[0].ties; + team = Teams[0].m_iTies; } else { - team = teams[pr_pickteam() % (lowestTie+1)].ties; + team = Teams[pr_pickteam() % (lowestTie+1)].m_iTies; } } @@ -292,7 +293,7 @@ static void UpdateTeam (int pnum, int team, bool update) { userinfo_t *info = &players[pnum].userinfo; - if ((dmflags2 & DF2_NO_TEAM_SWITCH) && (alwaysapplydmflags || deathmatch) && TEAMINFO_IsValidTeam (info->team)) + if ((dmflags2 & DF2_NO_TEAM_SWITCH) && (alwaysapplydmflags || deathmatch) && TeamLibrary.IsValidTeam (info->team)) { Printf ("Team changing has been disabled!\n"); return; @@ -300,21 +301,21 @@ static void UpdateTeam (int pnum, int team, bool update) int oldteam; - if (!TEAMINFO_IsValidTeam (team)) + if (!TeamLibrary.IsValidTeam (team)) { - team = TEAM_None; + team = TEAM_NONE; } oldteam = info->team; info->team = team; - if (teamplay && !TEAMINFO_IsValidTeam (info->team)) + if (teamplay && !TeamLibrary.IsValidTeam (info->team)) { // Force players onto teams in teamplay mode info->team = D_PickRandomTeam (); } if (update && oldteam != info->team) { - if (TEAMINFO_IsValidTeam (info->team)) - Printf ("%s joined the %s team\n", info->netname, teams[info->team].name.GetChars()); + if (TeamLibrary.IsValidTeam (info->team)) + Printf ("%s joined the %s team\n", info->netname, Teams[info->team].GetName ()); else Printf ("%s is now a loner\n", info->netname); } @@ -324,13 +325,13 @@ static void UpdateTeam (int pnum, int team, bool update) { StatusBar->AttachToPlayer (&players[pnum]); } - if (!TEAMINFO_IsValidTeam (info->team)) - info->team = TEAM_None; + if (!TeamLibrary.IsValidTeam (info->team)) + info->team = TEAM_NONE; } int D_GetFragCount (player_t *player) { - if (!teamplay || !TEAMINFO_IsValidTeam (player->userinfo.team)) + if (!teamplay || !TeamLibrary.IsValidTeam (player->userinfo.team)) { return player->fragcount; } @@ -360,7 +361,7 @@ void D_SetupUserInfo () memset (&players[i].userinfo, 0, sizeof(userinfo_t)); strncpy (coninfo->netname, name, MAXPLAYERNAME); - if (teamplay && !TEAMINFO_IsValidTeam (team)) + if (teamplay && !TeamLibrary.IsValidTeam (team)) { coninfo->team = D_PickRandomTeam (); } @@ -831,7 +832,7 @@ CCMD (playerinfo) int i = atoi (argv[1]); userinfo_t *ui = &players[i].userinfo; Printf ("Name: %s\n", ui->netname); - Printf ("Team: %s (%d)\n", ui->team == TEAM_None ? "None" : teams[ui->team].name.GetChars(), ui->team); + Printf ("Team: %s (%d)\n", ui->team == TEAM_NONE ? "None" : Teams[ui->team].GetName (), ui->team); Printf ("Aimdist: %d\n", ui->aimdist); Printf ("Color: %06x\n", ui->color); Printf ("Skin: %s (%d)\n", skins[ui->skin].name, ui->skin); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index edb32fbb..481df513 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -253,7 +253,7 @@ PClass *PClass::CreateDerivedClass (FName name, unsigned int size) const PClass *existclass = FindClass(name); // This is a placeholder so fill it in - if (existclass != NULL && existclass->Size == -1) + if (existclass != NULL && existclass->Size == (unsigned)-1) { type = const_cast(existclass); if (!IsDescendantOf(type->ParentClass)) diff --git a/src/doomstat.cpp b/src/doomstat.cpp index 0fe988a9..6fe88955 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 bbdd7d43..11d06b55 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 701a756e..8673e3b2 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/fragglescript/t_load.cpp b/src/fragglescript/t_load.cpp index f53ee762..4a326a1f 100644 --- a/src/fragglescript/t_load.cpp +++ b/src/fragglescript/t_load.cpp @@ -83,39 +83,22 @@ struct FFsOptions : public FOptionalMapinfoData bool nocheckposition; }; -static void ParseFunc(FScanner &sc, level_info_t *info) +DEFINE_MAP_OPTION(fs_nocheckposition, false) { - FName id = "fragglescript"; - FOptionalMapinfoData *dat = info->opdata; + FFsOptions *opt = info->GetOptData("fragglescript"); - while (dat && dat->identifier != id) dat = dat->Next; - if (!dat) dat = new FFsOptions; - dat->Next = info->opdata; - info->opdata = dat; - - FFsOptions *opt = static_cast(dat); - - while (!sc.CheckString("}")) + parse.ParseAssign(); + if (parse.CheckAssign()) { - sc.MustGetString(); - if (sc.Compare("nocheckposition")) - { - if (!sc.CheckNumber()) opt->nocheckposition = false; - else opt->nocheckposition = !!sc.Number; - } - else - { - sc.ScriptError(NULL); - } + parse.sc.MustGetNumber(); + opt->nocheckposition = !!parse.sc.Number; + } + else + { + opt->nocheckposition = true; } } -void AddFsMapinfoParser() -{ - AddOptionalMapinfoParser("fragglescript", ParseFunc); -} - - //----------------------------------------------------------------------------- // // Process the lump to strip all unneeded information from it @@ -176,8 +159,7 @@ void FScriptLoader::ParseInfoCmd(char *line, FString &scriptsrc) while (*beg<=' ') beg++; char * comm = strstr(beg, "//"); if (comm) *comm=0; - strncpy(level.level_name, beg, 63); - level.level_name[63]=0; + level.LevelName = beg; } else if (sc.Compare("partime")) { @@ -193,7 +175,7 @@ void FScriptLoader::ParseInfoCmd(char *line, FString &scriptsrc) sc.MustGetString(); if (!FS_ChangeMusic(sc.String)) { - S_ChangeMusic(level.music, level.musicorder); + S_ChangeMusic(level.Music, level.musicorder); } } else if (sc.Compare("skyname")) @@ -211,7 +193,7 @@ void FScriptLoader::ParseInfoCmd(char *line, FString &scriptsrc) { sc.MustGetStringName("="); sc.MustGetString(); - ReplaceString(&level.info->exitpic, sc.String); + level.info->ExitPic = sc.String; } else if (sc.Compare("gravity")) { @@ -319,13 +301,9 @@ bool FScriptLoader::ParseInfo(MapData * map) if (drownflag==-1) drownflag = ((level.flags&LEVEL_HEXENFORMAT) || fsglobal); if (!drownflag) level.airsupply=0; // Legacy doesn't to water damage. - FName id = "fragglescript"; - FOptionalMapinfoData *dat = level.info->opdata; - - while (dat && dat->identifier != id) dat = dat->Next; - if (dat != NULL) + FFsOptions *opt = level.info->GetOptData("fragglescript", false); + if (opt != NULL) { - FFsOptions *opt = static_cast(dat); DFraggleThinker::ActiveThinker->nocheckposition = opt->nocheckposition; } } @@ -356,7 +334,7 @@ void T_LoadScripts(MapData *map) // This code then then swaps 270 and 272 - but only if this is either Doom or Heretic and // the default translator is being used. // Custom translators will not be patched. - if ((gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Heretic) && level.info->translator == NULL && + if ((gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Heretic) && level.info->Translator.IsEmpty() && !((level.flags&LEVEL_HEXENFORMAT)) && SimpleLineTranslations[272 - 2*HasScripts].special == FS_Execute) { FLineTrans t = SimpleLineTranslations[270]; diff --git a/src/g_game.cpp b/src/g_game.cpp index 7ca1b242..491852ff 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 @@ -386,6 +387,11 @@ CCMD (invdrop) if (players[consoleplayer].mo) SendItemDrop = players[consoleplayer].mo->InvSel; } +CCMD (weapdrop) +{ + SendItemDrop = players[consoleplayer].ReadyWeapon; +} + CCMD (drop) { if (argv.argc() > 1 && who != NULL) @@ -1124,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) { @@ -1424,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 @@ -1574,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 () { @@ -1815,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) @@ -1980,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'; @@ -2064,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) { @@ -2506,11 +2384,11 @@ void G_DoPlayDemo (void) demo_p = demobuffer = NULL; if (singledemo) { - I_Error (eek); + I_Error ("%s", eek); } else { - Printf (PRINT_BOLD, eek); + Printf (PRINT_BOLD, "%s", eek); gameaction = ga_nothing; } } diff --git a/src/g_hexen/a_hexenspecialdecs.cpp b/src/g_hexen/a_hexenspecialdecs.cpp index c8c64b20..d9a704a0 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 f5338250..162aacd1 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 4a5ede75..2ab13790 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 @@ -101,10 +101,6 @@ 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') @@ -112,58 +108,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; +//========================================================================== - gl_AddMapinfoParser() ; - - 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; @@ -1483,6 +165,11 @@ static int FindEndSequence (int type, const char *picname) return -1; } +//========================================================================== +// +// +//========================================================================== + static void SetEndSequence (char *nextmap, int type) { int seqnum; @@ -1498,6 +185,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!!! @@ -1521,85 +213,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; @@ -1611,6 +232,11 @@ void G_DeferedInitNew (const char *mapname, int newskill) gameaction = ga_newgame2; } +//========================================================================== +// +// +//========================================================================== + CCMD (map) { if (netgame) @@ -1636,6 +262,11 @@ CCMD (map) } } +//========================================================================== +// +// +//========================================================================== + CCMD (open) { if (netgame) @@ -1664,6 +295,11 @@ CCMD (open) } +//========================================================================== +// +// +//========================================================================== + void G_NewInit () { int i; @@ -1696,6 +332,11 @@ void G_NewInit () NextSkill = -1; } +//========================================================================== +// +// +//========================================================================== + void G_DoNewGame (void) { G_NewInit (); @@ -1708,6 +349,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; @@ -1766,14 +439,14 @@ void G_InitNew (const char *mapname, bool bTitleLevel) } else { - StatusBar = CreateCustomStatusBar(GETSBARINFOSCRIPT(gameinfo.gametype)); + StatusBar = CreateCustomStatusBar(SCRIPT_DEFAULT); } } if (StatusBar == NULL) { if (gameinfo.gametype & GAME_DoomChex) { - StatusBar = CreateCustomStatusBar (GETSBARINFOSCRIPT(gameinfo.gametype)); + StatusBar = CreateCustomStatusBar (SCRIPT_DEFAULT); } else if (gameinfo.gametype == GAME_Heretic) { @@ -1823,16 +496,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; @@ -1888,9 +552,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) @@ -1905,7 +574,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; @@ -1969,6 +638,11 @@ void G_ChangeLevel(const char *levelname, int position, bool keepFacing, int nex } } +//========================================================================== +// +// +//========================================================================== + const char *G_GetExitMap() { return level.nextmap; @@ -1988,6 +662,11 @@ const char *G_GetSecretExitMap() return nextmap; } +//========================================================================== +// +// +//========================================================================== + void G_ExitLevel (int position, bool keepFacing) { G_ChangeLevel(G_GetExitMap(), position, keepFacing); @@ -1998,6 +677,11 @@ void G_SecretExitLevel (int position) G_ChangeLevel(G_GetSecretExitMap(), position, false); } +//========================================================================== +// +// +//========================================================================== + void G_DoCompleted (void) { int i; @@ -2120,11 +804,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 @@ -2154,6 +834,11 @@ void G_DoCompleted (void) WI_Start (&wminfo); } +//========================================================================== +// +// +//========================================================================== + class DAutosaver : public DThinker { DECLARE_CLASS (DAutosaver, DThinker) @@ -2169,9 +854,12 @@ void DAutosaver::Tick () Destroy (); } +//========================================================================== // // G_DoLoadLevel // +//========================================================================== + extern gamestate_t wipegamestate; void G_DoLoadLevel (int position, bool autosave) @@ -2197,18 +885,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; @@ -2245,11 +933,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); @@ -2316,9 +1004,12 @@ void G_DoLoadLevel (int position, bool autosave) } +//========================================================================== // // G_WorldDone // +//========================================================================== + void G_WorldDone (void) { cluster_info_t *nextcluster; @@ -2333,9 +1024,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, @@ -2349,21 +1040,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, @@ -2373,6 +1064,11 @@ void G_WorldDone (void) } } +//========================================================================== +// +// +//========================================================================== + void G_DoWorldDone (void) { gamestate = GS_LEVEL; @@ -2518,6 +1214,11 @@ void G_FinishTravel () } } +//========================================================================== +// +// +//========================================================================== + void G_InitLevelLocals () { level_info_t *info; @@ -2538,7 +1239,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; @@ -2577,7 +1277,7 @@ void G_InitLevelLocals () G_AirControlChanged (); - if (info->level_name) + if (!info->LevelName.IsEmpty()) { cluster_info_t *clus = FindClusterInfo (info->cluster); @@ -2587,11 +1287,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); @@ -2606,10 +1305,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; @@ -2621,6 +1320,11 @@ void G_InitLevelLocals () NormalLight.ChangeFade (level.fadeto); } +//========================================================================== +// +// +//========================================================================== + bool FLevelLocals::IsJumpingAllowed() const { if (dmflags & DF_NO_JUMP) @@ -2630,6 +1334,11 @@ bool FLevelLocals::IsJumpingAllowed() const return !(level.flags & LEVEL_JUMP_NO); } +//========================================================================== +// +// +//========================================================================== + bool FLevelLocals::IsCrouchingAllowed() const { if (dmflags & DF_NO_CROUCH) @@ -2639,6 +1348,11 @@ bool FLevelLocals::IsCrouchingAllowed() const return !(level.flags & LEVEL_CROUCH_NO); } +//========================================================================== +// +// +//========================================================================== + bool FLevelLocals::IsFreelookAllowed() const { if (level.flags & LEVEL_FREELOOK_NO) @@ -2648,6 +1362,11 @@ bool FLevelLocals::IsFreelookAllowed() const return !(dmflags & DF_NO_FREELOOK); } +//========================================================================== +// +// +//========================================================================== + FString CalcMapName (int episode, int level) { FString lumpname; @@ -2664,124 +1383,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 () { @@ -2797,6 +1402,11 @@ void G_AirControlChanged () } } +//========================================================================== +// +// +//========================================================================== + void G_SerializeLevel (FArchive &arc, bool hubLoad) { int i = level.totaltime; @@ -2914,13 +1524,18 @@ void G_SerializeLevel (FArchive &arc, bool hubLoad) gl_RecreateAllAttachedLights(); } +//========================================================================== +// // 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; @@ -2933,14 +1548,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 (); @@ -2978,21 +1598,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) { @@ -3009,6 +1621,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; @@ -3016,6 +1633,11 @@ static void writeSnapShot (FArchive &arc, level_info_t *i) i->snapshot->Serialize (arc); } +//========================================================================== +// +// +//========================================================================== + void G_WriteSnapshots (FILE *file) { unsigned int i; @@ -3083,6 +1705,11 @@ void G_WriteSnapshots (FILE *file) } } +//========================================================================== +// +// +//========================================================================== + void G_ReadSnapshots (PNGHandle *png) { DWORD chunkLen; @@ -3169,12 +1796,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; @@ -3200,6 +1837,11 @@ void P_WriteACSDefereds (FILE *file) } } +//========================================================================== +// +// +//========================================================================== + void P_ReadACSDefereds (PNGHandle *png) { BYTE namelen; @@ -3230,6 +1872,11 @@ void P_ReadACSDefereds (PNGHandle *png) } +//========================================================================== +// +// +//========================================================================== + void FLevelLocals::Tick () { // Reset carry sectors @@ -3239,6 +1886,11 @@ void FLevelLocals::Tick () } } +//========================================================================== +// +// +//========================================================================== + void FLevelLocals::AddScroller (DScroller *scroller, int secnum) { if (secnum < 0) @@ -3252,285 +1904,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) { @@ -3540,7 +1918,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 b288cc06..48db7b19 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; @@ -137,9 +212,6 @@ class DScroller; class FScanner; struct level_info_t; -typedef void (*MIParseFunc)(FScanner &sc, level_info_t *info); - -void AddOptionalMapinfoParser(const char *keyword, MIParseFunc parsefunc); struct FOptionalMapinfoData { @@ -150,6 +222,18 @@ struct FOptionalMapinfoData virtual FOptionalMapinfoData *Clone() const = 0; }; +struct FOptionalMapinfoDataPtr +{ + FOptionalMapinfoData *Ptr; + + FOptionalMapinfoDataPtr() throw() : Ptr(NULL) {} + ~FOptionalMapinfoDataPtr() { if (Ptr!=NULL) delete Ptr; } + FOptionalMapinfoDataPtr(const FOptionalMapinfoDataPtr &p) throw() : Ptr(p.Ptr->Clone()) {} + FOptionalMapinfoDataPtr &operator= (FOptionalMapinfoDataPtr &p) throw() { Ptr = p.Ptr->Clone(); return *this; } +}; + +typedef TMap FOptData; + struct level_info_t { char mapname[9]; @@ -161,13 +245,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,27 +269,61 @@ 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; + FOptData optdata; + 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 (); + + template + T *GetOptData(FName id, bool create = true) + { + FOptionalMapinfoDataPtr *pdat = optdata.CheckKey(id); + + if (pdat != NULL) + { + return static_cast(pdat->Ptr); + } + else if (create) + { + T *newobj = new T; + optdata[id].Ptr = newobj; + return newobj; + } + else return NULL; + } }; // [RH] These get zeroed every tic and are updated by thinkers. @@ -231,17 +349,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 +432,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 +453,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 +491,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 +499,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 00000000..170a9e4b --- /dev/null +++ b/src/g_mapinfo.cpp @@ -0,0 +1,1850 @@ +/* +** 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; + sc.MustGetStringName("{"); + 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 6ee93f4f..37bf5312 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 67635acb..e466d1cd 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.h b/src/g_shared/sbarinfo.h index e792b31e..daa13609 100644 --- a/src/g_shared/sbarinfo.h +++ b/src/g_shared/sbarinfo.h @@ -147,17 +147,9 @@ struct SBarInfo static void Load(); }; -#define NUM_SCRIPTS 5 #define SCRIPT_CUSTOM 0 -#define SCRIPT_DOOM 1 -// The next ones shouldn't be used... yet -#define SCRIPT_HERETIC 2 -#define SCRIPT_HEXEN 3 -#define SCRIPT_STRIFE 4 -// Converts GAME_x to it's script number -#define GETSBARINFOSCRIPT(game) \ - (game & GAME_DoomChex) ? SCRIPT_DOOM : (game == GAME_Heretic ? SCRIPT_HERETIC : (game == GAME_Hexen ? SCRIPT_HEXEN : (game == GAME_Strife ? SCRIPT_STRIFE : SCRIPT_CUSTOM))) -extern SBarInfo *SBarInfoScript[5]; +#define SCRIPT_DEFAULT 1 +extern SBarInfo *SBarInfoScript[2]; // Enums used between the parser and the display diff --git a/src/g_shared/sbarinfo_display.cpp b/src/g_shared/sbarinfo_display.cpp index 790a9a49..205c8205 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 8530afe2..e9e1f218 100644 --- a/src/g_shared/sbarinfo_parser.cpp +++ b/src/g_shared/sbarinfo_parser.cpp @@ -46,16 +46,9 @@ #include "gi.h" #include "i_system.h" #include "g_level.h" +#include "p_acs.h" -SBarInfo *SBarInfoScript[NUM_SCRIPTS] = {NULL,NULL,NULL,NULL,NULL}; -static const char *DefaultScriptNames[NUM_SCRIPTS] = -{ - "SBARINFO", //Custom - "sbarinfo/doom.txt", - NULL, //Heretic - NULL, //Hexen - NULL //Strife -}; +SBarInfo *SBarInfoScript[2] = {NULL,NULL}; static const char *SBarInfoTopLevel[] = { @@ -114,7 +107,7 @@ static const char *SBarInfoRoutineLevel[] = static void FreeSBarInfoScript() { - for(int i = 0;i < NUM_SCRIPTS;i++) + for(int i = 0;i < 2;i++) { if (SBarInfoScript[i] != NULL) { @@ -126,28 +119,25 @@ static void FreeSBarInfoScript() void SBarInfo::Load() { - Printf ("ParseSBarInfo: Loading default status bar definitions.\n"); - for(int i = 1;i < NUM_SCRIPTS;i++) // Read in default bars if they exist + if(gameinfo.statusbar != NULL) { - if(DefaultScriptNames[i] != NULL) + int lump = Wads.CheckNumForFullName(gameinfo.statusbar, true); + if(lump != -1) { - int lump = Wads.CheckNumForFullName(DefaultScriptNames[i], true); - if(lump != -1) - { - if(SBarInfoScript[i] == NULL) - SBarInfoScript[i] = new SBarInfo(lump); - else - SBarInfoScript[i]->ParseSBarInfo(lump); - } + Printf ("ParseSBarInfo: Loading default status bar definition.\n"); + if(SBarInfoScript[SCRIPT_DEFAULT] == NULL) + SBarInfoScript[SCRIPT_DEFAULT] = new SBarInfo(lump); + else + SBarInfoScript[SCRIPT_DEFAULT]->ParseSBarInfo(lump); } } - if(Wads.CheckNumForName(DefaultScriptNames[SCRIPT_CUSTOM]) != -1) + if(Wads.CheckNumForName("SBARINFO") != -1) { Printf ("ParseSBarInfo: Loading custom status bar definition.\n"); int lastlump, lump; lastlump = 0; - while((lump = Wads.FindLump(DefaultScriptNames[SCRIPT_CUSTOM], &lastlump)) != -1) + while((lump = Wads.FindLump("SBARINFO", &lastlump)) != -1) { if(SBarInfoScript[SCRIPT_CUSTOM] == NULL) SBarInfoScript[SCRIPT_CUSTOM] = new SBarInfo(lump); diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index cb77e40d..43b06002 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 cc25a3b1..644e2508 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 00000000..a664dc7a --- /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 c1028ccf..15bb4fbd 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/gi.cpp b/src/gi.cpp index d26733e2..9715675f 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -101,6 +101,7 @@ gameinfo_t HexenGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "BagOfHolding", // Hexen doesn't have a backpack so use Heretic's. + NULL, }; gameinfo_t HexenDKGameInfo = @@ -134,6 +135,7 @@ gameinfo_t HexenDKGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "BagOfHolding", + NULL, }; gameinfo_t HereticGameInfo = @@ -167,6 +169,7 @@ gameinfo_t HereticGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "BagOfHolding", + NULL, }; gameinfo_t HereticSWGameInfo = @@ -200,6 +203,7 @@ gameinfo_t HereticSWGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "BagOfHolding", + NULL, }; gameinfo_t SharewareGameInfo = @@ -233,6 +237,7 @@ gameinfo_t SharewareGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "Backpack", + "sbarinfo/doom.txt", }; gameinfo_t RegisteredGameInfo = @@ -266,6 +271,7 @@ gameinfo_t RegisteredGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "Backpack", + "sbarinfo/doom.txt", }; gameinfo_t ChexGameInfo = @@ -299,6 +305,7 @@ gameinfo_t ChexGameInfo = MAKERGB(63,125,57), MAKERGB(95,175,87), "ZorchPack", + "sbarinfo/doom.txt", }; gameinfo_t Chex3GameInfo = @@ -332,6 +339,7 @@ gameinfo_t Chex3GameInfo = MAKERGB(63,125,57), MAKERGB(95,175,87), "ZorchPack", + "sbarinfo/doom.txt", }; gameinfo_t RetailGameInfo = @@ -365,6 +373,7 @@ gameinfo_t RetailGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "Backpack", + "sbarinfo/doom.txt", }; gameinfo_t CommercialGameInfo = @@ -398,6 +407,7 @@ gameinfo_t CommercialGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "Backpack", + "sbarinfo/doom.txt", }; gameinfo_t PlutoniaGameInfo = @@ -431,6 +441,7 @@ gameinfo_t PlutoniaGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "Backpack", + "sbarinfo/doom.txt", }; gameinfo_t TNTGameInfo = @@ -464,6 +475,7 @@ gameinfo_t TNTGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "Backpack", + "sbarinfo/doom.txt", }; gameinfo_t StrifeGameInfo = @@ -497,6 +509,7 @@ gameinfo_t StrifeGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "AmmoSatchel", + NULL, }; gameinfo_t StrifeTeaserGameInfo = @@ -530,6 +543,7 @@ gameinfo_t StrifeTeaserGameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "AmmoSatchel", + NULL, }; gameinfo_t StrifeTeaser2GameInfo = @@ -563,4 +577,5 @@ gameinfo_t StrifeTeaser2GameInfo = MAKERGB(104,0,0), MAKERGB(255,0,0), "AmmoSatchel", + NULL, }; diff --git a/src/gi.h b/src/gi.h index 9ef71f8a..a83f94a5 100644 --- a/src/gi.h +++ b/src/gi.h @@ -122,6 +122,7 @@ typedef struct DWORD defaultbloodcolor; DWORD defaultbloodparticlecolor; const char *backpacktype; + const char *statusbar; } gameinfo_t; extern gameinfo_t gameinfo; diff --git a/src/gl/gl_data.cpp b/src/gl/gl_data.cpp index 98e8b078..2db1eaf8 100644 --- a/src/gl/gl_data.cpp +++ b/src/gl/gl_data.cpp @@ -93,7 +93,6 @@ void gl_InitSpecialTextures() glpart = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/glpart.png"), FTexture::TEX_MiscPatch); mirrortexture = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/mirror.png"), FTexture::TEX_MiscPatch); gllight = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/gllight.png"), FTexture::TEX_MiscPatch); - //atterm(DeleteGLTextures); } //----------------------------------------------------------------------------- @@ -221,88 +220,73 @@ struct FGLROptions : public FOptionalMapinfoData FVector3 skyrotatevector; }; -static void ParseFunc(FScanner &sc, level_info_t *info) +DEFINE_MAP_OPTION(fogdensity, false) { - FName id = "gl_renderer"; - FOptionalMapinfoData *dat = info->opdata; + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->fogdensity = parse.sc.Number; +} - while (dat && dat->identifier != id) dat = dat->Next; - if (!dat) +DEFINE_MAP_OPTION(outsidefogdensity, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->outsidefogdensity = parse.sc.Number; +} + +DEFINE_MAP_OPTION(skyfog, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->skyfog = parse.sc.Number; +} + +DEFINE_MAP_OPTION(lightmode, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->lightmode = BYTE(parse.sc.Number); +} + +DEFINE_MAP_OPTION(nocoloredspritelighting, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + if (parse.CheckAssign()) { - dat = new FGLROptions; - dat->Next = info->opdata; - info->opdata = dat; + parse.sc.MustGetNumber(); + opt->nocoloredspritelighting = !!parse.sc.Number; } - - FGLROptions *opt = static_cast(dat); - - while (!sc.CheckString("}")) + else { - sc.MustGetString(); - if (sc.Compare("fogdensity")) - { - sc.MustGetNumber(); - opt->fogdensity = sc.Number; - } - else if (sc.Compare("outsidefogdensity")) - { - sc.MustGetNumber(); - opt->outsidefogdensity = sc.Number; - } - else if (sc.Compare("skyfog")) - { - sc.MustGetNumber(); - opt->skyfog = sc.Number; - } - else if (sc.Compare("lightmode")) - { - sc.MustGetNumber(); - opt->lightmode = BYTE(sc.Number); - } - else if (sc.Compare("nocoloredspritelighting")) - { - if (sc.CheckNumber()) - { - opt->nocoloredspritelighting = !!sc.Number; - } - else - { - opt->nocoloredspritelighting = true; - } - } - else if (sc.Compare("skyrotate")) - { - sc.MustGetFloat(); - opt->skyrotatevector.X = (float)sc.Float; - sc.MustGetFloat(); - opt->skyrotatevector.Y = (float)sc.Float; - sc.MustGetFloat(); - opt->skyrotatevector.Z = (float)sc.Float; - opt->skyrotatevector.MakeUnit(); - } - else - { - sc.ScriptError("Unknown keyword %s", sc.String); - } + opt->nocoloredspritelighting = true; } } -void gl_AddMapinfoParser() -{ - AddOptionalMapinfoParser("gl_renderer", ParseFunc); +DEFINE_MAP_OPTION(skyrotate, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + + parse.ParseAssign(); + parse.sc.MustGetFloat(); + opt->skyrotatevector.X = (float)parse.sc.Float; + parse.sc.MustGetFloat(); + opt->skyrotatevector.Y = (float)parse.sc.Float; + parse.sc.MustGetFloat(); + opt->skyrotatevector.Z = (float)parse.sc.Float; + opt->skyrotatevector.MakeUnit(); } static void InitGLRMapinfoData() { - FName id = "gl_renderer"; - FOptionalMapinfoData *dat = level.info->opdata; + FGLROptions *opt = level.info->GetOptData("gl_renderer", false); - while (dat && dat->identifier != id) dat = dat->Next; - if (dat != NULL) + if (opt != NULL) { - FGLROptions *opt = static_cast(dat); gl_SetFogParams(opt->fogdensity, level.info->outsidefog, opt->outsidefogdensity, opt->skyfog); - glset.map_lightmode = opt->lightmode; glset.map_nocoloredspritelighting = opt->nocoloredspritelighting; glset.skyrotatevector = opt->skyrotatevector; diff --git a/src/gl/gl_models.cpp b/src/gl/gl_models.cpp index f965923c..310f5197 100644 --- a/src/gl/gl_models.cpp +++ b/src/gl/gl_models.cpp @@ -514,7 +514,7 @@ void gl_RenderFrameModels( const FSpriteModelFrame *smf, // [BB] To interpolate at more than 35 fps we take tic fractions into account. float ticFraction = 0.; // [BB] In case the tic counter is frozen we have to leave ticFraction at zero. - if ( ConsoleState == c_up && menuactive != MENU_On && !(level.flags & LEVEL_FROZEN) ) + if ( ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN) ) { float time = GetTimeFloat(); ticFraction = (time - static_cast(time)); diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index efaba169..456652bc 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -248,22 +248,22 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER y = MAX(BigFont->GetHeight() * 4, y); } - for (i = 0; i < teams.Size (); i++) + for (i = 0; i < Teams.Size (); i++) { - teams[i].players = 0; - teams[i].score = 0; + Teams[i].m_iPlayerCount = 0; + Teams[i].m_iScore = 0; } for (i = 0; i < MAXPLAYERS; ++i) { - if (playeringame[sortedplayers[i]-players] && TEAMINFO_IsValidTeam (sortedplayers[i]->userinfo.team)) + if (playeringame[sortedplayers[i]-players] && TeamLibrary.IsValidTeam (sortedplayers[i]->userinfo.team)) { - if (teams[sortedplayers[i]->userinfo.team].players++ == 0) + if (Teams[sortedplayers[i]->userinfo.team].m_iPlayerCount++ == 0) { numTeams++; } - teams[sortedplayers[i]->userinfo.team].score += sortedplayers[i]->fragcount; + Teams[sortedplayers[i]->userinfo.team].m_iScore += sortedplayers[i]->fragcount; } } @@ -271,9 +271,9 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER int numscores = 0; int scorex; - for (i = 0; i < teams.Size(); ++i) + for (i = 0; i < Teams.Size(); ++i) { - if (teams[i].players) + if (Teams[i].m_iPlayerCount) { numscores++; } @@ -281,14 +281,14 @@ static void HU_DoDrawScores (player_t *player, player_t *sortedplayers[MAXPLAYER scorex = (SCREENWIDTH - scorexwidth * (numscores - 1)) / 2; - for (i = 0; i < teams.Size(); ++i) + for (i = 0; i < Teams.Size(); ++i) { - if (teams[i].players) + if (Teams[i].m_iPlayerCount) { char score[80]; - mysnprintf (score, countof(score), "%d", teams[i].score); + mysnprintf (score, countof(score), "%d", Teams[i].m_iScore); - screen->DrawText (BigFont, teams[i].GetTextColor(), + screen->DrawText (BigFont, Teams[i].GetTextColor(), scorex - BigFont->StringWidth(score)*CleanXfac/2, y, score, DTA_CleanNoMove, true, TAG_DONE); @@ -400,9 +400,9 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, screen->DrawText (SmallFont, color, col4, y, player->userinfo.netname, DTA_CleanNoMove, true, TAG_DONE); - if (teamplay && teams[player->userinfo.team].logo.IsNotEmpty()) + if (teamplay && Teams[player->userinfo.team].GetLogo ().IsNotEmpty ()) { - FTexture *pic = TexMan[teams[player->userinfo.team].logo]; + FTexture *pic = TexMan[Teams[player->userinfo.team].GetLogo ().GetChars ()]; screen->DrawTexture (pic, col1 - (pic->GetWidth() + 2) * CleanXfac, y, DTA_CleanNoMove, true, TAG_DONE); } @@ -437,8 +437,8 @@ int HU_GetRowColor(player_t *player, bool highlight) { if (teamplay && deathmatch) { - if (TEAMINFO_IsValidTeam (player->userinfo.team)) - return teams[player->userinfo.team].GetTextColor(); + if (TeamLibrary.IsValidTeam (player->userinfo.team)) + return Teams[player->userinfo.team].GetTextColor(); else return CR_GREY; } diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 12356e40..b8b73aa6 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/m_menu.cpp b/src/m_menu.cpp index 96b69e8a..45f82a1a 100644 --- a/src/m_menu.cpp +++ b/src/m_menu.cpp @@ -2164,8 +2164,8 @@ static void M_PlayerSetupDrawer () screen->DrawText (SmallFont, label, PSetupDef.x, PSetupDef.y + LINEHEIGHT+yo, "Team", DTA_Clean, true, TAG_DONE); screen->DrawText (SmallFont, value, x, PSetupDef.y + LINEHEIGHT+yo, - !TEAMINFO_IsValidTeam (players[consoleplayer].userinfo.team) ? "None" : - teams[players[consoleplayer].userinfo.team].name, + !TeamLibrary.IsValidTeam (players[consoleplayer].userinfo.team) ? "None" : + Teams[players[consoleplayer].userinfo.team].GetName (), DTA_Clean, true, TAG_DONE); // Draw player character @@ -2640,11 +2640,11 @@ static void M_ChangePlayerTeam (int choice) { if (team == 0) { - team = TEAM_None; + team = TEAM_NONE; } - else if (team == TEAM_None) + else if (team == TEAM_NONE) { - team = teams.Size () - 1; + team = Teams.Size () - 1; } else { @@ -2653,11 +2653,11 @@ static void M_ChangePlayerTeam (int choice) } else { - if (team == int(teams.Size () - 1)) + if (team == int(Teams.Size () - 1)) { - team = TEAM_None; + team = TEAM_NONE; } - else if (team == TEAM_None) + else if (team == TEAM_NONE) { team = 0; } diff --git a/src/m_menu.h b/src/m_menu.h index 2d8d72d3..85187a8d 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -140,6 +140,7 @@ typedef struct menuitem_s { int key2; char *res2; void *extra; + float discretecenter; } c; union { float step; diff --git a/src/m_options.cpp b/src/m_options.cpp index 4c1c227d..d0573794 100644 --- a/src/m_options.cpp +++ b/src/m_options.cpp @@ -437,6 +437,7 @@ menuitem_t ControlsItems[] = { control, "Next item", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invnext"} }, { control, "Previous item", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invprev"} }, { control, "Drop item", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"invdrop"} }, + { control, "Drop weapon", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"weapdrop"} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { whitetext,"Other", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { control, "Toggle automap", {NULL}, {0.0}, {0.0}, {0.0}, {(value_t *)"togglemap"} }, @@ -1117,7 +1118,7 @@ static menu_t DMFlagsMenu = *=======================================*/ static menuitem_t CompatibilityItems[] = { - { discrete, "Compatibility mode", {&compatmode}, {5.0}, {0.0}, {0.0}, {CompatModes} }, + { discrete, "Compatibility mode", {&compatmode}, {5.0}, {1.0}, {0.0}, {CompatModes} }, { redtext, " ", {NULL}, {0.0}, {0.0}, {0.0}, {NULL} }, { bitflag, "Find shortest textures like Doom", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_SHORTTEX} }, { bitflag, "Use buggier stair building", {&compatflags}, {0}, {0}, {0}, {(value_t *)COMPATF_STAIRINDEX} }, @@ -1552,7 +1553,8 @@ static void CalcIndent (menu_t *menu) for (i = 0; i < menu->numitems; i++) { item = menu->items + i; - if (item->type != whitetext && item->type != redtext && item->type != screenres) + if (item->type != whitetext && item->type != redtext && item->type != screenres && + (item->type != discrete || !item->c.discretecenter)) { thiswidth = SmallFont->StringWidth (item->label); if (thiswidth > widest) @@ -1682,6 +1684,7 @@ void M_OptDrawer () UCVarValue value; DWORD overlay; int labelofs; + int indent; if (!CurrentMenu->DontDim) { @@ -1733,6 +1736,14 @@ void M_OptDrawer () item = CurrentMenu->items + i; overlay = 0; + if (item->type == discrete && item->c.discretecenter) + { + indent = 160; + } + else + { + indent = CurrentMenu->indent; + } if (item->type != screenres) { @@ -1741,14 +1752,14 @@ void M_OptDrawer () { case more: case safemore: - x = CurrentMenu->indent - width; + x = indent - width; color = MoreColor; break; case numberedmore: case rsafemore: case rightmore: - x = CurrentMenu->indent + 14; + x = indent + 14; color = item->type != rightmore ? CR_GREEN : MoreColor; break; @@ -1763,12 +1774,12 @@ void M_OptDrawer () break; case listelement: - x = CurrentMenu->indent + 14; + x = indent + 14; color = LabelColor; break; case colorpicker: - x = CurrentMenu->indent + 14; + x = indent + 14; color = MoreColor; break; @@ -1780,7 +1791,7 @@ void M_OptDrawer () // Intentional fall-through default: - x = CurrentMenu->indent - width; + x = indent - width; color = (item->type == control && menuactive == MENU_WaitKey && i == CurrentItem) ? CR_YELLOW : LabelColor; break; @@ -1795,7 +1806,7 @@ void M_OptDrawer () char tbuf[16]; mysnprintf (tbuf, countof(tbuf), "%d.", item->b.position); - x = CurrentMenu->indent - SmallFont->StringWidth (tbuf); + x = indent - SmallFont->StringWidth (tbuf); screen->DrawText (SmallFont, CR_GREY, x, y, tbuf, DTA_Clean, true, TAG_DONE); } break; @@ -1812,13 +1823,13 @@ void M_OptDrawer () if (v == vals) { - screen->DrawText (SmallFont, ValueColor, CurrentMenu->indent + 14, y, "Unknown", + screen->DrawText (SmallFont, ValueColor, indent + 14, y, "Unknown", DTA_Clean, true, TAG_DONE); } else { screen->DrawText (SmallFont, item->type == cdiscrete ? v : ValueColor, - CurrentMenu->indent + 14, y, item->e.values[v].name, + indent + 14, y, item->e.values[v].name, DTA_Clean, true, TAG_DONE); } @@ -1861,13 +1872,13 @@ void M_OptDrawer () if (v == vals) { - screen->DrawText (SmallFont, ValueColor, CurrentMenu->indent + 14, y, "Unknown", + screen->DrawText (SmallFont, ValueColor, indent + 14, y, "Unknown", DTA_Clean, true, DTA_ColorOverlay, overlay, TAG_DONE); } else { screen->DrawText (SmallFont, item->type == cdiscrete ? v : ValueColor, - CurrentMenu->indent + 14, y, + indent + 14, y, item->type != discretes ? item->e.values[v].name : item->e.valuestrings[v].name.GetChars(), DTA_Clean, true, DTA_ColorOverlay, overlay, TAG_DONE); } @@ -1881,7 +1892,7 @@ void M_OptDrawer () value = item->a.cvar->GetGenericRep (CVAR_String); v = M_FindCurVal(value.String, item->e.enumvalues, (int)item->b.numvalues); - screen->DrawText(SmallFont, ValueColor, CurrentMenu->indent + 14, y, v, DTA_Clean, true, TAG_DONE); + screen->DrawText(SmallFont, ValueColor, indent + 14, y, v, DTA_Clean, true, TAG_DONE); } break; @@ -1895,11 +1906,11 @@ void M_OptDrawer () if (v == vals) { UCVarValue val = item->a.guidcvar->GetGenericRep (CVAR_String); - screen->DrawText (SmallFont, ValueColor, CurrentMenu->indent + 14, y, val.String, DTA_Clean, true, TAG_DONE); + screen->DrawText (SmallFont, ValueColor, indent + 14, y, val.String, DTA_Clean, true, TAG_DONE); } else { - screen->DrawText (SmallFont, ValueColor, CurrentMenu->indent + 14, y, item->e.guidvalues[v].Name, + screen->DrawText (SmallFont, ValueColor, indent + 14, y, item->e.guidvalues[v].Name, DTA_Clean, true, TAG_DONE); } @@ -1907,22 +1918,22 @@ void M_OptDrawer () break; case nochoice: - screen->DrawText (SmallFont, CR_GOLD, CurrentMenu->indent + 14, y, + screen->DrawText (SmallFont, CR_GOLD, indent + 14, y, (item->e.values[(int)item->b.min]).name, DTA_Clean, true, TAG_DONE); break; case slider: value = item->a.cvar->GetGenericRep (CVAR_Float); - M_DrawSlider (CurrentMenu->indent + 14, y + labelofs, item->b.min, item->c.max, value.Float); + M_DrawSlider (indent + 14, y + labelofs, item->b.min, item->c.max, value.Float); break; case absslider: value = item->a.cvar->GetGenericRep (CVAR_Float); - M_DrawSlider (CurrentMenu->indent + 14, y + labelofs, item->b.min, item->c.max, fabs(value.Float)); + M_DrawSlider (indent + 14, y + labelofs, item->b.min, item->c.max, fabs(value.Float)); break; case intslider: - M_DrawSlider (CurrentMenu->indent + 14, y + labelofs, item->b.min, item->c.max, item->a.fval); + M_DrawSlider (indent + 14, y + labelofs, item->b.min, item->c.max, item->a.fval); break; case control: @@ -1932,11 +1943,11 @@ void M_OptDrawer () C_NameKeys (description, item->b.key1, item->c.key2); if (description[0]) { - M_DrawConText(CR_WHITE, CurrentMenu->indent + 14, y-1+labelofs, description); + M_DrawConText(CR_WHITE, indent + 14, y-1+labelofs, description); } else { - screen->DrawText(SmallFont, CR_BLACK, CurrentMenu->indent + 14, y + labelofs, "---", + screen->DrawText(SmallFont, CR_BLACK, indent + 14, y + labelofs, "---", DTA_Clean, true, TAG_DONE); } } @@ -1945,7 +1956,7 @@ void M_OptDrawer () case colorpicker: { int box_x, box_y; - box_x = (CurrentMenu->indent - 35 - 160) * CleanXfac + screen->GetWidth()/2; + box_x = (indent - 35 - 160) * CleanXfac + screen->GetWidth()/2; box_y = (y - ((gameinfo.gametype & GAME_Raven) ? 99 : 100)) * CleanYfac + screen->GetHeight()/2; screen->Clear (box_x, box_y, box_x + 32*CleanXfac, box_y + (fontheight-1)*CleanYfac, item->a.colorcvar->GetIndex(), 0); @@ -1961,7 +1972,7 @@ void M_OptDrawer () box_y = (y - 98) * CleanYfac + screen->GetHeight()/2; p = 0; - box_x = (CurrentMenu->indent - 32 - 160) * CleanXfac + screen->GetWidth()/2; + box_x = (indent - 32 - 160) * CleanXfac + screen->GetWidth()/2; for (x1 = 0, p = int(item->b.min * 16); x1 < 16; ++p, ++x1) { screen->Clear (box_x, box_y, box_x + w, box_y + h, p, 0); @@ -2016,7 +2027,7 @@ void M_OptDrawer () } screen->DrawText (SmallFont, ValueColor, - CurrentMenu->indent + 14, y, str, DTA_Clean, true, TAG_DONE); + indent + 14, y, str, DTA_Clean, true, TAG_DONE); } break; @@ -2028,7 +2039,7 @@ void M_OptDrawer () i == CurrentItem && (skullAnimCounter < 6 || menuactive == MENU_WaitKey)) { - M_DrawConText(CR_RED, CurrentMenu->indent + 3, y-1+labelofs, "\xd"); + M_DrawConText(CR_RED, indent + 3, y-1+labelofs, "\xd"); } } else diff --git a/src/oplsynth/fmopl.cpp b/src/oplsynth/fmopl.cpp index 07139c52..9bab8bbd 100644 --- a/src/oplsynth/fmopl.cpp +++ b/src/oplsynth/fmopl.cpp @@ -1,10 +1,11 @@ /* This file is based on fmopl.c from MAME 0.95. The non-YM3816 parts have been -ripped out in the interest of trying to make this a bit faster, since Doom -music doesn't need them. I also made it render the sound a voice at a time -instead of a sample at a time, so unused voices don't waste time being -calculated. +ripped out in the interest of making this simpler, since Doom music doesn't +need them. I also made it render the sound a voice at a time instead of a +sample at a time, so unused voices don't waste time being calculated. If all +voices are playing, it's not much difference, but it does offer a big +improvement when only a few voices are playing. Here is the appropriate section from mame.txt: @@ -1919,41 +1920,32 @@ void YM3812UpdateOne(void *chip, float *buffer, int length) UINT32 eg_timer_bak = OPL->eg_timer; UINT32 eg_cnt_bak = OPL->eg_cnt; - UINT32 noise_p_bak = OPL->noise_p; - UINT32 noise_rng_bak = OPL->noise_rng; - UINT32 lfo_am_cnt_out = lfo_am_cnt_bak; UINT32 eg_timer_out = eg_timer_bak; UINT32 eg_cnt_out = eg_cnt_bak; - UINT32 noise_p_out = noise_p_bak; - UINT32 noise_rng_out = noise_rng_bak; - for (i = 0; i <= (rhythm ? 5 : 8); ++i) { OPL->lfo_am_cnt = lfo_am_cnt_bak; OPL->eg_timer = eg_timer_bak; OPL->eg_cnt = eg_cnt_bak; - OPL->noise_p = noise_p_bak; - OPL->noise_rng = noise_rng_bak; if (CalcVoice (OPL, i, buffer, length)) { lfo_am_cnt_out = OPL->lfo_am_cnt; eg_timer_out = OPL->eg_timer; eg_cnt_out = OPL->eg_cnt; - noise_p_out = OPL->noise_p; - noise_rng_out = OPL->noise_rng; } } OPL->lfo_am_cnt = lfo_am_cnt_out; OPL->eg_timer = eg_timer_out; OPL->eg_cnt = eg_cnt_out; - OPL->noise_p = noise_p_out; - OPL->noise_rng = noise_rng_out; if (rhythm) /* Rhythm part */ { + OPL->lfo_am_cnt = lfo_am_cnt_bak; + OPL->eg_timer = eg_timer_bak; + OPL->eg_cnt = eg_cnt_bak; CalcRhythm (OPL, buffer, length); } } @@ -1979,7 +1971,6 @@ static bool CalcVoice (FM_OPL *OPL, int voice, float *buffer, int length) OPL_CALC_CH(CH, buffer + i); advance(OPL, voice, voice); - advance_noise(OPL); } return true; } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 3769328f..2c192e91 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 --------------------------------------// // @@ -2157,24 +2374,28 @@ void DLevelScript::DoSetFont (int fontnum) } } -#define APROP_Health 0 -#define APROP_Speed 1 -#define APROP_Damage 2 -#define APROP_Alpha 3 -#define APROP_RenderStyle 4 -#define APROP_Ambush 10 -#define APROP_Invulnerable 11 -#define APROP_JumpZ 12 // [GRB] -#define APROP_ChaseGoal 13 -#define APROP_Frightened 14 -#define APROP_Gravity 15 -#define APROP_Friendly 16 -#define APROP_SpawnHealth 17 -#define APROP_SeeSound 5 // Sounds can only be set, not gotten -#define APROP_AttackSound 6 -#define APROP_PainSound 7 -#define APROP_DeathSound 8 -#define APROP_ActiveSound 9 +enum +{ + APROP_Health = 0, + APROP_Speed = 1, + APROP_Damage = 2, + APROP_Alpha = 3, + APROP_RenderStyle = 4, + APROP_SeeSound = 5, // Sounds can only be set, not gotten + APROP_AttackSound = 6, + APROP_PainSound = 7, + APROP_DeathSound = 8, + APROP_ActiveSound = 9, + APROP_Ambush = 10, + APROP_Invulnerable = 11, + APROP_JumpZ = 12, // [GRB] + APROP_ChaseGoal = 13, + APROP_Frightened = 14, + APROP_Gravity = 15, + APROP_Friendly = 16, + APROP_SpawnHealth = 17, + APROP_Dropped = 18, +}; // These are needed for ACS's APROP_RenderStyle static const int LegacyRenderStyleIndices[] = @@ -2253,6 +2474,10 @@ void DLevelScript::DoSetActorProperty (AActor *actor, int property, int value) if (value) actor->flags |= MF_AMBUSH; else actor->flags &= ~MF_AMBUSH; break; + case APROP_Dropped: + if (value) actor->flags |= MF_DROPPED; else actor->flags &= ~MF_DROPPED; + break; + case APROP_Invulnerable: if (value) actor->flags2 |= MF2_INVULNERABLE; else actor->flags2 &= ~MF2_INVULNERABLE; break; @@ -2356,6 +2581,7 @@ int DLevelScript::GetActorProperty (int tid, int property) return STYLE_Normal; case APROP_Gravity: return actor->gravity; case APROP_Ambush: return !!(actor->flags & MF_AMBUSH); + case APROP_Dropped: return !!(actor->flags & MF_DROPPED); case APROP_ChaseGoal: return !!(actor->flags5 & MF5_CHASEGOAL); case APROP_Frightened: return !!(actor->flags4 & MF4_FRIGHTENED); case APROP_Friendly: return !!(actor->flags & MF_FRIENDLY); diff --git a/src/p_acs.h b/src/p_acs.h index a92b0d0c..59ff1274 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 bcbb0b42..45bb6ff4 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 6b18d4cc..e4abed26 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -1590,10 +1590,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Look) // Let the self wander around aimlessly looking for a fight if (self->SeeState != NULL) { - if (!(self->flags & MF_INCHASE)) - { - self->SetState (self->SeeState); - } + self->SetState (self->SeeState); } else { @@ -1638,7 +1635,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Look) } } - if (self->target && !(self->flags & MF_INCHASE)) + if (self->target) { self->SetState (self->SeeState); } @@ -1757,6 +1754,10 @@ void A_DoChase (AActor *actor, bool fastchase, FState *meleestate, FState *missi { int delta; + if (actor->flags & MF_INCHASE) + { + return; + } actor->flags |= MF_INCHASE; // [RH] Andy Baker's stealth monsters @@ -2183,7 +2184,6 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) S_Sound (corpsehit, CHAN_BODY, "vile/raise", 1, ATTN_IDLE); info = corpsehit->GetDefault (); - corpsehit->SetState (raisestate); corpsehit->height = info->height; // [RH] Use real mobj height corpsehit->radius = info->radius; // [RH] Use real radius /* @@ -2207,6 +2207,7 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) // You are the Archvile's minion now, so hate what it hates corpsehit->CopyFriendliness (self, false); + corpsehit->SetState (raisestate); return true; } @@ -2652,9 +2653,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 +2668,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 5f594870..372318e5 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -957,6 +957,7 @@ void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage if (df != NULL) { fixed_t * pdf = df->CheckKey(mod); + if (pdf== NULL && mod != NAME_None) pdf = df->CheckKey(NAME_None); if (pdf != NULL) { damage = FixedMul(damage, *pdf); @@ -1295,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 11205343..a26e71be 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 3307c3d6..3e93882b 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 1ea1e797..4d3f5929 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1831,7 +1831,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; @@ -2536,7 +2536,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; } @@ -2575,7 +2575,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; } @@ -3713,7 +3713,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); } @@ -4023,7 +4023,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; } @@ -4926,7 +4926,7 @@ bool AActor::IsTeammate (AActor *other) return false; if (!deathmatch) return true; - if (teamplay && other->player->userinfo.team != TEAM_None && + if (teamplay && other->player->userinfo.team != TEAM_NONE && player->userinfo.team == other->player->userinfo.team) { return true; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index d2dd03ee..996886d9 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -467,7 +467,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 46370f2c..c1e34fa0 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1602,7 +1602,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; @@ -1813,9 +1813,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; } @@ -1891,9 +1891,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); @@ -3354,7 +3354,7 @@ 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); } T_LoadScripts(map); @@ -3362,9 +3362,9 @@ void P_SetupLevel (char *lumpname, int position) { // 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; } } @@ -3373,9 +3373,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 f128e812..185719b2 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_states.cpp b/src/p_states.cpp index 68045b66..f3a99798 100644 --- a/src/p_states.cpp +++ b/src/p_states.cpp @@ -928,7 +928,7 @@ void DumpStateHelper(FStateLabels *StateList, const FString &prefix) else { Printf(PRINT_LOG, "%s%s: %s.%d\n", prefix.GetChars(), StateList->Labels[i].Label.GetChars(), - owner->TypeName.GetChars(), StateList->Labels[i].State - owner->ActorInfo->OwnedStates); + owner->TypeName.GetChars(), int(StateList->Labels[i].State - owner->ActorInfo->OwnedStates)); } } if (StateList->Labels[i].Children != NULL) diff --git a/src/p_things.cpp b/src/p_things.cpp index b8273db5..0404ac37 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 e0723756..b4a97dd8 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 94bfdc77..6bd5b26c 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 449fc76d..d1ef70fc 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 3087e0c9..673a585a 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 4117930b..0736c80c 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 07b1cb8c..effa0925 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -163,7 +163,8 @@ void S_NoiseDebug (void) screen->DrawText (SmallFont, CR_GOLD, 220, y, "vol", TAG_DONE); screen->DrawText (SmallFont, CR_GOLD, 260, y, "dist", TAG_DONE); screen->DrawText (SmallFont, CR_GOLD, 300, y, "chan", TAG_DONE); - screen->DrawText (SmallFont, CR_GOLD, 340, y, "flags", TAG_DONE); + screen->DrawText (SmallFont, CR_GOLD, 340, y, "pri", TAG_DONE); + screen->DrawText (SmallFont, CR_GOLD, 380, y, "flags", TAG_DONE); y += 8; if (Channels == NULL) @@ -231,6 +232,10 @@ void S_NoiseDebug (void) mysnprintf(temp, countof(temp), "%d", chan->EntChannel); screen->DrawText(SmallFont, color, 300, y, temp, TAG_DONE); + // Priority + mysnprintf(temp, countof(temp), "%d", chan->Priority); + screen->DrawText(SmallFont, color, 340, y, temp, TAG_DONE); + // Flags mysnprintf(temp, countof(temp), "%s3%sZ%sU%sM%sN%sA%sL%sE", (chan->ChanFlags & CHAN_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, @@ -241,7 +246,7 @@ void S_NoiseDebug (void) (chan->ChanFlags & CHAN_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, (chan->ChanFlags & CHAN_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, (chan->ChanFlags & CHAN_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK); - screen->DrawText(SmallFont, color, 340, y, temp, TAG_DONE); + screen->DrawText(SmallFont, color, 380, y, temp, TAG_DONE); y += 8; if (chan->PrevChan == &Channels) @@ -365,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; @@ -415,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; @@ -437,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); } } @@ -940,7 +937,7 @@ static FSoundChan *S_StartSound(AActor *actor, const sector_t *sec, const FPolyO // Select priority. if (type == SOURCE_None || actor == players[consoleplayer].camera) { - basepriority = 40; + basepriority = 80; } else { @@ -1664,7 +1661,7 @@ void S_EvictAllChannels() { S_StopChannel(chan); } - assert(chan->NextChan == next); +// assert(chan->NextChan == next); } } } @@ -2165,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 @@ -2428,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 9de966fd..c474439b 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 c74fca94..dcfb9254 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_input.cpp b/src/sdl/i_input.cpp index 2f8ba9c6..6cc16feb 100644 --- a/src/sdl/i_input.cpp +++ b/src/sdl/i_input.cpp @@ -281,6 +281,25 @@ static void MouseRead () } } +static void WheelMoved(event_t *event) +{ + if (GUICapture) + { + SDLMod mod = SDL_GetModState(); + event->type = EV_GUI_Event; + event->subtype = event->data1 == KEY_MWHEELUP ? EV_GUI_WheelUp : EV_GUI_WheelDown; + event->data1 = 0; + event->data3 = ((mod & KMOD_SHIFT) ? GKM_SHIFT : 0) | + ((mod & KMOD_CTRL) ? GKM_CTRL : 0) | + ((mod & KMOD_ALT) ? GKM_ALT : 0); + D_PostEvent(event); + } + else + { + D_PostEvent(event); + } +} + CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) { if (self < 0) self = 0; @@ -316,7 +335,7 @@ static void I_CheckNativeMouse () { SDL_ShowCursor (1); SDL_WM_GrabInput (SDL_GRAB_OFF); - FlushDIKState (KEY_MOUSE1, KEY_MOUSE4); + FlushDIKState (KEY_MOUSE1, KEY_MOUSE8); } else { @@ -358,6 +377,12 @@ void MessagePump (const SDL_Event &sev) case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: event.type = sev.type == SDL_MOUSEBUTTONDOWN ? EV_KeyDown : EV_KeyUp; + /* These button mappings work with my Gentoo system using the + * evdev driver and a Logitech MX510 mouse. Whether or not they + * carry over to other Linux systems, I have no idea, but I sure + * hope so. (Though buttons 11 and 12 are kind of useless, since + * they also trigger buttons 4 and 5.) + */ switch (sev.button.button) { case 1: event.data1 = KEY_MOUSE1; break; @@ -365,10 +390,28 @@ void MessagePump (const SDL_Event &sev) case 3: event.data1 = KEY_MOUSE2; break; case 4: event.data1 = KEY_MWHEELUP; break; case 5: event.data1 = KEY_MWHEELDOWN; break; - case 6: event.data1 = KEY_MOUSE4; break; /* dunno */ + case 6: event.data1 = KEY_MOUSE4; break; /* dunno; not generated by my mouse */ + case 7: event.data1 = KEY_MOUSE5; break; /* ditto */ + case 8: event.data1 = KEY_MOUSE4; break; + case 9: event.data1 = KEY_MOUSE5; break; + case 10: event.data1 = KEY_MOUSE6; break; + case 11: event.data1 = KEY_MOUSE7; break; + case 12: event.data1 = KEY_MOUSE8; break; + default: printf("SDL mouse button %s %d\n", + sev.type == SDL_MOUSEBUTTONDOWN ? "down" : "up", sev.button.button); break; + } + if (event.data1 != 0) + { + //DIKState[ActiveDIKState][event.data1] = (event.type == EV_KeyDown); + if (event.data1 == KEY_MWHEELUP || event.data1 == KEY_MWHEELDOWN) + { + WheelMoved(&event); + } + else + { + D_PostEvent(&event); + } } - //DIKState[ActiveDIKState][event.data1] = (event.type == EV_KeyDown); - D_PostEvent (&event); break; case SDL_KEYDOWN: diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index bf3ed803..540d6554 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/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 3bcc3714..0d1c5389 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -805,7 +805,7 @@ bool FMODSoundRenderer::Init() } for (;;) { - result = Sys->init(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS, initflags, 0); + result = Sys->init(MAX(*snd_channels, MAX_CHANNELS), initflags, 0); if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) { // Possible causes of a buffer creation failure: @@ -1435,7 +1435,7 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * GDistScale = distscale; // Experiments indicate that playSound will ignore priorities and always succeed - // as long as the paremeters are set properly. It will first try to kick out sounds + // as long as the parameters are set properly. It will first try to kick out sounds // with the same priority level but has no problem with kicking out sounds at // higher priority levels if it needs to. result = Sys->playSound(FMOD_CHANNEL_FREE, (FMOD::Sound *)sfx.data, true, &chan); @@ -1488,6 +1488,7 @@ FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener * return NULL; } chan->setPaused(false); + chan->getPriority(&def_priority); FISoundChannel *schan = CommonChannelSetup(chan, reuse_chan); schan->Rolloff = *rolloff; return schan; diff --git a/src/svnrevision.h b/src/svnrevision.h index 5725116b..87718b51 100644 --- a/src/svnrevision.h +++ b/src/svnrevision.h @@ -3,5 +3,5 @@ // This file was automatically generated by the // updaterevision tool. Do not edit by hand. -#define ZD_SVN_REVISION_STRING "1369" -#define ZD_SVN_REVISION_NUMBER 1369 +#define ZD_SVN_REVISION_STRING "1401" +#define ZD_SVN_REVISION_NUMBER 1401 diff --git a/src/teaminfo.cpp b/src/teaminfo.cpp index 0e6e3da9..39fe58ee 100644 --- a/src/teaminfo.cpp +++ b/src/teaminfo.cpp @@ -1,9 +1,9 @@ /* ** teaminfo.cpp -** Implementation of the TEAMINFO lump. +** Parses TEAMINFO and manages teams. ** **--------------------------------------------------------------------------- -** Copyright 2007-2008 Christopher Westley +** Copyright 2007-2009 Christopher Westley ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -34,12 +34,12 @@ // HEADER FILES ------------------------------------------------------------ +#include "c_dispatch.h" +#include "gi.h" #include "i_system.h" -#include "sc_man.h" #include "teaminfo.h" -#include "v_video.h" -#include "v_palette.h" #include "v_font.h" +#include "v_video.h" #include "w_wad.h" // MACROS ------------------------------------------------------------------ @@ -50,136 +50,300 @@ // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- -void TEAMINFO_Init (); -void TEAMINFO_ParseTeam (FScanner &sc); - -bool TEAMINFO_IsValidTeam (int team); - // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- // PUBLIC DATA DEFINITIONS ------------------------------------------------- -TArray teams; +FTeam TeamLibrary; +TArray Teams; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static const char *keywords_teaminfo [] = { - "PLAYERCOLOR", - "TEXTCOLOR", - "LOGO", - NULL +static const char *TeamInfoOptions[] = +{ + "Game", + "PlayerColor", + "TextColor", + "Logo", + "AllowCustomPlayerColor", + "RailColor", + "FlagItem", + "SkullItem", + "PlayerStartThingNumber", + "SmallFlagHUDIcon", + "SmallSkullHUDIcon", + "LargeFlagHUDIcon", + "LargeSkullHUDIcon", + "WinnerPic", + "LoserPic", + "WinnerTheme", + "LoserTheme", +}; + +enum ETeamOptions +{ + TEAMINFO_Game, + TEAMINFO_PlayerColor, + TEAMINFO_TextColor, + TEAMINFO_Logo, + TEAMINFO_AllowCustomPlayerColor, + TEAMINFO_RailColor, + TEAMINFO_FlagItem, + TEAMINFO_SkullItem, + TEAMINFO_PlayerStartThingNumber, + TEAMINFO_SmallFlagHUDIcon, + TEAMINFO_SmallSkullHUDIcon, + TEAMINFO_LargeFlagHUDIcon, + TEAMINFO_LargeSkullHUDIcon, + TEAMINFO_WinnerPic, + TEAMINFO_LoserPic, + TEAMINFO_WinnerTheme, + TEAMINFO_LoserTheme, }; // CODE -------------------------------------------------------------------- //========================================================================== // -// TEAMINFO_Init +// FTeam :: FTeam // //========================================================================== -void TEAMINFO_Init () +FTeam::FTeam () { - int lastlump = 0, lump; - - while ((lump = Wads.FindLump ("TEAMINFO", &lastlump)) != -1) - { - FScanner sc(lump); - while (sc.GetString ()) - { - if (sc.Compare("CLEARTEAMS")) - teams.Clear (); - else if (sc.Compare("TEAM")) - TEAMINFO_ParseTeam (sc); - else - sc.ScriptError ("Unknown command %s in TEAMINFO", sc.String); - } - } - - if (teams.Size () < 2) - I_FatalError ("At least two teams must be defined in TEAMINFO"); + m_GameFilter = 0; + m_iPlayerColor = 0; + m_iPlayerCount = 0; + m_iScore = 0; + m_iPresent = 0; + m_iTies = 0; + m_bAllowCustomPlayerColor = false; } //========================================================================== // -// TEAMINFO_ParseTeam +// FTeam :: ParseTeamInfo // //========================================================================== -void TEAMINFO_ParseTeam (FScanner &sc) +void FTeam::ParseTeamInfo () { - TEAMINFO team; - int i; + int iLump, iLastLump = 0; - sc.MustGetString (); - team.name = sc.String; - - sc.MustGetStringName("{"); - while (!sc.CheckString("}")) + while ((iLump = Wads.FindLump ("TEAMINFO", &iLastLump)) != -1) { - sc.MustGetString(); - switch(i = sc.MatchString (keywords_teaminfo)) + FScanner Scan (iLump); + + while (Scan.GetString ()) { - case 0: - sc.MustGetString (); - team.playercolor = V_GetColor (NULL, sc.String); + if (Scan.Compare ("ClearTeams")) + ClearTeams (); + else if (Scan.Compare ("Team")) + ParseTeamDefinition (Scan); + else + Scan.ScriptError ("ParseTeamInfo: Unknown team command '%s'.\n", Scan.String); + } + } + + if (Teams.Size () < 2) + I_FatalError ("ParseTeamInfo: At least two teams must be defined in TEAMINFO."); + else if (Teams.Size () > TEAM_MAXIMUM) + I_FatalError ("ParseTeamInfo: Too many teams defined. (Maximum: %d)", TEAM_MAXIMUM); +} + +//========================================================================== +// +// FTeam :: ParseTeamDefinition +// +//========================================================================== + +void FTeam::ParseTeamDefinition (FScanner &Scan) +{ + FTeam Team; + Scan.MustGetString (); + Team.m_Name = Scan.String; + Scan.MustGetStringName ("{"); + + while (!Scan.CheckString ("}")) + { + Scan.MustGetString (); + + switch (Scan.MatchString (TeamInfoOptions)) + { + case TEAMINFO_Game: + Scan.MustGetString (); + + if (!stricmp (Scan.String, "Doom")) + Team.m_GameFilter |= GAME_Doom; + else if (!stricmp (Scan.String, "Heretic")) + Team.m_GameFilter |= GAME_Heretic; + else if (!stricmp (Scan.String, "Hexen")) + Team.m_GameFilter |= GAME_Hexen; + else if (!stricmp (Scan.String, "Raven")) + Team.m_GameFilter |= GAME_Raven; + else if (!stricmp (Scan.String, "Strife")) + Team.m_GameFilter |= GAME_Strife; + else if (!stricmp (Scan.String, "Chex")) + Team.m_GameFilter |= GAME_Chex; + else if (!stricmp (Scan.String, "Any")) + Team.m_GameFilter |= GAME_Any; + else + Scan.ScriptError ("ParseTeamDefinition: Unknown game type '%s'.\n", Scan.String); break; - case 1: - sc.MustGetString(); - team.textcolor = '['; - team.textcolor << sc.String << ']'; + case TEAMINFO_PlayerColor: + Scan.MustGetString (); + Team.m_iPlayerColor = V_GetColor (NULL, Scan.String); break; - case 2: - sc.MustGetString (); - team.logo = sc.String; + case TEAMINFO_TextColor: + Scan.MustGetString (); + Team.m_TextColor.AppendFormat ("[%s]", Scan.String); + break; + + case TEAMINFO_Logo: + Scan.MustGetString (); + Team.m_Logo = Scan.String; + break; + + case TEAMINFO_AllowCustomPlayerColor: + Team.m_bAllowCustomPlayerColor = true; + break; + + case TEAMINFO_PlayerStartThingNumber: + Scan.MustGetNumber (); + break; + + case TEAMINFO_RailColor: + case TEAMINFO_FlagItem: + case TEAMINFO_SkullItem: + case TEAMINFO_SmallFlagHUDIcon: + case TEAMINFO_SmallSkullHUDIcon: + case TEAMINFO_LargeFlagHUDIcon: + case TEAMINFO_LargeSkullHUDIcon: + case TEAMINFO_WinnerPic: + case TEAMINFO_LoserPic: + case TEAMINFO_WinnerTheme: + case TEAMINFO_LoserTheme: + Scan.MustGetString (); break; default: + Scan.ScriptError ("ParseTeamDefinition: Unknown team option '%s'.\n", Scan.String); break; } } - teams.Push (team); + if (Team.m_GameFilter == 0 || Team.m_GameFilter & gameinfo.gametype) + Teams.Push (Team); } //========================================================================== // -// TEAMINFO_IsValidTeam +// FTeam :: ClearTeams // //========================================================================== -bool TEAMINFO_IsValidTeam (int team) +void FTeam::ClearTeams () { - if (team < 0 || team >= (signed)teams.Size ()) - { + Teams.Clear (); +} + +//========================================================================== +// +// FTeam :: IsValidTeam +// +//========================================================================== + +bool FTeam::IsValidTeam (unsigned int uiTeam) +{ + if (uiTeam < 0 || uiTeam >= Teams.Size ()) return false; - } return true; } //========================================================================== // -// TEAMINFO :: GetTextColor +// FTeam :: GetName // //========================================================================== -int TEAMINFO::GetTextColor () const +const char *FTeam::GetName () const { - if (textcolor.IsEmpty()) + return m_Name; +} + +//========================================================================== +// +// FTeam :: GetPlayerColor +// +//========================================================================== + +int FTeam::GetPlayerColor () const +{ + return m_iPlayerColor; +} + +//========================================================================== +// +// FTeam :: GetTextColor +// +//========================================================================== + +int FTeam::GetTextColor () const +{ + if (m_TextColor.IsEmpty ()) + return CR_UNTRANSLATED; + + const BYTE *pColor = (const BYTE *)m_TextColor.GetChars (); + int iColor = V_ParseFontColor (pColor, 0, 0); + + if (iColor == CR_UNDEFINED) { + Printf ("GetTextColor: Undefined color '%s' in definition of team '%s'.\n", m_TextColor.GetChars (), m_Name.GetChars ()); 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 team %s\n", textcolor.GetChars (), name.GetChars ()); - color = CR_UNTRANSLATED; - } - return color; + + return iColor; +} + +//========================================================================== +// +// FTeam :: GetLogo +// +//========================================================================== + +FString FTeam::GetLogo () const +{ + return m_Logo; +} + +//========================================================================== +// +// FTeam :: GetAllowCustomPlayerColor +// +//========================================================================== + +bool FTeam::GetAllowCustomPlayerColor () const +{ + return m_bAllowCustomPlayerColor; +} + +//========================================================================== +// +// CCMD teamlist +// +//========================================================================== + +CCMD (teamlist) +{ + Printf ("Defined teams are as follows:\n"); + + for (unsigned int uiTeam = 0; uiTeam < Teams.Size (); uiTeam++) + Printf ("%d : %s\n", uiTeam, Teams[uiTeam].GetName ()); + + Printf ("End of team list.\n"); } diff --git a/src/teaminfo.h b/src/teaminfo.h index cacf69fd..f18d63d0 100644 --- a/src/teaminfo.h +++ b/src/teaminfo.h @@ -1,9 +1,9 @@ /* ** teaminfo.h -** Implementation of the TEAMINFO lump. +** Parses TEAMINFO and manages teams. ** **--------------------------------------------------------------------------- -** Copyright 2007-2008 Christopher Westley +** Copyright 2007-2009 Christopher Westley ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -35,28 +35,43 @@ #ifndef __TEAMINFO_H__ #define __TEAMINFO_H__ -#define TEAM_None 255 - #include "doomtype.h" +#include "sc_man.h" -struct TEAMINFO +const int TEAM_NONE = 255; +const int TEAM_MAXIMUM = 16; + +class FTeam { - FString name; - int playercolor; - FString textcolor; - int GetTextColor () const; - FString logo; - int players; - int score; - int present; - int ties; +public: + FTeam (); + void ParseTeamInfo (); + bool IsValidTeam (unsigned int uiTeam); + + const char *GetName () const; + int GetPlayerColor () const; + int GetTextColor () const; + FString GetLogo () const; + bool GetAllowCustomPlayerColor () const; + + int m_iPlayerCount; + int m_iScore; + int m_iPresent; + int m_iTies; + +private: + void ParseTeamDefinition (FScanner &Scan); + void ClearTeams (); + + FString m_Name; + BYTE m_GameFilter; + int m_iPlayerColor; + FString m_TextColor; + FString m_Logo; + bool m_bAllowCustomPlayerColor; }; -extern TArray teams; - -extern void TEAMINFO_Init (); -extern void TEAMINFO_ParseTeam (); - -extern bool TEAMINFO_IsValidTeam (int team); +extern FTeam TeamLibrary; +extern TArray Teams; #endif diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 811bbc03..5510b1c0 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -268,7 +268,7 @@ static void FinishThingdef() // Skip non-actors if (!ti->IsDescendantOf(RUNTIME_CLASS(AActor))) continue; - if (ti->Size == -1) + if (ti->Size == (unsigned)-1) { Printf("Class %s referenced but not defined\n", ti->TypeName.GetChars()); errorcount++; diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 14150d30..611cedc4 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -124,7 +124,7 @@ public: void Copy(int dest, int src, int cnt); int ResolveAll(); FxExpression *Get(int no); - int Size() { return expressions.Size(); } + unsigned int Size() { return expressions.Size(); } }; extern FStateExpressions StateParams; diff --git a/src/version.h b/src/version.h index 2e247216..f86cc08b 100644 --- a/src/version.h +++ b/src/version.h @@ -77,7 +77,7 @@ // SAVESIG should match SAVEVER. // MINSAVEVER is the minimum level snapshot version that can be loaded. -#define MINSAVEVER 1304 +#define MINSAVEVER 1393 #if ZD_SVN_REVISION_NUMBER < MINSAVEVER // Never write a savegame with a version lower than what we need diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index 5eccd87d..64754950 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/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index 89aa6422..e8e6ce55 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -2405,18 +2405,7 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi float u1 = tex->Box->Right; float v1 = tex->Box->Bottom; float uscale = 1.f / tex->Box->Owner->Width; - float vscale = 1.f / tex->Box->Owner->Height / yscale; - - if (y0 < parms.uclip) - { - v0 += (float(parms.uclip) - y0) * vscale; - y0 = float(parms.uclip); - } - if (y1 > parms.dclip) - { - v1 -= (y1 - float(parms.dclip)) * vscale; - y1 = float(parms.dclip); - } + bool scissoring = false; if (parms.flipX) { @@ -2429,16 +2418,45 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi x1 -= (parms.texwidth - parms.windowright) * xscale; u1 -= (parms.texwidth - parms.windowright) * uscale; } +#if 0 + float vscale = 1.f / tex->Box->Owner->Height / yscale; + if (y0 < parms.uclip) + { + v0 += (float(parms.uclip) - y0) * vscale; + y0 = float(parms.uclip); + } + if (y1 > parms.dclip) + { + v1 -= (y1 - float(parms.dclip)) * vscale; + y1 = float(parms.dclip); + } if (x0 < parms.lclip) { - u0 += float(parms.lclip - x0) * uscale / xscale; + u0 += float(parms.lclip - x0) * uscale / xscale * 2; x0 = float(parms.lclip); } if (x1 > parms.rclip) { - u1 -= float(x1 - parms.rclip) * uscale / xscale; + u1 -= (x1 - parms.rclip) * uscale / xscale * 2; x1 = float(parms.rclip); } +#else + // Use a scissor test because the math above introduces some jitter + // that is noticeable at low resolutions. Unfortunately, this means this + // quad has to be in a batch by itself. + if (y0 < parms.uclip || y1 > parms.dclip || x0 < parms.lclip || x1 > parms.rclip) + { + scissoring = true; + if (QuadBatchPos > 0) + { + EndQuadBatch(); + BeginQuadBatch(); + } + RECT scissor = { parms.lclip, parms.uclip, parms.rclip, parms.dclip }; + D3DDevice->SetScissorRect(&scissor); + D3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + } +#endif parms.bilinear = false; D3DCOLOR color0, color1; @@ -2511,6 +2529,12 @@ void STACK_ARGS D3DFB::DrawTextureV (FTexture *img, int x, int y, uint32 tags_fi QuadBatchPos++; VertexPos += 4; IndexPos += 6; + + if (scissoring) + { + EndQuadBatch(); + D3DDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); + } } //========================================================================== diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 4318d95d..9da20744 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 9f392643..d126d607 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/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index 17b1a1ea..127aeb86 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -13,7 +13,7 @@ const int SXF_ABSOLUTEMOMENTUM=8; const int SXF_SETMASTER=16; const int SXF_NOCHECKPOSITION = 32; const int SXF_TELEFRAG=64; -// 128 was uses by Skulltag +const int SXF_CLIENTSPAWN=128; // only used by Skulltag const int SXF_TRANSFERAMBUSHFLAG=256; // Flags for A_Chase diff --git a/wadsrc/static/actors/shared/inventory.txt b/wadsrc/static/actors/shared/inventory.txt index 129eac58..5db058b3 100644 --- a/wadsrc/static/actors/shared/inventory.txt +++ b/wadsrc/static/actors/shared/inventory.txt @@ -327,6 +327,8 @@ Actor Weapon : Inventory native ACTOR WeaponGiver : Weapon native { + Weapon.AmmoGive1 -1 + Weapon.AmmoGive2 -1 } Actor WeaponHolder : Inventory native diff --git a/wadsrc/static/decaldef.txt b/wadsrc/static/decaldef.txt index 46d031e0..2bacce72 100644 --- a/wadsrc/static/decaldef.txt +++ b/wadsrc/static/decaldef.txt @@ -772,6 +772,30 @@ decal CrossbowScorch2 randomflipy } +/***** Phoenix Rod, flamethrower mode **************************************/ + +decal PhoenixThrowerScorch +{ + pic CBALSCR1 + shade "00 00 00" + x-scale 0.4 + y-scale 0.4 + randomflipx + randomflipy +} + +/***** "Horn Rod", rain maker projectile ***********************************/ + +decal HornyRainMaker +{ + pic TWIRL + shade "00 00 00" + x-scale 0.7 + y-scale 0.7 + randomflipx + randomflipy +} + /***** Centaur Scorches ****************************************************/ decal CentaurScorch @@ -990,15 +1014,17 @@ generator GoldWand RailScorchLower generator GoldWandPowered RailScorchLower generator GoldWandFX1 HImpScorch generator CrossbowFX1 CrossbowScorch +generator CrossbowFX2 CrossbowScorch generator CrossbowFX3 CrossbowScorch2 generator MaceFX1 BaronScorch +generator MaceFX4 BFGScorch generator Blaster RailScorchLower generator BlasterFX1 HImpScorch generator Ripper HImpScorch generator HornRodFX1 PlasmaScorchLower -generator HornRodFX2 PlasmaScorchLower +generator HornRodFX2 HornyRainMaker generator PhoenixFX1 Scorch -generator PhoenixFX2 Scorch +generator PhoenixFX2 PhoenixThrowerScorch generator CStaffMissile DoomImpScorch generator HammerMissile Scorch diff --git a/wadsrc/static/graphics/bfgscrc2.png b/wadsrc/static/graphics/bfgscrc2.png index 1f8dcf9c..fab62b87 100644 Binary files a/wadsrc/static/graphics/bfgscrc2.png and b/wadsrc/static/graphics/bfgscrc2.png differ