1 /*
2 ** g_level.cpp
3 ** controls movement between levels
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #include <assert.h>
36 #include "templates.h"
37 #include "d_main.h"
38 #include "g_level.h"
39 #include "g_game.h"
40 #include "s_sound.h"
41 #include "d_event.h"
42 #include "m_random.h"
43 #include "doomerrors.h"
44 #include "doomstat.h"
45 #include "wi_stuff.h"
46 #include "w_wad.h"
47 #include "am_map.h"
48 #include "c_dispatch.h"
49 #include "i_system.h"
50 #include "p_setup.h"
51 #include "p_local.h"
52 #include "r_sky.h"
53 #include "c_console.h"
54 #include "intermission/intermission.h"
55 #include "gstrings.h"
56 #include "v_video.h"
57 #include "st_stuff.h"
58 #include "hu_stuff.h"
59 #include "p_saveg.h"
60 #include "p_acs.h"
61 #include "d_protocol.h"
62 #include "v_text.h"
63 #include "s_sndseq.h"
64 #include "b_bot.h"
65 #include "sc_man.h"
66 #include "sbar.h"
67 #include "a_lightning.h"
68 #include "m_png.h"
69 #include "m_random.h"
70 #include "version.h"
71 #include "statnums.h"
72 #include "sbarinfo.h"
73 #include "r_data/r_translate.h"
74 #include "p_lnspec.h"
75 #include "r_data/r_interpolate.h"
76 #include "cmdlib.h"
77 #include "d_net.h"
78 #include "d_netinf.h"
79 #include "v_palette.h"
80 #include "menu/menu.h"
81 #include "a_sharedglobal.h"
82 #include "a_strifeglobal.h"
83 #include "r_data/colormaps.h"
84 #include "farchive.h"
85 #include "r_renderer.h"
86
87 #include "gi.h"
88
89 #include "g_hub.h"
90
91 void STAT_StartNewGame(const char *lev);
92 void STAT_ChangeLevel(const char *newl);
93
94
95 EXTERN_CVAR (Float, sv_gravity)
96 EXTERN_CVAR (Float, sv_aircontrol)
97 EXTERN_CVAR (Int, disableautosave)
98 EXTERN_CVAR (String, playerclass)
99
100 #define SNAP_ID MAKE_ID('s','n','A','p')
101 #define DSNP_ID MAKE_ID('d','s','N','p')
102 #define VIST_ID MAKE_ID('v','i','S','t')
103 #define ACSD_ID MAKE_ID('a','c','S','d')
104 #define RCLS_ID MAKE_ID('r','c','L','s')
105 #define PCLS_ID MAKE_ID('p','c','L','s')
106
107 void G_VerifySkill();
108
109
110 static FRandom pr_classchoice ("RandomPlayerClassChoice");
111
112 extern level_info_t TheDefaultLevelInfo;
113 extern bool timingdemo;
114
115 // Start time for timing demos
116 int starttime;
117
118
119 extern FString BackupSaveName;
120
121 bool savegamerestore;
122
123 extern int mousex, mousey;
124 extern bool sendpause, sendsave, sendturn180, SendLand;
125
126 void *statcopy; // for statistics driver
127
128 FLevelLocals level; // info about current level
129
130
131 //==========================================================================
132 //
133 // G_InitNew
134 // Can be called by the startup code or the menu task,
135 // consoleplayer, playeringame[] should be set.
136 //
137 //==========================================================================
138
139 static FString d_mapname;
140 static int d_skill=-1;
141
G_DeferedInitNew(const char * mapname,int newskill)142 void G_DeferedInitNew (const char *mapname, int newskill)
143 {
144 d_mapname = mapname;
145 d_skill = newskill;
146 CheckWarpTransMap (d_mapname, true);
147 gameaction = ga_newgame2;
148 }
149
G_DeferedInitNew(FGameStartup * gs)150 void G_DeferedInitNew (FGameStartup *gs)
151 {
152 if (gs->PlayerClass != NULL) playerclass = gs->PlayerClass;
153 d_mapname = AllEpisodes[gs->Episode].mEpisodeMap;
154 d_skill = gs->Skill;
155 CheckWarpTransMap (d_mapname, true);
156 gameaction = ga_newgame2;
157 }
158
159 //==========================================================================
160 //
161 //
162 //==========================================================================
163
CCMD(map)164 CCMD (map)
165 {
166 if (netgame)
167 {
168 Printf ("Use " TEXTCOLOR_BOLD "changemap" TEXTCOLOR_NORMAL " instead. " TEXTCOLOR_BOLD "Map"
169 TEXTCOLOR_NORMAL " is for single-player only.\n");
170 return;
171 }
172 if (argv.argc() > 1)
173 {
174 try
175 {
176 if (!P_CheckMapData(argv[1]))
177 {
178 Printf ("No map %s\n", argv[1]);
179 }
180 else
181 {
182 G_DeferedInitNew (argv[1]);
183 }
184 }
185 catch(CRecoverableError &error)
186 {
187 if (error.GetMessage())
188 Printf("%s", error.GetMessage());
189 }
190 }
191 else
192 {
193 Printf ("Usage: map <map name>\n");
194 }
195 }
196
197 //==========================================================================
198 //
199 //
200 //==========================================================================
201
CCMD(recordmap)202 CCMD(recordmap)
203 {
204 if (netgame)
205 {
206 Printf("You cannot record a new game while in a netgame.");
207 return;
208 }
209 if (argv.argc() > 2)
210 {
211 try
212 {
213 if (!P_CheckMapData(argv[2]))
214 {
215 Printf("No map %s\n", argv[2]);
216 }
217 else
218 {
219 G_DeferedInitNew(argv[2]);
220 gameaction = ga_recordgame;
221 newdemoname = argv[1];
222 newdemomap = argv[2];
223 }
224 }
225 catch (CRecoverableError &error)
226 {
227 if (error.GetMessage())
228 Printf("%s", error.GetMessage());
229 }
230 }
231 else
232 {
233 Printf("Usage: recordmap <filename> <map name>\n");
234 }
235 }
236
237 //==========================================================================
238 //
239 //
240 //==========================================================================
241
CCMD(open)242 CCMD (open)
243 {
244 if (netgame)
245 {
246 Printf ("You cannot use open in multiplayer games.\n");
247 return;
248 }
249 if (argv.argc() > 1)
250 {
251 d_mapname = "file:";
252 d_mapname += argv[1];
253 if (!P_CheckMapData(d_mapname))
254 {
255 Printf ("No map %s\n", d_mapname.GetChars());
256 }
257 else
258 {
259 gameaction = ga_newgame2;
260 d_skill = -1;
261 }
262 }
263 else
264 {
265 Printf ("Usage: open <map file>\n");
266 }
267 }
268
269
270 //==========================================================================
271 //
272 //
273 //==========================================================================
274
G_NewInit()275 void G_NewInit ()
276 {
277 int i;
278
279 // Destory all old player refrences that may still exist
280 TThinkerIterator<APlayerPawn> it(STAT_TRAVELLING);
281 APlayerPawn *pawn, *next;
282
283 next = it.Next();
284 while ((pawn = next) != NULL)
285 {
286 next = it.Next();
287 pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP;
288 pawn->Destroy();
289 }
290
291 G_ClearSnapshots ();
292 ST_SetNeedRefresh();
293 netgame = false;
294 multiplayer = false;
295 if (demoplayback)
296 {
297 C_RestoreCVars ();
298 demoplayback = false;
299 D_SetupUserInfo ();
300 }
301 for (i = 0; i < MAXPLAYERS; ++i)
302 {
303 player_t *p = &players[i];
304 userinfo_t saved_ui;
305 saved_ui.TransferFrom(players[i].userinfo);
306 int chasecam = p->cheats & CF_CHASECAM;
307 p->~player_t();
308 ::new(p) player_t;
309 players[i].cheats |= chasecam;
310 players[i].playerstate = PST_DEAD;
311 playeringame[i] = 0;
312 players[i].userinfo.TransferFrom(saved_ui);
313 }
314 BackupSaveName = "";
315 consoleplayer = 0;
316 NextSkill = -1;
317 }
318
319 //==========================================================================
320 //
321 //
322 //==========================================================================
323
G_DoNewGame(void)324 void G_DoNewGame (void)
325 {
326 G_NewInit ();
327 playeringame[consoleplayer] = 1;
328 if (d_skill != -1)
329 {
330 gameskill = d_skill;
331 }
332 G_InitNew (d_mapname, false);
333 gameaction = ga_nothing;
334 }
335
336 //==========================================================================
337 //
338 // Initializes player classes in case they are random.
339 // This gets called at the start of a new game, and the classes
340 // chosen here are used for the remainder of a single-player
341 // or coop game. These are ignored for deathmatch.
342 //
343 //==========================================================================
344
345
InitPlayerClasses()346 static void InitPlayerClasses ()
347 {
348 if (!savegamerestore)
349 {
350 for (int i = 0; i < MAXPLAYERS; ++i)
351 {
352 SinglePlayerClass[i] = players[i].userinfo.GetPlayerClassNum();
353 if (SinglePlayerClass[i] < 0 || !playeringame[i])
354 {
355 SinglePlayerClass[i] = (pr_classchoice()) % PlayerClasses.Size ();
356 }
357 players[i].cls = NULL;
358 players[i].CurrentPlayerClass = SinglePlayerClass[i];
359 }
360 }
361 }
362
363 //==========================================================================
364 //
365 //
366 //==========================================================================
367
G_InitNew(const char * mapname,bool bTitleLevel)368 void G_InitNew (const char *mapname, bool bTitleLevel)
369 {
370 bool wantFast;
371 int i;
372
373 G_ClearHubInfo();
374 if (!savegamerestore)
375 {
376 G_ClearSnapshots ();
377 P_RemoveDefereds ();
378
379 // [RH] Mark all levels as not visited
380 for (unsigned int i = 0; i < wadlevelinfos.Size(); i++)
381 wadlevelinfos[i].flags = wadlevelinfos[i].flags & ~LEVEL_VISITED;
382 }
383
384 UnlatchCVars ();
385 G_VerifySkill();
386 UnlatchCVars ();
387
388 if (paused)
389 {
390 paused = 0;
391 S_ResumeSound (false);
392 }
393
394 if (StatusBar != NULL)
395 {
396 StatusBar->Destroy();
397 StatusBar = NULL;
398 }
399 if (bTitleLevel)
400 {
401 StatusBar = new DBaseStatusBar (0);
402 }
403 else if (SBarInfoScript[SCRIPT_CUSTOM] != NULL)
404 {
405 int cstype = SBarInfoScript[SCRIPT_CUSTOM]->GetGameType();
406
407 //Did the user specify a "base"
408 if(cstype == GAME_Strife)
409 {
410 StatusBar = CreateStrifeStatusBar();
411 }
412 else if(cstype == GAME_Any) //Use the default, empty or custom.
413 {
414 StatusBar = CreateCustomStatusBar(SCRIPT_CUSTOM);
415 }
416 else
417 {
418 StatusBar = CreateCustomStatusBar(SCRIPT_DEFAULT);
419 }
420 }
421 if (StatusBar == NULL)
422 {
423 if (gameinfo.gametype & (GAME_DoomChex|GAME_Heretic|GAME_Hexen))
424 {
425 StatusBar = CreateCustomStatusBar (SCRIPT_DEFAULT);
426 }
427 else if (gameinfo.gametype == GAME_Strife)
428 {
429 StatusBar = CreateStrifeStatusBar ();
430 }
431 else
432 {
433 StatusBar = new DBaseStatusBar (0);
434 }
435 }
436 GC::WriteBarrier(StatusBar);
437 StatusBar->AttachToPlayer (&players[consoleplayer]);
438 StatusBar->NewGame ();
439 setsizeneeded = true;
440
441 if (gameinfo.gametype == GAME_Strife || (SBarInfoScript[SCRIPT_CUSTOM] != NULL && SBarInfoScript[SCRIPT_CUSTOM]->GetGameType() == GAME_Strife))
442 {
443 // Set the initial quest log text for Strife.
444 for (i = 0; i < MAXPLAYERS; ++i)
445 {
446 players[i].SetLogText ("Find help");
447 }
448 }
449
450 // [RH] If this map doesn't exist, bomb out
451 if (!P_CheckMapData(mapname))
452 {
453 I_Error ("Could not find map %s\n", mapname);
454 }
455
456 wantFast = !!G_SkillProperty(SKILLP_FastMonsters);
457 GameSpeed = wantFast ? SPEED_Fast : SPEED_Normal;
458
459 if (!savegamerestore)
460 {
461 if (!netgame && !demorecording && !demoplayback)
462 {
463 // [RH] Change the random seed for each new single player game
464 // [ED850] The demo already sets the RNG.
465 rngseed = use_staticrng ? staticrngseed : (rngseed + 1);
466 }
467 FRandom::StaticClearRandom ();
468 P_ClearACSVars(true);
469 level.time = 0;
470 level.maptime = 0;
471 level.totaltime = 0;
472
473 if (!multiplayer || !deathmatch)
474 {
475 InitPlayerClasses ();
476 }
477
478 // force players to be initialized upon first level load
479 for (i = 0; i < MAXPLAYERS; i++)
480 players[i].playerstate = PST_ENTER; // [BC]
481
482 STAT_StartNewGame(mapname);
483 }
484
485 usergame = !bTitleLevel; // will be set false if a demo
486 paused = 0;
487 demoplayback = false;
488 automapactive = false;
489 viewactive = true;
490 V_SetBorderNeedRefresh();
491
492 //Added by MC: Initialize bots.
493 if (!deathmatch)
494 {
495 bglobal.Init ();
496 }
497
498 level.MapName = mapname;
499 if (bTitleLevel)
500 {
501 gamestate = GS_TITLELEVEL;
502 }
503 else if (gamestate != GS_STARTUP)
504 {
505 gamestate = GS_LEVEL;
506 }
507 G_DoLoadLevel (0, false);
508 }
509
510 //
511 // G_DoCompleted
512 //
513 static FString nextlevel;
514 static int startpos; // [RH] Support for multiple starts per level
515 extern int NoWipe; // [RH] Don't wipe when travelling in hubs
516 static int changeflags;
517 static bool unloading;
518
519 //==========================================================================
520 //
521 // [RH] The position parameter to these next three functions should
522 // match the first parameter of the single player start spots
523 // that should appear in the next map.
524 //
525 //==========================================================================
526
G_ChangeLevel(const char * levelname,int position,int flags,int nextSkill)527 void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill)
528 {
529 level_info_t *nextinfo = NULL;
530
531 if (unloading)
532 {
533 Printf (TEXTCOLOR_RED "Unloading scripts cannot exit the level again.\n");
534 return;
535 }
536 if (gameaction == ga_completed) // do not exit multiple times.
537 {
538 return;
539 }
540
541 if (levelname == NULL || *levelname == 0)
542 {
543 // end the game
544 levelname = NULL;
545 if (!level.NextMap.Compare("enDSeQ",6))
546 {
547 nextlevel = level.NextMap; // If there is already an end sequence please leave it alone!
548 }
549 else
550 {
551 nextlevel.Format("enDSeQ%04x", int(gameinfo.DefaultEndSequence));
552 }
553 }
554 else if (strncmp(levelname, "enDSeQ", 6) != 0)
555 {
556 FString reallevelname = levelname;
557 CheckWarpTransMap(reallevelname, true);
558 nextinfo = FindLevelInfo (reallevelname, false);
559 if (nextinfo != NULL)
560 {
561 level_info_t *nextredir = nextinfo->CheckLevelRedirect();
562 if (nextredir != NULL)
563 {
564 nextinfo = nextredir;
565 }
566 nextlevel = nextinfo->MapName;
567 }
568 else
569 {
570 nextlevel = levelname;
571 }
572 }
573 else
574 {
575 nextlevel = levelname;
576 }
577
578 if (nextSkill != -1)
579 NextSkill = nextSkill;
580
581 if (flags & CHANGELEVEL_NOINTERMISSION)
582 {
583 level.flags |= LEVEL_NOINTERMISSION;
584 }
585
586 cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
587 cluster_info_t *nextcluster = nextinfo? FindClusterInfo (nextinfo->cluster) : NULL;
588
589 startpos = position;
590 gameaction = ga_completed;
591
592 if (nextinfo != NULL)
593 {
594 if (thiscluster != nextcluster || (thiscluster && !(thiscluster->flags & CLUSTER_HUB)))
595 {
596 if (nextinfo->flags2 & LEVEL2_RESETINVENTORY)
597 {
598 flags |= CHANGELEVEL_RESETINVENTORY;
599 }
600 if (nextinfo->flags2 & LEVEL2_RESETHEALTH)
601 {
602 flags |= CHANGELEVEL_RESETHEALTH;
603 }
604 }
605 }
606 changeflags = flags;
607
608 bglobal.End(); //Added by MC:
609
610 // [RH] Give scripts a chance to do something
611 unloading = true;
612 FBehavior::StaticStartTypedScripts (SCRIPT_Unloading, NULL, false, 0, true);
613 unloading = false;
614
615 STAT_ChangeLevel(nextlevel);
616
617 if (thiscluster && (thiscluster->flags & CLUSTER_HUB))
618 {
619 if ((level.flags & LEVEL_NOINTERMISSION) || (nextcluster == thiscluster))
620 NoWipe = 35;
621 D_DrawIcon = "TELEICON";
622 }
623
624 for(int i = 0; i < MAXPLAYERS; i++)
625 {
626 if (playeringame[i])
627 {
628 player_t *player = &players[i];
629
630 // Un-crouch all players here.
631 player->Uncrouch();
632
633 // If this is co-op, respawn any dead players now so they can
634 // keep their inventory on the next map.
635 if ((multiplayer || level.flags2 & LEVEL2_ALLOWRESPAWN) && !deathmatch && player->playerstate == PST_DEAD)
636 {
637 // Copied from the end of P_DeathThink [[
638 player->cls = NULL; // Force a new class if the player is using a random class
639 player->playerstate = PST_REBORN;
640 if (player->mo->special1 > 2)
641 {
642 player->mo->special1 = 0;
643 }
644 // ]]
645 G_DoReborn(i, false);
646 }
647 }
648 }
649 }
650
651 //==========================================================================
652 //
653 //
654 //==========================================================================
655
G_GetExitMap()656 const char *G_GetExitMap()
657 {
658 return level.NextMap;
659 }
660
G_GetSecretExitMap()661 const char *G_GetSecretExitMap()
662 {
663 const char *nextmap = level.NextMap;
664
665 if (level.NextSecretMap.Len() > 0)
666 {
667 if (P_CheckMapData(level.NextSecretMap))
668 {
669 nextmap = level.NextSecretMap;
670 }
671 }
672 return nextmap;
673 }
674
675 //==========================================================================
676 //
677 //
678 //==========================================================================
679
G_ExitLevel(int position,bool keepFacing)680 void G_ExitLevel (int position, bool keepFacing)
681 {
682 G_ChangeLevel(G_GetExitMap(), position, keepFacing ? CHANGELEVEL_KEEPFACING : 0);
683 }
684
G_SecretExitLevel(int position)685 void G_SecretExitLevel (int position)
686 {
687 G_ChangeLevel(G_GetSecretExitMap(), position, 0);
688 }
689
690 //==========================================================================
691 //
692 //
693 //==========================================================================
694
G_DoCompleted(void)695 void G_DoCompleted (void)
696 {
697 int i;
698
699 gameaction = ga_nothing;
700
701 if ( gamestate == GS_DEMOSCREEN
702 || gamestate == GS_FULLCONSOLE
703 || gamestate == GS_STARTUP)
704 {
705 return;
706 }
707
708 if (gamestate == GS_TITLELEVEL)
709 {
710 level.MapName = nextlevel;
711 G_DoLoadLevel (startpos, false);
712 startpos = 0;
713 viewactive = true;
714 return;
715 }
716
717 // [RH] Mark this level as having been visited
718 if (!(level.flags & LEVEL_CHANGEMAPCHEAT))
719 FindLevelInfo (level.MapName)->flags |= LEVEL_VISITED;
720
721 if (automapactive)
722 AM_Stop ();
723
724 wminfo.finished_ep = level.cluster - 1;
725 wminfo.LName0 = TexMan[TexMan.CheckForTexture(level.info->PName, FTexture::TEX_MiscPatch)];
726 wminfo.current = level.MapName;
727
728 if (deathmatch &&
729 (dmflags & DF_SAME_LEVEL) &&
730 !(level.flags & LEVEL_CHANGEMAPCHEAT))
731 {
732 wminfo.next = level.MapName;
733 wminfo.LName1 = wminfo.LName0;
734 }
735 else
736 {
737 level_info_t *nextinfo = FindLevelInfo (nextlevel, false);
738 if (nextinfo == NULL || strncmp (nextlevel, "enDSeQ", 6) == 0)
739 {
740 wminfo.next = nextlevel;
741 wminfo.LName1 = NULL;
742 }
743 else
744 {
745 wminfo.next = nextinfo->MapName;
746 wminfo.LName1 = TexMan[TexMan.CheckForTexture(nextinfo->PName, FTexture::TEX_MiscPatch)];
747 }
748 }
749
750 CheckWarpTransMap (wminfo.next, true);
751 nextlevel = wminfo.next;
752
753 wminfo.next_ep = FindLevelInfo (wminfo.next)->cluster - 1;
754 wminfo.maxkills = level.total_monsters;
755 wminfo.maxitems = level.total_items;
756 wminfo.maxsecret = level.total_secrets;
757 wminfo.maxfrags = 0;
758 wminfo.partime = TICRATE * level.partime;
759 wminfo.sucktime = level.sucktime;
760 wminfo.pnum = consoleplayer;
761 wminfo.totaltime = level.totaltime;
762
763 for (i=0 ; i<MAXPLAYERS ; i++)
764 {
765 wminfo.plyr[i].in = playeringame[i];
766 wminfo.plyr[i].skills = players[i].killcount;
767 wminfo.plyr[i].sitems = players[i].itemcount;
768 wminfo.plyr[i].ssecret = players[i].secretcount;
769 wminfo.plyr[i].stime = level.time;
770 memcpy (wminfo.plyr[i].frags, players[i].frags
771 , sizeof(wminfo.plyr[i].frags));
772 wminfo.plyr[i].fragcount = players[i].fragcount;
773 }
774
775 // [RH] If we're in a hub and staying within that hub, take a snapshot
776 // of the level. If we're traveling to a new hub, take stuff from
777 // the player and clear the world vars. If this is just an
778 // ordinary cluster (not a hub), take stuff from the player, but
779 // leave the world vars alone.
780 cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
781 cluster_info_t *nextcluster = FindClusterInfo (wminfo.next_ep+1); // next_ep is cluster-1
782 EFinishLevelType mode;
783
784 if (thiscluster != nextcluster || deathmatch ||
785 !(thiscluster->flags & CLUSTER_HUB))
786 {
787 if (nextcluster->flags & CLUSTER_HUB)
788 {
789 mode = FINISH_NextHub;
790 }
791 else
792 {
793 mode = FINISH_NoHub;
794 }
795 }
796 else
797 {
798 mode = FINISH_SameHub;
799 }
800
801 // Intermission stats for entire hubs
802 G_LeavingHub(mode, thiscluster, &wminfo);
803
804 for (i = 0; i < MAXPLAYERS; i++)
805 {
806 if (playeringame[i])
807 { // take away appropriate inventory
808 G_PlayerFinishLevel (i, mode, changeflags);
809 }
810 }
811
812 if (mode == FINISH_SameHub)
813 { // Remember the level's state for re-entry.
814 if (!(level.flags2 & LEVEL2_FORGETSTATE))
815 {
816 G_SnapshotLevel ();
817 // Do not free any global strings this level might reference
818 // while it's not loaded.
819 FBehavior::StaticLockLevelVarStrings();
820 }
821 else
822 { // Make sure we don't have a snapshot lying around from before.
823 level.info->ClearSnapshot();
824 }
825 }
826 else
827 { // Forget the states of all existing levels.
828 G_ClearSnapshots ();
829
830 if (mode == FINISH_NextHub)
831 { // Reset world variables for the new hub.
832 P_ClearACSVars(false);
833 }
834 level.time = 0;
835 level.maptime = 0;
836 }
837
838 if (!deathmatch &&
839 ((level.flags & LEVEL_NOINTERMISSION) ||
840 ((nextcluster == thiscluster) && (thiscluster->flags & CLUSTER_HUB))))
841 {
842 G_WorldDone ();
843 return;
844 }
845
846 gamestate = GS_INTERMISSION;
847 viewactive = false;
848 automapactive = false;
849
850 // [RH] If you ever get a statistics driver operational, adapt this.
851 // if (statcopy)
852 // memcpy (statcopy, &wminfo, sizeof(wminfo));
853
854 WI_Start (&wminfo);
855 }
856
857 //==========================================================================
858 //
859 //
860 //==========================================================================
861
862 class DAutosaver : public DThinker
863 {
864 DECLARE_CLASS (DAutosaver, DThinker)
865 public:
866 void Tick ();
867 };
868
IMPLEMENT_CLASS(DAutosaver)869 IMPLEMENT_CLASS (DAutosaver)
870
871 void DAutosaver::Tick ()
872 {
873 Net_WriteByte (DEM_CHECKAUTOSAVE);
874 Destroy ();
875 }
876
877 //==========================================================================
878 //
879 // G_DoLoadLevel
880 //
881 //==========================================================================
882
883 extern gamestate_t wipegamestate;
884
G_DoLoadLevel(int position,bool autosave)885 void G_DoLoadLevel (int position, bool autosave)
886 {
887 static int lastposition = 0;
888 gamestate_t oldgs = gamestate;
889 int i;
890
891 if (NextSkill >= 0)
892 {
893 UCVarValue val;
894 val.Int = NextSkill;
895 gameskill.ForceSet (val, CVAR_Int);
896 NextSkill = -1;
897 }
898
899 if (position == -1)
900 position = lastposition;
901 else
902 lastposition = position;
903
904 G_InitLevelLocals ();
905 StatusBar->DetachAllMessages ();
906
907 // Force 'teamplay' to 'true' if need be.
908 if (level.flags2 & LEVEL2_FORCETEAMPLAYON)
909 teamplay = true;
910
911 // Force 'teamplay' to 'false' if need be.
912 if (level.flags2 & LEVEL2_FORCETEAMPLAYOFF)
913 teamplay = false;
914
915 FString mapname = level.MapName;
916 mapname.ToLower();
917 Printf (
918 "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
919 "\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"
920 TEXTCOLOR_BOLD "%s - %s\n\n",
921 mapname.GetChars(), level.LevelName.GetChars());
922
923 if (wipegamestate == GS_LEVEL)
924 wipegamestate = GS_FORCEWIPE;
925
926 if (gamestate != GS_TITLELEVEL)
927 {
928 gamestate = GS_LEVEL;
929 }
930
931 // Set the sky map.
932 // First thing, we have a dummy sky texture name,
933 // a flat. The data is in the WAD only because
934 // we look for an actual index, instead of simply
935 // setting one.
936 skyflatnum = TexMan.GetTexture (gameinfo.SkyFlatName, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
937
938 // DOOM determines the sky texture to be used
939 // depending on the current episode and the game version.
940 // [RH] Fetch sky parameters from FLevelLocals.
941 sky1texture = level.skytexture1;
942 sky2texture = level.skytexture2;
943
944 // [RH] Set up details about sky rendering
945 R_InitSkyMap ();
946
947 for (i = 0; i < MAXPLAYERS; i++)
948 {
949 if (playeringame[i] && (deathmatch || players[i].playerstate == PST_DEAD))
950 players[i].playerstate = PST_ENTER; // [BC]
951 memset (players[i].frags,0,sizeof(players[i].frags));
952 if (!(dmflags2 & DF2_YES_KEEPFRAGS) && (alwaysapplydmflags || deathmatch))
953 players[i].fragcount = 0;
954 }
955
956 if (changeflags & CHANGELEVEL_NOMONSTERS)
957 {
958 level.flags2 |= LEVEL2_NOMONSTERS;
959 }
960 else
961 {
962 level.flags2 &= ~LEVEL2_NOMONSTERS;
963 }
964 if (changeflags & CHANGELEVEL_PRERAISEWEAPON)
965 {
966 level.flags2 |= LEVEL2_PRERAISEWEAPON;
967 }
968
969 level.maptime = 0;
970 P_SetupLevel (level.MapName, position);
971
972 AM_LevelInit();
973
974 // [RH] Start lightning, if MAPINFO tells us to
975 if (level.flags & LEVEL_STARTLIGHTNING)
976 {
977 P_StartLightning ();
978 }
979
980 gameaction = ga_nothing;
981
982 // clear cmd building stuff
983 ResetButtonStates ();
984
985 SendItemUse = NULL;
986 SendItemDrop = NULL;
987 mousex = mousey = 0;
988 sendpause = sendsave = sendturn180 = SendLand = false;
989 LocalViewAngle = 0;
990 LocalViewPitch = 0;
991 paused = 0;
992
993 //Added by MC: Initialize bots.
994 if (deathmatch)
995 {
996 bglobal.Init ();
997 }
998
999 if (timingdemo)
1000 {
1001 static bool firstTime = true;
1002
1003 if (firstTime)
1004 {
1005 starttime = I_GetTime (false);
1006 firstTime = false;
1007 }
1008 }
1009
1010 level.starttime = gametic;
1011 G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level.
1012 G_FinishTravel ();
1013 // For each player, if they are viewing through a player, make sure it is themselves.
1014 for (int ii = 0; ii < MAXPLAYERS; ++ii)
1015 {
1016 if (playeringame[ii] && (players[ii].camera == NULL || players[ii].camera->player != NULL))
1017 {
1018 players[ii].camera = players[ii].mo;
1019 }
1020 }
1021 StatusBar->AttachToPlayer (&players[consoleplayer]);
1022 P_DoDeferedScripts (); // [RH] Do script actions that were triggered on another map.
1023
1024 if (demoplayback || oldgs == GS_STARTUP || oldgs == GS_TITLELEVEL)
1025 C_HideConsole ();
1026
1027 C_FlushDisplay ();
1028
1029 // [RH] Always save the game when entering a new level.
1030 if (autosave && !savegamerestore && disableautosave < 1)
1031 {
1032 DAutosaver GCCNOWARN *dummy = new DAutosaver;
1033 }
1034 }
1035
1036
1037 //==========================================================================
1038 //
1039 // G_WorldDone
1040 //
1041 //==========================================================================
1042
G_WorldDone(void)1043 void G_WorldDone (void)
1044 {
1045 cluster_info_t *nextcluster;
1046 cluster_info_t *thiscluster;
1047
1048 gameaction = ga_worlddone;
1049
1050 if (level.flags & LEVEL_CHANGEMAPCHEAT)
1051 return;
1052
1053 thiscluster = FindClusterInfo (level.cluster);
1054
1055 if (strncmp (nextlevel, "enDSeQ", 6) == 0)
1056 {
1057 FName endsequence = ENamedName(strtol(nextlevel.GetChars()+6, NULL, 16));
1058 // Strife needs a special case here to choose between good and sad ending. Bad is handled elsewherw.
1059 if (endsequence == NAME_Inter_Strife)
1060 {
1061 if (players[0].mo->FindInventory (QuestItemClasses[24]) ||
1062 players[0].mo->FindInventory (QuestItemClasses[27]))
1063 {
1064 endsequence = NAME_Inter_Strife_Good;
1065 }
1066 else
1067 {
1068 endsequence = NAME_Inter_Strife_Sad;
1069 }
1070 }
1071
1072 F_StartFinale (thiscluster->MessageMusic, thiscluster->musicorder,
1073 thiscluster->cdtrack, thiscluster->cdid,
1074 thiscluster->FinaleFlat, thiscluster->ExitText,
1075 thiscluster->flags & CLUSTER_EXITTEXTINLUMP,
1076 thiscluster->flags & CLUSTER_FINALEPIC,
1077 thiscluster->flags & CLUSTER_LOOKUPEXITTEXT,
1078 true, endsequence);
1079 }
1080 else
1081 {
1082 nextcluster = FindClusterInfo (FindLevelInfo (nextlevel)->cluster);
1083
1084 if (nextcluster->cluster != level.cluster && !deathmatch)
1085 {
1086 // Only start the finale if the next level's cluster is different
1087 // than the current one and we're not in deathmatch.
1088 if (nextcluster->EnterText.IsNotEmpty())
1089 {
1090 F_StartFinale (nextcluster->MessageMusic, nextcluster->musicorder,
1091 nextcluster->cdtrack, nextcluster->cdid,
1092 nextcluster->FinaleFlat, nextcluster->EnterText,
1093 nextcluster->flags & CLUSTER_ENTERTEXTINLUMP,
1094 nextcluster->flags & CLUSTER_FINALEPIC,
1095 nextcluster->flags & CLUSTER_LOOKUPENTERTEXT,
1096 false);
1097 }
1098 else if (thiscluster->ExitText.IsNotEmpty())
1099 {
1100 F_StartFinale (thiscluster->MessageMusic, thiscluster->musicorder,
1101 thiscluster->cdtrack, nextcluster->cdid,
1102 thiscluster->FinaleFlat, thiscluster->ExitText,
1103 thiscluster->flags & CLUSTER_EXITTEXTINLUMP,
1104 thiscluster->flags & CLUSTER_FINALEPIC,
1105 thiscluster->flags & CLUSTER_LOOKUPEXITTEXT,
1106 false);
1107 }
1108 }
1109 }
1110 }
1111
1112 //==========================================================================
1113 //
1114 //
1115 //==========================================================================
1116
G_DoWorldDone(void)1117 void G_DoWorldDone (void)
1118 {
1119 gamestate = GS_LEVEL;
1120 if (wminfo.next[0] == 0)
1121 {
1122 // Don't crash if no next map is given. Just repeat the current one.
1123 Printf ("No next map specified.\n");
1124 }
1125 else
1126 {
1127 level.MapName = nextlevel;
1128 }
1129 G_StartTravel ();
1130 G_DoLoadLevel (startpos, true);
1131 startpos = 0;
1132 gameaction = ga_nothing;
1133 viewactive = true;
1134 }
1135
1136 //==========================================================================
1137 //
1138 // G_StartTravel
1139 //
1140 // Moves players (and eventually their inventory) to a different statnum,
1141 // so they will not be destroyed when switching levels. This only applies
1142 // to real players, not voodoo dolls.
1143 //
1144 //==========================================================================
1145
G_StartTravel()1146 void G_StartTravel ()
1147 {
1148 if (deathmatch)
1149 return;
1150
1151 for (int i = 0; i < MAXPLAYERS; ++i)
1152 {
1153 if (playeringame[i])
1154 {
1155 AActor *pawn = players[i].mo;
1156 AInventory *inv;
1157 players[i].camera = NULL;
1158
1159 // Only living players travel. Dead ones get a new body on the new level.
1160 if (players[i].health > 0)
1161 {
1162 pawn->UnlinkFromWorld ();
1163 P_DelSector_List ();
1164 int tid = pawn->tid; // Save TID
1165 pawn->RemoveFromHash ();
1166 pawn->tid = tid; // Restore TID (but no longer linked into the hash chain)
1167 pawn->ChangeStatNum (STAT_TRAVELLING);
1168
1169 for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory)
1170 {
1171 inv->ChangeStatNum (STAT_TRAVELLING);
1172 inv->UnlinkFromWorld ();
1173 P_DelSector_List ();
1174 }
1175 }
1176 }
1177 }
1178
1179 bglobal.StartTravel ();
1180 }
1181
1182 //==========================================================================
1183 //
1184 // G_FinishTravel
1185 //
1186 // Moves any travelling players so that they occupy their newly-spawned
1187 // copies' locations, destroying the new players in the process (because
1188 // they are really fake placeholders to show where the travelling players
1189 // should go).
1190 //
1191 //==========================================================================
1192
G_FinishTravel()1193 void G_FinishTravel ()
1194 {
1195 TThinkerIterator<APlayerPawn> it (STAT_TRAVELLING);
1196 APlayerPawn *pawn, *pawndup, *oldpawn, *next;
1197 AInventory *inv;
1198 FPlayerStart *start;
1199 int pnum;
1200
1201 next = it.Next ();
1202 while ( (pawn = next) != NULL)
1203 {
1204 next = it.Next ();
1205 pnum = int(pawn->player - players);
1206 pawn->ChangeStatNum (STAT_PLAYER);
1207 pawndup = pawn->player->mo;
1208 start = NULL;
1209 assert (pawn != pawndup);
1210 if (pawndup == NULL)
1211 { // Oh no! there was no start for this player!
1212 start = G_PickPlayerStart(pnum, PPS_FORCERANDOM);
1213 if (start != NULL) pawndup = P_SpawnPlayer(start, pnum, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0);
1214 if (pawndup == NULL)
1215 {
1216 pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP;
1217 pawn->Destroy();
1218 continue;
1219 }
1220 }
1221
1222 if (start == NULL) start = G_PickPlayerStart(pnum, 0);
1223 oldpawn = pawndup;
1224
1225 // The player being spawned here is a short lived dummy and
1226 // must not start any ENTER script or big problems will happen.
1227 pawndup = P_SpawnPlayer(start, pnum, SPF_TEMPPLAYER);
1228 if (!(changeflags & CHANGELEVEL_KEEPFACING))
1229 {
1230 pawn->angle = pawndup->angle;
1231 pawn->pitch = pawndup->pitch;
1232 }
1233 pawn->SetXYZ(pawndup->X(), pawndup->Y(), pawndup->Z());
1234 pawn->velx = pawndup->velx;
1235 pawn->vely = pawndup->vely;
1236 pawn->velz = pawndup->velz;
1237 pawn->Sector = pawndup->Sector;
1238 pawn->floorz = pawndup->floorz;
1239 pawn->ceilingz = pawndup->ceilingz;
1240 pawn->dropoffz = pawndup->dropoffz;
1241 pawn->floorsector = pawndup->floorsector;
1242 pawn->floorpic = pawndup->floorpic;
1243 pawn->floorterrain = pawndup->floorterrain;
1244 pawn->ceilingsector = pawndup->ceilingsector;
1245 pawn->ceilingpic = pawndup->ceilingpic;
1246 pawn->floorclip = pawndup->floorclip;
1247 pawn->waterlevel = pawndup->waterlevel;
1248 pawn->target = NULL;
1249 pawn->lastenemy = NULL;
1250 pawn->player->mo = pawn;
1251 pawn->player->camera = pawn;
1252 pawn->player->viewheight = pawn->ViewHeight;
1253 pawn->flags2 &= ~MF2_BLASTED;
1254 DObject::StaticPointerSubstitution (oldpawn, pawn);
1255 oldpawn->Destroy();
1256 pawndup->Destroy ();
1257 pawn->LinkToWorld ();
1258 pawn->AddToHash ();
1259 pawn->SetState(pawn->SpawnState);
1260 pawn->player->SendPitchLimits();
1261
1262 for (inv = pawn->Inventory; inv != NULL; inv = inv->Inventory)
1263 {
1264 inv->ChangeStatNum (STAT_INVENTORY);
1265 inv->LinkToWorld ();
1266 inv->Travelled ();
1267 }
1268 if (ib_compatflags & BCOMPATF_RESETPLAYERSPEED)
1269 {
1270 pawn->Speed = pawn->GetDefault()->Speed;
1271 }
1272 if (level.FromSnapshot)
1273 {
1274 FBehavior::StaticStartTypedScripts (SCRIPT_Return, pawn, true);
1275 }
1276 }
1277
1278 bglobal.FinishTravel ();
1279 }
1280
1281 //==========================================================================
1282 //
1283 //
1284 //==========================================================================
1285
G_InitLevelLocals()1286 void G_InitLevelLocals ()
1287 {
1288 level_info_t *info;
1289
1290 BaseBlendA = 0.0f; // Remove underwater blend effect, if any
1291 NormalLight.Maps = realcolormaps;
1292
1293 // [BB] Instead of just setting the color, we also have to reset Desaturate and build the lights.
1294 NormalLight.ChangeColor (PalEntry (255, 255, 255), 0);
1295
1296 level.gravity = sv_gravity * 35/TICRATE;
1297 level.aircontrol = (fixed_t)(sv_aircontrol * 65536.f);
1298 level.teamdamage = teamdamage;
1299 level.flags = 0;
1300 level.flags2 = 0;
1301 level.flags3 = 0;
1302
1303 info = FindLevelInfo (level.MapName);
1304
1305 level.info = info;
1306 level.skyspeed1 = info->skyspeed1;
1307 level.skyspeed2 = info->skyspeed2;
1308 level.skytexture1 = TexMan.GetTexture(info->SkyPic1, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
1309 level.skytexture2 = TexMan.GetTexture(info->SkyPic2, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
1310 level.fadeto = info->fadeto;
1311 level.cdtrack = info->cdtrack;
1312 level.cdid = info->cdid;
1313 level.FromSnapshot = false;
1314 if (level.fadeto == 0)
1315 {
1316 R_SetDefaultColormap (info->FadeTable);
1317 if (strnicmp (info->FadeTable, "COLORMAP", 8) != 0)
1318 {
1319 level.flags |= LEVEL_HASFADETABLE;
1320 }
1321 /*
1322 }
1323 else
1324 {
1325 NormalLight.ChangeFade (level.fadeto);
1326 */
1327 }
1328 level.airsupply = info->airsupply*TICRATE;
1329 level.outsidefog = info->outsidefog;
1330 level.WallVertLight = info->WallVertLight*2;
1331 level.WallHorizLight = info->WallHorizLight*2;
1332 if (info->gravity != 0.f)
1333 {
1334 level.gravity = info->gravity * 35/TICRATE;
1335 }
1336 if (info->aircontrol != 0.f)
1337 {
1338 level.aircontrol = (fixed_t)(info->aircontrol * 65536.f);
1339 }
1340 if (info->teamdamage != 0.f)
1341 {
1342 level.teamdamage = info->teamdamage;
1343 }
1344
1345 G_AirControlChanged ();
1346
1347 cluster_info_t *clus = FindClusterInfo (info->cluster);
1348
1349 level.partime = info->partime;
1350 level.sucktime = info->sucktime;
1351 level.cluster = info->cluster;
1352 level.clusterflags = clus ? clus->flags : 0;
1353 level.flags |= info->flags;
1354 level.flags2 |= info->flags2;
1355 level.flags3 |= info->flags3;
1356 level.levelnum = info->levelnum;
1357 level.Music = info->Music;
1358 level.musicorder = info->musicorder;
1359
1360 level.LevelName = level.info->LookupLevelName();
1361 level.NextMap = info->NextMap;
1362 level.NextSecretMap = info->NextSecretMap;
1363
1364 compatflags.Callback();
1365 compatflags2.Callback();
1366
1367 NormalLight.ChangeFade (level.fadeto);
1368
1369 level.DefaultEnvironment = info->DefaultEnvironment;
1370 level.DefaultSkybox = NULL;
1371 }
1372
1373 //==========================================================================
1374 //
1375 //
1376 //==========================================================================
1377
IsJumpingAllowed() const1378 bool FLevelLocals::IsJumpingAllowed() const
1379 {
1380 if (dmflags & DF_NO_JUMP)
1381 return false;
1382 if (dmflags & DF_YES_JUMP)
1383 return true;
1384 return !(level.flags & LEVEL_JUMP_NO);
1385 }
1386
1387 //==========================================================================
1388 //
1389 //
1390 //==========================================================================
1391
IsCrouchingAllowed() const1392 bool FLevelLocals::IsCrouchingAllowed() const
1393 {
1394 if (dmflags & DF_NO_CROUCH)
1395 return false;
1396 if (dmflags & DF_YES_CROUCH)
1397 return true;
1398 return !(level.flags & LEVEL_CROUCH_NO);
1399 }
1400
1401 //==========================================================================
1402 //
1403 //
1404 //==========================================================================
1405
IsFreelookAllowed() const1406 bool FLevelLocals::IsFreelookAllowed() const
1407 {
1408 if (dmflags & DF_NO_FREELOOK)
1409 return false;
1410 if (dmflags & DF_YES_FREELOOK)
1411 return true;
1412 return !(level.flags & LEVEL_FREELOOK_NO);
1413 }
1414
1415 //==========================================================================
1416 //
1417 //
1418 //==========================================================================
1419
CalcMapName(int episode,int level)1420 FString CalcMapName (int episode, int level)
1421 {
1422 FString lumpname;
1423
1424 if (gameinfo.flags & GI_MAPxx)
1425 {
1426 lumpname.Format("MAP%02d", level);
1427 }
1428 else
1429 {
1430 lumpname = "";
1431 lumpname << 'E' << ('0' + episode) << 'M' << ('0' + level);
1432 }
1433 return lumpname;
1434 }
1435
1436 //==========================================================================
1437 //
1438 //
1439 //==========================================================================
1440
G_AirControlChanged()1441 void G_AirControlChanged ()
1442 {
1443 if (level.aircontrol <= 256)
1444 {
1445 level.airfriction = FRACUNIT;
1446 }
1447 else
1448 {
1449 // Friction is inversely proportional to the amount of control
1450 float fric = ((float)level.aircontrol/65536.f) * -0.0941f + 1.0004f;
1451 level.airfriction = (fixed_t)(fric * 65536.f);
1452 }
1453 }
1454
1455 //==========================================================================
1456 //
1457 //
1458 //==========================================================================
1459
G_SerializeLevel(FArchive & arc,bool hubLoad)1460 void G_SerializeLevel (FArchive &arc, bool hubLoad)
1461 {
1462 int i = level.totaltime;
1463
1464 Renderer->StartSerialize(arc);
1465
1466 arc << level.flags
1467 << level.flags2
1468 << level.fadeto
1469 << level.found_secrets
1470 << level.found_items
1471 << level.killed_monsters
1472 << level.gravity
1473 << level.aircontrol
1474 << level.teamdamage
1475 << level.maptime
1476 << i;
1477
1478 if (SaveVersion >= 3313)
1479 {
1480 // This is a player property now
1481 int nextmusic;
1482 arc << nextmusic;
1483 }
1484
1485 // Hub transitions must keep the current total time
1486 if (!hubLoad)
1487 level.totaltime = i;
1488
1489 if (SaveVersion >= 4507)
1490 {
1491 arc << level.skytexture1 << level.skytexture2;
1492 }
1493 else
1494 {
1495 level.skytexture1 = TexMan.GetTexture(arc.ReadName(), FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
1496 level.skytexture2 = TexMan.GetTexture(arc.ReadName(), FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_ReturnFirst);
1497 }
1498 if (arc.IsLoading())
1499 {
1500 sky1texture = level.skytexture1;
1501 sky2texture = level.skytexture2;
1502 R_InitSkyMap();
1503 }
1504
1505 G_AirControlChanged ();
1506
1507 BYTE t;
1508
1509 // Does this level have scrollers?
1510 if (arc.IsStoring ())
1511 {
1512 t = level.Scrolls ? 1 : 0;
1513 arc << t;
1514 }
1515 else
1516 {
1517 arc << t;
1518 if (level.Scrolls)
1519 {
1520 delete[] level.Scrolls;
1521 level.Scrolls = NULL;
1522 }
1523 if (t)
1524 {
1525 level.Scrolls = new FSectorScrollValues[numsectors];
1526 memset (level.Scrolls, 0, sizeof(level.Scrolls)*numsectors);
1527 }
1528 }
1529
1530 FBehavior::StaticSerializeModuleStates (arc);
1531 if (arc.IsLoading()) interpolator.ClearInterpolations();
1532 P_SerializeThinkers (arc, hubLoad);
1533 P_SerializeWorld (arc);
1534 P_SerializePolyobjs (arc);
1535 P_SerializeSubsectors(arc);
1536 StatusBar->Serialize (arc);
1537
1538 if (SaveVersion >= 4222)
1539 { // This must be done *after* thinkers are serialized.
1540 arc << level.DefaultSkybox;
1541 }
1542
1543 arc << level.total_monsters << level.total_items << level.total_secrets;
1544
1545 // Does this level have custom translations?
1546 FRemapTable *trans;
1547 WORD w;
1548 if (arc.IsStoring ())
1549 {
1550 for (unsigned int i = 0; i < translationtables[TRANSLATION_LevelScripted].Size(); ++i)
1551 {
1552 trans = translationtables[TRANSLATION_LevelScripted][i];
1553 if (trans != NULL && !trans->IsIdentity())
1554 {
1555 w = WORD(i);
1556 arc << w;
1557 trans->Serialize(arc);
1558 }
1559 }
1560 w = 0xffff;
1561 arc << w;
1562 }
1563 else
1564 {
1565 while (arc << w, w != 0xffff)
1566 {
1567 trans = translationtables[TRANSLATION_LevelScripted].GetVal(w);
1568 if (trans == NULL)
1569 {
1570 trans = new FRemapTable;
1571 translationtables[TRANSLATION_LevelScripted].SetVal(w, trans);
1572 }
1573 trans->Serialize(arc);
1574 }
1575 }
1576
1577 // This must be saved, too, of course!
1578 FCanvasTextureInfo::Serialize (arc);
1579 AM_SerializeMarkers(arc);
1580
1581 P_SerializePlayers (arc, hubLoad);
1582 P_SerializeSounds (arc);
1583 if (arc.IsLoading())
1584 {
1585 for (i = 0; i < numsectors; i++)
1586 {
1587 P_Recalculate3DFloors(§ors[i]);
1588 }
1589 for (i = 0; i < MAXPLAYERS; ++i)
1590 {
1591 if (playeringame[i] && players[i].mo != NULL)
1592 {
1593 players[i].mo->SetupWeaponSlots();
1594 }
1595 }
1596 }
1597 Renderer->EndSerialize(arc);
1598 }
1599
1600 //==========================================================================
1601 //
1602 // Archives the current level
1603 //
1604 //==========================================================================
1605
G_SnapshotLevel()1606 void G_SnapshotLevel ()
1607 {
1608 if (level.info->snapshot)
1609 delete level.info->snapshot;
1610
1611 if (level.info->isValid())
1612 {
1613 level.info->snapshotVer = SAVEVER;
1614 level.info->snapshot = new FCompressedMemFile;
1615 level.info->snapshot->Open ();
1616
1617 FArchive arc (*level.info->snapshot);
1618
1619 SaveVersion = SAVEVER;
1620 G_SerializeLevel (arc, false);
1621 }
1622 }
1623
1624 //==========================================================================
1625 //
1626 // Unarchives the current level based on its snapshot
1627 // The level should have already been loaded and setup.
1628 //
1629 //==========================================================================
1630
G_UnSnapshotLevel(bool hubLoad)1631 void G_UnSnapshotLevel (bool hubLoad)
1632 {
1633 if (level.info->snapshot == NULL)
1634 return;
1635
1636 if (level.info->isValid())
1637 {
1638 SaveVersion = level.info->snapshotVer;
1639 level.info->snapshot->Reopen ();
1640 FArchive arc (*level.info->snapshot);
1641 if (hubLoad)
1642 arc.SetHubTravel ();
1643 G_SerializeLevel (arc, hubLoad);
1644 arc.Close ();
1645 level.FromSnapshot = true;
1646
1647 TThinkerIterator<APlayerPawn> it;
1648 APlayerPawn *pawn, *next;
1649
1650 next = it.Next();
1651 while ((pawn = next) != 0)
1652 {
1653 next = it.Next();
1654 if (pawn->player == NULL || pawn->player->mo == NULL || !playeringame[pawn->player - players])
1655 {
1656 int i;
1657
1658 // If this isn't the unmorphed original copy of a player, destroy it, because it's extra.
1659 for (i = 0; i < MAXPLAYERS; ++i)
1660 {
1661 if (playeringame[i] && players[i].morphTics && players[i].mo->tracer == pawn)
1662 {
1663 break;
1664 }
1665 }
1666 if (i == MAXPLAYERS)
1667 {
1668 pawn->Destroy ();
1669 }
1670 }
1671 }
1672 }
1673 // No reason to keep the snapshot around once the level's been entered.
1674 level.info->ClearSnapshot();
1675 if (hubLoad)
1676 {
1677 // Unlock ACS global strings that were locked when the snapshot was made.
1678 FBehavior::StaticUnlockLevelVarStrings();
1679 }
1680 }
1681
1682 //==========================================================================
1683 //
1684 //
1685 //==========================================================================
1686
writeSnapShot(FArchive & arc,level_info_t * i)1687 static void writeSnapShot (FArchive &arc, level_info_t *i)
1688 {
1689 arc << i->snapshotVer << i->MapName;
1690 i->snapshot->Serialize (arc);
1691 }
1692
1693 //==========================================================================
1694 //
1695 //
1696 //==========================================================================
1697
G_WriteSnapshots(FILE * file)1698 void G_WriteSnapshots (FILE *file)
1699 {
1700 unsigned int i;
1701
1702 for (i = 0; i < wadlevelinfos.Size(); i++)
1703 {
1704 if (wadlevelinfos[i].snapshot)
1705 {
1706 FPNGChunkArchive arc (file, SNAP_ID);
1707 writeSnapShot (arc, (level_info_t *)&wadlevelinfos[i]);
1708 }
1709 }
1710 if (TheDefaultLevelInfo.snapshot != NULL)
1711 {
1712 FPNGChunkArchive arc (file, DSNP_ID);
1713 writeSnapShot(arc, &TheDefaultLevelInfo);
1714 }
1715
1716 FPNGChunkArchive *arc = NULL;
1717
1718 // Write out which levels have been visited
1719 for (i = 0; i < wadlevelinfos.Size(); ++i)
1720 {
1721 if (wadlevelinfos[i].flags & LEVEL_VISITED)
1722 {
1723 if (arc == NULL)
1724 {
1725 arc = new FPNGChunkArchive (file, VIST_ID);
1726 }
1727 (*arc) << wadlevelinfos[i].MapName;
1728 }
1729 }
1730
1731 if (arc != NULL)
1732 {
1733 FString empty = "";
1734 (*arc) << empty;
1735 delete arc;
1736 }
1737
1738 // Store player classes to be used when spawning a random class
1739 if (multiplayer)
1740 {
1741 FPNGChunkArchive arc2 (file, RCLS_ID);
1742 for (i = 0; i < MAXPLAYERS; ++i)
1743 {
1744 SBYTE cnum = SinglePlayerClass[i];
1745 arc2 << cnum;
1746 }
1747 }
1748
1749 // Store player classes that are currently in use
1750 FPNGChunkArchive arc3 (file, PCLS_ID);
1751 for (i = 0; i < MAXPLAYERS; ++i)
1752 {
1753 BYTE pnum;
1754 if (playeringame[i])
1755 {
1756 pnum = i;
1757 arc3 << pnum;
1758 arc3.UserWriteClass (players[i].cls);
1759 }
1760 pnum = 255;
1761 arc3 << pnum;
1762 }
1763 }
1764
1765 //==========================================================================
1766 //
1767 //
1768 //==========================================================================
1769
G_ReadSnapshots(PNGHandle * png)1770 void G_ReadSnapshots (PNGHandle *png)
1771 {
1772 DWORD chunkLen;
1773 BYTE namelen;
1774 char mapname[256];
1775 FString MapName;
1776 level_info_t *i;
1777
1778 G_ClearSnapshots ();
1779
1780 chunkLen = (DWORD)M_FindPNGChunk (png, SNAP_ID);
1781 while (chunkLen != 0)
1782 {
1783 FPNGChunkArchive arc (png->File->GetFile(), SNAP_ID, chunkLen);
1784 DWORD snapver;
1785
1786 arc << snapver;
1787 if (SaveVersion < 4508)
1788 {
1789 arc << namelen;
1790 arc.Read(mapname, namelen);
1791 mapname[namelen] = 0;
1792 MapName = mapname;
1793 }
1794 else arc << MapName;
1795 i = FindLevelInfo (MapName);
1796 i->snapshotVer = snapver;
1797 i->snapshot = new FCompressedMemFile;
1798 i->snapshot->Serialize (arc);
1799 chunkLen = (DWORD)M_NextPNGChunk (png, SNAP_ID);
1800 }
1801
1802 chunkLen = (DWORD)M_FindPNGChunk (png, DSNP_ID);
1803 if (chunkLen != 0)
1804 {
1805 FPNGChunkArchive arc (png->File->GetFile(), DSNP_ID, chunkLen);
1806 DWORD snapver;
1807
1808 arc << snapver;
1809 if (SaveVersion < 4508)
1810 {
1811 arc << namelen;
1812 arc.Read(mapname, namelen);
1813 mapname[namelen] = 0;
1814 MapName = mapname;
1815 }
1816 else arc << MapName;
1817 TheDefaultLevelInfo.snapshotVer = snapver;
1818 TheDefaultLevelInfo.snapshot = new FCompressedMemFile;
1819 TheDefaultLevelInfo.snapshot->Serialize (arc);
1820 }
1821
1822 chunkLen = (DWORD)M_FindPNGChunk (png, VIST_ID);
1823 if (chunkLen != 0)
1824 {
1825 FPNGChunkArchive arc (png->File->GetFile(), VIST_ID, chunkLen);
1826
1827 if (SaveVersion < 4508)
1828 {
1829 arc << namelen;
1830 while (namelen != 0)
1831 {
1832 arc.Read(mapname, namelen);
1833 mapname[namelen] = 0;
1834 i = FindLevelInfo(mapname);
1835 i->flags |= LEVEL_VISITED;
1836 arc << namelen;
1837 }
1838 }
1839 else
1840 {
1841 while (arc << MapName, MapName.Len() > 0)
1842 {
1843 i = FindLevelInfo(MapName);
1844 i->flags |= LEVEL_VISITED;
1845 }
1846 }
1847 }
1848
1849 chunkLen = (DWORD)M_FindPNGChunk (png, RCLS_ID);
1850 if (chunkLen != 0)
1851 {
1852 FPNGChunkArchive arc (png->File->GetFile(), PCLS_ID, chunkLen);
1853 SBYTE cnum;
1854
1855 for (DWORD j = 0; j < chunkLen; ++j)
1856 {
1857 arc << cnum;
1858 SinglePlayerClass[j] = cnum;
1859 }
1860 }
1861
1862 chunkLen = (DWORD)M_FindPNGChunk (png, PCLS_ID);
1863 if (chunkLen != 0)
1864 {
1865 FPNGChunkArchive arc (png->File->GetFile(), RCLS_ID, chunkLen);
1866 BYTE pnum;
1867
1868 arc << pnum;
1869 while (pnum != 255)
1870 {
1871 arc.UserReadClass (players[pnum].cls);
1872 arc << pnum;
1873 }
1874 }
1875 png->File->ResetFilePtr();
1876 }
1877
1878 //==========================================================================
1879
CCMD(listsnapshots)1880 CCMD(listsnapshots)
1881 {
1882 for (unsigned i = 0; i < wadlevelinfos.Size(); ++i)
1883 {
1884 FCompressedMemFile *snapshot = wadlevelinfos[i].snapshot;
1885 if (snapshot != NULL)
1886 {
1887 unsigned int comp, uncomp;
1888 snapshot->GetSizes(comp, uncomp);
1889 Printf("%s (%u -> %u bytes)\n", wadlevelinfos[i].MapName.GetChars(), comp, uncomp);
1890 }
1891 }
1892 }
1893
1894 //==========================================================================
1895 //
1896 //
1897 //==========================================================================
1898
writeDefereds(FArchive & arc,level_info_t * i)1899 static void writeDefereds (FArchive &arc, level_info_t *i)
1900 {
1901 arc << i->MapName << i->defered;
1902 }
1903
1904 //==========================================================================
1905 //
1906 //
1907 //==========================================================================
1908
P_WriteACSDefereds(FILE * file)1909 void P_WriteACSDefereds (FILE *file)
1910 {
1911 FPNGChunkArchive *arc = NULL;
1912
1913 for (unsigned int i = 0; i < wadlevelinfos.Size(); i++)
1914 {
1915 if (wadlevelinfos[i].defered)
1916 {
1917 if (arc == NULL)
1918 {
1919 arc = new FPNGChunkArchive (file, ACSD_ID);
1920 }
1921 writeDefereds (*arc, (level_info_t *)&wadlevelinfos[i]);
1922 }
1923 }
1924
1925 if (arc != NULL)
1926 {
1927 // Signal end of defereds
1928 FString empty = "";
1929 (*arc) << empty;
1930 delete arc;
1931 }
1932 }
1933
1934 //==========================================================================
1935 //
1936 //
1937 //==========================================================================
1938
P_ReadACSDefereds(PNGHandle * png)1939 void P_ReadACSDefereds (PNGHandle *png)
1940 {
1941 BYTE namelen;
1942 char mapname[256];
1943 FString MapName;
1944 size_t chunklen;
1945
1946 P_RemoveDefereds ();
1947
1948 if ((chunklen = M_FindPNGChunk (png, ACSD_ID)) != 0)
1949 {
1950 FPNGChunkArchive arc (png->File->GetFile(), ACSD_ID, chunklen);
1951
1952 if (SaveVersion < 4508)
1953 {
1954 arc << namelen;
1955 while (namelen != 0)
1956 {
1957 arc.Read(mapname, namelen);
1958 mapname[namelen] = 0;
1959 level_info_t *i = FindLevelInfo(mapname);
1960 if (i == NULL)
1961 {
1962 I_Error("Unknown map '%s' in savegame", mapname);
1963 }
1964 arc << i->defered;
1965 arc << namelen;
1966 }
1967 }
1968 else
1969 {
1970 while (arc << MapName, MapName.Len() > 0)
1971 {
1972 level_info_t *i = FindLevelInfo(MapName);
1973 if (i == NULL)
1974 {
1975 I_Error("Unknown map '%s' in savegame", MapName.GetChars());
1976 }
1977 arc << i->defered;
1978 }
1979 }
1980 }
1981 png->File->ResetFilePtr();
1982 }
1983
1984
1985 //==========================================================================
1986 //
1987 //
1988 //==========================================================================
1989
Tick()1990 void FLevelLocals::Tick ()
1991 {
1992 // Reset carry sectors
1993 if (Scrolls != NULL)
1994 {
1995 memset (Scrolls, 0, sizeof(*Scrolls)*numsectors);
1996 }
1997 }
1998
1999 //==========================================================================
2000 //
2001 //
2002 //==========================================================================
2003
AddScroller(DScroller * scroller,int secnum)2004 void FLevelLocals::AddScroller (DScroller *scroller, int secnum)
2005 {
2006 if (secnum < 0)
2007 {
2008 return;
2009 }
2010 if (Scrolls == NULL)
2011 {
2012 Scrolls = new FSectorScrollValues[numsectors];
2013 memset (Scrolls, 0, sizeof(*Scrolls)*numsectors);
2014 }
2015 }
2016
2017 //==========================================================================
2018 //
2019 // Lists all currently defined maps
2020 //
2021 //==========================================================================
2022
CCMD(listmaps)2023 CCMD(listmaps)
2024 {
2025 for(unsigned i = 0; i < wadlevelinfos.Size(); i++)
2026 {
2027 level_info_t *info = &wadlevelinfos[i];
2028 MapData *map = P_OpenMapData(info->MapName, true);
2029
2030 if (map != NULL)
2031 {
2032 Printf("%s: '%s' (%s)\n", info->MapName.GetChars(), info->LookupLevelName().GetChars(),
2033 Wads.GetWadName(Wads.GetLumpFile(map->lumpnum)));
2034 delete map;
2035 }
2036 }
2037 }
2038
2039
2040
2041
2042