From 70ee43a95a4035af2a17d9e567e503cf9db8cdc9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 11 Jan 2019 19:31:56 +0100 Subject: [PATCH] - started cleaning up g_level.cpp. This works up to the point where it tries to apply snapshots, it doesn't start the level yet. --- src/d_main.cpp | 2 +- src/d_net.cpp | 11 +- src/dthinker.cpp | 4 +- src/fragglescript/t_cmd.cpp | 8 +- src/fragglescript/t_func.cpp | 4 +- src/fragglescript/t_load.cpp | 2 +- src/fragglescript/t_script.cpp | 2 +- src/g_game.cpp | 16 +- src/g_level.cpp | 646 +++++++++++++----------- src/g_level.h | 36 +- src/g_levellocals.h | 51 +- src/g_mapinfo.cpp | 40 +- src/hwrenderer/dynlights/hw_shadowmap.h | 2 +- src/hwrenderer/utility/hw_lighting.cpp | 6 +- src/p_acs.cpp | 128 +++-- src/p_acs.h | 2 +- src/p_enemy.cpp | 4 +- src/p_interaction.cpp | 2 +- src/p_lnspec.cpp | 14 +- src/p_saveg.cpp | 1 + src/p_setup.cpp | 139 ----- src/p_spec.cpp | 4 +- src/p_tick.cpp | 3 +- src/p_user.cpp | 2 +- src/portal.h | 2 +- src/r_data/r_interpolate.h | 4 + src/resourcefiles/resourcefile.h | 2 +- src/s_sndseq.cpp | 2 +- src/s_sound.cpp | 2 +- src/s_sound.h | 2 +- src/scripting/vmthunks.cpp | 2 +- src/statistics.cpp | 2 +- src/wi_stuff.cpp | 9 +- wadsrc/static/zscript/base.txt | 10 +- wadsrc/static/zscript/mapdata.txt | 13 +- 35 files changed, 548 insertions(+), 631 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 54dd5e00a2..c59b4cbf0b 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -170,7 +170,7 @@ CUSTOM_CVAR (Int, fraglimit, 0, CVAR_SERVERINFO) if (playeringame[i] && self <= D_GetFragCount(&players[i])) { Printf ("%s\n", GStrings("TXT_FRAGLIMIT")); - G_ExitLevel (0, false); + G_ExitLevel (currentSession->Levelinfo[0], 0, false); break; } } diff --git a/src/d_net.cpp b/src/d_net.cpp index 34ac2f1b1e..285c88e8c0 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2228,8 +2228,9 @@ void Net_DoCommand (int type, uint8_t **stream, int player) // That was a long time ago. Maybe it works now? if (currentSession) { - currentSession->Levelinfo[0]->flags |= LEVEL_CHANGEMAPCHEAT; - G_ChangeLevel(s, pos, 0); + auto Level = currentSession->Levelinfo[0]; + Level->flags |= LEVEL_CHANGEMAPCHEAT; + G_ChangeLevel(Level, s, pos, 0); } break; @@ -2667,7 +2668,11 @@ void Net_DoCommand (int type, uint8_t **stream, int player) case DEM_FINISHGAME: // Simulate an end-of-game action - G_ChangeLevel(NULL, 0, 0); + if (currentSession) + { + auto Level = currentSession->Levelinfo[0]; + G_ChangeLevel(Level, nullptr, 0, 0); + } break; case DEM_NETEVENT: diff --git a/src/dthinker.cpp b/src/dthinker.cpp index 674d16e0ed..331ba39438 100644 --- a/src/dthinker.cpp +++ b/src/dthinker.cpp @@ -229,7 +229,7 @@ void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) void DThinker::Serialize(FSerializer &arc) { Super::Serialize(arc); - Level = &level; // Must be written out once a real object. + Level = arc.Level; } //========================================================================== @@ -242,7 +242,7 @@ DThinker::DThinker (int statnum) throw() { NextThinker = NULL; PrevThinker = NULL; - Level = &level; // do this properly later. + Level = bSerialOverride? nullptr : currentSession->Levelinfo[0]; // do this properly later. if (bSerialOverride) { // The serializer will insert us into the right list return; diff --git a/src/fragglescript/t_cmd.cpp b/src/fragglescript/t_cmd.cpp index f3673c2776..44f69c9010 100644 --- a/src/fragglescript/t_cmd.cpp +++ b/src/fragglescript/t_cmd.cpp @@ -77,7 +77,7 @@ static void FS_Gimme(const char * what) // //========================================================================== -void FS_MapCmd(FScanner &sc) +void FS_MapCmd(FLevelLocals *Level, FScanner &sc) { char nextmap[9]; int NextSkill = -1; @@ -108,7 +108,7 @@ void FS_MapCmd(FScanner &sc) flags &= ~(CHANGELEVEL_RESETINVENTORY|CHANGELEVEL_RESETHEALTH); } } - G_ChangeLevel(nextmap, 0, flags, NextSkill); + G_ChangeLevel(Level, nextmap, 0, flags, NextSkill); } //========================================================================== @@ -168,7 +168,7 @@ void FS_EmulateCmd(FLevelLocals *Level, char * string) } else if (sc.Compare("map")) { - FS_MapCmd(sc); + FS_MapCmd(Level, sc); } else if (sc.Compare("gr_fogdensity")) { @@ -177,7 +177,7 @@ void FS_EmulateCmd(FLevelLocals *Level, char * string) Level->fogdensity = sc.Number * 70 / 400; Level->outsidefogdensity = 0; Level->skyfog = 0; - Level->info->outsidefog = 0; + Level->outsidefog = 0; } else if (sc.Compare("gr_fogcolor")) { diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 4214cf7ac4..0a42278396 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -635,7 +635,7 @@ void FParser::SF_Clock(void) void FParser::SF_ExitLevel(void) { - G_ExitLevel(0, false); + G_ExitLevel(Level, 0, false); } //========================================================================== @@ -2853,7 +2853,7 @@ void FParser::SF_AmbientSound(void) void FParser::SF_ExitSecret(void) { - G_SecretExitLevel(0); + G_SecretExitLevel(Level, 0); } diff --git a/src/fragglescript/t_load.cpp b/src/fragglescript/t_load.cpp index 29674fe3b3..8482230d0e 100644 --- a/src/fragglescript/t_load.cpp +++ b/src/fragglescript/t_load.cpp @@ -150,7 +150,7 @@ void FScriptLoader::ParseInfoCmd(char *line, FString &scriptsrc) { sc.MustGetStringName("="); sc.MustGetString(); - Level->info->ExitPic = sc.String; + const_cast(Level->info)->ExitPic = sc.String; } else if (sc.Compare("gravity")) { diff --git a/src/fragglescript/t_script.cpp b/src/fragglescript/t_script.cpp index 2f5b051ddf..a106dda919 100644 --- a/src/fragglescript/t_script.cpp +++ b/src/fragglescript/t_script.cpp @@ -179,7 +179,7 @@ void DFsScript::Serialize(FSerializer &arc) // don't save a reference to the global script, which contains unserializable data. if (parent == Level->FraggleScriptThinker->GlobalScript) parent = nullptr; - if (arc.isReading()) Level = &level; // must later be written out. + if (arc.isReading()) Level = arc.Level; arc("data", data) ("scriptnum", scriptnum) diff --git a/src/g_game.cpp b/src/g_game.cpp index 9d918a601c..794926b09a 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1028,7 +1028,7 @@ void G_Ticker () switch (gameaction) { case ga_loadlevel: - G_DoLoadLevel (-1, false, false); + G_DoLoadLevel (currentSession->nextlevel, -1, false, false); break; case ga_recordgame: G_CheckDemoStatus(); @@ -1103,7 +1103,8 @@ void G_Ticker () uint32_t rngsum = FRandom::StaticSumSeeds (); //Added by MC: For some of that bot stuff. The main bot function. - bglobal.Main (currentSession->Levelinfo[0]); + if (currentSession->Levelinfo.Size() > 0) + bglobal.Main (currentSession->Levelinfo[0]); for (i = 0; i < MAXPLAYERS; i++) { @@ -1925,8 +1926,7 @@ void G_DoLoadGame () NextSkill = -1; arc("nextskill", NextSkill); - if (level.info != nullptr) - level.info->Snapshot.Clean(); + //currentSession->Snapshots.Remove(MapName); fixme BackupSaveName = savename; @@ -2137,7 +2137,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio { // delete the snapshot. Since the save failed it is broken. insave = false; - level.info->Snapshot.Clean(); + //level.info->Snapshot.Clean(); Printf(PRINT_HIGH, "Save failed\n"); Printf(PRINT_HIGH, "%s\n", err.GetMessage()); // The time freeze must be reset if the save fails. @@ -2236,8 +2236,8 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio // delete the JSON buffers we created just above. Everything else will // either still be needed or taken care of automatically. - savegame_content[1].Clean(); - savegame_content[2].Clean(); + //savegame_content[1].Clean(); + //savegame_content[2].Clean(); // Check whether the file is ok by trying to open it. FResourceFile *test = FResourceFile::OpenResourceFile(filename, true); @@ -2253,7 +2253,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio BackupSaveName = filename; // We don't need the snapshot any longer. - level.info->Snapshot.Clean(); + //currentSession->Snapshots.Remove(???); insave = false; diff --git a/src/g_level.cpp b/src/g_level.cpp index 893293355d..df579d2874 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -149,7 +149,6 @@ CUSTOM_CVAR(Int, gl_lightmode, 3, CVAR_ARCHIVE | CVAR_NOINITCALL) static FRandom pr_classchoice ("RandomPlayerClassChoice"); -extern level_info_t TheDefaultLevelInfo; extern bool timingdemo; // Start time for timing demos @@ -166,23 +165,8 @@ extern bool sendpause, sendsave, sendturn180, SendLand; void *statcopy; // for statistics driver -FLevelLocals level; // info about current level - -FGameSession *currentSession; - -// Temporary placeholder to initialize the data until it can be done properly. -class HackyInitializer -{ - FGameSession session; - -public: - HackyInitializer() - { - session.Levelinfo.Push(&level); - currentSession = &session; - } -}; -HackyInitializer hackyinit; +static FGameSession session; // do not use directly. The pointer is more future proof. +FGameSession *currentSession = &session; //========================================================================== // @@ -386,17 +370,20 @@ void G_NewInit () { int i; - // Destory all old player refrences that may still exist - TThinkerIterator it(currentSession->Levelinfo[0], NAME_PlayerPawn, STAT_TRAVELLING); - AActor *pawn, *next; - - next = it.Next(); - while ((pawn = next) != NULL) + ForAllLevels([](FLevelLocals *Level) { + // Destory all old player refrences that may still exist + TThinkerIterator it(Level, NAME_PlayerPawn, STAT_TRAVELLING); + AActor *pawn, *next; + next = it.Next(); - pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP; - pawn->Destroy(); - } + while ((pawn = next) != NULL) + { + next = it.Next(); + pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP; + pawn->Destroy(); + } + }); G_ClearSnapshots (); netgame = false; @@ -483,7 +470,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel) int i; // did we have any level before? - if (currentSession->Levelinfo[0]->info) + if (currentSession->Levelinfo.Size()) E_WorldUnloadedUnsafe(); if (!savegamerestore) @@ -493,8 +480,7 @@ void G_InitNew (const char *mapname, bool bTitleLevel) P_RemoveDefereds (); // [RH] Mark all levels as not visited - for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) - wadlevelinfos[i].flags = wadlevelinfos[i].flags & ~LEVEL_VISITED; + currentSession->Visited.Clear(); } UnlatchCVars (); @@ -543,9 +529,6 @@ void G_InitNew (const char *mapname, bool bTitleLevel) currentSession->time = 0; currentSession->totaltime = 0; - level.maptime = 0; - level.spawnindex = 0; - if (!multiplayer || !deathmatch) { InitPlayerClasses (); @@ -570,7 +553,6 @@ void G_InitNew (const char *mapname, bool bTitleLevel) bglobal.Init (); } - level.MapName = mapname; if (bTitleLevel) { gamestate = GS_TITLELEVEL; @@ -580,14 +562,12 @@ void G_InitNew (const char *mapname, bool bTitleLevel) gamestate = GS_LEVEL; } - G_DoLoadLevel (0, false, !savegamerestore); + G_DoLoadLevel (mapname, 0, false, !savegamerestore); } // // G_DoCompleted // -static FString nextlevel; -static int startpos; // [RH] Support for multiple starts per level extern int NoWipe; // [RH] Don't wipe when travelling in hubs static int changeflags; static bool unloading; @@ -602,10 +582,15 @@ static bool unloading; EXTERN_CVAR(Bool, sv_singleplayerrespawn) -void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill) +void G_ChangeLevel(FLevelLocals *OldLevel, const char *levelname, int position, int flags, int nextSkill) { level_info_t *nextinfo = NULL; + if (OldLevel != currentSession->Levelinfo[0]) + { + // Level exit must be initiated from the primary level. + return; + } if (unloading) { Printf (TEXTCOLOR_RED "Unloading scripts cannot exit the level again.\n"); @@ -616,11 +601,11 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill return; } - auto OldLevel = currentSession->Levelinfo[0]; // Only the primary level is relevant here. - if (levelname == NULL || *levelname == 0) + FString nextlevel; + if (levelname == nullptr || *levelname == 0) { // end the game - levelname = NULL; + levelname = nullptr; if (!OldLevel->NextMap.Compare("enDSeQ",6)) { nextlevel = OldLevel->NextMap; // If there is already an end sequence please leave it alone! @@ -665,7 +650,8 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill cluster_info_t *thiscluster = FindClusterInfo (OldLevel->cluster); cluster_info_t *nextcluster = nextinfo? FindClusterInfo (nextinfo->cluster) : NULL; - startpos = position; + currentSession->nextlevel = nextlevel; + currentSession->nextstartpos = position; gameaction = ga_completed; currentSession->SetMusicVolume(1.0); @@ -738,14 +724,13 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill // //========================================================================== -const char *G_GetExitMap() +const char *G_GetExitMap(FLevelLocals *Level) { - return currentSession->Levelinfo[0]->NextMap; + return Level->NextMap; } -const char *G_GetSecretExitMap() +const char *G_GetSecretExitMap(FLevelLocals *Level) { - auto Level = currentSession->Levelinfo[0]; const char *nextmap = Level->NextMap; if (Level->NextSecretMap.Len() > 0) @@ -764,16 +749,16 @@ const char *G_GetSecretExitMap() // //========================================================================== -void G_ExitLevel (int position, bool keepFacing) +void G_ExitLevel (FLevelLocals *Level, int position, bool keepFacing) { - currentSession->Levelinfo[0]->flags3 |= LEVEL3_EXITNORMALUSED; - G_ChangeLevel(G_GetExitMap(), position, keepFacing ? CHANGELEVEL_KEEPFACING : 0); + Level->flags3 |= LEVEL3_EXITNORMALUSED; + G_ChangeLevel(Level, G_GetExitMap(Level), position, keepFacing ? CHANGELEVEL_KEEPFACING : 0, -1); } -void G_SecretExitLevel (int position) +void G_SecretExitLevel (FLevelLocals *Level, int position) { - currentSession->Levelinfo[0]->flags3 |= LEVEL3_EXITSECRETUSED; - G_ChangeLevel(G_GetSecretExitMap(), position, 0); + Level->flags3 |= LEVEL3_EXITSECRETUSED; + G_ChangeLevel(Level, G_GetSecretExitMap(Level), position, 0, -1); } //========================================================================== @@ -784,7 +769,7 @@ void G_SecretExitLevel (int position) void G_DoCompleted () { int i; - auto Level = &level; + auto Level = currentSession->Levelinfo[0]; gameaction = ga_nothing; @@ -797,16 +782,18 @@ void G_DoCompleted () if (gamestate == GS_TITLELEVEL) { - level.MapName = nextlevel; - G_DoLoadLevel (startpos, false, false); - startpos = 0; + G_DoLoadLevel (currentSession->nextlevel, currentSession->nextstartpos, false, false); + currentSession->nextlevel = ""; + currentSession->nextstartpos = 0; viewactive = true; return; } // [RH] Mark this level as having been visited if (!(Level->flags & LEVEL_CHANGEMAPCHEAT)) - Level->info->flags |= LEVEL_VISITED; + { + currentSession->Visited.Insert(Level->MapName, true); + } if (automapactive) AM_Stop (); @@ -827,10 +814,10 @@ void G_DoCompleted () } else { - level_info_t *nextinfo = FindLevelInfo (nextlevel, false); - if (nextinfo == NULL || strncmp (nextlevel, "enDSeQ", 6) == 0) + level_info_t *nextinfo = FindLevelInfo (currentSession->nextlevel, false); + if (nextinfo == NULL || strncmp (currentSession->nextlevel, "enDSeQ", 6) == 0) { - wminfo.next = nextlevel; + wminfo.next = currentSession->nextlevel; wminfo.LName1.SetInvalid(); } else @@ -841,7 +828,7 @@ void G_DoCompleted () } CheckWarpTransMap (wminfo.next, true); - nextlevel = wminfo.next; + currentSession->nextlevel = wminfo.next; wminfo.next_ep = FindLevelInfo (wminfo.next)->cluster - 1; wminfo.maxkills = Level->total_monsters; @@ -891,7 +878,7 @@ void G_DoCompleted () } // Intermission stats for entire hubs - G_LeavingHub(mode, thiscluster, &wminfo, &level); + G_LeavingHub(mode, thiscluster, &wminfo, Level); for (i = 0; i < MAXPLAYERS; i++) { @@ -914,8 +901,8 @@ void G_DoCompleted () }); } else - { // Make sure we don't have a snapshot lying around from before. (Snapshots are only attached to the primary level!) - Level->info->Snapshot.Clean(); + { // Make sure we don't have a snapshot lying around from before. + currentSession->Snapshots.Remove(Level->MapName); } } else @@ -927,8 +914,6 @@ void G_DoCompleted () P_ClearACSVars(false); } currentSession->time = 0; - level.maptime = 0; - level.spawnindex = 0; } finishstate = mode; @@ -972,6 +957,46 @@ void DAutosaver::Tick () Destroy (); } + +//========================================================================== +// +// +// +//========================================================================== + +void InitGlobalState() +{ + gameaction = ga_nothing; + + // clear cmd building stuff + ResetButtonStates (); + + SendItemUse = NULL; + SendItemDrop = NULL; + mousex = mousey = 0; + sendpause = sendsave = sendturn180 = SendLand = false; + LocalViewAngle = 0; + LocalViewPitch = 0; + paused = 0; + + //Added by MC: Initialize bots. + if (deathmatch) + { + bglobal.Init (); + } + + if (timingdemo) + { + static bool firstTime = true; + + if (firstTime) + { + starttime = I_GetTime (); + firstTime = false; + } + } +} + //========================================================================== // // G_DoLoadLevel @@ -980,10 +1005,15 @@ void DAutosaver::Tick () extern gamestate_t wipegamestate; -void G_DoLoadLevel (int position, bool autosave, bool newGame) -{ +void G_DoLoadLevel (const FString &nextlevel, int position, bool autosave, bool newGame) +{ + auto levelinfo = FindLevelInfo(nextlevel); + TArray MapSet; + + MapSet.Push(levelinfo); + //MapSet.Append(levelinfo->SubLevels); + static int lastposition = 0; - auto &level = *currentSession->Levelinfo[0]; gamestate_t oldgs = gamestate; int i; @@ -1000,24 +1030,26 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame) else lastposition = position; - level.InitLevelLocals (); StatusBar->DetachAllMessages (); - + currentSession->Levelinfo.Clear(); // Delete all old levels. + // Force 'teamplay' to 'true' if need be. - if (level.flags2 & LEVEL2_FORCETEAMPLAYON) + if (levelinfo->flags2 & LEVEL2_FORCETEAMPLAYON) teamplay = true; // Force 'teamplay' to 'false' if need be. - if (level.flags2 & LEVEL2_FORCETEAMPLAYOFF) + if (levelinfo->flags2 & LEVEL2_FORCETEAMPLAYOFF) teamplay = false; - FString mapname = level.MapName; + FString levelname = levelinfo->LookupLevelName(); + FString mapname = levelinfo->MapName; mapname.ToLower(); + 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", - mapname.GetChars(), level.LevelName.GetChars()); + mapname.GetChars(), levelname.GetChars()); if (wipegamestate == GS_LEVEL) wipegamestate = GS_FORCEWIPE; @@ -1034,100 +1066,78 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame) // setting one. skyflatnum = TexMan.GetTextureID (gameinfo.SkyFlatName, ETextureType::Flat, FTextureManager::TEXMAN_Overridable); - // [RH] Set up details about sky rendering - R_InitSkyMap (); - for (i = 0; i < MAXPLAYERS; i++) - { + { if (playeringame[i] && (deathmatch || players[i].playerstate == PST_DEAD)) players[i].playerstate = PST_ENTER; // [BC] - memset (players[i].frags,0,sizeof(players[i].frags)); + memset (players[i].frags, 0, sizeof(players[i].frags)); if (!(dmflags2 & DF2_YES_KEEPFRAGS) && (alwaysapplydmflags || deathmatch)) players[i].fragcount = 0; } - if (changeflags & CHANGELEVEL_NOMONSTERS) + // Set up all needed levels. + for(auto &linfo : MapSet) { - level.flags2 |= LEVEL2_NOMONSTERS; + FLevelLocals *Level = new FLevelLocals; + auto pos = currentSession->Levelinfo.Push(Level); + Level->InitLevelLocals (linfo, pos == 0); + + if (changeflags & CHANGELEVEL_NOMONSTERS) + { + Level->flags2 |= LEVEL2_NOMONSTERS; + } + else + { + Level->flags2 &= ~LEVEL2_NOMONSTERS; + } + if (changeflags & CHANGELEVEL_PRERAISEWEAPON) + { + Level->flags2 |= LEVEL2_PRERAISEWEAPON; + } } - else - { - level.flags2 &= ~LEVEL2_NOMONSTERS; - } - if (changeflags & CHANGELEVEL_PRERAISEWEAPON) - { - level.flags2 |= LEVEL2_PRERAISEWEAPON; - } - - level.maptime = 0; - + if (newGame) { E_NewGame(EventHandlerType::Global); } - P_SetupLevel (&level, level.MapName, position, newGame); - - AM_LevelInit(&level); - - // [RH] Start lightning, if MAPINFO tells us to - if (level.flags & LEVEL_STARTLIGHTNING) + // Load all levels. + ForAllLevels([&](FLevelLocals *Level) { - P_StartLightning (&level); - } + P_SetupLevel (Level, Level->MapName, position, newGame); + AM_LevelInit(Level); - gameaction = ga_nothing; - - // clear cmd building stuff - ResetButtonStates (); - - SendItemUse = NULL; - SendItemDrop = NULL; - mousex = mousey = 0; - sendpause = sendsave = sendturn180 = SendLand = false; - LocalViewAngle = 0; - LocalViewPitch = 0; - paused = 0; - - //Added by MC: Initialize bots. - if (deathmatch) - { - bglobal.Init (); - } - - if (timingdemo) - { - static bool firstTime = true; - - if (firstTime) + // [RH] Start lightning, if MAPINFO tells us to + if (Level->flags & LEVEL_STARTLIGHTNING) { - starttime = I_GetTime (); - firstTime = false; + P_StartLightning (Level); } - } + }); - level.starttime = gametic; + // Init global state after all levels have been loaded. + InitGlobalState(); - G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level. - int pnumerr = G_FinishTravel (); + // Restore the state of the levels + G_UnSnapshotLevel (currentSession->Levelinfo, !savegamerestore); + + int pnumerr = G_FinishTravel (currentSession->Levelinfo[0]); - if (!level.FromSnapshot) + for (int i = 0; iLevel->FromSnapshot) + P_PlayerStartStomp(players[i].mo); } - + // For each player, if they are viewing through a player, make sure it is themselves. - for (int ii = 0; ii < MAXPLAYERS; ++ii) + for (int i = 0; i < MAXPLAYERS; ++i) { - if (playeringame[ii]) + if (playeringame[i]) { - if (players[ii].camera == NULL || players[ii].camera->player != NULL) + if (players[i].camera == nullptr || players[i].camera->player != nullptr) { - players[ii].camera = players[ii].mo; + players[i].camera = players[i].mo; } if (savegamerestore) @@ -1135,29 +1145,37 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame) continue; } - const bool fromSnapshot = level.FromSnapshot; - E_PlayerEntered(ii, fromSnapshot && finishstate == FINISH_SameHub); + auto Level = players[i].mo->Level; + const bool fromSnapshot = Level->FromSnapshot; + E_PlayerEntered(i, fromSnapshot && finishstate == FINISH_SameHub); if (fromSnapshot) { // ENTER scripts are being handled when the player gets spawned, this cannot be changed due to its effect on voodoo dolls. - level.Behaviors.StartTypedScripts(&level, SCRIPT_Return, players[ii].mo, true); + Level->Behaviors.StartTypedScripts(Level, SCRIPT_Return, players[i].mo, true); } } } - if (level.FromSnapshot) + ForAllLevels([&](FLevelLocals *Level) { - // [Nash] run REOPEN scripts upon map re-entry - level.Behaviors.StartTypedScripts(&level, SCRIPT_Reopen, NULL, false); - } + if (Level->FromSnapshot) + { + // [Nash] run REOPEN scripts upon map re-entry + Level->Behaviors.StartTypedScripts(Level, SCRIPT_Reopen, NULL, false); + } + }); StatusBar->AttachToPlayer (&players[consoleplayer]); // unsafe world load E_WorldLoadedUnsafe(); // regular world load (savegames are handled internally) E_WorldLoaded(); - P_DoDeferedScripts (&level); // [RH] Do script actions that were triggered on another map. + + ForAllLevels([&](FLevelLocals *Level) + { + P_DoDeferedScripts (Level); // [RH] Do script actions that were triggered on another map. + }); if (demoplayback || oldgs == GS_STARTUP || oldgs == GS_TITLELEVEL) C_HideConsole (); @@ -1194,6 +1212,8 @@ void G_WorldDone (FLevelLocals *Level) thiscluster = FindClusterInfo (Level->cluster); + auto nextlevel = Level->NextMap; + if (strncmp (nextlevel, "enDSeQ", 6) == 0) { FName endsequence = ENamedName(strtoll(nextlevel.GetChars()+6, NULL, 16)); @@ -1237,7 +1257,7 @@ void G_WorldDone (FLevelLocals *Level) } else { - FExitText *ext = nullptr; + const FExitText *ext = nullptr; if (Level->flags3 & LEVEL3_EXITSECRETUSED) ext = Level->info->ExitMapTexts.CheckKey(NAME_Secret); else if (Level->flags3 & LEVEL3_EXITNORMALUSED) ext = Level->info->ExitMapTexts.CheckKey(NAME_Normal); @@ -1305,18 +1325,16 @@ DEFINE_ACTION_FUNCTION(FGameSession, WorldDone) void G_DoWorldDone (void) { gamestate = GS_LEVEL; - if (wminfo.next[0] == 0) + if (currentSession->nextlevel.IsEmpty()) { - // Don't crash if no next map is given. Just repeat the current one. - Printf ("No next map specified.\n"); - } - else - { - level.MapName = nextlevel; + // Don't crash if no next map is given. + I_Error ("No next map specified.\n"); } G_StartTravel (); - G_DoLoadLevel (startpos, true, false); - startpos = 0; + G_DoLoadLevel (currentSession->nextlevel, currentSession->nextstartpos, true, false); + currentSession->nextlevel = ""; + currentSession->nextstartpos = 0; + gameaction = ga_nothing; viewactive = true; } @@ -1378,9 +1396,9 @@ void G_StartTravel () // //========================================================================== -int G_FinishTravel () +int G_FinishTravel (FLevelLocals *Level) { - TThinkerIterator it (currentSession->Levelinfo[0], NAME_PlayerPawn, STAT_TRAVELLING); + TThinkerIterator it (Level, NAME_PlayerPawn, STAT_TRAVELLING); AActor *pawn, *pawndup, *oldpawn, *next; AActor *inv; FPlayerStart *start; @@ -1400,7 +1418,7 @@ int G_FinishTravel () pawndup = pawn->player->mo; assert (pawn != pawndup); - start = G_PickPlayerStart(&level, pnum, 0); + start = G_PickPlayerStart(Level, pnum, 0); if (start == NULL) { if (pawndup != nullptr) @@ -1419,7 +1437,7 @@ int G_FinishTravel () // The player being spawned here is a short lived dummy and // must not start any ENTER script or big problems will happen. - pawndup = P_SpawnPlayer(pawn->Level, start, pnum, SPF_TEMPPLAYER); + pawndup = P_SpawnPlayer(Level, start, pnum, SPF_TEMPPLAYER); if (pawndup != NULL) { if (!(changeflags & CHANGELEVEL_KEEPFACING)) @@ -1496,11 +1514,72 @@ int G_FinishTravel () //========================================================================== // // +// //========================================================================== -void FLevelLocals::InitLevelLocals () +FLevelLocals::~FLevelLocals() { - info = FindLevelInfo(MapName); + SN_StopAllSequences(this); + ClearAllSubsectorLinks(); // can't be done as part of the polyobj deletion process. + DThinker::DestroyAllThinkers(); + + // delete allocated data in the level arrays. + if (sectors.Size() > 0) + { + delete[] sectors[0].e; + } + for (auto &sub : subsectors) + { + if (sub.BSP != nullptr) delete sub.BSP; + } + + // also clear the render data + for (auto &sub : subsectors) + { + for (int j = 0; j < 2; j++) + { + if (sub.portalcoverage[j].subsectors != nullptr) + { + delete[] sub.portalcoverage[j].subsectors; + sub.portalcoverage[j].subsectors = nullptr; + } + } + } + + for (auto &pb : PolyBlockMap) + { + polyblock_t *link = pb; + while (link != nullptr) + { + polyblock_t *next = link->next; + delete link; + link = next; + } + } +} + + +//========================================================================== +// +// +//========================================================================== + +void FLevelLocals::InitLevelLocals (const level_info_t *info, bool isprimary) +{ + this->info = info; + MapName = info->MapName; + + // Set up the two default portals. + sectorPortals.Resize(2); + // The first entry must always be the default skybox. This is what every sector gets by default. + memset(§orPortals[0], 0, sizeof(sectorPortals[0])); + sectorPortals[0].mType = PORTS_SKYVIEWPOINT; + sectorPortals[0].mFlags = PORTSF_SKYFLATONLY; + // The second entry will be the default sky. This is for forcing a regular sky through the skybox picker + memset(§orPortals[1], 0, sizeof(sectorPortals[0])); + sectorPortals[1].mType = PORTS_SKYVIEWPOINT; + sectorPortals[1].mFlags = PORTSF_SKYFLATONLY; + // Session data should be moved out of here later! currentSession->F1Pic = info->F1Pic; @@ -1514,17 +1593,21 @@ void FLevelLocals::InitLevelLocals () gravity = sv_gravity * 35/TICRATE; aircontrol = sv_aircontrol; teamdamage = ::teamdamage; - flags = 0; - flags2 = 0; - flags3 = 0; + flags = info->flags; + flags2 = info->flags2; + flags3 = info->flags3; freeze = false; changefreeze = false; + maptime = 0; + spawnindex = 0; + starttime = gametic; skyspeed1 = info->skyspeed1; skyspeed2 = info->skyspeed2; skytexture1 = TexMan.GetTextureID(info->SkyPic1, ETextureType::Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst); skytexture2 = TexMan.GetTextureID(info->SkyPic2, ETextureType::Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst); + InitSkyMap(this); fadeto = info->fadeto; FromSnapshot = false; if (fadeto == 0) @@ -1555,9 +1638,6 @@ void FLevelLocals::InitLevelLocals () sucktime = info->sucktime; cluster = info->cluster; clusterflags = clus ? clus->flags : 0; - flags |= info->flags; - flags2 |= info->flags2; - flags3 |= info->flags3; levelnum = info->levelnum; Music = info->Music; musicorder = info->musicorder; @@ -1577,6 +1657,7 @@ void FLevelLocals::InitLevelLocals () pixelstretch = info->pixelstretch; + // Fixme: This should not process all other levels again. compatflags.Callback(); compatflags2.Callback(); @@ -1588,7 +1669,7 @@ void FLevelLocals::InitLevelLocals () notexturefill = info->notexturefill < 0 ? gl_notexturefill : !!info->notexturefill; // This may only be done for the primary level in a set! - FLightDefaults::SetAttenuationForLevel(this); + if (isprimary) FLightDefaults::SetAttenuationForLevel(this); } //========================================================================== @@ -1693,6 +1774,30 @@ void FLevelLocals::ChangeAirControl(double newval) } } +//========================================================================== +// +// +//========================================================================== + +void G_ClearSnapshots (void) +{ + currentSession->ClearSnapshots(); + // Since strings are only locked when snapshotting a level, unlock them + // all now, since we got rid of all the snapshots that cared about them. + GlobalACSStrings.UnlockAll(); +} + +//========================================================================== +// +// Remove any existing defereds +// +//========================================================================== + +void P_RemoveDefereds (void) +{ + currentSession->ClearDefered(); +} + //========================================================================== // // Archives the current level @@ -1701,21 +1806,22 @@ void FLevelLocals::ChangeAirControl(double newval) void G_SnapshotLevel () { - level.info->Snapshot.Clean(); - - if (level.info->isValid()) + // Create snapshots of all active levels. + ForAllLevels([](FLevelLocals *Level) { + // first remove the old snapshot, if it exists. + currentSession->Snapshots.Remove(Level->MapName); + FSerializer arc; - if (arc.OpenWriter(save_formatted)) { SaveVersion = SAVEVER; - arc.SetLevel(&level); - G_SerializeLevel(arc, &level, false); - level.info->Snapshot = arc.GetCompressedOutput(); + arc.SetLevel(Level); + G_SerializeLevel(arc, Level, false); + currentSession->Snapshots.Insert(Level->MapName, arc.GetCompressedOutput()); arc.SetLevel(nullptr); } - } + }); } //========================================================================== @@ -1725,58 +1831,61 @@ void G_SnapshotLevel () // //========================================================================== -void G_UnSnapshotLevel (bool hubLoad) +void G_UnSnapshotLevel (const TArray &levels, bool hubLoad) { - if (level.info->Snapshot.mBuffer == nullptr) - return; - - if (level.info->isValid()) + for (auto &Level : levels) { - FSerializer arc; - if (!arc.OpenReader(&level.info->Snapshot)) + auto snapshot = currentSession->Snapshots.CheckKey(Level->MapName); + if (snapshot == nullptr) { - I_Error("Failed to load savegame"); + // should be handled by the caller when reading in the savegame. + I_Error("Bad list of levels passed to G_UnSnapshotLevel"); return; } - arc.SetLevel(&level); - G_SerializeLevel (arc, &level, hubLoad); - arc.SetLevel(nullptr); - level.FromSnapshot = true; - - TThinkerIterator it(currentSession->Levelinfo[0], NAME_PlayerPawn); - AActor *pawn, *next; - - next = it.Next(); - while ((pawn = next) != 0) + if (Level->info->isValid()) { - next = it.Next(); - if (pawn->player == NULL || pawn->player->mo == NULL || !playeringame[pawn->player - players]) + FSerializer arc; + if (!arc.OpenReader(snapshot)) { - int i; + I_Error("Failed to load savegame"); + return; + } + + arc.SetLevel(Level); + G_SerializeLevel (arc, Level, hubLoad); + arc.SetLevel(nullptr); + Level->FromSnapshot = true; - // If this isn't the unmorphed original copy of a player, destroy it, because it's extra. - for (i = 0; i < MAXPLAYERS; ++i) + TThinkerIterator it(Level, NAME_PlayerPawn); + AActor *pawn; + + while ((pawn = it.Next()) != 0) + { + if (pawn->player == NULL || pawn->player->mo == NULL || !playeringame[pawn->player - players]) { - if (playeringame[i] && players[i].morphTics && players[i].mo->alternative == pawn) + int i; + + // If this isn't the unmorphed original copy of a player, destroy it, because it's extra. + for (i = 0; i < MAXPLAYERS; ++i) { - break; + if (playeringame[i] && players[i].morphTics && players[i].mo->alternative == pawn) + { + break; + } + } + if (i == MAXPLAYERS) + { + pawn->Destroy (); } } - if (i == MAXPLAYERS) - { - pawn->Destroy (); - } } + arc.Close(); } - arc.Close(); - } - // No reason to keep the snapshot around once the level's been entered. - level.info->Snapshot.Clean(); - if (hubLoad) - { + // No reason to keep the snapshot around once the level's been entered. + currentSession->Snapshots.Remove(Level->MapName); // Unlock ACS global strings that were locked when the snapshot was made. - level.Behaviors.UnlockLevelVarStrings(level.levelnum); + Level->Behaviors.UnlockLevelVarStrings(Level->levelnum); } } @@ -1787,26 +1896,19 @@ void G_UnSnapshotLevel (bool hubLoad) void G_WriteSnapshots(TArray &filenames, TArray &buffers) { - unsigned int i; - FString filename; - - for (i = 0; i < wadlevelinfos.Size(); i++) + decltype(currentSession->Snapshots)::Iterator it(currentSession->Snapshots); + decltype(currentSession->Snapshots)::Pair *pair; + + while (it.NextPair(pair)) { - if (wadlevelinfos[i].Snapshot.mCompressedSize > 0) + if (pair->Value.mCompressedSize > 0) { - filename.Format("%s.map.json", wadlevelinfos[i].MapName.GetChars()); + FStringf filename("%s.map.json", pair->Key.GetChars()); filename.ToLower(); filenames.Push(filename); - buffers.Push(wadlevelinfos[i].Snapshot); + buffers.Push(pair->Value); } } - if (TheDefaultLevelInfo.Snapshot.mCompressedSize > 0) - { - filename.Format("%s.mapd.json", TheDefaultLevelInfo.MapName.GetChars()); - filename.ToLower(); - filenames.Push(filename); - buffers.Push(TheDefaultLevelInfo.Snapshot); - } } //========================================================================== @@ -1816,15 +1918,15 @@ void G_WriteSnapshots(TArray &filenames, TArray &buf void G_WriteVisited(FSerializer &arc) { + decltype(currentSession->Visited)::Iterator it(currentSession->Visited); + decltype(currentSession->Visited)::Pair *pair; + if (arc.BeginArray("visited")) { - // Write out which levels have been visited - for (auto & wi : wadlevelinfos) + while (it.NextPair(pair)) { - if (wi.flags & LEVEL_VISITED) - { - arc.AddString(nullptr, wi.MapName); - } + // Write out which levels have been visited + arc.AddString(nullptr, pair->Key); } arc.EndArray(); } @@ -1857,9 +1959,6 @@ void G_WriteVisited(FSerializer &arc) void G_ReadSnapshots(FResourceFile *resf) { - FString MapName; - level_info_t *i; - G_ClearSnapshots(); for (unsigned j = 0; j < resf->LumpCount(); j++) @@ -1871,22 +1970,8 @@ void G_ReadSnapshots(FResourceFile *resf) if (ptr != nullptr) { ptrdiff_t maplen = ptr - resl->FullName.GetChars(); - FString mapname(resl->FullName.GetChars(), (size_t)maplen); - i = FindLevelInfo(mapname); - if (i != nullptr) - { - i->Snapshot = resl->GetRawData(); - } - } - else - { - auto ptr = strstr(resl->FullName, ".mapd.json"); - if (ptr != nullptr) - { - ptrdiff_t maplen = ptr - resl->FullName.GetChars(); - FString mapname(resl->FullName.GetChars(), (size_t)maplen); - TheDefaultLevelInfo.Snapshot = resl->GetRawData(); - } + FName mapname(resl->FullName.GetChars(), (size_t)maplen); + currentSession->Snapshots.Insert(mapname, resl->GetRawData()); } } } @@ -1905,8 +1990,7 @@ void G_ReadVisited(FSerializer &arc) { FString str; arc(nullptr, str); - auto i = FindLevelInfo(str); - if (i != nullptr) i->flags |= LEVEL_VISITED; + currentSession->Visited.Insert(str, true); } arc.EndArray(); } @@ -1932,12 +2016,15 @@ void G_ReadVisited(FSerializer &arc) CCMD(listsnapshots) { - for (unsigned i = 0; i < wadlevelinfos.Size(); ++i) + decltype(currentSession->Snapshots)::Iterator it(currentSession->Snapshots); + decltype(currentSession->Snapshots)::Pair *pair; + + while (it.NextPair(pair)) { - FCompressedBuffer *snapshot = &wadlevelinfos[i].Snapshot; + FCompressedBuffer *snapshot = &pair->Value; if (snapshot->mBuffer != nullptr) { - Printf("%s (%u -> %u bytes)\n", wadlevelinfos[i].MapName.GetChars(), snapshot->mCompressedSize, snapshot->mSize); + Printf("%s (%u -> %u bytes)\n", pair->Key.GetChars(), snapshot->mCompressedSize, snapshot->mSize); } } } @@ -1949,31 +2036,18 @@ CCMD(listsnapshots) void P_WriteACSDefereds (FSerializer &arc) { - bool found = false; + if (currentSession->DeferredScripts.CountUsed() == 0) return; + decltype(currentSession->DeferredScripts)::Iterator it(currentSession->DeferredScripts); + decltype(currentSession->DeferredScripts)::Pair *pair; - // only write this stuff if needed - for (auto &wi : wadlevelinfos) + if (arc.BeginObject("deferred")) { - if (wi.deferred.Size() > 0) + while (it.NextPair(pair)) { - found = true; - break; + arc(pair->Key, pair->Value); } } - if (found && arc.BeginObject("deferred")) - { - for (auto &wi : wadlevelinfos) - { - if (wi.deferred.Size() > 0) - { - if (wi.deferred.Size() > 0) - { - arc(wi.MapName, wi.deferred); - } - } - } - arc.EndObject(); - } + arc.EndObject(); } //========================================================================== @@ -1993,18 +2067,14 @@ void P_ReadACSDefereds (FSerializer &arc) while ((key = arc.GetKey())) { - level_info_t *i = FindLevelInfo(key); - if (i == NULL) - { - I_Error("Unknown map '%s' in savegame", key); - } - arc(nullptr, i->deferred); + TArray deferred; + arc(nullptr, deferred); + currentSession->DeferredScripts.Insert(key, std::move(deferred)); } arc.EndObject(); } } - //========================================================================== // // This object is responsible for marking sectors during the propagate diff --git a/src/g_level.h b/src/g_level.h index 5f2275777c..e657296bb7 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -42,6 +42,7 @@ struct level_info_t; struct cluster_info_t; class FSerializer; +struct FLevelLocals; #if defined(_MSC_VER) #pragma section(".yreg$u",read) @@ -191,7 +192,7 @@ enum ELevelFlags : unsigned int 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 + //LEVEL_VISITED = 0x80000000, // Used for intermission map // The flags uint64_t is now split into 2 DWORDs LEVEL2_RANDOMPLAYERSTARTS = 0x00000001, // Select single player starts randomnly (no voodoo dolls) @@ -325,8 +326,6 @@ struct level_info_t FString LevelName; int8_t WallVertLight, WallHorizLight; int musicorder; - FCompressedBuffer Snapshot; - TArray deferred; float skyspeed1; float skyspeed2; uint32_t fadeto; @@ -393,19 +392,10 @@ struct level_info_t { Reset(); } - ~level_info_t() - { - Snapshot.Clean(); - ClearDefered(); - } void Reset(); - bool isValid(); - FString LookupLevelName (); - void ClearDefered() - { - deferred.Clear(); - } - level_info_t *CheckLevelRedirect (); + bool isValid() const; + FString LookupLevelName () const; + level_info_t *CheckLevelRedirect () const; }; @@ -454,10 +444,10 @@ void G_DeferedInitNew (const char *mapname, int skill = -1); struct FGameStartup; void G_DeferedInitNew (FGameStartup *gs); -void G_ExitLevel (int position, bool keepFacing); -void G_SecretExitLevel (int position); -const char *G_GetExitMap(); -const char *G_GetSecretExitMap(); +void G_ExitLevel (FLevelLocals *Level, int position, bool keepFacing); +void G_SecretExitLevel (FLevelLocals *Level, int position); +const char *G_GetExitMap(FLevelLocals *Level); +const char *G_GetSecretExitMap(FLevelLocals *Level); enum { @@ -470,12 +460,12 @@ enum CHANGELEVEL_PRERAISEWEAPON = 64, }; -void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill=-1); +void G_ChangeLevel(FLevelLocals *Level, const char *levelname, int position, int flags, int nextSkill=-1); void G_StartTravel (); -int G_FinishTravel (); +int G_FinishTravel (FLevelLocals *); -void G_DoLoadLevel (int position, bool autosave, bool newGame); +void G_DoLoadLevel (const FString &mapname, int position, bool autosave, bool newGame); void G_InitLevelLocals (void); @@ -493,7 +483,7 @@ void G_ParseMapInfo (FString basemapinfo); void G_ClearSnapshots (void); void P_RemoveDefereds (); void G_SnapshotLevel (void); -void G_UnSnapshotLevel (bool keepPlayers); +void G_UnSnapshotLevel(const TArray &levels, bool hubLoad); void G_ReadSnapshots (FResourceFile *); void G_WriteSnapshots (TArray &, TArray &); void G_WriteVisited(FSerializer &arc); diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 4db0dbe896..313c040033 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -47,6 +47,7 @@ #include "p_tags.h" #include "p_effect.h" #include "p_destructible.h" +#include "p_conversation.h" #include "r_data/r_interpolate.h" #include "r_data/r_sections.h" #include "r_data/r_canvastexture.h" @@ -88,7 +89,7 @@ struct FLevelData FDisplacementTable Displacements; FPortalBlockmap PortalBlockmap; TArray linkedPortals; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups. - TArray portalGroups; + TDeletingArray portalGroups; TArray linePortalSpans; FSectionContainer sections; FCanvasTextureInfo canvasTextureInfo; @@ -112,7 +113,7 @@ struct FLevelData FBehaviorContainer Behaviors; - TArray StrifeDialogues; + TDeletingArray StrifeDialogues; FDialogueIDMap DialogueRoots; FDialogueMap ClassRoots; @@ -129,7 +130,16 @@ struct FLevelData struct FLevelLocals : public FLevelData { + void *operator new(size_t blocksize) + { + // Null the allocated memory before running the constructor. + // This was previously static memory that relied on being nulled to avoid uninitialized parts. + auto block = ::operator new(blocksize); + memset(block, 0, blocksize); + return block; + } FLevelLocals() : tagManager(this) {} + ~FLevelLocals(); void Tick(); void Mark(); @@ -137,13 +147,12 @@ struct FLevelLocals : public FLevelData void SetInterMusic(const char *nextmap); void SetMusic(); void ClearLevelData(); - void ClearPortals(); bool CheckIfExitIsGood(AActor *self, level_info_t *newmap); void FormatMapName(FString &mapname, const char *mapnamecolor); void TranslateTeleportThings(void); void ClearAllSubsectorLinks(); void ChangeAirControl(double newval); - void InitLevelLocals(); + void InitLevelLocals(const level_info_t *info, bool isprimary); bool IsTIDUsed(int tid); int FindUniqueTID(int start_tid, int limit); int GetConversation(int conv_id); @@ -175,7 +184,7 @@ struct FLevelLocals : public FLevelData int sucktime; uint32_t spawnindex; - level_info_t *info; + const level_info_t * info; // The info is supposed to be immutable. int cluster; int clusterflags; int levelnum; @@ -299,8 +308,8 @@ struct FLevelLocals : public FLevelData // Returns true if level is loaded from saved game or is being revisited as a part of a hub bool IsReentering() const { - return savegamerestore - || (info != nullptr && info->Snapshot.mBuffer != nullptr && info->isValid()); + // This is actually very simple: A reentered map never has a map time of 0 when it starts. If the map time is 0 it must be a freshly loaded instance. + return maptime > 0; } }; @@ -309,21 +318,39 @@ class FGameSession { public: TArray Levelinfo; + + TMap Snapshots; + TMap> DeferredScripts; + TMap Visited; + FString F1Pic; float MusicVolume; int time; // time in the hub int totaltime; // time in the game + + FString nextlevel; // Level to go to on exit + int nextstartpos; // [RH] Support for multiple starts per level + void SetMusicVolume(float vol); + + void Reset(); + bool isValid(); + FString LookupLevelName (); + void ClearDefered() + { + DeferredScripts.Clear(); + } + void ClearSnapshots() + { + Snapshots.Clear(); + } + }; extern FGameSession *currentSession; -#ifndef NO_DEFINE_LEVEL - -extern FLevelLocals level; - inline FSectorPortal *line_t::GetTransferredPortal() { auto Level = GetLevel(); @@ -430,5 +457,3 @@ inline void ForAllLevels(T func) for (auto Level : currentSession->Levelinfo) func(Level); } } - -#endif diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 7b1435b7aa..e9ae0b9fb1 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -184,37 +184,6 @@ cluster_info_t *FindClusterInfo (int cluster) return &TheDefaultClusterInfo; } -//========================================================================== -// -// -//========================================================================== - -void G_ClearSnapshots (void) -{ - for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) - { - wadlevelinfos[i].Snapshot.Clean(); - } - // Since strings are only locked when snapshotting a level, unlock them - // all now, since we got rid of all the snapshots that cared about them. - GlobalACSStrings.UnlockAll(); -} - -//========================================================================== -// -// Remove any existing defereds -// -//========================================================================== - -void P_RemoveDefereds (void) -{ - for (unsigned int i = 0; i < wadlevelinfos.Size(); i++) - { - wadlevelinfos[i].ClearDefered(); - } -} - - //========================================================================== // // @@ -245,8 +214,6 @@ void level_info_t::Reset() WallVertLight = +8; F1Pic = ""; musicorder = 0; - Snapshot = { 0,0,0,0,0,nullptr }; - deferred.Clear(); skyspeed1 = skyspeed2 = 0.f; fadeto = 0; outsidefog = 0xff000000; @@ -298,7 +265,7 @@ void level_info_t::Reset() // //========================================================================== -FString level_info_t::LookupLevelName() +FString level_info_t::LookupLevelName() const { if (flags & LEVEL_LOOKUPLEVELNAME) { @@ -353,7 +320,7 @@ FString level_info_t::LookupLevelName() // //========================================================================== -level_info_t *level_info_t::CheckLevelRedirect () +level_info_t *level_info_t::CheckLevelRedirect () const { if (RedirectType != NAME_None) { @@ -382,7 +349,7 @@ level_info_t *level_info_t::CheckLevelRedirect () // //========================================================================== -bool level_info_t::isValid() +bool level_info_t::isValid() const { return MapName.Len() != 0 || this == &TheDefaultLevelInfo; } @@ -2284,7 +2251,6 @@ static void ClearMapinfo() AllSkills.Clear(); DefaultSkill = -1; DeinitIntermissions(); - level.info = NULL; currentSession->F1Pic = ""; } diff --git a/src/hwrenderer/dynlights/hw_shadowmap.h b/src/hwrenderer/dynlights/hw_shadowmap.h index 63c5b2a9ee..704d5074bc 100644 --- a/src/hwrenderer/dynlights/hw_shadowmap.h +++ b/src/hwrenderer/dynlights/hw_shadowmap.h @@ -46,7 +46,7 @@ protected: TArray mLights; // Used to detect when a level change requires the AABB tree to be regenerated - level_info_t *mLastLevel = nullptr; + const level_info_t *mLastLevel = nullptr; unsigned mLastNumNodes = 0; unsigned mLastNumSegs = 0; diff --git a/src/hwrenderer/utility/hw_lighting.cpp b/src/hwrenderer/utility/hw_lighting.cpp index 5660430585..78f80c26da 100644 --- a/src/hwrenderer/utility/hw_lighting.cpp +++ b/src/hwrenderer/utility/hw_lighting.cpp @@ -200,7 +200,7 @@ float HWDrawInfo::GetFogDensity(int lightlevel, PalEntry fogcolor, int sectorfog density = 0; } } - else if (Level->outsidefogdensity != 0 && APART(Level->info->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->info->outsidefog & 0xffffff)) + else if (Level->outsidefogdensity != 0 && APART(Level->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->outsidefog & 0xffffff)) { // case 3. outsidefogdensity has already been set as needed density = (float)Level->outsidefogdensity; @@ -248,7 +248,7 @@ bool HWDrawInfo::CheckFog(sector_t *frontsector, sector_t *backsector) else if (fogcolor.a != 0) { } - else if (Level->outsidefogdensity != 0 && APART(Level->info->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->info->outsidefog & 0xffffff)) + else if (Level->outsidefogdensity != 0 && APART(Level->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->outsidefog & 0xffffff)) { } else if (Level->fogdensity!=0 || lightmode == ELightMode::DoomLegacy) @@ -266,7 +266,7 @@ bool HWDrawInfo::CheckFog(sector_t *frontsector, sector_t *backsector) if ((fogcolor.d & 0xffffff) == 0) { } - else if (Level->outsidefogdensity != 0 && APART(Level->info->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->info->outsidefog & 0xffffff)) + else if (Level->outsidefogdensity != 0 && APART(Level->outsidefog) != 0xff && (fogcolor.d & 0xffffff) == (Level->outsidefog & 0xffffff)) { return false; } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 089a2a2357..b123f6f8e8 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1988,11 +1988,7 @@ bool FBehaviorContainer::CheckAllGood () void FBehaviorContainer::UnloadModules () { - for (unsigned int i = StaticModules.Size(); i-- > 0; ) - { - delete StaticModules[i]; - } - StaticModules.Clear (); + StaticModules.DeleteAndClear (); } FBehavior *FBehaviorContainer::GetModule (int lib) @@ -3447,8 +3443,7 @@ void DLevelScript::Serialize(FSerializer &arc) uint32_t pcofs; uint16_t lib; - // This cannot be serialized yet. - if(arc.isReading()) Level = &level; + if(arc.isReading()) Level = arc.Level; if (arc.isWriting()) { @@ -9928,7 +9923,7 @@ scriptwait: case PCD_CHANGELEVEL: { - G_ChangeLevel(Level->Behaviors.LookupString(STACK(4)), STACK(3), STACK(2), STACK(1)); + G_ChangeLevel(Level, Level->Behaviors.LookupString(STACK(4)), STACK(3), STACK(2), STACK(1)); sp -= 4; } break; @@ -10321,70 +10316,73 @@ void P_DoDeferedScripts (FLevelLocals *Level) FBehavior *module; // Handle defered scripts in this step, too - for(int i = Level->info->deferred.Size()-1; i>=0; i--) + auto deferred = currentSession->DeferredScripts.CheckKey(Level->MapName); + if (deferred) { - acsdefered_t *def = &Level->info->deferred[i]; - switch (def->type) + for(int i = deferred->Size()-1; i>=0; i--) { - case acsdefered_t::defexecute: - case acsdefered_t::defexealways: - scriptdata = Level->Behaviors.FindScript (def->script, module); - if (scriptdata) + acsdefered_t *def = &(*deferred)[i]; + switch (def->type) { - P_GetScriptGoing (Level, (unsigned)def->playernum < MAXPLAYERS && - playeringame[def->playernum] ? players[def->playernum].mo : NULL, - NULL, def->script, - scriptdata, module, - def->args, 3, - def->type == acsdefered_t::defexealways ? ACS_ALWAYS : 0); + case acsdefered_t::defexecute: + case acsdefered_t::defexealways: + scriptdata = Level->Behaviors.FindScript (def->script, module); + if (scriptdata) + { + P_GetScriptGoing (Level, (unsigned)def->playernum < MAXPLAYERS && + playeringame[def->playernum] ? players[def->playernum].mo : NULL, + NULL, def->script, + scriptdata, module, + def->args, 3, + def->type == acsdefered_t::defexealways ? ACS_ALWAYS : 0); + } + else + { + Printf ("P_DoDeferredScripts: Unknown %s\n", ScriptPresentation(def->script).GetChars()); + } + break; + + case acsdefered_t::defsuspend: + SetScriptState (Level->ACSThinker, def->script, DLevelScript::SCRIPT_Suspended); + DPrintf (DMSG_SPAMMY, "Deferred suspend of %s\n", ScriptPresentation(def->script).GetChars()); + break; + + case acsdefered_t::defterminate: + SetScriptState (Level->ACSThinker, def->script, DLevelScript::SCRIPT_PleaseRemove); + DPrintf (DMSG_SPAMMY, "Deferred terminate of %s\n", ScriptPresentation(def->script).GetChars()); + break; } - else - { - Printf ("P_DoDeferredScripts: Unknown %s\n", ScriptPresentation(def->script).GetChars()); - } - break; - - case acsdefered_t::defsuspend: - SetScriptState (Level->ACSThinker, def->script, DLevelScript::SCRIPT_Suspended); - DPrintf (DMSG_SPAMMY, "Deferred suspend of %s\n", ScriptPresentation(def->script).GetChars()); - break; - - case acsdefered_t::defterminate: - SetScriptState (Level->ACSThinker, def->script, DLevelScript::SCRIPT_PleaseRemove); - DPrintf (DMSG_SPAMMY, "Deferred terminate of %s\n", ScriptPresentation(def->script).GetChars()); - break; } } - Level->info->deferred.Clear(); + currentSession->DeferredScripts.Remove(Level->MapName); } -static void addDefered (level_info_t *i, acsdefered_t::EType type, int script, const int *args, int argcount, AActor *who) +static void addDefered (FLevelLocals *Level, acsdefered_t::EType type, int script, const int *args, int argcount, AActor *who) { - if (i) + auto &deferred = currentSession->DeferredScripts[Level->MapName]; + + acsdefered_t &def = deferred[deferred.Reserve(1)]; + int j; + + def.type = type; + def.script = script; + for (j = 0; (size_t)j < countof(def.args) && j < argcount; ++j) { - acsdefered_t &def = i->deferred[i->deferred.Reserve(1)]; - int j; - - def.type = type; - def.script = script; - for (j = 0; (size_t)j < countof(def.args) && j < argcount; ++j) - { - def.args[j] = args[j]; - } - while ((size_t)j < countof(def.args)) - { - def.args[j++] = 0; - } - if (who != NULL && who->player != NULL) - { - def.playernum = int(who->player - players); - } - else - { - def.playernum = -1; - } - DPrintf (DMSG_SPAMMY, "%s on map %s deferred\n", ScriptPresentation(script).GetChars(), i->MapName.GetChars()); + def.args[j] = args[j]; } + while ((size_t)j < countof(def.args)) + { + def.args[j++] = 0; + } + if (who != NULL && who->player != NULL) + { + def.playernum = int(who->player - players); + } + else + { + def.playernum = -1; + } + DPrintf (DMSG_SPAMMY, "%s on map %s deferred\n", ScriptPresentation(script).GetChars(), Level->MapName.GetChars()); } EXTERN_CVAR (Bool, sv_cheats) @@ -10436,9 +10434,7 @@ int P_StartScript (FLevelLocals *Level, AActor *who, line_t *where, int script, } else { - addDefered (FindLevelInfo (map), - (flags & ACS_ALWAYS) ? acsdefered_t::defexealways : acsdefered_t::defexecute, - script, args, argcount, who); + addDefered (Level, (flags & ACS_ALWAYS) ? acsdefered_t::defexealways : acsdefered_t::defexecute, script, args, argcount, who); return true; } return false; @@ -10447,7 +10443,7 @@ int P_StartScript (FLevelLocals *Level, AActor *who, line_t *where, int script, void P_SuspendScript (FLevelLocals *Level, int script, const char *map) { if (strnicmp (Level->MapName, map, 8)) - addDefered (FindLevelInfo (map), acsdefered_t::defsuspend, script, NULL, 0, NULL); + addDefered (Level, acsdefered_t::defsuspend, script, NULL, 0, NULL); else SetScriptState (Level->ACSThinker, script, DLevelScript::SCRIPT_Suspended); } @@ -10455,7 +10451,7 @@ void P_SuspendScript (FLevelLocals *Level, int script, const char *map) void P_TerminateScript (FLevelLocals *Level, int script, const char *map) { if (strnicmp (Level->MapName, map, 8)) - addDefered (FindLevelInfo (map), acsdefered_t::defterminate, script, NULL, 0, NULL); + addDefered (Level, acsdefered_t::defterminate, script, NULL, 0, NULL); else SetScriptState (Level->ACSThinker, script, DLevelScript::SCRIPT_PleaseRemove); } diff --git a/src/p_acs.h b/src/p_acs.h index 777a0b4a04..e8474a2977 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -428,7 +428,7 @@ private: struct FBehaviorContainer { - TArray StaticModules; + TDeletingArray StaticModules; FBehavior *LoadModule(FLevelLocals *Level, int lumpnum, FileReader *fr = nullptr, int len = 0); void LoadDefaultModules(FLevelLocals *Level); diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index feb4da30ef..5b1dddf2bf 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -3168,11 +3168,11 @@ void A_BossDeath(AActor *self) } } - // [RH] If noexit, then don't end the Level-> + // [RH] If noexit, then don't end the map. if ((deathmatch || alwaysapplydmflags) && (dmflags & DF_NO_EXIT)) return; - G_ExitLevel (0, false); + G_ExitLevel (Level, 0, false); } //---------------------------------------------------------------------------- diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 80aa82914d..343de9e5d3 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -543,7 +543,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf fraglimit <= D_GetFragCount (source->player)) { Printf ("%s\n", GStrings("TXT_FRAGLIMIT")); - G_ExitLevel (0, false); + G_ExitLevel (Level, 0, false); } } } diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index ffad736c88..99828da161 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1061,9 +1061,9 @@ FUNC(LS_Generic_Lift) FUNC(LS_Exit_Normal) // Exit_Normal (position) { - if (Level->CheckIfExitIsGood (it, FindLevelInfo(G_GetExitMap()))) + if (Level->CheckIfExitIsGood (it, FindLevelInfo(G_GetExitMap(Level)))) { - G_ExitLevel (arg0, false); + G_ExitLevel (Level, arg0, false); return true; } return false; @@ -1072,9 +1072,9 @@ FUNC(LS_Exit_Normal) FUNC(LS_Exit_Secret) // Exit_Secret (position) { - if (Level->CheckIfExitIsGood (it, FindLevelInfo(G_GetSecretExitMap()))) + if (Level->CheckIfExitIsGood (it, FindLevelInfo(G_GetSecretExitMap(Level)))) { - G_SecretExitLevel (arg0); + G_SecretExitLevel (Level, arg0); return true; } return false; @@ -1089,7 +1089,7 @@ FUNC(LS_Teleport_NewMap) if (info && Level->CheckIfExitIsGood (it, info)) { - G_ChangeLevel(info->MapName, arg1, arg2 ? CHANGELEVEL_KEEPFACING : 0); + G_ChangeLevel(Level, info->MapName, arg1, arg2 ? CHANGELEVEL_KEEPFACING : 0); return true; } } @@ -1182,9 +1182,9 @@ FUNC(LS_TeleportInSector) FUNC(LS_Teleport_EndGame) // Teleport_EndGame () { - if (!backSide && Level->CheckIfExitIsGood (it, NULL)) + if (!backSide && Level->CheckIfExitIsGood (it, nullptr)) { - G_ChangeLevel(NULL, 0, 0); + G_ChangeLevel(Level, nullptr, 0, 0); return true; } return false; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index be1489f3d4..50ed5ed0ef 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -939,6 +939,7 @@ void G_SerializeLevel(FSerializer &arc, FLevelLocals *Level, bool hubload) ("skytexture2", Level->skytexture2) ("fogdensity", Level->fogdensity) ("outsidefogdensity", Level->outsidefogdensity) + ("outsidefog", Level->outsidefog) ("skyfog", Level->skyfog) ("deathsequence", Level->deathsequence) ("bodyqueslot", Level->bodyqueslot) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index e5f69e860b..ea2de1ce5d 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -199,144 +199,6 @@ static void PrecacheLevel(FLevelLocals *Level) } -//============================================================================ -// -// clears all portal data for a new level start -// -//============================================================================ - -void FLevelLocals::ClearPortals() -{ - Displacements.Create(1); - linePortals.Clear(); - linkedPortals.Clear(); - sectorPortals.Resize(2); - PortalBlockmap.Clear(); - - // The first entry must always be the default skybox. This is what every sector gets by default. - memset(§orPortals[0], 0, sizeof(sectorPortals[0])); - sectorPortals[0].mType = PORTS_SKYVIEWPOINT; - sectorPortals[0].mFlags = PORTSF_SKYFLATONLY; - // The second entry will be the default sky. This is for forcing a regular sky through the skybox picker - memset(§orPortals[1], 0, sizeof(sectorPortals[0])); - sectorPortals[1].mType = PORTS_SKYVIEWPOINT; - sectorPortals[1].mFlags = PORTSF_SKYFLATONLY; - - // also clear the render data - for (auto &sub : subsectors) - { - for (int j = 0; j < 2; j++) - { - if (sub.portalcoverage[j].subsectors != nullptr) - { - delete[] sub.portalcoverage[j].subsectors; - sub.portalcoverage[j].subsectors = nullptr; - } - } - } - for (unsigned i = 0; i < portalGroups.Size(); i++) - { - delete portalGroups[i]; - } - portalGroups.Clear(); - linePortalSpans.Clear(); -} - -//========================================================================== -// -// -// -//========================================================================== - -void FLevelLocals::ClearLevelData() -{ - total_monsters = total_items = total_secrets = - killed_monsters = found_items = found_secrets = - wminfo.maxfrags = 0; - - for (int i = 0; i < 4; i++) - { - UDMFKeys[i].Clear(); - } - - SN_StopAllSequences(this); - - FStrifeDialogueNode *node; - - while (StrifeDialogues.Pop (node)) - { - delete node; - } - - DialogueRoots.Clear(); - ClassRoots.Clear(); - - interpolator.ClearInterpolations(); // [RH] Nothing to interpolate on a fresh map. - ClearAllSubsectorLinks(); // can't be done as part of the polyobj deletion process. - DThinker::DestroyAllThinkers(); - - // delete allocated data in the level arrays. - if (sectors.Size() > 0) - { - delete[] sectors[0].e; - } - for (auto &sub : subsectors) - { - if (sub.BSP != nullptr) delete sub.BSP; - } - ClearPortals(); - - tagManager.Clear(); - // [RH] Clear all ThingID hash chains. - ClearTIDHashes(); - - Behaviors.UnloadModules(); - - SpotState = nullptr; - ACSThinker = nullptr; - FraggleScriptThinker = nullptr; - CorpseQueue.Clear(); - canvasTextureInfo.EmptyList(); - sections.Clear(); - segs.Clear(); - sectors.Clear(); - linebuffer.Clear(); - subsectorbuffer.Clear(); - lines.Clear(); - sides.Clear(); - segbuffer.Clear(); - loadsectors.Clear(); - loadlines.Clear(); - loadsides.Clear(); - vertexes.Clear(); - nodes.Clear(); - gamenodes.Reset(); - subsectors.Clear(); - gamesubsectors.Reset(); - rejectmatrix.Clear(); - Zones.Clear(); - blockmap.Clear(); - Polyobjects.Clear(); - - for (auto &pb : PolyBlockMap) - { - polyblock_t *link = pb; - while (link != nullptr) - { - polyblock_t *next = link->next; - delete link; - link = next; - } - } - PolyBlockMap.Reset(); - - deathmatchstarts.Clear(); - AllPlayerStarts.Clear(); - memset(playerstarts, 0, sizeof(playerstarts)); - Scrolls.Clear(); - -} - //========================================================================== // // @@ -349,7 +211,6 @@ void P_FreeLevelData () // [ZZ] delete per-map event handlers E_Shutdown(true); R_FreePastViewers(); - level.ClearLevelData(); } //=========================================================================== diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 0f1c72c682..95893aa6db 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -468,7 +468,7 @@ void P_PlayerInSpecialSector (player_t *player, sector_t * sector) if (!(player->cheats & (CF_GODMODE|CF_GODMODE2))) P_DamageMobj(player->mo, NULL, NULL, sector->damageamount, sector->damagetype); if ((sector->Flags & SECF_ENDLEVEL) && player->health <= 10 && (!deathmatch || !(dmflags & DF_NO_EXIT))) { - G_ExitLevel(0, false); + G_ExitLevel(player->mo->Level, 0, false); } if (sector->Flags & SECF_DMGTERRAINFX) { @@ -683,7 +683,7 @@ void P_UpdateSpecials (FLevelLocals *Level) if (Level->maptime >= (int)(timelimit * TICRATE * 60)) { Printf ("%s\n", GStrings("TXT_TIMELIMIT")); - G_ExitLevel(0, false); + G_ExitLevel(Level, 0, false); } } } diff --git a/src/p_tick.cpp b/src/p_tick.cpp index 01bb119e5e..e62ced78db 100644 --- a/src/p_tick.cpp +++ b/src/p_tick.cpp @@ -62,7 +62,8 @@ bool P_CheckTickerPaused () && players[consoleplayer].viewz != NO_VALUE && wipegamestate == gamestate) { - S_PauseSound (!currentSession || !(currentSession->Levelinfo[0]->flags2 & LEVEL2_PAUSE_MUSIC_IN_MENUS), false); + auto nopause = gamestate != GS_LEVEL || !(currentSession->Levelinfo[0]->flags2 & LEVEL2_PAUSE_MUSIC_IN_MENUS); + S_PauseSound (nopause, false); return true; } return false; diff --git a/src/p_user.cpp b/src/p_user.cpp index 6791ff413c..c43a7f8508 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1116,7 +1116,7 @@ void P_CheckMusicChange(player_t *player) if (player->MUSINFOactor->args[0] != 0) { auto Level = player->mo->Level; - FName *music = Level->info->MusicMap.CheckKey(player->MUSINFOactor->args[0]); + const FName *music = Level->info->MusicMap.CheckKey(player->MUSINFOactor->args[0]); if (music != NULL) { diff --git a/src/portal.h b/src/portal.h index eb6d06dfd4..244150136b 100644 --- a/src/portal.h +++ b/src/portal.h @@ -100,7 +100,7 @@ struct FPortalBlock struct FPortalBlockmap { TArray data; - int dx, dy; + int dx = 0, dy = 0; bool containsLines; bool hasLinkedSectorPortals; // global flag to shortcut portal checks if the map has none. bool hasLinkedPolyPortals; // this means that any early-outs in P_CheckSight need to be disabled if a block contains polyobjects. diff --git a/src/r_data/r_interpolate.h b/src/r_data/r_interpolate.h index fa64534456..6b7f3f49af 100644 --- a/src/r_data/r_interpolate.h +++ b/src/r_data/r_interpolate.h @@ -59,6 +59,10 @@ public: didInterp = false; count = 0; } + ~FInterpolator() + { + ClearInterpolations(); + } void UpdateInterpolations(); void AddInterpolation(DInterpolation *); void RemoveInterpolation(DInterpolation *); diff --git a/src/resourcefiles/resourcefile.h b/src/resourcefiles/resourcefile.h index 9ad2e43cf5..53d0cb1242 100644 --- a/src/resourcefiles/resourcefile.h +++ b/src/resourcefiles/resourcefile.h @@ -19,7 +19,7 @@ struct FCompressedBuffer char *mBuffer; bool Decompress(char *destbuffer); - void Clean() + ~FCompressedBuffer() { mSize = mCompressedSize = 0; if (mBuffer != nullptr) diff --git a/src/s_sndseq.cpp b/src/s_sndseq.cpp index 18e65e9396..c9d99ef215 100644 --- a/src/s_sndseq.cpp +++ b/src/s_sndseq.cpp @@ -354,7 +354,7 @@ void DSeqNode::Serialize(FSerializer &arc) } else { - Level = &level; // temporary solution + Level = arc.Level; seqnum = FindSequence (seqName); if (seqnum >= 0) { diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 6e290f49c8..8139d48350 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -476,7 +476,7 @@ void S_Start (FLevelLocals *Level) // //========================================================================== -void S_PrecacheLevel (TArray &levelsounds) +void S_PrecacheLevel (const TArray &levelsounds) { unsigned int i; diff --git a/src/s_sound.h b/src/s_sound.h index 730c89ed01..bff933732e 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -218,7 +218,7 @@ void S_Shutdown (); void S_Start (FLevelLocals *Level); // Called after a level is loaded. Ensures that most sounds are loaded. -void S_PrecacheLevel (TArray &levelsounds); +void S_PrecacheLevel (const TArray &levelsounds); // Loads a sound, including any random sounds it might reference. void S_CacheSound (sfxinfo_t *sfx); diff --git a/src/scripting/vmthunks.cpp b/src/scripting/vmthunks.cpp index 48960d7469..bc5321e5fc 100644 --- a/src/scripting/vmthunks.cpp +++ b/src/scripting/vmthunks.cpp @@ -2705,7 +2705,6 @@ DEFINE_ACTION_FUNCTION_NATIVE(_AltHUD, GetLatency, Net_GetLatency) // // //========================================================================== -DEFINE_GLOBAL(level); DEFINE_FIELD(FLevelLocals, sectors) DEFINE_FIELD(FLevelLocals, lines) DEFINE_FIELD(FLevelLocals, sides) @@ -2770,6 +2769,7 @@ DEFINE_FIELD(FGameSession, time) DEFINE_FIELD(FGameSession, totaltime) DEFINE_GLOBAL(currentSession) +DEFINE_FIELD_X(Sector, sector_t, Level) DEFINE_FIELD_X(Sector, sector_t, floorplane) DEFINE_FIELD_X(Sector, sector_t, ceilingplane) DEFINE_FIELD_X(Sector, sector_t, Colormap) diff --git a/src/statistics.cpp b/src/statistics.cpp index bc08f86ec1..86110f22e8 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -437,7 +437,7 @@ void STAT_ChangeLevel(const char *newl, FLevelLocals *Level) // record the current level's stats. StoreLevelStats(Level); - level_info_t *thisinfo = Level->info; + const level_info_t *thisinfo = Level->info; level_info_t *nextinfo = NULL; if (strncmp(newl, "enDSeQ", 6)) diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index c8f418faaa..b3dce081f3 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -620,13 +620,11 @@ void DInterBackground::drawBackground(int state, bool drawsplat, bool snl_pointe switch (a->type & ANIM_CONDITION) { case ANIM_IFVISITED: - li = FindLevelInfo(a->LevelName); - if (li == NULL || !(li->flags & LEVEL_VISITED)) continue; + if (!currentSession->Visited.CheckKey(a->LevelName)) continue; break; case ANIM_IFNOTVISITED: - li = FindLevelInfo(a->LevelName); - if (li == NULL || (li->flags & LEVEL_VISITED)) continue; + if (currentSession->Visited.CheckKey(a->LevelName)) continue; break; // StatCount means 'leaving' - everything else means 'entering'! @@ -663,8 +661,7 @@ void DInterBackground::drawBackground(int state, bool drawsplat, bool snl_pointe { for (i = 0; iflags & LEVEL_VISITED) drawOnLnode(i, &splat, 1); // draw a splat on taken cities. + if (currentSession->Visited.CheckKey(lnodes[i].Level)) drawOnLnode(i, &splat, 1); // draw a splat on taken cities. } } diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 4ae1ce79b5..0591ad0267 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -7,7 +7,7 @@ struct _ native // These are the global variables, the struct is only here to av native readonly Array<@Team> Teams; native int validcount; native readonly bool multiplayer; - native play @LevelLocals level; + //native play @LevelLocals level; native @KeyBindings Bindings; native @KeyBindings AutomapBindings; native play @DehInfo deh; @@ -506,10 +506,10 @@ class BlockThingsIterator : Object native native static BlockThingsIterator Create(Actor origin, double checkradius = -1, bool ignorerestricted = false); native static BlockThingsIterator CreateFromPosition(LevelLocals Level, double checkx, double checky, double checkz, double checkh, double checkradius, bool ignorerestricted); - // Since this depends on the global level variable it should not be used any longer because this may block compatibility with future features. + // Since this has no good means to deduce the level from its parameters it should not be used any longer because this may block compatibility with future features. deprecated("3.8") static BlockThingsIterator CreateFromPos(double checkx, double checky, double checkz, double checkh, double checkradius, bool ignorerestricted) { - return CreateFromPosition(level, checkx, checky, checkz, checkh, checkradius, ignorerestricted); + return CreateFromPosition(currentSession.LevelInfo[0], checkx, checky, checkz, checkh, checkradius, ignorerestricted); } native bool Next(); } @@ -523,10 +523,10 @@ class BlockLinesIterator : Object native native static BlockLinesIterator Create(Actor origin, double checkradius = -1); native static BlockLinesIterator CreateFromPosition(LevelLocals Level, Vector3 pos, double checkh, double checkradius, Sector sec = null); - // Since this depends on the global level variable it should not be used any longer because this may block compatibility with future features. + // Since this has no good means to deduce the level from its parameters it should not be used any longer because this may block compatibility with future features. deprecated("3.8") static BlockLinesIterator CreateFromPos(Vector3 pos, double checkh, double checkradius, Sector sec = null) { - return CreateFromPosition(level, pos, checkh, checkradius, sec); + return CreateFromPosition(currentSession.LevelInfo[0], pos, checkh, checkradius, sec); } native bool Next(); } diff --git a/wadsrc/static/zscript/mapdata.txt b/wadsrc/static/zscript/mapdata.txt index 109c1afe54..3e5ea03a03 100644 --- a/wadsrc/static/zscript/mapdata.txt +++ b/wadsrc/static/zscript/mapdata.txt @@ -99,15 +99,15 @@ struct Side native play int GetUDMFInt(Name nm) { - return Level.GetUDMFInt(LevelLocals.UDMF_Side, Index(), nm); + return sector.Level.GetUDMFInt(LevelLocals.UDMF_Side, Index(), nm); } double GetUDMFFloat(Name nm) { - return Level.GetUDMFFloat(LevelLocals.UDMF_Side, Index(), nm); + return sector.Level.GetUDMFFloat(LevelLocals.UDMF_Side, Index(), nm); } String GetUDMFString(Name nm) { - return Level.GetUDMFString(LevelLocals.UDMF_Side, Index(), nm); + return sector.Level.GetUDMFString(LevelLocals.UDMF_Side, Index(), nm); } }; @@ -182,15 +182,15 @@ struct Line native play int GetUDMFInt(Name nm) { - return Level.GetUDMFInt(LevelLocals.UDMF_Line, Index(), nm); + return frontsector.Level.GetUDMFInt(LevelLocals.UDMF_Line, Index(), nm); } double GetUDMFFloat(Name nm) { - return Level.GetUDMFFloat(LevelLocals.UDMF_Line, Index(), nm); + return frontsector.Level.GetUDMFFloat(LevelLocals.UDMF_Line, Index(), nm); } String GetUDMFString(Name nm) { - return Level.GetUDMFString(LevelLocals.UDMF_Line, Index(), nm); + return frontsector.Level.GetUDMFString(LevelLocals.UDMF_Line, Index(), nm); } native clearscope int GetHealth(); @@ -241,6 +241,7 @@ struct Sector native play native readonly Color SpecialColors[5]; native readonly Color AdditiveColors[5]; + native LevelLocals Level; native Actor SoundTarget; native int16 special;