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