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(&sectors[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