From 512792e240bf831a2021f0485741ba0c48c9ef5d Mon Sep 17 00:00:00 2001 From: Lactozilla <jp6781615@gmail.com> Date: Tue, 27 Jun 2023 15:08:25 -0300 Subject: [PATCH] Refactor world loading --- src/d_main.c | 2 +- src/d_netcmd.c | 2 +- src/g_game.c | 31 ++- src/hardware/hw_main.c | 25 --- src/p_local.h | 5 +- src/p_mobj.c | 40 +--- src/p_mobj.h | 14 -- src/p_saveg.c | 6 +- src/p_setup.c | 489 +++++++++++++++++++++-------------------- src/p_setup.h | 17 +- src/p_tick.c | 5 +- src/p_world.c | 49 ++++- src/p_world.h | 7 + src/r_fps.c | 11 +- src/r_fps.h | 2 +- 15 files changed, 343 insertions(+), 362 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index adfc82d064..9d7ae43791 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1739,7 +1739,7 @@ void D_SRB2Main(void) { levelstarttic = gametic; G_SetGamestate(GS_LEVEL); - if (!P_LoadLevel(&players[consoleplayer], false, false, false)) + if (!P_LoadLevel(false, false)) I_Quit(); // fail so reset game stuff } } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index e933c733c2..9e67df833e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2136,7 +2136,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) { DEBFILE(va("Warping to %s [resetplayer=%d lastgametype=%d gametype=%d cpnd=%d]\n", mapname, resetplayer, lastgametype, gametype, chmappending)); - if (!addworld || (addworld && playernum == consoleplayer)) + if (!addworld) CONS_Printf(M_GetText("Speeding off to level...\n")); } diff --git a/src/g_game.c b/src/g_game.c index 837d4afba6..159080b64a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1839,13 +1839,17 @@ void G_DoLoadLevel(player_t *player, boolean addworld, boolean resetplayer) P_UnloadWorldList(); } - else if (resetplayer) - player->playerstate = PST_REBORN; // Setup the level. - if (!P_LoadLevel(player, addworld, false, false)) // this never returns false? + boolean success; + if (addworld) + success = P_LoadWorld(false); + else + success = P_LoadLevel(false, false); // this never returns false? (yes it can) + + // fail so reset game stuff + if (!success) { - // fail so reset game stuff Command_ExitGame_f(); return; } @@ -1877,14 +1881,16 @@ void G_DoLoadLevel(player_t *player, boolean addworld, boolean resetplayer) G_ResetCamera(1); } - if (player == &players[consoleplayer]) + if (!addworld && player == &players[consoleplayer]) { G_ResetCamera(consoleplayer); // clear hud messages remains (usually from game startup) CON_ClearHUD(); } - else if (addworld && !splitscreen) // change world back to yours + + // change world back to yours, for consistency + if (addworld) P_SetWorld(localworld); } @@ -5026,13 +5032,7 @@ void G_InitNew(player_t *player, numgameovers = tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; countdown = countdown2 = exitfadestarted = 0; - if (addworld) - { - P_DetachPlayerWorld(player); - P_UnloadWorldPlayer(player); - G_ResetPlayer(player, pultmode, FLS); - } - else + if (!addworld) { for (i = 0; i < MAXPLAYERS; i++) G_ResetPlayer(&players[i], pultmode, FLS); @@ -5082,11 +5082,8 @@ void G_InitNew(player_t *player, else G_DoLoadLevel(player, addworld, resetplayer); - if (addworld && player != &players[consoleplayer]) - return; - // current world is hopefully the newly loaded world at this point - if (netgame) + if (netgame && !addworld) { char *title = G_BuildMapTitle(gamemap); diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 3b51fc4124..561577b99b 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -132,7 +132,6 @@ ps_metric_t ps_hw_batchsorttime = {0}; ps_metric_t ps_hw_batchdrawtime = {0}; boolean gl_init = false; -boolean gl_maploaded = false; boolean gl_sessioncommandsadded = false; boolean gl_shadersavailable = true; @@ -5548,23 +5547,6 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) HWD.pfnGClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE); } -void HWR_LoadLevel(void) -{ -#ifdef ALAM_LIGHTING - // BP: reset light between levels (we draw preview frame lights on current frame) - HWR_ResetLights(); -#endif - - if (world->extrasubsectors == NULL) - HWR_CreatePlanePolygons((INT32)world->numnodes - 1); - - // Build the sky dome - HWR_ClearSkyDome(world); - HWR_BuildSkyDome(world); - - gl_maploaded = true; -} - // ========================================================================== // 3D ENGINE COMMANDS // ========================================================================== @@ -5705,13 +5687,6 @@ void HWR_Switch(void) // Load textures if (!gl_maptexturesloaded) HWR_LoadMapTextures(numtextures); - - // Create plane polygons - if (!gl_maploaded && (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) - { - HWR_ClearAllTextures(); - HWR_LoadLevel(); - } } // -------------------------------------------------------------------------- diff --git a/src/p_local.h b/src/p_local.h index 8f3bc9edc3..2cc777058d 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -72,9 +72,10 @@ typedef enum THINK_PRECIP, NUM_THINKERLISTS } thinklistnum_t; /**< Thinker lists. */ -extern thinker_t *thlist; -void P_InitThinkers(void); +extern thinker_t *thlist; // the current thinker list + +void P_InitThinkers(world_t *w); void P_AddThinker(const thinklistnum_t n, thinker_t *thinker); void P_RemoveThinker(thinker_t *thinker); void P_MoveThinkerToWorld(world_t *w, const thinklistnum_t n, thinker_t *thinker); diff --git a/src/p_mobj.c b/src/p_mobj.c index 02308680e2..0940bf1ac3 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -19,6 +19,7 @@ #include "hu_stuff.h" #include "p_local.h" #include "p_setup.h" +#include "p_world.h" #include "r_fps.h" #include "r_main.h" #include "r_skins.h" @@ -40,41 +41,6 @@ static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); -actioncache_t actioncachehead; - -void P_InitCachedActions(void) -{ - actioncachehead.prev = actioncachehead.next = &actioncachehead; -} - -void P_RunCachedActions(void) -{ - actioncache_t *ac; - actioncache_t *next; - - for (ac = actioncachehead.next; ac != &actioncachehead; ac = next) - { - var1 = states[ac->statenum].var1; - var2 = states[ac->statenum].var2; - astate = &states[ac->statenum]; - if (ac->mobj && !P_MobjWasRemoved(ac->mobj)) // just in case... - states[ac->statenum].action.acp1(ac->mobj); - next = ac->next; - Z_Free(ac); - } -} - -void P_AddCachedAction(mobj_t *mobj, INT32 statenum) -{ - actioncache_t *newaction = Z_Calloc(sizeof(actioncache_t), PU_LEVEL, NULL); - newaction->mobj = mobj; - newaction->statenum = statenum; - actioncachehead.prev->next = newaction; - newaction->next = &actioncachehead; - newaction->prev = actioncachehead.prev; - actioncachehead.prev = newaction; -} - // // P_SetupStateAnimation // @@ -10977,13 +10943,13 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // Call action functions when the state is set if (st->action.acp1 && (mobj->flags & MF_RUNSPAWNFUNC)) { - if (levelloading) + if (world->loading) { // Cache actions in a linked list // with function pointer, and // var1 & var2, which will be executed // when the level finishes loading. - P_AddCachedAction(mobj, mobj->info->spawnstate); + P_AddCachedAction(world, mobj, mobj->info->spawnstate); } else { diff --git a/src/p_mobj.h b/src/p_mobj.h index 86bb07676b..3e597c8e5f 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -472,20 +472,6 @@ typedef struct precipmobj_s INT32 flags; // flags from mobjinfo tables } precipmobj_t; -typedef struct actioncache_s -{ - struct actioncache_s *next; - struct actioncache_s *prev; - struct mobj_s *mobj; - INT32 statenum; -} actioncache_t; - -extern actioncache_t actioncachehead; - -void P_InitCachedActions(void); -void P_RunCachedActions(void); -void P_AddCachedAction(mobj_t *mobj, INT32 statenum); - // check mobj against water content, before movement code void P_MobjCheckWater(mobj_t *mobj); diff --git a/src/p_saveg.c b/src/p_saveg.c index 53076ade5e..835ea19123 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3717,7 +3717,7 @@ static void P_NetUnArchiveThinkers(void) // we don't want the removed mobjs to come back unarchiveworld->iquetail = unarchiveworld->iquehead = 0; - P_InitThinkers(); + P_InitThinkers(unarchiveworld); // clear sector thinker pointers so they don't point to non-existant thinkers for all of eternity for (i = 0; i < numsectors; i++) @@ -4400,7 +4400,7 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) tokenlist = READUINT32(save_p); - if (!P_LoadLevel(&players[consoleplayer], false, true, reloading)) + if (!P_LoadLevel(true, reloading)) { CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n")); return false; @@ -4927,7 +4927,7 @@ static void P_NetUnArchiveWorlds(boolean reloading) // Don't load the first world (because it already is loaded at this point) if (i != 0) { - if (!P_LoadLevel(player, true, true, reloading)) + if (!P_LoadWorld(true)) I_Error("P_NetUnArchiveWorlds: failed loading world"); } diff --git a/src/p_setup.c b/src/p_setup.c index 81ac86be08..baf5c093dd 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -115,7 +115,6 @@ side_t *spawnsides; INT32 numstarposts; UINT16 bossdisabled; boolean stoppedclock; -boolean levelloading; UINT8 levelfadecol; // BLOCKMAP @@ -6832,9 +6831,9 @@ static void P_MakeMapMD5(virtres_t *virt, void *dest) M_Memcpy(dest, &resmd5, 16); } -static boolean P_LoadMapFromFile(void) +static boolean P_LoadMapFromFile(lumpnum_t maplumpnum) { - virtres_t *virt = vres_GetMap(lastloadedmaplumpnum); + virtres_t *virt = vres_GetMap(maplumpnum); virtlump_t *textmap = vres_Find(virt, "TEXTMAP"); size_t i; udmf = textmap != NULL; @@ -6911,9 +6910,6 @@ void P_SetupSkyTexture(INT32 skynum) world->skytexture = R_TextureNumForName(skytexname); } -static const char *maplumpname; -lumpnum_t lastloadedmaplumpnum; // for comparative savegame - // // Some player initialization for map start. // @@ -6986,7 +6982,7 @@ static void P_InitWorldSettings(mapheader_t *mapheader) stagefailed = G_IsSpecialStage(gamemap); } -static void P_InitLevelSettings(mapheader_t *mapheader, player_t *player, boolean addworld, boolean fromnetsave) +static void P_InitLevelSettings(mapheader_t *mapheader, boolean addworld) { INT32 i; boolean canresetlives = true; @@ -7010,18 +7006,12 @@ static void P_InitLevelSettings(mapheader_t *mapheader, player_t *player, boolea } if (!addworld) + { countdown = countdown2 = exitfadestarted = 0; - if (!addworld) - { for (i = 0; i < MAXPLAYERS; i++) P_InitPlayerSettings(i, canresetlives); } - else if (player && !fromnetsave) - { - P_DetachPlayerWorld(player); - P_InitPlayerSettings((INT32)(player - players), canresetlives); - } if (botingame) CV_SetValue(&cv_analog[1], true); @@ -7051,7 +7041,7 @@ void P_RespawnThings(void) P_RemoveMobj((mobj_t *)think); } - P_InitLevelSettings(worldmapheader, NULL, false, false); + P_InitLevelSettings(worldmapheader, false); localaiming = 0; localaiming2 = 0; @@ -7531,7 +7521,7 @@ static void P_WriteLetter(void) Z_Free(buf); } -static void P_InitGametype(player_t *player, boolean addworld) +static void P_InitGametype(boolean addworld) { UINT8 i; @@ -7552,8 +7542,6 @@ static void P_InitGametype(player_t *player, boolean addworld) leveltime = maxstarposttime; } } - else - P_InitPlayer((INT32)(player - players)); P_WriteLetter(); @@ -7571,20 +7559,170 @@ static void P_InitGametype(player_t *player, boolean addworld) : nextmapheader->numlaps); } +static world_t *P_InitWorldFromMap(INT16 mapnumber, mapheader_t *mapheader, boolean addworld, boolean fromnetsave) +{ + world_t *curworld = world; + world_t *w = P_InitNewWorld(); + + w->loading = true; + + P_SetWorld(w); + + if (!addworld && !fromnetsave) + { + for (INT32 i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + player_t *p = &players[i]; + p->world = NULL; + P_SwitchPlayerWorld(p, w); + } + + w->players = D_NumPlayers(); + } + + R_InitializeLevelInterpolators(w); + P_InitThinkers(w); + R_InitMobjInterpolators(w); + + // internal game map + const char *maplumpname = G_BuildMapName(mapnumber); + lumpnum_t maplumpnum = W_CheckNumForMap(maplumpname); + if (maplumpnum == LUMPERROR) + { + P_SetWorld(curworld); + return NULL; + } + + R_ReInitColormaps(mapheader->palette); + + // Init Boom colormaps. + if (!addworld) + R_ClearColormaps(); + + // SRB2 determines the sky texture to be used depending on the map header. + P_InitLevelSky(mapheader->skynum); + + P_ResetSpawnpoints(); + + P_ResetWaypoints(); + + P_MapStart(); // tmthing can be used starting from this point + + P_InitSlopes(); + + if (!P_LoadMapFromFile(maplumpnum)) + return false; + + // init anything that P_SpawnSlopes/P_SpawnMapThings needs to know + P_InitSpecials(); + + // actually do that now + P_SpawnSlopes(fromnetsave); + P_SpawnMapThings(!fromnetsave); + + // Init skybox objects + w->skyboxmo[0] = w->skyboxviewpnts[0]; + w->skyboxmo[1] = w->skyboxcenterpnts[0]; + + // Count Co-Op starts + for (w->numcoopstarts = 0; w->numcoopstarts < MAXPLAYERS; w->numcoopstarts++) + if (!w->playerstarts[w->numcoopstarts]) + break; + + // set up world state + P_SpawnSpecials(fromnetsave); + + // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame) + if (!fromnetsave) + P_SpawnPrecipitation(); + +#ifdef HWRENDER + if (rendermode == render_opengl && world->extrasubsectors == NULL) + HWR_CreatePlanePolygons((INT32)world->numnodes - 1); +#endif + + // oh god I hope this helps + // (addendum: apparently it does! + // none of this needs to be done because it's not the beginning of the map when + // a netgame save is being loaded, and could actively be harmful by messing with + // the client's view of the data.) + if (!fromnetsave) + P_InitGametype(addworld); + + // clear special respawning que + w->iquehead = w->iquetail = 0; + + if (precache || dedicated) + R_PrecacheLevel(); + + if (!demoplayback) + { + clientGamedata->mapvisited[mapnumber-1] |= MV_VISITED; + serverGamedata->mapvisited[mapnumber-1] |= MV_VISITED; + } + + w->loading = false; + + P_RunCachedActions(w); + + P_MapEnd(); // tmthing is no longer needed from this point onwards + + P_SetWorld(curworld); + + return w; +} + +static void P_DoLevelProgression(void) +{ + // Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap... + if (titlemapinaction) + return; + + if (!lastmaploaded) // Start a new game? + { + // I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020 + if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || marathonmode) + && !usedCheats && cursaveslot > 0) + { + G_SaveGame((UINT32)cursaveslot, gamemap); + } + // If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted. + } + lastmaploaded = gamemap; // HAS to be set after saving!! +} + +static void P_FinishMapLoad(boolean fromnetsave) +{ + if (!fromnetsave) // uglier hack + { + // to make a newly loaded level start on the second frame. + INT32 buf = gametic % BACKUPTICS; + for (INT32 i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1); + } + P_PreTicker(2); + P_MapStart(); // just in case MapLoad modifies tmthing + LUA_HookInt(gamemap, HOOK(MapLoad)); + P_MapEnd(); // just in case MapLoad modifies tmthing + } +} + /** Loads a level from a lump or external wad. * * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot. * \todo Clean up, refactor, split up; get rid of the bloat. */ -boolean P_LoadLevel(player_t *player, boolean addworld, boolean fromnetsave, boolean reloadinggamestate) +boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) { // use gamemap to get map number. // 99% of the things already did, so. // Map header should always be in place at this point - INT32 i, ranspecialwipe = 0; - boolean runforself = (!addworld || (addworld && player == &players[consoleplayer])); - - levelloading = true; + INT32 ranspecialwipe = 0; // This is needed. Don't touch. maptol = nextmapheader->typeoflevel; @@ -7615,7 +7753,7 @@ boolean P_LoadLevel(player_t *player, boolean addworld, boolean fromnetsave, boo if (cv_runscripts.value && nextmapheader->scriptname[0] != '#') P_RunLevelScript(nextmapheader->scriptname); - P_InitLevelSettings(nextmapheader, player, addworld, fromnetsave); + P_InitLevelSettings(nextmapheader, false); postimgtype = postimgtype2 = postimg_none; @@ -7647,127 +7785,80 @@ boolean P_LoadLevel(player_t *player, boolean addworld, boolean fromnetsave, boo players[consoleplayer].viewz = 1; // Cancel all d_main.c fadeouts (keep fade in though). - if (addworld || reloadinggamestate) + if (reloadinggamestate) wipegamestate = gamestate; // Don't fade if reloading the gamestate else wipegamestate = FORCEWIPEOFF; wipestyleflags = 0; - if (!addworld) + // Special stage & record attack retry fade to white + // This is handled BEFORE sounds are stopped. + if (G_GetModeAttackRetryFlag()) { - // Special stage & record attack retry fade to white - // This is handled BEFORE sounds are stopped. - if (G_GetModeAttackRetryFlag()) - { - if (modeattacking && !demoplayback) - { - ranspecialwipe = 2; - wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE); - } - G_ClearModeAttackRetryFlag(); - } - else if (rendermode != render_none && G_IsSpecialStage(gamemap)) - { - P_RunSpecialStageWipe(); - ranspecialwipe = 1; - } - - // Make sure all sounds are stopped before Z_FreeTags. - S_StopSounds(); - S_ClearSfx(); - - // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. - // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. - if (!(reloadinggamestate || titlemapinaction) && (S_ShouldResetMusic(nextmapheader) || - strnicmp(S_MusicName(), - (mapmusflags & MUSIC_RELOADRESET) ? nextmapheader->musname : mapmusname, 7))) - { - S_FadeMusic(0, FixedMul( - FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); - } - - // Let's fade to black here - // But only if we didn't do the special stage wipe - if (rendermode != render_none && !(ranspecialwipe || reloadinggamestate)) - P_RunLevelWipe(); - - if (!(reloadinggamestate || titlemapinaction)) + if (modeattacking && !demoplayback) { - if (ranspecialwipe == 2) - { - pausedelay = -3; // preticker plus one - S_StartSound(NULL, sfx_s3k73); - } - - // Print "SPEEDING OFF TO [ZONE] [ACT 1]..." - if (rendermode != render_none) - { - // Don't include these in the fade! - char tx[64]; - V_DrawSmallString(1, 191, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Speeding off to...")); - snprintf(tx, 63, "%s%s%s", - nextmapheader->lvlttl, - (nextmapheader->levelflags & LF_NOZONE) ? "" : " Zone", - (nextmapheader->actnum > 0) ? va(" %d",nextmapheader->actnum) : ""); - V_DrawSmallString(1, 195, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, tx); - I_UpdateNoVsync(); - } - - // As oddly named as this is, this handles music only. - // We should be fine starting it here. - // Don't do this during titlemap, because the menu code handles music by itself. - S_Start(nextmapheader); - - levelfadecol = (ranspecialwipe) ? 0 : 31; - - // Close text prompt before freeing the old level - F_EndTextPrompt(false, true); + ranspecialwipe = 2; + wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE); } + G_ClearModeAttackRetryFlag(); } - else + else if (rendermode != render_none && G_IsSpecialStage(gamemap)) { - S_SetMapMusic(nextmapheader); - S_StopMusic(); - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); + P_RunSpecialStageWipe(); + ranspecialwipe = 1; } - if (player && !titlemapinaction && !fromnetsave) - P_UnloadWorldPlayer(player); - - // Initialize the world - world = P_InitNewWorld(); - thlist = world->thlist; + // Make sure all sounds are stopped before Z_FreeTags. + S_StopSounds(); + S_ClearSfx(); - if (!fromnetsave) + // Fade out music here. Deduct 2 tics so the fade volume actually reaches 0. + // But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug. + if (!(reloadinggamestate || titlemapinaction) && (S_ShouldResetMusic(nextmapheader) || + strnicmp(S_MusicName(), + (mapmusflags & MUSIC_RELOADRESET) ? nextmapheader->musname : mapmusname, 7))) { - if (addworld) - P_SwitchPlayerWorld(player, world); - else - { - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *p; + S_FadeMusic(0, FixedMul( + FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)); + } - if (!playeringame[i]) - continue; + // Let's fade to black here + // But only if we didn't do the special stage wipe + if (rendermode != render_none && !(ranspecialwipe || reloadinggamestate)) + P_RunLevelWipe(); - p = &players[i]; - p->world = NULL; - P_SwitchPlayerWorld(p, world); - } + if (!(reloadinggamestate || titlemapinaction)) + { + if (ranspecialwipe == 2) + { + pausedelay = -3; // preticker plus one + S_StartSound(NULL, sfx_s3k73); + } - world->players = D_NumPlayers(); + // Print "SPEEDING OFF TO [ZONE] [ACT 1]..." + if (rendermode != render_none) + { + // Don't include these in the fade! + char tx[64]; + V_DrawSmallString(1, 191, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Speeding off to...")); + snprintf(tx, 63, "%s%s%s", + nextmapheader->lvlttl, + (nextmapheader->levelflags & LF_NOZONE) ? "" : " Zone", + (nextmapheader->actnum > 0) ? va(" %d",nextmapheader->actnum) : ""); + V_DrawSmallString(1, 195, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, tx); + I_UpdateNoVsync(); } - } - if (!addworld || player == &players[consoleplayer]) - localworld = world; + // As oddly named as this is, this handles music only. + // We should be fine starting it here. + // Don't do this during titlemap, because the menu code handles music by itself. + S_Start(nextmapheader); - R_InitializeLevelInterpolators(world); + levelfadecol = (ranspecialwipe) ? 0 : 31; - P_InitThinkers(); - R_InitMobjInterpolators(); - P_InitCachedActions(); + // Close text prompt before freeing the old level + F_EndTextPrompt(false, true); + } if (!fromnetsave && savedata.lives > 0) { @@ -7781,139 +7872,33 @@ boolean P_LoadLevel(player_t *player, boolean addworld, boolean fromnetsave, boo savedata.lives = 0; } - // internal game map - maplumpname = G_BuildMapName(gamemap); - lastloadedmaplumpnum = W_CheckNumForMap(maplumpname); - if (lastloadedmaplumpnum == LUMPERROR) - I_Error("Map %s not found.\n", maplumpname); - - R_ReInitColormaps(worldmapheader->palette); - if (!addworld) - { - // Init Boom colormaps. - R_ClearColormaps(); - } CON_SetupBackColormap(); - // SRB2 determines the sky texture to be used depending on the map header. - P_InitLevelSky(worldmapheader->skynum); - - P_ResetSpawnpoints(); - - P_ResetWaypoints(); - - P_MapStart(); // tmthing can be used starting from this point - - P_InitSlopes(); - - if (!P_LoadMapFromFile()) - return false; - - // init anything that P_SpawnSlopes/P_LoadThings needs to know - P_InitSpecials(); - // Defaults in case levels don't have them set. sstimer = worldmapheader->sstimer*TICRATE + 6; ssspheres = worldmapheader->ssspheres; - P_SpawnSlopes(fromnetsave); - - P_SpawnMapThings(!fromnetsave); - world->skyboxmo[0] = world->skyboxviewpnts[0]; - world->skyboxmo[1] = world->skyboxcenterpnts[0]; - - for (world->numcoopstarts = 0; world->numcoopstarts < MAXPLAYERS; world->numcoopstarts++) - if (!world->playerstarts[world->numcoopstarts]) - break; - - // set up world state - P_SpawnSpecials(fromnetsave); + world_t *w = P_InitWorldFromMap(gamemap, worldmapheader, false, fromnetsave); + if (w == NULL) + I_Error("Map %s not found.\n", G_BuildMapName(gamemap)); - // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame) - if (!fromnetsave && runforself) - P_SpawnPrecipitation(); - -#ifdef HWRENDER // not win32 only 19990829 by Kin - gl_maploaded = false; - - // Create plane polygons. - if (rendermode == render_opengl) - HWR_LoadLevel(); -#endif - - // oh god I hope this helps - // (addendum: apparently it does! - // none of this needs to be done because it's not the beginning of the map when - // a netgame save is being loaded, and could actively be harmful by messing with - // the client's view of the data.) - if (!fromnetsave) - P_InitGametype(player, addworld); - - if (runforself && !reloadinggamestate) + if (!reloadinggamestate) { P_InitCamera(); localaiming = 0; localaiming2 = 0; } - // clear special respawning que - world->iquehead = world->iquetail = 0; - // Remove the loading shit from the screen - if (rendermode != render_none && !(titlemapinaction || reloadinggamestate) && runforself) + if (rendermode != render_none && !(titlemapinaction || reloadinggamestate)) F_WipeColorFill(levelfadecol); - if (precache || dedicated) - R_PrecacheLevel(); - nextmapoverride = 0; skipstats = 0; - if (!demoplayback) - { - clientGamedata->mapvisited[gamemap-1] |= MV_VISITED; - serverGamedata->mapvisited[gamemap-1] |= MV_VISITED; - } - - levelloading = false; - - P_RunCachedActions(); - - P_MapEnd(); // tmthing is no longer needed from this point onwards - - // Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap... - if (!titlemapinaction) - { - if (!lastmaploaded) // Start a new game? - { - // I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020 - if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || marathonmode) - && !usedCheats && cursaveslot > 0) - { - G_SaveGame((UINT32)cursaveslot, gamemap); - } - // If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted. - } - lastmaploaded = gamemap; // HAS to be set after saving!! - } - - if (!fromnetsave) // uglier hack - { // to make a newly loaded level start on the second frame. - INT32 buf = gametic % BACKUPTICS; - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1); - } - P_PreTicker(2); - P_MapStart(); // just in case MapLoad modifies tmthing - LUA_HookInt(gamemap, HOOK(MapLoad)); - P_MapEnd(); // just in case MapLoad modifies tmthing - } + P_DoLevelProgression(); - // Done here - if (addworld) - return true; + P_FinishMapLoad(fromnetsave); // No render mode or reloading gamestate, stop here. if (rendermode == render_none || reloadinggamestate) @@ -7938,6 +7923,36 @@ boolean P_LoadLevel(player_t *player, boolean addworld, boolean fromnetsave, boo return true; } +boolean P_LoadWorld(boolean fromnetsave) +{ + // Initialize sector node list. + P_Initsecnode(); + + if (nextmapheader->runsoc[0] != '#') + P_RunSOC(nextmapheader->runsoc); + + if (cv_runscripts.value && nextmapheader->scriptname[0] != '#') + P_RunLevelScript(nextmapheader->scriptname); + + P_InitLevelSettings(nextmapheader, true); + + // Don't cause a wipe here + if (wipegamestate != gamestate) + { + wipegamestate = gamestate; + wipestyleflags = 0; + } + + world_t *w = P_InitWorldFromMap(gamemap, worldmapheader, true, fromnetsave); + if (w == NULL) + I_Error("Map %s not found.\n", G_BuildMapName(gamemap)); + + P_FinishMapLoad(fromnetsave); + + return true; +} + + // // P_RunSOC // diff --git a/src/p_setup.h b/src/p_setup.h index b541e7c9e0..b293d4e65d 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -21,11 +21,8 @@ // map md5, sent to players via PT_SERVERINFO extern unsigned char mapmd5[16]; -extern boolean levelloading; extern UINT8 levelfadecol; -extern lumpnum_t lastloadedmaplumpnum; // for comparative savegame - /* for levelflat type */ enum { @@ -83,6 +80,14 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat); INT32 P_AddLevelFlatRuntime(const char *flatname); INT32 P_CheckLevelFlat(const char *flatname); +typedef struct actioncache_s +{ + struct actioncache_s *next; + struct actioncache_s *prev; + struct mobj_s *mobj; + INT32 statenum; +} actioncache_t; + extern size_t nummapthings; extern mapthing_t *mapthings; @@ -92,10 +97,8 @@ void P_SetupSkyTexture(INT32 skynum); void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum); #endif void P_RespawnThings(void); -boolean P_LoadLevel(player_t *player, boolean addworld, boolean fromnetsave, boolean reloadinggamestate); -#ifdef HWRENDER -void HWR_LoadLevel(void); -#endif +boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate); +boolean P_LoadWorld(boolean fromnetsave); boolean P_AddWadFile(const char *wadfilename); boolean P_AddFolder(const char *folderpath); diff --git a/src/p_tick.c b/src/p_tick.c index e9236cd60d..c24f0236d2 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -191,10 +191,9 @@ void Command_CountMobjs_f(void) // // P_InitThinkers // -void P_InitThinkers(void) +void P_InitThinkers(world_t *w) { - UINT8 i; - for (i = 0; i < NUM_THINKERLISTS; i++) + for (UINT8 i = 0; i < NUM_THINKERLISTS; i++) thlist[i].prev = thlist[i].next = &thlist[i]; } diff --git a/src/p_world.c b/src/p_world.c index dd9fa553c2..114d56e4fa 100644 --- a/src/p_world.c +++ b/src/p_world.c @@ -59,10 +59,11 @@ world_t *P_InitWorld(void) { world_t *w = Z_Calloc(sizeof(world_t), PU_STATIC, NULL); w->gamemap = gamemap; - if (!mapheaderinfo[w->gamemap-1]) - P_AllocMapHeader(w->gamemap-1); - w->header = mapheaderinfo[w->gamemap-1]; + if (!mapheaderinfo[gamemap-1]) + P_AllocMapHeader(gamemap-1); + w->header = mapheaderinfo[gamemap-1]; w->thlist = Z_Calloc(sizeof(thinker_t) * NUM_THINKERLISTS, PU_STATIC, NULL); + P_InitCachedActions(w); return w; } @@ -309,11 +310,6 @@ void P_SwitchWorld(player_t *player, world_t *w) P_ResetPlayer(player); P_MapEnd(); -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_LoadLevel(); -#endif - if (local || splitscreen) { S_SetMapMusic(worldmapheader); @@ -496,3 +492,40 @@ world_t *P_GetMobjWorld(mobj_t *mobj) { return (world_t *)mobj->world; } + +void P_InitCachedActions(world_t *w) +{ + actioncache_t *head = &w->actioncachehead; + + memset(head, 0x00, sizeof(actioncache_t)); + + w->actioncachehead.prev = w->actioncachehead.next = head; +} + +void P_RunCachedActions(world_t *w) +{ + actioncache_t *ac; + actioncache_t *next; + + for (ac = w->actioncachehead.next; ac != &w->actioncachehead; ac = next) + { + var1 = states[ac->statenum].var1; + var2 = states[ac->statenum].var2; + astate = &states[ac->statenum]; + if (ac->mobj && !P_MobjWasRemoved(ac->mobj)) // just in case... + states[ac->statenum].action.acp1(ac->mobj); + next = ac->next; + Z_Free(ac); + } +} + +void P_AddCachedAction(world_t *w, mobj_t *mobj, INT32 statenum) +{ + actioncache_t *newaction = Z_Calloc(sizeof(actioncache_t), PU_LEVEL, NULL); + newaction->mobj = mobj; + newaction->statenum = statenum; + w->actioncachehead.prev->next = newaction; + newaction->next = &w->actioncachehead; + newaction->prev = w->actioncachehead.prev; + w->actioncachehead.prev = newaction; +} diff --git a/src/p_world.h b/src/p_world.h index 151c279ee9..6f71c649f5 100644 --- a/src/p_world.h +++ b/src/p_world.h @@ -36,6 +36,7 @@ typedef struct thinker_t *thlist; INT32 players; + boolean loading; size_t numvertexes, numsegs, numsectors, numsubsectors, numnodes, numlines, numsides, nummapthings; vertex_t *vertexes; @@ -65,6 +66,8 @@ typedef struct mobj_t *overlaycap; + actioncache_t actioncachehead; + mapheader_t *header; fixed_t gravity; @@ -168,6 +171,10 @@ void P_SetWorld(world_t *w); void P_RoamIntoWorld(player_t *player, INT32 mapnum); void P_SwitchWorld(player_t *player, world_t *w); +void P_InitCachedActions(world_t *w); +void P_RunCachedActions(world_t *w); +void P_AddCachedAction(world_t *w, mobj_t *mobj, INT32 statenum); + world_t *P_GetPlayerWorld(player_t *player); void P_DetachPlayerWorld(player_t *player); diff --git a/src/r_fps.c b/src/r_fps.c index 32d8718ed8..f2f9a15aa0 100644 --- a/src/r_fps.c +++ b/src/r_fps.c @@ -748,13 +748,12 @@ void R_RemoveMobjInterpolator(mobj_t *mobj) } } -void R_InitMobjInterpolators(void) +void R_InitMobjInterpolators(void *wptr) { - // apparently it's not acceptable to free something already unallocated - // Z_Free(world->interpolated_mobjs); - world->interpolated_mobjs = NULL; - world->interpolated_mobjs_len = 0; - world->interpolated_mobjs_capacity = 0; + world_t *w = (world_t *)wptr; + w->interpolated_mobjs = NULL; + w->interpolated_mobjs_len = 0; + w->interpolated_mobjs_capacity = 0; } void R_UpdateMobjInterpolators(void *wptr) diff --git a/src/r_fps.h b/src/r_fps.h index 9e074cd71f..3e4d5b64a9 100644 --- a/src/r_fps.h +++ b/src/r_fps.h @@ -150,7 +150,7 @@ void R_RestoreLevelInterpolators(void *wptr); void R_DestroyLevelInterpolators(void *wptr, thinker_t *thinker); // Initialize internal mobj interpolator list (e.g. during level loading) -void R_InitMobjInterpolators(void); +void R_InitMobjInterpolators(void *wptr); // Add interpolation state for the given mobj void R_AddMobjInterpolator(mobj_t *mobj); // Remove the interpolation state for the given mobj -- GitLab