1 //----------------------------------------------------------------------------
2 //  EDGE Player Handling
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2009  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 //  Based on the DOOM source code, released by Id Software under the
20 //  following copyright:
21 //
22 //    Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 
26 #include "i_defs.h"
27 
28 #include <time.h>
29 #include <limits.h>
30 
31 #include "epi/endianess.h"
32 #include "epi/path.h"
33 #include "epi/str_format.h"
34 #include "epi/filesystem.h"
35 
36 #include "con_main.h"
37 #include "dstrings.h"
38 #include "e_input.h"
39 #include "e_main.h"
40 #include "f_finale.h"
41 #include "g_game.h"
42 #include "m_cheat.h"
43 #include "m_menu.h"
44 #include "m_random.h"
45 #include "n_network.h"
46 #include "p_bot.h"
47 #include "p_setup.h"
48 #include "p_tick.h"
49 #include "rad_trig.h"
50 #include "am_map.h"
51 #include "r_sky.h"
52 #include "r_modes.h"
53 #include "s_sound.h"
54 #include "s_music.h"
55 #include "sv_chunk.h"
56 #include "sv_main.h"
57 #include "r_colormap.h"
58 #include "version.h"
59 #include "w_wad.h"
60 #include "f_interm.h"
61 #include "vm_coal.h"
62 #include "z_zone.h"
63 
64 
65 gamestate_e gamestate = GS_NOTHING;
66 
67 gameaction_e gameaction = ga_nothing;
68 
69 bool paused = false;
70 
71 int key_pause;
72 
73 // for comparative timing purposes
74 bool nodrawers;
75 bool noblit;
76 
77 // if true, load all graphics at start
78 bool precache = true;
79 
80 int starttime;
81 
82 // -KM- 1998/11/25 Exit time is the time when the level will actually finish
83 // after hitting the exit switch/killing the boss.  So that you see the
84 // switch change or the boss die.
85 
86 int exittime = INT_MAX;
87 bool exit_skipall = false;  // -AJA- temporary (maybe become "exit_mode")
88 int exit_hub_tag  = 0;
89 
90 
91 // GAMEPLAY MODES:
92 //
93 //   numplayers  deathmatch   mode
94 //   --------------------------------------
95 //     <= 1         0         single player
96 //     >  1         0         coop
97 //     -            1         deathmatch
98 //     -            2         altdeath
99 
100 int deathmatch;
101 
102 skill_t gameskill = sk_medium;
103 
104 // -ACB- 2004/05/25 We need to store our current/next mapdefs
105 const mapdef_c *currmap = NULL;
106 const mapdef_c *nextmap = NULL;
107 
108 int curr_hub_tag = 0;  // affects where players are spawned
109 const mapdef_c *curr_hub_first;  // first map in group of hubs
110 
111 // -KM- 1998/12/16 These flags hold everything needed about a level
112 gameflags_t level_flags;
113 
114 
115 //--------------------------------------------
116 
117 static int defer_load_slot;
118 static int defer_save_slot;
119 static char defer_save_desc[32];
120 
121 // deferred stuff...
122 static newgame_params_c *defer_params = NULL;
123 
124 
125 static void G_DoNewGame(void);
126 static void G_DoLoadGame(void);
127 static void G_DoCompleted(void);
128 static void G_DoSaveGame(void);
129 static void G_DoEndGame(void);
130 
131 static void InitNew(newgame_params_c& params);
132 static void RespawnPlayer(player_t *p);
133 static void SpawnInitialPlayers(void);
134 
135 static bool G_LoadGameFromFile(const char *filename, bool is_hub = false);
136 static bool G_SaveGameToFile(const char *filename, const char *description);
137 
138 
LoadLevel_Bits(void)139 void LoadLevel_Bits(void)
140 {
141 	if (currmap == NULL)
142 		I_Error("G_DoLoadLevel: No Current Map selected");
143 
144 	// Set the sky map.
145 	//
146 	// First thing, we have a dummy sky texture name, a flat. The data is
147 	// in the WAD only because we look for an actual index, instead of simply
148 	// setting one.
149 	//
150 	// -ACB- 1998/08/09 Reference current map for sky name.
151 
152 	sky_image = W_ImageLookup(currmap->sky, INS_Texture);
153 
154 	gamestate = GS_NOTHING; //FIXME: needed ???
155 
156 	// -AJA- FIXME: this background camera stuff is a mess
157 	background_camera_mo = NULL;
158 
159 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
160 	{
161 		player_t *p = players[pnum];
162 		if (! p) continue;
163 
164 		if (p->playerstate == PST_DEAD ||
165 			(currmap->force_on & MPF_ResetPlayer))
166 		{
167 			p->playerstate = PST_REBORN;
168 		}
169 
170 		p->frags = 0;
171 	}
172 
173 	// -KM- 1998/12/16 Make map flags actually do stuff.
174 	// -AJA- 2000/02/02: Made it more generic.
175 
176 #define HANDLE_FLAG(var, specflag)  \
177 	if (currmap->force_on & (specflag))  \
178 	(var) = true;  \
179 	else if (currmap->force_off & (specflag))  \
180 	(var) = false;
181 
182 	HANDLE_FLAG(level_flags.jump, MPF_Jumping);
183 	HANDLE_FLAG(level_flags.crouch, MPF_Crouching);
184 	HANDLE_FLAG(level_flags.mlook, MPF_Mlook);
185 	HANDLE_FLAG(level_flags.itemrespawn, MPF_ItemRespawn);
186 	HANDLE_FLAG(level_flags.fastparm, MPF_FastParm);
187 	HANDLE_FLAG(level_flags.true3dgameplay, MPF_True3D);
188 	HANDLE_FLAG(level_flags.more_blood, MPF_MoreBlood);
189 	HANDLE_FLAG(level_flags.cheats, MPF_Cheats);
190 	HANDLE_FLAG(level_flags.respawn, MPF_Respawn);
191 	HANDLE_FLAG(level_flags.res_respawn, MPF_ResRespawn);
192 	HANDLE_FLAG(level_flags.have_extra, MPF_Extras);
193 	HANDLE_FLAG(level_flags.limit_zoom, MPF_LimitZoom);
194 	HANDLE_FLAG(level_flags.shadows, MPF_Shadows);
195 	HANDLE_FLAG(level_flags.halos, MPF_Halos);
196 	HANDLE_FLAG(level_flags.kicking, MPF_Kicking);
197 	HANDLE_FLAG(level_flags.weapon_switch, MPF_WeaponSwitch);
198 	HANDLE_FLAG(level_flags.pass_missile, MPF_PassMissile);
199 	HANDLE_FLAG(level_flags.team_damage, MPF_TeamDamage);
200 
201 #undef HANDLE_FLAG
202 
203 	if (currmap->force_on & MPF_BoomCompat)
204 		level_flags.edge_compat = false;
205 	else if (currmap->force_off & MPF_BoomCompat)
206 		level_flags.edge_compat = true;
207 
208 	if (currmap->force_on & MPF_AutoAim)
209 	{
210 		if (currmap->force_on & MPF_AutoAimMlook)
211 			level_flags.autoaim = AA_MLOOK;
212 		else
213 			level_flags.autoaim = AA_ON;
214 	}
215 	else if (currmap->force_off & MPF_AutoAim)
216 		level_flags.autoaim = AA_OFF;
217 
218 	//
219 	// Note: It should be noted that only the gameskill is
220 	// passed as the level is already defined in currmap,
221 	// The method for changing currmap, is using by
222 	// G_DeferredNewGame.
223 	//
224 	// -ACB- 1998/08/09 New P_SetupLevel
225 	// -KM- 1998/11/25 P_SetupLevel accepts the autotag
226 	//
227 	RAD_ClearTriggers();
228 	RAD_FinishMenu(0);
229 
230 	wi_stats.kills = wi_stats.items = wi_stats.secret = 0;
231 
232 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
233 	{
234 		player_t *p = players[pnum];
235 		if (! p) continue;
236 
237 		p->killcount = p->secretcount = p->itemcount = 0;
238 		p->mo = NULL;
239 	}
240 
241 	// Initial height of PointOfView will be set by player think.
242 	players[consoleplayer]->viewz = FLO_UNUSED;
243 
244 	leveltime = 0;
245 
246 	P_SetupLevel();
247 
248 	RAD_SpawnTriggers(currmap->name.c_str());
249 
250 	starttime = I_GetTime();
251 	exittime = INT_MAX;
252 	exit_skipall = false;
253 	exit_hub_tag = 0;
254 
255 	VM_BeginLevel();
256 
257 	BOT_BeginLevel();
258 
259 	gamestate = GS_LEVEL;
260 
261 	CON_SetVisible(vs_notvisible);
262 
263 	// clear cmd building stuff
264 	E_ClearInput();
265 
266 	paused = false;
267 }
268 
269 //
270 // REQUIRED STATE:
271 //   (a) currmap
272 //   (b) curr_hub_tag
273 //   (c) players[], numplayers (etc)
274 //   (d) gameskill + deathmatch
275 //   (e) level_flags
276 //
277 //   ??  exittime
278 //
G_DoLoadLevel(void)279 void G_DoLoadLevel(void)
280 {
281 	HU_Start();
282 
283 	if (curr_hub_tag == 0)
284 		SV_ClearSlot("current");
285 
286 	if (curr_hub_tag > 0)
287 	{
288 		// HUB system: check for loading a previously visited map
289 		const char *mapname = SV_MapName(currmap);
290 
291 		std::string fn(SV_FileName("current", mapname));
292 
293 		if (epi::FS_Access(fn.c_str(), epi::file_c::ACCESS_READ))
294 		{
295 			I_Printf("Loading HUB...\n");
296 
297 			if (! G_LoadGameFromFile(fn.c_str(), true))
298 				I_Error("LOAD-HUB failed with filename: %s\n", fn.c_str());
299 
300 			SpawnInitialPlayers();
301 
302 			G_RemoveOldAvatars();
303 
304 			P_HubFastForward();
305 			return;
306 		}
307 	}
308 
309 	LoadLevel_Bits();
310 
311 	SpawnInitialPlayers();
312 }
313 
314 
315 //
316 // G_Responder
317 //
318 // Get info needed to make ticcmd_ts for the players.
319 //
G_Responder(event_t * ev)320 bool G_Responder(event_t * ev)
321 {
322 	// any other key pops up menu if in demos
323 	if (gameaction == ga_nothing && (gamestate == GS_TITLESCREEN))
324 	{
325 		if (ev->type == ev_keydown)
326 		{
327 			M_StartControlPanel();
328 			S_StartFX(sfx_swtchn, SNCAT_UI);
329 			return true;
330 		}
331 
332 		return false;
333 	}
334 
335 	if (ev->type == ev_keydown && ev->value.key.sym == KEYD_F12)
336 	{
337 		// 25-6-98 KM Allow spy mode for demos even in deathmatch
338 		if (gamestate == GS_LEVEL) //!!!! && !DEATHMATCH())
339 		{
340 			G_ToggleDisplayPlayer();
341 			return true;
342 		}
343 	}
344 
345 	if (!netgame && ev->type == ev_keydown && E_MatchesKey(key_pause, ev->value.key.sym))
346 	{
347 		paused = !paused;
348 
349 		if (paused)
350 		{
351 			S_PauseMusic();
352 			S_PauseSound();
353 			I_GrabCursor(false);
354 		}
355 		else
356 		{
357 			S_ResumeMusic();
358 			S_ResumeSound();
359 			I_GrabCursor(true);
360 		}
361 
362 		// explicit as probably killed the initial effect
363 		S_StartFX(sfx_swtchn, SNCAT_UI);
364 		return true;
365 	}
366 
367 	if (gamestate == GS_LEVEL)
368 	{
369 		if (RAD_Responder(ev))
370 			return true;  // RTS system ate it
371 
372 		if (AM_Responder(ev))
373 			return true;  // automap ate it
374 
375 		if (HU_Responder(ev))
376 			return true;  // chat ate the event
377 
378 		if (M_CheatResponder(ev))
379 			return true;  // cheat code at it
380 	}
381 
382 	if (gamestate == GS_FINALE)
383 	{
384 		if (F_Responder(ev))
385 			return true;  // finale ate the event
386 	}
387 
388 	return INP_Responder(ev);
389 }
390 
391 
CheckPlayersReborn(void)392 static void CheckPlayersReborn(void)
393 {
394 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
395 	{
396 		player_t *p = players[pnum];
397 
398 		if (!p || p->playerstate != PST_REBORN)
399 			continue;
400 
401 		if (SP_MATCH())
402 		{
403 			// reload the level
404 			E_ForceWipe();
405 			gameaction = ga_loadlevel;
406 
407 			// -AJA- if we are on a HUB map, then we must go all the
408 			//       way back to the beginning.
409 			if (curr_hub_first)
410 			{
411 				currmap = curr_hub_first;
412 				curr_hub_tag = 0;
413 				curr_hub_first = NULL;
414 			}
415 			return;
416 		}
417 
418 		RespawnPlayer(p);
419 	}
420 }
421 
422 
G_BigStuff(void)423 void G_BigStuff(void)
424 {
425 	// do things to change the game state
426 	while (gameaction != ga_nothing)
427 	{
428 		gameaction_e action = gameaction;
429 		gameaction = ga_nothing;
430 
431 		switch (action)
432 		{
433 			case ga_newgame:
434 				G_DoNewGame();
435 				break;
436 
437 			case ga_loadlevel:
438 				G_DoLoadLevel();
439 				break;
440 
441 			case ga_loadgame:
442 				G_DoLoadGame();
443 				break;
444 
445 			case ga_savegame:
446 				G_DoSaveGame();
447 				break;
448 
449 			case ga_playdemo:
450 				// G_DoPlayDemo();
451 				break;
452 
453 			case ga_recorddemo:
454 				// G_DoRecordDemo();
455 				break;
456 
457 			case ga_intermission:
458 				G_DoCompleted();
459 				break;
460 
461 			case ga_finale:
462 				SYS_ASSERT(nextmap);
463 				currmap = nextmap;
464 				curr_hub_tag = 0;
465 				curr_hub_first = NULL;
466 				F_StartFinale(&currmap->f_pre, ga_loadlevel);
467 				break;
468 
469 			case ga_endgame:
470 				G_DoEndGame();
471 				break;
472 
473 			default:
474 				I_Error("G_BigStuff: Unknown gameaction %d", gameaction);
475 				break;
476 		}
477 	}
478 }
479 
G_Ticker(void)480 void G_Ticker(void)
481 {
482 	// ANIMATE FLATS AND TEXTURES GLOBALLY
483 	W_UpdateImageAnims();
484 
485 	// do main actions
486 	switch (gamestate)
487 	{
488 		case GS_TITLESCREEN:
489 			E_TitleTicker();
490 			break;
491 
492 		case GS_LEVEL:
493 			// get commands, check consistency,
494 			// and build new consistency check.
495 			N_TiccmdTicker();
496 
497 			P_Ticker();
498 			AM_Ticker();
499 			HU_Ticker();
500 			RAD_Ticker();
501 
502 			// do player reborns if needed
503 			CheckPlayersReborn();
504 			break;
505 
506 		case GS_INTERMISSION:
507 			N_TiccmdTicker();
508 			WI_Ticker();
509 			break;
510 
511 		case GS_FINALE:
512 			N_TiccmdTicker();
513 			F_Ticker();
514 			break;
515 
516 		default:
517 			break;
518 	}
519 }
520 
521 
RespawnPlayer(player_t * p)522 static void RespawnPlayer(player_t *p)
523 {
524 	// first disassociate the corpse (if any)
525 	if (p->mo)
526 		p->mo->player = NULL;
527 
528 	p->mo = NULL;
529 
530 	// spawn at random spot if in death match
531 	if (DEATHMATCH())
532 		G_DeathMatchSpawnPlayer(p);
533 	else if (curr_hub_tag > 0)
534 		G_HubSpawnPlayer(p, curr_hub_tag);
535 	else
536 		G_CoopSpawnPlayer(p); // respawn at the start
537 }
538 
SpawnInitialPlayers(void)539 static void SpawnInitialPlayers(void)
540 {
541 	L_WriteDebug("Deathmatch %d\n", deathmatch);
542 
543 	// spawn the active players
544 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
545 	{
546 		player_t *p = players[pnum];
547 		if (! p) continue;
548 
549 		RespawnPlayer(p);
550 
551 		if (!DEATHMATCH())
552 			G_SpawnVoodooDolls(p);
553 	}
554 
555 	// check for missing player start.
556 	if (players[consoleplayer]->mo == NULL)
557 		I_Error("Missing player start !\n");
558 
559 	G_SetDisplayPlayer(consoleplayer); // view the guy you are playing
560 }
561 
G_DeferredScreenShot(void)562 void G_DeferredScreenShot(void)
563 {
564 	m_screenshot_required = true;
565 }
566 
567 // -KM- 1998/11/25 Added time param which is the time to wait before
568 //  actually exiting level.
G_ExitLevel(int time)569 void G_ExitLevel(int time)
570 {
571 	nextmap = G_LookupMap(currmap->nextmapname);
572 	exittime = leveltime + time;
573 	exit_skipall = false;
574 	exit_hub_tag = 0;
575 }
576 
577 // -ACB- 1998/08/08 We don't have support for the german edition
578 //                  removed the check for map31.
G_SecretExitLevel(int time)579 void G_SecretExitLevel(int time)
580 {
581 	nextmap = G_LookupMap(currmap->secretmapname);
582 	exittime = leveltime + time;
583 	exit_skipall = false;
584 	exit_hub_tag = 0;
585 }
586 
G_ExitToLevel(char * name,int time,bool skip_all)587 void G_ExitToLevel(char *name, int time, bool skip_all)
588 {
589 	nextmap = G_LookupMap(name);
590 	exittime = leveltime + time;
591 	exit_skipall = skip_all;
592 	exit_hub_tag = 0;
593 }
594 
G_ExitToHub(const char * map_name,int tag)595 void G_ExitToHub(const char *map_name, int tag)
596 {
597 	if (tag <= 0)
598 		I_Error("Hub exit line/command: bad tag %d\n", tag);
599 
600 	nextmap = G_LookupMap(map_name);
601 	if (! nextmap)
602 		I_Error("G_ExitToHub: No such map %s !\n", map_name);
603 
604 	exittime = leveltime + 5;
605 	exit_skipall = true;
606 	exit_hub_tag = tag;
607 }
608 
G_ExitToHub(int map_number,int tag)609 void G_ExitToHub(int map_number, int tag)
610 {
611 	SYS_ASSERT(currmap);
612 
613 	char name_buf[32];
614 
615 	// bit hackish: decided whether to use MAP## or E#M#
616 	if (currmap->name[0] == 'E')
617 	{
618 		sprintf(name_buf, "E%dM%d", 1+(map_number/10), map_number%10);
619 	}
620 	else
621 		sprintf(name_buf, "MAP%02d", map_number);
622 
623 	G_ExitToHub(name_buf, tag);
624 }
625 
626 //
627 // REQUIRED STATE:
628 //   (a) currmap, nextmap
629 //   (b) players[]
630 //   (c) leveltime
631 //   (d) exit_skipall
632 //   (d) exit_hub_tag
633 //   (e) wi_stats.kills (etc)
634 //
G_DoCompleted(void)635 static void G_DoCompleted(void)
636 {
637 	SYS_ASSERT(currmap);
638 
639 	E_ForceWipe();
640 
641 	exittime = INT_MAX;
642 
643 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
644 	{
645 		player_t *p = players[pnum];
646 		if (! p) continue;
647 
648 		p->leveltime = leveltime;
649 
650 		// take away cards and stuff
651 		G_PlayerFinishLevel(p, exit_hub_tag > 0);
652 	}
653 
654 	if (automapactive)
655 		AM_Stop();
656 
657 	if (rts_menuactive)
658 		RAD_FinishMenu(0);
659 
660 	BOT_EndLevel();
661 
662 	automapactive = false;
663 
664 	// handle "no stat" levels
665 	if (currmap->wistyle == WISTYLE_None || exit_skipall)
666 	{
667 		if (exit_skipall && nextmap)
668 		{
669 			if (exit_hub_tag <= 0)
670 				curr_hub_first = NULL;
671 			else
672 			{
673 				// save current map for HUB system
674 				I_Printf("Saving HUB...\n");
675 
676 				// remember avatars of players, so we can remove them
677 				// when we return to this level.
678 				G_MarkPlayerAvatars();
679 
680 				const char *mapname = SV_MapName(currmap);
681 
682 				std::string fn(SV_FileName("current", mapname));
683 
684 				if (! G_SaveGameToFile(fn.c_str(), "__HUB_SAVE__"))
685 					I_Error("SAVE-HUB failed with filename: %s\n", fn.c_str());
686 
687 				if (! curr_hub_first)
688 					curr_hub_first = currmap;
689 			}
690 
691 			currmap = nextmap;
692 			curr_hub_tag = exit_hub_tag;
693 
694 			gameaction = ga_loadlevel;
695 		}
696 		else
697 		{
698 			F_StartFinale(&currmap->f_end, nextmap ? ga_finale : ga_nothing);
699 		}
700 
701 		return;
702 	}
703 
704 	wi_stats.cur  = currmap;
705 	wi_stats.next = nextmap;
706 
707 	gamestate = GS_INTERMISSION;
708 
709 	WI_Start();
710 }
711 
712 
G_DeferredLoadGame(int slot)713 void G_DeferredLoadGame(int slot)
714 {
715 	// Can be called by the startup code or the menu task.
716 
717 	defer_load_slot = slot;
718 	gameaction = ga_loadgame;
719 }
720 
721 
G_LoadGameFromFile(const char * filename,bool is_hub)722 static bool G_LoadGameFromFile(const char *filename, bool is_hub)
723 {
724 	if (! SV_OpenReadFile(filename))
725 	{
726 		I_Printf("LOAD-GAME: cannot open %s\n", filename);
727 		return false;
728 	}
729 
730 	int version;
731 
732 	if (! SV_VerifyHeader(&version) || ! SV_VerifyContents())
733 	{
734 		I_Printf("LOAD-GAME: Savegame is corrupt !\n");
735 		SV_CloseReadFile();
736 		return false;
737 	}
738 
739 	SV_BeginLoad(is_hub);
740 
741 	saveglobals_t *globs = SV_LoadGLOB();
742 
743 	if (!globs)
744 		I_Error("LOAD-GAME: Bad savegame file (no GLOB)\n");
745 
746 	// --- pull info from global structure ---
747 
748 	if (is_hub)
749 	{
750 		currmap = G_LookupMap(globs->level);
751 		if (! currmap)
752 			I_Error("LOAD-HUB: No such map %s !  Check WADS\n", globs->level);
753 
754 		G_SetDisplayPlayer(consoleplayer);
755 		automapactive = false;
756 
757 		N_ResetTics();
758 	}
759 	else
760 	{
761 		newgame_params_c params;
762 
763 		params.map = G_LookupMap(globs->level);
764 		if (! params.map)
765 			I_Error("LOAD-GAME: No such map %s !  Check WADS\n", globs->level);
766 
767 		SYS_ASSERT(params.map->episode);
768 
769 		params.skill      = (skill_t) globs->skill;
770 		params.deathmatch = (globs->netgame >= 2) ? (globs->netgame - 1) : 0;
771 
772 		params.random_seed = globs->p_random;
773 
774 		// this player is a dummy one, replaced during actual load
775 		params.SinglePlayer(0);
776 
777 		params.CopyFlags(&globs->flags);
778 
779 		InitNew(params);
780 
781 		curr_hub_tag = globs->hub_tag;
782 		curr_hub_first = globs->hub_first ? G_LookupMap(globs->hub_first) : NULL;
783 	}
784 
785 	LoadLevel_Bits();
786 
787 	// -- Check LEVEL consistency (crc) --
788 	//
789 	// FIXME: ideally we shouldn't bomb out, just display an error box
790 
791 	if (globs->mapsector.count != numsectors ||
792 		globs->mapsector.crc != mapsector_CRC.crc ||
793 		globs->mapline.count != numlines ||
794 		globs->mapline.crc != mapline_CRC.crc ||
795 		globs->mapthing.count != mapthing_NUM ||
796 		globs->mapthing.crc != mapthing_CRC.crc)
797 	{
798 		SV_CloseReadFile();
799 
800 		I_Error("LOAD-GAME: Level data does not match !  Check WADs\n");
801 	}
802 
803 	if (! is_hub)
804 	{
805 		leveltime = globs->level_time;
806 		exittime  = globs->exit_time;
807 
808 		wi_stats.kills  = globs->total_kills;
809 		wi_stats.items  = globs->total_items;
810 		wi_stats.secret = globs->total_secrets;
811 	}
812 
813 	if (globs->sky_image)  // backwards compat (sky_image added 2003/12/19)
814 		sky_image = globs->sky_image;
815 
816 	// clear line/sector lookup caches, in case level_flags.edge_compat
817 	// has changed.
818 	DDF_BoomClearGenTypes();
819 
820 	if (SV_LoadEverything() && SV_GetError() == 0)
821 	{
822 		/* all went well */
823 	}
824 	else
825 	{
826 		// something went horribly wrong...
827 		// FIXME (oneday) : show message & go back to title screen
828 
829 		I_Error("Bad Save Game !\n");
830 	}
831 
832 	SV_FreeGLOB(globs);
833 
834 	SV_FinishLoad();
835 	SV_CloseReadFile();
836 
837 	return true; //OK
838 }
839 
840 //
841 // REQUIRED STATE:
842 //   (a) defer_load_slot
843 //
844 //   ?? nothing else ??
845 //
G_DoLoadGame(void)846 static void G_DoLoadGame(void)
847 {
848 	E_ForceWipe();
849 
850 	const char *dir_name = SV_SlotName(defer_load_slot);
851 	I_Debugf("G_DoLoadGame : %s\n", dir_name);
852 
853 	SV_ClearSlot("current");
854 	SV_CopySlot(dir_name, "current");
855 
856 	std::string fn(SV_FileName("current", "head"));
857 
858 	if (! G_LoadGameFromFile(fn.c_str()))
859 	{
860 		// !!! FIXME: what to do?
861 	}
862 
863 	HU_Start();
864 
865 	V_SetPalette(PALETTE_NORMAL, 0);
866 }
867 
868 //
869 // G_DeferredSaveGame
870 //
871 // Called by the menu task.
872 // Description is a 24 byte text string
873 //
G_DeferredSaveGame(int slot,const char * description)874 void G_DeferredSaveGame(int slot, const char *description)
875 {
876 	defer_save_slot = slot;
877 	strcpy(defer_save_desc, description);
878 
879 	gameaction = ga_savegame;
880 }
881 
G_SaveGameToFile(const char * filename,const char * description)882 static bool G_SaveGameToFile(const char *filename, const char *description)
883 {
884 	time_t cur_time;
885 	char timebuf[100];
886 
887 	epi::FS_Delete(filename);
888 
889 	if (! SV_OpenWriteFile(filename, (EDGEVERHEX << 8) | EDGEPATCH))
890 	{
891 		I_Printf("Unable to create savegame file: %s\n", filename);
892 		return false; /* NOT REACHED */
893 	}
894 
895 	saveglobals_t *globs = SV_NewGLOB();
896 
897 	// --- fill in global structure ---
898 
899 	globs->game  = SV_DupString(currmap->episode_name);
900 	globs->level = SV_DupString(currmap->name);
901 	globs->flags = level_flags;
902 	globs->hub_tag = curr_hub_tag;
903 	globs->hub_first = curr_hub_first ? SV_DupString(curr_hub_first->name) : NULL;
904 
905 	globs->skill = gameskill;
906 	globs->netgame = netgame ? (1+deathmatch) : 0;
907 	globs->p_random = P_ReadRandomState();
908 
909 	globs->console_player = consoleplayer; // NB: not used
910 
911 	globs->level_time = leveltime;
912 	globs->exit_time  = exittime;
913 
914 	globs->total_kills   = wi_stats.kills;
915 	globs->total_items   = wi_stats.items;
916 	globs->total_secrets = wi_stats.secret;
917 
918 	globs->sky_image = sky_image;
919 
920 	time(&cur_time);
921 	strftime(timebuf, 99, "%I:%M %p  %d/%b/%Y", localtime(&cur_time));
922 
923 	if (timebuf[0] == '0' && isdigit(timebuf[1]))
924 		timebuf[0] = ' ';
925 
926 	globs->description = SV_DupString(description);
927 	globs->desc_date   = SV_DupString(timebuf);
928 
929 	globs->mapsector.count = numsectors;
930 	globs->mapsector.crc = mapsector_CRC.crc;
931 	globs->mapline.count = numlines;
932 	globs->mapline.crc = mapline_CRC.crc;
933 	globs->mapthing.count = mapthing_NUM;
934 	globs->mapthing.crc = mapthing_CRC.crc;
935 
936 	SV_BeginSave();
937 
938 	SV_SaveGLOB(globs);
939 	SV_SaveEverything();
940 
941 	SV_FreeGLOB(globs);
942 
943 	SV_FinishSave();
944 	SV_CloseWriteFile();
945 
946 	return true; //OK
947 }
948 
G_DoSaveGame(void)949 static void G_DoSaveGame(void)
950 {
951 	std::string fn(SV_FileName("current", "head"));
952 
953 	if (G_SaveGameToFile(fn.c_str(), defer_save_desc))
954 	{
955 		const char *dir_name = SV_SlotName(defer_save_slot);
956 
957 		SV_ClearSlot(dir_name);
958 		SV_CopySlot("current", dir_name);
959 
960 		CON_Printf("%s", language["GameSaved"]);
961 	}
962 	else
963 	{
964 		// !!! FIXME: what to do?
965 	}
966 
967 	defer_save_desc[0] = 0;
968 }
969 
970 //
971 // G_InitNew Stuff
972 //
973 // Can be called by the startup code or the menu task.
974 // consoleplayer, displayplayer, players[] are setup.
975 //
976 
977 //---> newgame_params_c class
978 
newgame_params_c()979 newgame_params_c::newgame_params_c() :
980 	skill(sk_medium), deathmatch(0),
981 	map(NULL), random_seed(0),
982 	total_players(0), flags(NULL)
983 {
984 	for (int i = 0; i < MAXPLAYERS; i++)
985 	{
986 		players[i] = PFL_NOPLAYER;
987 		nodes[i]   = NULL;
988 	}
989 }
990 
newgame_params_c(const newgame_params_c & src)991 newgame_params_c::newgame_params_c(const newgame_params_c& src)
992 {
993 	skill = src.skill;
994 	deathmatch = src.deathmatch;
995 
996 	map = src.map;
997 
998 	random_seed   = src.random_seed;
999 	total_players = src.total_players;
1000 
1001 	for (int i = 0; i < MAXPLAYERS; i++)
1002 	{
1003 		players[i] = src.players[i];
1004 		nodes[i] = src.nodes[i];
1005 	}
1006 
1007 	flags = NULL;
1008 
1009 	if (src.flags)
1010 		CopyFlags(src.flags);
1011 }
1012 
~newgame_params_c()1013 newgame_params_c::~newgame_params_c()
1014 {
1015 	if (flags)
1016 		delete flags;
1017 }
1018 
SinglePlayer(int num_bots)1019 void newgame_params_c::SinglePlayer(int num_bots)
1020 {
1021 	total_players = 1 + num_bots;
1022 	players[0] = PFL_Zero;  // i.e. !BOT and !NETWORK
1023 	nodes[0]   = NULL;
1024 
1025 	for (int pnum = 1; pnum <= num_bots; pnum++)
1026 	{
1027 		players[pnum] = PFL_Bot;
1028 		nodes[pnum]   = NULL;
1029 	}
1030 }
1031 
CopyFlags(const gameflags_t * F)1032 void newgame_params_c::CopyFlags(const gameflags_t *F)
1033 {
1034 	if (flags)
1035 		delete flags;
1036 
1037 	flags = new gameflags_t;
1038 
1039 	memcpy(flags, F, sizeof(gameflags_t));
1040 }
1041 
1042 //
1043 // This is the procedure that changes the currmap
1044 // at the start of the game and outside the normal
1045 // progression of the game. All thats needed is the
1046 // skill and the name (The name in the DDF File itself).
1047 //
G_DeferredNewGame(newgame_params_c & params)1048 void G_DeferredNewGame(newgame_params_c& params)
1049 {
1050 	SYS_ASSERT(params.map);
1051 
1052 	defer_params = new newgame_params_c(params);
1053 
1054 	gameaction = ga_newgame;
1055 }
1056 
G_MapExists(const mapdef_c * map)1057 bool G_MapExists(const mapdef_c *map)
1058 {
1059 	return (W_CheckNumForName(map->lump) >= 0);
1060 }
1061 
1062 
1063 //
1064 // REQUIRED STATE:
1065 //   (a) defer_params
1066 //
G_DoNewGame(void)1067 static void G_DoNewGame(void)
1068 {
1069 	SYS_ASSERT(defer_params);
1070 
1071 	E_ForceWipe();
1072 
1073 	SV_ClearSlot("current");
1074 	quickSaveSlot = -1;
1075 
1076 	InitNew(*defer_params);
1077 
1078 	delete defer_params;
1079 	defer_params = NULL;
1080 
1081 	// -AJA- 2003/10/09: support for pre-level briefing screen on first map.
1082 	//       FIXME: kludgy. All this game logic desperately needs rethinking.
1083 	F_StartFinale(&currmap->f_pre, ga_loadlevel);
1084 }
1085 
1086 //
1087 // InitNew
1088 //
1089 // -ACB- 1998/07/12 Removed Lost Soul/Spectre Ability stuff
1090 // -ACB- 1998/08/10 Inits new game without the need for gamemap or episode.
1091 // -ACB- 1998/09/06 Removed remarked code.
1092 // -KM- 1998/12/21 Added mapdef param so no need for defered init new
1093 //   which was conflicting with net games.
1094 //
1095 // REQUIRED STATE:
1096 //   ?? nothing ??
1097 //
InitNew(newgame_params_c & params)1098 static void InitNew(newgame_params_c& params)
1099 {
1100 	// --- create players ---
1101 
1102 	P_DestroyAllPlayers();
1103 
1104 	for (int pnum = 0; pnum < MAXPLAYERS; pnum++)
1105 	{
1106 		if (params.players[pnum] == PFL_NOPLAYER)
1107 			continue;
1108 
1109 		P_CreatePlayer(pnum, (params.players[pnum] & PFL_Bot) ? true : false);
1110 
1111 		if (consoleplayer < 0 && ! (params.players[pnum] & PFL_Bot) &&
1112 			! (params.players[pnum] & PFL_Network))
1113 		{
1114 			G_SetConsolePlayer(pnum);
1115 		}
1116 
1117 		players[pnum]->node = params.nodes[pnum];
1118 	}
1119 
1120 	if (numplayers != params.total_players)
1121 		I_Error("Internal Error: InitNew: player miscount (%d != %d)\n",
1122 			numplayers, params.total_players);
1123 
1124 	if (consoleplayer < 0)
1125 		I_Error("Internal Error: InitNew: no local players!\n");
1126 
1127 	G_SetDisplayPlayer(consoleplayer);
1128 
1129 	if (paused)
1130 	{
1131 		paused = false;
1132 		S_ResumeMusic(); // -ACB- 1999/10/07 New Music API
1133 		S_ResumeSound();
1134 	}
1135 
1136 	currmap = params.map;
1137 	curr_hub_tag = 0;
1138 	curr_hub_first = NULL;
1139 
1140 	if (params.skill > sk_nightmare)
1141 		params.skill = sk_nightmare;
1142 
1143 	P_WriteRandomState(params.random_seed);
1144 
1145 	automapactive = false;
1146 
1147 	gameskill = params.skill;
1148 	deathmatch = params.deathmatch;
1149 
1150 // L_WriteDebug("G_InitNew: Deathmatch %d Skill %d\n", params.deathmatch, (int)params.skill);
1151 
1152 	// copy global flags into the level-specific flags
1153 	if (params.flags)
1154 		level_flags = *params.flags;
1155 	else
1156 		level_flags = global_flags;
1157 
1158 	if (params.skill == sk_nightmare)
1159 	{
1160 		level_flags.fastparm = true;
1161 		level_flags.respawn = true;
1162 	}
1163 
1164 	N_ResetTics();
1165 }
1166 
G_DeferredEndGame(void)1167 void G_DeferredEndGame(void)
1168 {
1169 	if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION ||
1170 	    gamestate == GS_FINALE)
1171 	{
1172 		gameaction = ga_endgame;
1173 	}
1174 }
1175 
1176 //
1177 // REQUIRED STATE:
1178 //    ?? nothing ??
1179 //
G_DoEndGame(void)1180 static void G_DoEndGame(void)
1181 {
1182 	E_ForceWipe();
1183 
1184 	P_DestroyAllPlayers();
1185 
1186 	SV_ClearSlot("current");
1187 
1188 	if (gamestate == GS_LEVEL)
1189 	{
1190 		BOT_EndLevel();
1191 
1192 		// FIXME: P_ShutdownLevel()
1193 	}
1194 
1195 	gamestate = GS_NOTHING;
1196 
1197 	V_SetPalette(PALETTE_NORMAL, 0);
1198 
1199 	E_StartTitle();
1200 }
1201 
1202 
G_CheckWhenAppear(when_appear_e appear)1203 bool G_CheckWhenAppear(when_appear_e appear)
1204 {
1205 	if (! (appear & (1 << gameskill)))
1206 		return false;
1207 
1208 	if (SP_MATCH() && !(appear & WNAP_Single))
1209 		return false;
1210 
1211 	if (COOP_MATCH() && !(appear & WNAP_Coop))
1212 		return false;
1213 
1214 	if (DEATHMATCH() && !(appear & WNAP_DeathMatch))
1215 		return false;
1216 
1217 	return true;
1218 }
1219 
1220 
G_LookupMap(const char * refname)1221 mapdef_c *G_LookupMap(const char *refname)
1222 {
1223 	mapdef_c *m = mapdefs.Lookup(refname);
1224 
1225 	if (m && G_MapExists(m))
1226 		return m;
1227 
1228 	// -AJA- handle numbers (like original DOOM)
1229 	if (strlen(refname) <= 2 && isdigit(refname[0]) &&
1230 		(!refname[1] || isdigit(refname[1])))
1231 	{
1232 		int num = atoi(refname);
1233 		char new_ref[20];
1234 
1235 		// try MAP## first
1236 		sprintf(new_ref, "MAP%02d", num);
1237 
1238 		m = mapdefs.Lookup(new_ref);
1239 		if (m && G_MapExists(m))
1240 			return m;
1241 
1242 		// otherwise try E#M#
1243 		if (1 <= num && num <= 9) num = num + 10;
1244 		sprintf(new_ref, "E%dM%d", num/10, num%10);
1245 
1246 		m = mapdefs.Lookup(new_ref);
1247 		if (m && G_MapExists(m))
1248 			return m;
1249 	}
1250 
1251 	return NULL;
1252 }
1253 
1254 //--- editor settings ---
1255 // vi:ts=4:sw=4:noexpandtab
1256