1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: g_level.cpp 4664 2014-03-21 19:57:19Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 1998-2006 by Randy Heit (ZDoom).
8 // Copyright (C) 2006-2014 by The Odamex Team.
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License
12 // as published by the Free Software Foundation; either version 2
13 // of the License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // DESCRIPTION:
21 // G_LEVEL
22 //
23 //-----------------------------------------------------------------------------
24
25 #include <set>
26
27 #include "am_map.h"
28 #include "c_console.h"
29 #include "c_dispatch.h"
30 #include "c_level.h"
31 #include "cl_main.h"
32 #include "d_event.h"
33 #include "d_main.h"
34 #include "doomstat.h"
35 #include "f_finale.h"
36 #include "g_level.h"
37 #include "g_game.h"
38 #include "gstrings.h"
39 #include "gi.h"
40 #include "hu_stuff.h"
41 #include "i_system.h"
42 #include "m_alloc.h"
43 #include "m_fileio.h"
44 #include "m_misc.h"
45 #include "minilzo.h"
46 #include "m_random.h"
47 #include "p_acs.h"
48 #include "p_ctf.h"
49 #include "p_local.h"
50 #include "p_mobj.h"
51 #include "p_saveg.h"
52 #include "p_setup.h"
53 #include "p_unlag.h"
54 #include "r_data.h"
55 #include "r_sky.h"
56 #include "s_sound.h"
57 #include "s_sndseq.h"
58 #include "sc_man.h"
59 #include "st_stuff.h"
60 #include "v_video.h"
61 #include "v_text.h"
62 #include "w_wad.h"
63 #include "wi_stuff.h"
64 #include "z_zone.h"
65
66
67 #define lioffset(x) myoffsetof(level_pwad_info_t,x)
68 #define cioffset(x) myoffsetof(cluster_info_t,x)
69
70 bool G_CheckSpot (player_t &player, mapthing2_t *mthing);
71 void P_SpawnPlayer (player_t &player, mapthing2_t *mthing);
72 void R_ResetInterpolation();
73
74 extern int shotclock;
75
76 EXTERN_CVAR(sv_fastmonsters)
77 EXTERN_CVAR(sv_monstersrespawn)
78 EXTERN_CVAR(sv_gravity)
79 EXTERN_CVAR(sv_aircontrol)
80
81 // Start time for timing demos
82 dtime_t starttime;
83
84 // ACS variables with world scope
85 int ACS_WorldVars[NUM_WORLDVARS];
86
87 // ACS variables with global scope
88 int ACS_GlobalVars[NUM_GLOBALVARS];
89
90 // [AM] Stores the reset snapshot
91 FLZOMemFile *reset_snapshot = NULL;
92
93 extern bool r_underwater;
94 BOOL savegamerestore;
95
96 extern int mousex, mousey, joyforward, joystrafe, joyturn, joylook, Impulse;
97 extern BOOL sendpause, sendsave, sendcenterview;
98
99
100 bool isFast = false;
101
102 //
103 // G_InitNew
104 // Can be called by the startup code or the menu task,
105 // consoleplayer, displayplayer, should be set.
106 //
107 static char d_mapname[9];
108
G_DeferedInitNew(char * mapname)109 void G_DeferedInitNew (char *mapname)
110 {
111 G_CleanupDemo();
112 strncpy (d_mapname, mapname, 8);
113 gameaction = ga_newgame;
114 }
115
116
BEGIN_COMMAND(wad)117 BEGIN_COMMAND (wad) // denis - changes wads
118 {
119 // [Russell] print out some useful info
120 if (argc == 1)
121 {
122 Printf(PRINT_HIGH, "Usage: wad pwad [...] [deh/bex [...]]\n");
123 Printf(PRINT_HIGH, " wad iwad [pwad [...]] [deh/bex [...]]\n");
124 Printf(PRINT_HIGH, "\n");
125 Printf(PRINT_HIGH, "Load a wad file on the fly, pwads/dehs/bexs require extension\n");
126 Printf(PRINT_HIGH, "eg: wad doom\n");
127
128 return;
129 }
130
131 if (paused)
132 {
133 paused = false;
134 S_ResumeSound ();
135 }
136
137 C_HideConsole();
138
139 std::string str = JoinStrings(VectorArgs(argc, argv), " ");
140 G_LoadWad(str);
141
142 D_StartTitle ();
143 CL_QuitNetGame();
144 S_StopMusic();
145 S_StartMusic(gameinfo.titleMusic);
146 }
147 END_COMMAND (wad)
148
EXTERN_CVAR(sv_allowexit)149 EXTERN_CVAR(sv_allowexit)
150 EXTERN_CVAR(sv_nomonsters)
151 EXTERN_CVAR(sv_freelook)
152 EXTERN_CVAR(sv_allowjump)
153
154 void G_DoNewGame (void)
155 {
156 if (demoplayback)
157 {
158 cvar_t::C_RestoreCVars ();
159 demoplayback = false;
160 D_SetupUserInfo ();
161 }
162
163 CL_QuitNetGame();
164
165 netgame = false;
166 multiplayer = false;
167
168 // denis - single player warp (like in d_main)
169 serverside = true;
170
171 players.clear();
172 players.push_back(player_t());
173 players.back().playerstate = PST_REBORN;
174 consoleplayer_id = displayplayer_id = players.back().id = 1;
175
176 G_InitNew (d_mapname);
177 gameaction = ga_nothing;
178 }
179
G_InitNew(const char * mapname)180 void G_InitNew (const char *mapname)
181 {
182 size_t i;
183
184 // [RH] Remove all particles
185 R_ClearParticles ();
186
187 for (Players::iterator it = players.begin();it != players.end();++it)
188 {
189 it->mo = AActor::AActorPtr();
190 it->camera = AActor::AActorPtr();
191 it->attacker = AActor::AActorPtr();
192 }
193
194 if (!savegamerestore)
195 G_ClearSnapshots ();
196
197 // [RH] Mark all levels as not visited
198 if (!savegamerestore)
199 {
200 for (i = 0; i < wadlevelinfos.size(); i++)
201 wadlevelinfos[i].flags &= ~LEVEL_VISITED;
202
203 for (i = 0; LevelInfos[i].mapname[0]; i++)
204 LevelInfos[i].flags &= ~LEVEL_VISITED;
205 }
206
207 cvar_t::UnlatchCVars ();
208
209 if (sv_skill > sk_nightmare)
210 sv_skill.Set (sk_nightmare);
211 else if (sv_skill < sk_baby)
212 sv_skill.Set (sk_baby);
213
214 cvar_t::UnlatchCVars ();
215
216 if (paused)
217 {
218 paused = false;
219 S_ResumeSound ();
220 }
221
222 // If were in chasecam mode, clear out // [Toke - fix]
223 if ((consoleplayer().cheats & CF_CHASECAM))
224 {
225 consoleplayer().cheats &= ~CF_CHASECAM;
226 }
227
228 // [RH] If this map doesn't exist, bomb out
229 if (W_CheckNumForName (mapname) == -1)
230 {
231 I_Error ("Could not find map %s\n", mapname);
232 }
233
234 if (sv_skill == sk_nightmare || sv_monstersrespawn)
235 respawnmonsters = true;
236 else
237 respawnmonsters = false;
238
239 bool wantFast = sv_fastmonsters || (sv_skill == sk_nightmare);
240 if (wantFast != isFast)
241 {
242 if (wantFast)
243 {
244 for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
245 states[i].tics >>= 1;
246 mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT;
247 mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT;
248 mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT;
249 }
250 else
251 {
252 for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
253 states[i].tics <<= 1;
254 mobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT;
255 mobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT;
256 mobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT;
257 }
258 isFast = wantFast;
259 }
260
261 if (!savegamerestore)
262 {
263 M_ClearRandom ();
264 memset (ACS_WorldVars, 0, sizeof(ACS_WorldVars));
265 memset (ACS_GlobalVars, 0, sizeof(ACS_GlobalVars));
266 level.time = 0;
267 level.timeleft = 0;
268 level.inttimeleft = 0;
269
270 // force players to be initialized upon first level load
271 for (Players::iterator it = players.begin();it != players.end();++it)
272 it->playerstate = PST_ENTER; // [BC]
273 }
274
275 usergame = true; // will be set false if a demo
276 paused = false;
277 demoplayback = false;
278 automapactive = false;
279 viewactive = true;
280 shotclock = 0;
281
282 D_SetupUserInfo();
283
284 strncpy (level.mapname, mapname, 8);
285 G_DoLoadLevel (0);
286 }
287
288 //
289 // G_DoCompleted
290 //
291 BOOL secretexit;
292 static int startpos; // [RH] Support for multiple starts per level
293 extern BOOL NoWipe; // [RH] Don't wipe when travelling in hubs
294
295 // [RH] The position parameter to these next three functions should
296 // match the first parameter of the single player start spots
297 // that should appear in the next map.
goOn(int position)298 static void goOn (int position)
299 {
300 cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
301 cluster_info_t *nextcluster = FindClusterInfo (FindLevelInfo (level.nextmap)->cluster);
302
303 startpos = position;
304 gameaction = ga_completed;
305
306 if (thiscluster && (thiscluster->flags & CLUSTER_HUB))
307 {
308 if ((level.flags & LEVEL_NOINTERMISSION) || (nextcluster == thiscluster))
309 NoWipe = 4;
310 D_DrawIcon = "TELEICON";
311 }
312 }
313
G_ExitLevel(int position,int drawscores)314 void G_ExitLevel (int position, int drawscores)
315 {
316 secretexit = false;
317 shotclock = 0;
318
319 goOn (position);
320
321 //gameaction = ga_completed;
322 }
323
324 // Here's for the german edition.
G_SecretExitLevel(int position,int drawscores)325 void G_SecretExitLevel (int position, int drawscores)
326 {
327 // IF NO WOLF3D LEVELS, NO SECRET EXIT!
328 if ( (gameinfo.flags & GI_MAPxx)
329 && (W_CheckNumForName("map31")<0))
330 secretexit = false;
331 else
332 secretexit = true;
333
334 shotclock = 0;
335
336 goOn (position);
337 //gameaction = ga_completed;
338 }
339
G_DoCompleted(void)340 void G_DoCompleted (void)
341 {
342 size_t i;
343
344 gameaction = ga_nothing;
345
346 for (Players::iterator it = players.begin();it != players.end();++it)
347 if (it->ingame())
348 G_PlayerFinishLevel(*it);
349
350 V_RestoreScreenPalette();
351
352 // [RH] Mark this level as having been visited
353 if (!(level.flags & LEVEL_CHANGEMAPCHEAT))
354 FindLevelInfo (level.mapname)->flags |= LEVEL_VISITED;
355
356 if (automapactive)
357 AM_Stop ();
358
359 // [ML] Chex mode: they didn't even show the intermission screen
360 // after the fifth level - I checked.
361 if (gamemode == retail_chex && !strncmp(level.mapname,"E1M5",4)) {
362 G_WorldDone();
363 return;
364 }
365
366 wminfo.epsd = level.cluster - 1; // Only used for DOOM I.
367 strncpy (wminfo.lname0, level.info->pname, 8);
368 strncpy (wminfo.current, level.mapname, 8);
369
370 if (sv_gametype != GM_COOP &&
371 !(level.flags & LEVEL_CHANGEMAPCHEAT)) {
372 strncpy (wminfo.next, level.mapname, 8);
373 strncpy (wminfo.lname1, level.info->pname, 8);
374 } else {
375 wminfo.next[0] = 0;
376 if (secretexit) {
377 if (W_CheckNumForName (level.secretmap) != -1) {
378 strncpy (wminfo.next, level.secretmap, 8);
379 strncpy (wminfo.lname1, FindLevelInfo (level.secretmap)->pname, 8);
380 } else {
381 secretexit = false;
382 }
383 }
384 if (!wminfo.next[0]) {
385 strncpy (wminfo.next, level.nextmap, 8);
386 strncpy (wminfo.lname1, FindLevelInfo (level.nextmap)->pname, 8);
387 }
388 }
389
390 wminfo.maxkills = level.total_monsters;
391 wminfo.maxitems = level.total_items;
392 wminfo.maxsecret = level.total_secrets;
393 wminfo.maxfrags = 0;
394 wminfo.partime = TICRATE * level.partime;
395
396 wminfo.plyr.resize(players.size());
397
398 i = 0;
399 for (Players::iterator it = players.begin();it != players.end();++it,++i)
400 {
401 wminfo.plyr[i].in = it->ingame();
402 wminfo.plyr[i].skills = it->killcount;
403 wminfo.plyr[i].sitems = it->itemcount;
404 wminfo.plyr[i].ssecret = it->secretcount;
405 wminfo.plyr[i].stime = level.time;
406 //memcpy (wminfo.plyr[i].frags, players[i].frags
407 // , sizeof(wminfo.plyr[i].frags));
408 wminfo.plyr[i].fragcount = it->fragcount;
409
410 if(&*it == &consoleplayer())
411 wminfo.pnum = i;
412 }
413
414 // [RH] If we're in a hub and staying within that hub, take a snapshot
415 // of the level. If we're traveling to a new hub, take stuff from
416 // the player and clear the world vars. If this is just an
417 // ordinary cluster (not a hub), take stuff from the player, but
418 // leave the world vars alone.
419 {
420 cluster_info_t *thiscluster = FindClusterInfo (level.cluster);
421 cluster_info_t *nextcluster = FindClusterInfo (FindLevelInfo (level.nextmap)->cluster);
422
423 if (thiscluster != nextcluster ||
424 sv_gametype == GM_DM ||
425 !(thiscluster->flags & CLUSTER_HUB)) {
426 for (Players::iterator it = players.begin();it != players.end();++it)
427 if (it->ingame())
428 G_PlayerFinishLevel(*it); // take away cards and stuff
429
430 if (nextcluster->flags & CLUSTER_HUB) {
431 memset (ACS_WorldVars, 0, sizeof(ACS_WorldVars));
432 P_RemoveDefereds ();
433 G_ClearSnapshots ();
434 }
435 } else {
436 G_SnapshotLevel ();
437 }
438 if (!(nextcluster->flags & CLUSTER_HUB) || !(thiscluster->flags & CLUSTER_HUB))
439 {
440 level.time = 0; // Reset time to zero if not entering/staying in a hub
441 level.timeleft = 0;
442 //level.inttimeleft = 0;
443 }
444
445 if (!(sv_gametype == GM_DM) &&
446 ((level.flags & LEVEL_NOINTERMISSION) ||
447 ((nextcluster == thiscluster) && (thiscluster->flags & CLUSTER_HUB)))) {
448 G_WorldDone ();
449 return;
450 }
451 }
452
453 gamestate = GS_INTERMISSION;
454 viewactive = false;
455 automapactive = false;
456
457 WI_Start (&wminfo);
458 }
459
460 //
461 // G_DoLoadLevel
462 //
463 extern gamestate_t wipegamestate;
464
465
G_DoLoadLevel(int position)466 void G_DoLoadLevel (int position)
467 {
468 static int lastposition = 0;
469 size_t i;
470
471 if (position == -1)
472 position = lastposition;
473 else
474 lastposition = position;
475
476 cvar_t::UnlatchCVars();
477
478 G_InitLevelLocals ();
479
480 Printf_Bold ("\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
481 "\36\36\36\36\36\36\36\36\36\36\36\36\37\n"
482 "%s: \"%s\"\n\n", level.mapname, level.level_name);
483
484 if (wipegamestate == GS_LEVEL)
485 wipegamestate = GS_FORCEWIPE;
486
487 if(gamestate != GS_DEMOSCREEN && ConsoleState == c_down)
488 C_HideConsole();
489
490 gamestate = GS_LEVEL;
491
492 // [SL] clear the saved sector data from the last level
493 R_ResetInterpolation();
494
495 // Set the sky map.
496 // First thing, we have a dummy sky texture name,
497 // a flat. The data is in the WAD only because
498 // we look for an actual index, instead of simply
499 // setting one.
500 skyflatnum = R_FlatNumForName ( SKYFLATNAME );
501
502 // DOOM determines the sky texture to be used
503 // depending on the current episode, and the game version.
504 // [RH] Fetch sky parameters from level_locals_t.
505 // [ML] 5/11/06 - remove sky2 remenants
506 // [SL] 2012-03-19 - Add sky2 back
507 sky1texture = R_TextureNumForName (level.skypic);
508 if (strlen(level.skypic2))
509 sky2texture = R_TextureNumForName (level.skypic2);
510 else
511 sky2texture = 0;
512
513 // [RH] Set up details about sky rendering
514 R_InitSkyMap ();
515
516 for (Players::iterator it = players.begin();it != players.end();++it)
517 {
518 if (it->ingame() && it->playerstate == PST_DEAD)
519 it->playerstate = PST_REBORN;
520
521 // [AM] If sv_keepkeys is on, players might still be carrying keys, so
522 // make sure they're gone.
523 for (size_t j = 0; j < NUMCARDS; j++)
524 it->cards[j] = false;
525
526 it->fragcount = 0;
527 it->itemcount = 0;
528 it->secretcount = 0;
529 it->deathcount = 0; // [Toke - Scores - deaths]
530 it->killcount = 0; // [deathz0r] Coop kills
531 it->points = 0;
532 }
533
534 // initialize the msecnode_t freelist. phares 3/25/98
535 // any nodes in the freelist are gone by now, cleared
536 // by Z_FreeTags() when the previous level ended or player
537 // died.
538
539 {
540 extern msecnode_t *headsecnode; // phares 3/25/98
541 headsecnode = NULL;
542
543 // denis - todo - wtf is this crap?
544 // [RH] Need to prevent the AActor destructor from trying to
545 // free the nodes
546 AActor *actor;
547 TThinkerIterator<AActor> iterator;
548
549 while ( (actor = iterator.Next ()) )
550 actor->touching_sectorlist = NULL;
551 }
552
553 SN_StopAllSequences (); // denis - todo - equivalent?
554 P_SetupLevel (level.mapname, position);
555
556 // [SL] 2011-09-18 - Find an alternative start if the single player start
557 // point is not availible.
558 if (!multiplayer && !consoleplayer().mo && consoleplayer().ingame())
559 {
560 // Check for a co-op start point
561 for (size_t n = 0; n < playerstarts.size() && !consoleplayer().mo; n++)
562 {
563 if (G_CheckSpot(consoleplayer(), &playerstarts[n]))
564 P_SpawnPlayer(consoleplayer(), &playerstarts[n]);
565 }
566
567 // Check for a free deathmatch start point
568 for (int n = 0; n < deathmatch_p - deathmatchstarts && !consoleplayer().mo; n++)
569 {
570 if (G_CheckSpot(consoleplayer(), &deathmatchstarts[n]))
571 P_SpawnPlayer(consoleplayer(), &deathmatchstarts[n]);
572 }
573
574 // Check for a free team start point
575 for (int n = 0; n < blueteam_p - blueteamstarts && !consoleplayer().mo; n++)
576 {
577 if (G_CheckSpot(consoleplayer(), &blueteamstarts[n]))
578 P_SpawnPlayer(consoleplayer(), &blueteamstarts[n]);
579 }
580
581 // Check for a free team start point
582 for (int n = 0; n <redteam_p - redteamstarts && !consoleplayer().mo; n++)
583 {
584 if (G_CheckSpot(consoleplayer(), &redteamstarts[n]))
585 P_SpawnPlayer(consoleplayer(), &redteamstarts[n]);
586 }
587 }
588
589 displayplayer_id = consoleplayer_id; // view the guy you are playing
590 ST_Start(); // [RH] Make sure status bar knows who we are
591 gameaction = ga_nothing;
592 Z_CheckHeap ();
593
594 // clear cmd building stuff // denis - todo - could we get rid of this?
595 Impulse = 0;
596 for (i = 0; i < NUM_ACTIONS; i++)
597 if (i != ACTION_MLOOK && i != ACTION_KLOOK)
598 Actions[i] = 0;
599 joyforward = joystrafe = joyturn = joylook = 0;
600 mousex = mousey = 0;
601 sendpause = sendsave = paused = sendcenterview = false;
602
603 if (timingdemo)
604 {
605 static BOOL firstTime = true;
606
607 if (firstTime)
608 {
609 starttime = I_MSTime();
610 firstTime = false;
611 }
612 }
613
614 level.starttime = I_MSTime() * TICRATE / 1000;
615 G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level.
616 P_DoDeferedScripts (); // [RH] Do script actions that were triggered on another map.
617
618 C_FlushDisplay ();
619 }
620
621 //
622 // G_WorldDone
623 //
G_WorldDone(void)624 void G_WorldDone (void)
625 {
626 cluster_info_t *nextcluster;
627 cluster_info_t *thiscluster;
628
629 gameaction = ga_worlddone;
630
631 if (level.flags & LEVEL_CHANGEMAPCHEAT)
632 return;
633
634 thiscluster = FindClusterInfo (level.cluster);
635 if (!strncmp (level.nextmap, "EndGame", 7) || (gamemode == retail_chex && !strncmp (level.nextmap, "E1M6", 4))) {
636 automapactive = false;
637 F_StartFinale (thiscluster->messagemusic, thiscluster->finaleflat, thiscluster->exittext);
638 } else {
639 if (!secretexit)
640 nextcluster = FindClusterInfo (FindLevelInfo (level.nextmap)->cluster);
641 else
642 nextcluster = FindClusterInfo (FindLevelInfo (level.secretmap)->cluster);
643
644 if (nextcluster->cluster != level.cluster && sv_gametype == GM_COOP) {
645 // Only start the finale if the next level's cluster is different
646 // than the current one and we're not in deathmatch.
647 if (nextcluster->entertext) {
648 automapactive = false;
649 F_StartFinale (nextcluster->messagemusic, nextcluster->finaleflat, nextcluster->entertext);
650 } else if (thiscluster->exittext) {
651 automapactive = false;
652 F_StartFinale (thiscluster->messagemusic, thiscluster->finaleflat, thiscluster->exittext);
653 }
654 }
655 }
656 }
657
658
659
660
661 VERSION_CONTROL (g_level_cpp, "$Id: g_level.cpp 4664 2014-03-21 19:57:19Z dr_sean $")
662