summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/g_game.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/doom/g_game.c')
-rw-r--r--apps/plugins/doom/g_game.c2818
1 files changed, 2818 insertions, 0 deletions
diff --git a/apps/plugins/doom/g_game.c b/apps/plugins/doom/g_game.c
new file mode 100644
index 0000000000..426ab1ca13
--- /dev/null
+++ b/apps/plugins/doom/g_game.c
@@ -0,0 +1,2818 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 * 02111-1307, USA.
26 *
27 * DESCRIPTION: none
28 * The original Doom description was none, basically because this file
29 * has everything. This ties up the game logic, linking the menu and
30 * input code to the underlying game by creating & respawning players,
31 * building game tics, calling the underlying thing logic.
32 *
33 *-----------------------------------------------------------------------------
34 */
35
36#include "doomdef.h"
37#include "doomstat.h"
38#include "z_zone.h"
39#include "f_finale.h"
40#include "m_argv.h"
41#include "m_misc.h"
42#include "m_menu.h"
43#include "m_random.h"
44#include "i_system.h"
45#include "p_map.h"
46#include "p_setup.h"
47#include "p_saveg.h"
48#include "p_tick.h"
49
50#include "d_main.h"
51
52#include "wi_stuff.h"
53#include "hu_stuff.h"
54#include "st_stuff.h"
55#include "am_map.h"
56
57// Needs access to LFB.
58#include "v_video.h"
59
60#include "w_wad.h"
61#include "r_main.h"
62#include "s_sound.h"
63
64// Data.
65#include "dstrings.h"
66#include "sounds.h"
67
68// SKY handling - still the wrong place.
69#include "r_data.h"
70#include "r_sky.h"
71#include "p_inter.h"
72#include "g_game.h"
73
74#include "rockmacros.h"
75
76#define SAVEGAMESIZE 0x20000
77#define SAVESTRINGSIZE 24
78
79static size_t savegamesize = SAVEGAMESIZE; // killough
80static boolean netdemo;
81static const byte *demobuffer; /* cph - only used for playback */
82static int demofd; /* cph - record straight to file */
83static const byte *demo_p;
84static short consistancy[MAXPLAYERS][BACKUPTICS];
85
86gameaction_t gameaction;
87gamestate_t gamestate;
88skill_t gameskill;
89boolean respawnmonsters;
90int gameepisode;
91int gamemap;
92boolean paused;
93// CPhipps - moved *_loadgame vars here
94static boolean forced_loadgame = false;
95static boolean command_loadgame = false;
96
97boolean usergame; // ok to save / end game
98boolean timingdemo; // if true, exit with report on completion
99boolean fastdemo; // if true, run at full speed -- killough
100boolean nodrawers; // for comparative timing purposes
101boolean noblit; // for comparative timing purposes
102int starttime; // for comparative timing purposes
103boolean deathmatch; // only if started as net death
104boolean netgame; // only true if packets are broadcast
105boolean playeringame[MAXPLAYERS];
106player_t players[MAXPLAYERS];
107int consoleplayer; // player taking events and displaying
108int displayplayer; // view being displayed
109int gametic;
110int levelstarttic; // gametic at level start
111int basetic; /* killough 9/29/98: for demo sync */
112int totalkills, totallive, totalitems, totalsecret; // for intermission
113boolean demorecording;
114boolean demoplayback;
115boolean singledemo; // quit after playing a demo from cmdline
116wbstartstruct_t wminfo; // parms for world map / intermission
117boolean haswolflevels = false;// jff 4/18/98 wolf levels present
118static byte *savebuffer; // CPhipps - static
119int autorun = false; // always running? // phares
120int totalleveltimes; // CPhipps - total time for all completed levels
121int longtics;
122
123//
124// controls (have defaults)
125//
126int key_right;
127int key_left;
128int key_up;
129int key_down;
130int key_menu_right; // phares 3/7/98
131int key_menu_left; // |
132int key_menu_up; // V
133int key_menu_down;
134int key_menu_backspace; // ^
135int key_menu_escape; // |
136int key_menu_enter; // phares 3/7/98
137int key_strafeleft;
138int key_straferight;
139int key_fire;
140int key_use;
141int key_strafe;
142int key_speed;
143int key_escape = KEY_ESCAPE; // phares 4/13/98
144int key_weapon;
145
146int key_savegame; // phares
147int key_loadgame; // |
148int key_autorun; // V
149int key_reverse;
150int key_zoomin;
151int key_zoomout;
152
153int key_reverse;
154
155int key_chat;
156int key_backspace;
157int key_enter;
158int key_map_right;
159int key_map_left;
160int key_map_up;
161int key_map_down;
162int key_map_zoomin;
163int key_map_zoomout;
164int key_map;
165int key_map_gobig;
166int key_map_follow;
167int key_map_mark;
168int key_map_clear;
169int key_map_grid;
170int key_map_overlay; // cph - map overlay
171int key_map_rotate; // cph - map rotation
172int key_help = KEY_F1; // phares 4/13/98
173int key_soundvolume;
174int key_hud;
175int key_quicksave;
176int key_endgame;
177int key_messages;
178int key_quickload;
179int key_quit;
180int key_gamma;
181int key_spy;
182int key_pause;
183int key_setup;
184int destination_keys[MAXPLAYERS];
185int key_weapontoggle;
186int key_weapon1;
187int key_weapon2;
188int key_weapon3;
189int key_weapon4;
190int key_weapon5;
191int key_weapon6;
192int key_weapon7; // ^
193int key_weapon8; // |
194int key_weapon9; // phares
195
196int key_screenshot; // killough 2/22/98: screenshot key
197int mousebfire;
198int mousebstrafe;
199int mousebforward;
200int joybfire;
201int joybstrafe;
202int joybuse;
203int joybspeed;
204
205#define MAXPLMOVE (forwardmove[1])
206#define TURBOTHRESHOLD 0x32
207#define SLOWTURNTICS 6
208#define QUICKREVERSE (short)32768 // 180 degree reverse // phares
209#define NUMKEYS 512
210
211fixed_t forwardmove[2] = {0x19, 0x32};
212fixed_t sidemove[2] = {0x18, 0x28};
213fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
214
215// CPhipps - made lots of key/button state vars static
216static boolean gamekeydown[NUMKEYS];
217static int turnheld; // for accelerative turning
218
219static boolean mousearray[4];
220static boolean *mousebuttons = &mousearray[1]; // allow [-1]
221
222// mouse values are used once
223static int mousex;
224static int mousey;
225static unsigned int dclicktime;
226static unsigned int dclickstate;
227static unsigned int dclicks;
228static unsigned int dclicktime2;
229static unsigned int dclickstate2;
230static unsigned int dclicks2;
231
232// joystick values are repeated
233static int joyxmove;
234static int joyymove;
235static boolean joyarray[5];
236static boolean *joybuttons = &joyarray[1]; // allow [-1]
237
238// Game events info
239static buttoncode_t special_event; // Event triggered by local player, to send
240static byte savegameslot; // Slot to load if gameaction == ga_loadgame
241char savedescription[SAVEDESCLEN]; // Description to save in savegame if gameaction == ga_savegame
242
243//jff 3/24/98 declare startskill external, define defaultskill here
244extern skill_t startskill; //note 0-based
245int defaultskill; //note 1-based
246
247// killough 2/8/98: make corpse queue variable in size
248int bodyqueslot, bodyquesize; // killough 2/8/98
249mobj_t **bodyque = 0; // phares 8/10/98
250
251void* statcopy; // for statistics driver
252
253static void G_DoSaveGame (boolean menu);
254static const byte* G_ReadDemoHeader(const byte* demo_p);
255
256//
257// G_BuildTiccmd
258// Builds a ticcmd from all of the available inputs
259// or reads it from the demo buffer.
260// If recording a demo, write it out
261//
262static inline signed char fudgef(signed char b)
263{
264 static int c;
265 if (!b || !demo_compatibility || longtics) return b;
266 if (++c & 0x1f) return b;
267 b |= 1; if (b>2) b-=2;
268 return b;
269}
270
271static inline signed short fudgea(signed short b)
272{
273 if (!b || !demo_compatibility || !longtics) return b;
274 b |= 1; if (b>2) b-=2;
275 return b;
276}
277
278void G_BuildTiccmd(ticcmd_t* cmd)
279{
280 boolean strafe;
281 boolean bstrafe;
282 int speed;
283 int tspeed;
284 int forward;
285 int side;
286 int newweapon=0; // phares
287 /* cphipps - remove needless I_BaseTiccmd call, just set the ticcmd to zero */
288 memset(cmd,0,sizeof*cmd);
289 cmd->consistancy = consistancy[consoleplayer][maketic%BACKUPTICS];
290
291 strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
292 || joybuttons[joybstrafe];
293 speed = autorun || gamekeydown[key_speed] || joybuttons[joybspeed]; // phares
294
295 forward = side = 0;
296
297 // use two stage accelerative turning
298 // on the keyboard and joystick
299 if (joyxmove < 0 || joyxmove > 0 ||
300 gamekeydown[key_right] || gamekeydown[key_left])
301 turnheld += ticdup;
302 else
303 turnheld = 0;
304
305 if (turnheld < SLOWTURNTICS)
306 tspeed = 2; // slow turn
307 else
308 tspeed = speed;
309
310 // turn 180 degrees in one keystroke? // phares
311 // |
312 if (gamekeydown[key_reverse]) // V
313 {
314 cmd->angleturn += QUICKREVERSE; // ^
315 gamekeydown[key_reverse] = false; // |
316 } // phares
317
318 // let movement keys cancel each other out
319
320 if (strafe)
321 {
322 if (gamekeydown[key_right])
323 side += sidemove[speed];
324 if (gamekeydown[key_left])
325 side -= sidemove[speed];
326 if (joyxmove > 0)
327 side += sidemove[speed];
328 if (joyxmove < 0)
329 side -= sidemove[speed];
330 }
331 else
332 {
333 if (gamekeydown[key_right])
334 cmd->angleturn -= angleturn[tspeed];
335 if (gamekeydown[key_left])
336 cmd->angleturn += angleturn[tspeed];
337 if (joyxmove > 0)
338 cmd->angleturn -= angleturn[tspeed];
339 if (joyxmove < 0)
340 cmd->angleturn += angleturn[tspeed];
341 }
342
343 if (gamekeydown[key_up])
344 forward += forwardmove[speed];
345 if (gamekeydown[key_down])
346 forward -= forwardmove[speed];
347 if (joyymove < 0)
348 forward += forwardmove[speed];
349 if (joyymove > 0)
350 forward -= forwardmove[speed];
351 if (gamekeydown[key_straferight])
352 side += sidemove[speed];
353 if (gamekeydown[key_strafeleft])
354 side -= sidemove[speed];
355
356 // buttons
357 cmd->chatchar = HU_dequeueChatChar();
358
359 if (gamekeydown[key_fire] || mousebuttons[mousebfire] ||
360 joybuttons[joybfire])
361 cmd->buttons |= BT_ATTACK;
362
363 if (gamekeydown[key_use] || joybuttons[joybuse])
364 {
365 cmd->buttons |= BT_USE;
366 // clear double clicks if hit use button
367 dclicks = 0;
368 }
369
370 // Toggle between the top 2 favorite weapons. // phares
371 // If not currently aiming one of these, switch to // phares
372 // the favorite. Only switch if you possess the weapon. // phares
373
374 // killough 3/22/98:
375 //
376 // Perform automatic weapons switch here rather than in p_pspr.c,
377 // except in demo_compatibility mode.
378 //
379 // killough 3/26/98, 4/2/98: fix autoswitch when no weapons are left
380
381 if ((!demo_compatibility && players[consoleplayer].attackdown && // killough
382 !P_CheckAmmo(&players[consoleplayer])) || gamekeydown[key_weapontoggle])
383 newweapon = P_SwitchWeapon(&players[consoleplayer]); // phares
384 else
385 { // phares 02/26/98: Added gamemode checks
386 if(gamekeydown[key_weapon])
387 {
388 volatile unsigned int wpcheck; // I don't know why this is needed, but it is
389 for(wpcheck=0; wpcheck<9; wpcheck++)
390 if(players[consoleplayer].weaponowned[wpcheck] && wpcheck>players[consoleplayer].readyweapon )
391 {
392 newweapon=wpcheck;
393 break;
394 }
395 if(players[consoleplayer].weaponowned[wp_chainsaw]&&newweapon==0)
396 newweapon=1;
397 }
398 else
399 {
400 newweapon =
401 gamekeydown[key_weapon1] ? wp_fist : // killough 5/2/98: reformatted
402 gamekeydown[key_weapon2] ? wp_pistol :
403 gamekeydown[key_weapon3] ? wp_shotgun :
404 gamekeydown[key_weapon4] ? wp_chaingun :
405 gamekeydown[key_weapon5] ? wp_missile :
406 gamekeydown[key_weapon6] && gamemode != shareware ? wp_plasma :
407 gamekeydown[key_weapon7] && gamemode != shareware ? wp_bfg :
408 gamekeydown[key_weapon8] ? wp_chainsaw :
409 gamekeydown[key_weapon9] && gamemode == commercial ? wp_supershotgun :
410 wp_nochange;
411 }
412
413 // killough 3/22/98: For network and demo consistency with the
414 // new weapons preferences, we must do the weapons switches here
415 // instead of in p_user.c. But for old demos we must do it in
416 // p_user.c according to the old rules. Therefore demo_compatibility
417 // determines where the weapons switch is made.
418
419 // killough 2/8/98:
420 // Allow user to switch to fist even if they have chainsaw.
421 // Switch to fist or chainsaw based on preferences.
422 // Switch to shotgun or SSG based on preferences.
423
424 if (!demo_compatibility)
425 {
426 const player_t *player = &players[consoleplayer];
427
428 // only select chainsaw from '1' if it's owned, it's
429 // not already in use, and the player prefers it or
430 // the fist is already in use, or the player does not
431 // have the berserker strength.
432
433 if (newweapon==wp_fist && player->weaponowned[wp_chainsaw] &&
434 player->readyweapon!=wp_chainsaw &&
435 (player->readyweapon==wp_fist ||
436 !player->powers[pw_strength] ||
437 P_WeaponPreferred(wp_chainsaw, wp_fist)))
438 newweapon = wp_chainsaw;
439
440 // Select SSG from '3' only if it's owned and the player
441 // does not have a shotgun, or if the shotgun is already
442 // in use, or if the SSG is not already in use and the
443 // player prefers it.
444 if(!gamekeydown[key_weapon])
445 if (newweapon == wp_shotgun && gamemode == commercial &&
446 player->weaponowned[wp_supershotgun] &&
447 (!player->weaponowned[wp_shotgun] ||
448 player->readyweapon == wp_shotgun ||
449 (player->readyweapon != wp_supershotgun &&
450 P_WeaponPreferred(wp_supershotgun, wp_shotgun))))
451 newweapon = wp_supershotgun;
452
453 }
454 // killough 2/8/98, 3/22/98 -- end of weapon selection changes
455 }
456
457 if(newweapon >wp_nochange) // something is messed up with the weapon switching code above allowing it to give values greater
458 { // then wp_nochange which really screws the game up
459 newweapon=0;
460 }
461 if (newweapon != wp_nochange)
462 {
463 cmd->buttons |= BT_CHANGE;
464 cmd->buttons |= newweapon<<BT_WEAPONSHIFT;
465 }
466
467 // mouse
468 if (mousebuttons[mousebforward])
469 forward += forwardmove[speed];
470
471 // forward double click
472 if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
473 {
474 dclickstate = mousebuttons[mousebforward];
475 if (dclickstate)
476 dclicks++;
477 if (dclicks == 2)
478 {
479 cmd->buttons |= BT_USE;
480 dclicks = 0;
481 }
482 else
483 dclicktime = 0;
484 }
485 else
486 if ((dclicktime += ticdup) > 20)
487 {
488 dclicks = 0;
489 dclickstate = 0;
490 }
491
492 // strafe double click
493
494 bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe];
495 if (bstrafe != dclickstate2 && dclicktime2 > 1 )
496 {
497 dclickstate2 = bstrafe;
498 if (dclickstate2)
499 dclicks2++;
500 if (dclicks2 == 2)
501 {
502 cmd->buttons |= BT_USE;
503 dclicks2 = 0;
504 }
505 else
506 dclicktime2 = 0;
507 }
508 else
509 if ((dclicktime2 += ticdup) > 20)
510 {
511 dclicks2 = 0;
512 dclickstate2 = 0;
513 }
514 forward += mousey;
515 if (strafe)
516 side += mousex / 4; /* mead Don't want to strafe as fast as turns.*/
517 else
518 cmd->angleturn -= mousex; /* mead now have enough dynamic range 2-10-00 */
519
520 mousex = mousey = 0;
521
522 if (forward > MAXPLMOVE)
523 forward = MAXPLMOVE;
524 else if (forward < -MAXPLMOVE)
525 forward = -MAXPLMOVE;
526 if (side > MAXPLMOVE)
527 side = MAXPLMOVE;
528 else if (side < -MAXPLMOVE)
529 side = -MAXPLMOVE;
530
531 cmd->forwardmove += fudgef(forward);
532 cmd->sidemove += side;
533 cmd->angleturn = fudgea(cmd->angleturn);
534
535 // CPhipps - special events (game new/load/save/pause)
536 if (special_event & BT_SPECIAL) {
537 cmd->buttons = special_event;
538 special_event = 0;
539 }
540}
541
542//
543// G_RestartLevel
544//
545
546void G_RestartLevel(void)
547{
548 special_event = BT_SPECIAL | (BTS_RESTARTLEVEL & BT_SPECIALMASK);
549}
550
551#include "z_bmalloc.h"
552//
553// G_DoLoadLevel
554//
555extern gamestate_t wipegamestate;
556
557static void G_DoLoadLevel (void)
558{
559 int i;
560
561 // Set the sky map.
562 // First thing, we have a dummy sky texture name,
563 // a flat. The data is in the WAD only because
564 // we look for an actual index, instead of simply
565 // setting one.
566
567 skyflatnum = R_FlatNumForName ( SKYFLATNAME );
568
569 // DOOM determines the sky texture to be used
570 // depending on the current episode, and the game version.
571 if (gamemode == commercial)
572 // || gamemode == pack_tnt //jff 3/27/98 sorry guys pack_tnt,pack_plut
573 // || gamemode == pack_plut) //aren't gamemodes, this was matching retail
574 {
575 skytexture = R_TextureNumForName ("SKY3");
576 if (gamemap < 12)
577 skytexture = R_TextureNumForName ("SKY1");
578 else
579 if (gamemap < 21)
580 skytexture = R_TextureNumForName ("SKY2");
581 }
582 else //jff 3/27/98 and lets not forget about DOOM and Ultimate DOOM huh?
583 switch (gameepisode)
584 {
585 case 1:
586 skytexture = R_TextureNumForName ("SKY1");
587 break;
588 case 2:
589 skytexture = R_TextureNumForName ("SKY2");
590 break;
591 case 3:
592 skytexture = R_TextureNumForName ("SKY3");
593 break;
594 case 4: // Special Edition sky
595 skytexture = R_TextureNumForName ("SKY4");
596 break;
597 }//jff 3/27/98 end sky setting fix
598
599 levelstarttic = gametic; // for time calculation
600
601 if (!demo_compatibility && !mbf_features) // killough 9/29/98
602 basetic = gametic;
603
604 if (wipegamestate == GS_LEVEL)
605 wipegamestate = -1; // force a wipe
606
607 gamestate = GS_LEVEL;
608
609 for (i=0 ; i<MAXPLAYERS ; i++)
610 {
611 if (playeringame[i] && players[i].playerstate == PST_DEAD)
612 players[i].playerstate = PST_REBORN;
613 memset (players[i].frags,0,sizeof(players[i].frags));
614 }
615
616 // initialize the msecnode_t freelist. phares 3/25/98
617 // any nodes in the freelist are gone by now, cleared
618 // by Z_FreeTags() when the previous level ended or player
619 // died.
620
621 {
622 DECLARE_BLOCK_MEMORY_ALLOC_ZONE(secnodezone);
623 NULL_BLOCK_MEMORY_ALLOC_ZONE(secnodezone);
624 //extern msecnode_t *headsecnode; // phares 3/25/98
625 //headsecnode = NULL;
626 }
627
628 P_SetupLevel (gameepisode, gamemap, 0, gameskill);
629 displayplayer = consoleplayer; // view the guy you are playing
630 gameaction = ga_nothing;
631 Z_CheckHeap ();
632
633 // clear cmd building stuff
634 memset (gamekeydown, 0, sizeof(gamekeydown));
635 joyxmove = joyymove = 0;
636 mousex = mousey = 0;
637 special_event = 0; paused = false;
638 memset (mousebuttons, 0, sizeof(mousebuttons));
639 memset (joybuttons, 0, sizeof(joybuttons));
640
641 // killough 5/13/98: in case netdemo has consoleplayer other than green
642 ST_Start();
643 HU_Start();
644
645 // killough: make -timedemo work on multilevel demos
646 // Move to end of function to minimize noise -- killough 2/22/98:
647
648 if (timingdemo)
649 {
650 static int first=1;
651 if (first)
652 {
653 starttime = I_GetTime ();
654 first=0;
655 }
656 }
657}
658
659//
660// G_Responder
661// Get info needed to make ticcmd_ts for the players.
662//
663boolean G_Responder (event_t* ev)
664{
665 // allow spy mode changes even during the demo
666 // killough 2/22/98: even during DM demo
667 //
668 // killough 11/98: don't autorepeat spy mode switch
669
670 if (ev->data1 == key_spy && netgame && (demoplayback || !deathmatch) &&
671 gamestate == GS_LEVEL)
672 {
673 if (ev->type == ev_keyup)
674 gamekeydown[key_spy] = false;
675 if (ev->type == ev_keydown && !gamekeydown[key_spy])
676 {
677 gamekeydown[key_spy] = true;
678 do // spy mode
679 if (++displayplayer >= MAXPLAYERS)
680 displayplayer = 0;
681 while (!playeringame[displayplayer] && displayplayer!=consoleplayer);
682
683 ST_Start(); // killough 3/7/98: switch status bar views too
684 HU_Start();
685 S_UpdateSounds(players[displayplayer].mo);
686 }
687 return true;
688 }
689
690 // any other key pops up menu if in demos
691 //
692 // killough 8/2/98: enable automap in -timedemo demos
693 //
694 // killough 9/29/98: make any key pop up menu regardless of
695 // which kind of demo, and allow other events during playback
696
697 if (gameaction == ga_nothing && (demoplayback || gamestate == GS_DEMOSCREEN))
698 {
699 // killough 9/29/98: allow user to pause demos during playback
700 if (ev->type == ev_keydown && ev->data1 == key_pause)
701 {
702 if (paused ^= 2)
703 S_PauseSound();
704 else
705 S_ResumeSound();
706 return true;
707 }
708 // killough 10/98:
709 // Don't pop up menu, if paused in middle
710 // of demo playback, or if automap active.
711 // Don't suck up keys, which may be cheats
712
713 return gamestate == GS_DEMOSCREEN &&
714 !(paused & 2) && !(automapmode & am_active) &&
715 ((ev->type == ev_keydown) ||
716 (ev->type == ev_mouse && ev->data1) ||
717 (ev->type == ev_joystick && ev->data1)) ?
718 M_StartControlPanel(), true : false;
719 }
720
721 if (gamestate == GS_FINALE && F_Responder(ev))
722 return true; // finale ate the event
723
724 switch (ev->type)
725 {
726 case ev_keydown:
727 if (ev->data1 == key_pause) // phares
728 {
729 special_event = BT_SPECIAL | (BTS_PAUSE & BT_SPECIALMASK);
730 return true;
731 }
732 if (ev->data1 <NUMKEYS)
733 gamekeydown[ev->data1] = true;
734 return true; // eat key down events
735
736 case ev_keyup:
737 if (ev->data1 <NUMKEYS)
738 gamekeydown[ev->data1] = false;
739 return false; // always let key up events filter down
740
741 case ev_mouse:
742 mousebuttons[0] = ev->data1 & 1;
743 mousebuttons[1] = ev->data1 & 2;
744 mousebuttons[2] = ev->data1 & 4;
745// mousex = ev->data2*(mouseSensitivity+5)/10;
746// mousey = ev->data3*(mouseSensitivity+5)/10;
747 return true; // eat events
748
749 case ev_joystick:
750 joybuttons[0] = ev->data1 & 1;
751 joybuttons[1] = ev->data1 & 2;
752 joybuttons[2] = ev->data1 & 4;
753 joybuttons[3] = ev->data1 & 8;
754 joyxmove = ev->data2;
755 joyymove = ev->data3;
756 return true; // eat events
757
758 default:
759 break;
760 }
761 return false;
762}
763
764//
765// G_Ticker
766// Make ticcmd_ts for the players.
767//
768extern int mapcolor_me;
769
770void G_Ticker (void)
771{
772 int i;
773 static gamestate_t prevgamestate;
774
775 P_MapStart();
776 // do player reborns if needed
777 for (i=0 ; i<MAXPLAYERS ; i++)
778 if (playeringame[i] && players[i].playerstate == PST_REBORN)
779 G_DoReborn (i);
780 P_MapEnd();
781
782 // do things to change the game state
783 while (gameaction != ga_nothing)
784 {
785 switch (gameaction)
786 {
787 case ga_loadlevel:
788 // force players to be initialized on level reload
789 for (i=0 ; i<MAXPLAYERS ; i++)
790 players[i].playerstate = PST_REBORN;
791 G_DoLoadLevel ();
792 break;
793 case ga_newgame:
794 G_DoNewGame ();
795 break;
796 case ga_loadgame:
797 G_DoLoadGame ();
798 break;
799 case ga_savegame:
800 G_DoSaveGame (false);
801 break;
802 case ga_playdemo:
803 G_DoPlayDemo ();
804 break;
805 case ga_completed:
806 G_DoCompleted ();
807 break;
808 case ga_victory:
809 F_StartFinale ();
810 break;
811 case ga_worlddone:
812 G_DoWorldDone ();
813 break;
814 case ga_nothing:
815 break;
816 }
817 }
818
819 if (paused & 2 || (!demoplayback && menuactive && !netgame))
820 basetic++; // For revenant tracers and RNG -- we must maintain sync
821 else
822 {
823 // get commands, check consistancy, and build new consistancy check
824 int buf = (gametic/ticdup)%BACKUPTICS;
825
826 for (i=0 ; i<MAXPLAYERS ; i++)
827 {
828 if (playeringame[i])
829 {
830 ticcmd_t *cmd = &players[i].cmd;
831
832 memcpy(cmd, &netcmds[i][buf], sizeof *cmd);
833
834 if (demoplayback)
835 G_ReadDemoTiccmd (cmd);
836 if (demorecording)
837 G_WriteDemoTiccmd (cmd);
838
839 // check for turbo cheats
840 // killough 2/14/98, 2/20/98 -- only warn in netgames and demos
841
842 if ((netgame || demoplayback) && cmd->forwardmove > TURBOTHRESHOLD &&
843 !(gametic&31) && ((gametic>>5)&3) == i )
844 {
845 extern char *player_names[];
846 /* cph - don't use sprintf, use doom_printf */
847 doom_printf ("%s is turbo!", player_names[i]);
848 }
849
850 if (netgame && !netdemo && !(gametic%ticdup) )
851 {
852 if (gametic > BACKUPTICS
853 && consistancy[i][buf] != cmd->consistancy)
854 I_Error("G_Ticker: Consistency failure (%i should be %i)",
855 cmd->consistancy, consistancy[i][buf]);
856 if (players[i].mo)
857 consistancy[i][buf] = players[i].mo->x;
858 else
859 consistancy[i][buf] = 0; // killough 2/14/98
860 }
861 }
862 }
863
864 // check for special buttons
865 for (i=0 ; i<MAXPLAYERS ; i++)
866 {
867 if (playeringame[i])
868 {
869 if (players[i].cmd.buttons & BT_SPECIAL)
870 {
871 switch (players[i].cmd.buttons & BT_SPECIALMASK)
872 {
873 case BTS_PAUSE:
874 paused ^= 1;
875 if (paused)
876 S_PauseSound ();
877 else
878 S_ResumeSound ();
879 break;
880
881 case BTS_SAVEGAME:
882 if (!savedescription[0])
883 strcpy(savedescription, "NET GAME");
884 savegameslot =
885 (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
886 gameaction = ga_savegame;
887 break;
888
889 // CPhipps - remote loadgame request
890 case BTS_LOADGAME:
891 savegameslot =
892 (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
893 gameaction = ga_loadgame;
894 forced_loadgame = netgame; // Force if a netgame
895 command_loadgame = false;
896 break;
897
898 // CPhipps - Restart the level
899 case BTS_RESTARTLEVEL:
900 if (demoplayback || (compatibility_level < lxdoom_1_compatibility))
901 break; // CPhipps - Ignore in demos or old games
902 gameaction = ga_loadlevel;
903 break;
904 }
905 players[i].cmd.buttons = 0;
906 }
907 }
908 }
909 }
910
911 // cph - if the gamestate changed, we may need to clean up the old gamestate
912 if (gamestate != prevgamestate) {
913 switch (prevgamestate) {
914 case GS_INTERMISSION:
915 WI_End();
916 default:
917 break;
918 }
919 prevgamestate = gamestate;
920 }
921
922 // do main actions
923 switch (gamestate)
924 {
925 case GS_LEVEL:
926 P_Ticker ();
927 ST_Ticker ();
928 AM_Ticker ();
929 HU_Ticker ();
930 break;
931
932 case GS_INTERMISSION:
933 WI_Ticker ();
934 break;
935
936 case GS_FINALE:
937 F_Ticker ();
938 break;
939
940 case GS_DEMOSCREEN:
941 D_PageTicker ();
942 break;
943 }
944}
945
946
947//
948// PLAYER STRUCTURE FUNCTIONS
949// also see P_SpawnPlayer in P_Things
950//
951
952//
953// G_PlayerFinishLevel
954// Can when a player completes a level.
955//
956void G_PlayerFinishLevel(int player)
957{
958 player_t *p = &players[player];
959 memset(p->powers, 0, sizeof (p->powers));
960 memset(p->cards, 0, sizeof (p->cards));
961 p->mo->flags &= ~MF_SHADOW; // cancel invisibility
962 p->extralight = 0; // cancel gun flashes
963 p->fixedcolormap = 0; // cancel ir gogles
964 p->damagecount = 0; // no palette changes
965 p->bonuscount = 0;
966}
967
968// CPhipps - G_SetPlayerColour
969// Player colours stuff
970//
971// G_SetPlayerColour
972
973#include "r_draw.h"
974extern byte playernumtotrans[MAXPLAYERS];
975
976void G_ChangedPlayerColour(int pn, int cl)
977{
978 int i;
979
980 if (!netgame) return;
981
982 mapcolor_plyr[pn] = cl;
983
984 // Rebuild colour translation tables accordingly
985 R_InitTranslationTables();
986 // Change translations on existing player mobj's
987 for (i=0; i<MAXPLAYERS; i++) {
988 if ((gamestate == GS_LEVEL) && playeringame[i] && (players[i].mo != NULL)) {
989 players[i].mo->flags &= ~MF_TRANSLATION;
990 players[i].mo->flags |= playernumtotrans[i] << MF_TRANSSHIFT;
991 }
992 }
993}
994
995//
996// G_PlayerReborn
997// Called after a player dies
998// almost everything is cleared and initialized
999//
1000void G_PlayerReborn (int player)
1001{
1002 player_t *p;
1003 int i;
1004 int frags[MAXPLAYERS];
1005 int killcount;
1006 int itemcount;
1007 int secretcount;
1008
1009 memcpy (frags, players[player].frags, sizeof frags);
1010 killcount = players[player].killcount;
1011 itemcount = players[player].itemcount;
1012 secretcount = players[player].secretcount;
1013
1014 p = &players[player];
1015
1016 // killough 3/10/98,3/21/98: preserve cheats across idclev
1017 {
1018 int cheats = p->cheats;
1019 memset (p, 0, sizeof(*p));
1020 p->cheats = cheats;
1021 }
1022
1023 memcpy(players[player].frags, frags, sizeof(players[player].frags));
1024 players[player].killcount = killcount;
1025 players[player].itemcount = itemcount;
1026 players[player].secretcount = secretcount;
1027
1028 p->usedown = p->attackdown = true; // don't do anything immediately
1029 p->playerstate = PST_LIVE;
1030 p->health = MAXHEALTH;
1031 p->readyweapon = p->pendingweapon = wp_pistol;
1032 p->weaponowned[wp_fist] = true;
1033 p->weaponowned[wp_pistol] = true;
1034 p->ammo[am_clip] = 50;
1035
1036 for (i=0 ; i<NUMAMMO ; i++)
1037 p->maxammo[i] = maxammo[i];
1038
1039}
1040
1041//
1042// G_CheckSpot
1043// Returns false if the player cannot be respawned
1044// at the given mapthing_t spot
1045// because something is occupying it
1046//
1047
1048void P_SpawnPlayer(mapthing_t *mthing);
1049
1050boolean G_CheckSpot(int playernum, mapthing_t *mthing)
1051{
1052 fixed_t x,y;
1053 subsector_t *ss;
1054 int i;
1055
1056 if (!players[playernum].mo)
1057 {
1058 // first spawn of level, before corpses
1059 for (i=0 ; i<playernum ; i++)
1060 if (players[i].mo->x == mthing->x << FRACBITS
1061 && players[i].mo->y == mthing->y << FRACBITS)
1062 return false;
1063 return true;
1064 }
1065
1066 x = mthing->x << FRACBITS;
1067 y = mthing->y << FRACBITS;
1068
1069 // killough 4/2/98: fix bug where P_CheckPosition() uses a non-solid
1070 // corpse to detect collisions with other players in DM starts
1071 //
1072 // Old code:
1073 // if (!P_CheckPosition (players[playernum].mo, x, y))
1074 // return false;
1075
1076 players[playernum].mo->flags |= MF_SOLID;
1077 i = P_CheckPosition(players[playernum].mo, x, y);
1078 players[playernum].mo->flags &= ~MF_SOLID;
1079 if (!i)
1080 return false;
1081
1082 // flush an old corpse if needed
1083 // killough 2/8/98: make corpse queue have an adjustable limit
1084 // killough 8/1/98: Fix bugs causing strange crashes
1085
1086 if (bodyquesize > 0)
1087 {
1088 static mobj_t **bodyque;
1089 static int queuesize;
1090 if (queuesize < bodyquesize)
1091 {
1092 bodyque = realloc(bodyque, bodyquesize*sizeof*bodyque);
1093 memset(bodyque+queuesize, 0,
1094 (bodyquesize-queuesize)*sizeof*bodyque);
1095 queuesize = bodyquesize;
1096 }
1097 if (bodyqueslot >= bodyquesize)
1098 P_RemoveMobj(bodyque[bodyqueslot % bodyquesize]);
1099 bodyque[bodyqueslot++ % bodyquesize] = players[playernum].mo;
1100 }
1101 else
1102 if (!bodyquesize)
1103 P_RemoveMobj(players[playernum].mo);
1104
1105 // spawn a teleport fog
1106 ss = R_PointInSubsector (x,y);
1107 { // Teleport fog at respawn point
1108 fixed_t xa=0,ya=0;
1109 int an;
1110 mobj_t *mo;
1111
1112 /* BUG: an can end up negative, because mthing->angle is (signed) short.
1113 * We have to emulate original Doom's behaviour, deferencing past the start
1114 * of the array, into the previous array (finetangent) */
1115 an = ( ANG45 * ((signed)mthing->angle/45) ) >> ANGLETOFINESHIFT;
1116 switch (an) {
1117 case -4096: xa = finetangent[2048]; // finecosine[-4096]
1118 ya = finetangent[0]; // finesine[-4096]
1119 break;
1120 case -3072: xa = finetangent[3072]; // finecosine[-3072]
1121 ya = finetangent[1024]; // finesine[-3072]
1122 break;
1123 case -2048: xa = finesine[0]; // finecosine[-2048]
1124 ya = finetangent[2048]; // finesine[-2048]
1125 break;
1126 case -1024: xa = finesine[1024]; // finecosine[-1024]
1127 ya = finetangent[3072]; // finesine[-1024]
1128 break;
1129 case 1024:
1130 case 2048:
1131 case 3072:
1132 case 4096:
1133 case 0: xa = finecosine[an];
1134 ya = finesine[an];
1135 break;
1136 default: I_Error("G_CheckSpot: unexpected angle %d\n",an);
1137 }
1138
1139
1140 mo = P_SpawnMobj(x+20*xa, y+20*ya, ss->sector->floorheight, MT_TFOG);
1141
1142 if (players[consoleplayer].viewz != 1)
1143 S_StartSound(mo, sfx_telept); // don't start sound on first frame
1144 }
1145
1146 return true;
1147}
1148
1149
1150//
1151// G_DeathMatchSpawnPlayer
1152// Spawns a player at one of the random death match spots
1153// called at level load and each death
1154//
1155void G_DeathMatchSpawnPlayer (int playernum)
1156{
1157 int j, selections = deathmatch_p - deathmatchstarts;
1158
1159 if (selections < MAXPLAYERS)
1160 I_Error("G_DeathMatchSpawnPlayer: Only %i deathmatch spots, %d required",
1161 selections, MAXPLAYERS);
1162
1163 for (j=0 ; j<20 ; j++)
1164 {
1165 int i = P_Random(pr_dmspawn) % selections;
1166 if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
1167 {
1168 deathmatchstarts[i].type = playernum+1;
1169 P_SpawnPlayer (&deathmatchstarts[i]);
1170 return;
1171 }
1172 }
1173
1174 // no good spot, so the player will probably get stuck
1175 P_SpawnPlayer (&playerstarts[playernum]);
1176}
1177
1178//
1179// G_DoReborn
1180//
1181
1182void G_DoReborn (int playernum)
1183{
1184 if (!netgame)
1185 gameaction = ga_loadlevel; // reload the level from scratch
1186 else
1187 { // respawn at the start
1188 int i;
1189
1190 // first dissasociate the corpse
1191 players[playernum].mo->player = NULL;
1192
1193 // spawn at random spot if in death match
1194 if (deathmatch)
1195 {
1196 G_DeathMatchSpawnPlayer (playernum);
1197 return;
1198 }
1199
1200 if (G_CheckSpot (playernum, &playerstarts[playernum]) )
1201 {
1202 P_SpawnPlayer (&playerstarts[playernum]);
1203 return;
1204 }
1205
1206 // try to spawn at one of the other players spots
1207 for (i=0 ; i<MAXPLAYERS ; i++)
1208 {
1209 if (G_CheckSpot (playernum, &playerstarts[i]) )
1210 {
1211 playerstarts[i].type = playernum+1; // fake as other player
1212 P_SpawnPlayer (&playerstarts[i]);
1213 playerstarts[i].type = i+1; // restore
1214 return;
1215 }
1216 // he's going to be inside something. Too bad.
1217 }
1218 P_SpawnPlayer (&playerstarts[playernum]);
1219 }
1220}
1221
1222// DOOM Par Times
1223int pars[4][10] =
1224 {
1225 {0},
1226 {0,30,75,120,90,165,180,180,30,165},
1227 {0,90,90,90,120,90,360,240,30,170},
1228 {0,90,45,90,150,90,90,165,30,135}
1229 };
1230
1231// DOOM II Par Times
1232int cpars[32] =
1233 {
1234 30,90,120,120,90,150,120,120,270,90, // 1-10
1235 210,150,150,150,210,150,420,150,210,150, // 11-20
1236 240,150,180,150,150,300,330,420,300,180, // 21-30
1237 120,30 // 31-32
1238 };
1239
1240static boolean secretexit;
1241
1242void G_ExitLevel (void)
1243{
1244 secretexit = false;
1245 gameaction = ga_completed;
1246}
1247
1248// Here's for the german edition.
1249// IF NO WOLF3D LEVELS, NO SECRET EXIT!
1250
1251void G_SecretExitLevel (void)
1252{
1253 if (gamemode!=commercial || haswolflevels)
1254 secretexit = true;
1255 else
1256 secretexit = false;
1257 gameaction = ga_completed;
1258}
1259
1260//
1261// G_DoCompleted
1262//
1263
1264void G_DoCompleted (void)
1265{
1266 int i;
1267
1268 gameaction = ga_nothing;
1269
1270 for (i=0; i<MAXPLAYERS; i++)
1271 if (playeringame[i])
1272 G_PlayerFinishLevel(i); // take away cards and stuff
1273
1274 if (automapmode & am_active)
1275 AM_Stop();
1276
1277 if (gamemode != commercial) // kilough 2/7/98
1278 switch(gamemap)
1279 {
1280 // cph - Remove ExM8 special case, so it gets summary screen displayed
1281 case 9:
1282 for (i=0 ; i<MAXPLAYERS ; i++)
1283 players[i].didsecret = true;
1284 break;
1285 }
1286
1287 wminfo.didsecret = players[consoleplayer].didsecret;
1288 wminfo.epsd = gameepisode -1;
1289 wminfo.last = gamemap -1;
1290
1291 // wminfo.next is 0 biased, unlike gamemap
1292 if (gamemode == commercial)
1293 {
1294 if (secretexit)
1295 switch(gamemap)
1296 {
1297 case 15:
1298 wminfo.next = 30; break;
1299 case 31:
1300 wminfo.next = 31; break;
1301 }
1302 else
1303 switch(gamemap)
1304 {
1305 case 31:
1306 case 32:
1307 wminfo.next = 15; break;
1308 default:
1309 wminfo.next = gamemap;
1310 }
1311 }
1312 else
1313 {
1314 if (secretexit)
1315 wminfo.next = 8; // go to secret level
1316 else
1317 if (gamemap == 9)
1318 {
1319 // returning from secret level
1320 switch (gameepisode)
1321 {
1322 case 1:
1323 wminfo.next = 3;
1324 break;
1325 case 2:
1326 wminfo.next = 5;
1327 break;
1328 case 3:
1329 wminfo.next = 6;
1330 break;
1331 case 4:
1332 wminfo.next = 2;
1333 break;
1334 }
1335 }
1336 else
1337 wminfo.next = gamemap; // go to next level
1338 }
1339
1340 wminfo.maxkills = totalkills;
1341 wminfo.maxitems = totalitems;
1342 wminfo.maxsecret = totalsecret;
1343 wminfo.maxfrags = 0;
1344
1345 if ( gamemode == commercial )
1346 wminfo.partime = TICRATE*cpars[gamemap-1];
1347 else
1348 wminfo.partime = TICRATE*pars[gameepisode][gamemap];
1349
1350 wminfo.pnum = consoleplayer;
1351
1352 for (i=0 ; i<MAXPLAYERS ; i++)
1353 {
1354 wminfo.plyr[i].in = playeringame[i];
1355 wminfo.plyr[i].skills = players[i].killcount;
1356 wminfo.plyr[i].sitems = players[i].itemcount;
1357 wminfo.plyr[i].ssecret = players[i].secretcount;
1358 wminfo.plyr[i].stime = leveltime;
1359 memcpy (wminfo.plyr[i].frags, players[i].frags,
1360 sizeof(wminfo.plyr[i].frags));
1361 }
1362
1363 /* cph - modified so that only whole seconds are added to the totalleveltimes
1364 * value; so our total is compatible with the "naive" total of just adding
1365 * the times in seconds shown for each level. Also means our total time
1366 * will agree with Compet-n.
1367 */
1368 wminfo.totaltimes = (totalleveltimes += (leveltime - leveltime%35));
1369
1370 gamestate = GS_INTERMISSION;
1371 automapmode &= ~am_active;
1372
1373 if (statcopy)
1374 memcpy (statcopy, &wminfo, sizeof(wminfo));
1375
1376 WI_Start (&wminfo);
1377}
1378
1379
1380//
1381// G_WorldDone
1382//
1383
1384void G_WorldDone (void)
1385{
1386 gameaction = ga_worlddone;
1387
1388 if (secretexit)
1389 players[consoleplayer].didsecret = true;
1390
1391 if (gamemode == commercial)
1392 {
1393 switch (gamemap)
1394 {
1395 case 15:
1396 case 31:
1397 if (!secretexit)
1398 break;
1399 case 6:
1400 case 11:
1401 case 20:
1402 case 30:
1403 F_StartFinale ();
1404 break;
1405 }
1406 }
1407 else if (gamemap == 8)
1408 gameaction = ga_victory; // cph - after ExM8 summary screen, show victory stuff
1409}
1410
1411void G_DoWorldDone (void)
1412{
1413 idmusnum = -1; //jff 3/17/98 allow new level's music to be loaded
1414 gamestate = GS_LEVEL;
1415 gamemap = wminfo.next+1;
1416 G_DoLoadLevel();
1417 gameaction = ga_nothing;
1418 AM_clearMarks(); //jff 4/12/98 clear any marks on the automap
1419}
1420
1421// killough 2/28/98: A ridiculously large number
1422// of players, the most you'll ever need in a demo
1423// or savegame. This is used to prevent problems, in
1424// case more players in a game are supported later.
1425
1426#define MIN_MAXPLAYERS 32
1427
1428extern boolean setsizeneeded;
1429void R_ExecuteSetViewSize(void);
1430
1431//CPhipps - savename variable redundant
1432
1433/* killough 12/98:
1434 * This function returns a signature for the current wad.
1435 * It is used to distinguish between wads, for the purposes
1436 * of savegame compatibility warnings, and options lookups.
1437 */
1438
1439static uint_64_t G_UpdateSignature(uint_64_t s, const char *name)
1440{
1441 int i, lump = W_CheckNumForName(name);
1442 if (lump != -1 && (i = lump+10) < numlumps)
1443 do
1444 {
1445 int size = W_LumpLength(i);
1446 const byte *p = W_CacheLumpNum(i);
1447 while (size--)
1448 s <<= 1, s += *p++;
1449 W_UnlockLumpNum(i);
1450 }
1451 while (--i > lump);
1452 return s;
1453}
1454
1455static uint_64_t G_Signature(void)
1456{
1457 static uint_64_t s = 0;
1458 static boolean computed = false;
1459 char name[9];
1460 int episode, map;
1461
1462 if (!computed) {
1463 computed = true;
1464 if (gamemode == commercial)
1465 for (map = haswolflevels ? 32 : 30; map; map--)
1466 snprintf(name, sizeof(name), "map%02d", map), s = G_UpdateSignature(s, name);
1467 else
1468 for (episode = gamemode==retail ? 4 :
1469 gamemode==shareware ? 1 : 3; episode; episode--)
1470 for (map = 9; map; map--)
1471 snprintf(name, sizeof(name), "E%dM%d", episode, map), s = G_UpdateSignature(s, name);
1472 }
1473 return s;
1474}
1475
1476//
1477// killough 5/15/98: add forced loadgames, which allow user to override checks
1478//
1479
1480void G_ForcedLoadGame(void)
1481{
1482 // CPhipps - net loadgames are always forced, so we only reach here
1483 // in single player
1484 gameaction = ga_loadgame;
1485 forced_loadgame = true;
1486}
1487
1488// killough 3/16/98: add slot info
1489// killough 5/15/98: add command-line
1490void G_LoadGame(int slot, boolean command)
1491{
1492 if (!demoplayback && !command) {
1493 // CPhipps - handle savegame filename in G_DoLoadGame
1494 // - Delay load so it can be communicated in net game
1495 // - store info in special_event
1496 special_event = BT_SPECIAL | (BTS_LOADGAME & BT_SPECIALMASK) |
1497 ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK);
1498 forced_loadgame = netgame; // CPhipps - always force load netgames
1499 } else {
1500 // Do the old thing, immediate load
1501 gameaction = ga_loadgame;
1502 forced_loadgame = false;
1503 savegameslot = slot;
1504 demoplayback = false;
1505 }
1506 command_loadgame = command;
1507}
1508
1509// killough 5/15/98:
1510// Consistency Error when attempting to load savegame.
1511
1512static void G_LoadGameErr(const char *msg)
1513{
1514 (void) msg; // Need to fix, but right now we're ignoring a forced load
1515 Z_Free(savebuffer); // Free the savegame buffer
1516// M_ForcedLoadGame(msg); // Print message asking for 'Y' to force
1517 if (command_loadgame) // If this was a command-line -loadgame
1518 {
1519 D_StartTitle(); // Start the title screen
1520 gamestate = GS_DEMOSCREEN; // And set the game state accordingly
1521 }
1522}
1523
1524// CPhipps - size of version header
1525#define VERSIONSIZE 16
1526
1527const char * comp_lev_str[MAX_COMPATIBILITY_LEVEL] =
1528 { "doom v1.2", "demo", "doom", "\"boom compatibility\"", "boom v2.01", "boom v2.02", "lxdoom v1.3.2+",
1529 "MBF", "PrBoom 2.03beta", "PrBoom v2.1.0-2.1.1",
1530 "Current PrBoom" };
1531
1532static const struct {
1533 unsigned int comp_level;
1534 const char* ver_printf;
1535 int version;
1536} version_headers[] = {
1537 { prboom_1_compatibility, "PrBoom %d", 260},
1538 /* cph - we don't need a new version_header for prboom_3_comp/v2.1.1, since
1539 * the file format is unchanged. */
1540 { prboom_3_compatibility, "PrBoom %d", 210}
1541 };
1542
1543static const size_t num_version_headers = sizeof(version_headers) / sizeof(version_headers[0]);
1544
1545void G_DoLoadGame(void)
1546{
1547 int length, i;
1548 // CPhipps - do savegame filename stuff here
1549 char name[100+1]; // killough 3/22/98
1550 int savegame_compatibility = -1;
1551
1552 G_SaveGameName(name,sizeof(name),savegameslot, demoplayback);
1553
1554 gameaction = ga_nothing;
1555
1556 length = M_ReadFile(name, &savebuffer);
1557 save_p = savebuffer + SAVESTRINGSIZE;
1558
1559 // CPhipps - read the description field, compare with supported ones
1560 for (i=0; (size_t)i<num_version_headers; i++) {
1561 char vcheck[VERSIONSIZE];
1562 // killough 2/22/98: "proprietary" version string :-)
1563 snprintf (vcheck,sizeof(vcheck), version_headers[i].ver_printf, version_headers[i].version);
1564
1565 if (!strncmp(save_p, vcheck, VERSIONSIZE)) {
1566 savegame_compatibility = version_headers[i].comp_level;
1567 i = num_version_headers;
1568 }
1569 }
1570 if (savegame_compatibility == -1) {
1571 if (forced_loadgame) {
1572 savegame_compatibility = MAX_COMPATIBILITY_LEVEL-1;
1573 } else {
1574 G_LoadGameErr("Unrecognised savegame version!\nAre you sure? (y/n) ");
1575 return;
1576 }
1577 }
1578
1579 save_p += VERSIONSIZE;
1580
1581 // CPhipps - always check savegames even when forced,
1582 // only print a warning if forced
1583 { // killough 3/16/98: check lump name checksum (independent of order)
1584 uint_64_t checksum = 0;
1585
1586 checksum = G_Signature();
1587
1588 if (memcmp(&checksum, save_p, sizeof checksum)) {
1589 if (!forced_loadgame) {
1590 char *msg = malloc(strlen(save_p + sizeof checksum) + 128);
1591 strcpy(msg,"Incompatible Savegame!!!\n");
1592 if (save_p[sizeof checksum])
1593 strcat(strcat(msg,"Wads expected:\n\n"), save_p + sizeof checksum);
1594 strcat(msg, "\nAre you sure?");
1595 G_LoadGameErr(msg);
1596 free(msg);
1597 return;
1598 } else
1599 printf("G_DoLoadGame: Incompatible savegame\n");
1600 }
1601 save_p += sizeof checksum;
1602 }
1603
1604 save_p += strlen(save_p)+1;
1605
1606 /* cph - FIXME - compatibility flag? */
1607 compatibility_level = savegame_compatibility;
1608 save_p++;
1609
1610 gameskill = *save_p++;
1611 gameepisode = *save_p++;
1612 gamemap = *save_p++;
1613
1614 for (i=0 ; i<MAXPLAYERS ; i++)
1615 playeringame[i] = *save_p++;
1616 save_p += MIN_MAXPLAYERS-MAXPLAYERS; // killough 2/28/98
1617
1618 idmusnum = *save_p++; // jff 3/17/98 restore idmus music
1619 if (idmusnum==255) idmusnum=-1; // jff 3/18/98 account for unsigned byte
1620
1621 /* killough 3/1/98: Read game options
1622 * killough 11/98: move down to here
1623 */
1624 save_p = (char*)G_ReadOptions(save_p);
1625
1626 // load a base level
1627 G_InitNew (gameskill, gameepisode, gamemap);
1628
1629 /* get the times - killough 11/98: save entire word */
1630 memcpy(&leveltime, save_p, sizeof leveltime);
1631 save_p += sizeof leveltime;
1632
1633 /* cph - total episode time */
1634 if (compatibility_level >= prboom_2_compatibility) {
1635 memcpy(&totalleveltimes, save_p, sizeof totalleveltimes);
1636 save_p += sizeof totalleveltimes;
1637 }
1638 else totalleveltimes = 0;
1639
1640 // killough 11/98: load revenant tracer state
1641 basetic = gametic - *save_p++;
1642
1643 // dearchive all the modifications
1644 P_UnArchivePlayers ();
1645 P_UnArchiveWorld ();
1646 P_UnArchiveThinkers ();
1647 P_UnArchiveSpecials ();
1648 P_UnArchiveRNG (); // killough 1/18/98: load RNG information
1649 P_UnArchiveMap (); // killough 1/22/98: load automap information
1650
1651 if (*save_p != 0xe6)
1652 I_Error ("G_DoLoadGame: Bad savegame");
1653
1654 // done
1655 Z_Free (savebuffer);
1656
1657 if (setsizeneeded)
1658 R_ExecuteSetViewSize ();
1659
1660 // draw the pattern into the back screen
1661 R_FillBackScreen ();
1662
1663 /* killough 12/98: support -recordfrom and -loadgame -playdemo */
1664 if (!command_loadgame)
1665 singledemo = false; /* Clear singledemo flag if loading from menu */
1666 else
1667 if (singledemo) {
1668 gameaction = ga_loadgame; /* Mark that we're loading a game before demo */
1669 G_DoPlayDemo(); /* This will detect it and won't reinit level */
1670 } else /* Command line + record means it's a recordfrom */
1671 if (demorecording)
1672 G_BeginRecording();
1673}
1674
1675//
1676// G_SaveGame
1677// Called by the menu task.
1678// Description is a 24 byte text string
1679//
1680
1681void G_SaveGame(int slot, char *description)
1682{
1683 strcpy(savedescription, description);
1684 if (demoplayback) {
1685 /* cph - We're doing a user-initiated save game while a demo is
1686 * running so, go outside normal mechanisms
1687 */
1688 savegameslot = slot;
1689 G_DoSaveGame(true);
1690 }
1691 // CPhipps - store info in special_event
1692 special_event = BT_SPECIAL | (BTS_SAVEGAME & BT_SPECIALMASK) |
1693 ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK);
1694#ifdef HAVE_NET
1695 D_NetSendMisc(nm_savegamename, strlen(savedescription)+1, savedescription);
1696#endif
1697}
1698
1699// Check for overrun and realloc if necessary -- Lee Killough 1/22/98
1700void CheckSaveGame(size_t size)
1701{
1702 size_t pos = save_p - savebuffer;
1703 size += 1024; // breathing room
1704 if (pos+size > savegamesize)
1705 save_p = (savebuffer = realloc(savebuffer,
1706 savegamesize += (size+1023) & ~1023)) + pos;
1707}
1708
1709/* killough 3/22/98: form savegame name in one location
1710 * (previously code was scattered around in multiple places)
1711 * cph - Avoid possible buffer overflow problems by passing
1712 * size to this function and using snprintf */
1713
1714void G_SaveGameName(char *name, size_t size, int slot, boolean demoplayback)
1715{
1716 const char* sgn = demoplayback ? "demosav" : SAVEGAMENAME;
1717 snprintf (name, size, "%s%d.dsg", sgn, slot);
1718}
1719
1720static void G_DoSaveGame (boolean menu)
1721{
1722 char name[100+1];
1723 char name2[VERSIONSIZE];
1724 char *description;
1725 int length, i;
1726
1727 gameaction = ga_nothing; // cph - cancel savegame at top of this function,
1728 // in case later problems cause a premature exit
1729
1730 G_SaveGameName(name,sizeof(name),savegameslot, demoplayback && !menu);
1731
1732 description = savedescription;
1733
1734 save_p = savebuffer = malloc(savegamesize);
1735
1736 CheckSaveGame(SAVESTRINGSIZE+VERSIONSIZE+sizeof(unsigned long));
1737 memcpy (save_p, description, SAVESTRINGSIZE);
1738 save_p += SAVESTRINGSIZE;
1739 memset (name2,0,sizeof(name2));
1740
1741 // CPhipps - scan for the version header
1742 for (i=0; (size_t)i<num_version_headers; i++)
1743 if (version_headers[i].comp_level == compatibility_level) {
1744 // killough 2/22/98: "proprietary" version string :-)
1745 snprintf (name2,sizeof(name2),version_headers[i].ver_printf,version_headers[i].version);
1746 memcpy (save_p, name2, VERSIONSIZE);
1747 i = num_version_headers+1;
1748 }
1749
1750 if ((size_t)i == num_version_headers) {
1751 doom_printf("No savegame signature known for\nthis compatibility level\n"
1752 "%d/%d, %d registered", compatibility_level,
1753 MAX_COMPATIBILITY_LEVEL, (signed)num_version_headers);
1754 free(savebuffer); // cph - free data
1755 return;
1756 }
1757
1758 save_p += VERSIONSIZE;
1759
1760 { /* killough 3/16/98, 12/98: store lump name checksum */
1761 uint_64_t checksum = G_Signature();
1762 memcpy(save_p, &checksum, sizeof checksum);
1763 save_p += sizeof checksum;
1764 }
1765
1766 // killough 3/16/98: store pwad filenames in savegame
1767 {
1768 // CPhipps - changed for new wadfiles handling
1769 int i = 0;
1770 for (*save_p = 0; (size_t)i<numwadfiles; i++)
1771 {
1772 const char *const w = wadfiles[i].name;
1773 CheckSaveGame(strlen(w)+2);
1774 strcat(strcat(save_p, w), "\n");
1775 }
1776 save_p += strlen(save_p)+1;
1777 }
1778
1779 CheckSaveGame(GAME_OPTION_SIZE+MIN_MAXPLAYERS+10);
1780
1781 /* cph - FIXME? - Save compatibility level */
1782 *save_p++ = 0;
1783
1784 *save_p++ = gameskill;
1785 *save_p++ = gameepisode;
1786 *save_p++ = gamemap;
1787
1788 for (i=0 ; i<MAXPLAYERS ; i++)
1789 *save_p++ = playeringame[i];
1790
1791 for (;i<MIN_MAXPLAYERS;i++) // killough 2/28/98
1792 *save_p++ = 0;
1793
1794 *save_p++ = idmusnum; // jff 3/17/98 save idmus state
1795
1796 save_p = G_WriteOptions(save_p); // killough 3/1/98: save game options
1797
1798 /* cph - FIXME - endianness? */
1799 /* killough 11/98: save entire word */
1800 memcpy(save_p, &leveltime, sizeof leveltime);
1801 save_p += sizeof leveltime;
1802
1803 /* cph - total episode time */
1804 if (compatibility_level >= prboom_2_compatibility) {
1805 memcpy(save_p, &totalleveltimes, sizeof totalleveltimes);
1806 save_p += sizeof totalleveltimes;
1807 }
1808 else totalleveltimes = 0;
1809
1810 // killough 11/98: save revenant tracer state
1811 *save_p++ = (gametic-basetic) & 255;
1812
1813 // killough 3/22/98: add Z_CheckHeap after each call to ensure consistency
1814 Z_CheckHeap();
1815 P_ArchivePlayers();
1816 Z_CheckHeap();
1817
1818 // phares 9/13/98: Move mobj_t->index out of P_ArchiveThinkers so the
1819 // indices can be used by P_ArchiveWorld when the sectors are saved.
1820 // This is so we can save the index of the mobj_t of the thinker that
1821 // caused a sound, referenced by sector_t->soundtarget.
1822 P_ThinkerToIndex();
1823
1824 P_ArchiveWorld();
1825 Z_CheckHeap();
1826 P_ArchiveThinkers();
1827
1828 // phares 9/13/98: Move index->mobj_t out of P_ArchiveThinkers, simply
1829 // for symmetry with the P_ThinkerToIndex call above.
1830
1831 P_IndexToThinker();
1832
1833 Z_CheckHeap();
1834 P_ArchiveSpecials();
1835 P_ArchiveRNG(); // killough 1/18/98: save RNG information
1836 Z_CheckHeap();
1837 P_ArchiveMap(); // killough 1/22/98: save automap information
1838
1839 *save_p++ = 0xe6; // consistancy marker
1840
1841 length = save_p - savebuffer;
1842
1843 Z_CheckHeap();
1844 doom_printf( "%s", M_WriteFile(name, savebuffer, length)
1845 ? GGSAVED /* Ty - externalised */
1846 : "Game save failed!"); // CPhipps - not externalised
1847
1848 free(savebuffer); // killough
1849 savebuffer = save_p = NULL;
1850
1851 savedescription[0] = 0;
1852}
1853
1854static skill_t d_skill;
1855static int d_episode;
1856static int d_map;
1857
1858void G_DeferedInitNew(skill_t skill, int episode, int map)
1859{
1860 d_skill = skill;
1861 d_episode = episode;
1862 d_map = map;
1863 gameaction = ga_newgame;
1864}
1865
1866extern int variable_friction;
1867extern int default_variable_friction; // ice & mud
1868
1869extern int weapon_recoil, default_weapon_recoil; // weapon recoil
1870
1871extern int allow_pushers;
1872extern int default_allow_pushers; // MT_PUSH Things
1873
1874extern int player_bobbing;
1875extern int default_player_bobbing; // whether player bobs or not
1876
1877extern int monsters_remember, default_monsters_remember;
1878
1879/* cph -
1880 * G_Compatibility
1881 *
1882 * Initialises the comp[] array based on the compatibility_level
1883 * For reference, MBF did:
1884 * for (i=0; i < COMP_TOTAL; i++)
1885 * comp[i] = compatibility;
1886 *
1887 * Instead, we have a lookup table showing at what version a fix was
1888 * introduced.
1889 */
1890
1891void G_Compatibility(void)
1892{
1893 static const complevel_t fix_levels[COMP_NUM] = {
1894 mbf_compatibility, /* comp_telefrag - monsters used to telefrag only
1895 * on MAP30, now they do it for spawners only */
1896 mbf_compatibility, /* comp_dropoff - MBF encourages things to drop
1897 * off of overhangs */
1898 boom_compatibility,/* comp_vile - original Doom archville bugs like
1899 * ghosts */
1900 boom_compatibility,/* comp_pain - original Doom limits Pain Elements
1901 * from spawning too many skulls */
1902 boom_compatibility,/* comp_skull - original Doom let skulls be spit
1903 * through walls by Pain Elementals */
1904 boom_compatibility,/* comp_blazing - original Doom duplicated
1905 * blazing door sound */
1906 mbf_compatibility, /* comp_doorlight - MBF made door lighting changes
1907 * more gradual */
1908 boom_compatibility,/* comp_model - improvements to the game physics */
1909 boom_compatibility,/* comp_god - fixes to God mode */
1910 mbf_compatibility, /* comp_falloff - MBF encourages things to drop
1911 * off of overhangs */
1912 boom_compatibility_compatibility,
1913 /* comp_floors - fixes for moving floors bugs */
1914 boom_compatibility,/* comp_skymap */
1915 mbf_compatibility, /* comp_pursuit - MBF AI change, limited pursuit? */
1916 boom_compatibility,/* comp_doorstuck - monsters stuck in doors fix */
1917 mbf_compatibility, /* comp_staylift - MBF AI change, monsters try
1918 * to stay on lifts */
1919 lxdoom_1_compatibility, /* comp_zombie - prevent dead players
1920 * triggering stuff */
1921 boom_compatibility_compatibility, /* comp_stairs - see p_floor.c */
1922 mbf_compatibility, /* comp_infcheat - FIXME */
1923 boom_compatibility,/* comp_zerotags - allow zero tags in wads */
1924 lxdoom_1_compatibility, /* comp_moveblock - enables keygrab and
1925 * mancubi shots going thru walls */
1926 prboom_2_compatibility, /* comp_respawn - objects which aren't on the map
1927 * at game start respawn at (0,0) */
1928 boom_compatibility_compatibility, /* comp_sound - see s_sound.c */
1929 };
1930 int i;
1931 for (i=0; i<COMP_NUM; i++)
1932 comp[i] = compatibility_level < fix_levels[i];
1933 for (; i<COMP_TOTAL; i++) comp[i] = 1;
1934}
1935
1936#ifdef DOGS
1937/* killough 7/19/98: Marine's best friend :) */
1938static int G_GetHelpers(void)
1939{
1940 int j = M_CheckParm ("-dog");
1941
1942 if (!j)
1943 j = M_CheckParm ("-dogs");
1944return j ? j+1 < myargc ? atoi(myargv[j+1]) : 1 : default_dogs;
1945}
1946#endif
1947
1948
1949// killough 3/1/98: function to reload all the default parameter
1950// settings before a new game begins
1951
1952void G_ReloadDefaults(void)
1953{
1954 // killough 3/1/98: Initialize options based on config file
1955 // (allows functions above to load different values for demos
1956 // and savegames without messing up defaults).
1957
1958 weapon_recoil = default_weapon_recoil; // weapon recoil
1959
1960 player_bobbing = default_player_bobbing; // whether player bobs or not
1961
1962 variable_friction = allow_pushers = true;
1963
1964 monsters_remember = default_monsters_remember; // remember former enemies
1965
1966 monster_infighting = default_monster_infighting; // killough 7/19/98
1967
1968#ifdef DOGS
1969 dogs = netgame ? 0 : G_GetHelpers(); // killough 7/19/98
1970 dog_jumping = default_dog_jumping;
1971#endif
1972
1973 distfriend = default_distfriend; // killough 8/8/98
1974
1975 monster_backing = default_monster_backing; // killough 9/8/98
1976
1977 monster_avoid_hazards = default_monster_avoid_hazards; // killough 9/9/98
1978
1979 monster_friction = default_monster_friction; // killough 10/98
1980
1981 help_friends = default_help_friends; // killough 9/9/98
1982
1983 monkeys = default_monkeys;
1984
1985 // jff 1/24/98 reset play mode to command line spec'd version
1986 // killough 3/1/98: moved to here
1987// respawnparm = clrespawnparm;
1988// fastparm = clfastparm;
1989// nomonsters = clnomonsters;
1990
1991 //jff 3/24/98 set startskill from defaultskill in config file, unless
1992 // it has already been set by a -skill parameter
1993 if (startskill==sk_none)
1994 startskill = (skill_t)(defaultskill-1);
1995
1996 demoplayback = false;
1997 singledemo = false; // killough 9/29/98: don't stop after 1 demo
1998 netdemo = false;
1999
2000 // killough 2/21/98:
2001 memset(playeringame+1, 0, sizeof(*playeringame)*(MAXPLAYERS-1));
2002
2003 consoleplayer = 0;
2004
2005 compatibility_level = default_compatibility_level;
2006 {
2007 int i = M_CheckParm("-complevel");
2008 if (i && (1+i) < myargc) compatibility_level = atoi(myargv[i+1]);
2009 }
2010 if ((signed)compatibility_level == -1)
2011 compatibility_level = MAX_COMPATIBILITY_LEVEL-1;
2012
2013 if (mbf_features)
2014 memcpy(comp, default_comp, sizeof comp);
2015 else
2016 G_Compatibility();
2017
2018 // killough 3/31/98, 4/5/98: demo sync insurance
2019 demo_insurance = default_demo_insurance == 1;
2020
2021 rngseed += 1 + gametic; // CPhipps
2022// rngseed += I_GetRandomTimeSeed() + gametic; // CPhipps
2023}
2024
2025void G_DoNewGame (void)
2026{
2027 G_ReloadDefaults(); // killough 3/1/98
2028 netgame = false; // killough 3/1/98
2029 deathmatch = false;
2030 G_InitNew (d_skill, d_episode, d_map);
2031 gameaction = ga_nothing;
2032
2033 //jff 4/26/98 wake up the status bar in case were coming out of a DM demo
2034 ST_Start();
2035}
2036
2037// killough 4/10/98: New function to fix bug which caused Doom
2038// lockups when idclev was used in conjunction with -fast.
2039
2040void G_SetFastParms(int fast_pending)
2041{
2042 static int fast = 0; // remembers fast state
2043 int i;
2044 if (fast != fast_pending) { /* only change if necessary */
2045 if ((fast = fast_pending))
2046 {
2047 for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++)
2048 if (states[i].tics != 1 || demo_compatibility) // killough 4/10/98
2049 states[i].tics >>= 1; // don't change 1->0 since it causes cycles
2050 mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT;
2051 mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT;
2052 mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT;
2053 }
2054 else
2055 {
2056 for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++)
2057 states[i].tics <<= 1;
2058 mobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT;
2059 mobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT;
2060 mobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT;
2061 }
2062 }
2063}
2064
2065// The sky texture to be used instead of the F_SKY1 dummy.
2066extern int skytexture;
2067
2068//
2069// G_InitNew
2070// Can be called by the startup code or the menu task,
2071// consoleplayer, displayplayer, playeringame[] should be set.
2072//
2073
2074void G_InitNew(skill_t skill, int episode, int map)
2075{
2076 int i;
2077
2078 if (paused)
2079 {
2080 paused = false;
2081 S_ResumeSound();
2082 }
2083
2084 if (skill > sk_nightmare)
2085 skill = sk_nightmare;
2086
2087 if (episode < 1)
2088 episode = 1;
2089
2090 if (gamemode == retail)
2091 {
2092 if (episode > 4)
2093 episode = 4;
2094 }
2095 else
2096 if (gamemode == shareware)
2097 {
2098 if (episode > 1)
2099 episode = 1; // only start episode 1 on shareware
2100 }
2101 else
2102 if (episode > 3)
2103 episode = 3;
2104
2105 if (map < 1)
2106 map = 1;
2107 if (map > 9 && gamemode != commercial)
2108 map = 9;
2109
2110 G_SetFastParms(fastparm || skill == sk_nightmare); // killough 4/10/98
2111
2112 M_ClearRandom();
2113
2114 respawnmonsters = skill == sk_nightmare || respawnparm;
2115
2116 // force players to be initialized upon first level load
2117 for (i=0 ; i<MAXPLAYERS ; i++)
2118 players[i].playerstate = PST_REBORN;
2119
2120 usergame = true; // will be set false if a demo
2121 paused = false;
2122 automapmode &= ~am_active;
2123 gameepisode = episode;
2124 gamemap = map;
2125 gameskill = skill;
2126
2127 totalleveltimes = 0; // cph
2128
2129 //jff 4/16/98 force marks on automap cleared every new level start
2130 AM_clearMarks();
2131
2132 G_DoLoadLevel ();
2133}
2134
2135
2136//
2137// DEMO RECORDING
2138//
2139
2140unsigned char DEMOMARKER=0x80;
2141
2142void G_ReadDemoTiccmd (ticcmd_t* cmd)
2143{
2144 if (*demo_p == DEMOMARKER)
2145 G_CheckDemoStatus(); // end of demo data stream
2146 else
2147 {
2148 cmd->forwardmove = ((signed char)*demo_p++);
2149 cmd->sidemove = ((signed char)*demo_p++);
2150 if (!longtics) {
2151 cmd->angleturn = ((unsigned char)*demo_p++)<<8;
2152 } else {
2153 unsigned int lowbyte = (unsigned char)*demo_p++;
2154 cmd->angleturn = (((signed int)(*demo_p++))<<8) + lowbyte;
2155 }
2156 cmd->buttons = (unsigned char)*demo_p++;
2157 }
2158}
2159
2160/* Demo limits removed -- killough
2161 * cph - record straight to file
2162 */
2163void G_WriteDemoTiccmd (ticcmd_t* cmd)
2164{
2165 char buf[5];
2166 char *p = buf;
2167
2168 *p++ = cmd->forwardmove;
2169 *p++ = cmd->sidemove;
2170 if (!longtics) {
2171 *p++ = (cmd->angleturn+128)>>8;
2172 } else {
2173 signed short a = cmd->angleturn;
2174 *p++ = a & 0xff;
2175 *p++ = (a >> 8) & 0xff;
2176 }
2177 *p++ = cmd->buttons;
2178 if (write(demofd, buf, p-buf) != p-buf)
2179 I_Error("G_WriteDemoTiccmd: error writing demo");
2180
2181 /* cph - alias demo_p to it so we can read it back */
2182 demo_p = buf;
2183 G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same
2184}
2185
2186//
2187// G_RecordDemo
2188//
2189
2190void G_RecordDemo (const char* name)
2191{
2192 char demoname[100];
2193 usergame = false;
2194 AddDefaultExtension(strcpy(demoname, name), ".lmp"); // 1/18/98 killough
2195 demorecording = true;
2196 /* cph - Record demos straight to file
2197 * If file already exists, try to continue existing demo
2198 */
2199 if (fileexists(demoname)) {
2200 demofd = open(demoname, O_WRONLY | O_APPEND);
2201 } else {
2202 demofd = open(demoname, O_WRONLY | O_RDONLY);
2203 if (demofd) {
2204 int slot = -1;
2205 int rc;
2206 {
2207 byte buf[200];
2208 size_t len;
2209 read(demofd, buf, sizeof(buf));
2210
2211 len = G_ReadDemoHeader(buf) - buf;
2212 lseek(demofd, len, SEEK_SET);
2213 }
2214 do {
2215 byte buf[4];
2216 rc = read(demofd, buf, sizeof(buf));
2217 if (buf[0] == DEMOMARKER) break;
2218 if (buf[3] & BT_SPECIAL)
2219 if ((buf[3] & BT_SPECIALMASK) == BTS_SAVEGAME)
2220 slot = (buf[3] & BTS_SAVEMASK)>>BTS_SAVESHIFT;
2221 } while (rc == /* sizeof(buf) is out of scope here */ 4 );
2222 if (slot == -1) I_Error("G_RecordDemo: No save in demo, can't continue");
2223 lseek(demofd, -rc, SEEK_CUR);
2224 G_LoadGame(slot, false);
2225 autostart = false;
2226 }
2227 }
2228 if (!demofd) I_Error("G_RecordDemo: failed to open %s", name);
2229}
2230
2231// These functions are used to read and write game-specific options in demos
2232// and savegames so that demo sync is preserved and savegame restoration is
2233// complete. Not all options (for example "compatibility"), however, should
2234// be loaded and saved here. It is extremely important to use the same
2235// positions as before for the variables, so if one becomes obsolete, the
2236// byte(s) should still be skipped over or padded with 0's.
2237// Lee Killough 3/1/98
2238
2239extern int forceOldBsp;
2240
2241byte *G_WriteOptions(byte *demo_p)
2242{
2243 byte *target = demo_p + GAME_OPTION_SIZE;
2244
2245 *demo_p++ = monsters_remember; // part of monster AI
2246
2247 *demo_p++ = variable_friction; // ice & mud
2248
2249 *demo_p++ = weapon_recoil; // weapon recoil
2250
2251 *demo_p++ = allow_pushers; // MT_PUSH Things
2252
2253 *demo_p++ = 0;
2254
2255 *demo_p++ = player_bobbing; // whether player bobs or not
2256
2257 // killough 3/6/98: add parameters to savegame, move around some in demos
2258 *demo_p++ = respawnparm;
2259 *demo_p++ = fastparm;
2260 *demo_p++ = nomonsters;
2261
2262 *demo_p++ = demo_insurance; // killough 3/31/98
2263
2264 // killough 3/26/98: Added rngseed. 3/31/98: moved here
2265 *demo_p++ = (byte)((rngseed >> 24) & 0xff);
2266 *demo_p++ = (byte)((rngseed >> 16) & 0xff);
2267 *demo_p++ = (byte)((rngseed >> 8) & 0xff);
2268 *demo_p++ = (byte)( rngseed & 0xff);
2269
2270 // Options new to v2.03 begin here
2271
2272 *demo_p++ = monster_infighting; // killough 7/19/98
2273
2274#ifdef DOGS
2275 *demo_p++ = dogs; // killough 7/19/98
2276#else
2277 *demo_p++ = 0;
2278#endif
2279
2280 *demo_p++ = 0;
2281 *demo_p++ = 0;
2282
2283 *demo_p++ = (distfriend >> 8) & 0xff; // killough 8/8/98
2284 *demo_p++ = distfriend & 0xff; // killough 8/8/98
2285
2286 *demo_p++ = monster_backing; // killough 9/8/98
2287
2288 *demo_p++ = monster_avoid_hazards; // killough 9/9/98
2289
2290 *demo_p++ = monster_friction; // killough 10/98
2291
2292 *demo_p++ = help_friends; // killough 9/9/98
2293
2294#ifdef DOGS
2295 *demo_p++ = dog_jumping;
2296#else
2297 *demo_p++ = 0;
2298#endif
2299
2300 *demo_p++ = monkeys;
2301
2302 { // killough 10/98: a compatibility vector now
2303 int i;
2304 for (i=0; i < COMP_TOTAL; i++)
2305 *demo_p++ = comp[i] != 0;
2306 }
2307
2308 *demo_p++ = (compatibility_level >= prboom_2_compatibility) && forceOldBsp; // cph 2002/07/20
2309
2310 //----------------
2311 // Padding at end
2312 //----------------
2313 while (demo_p < target)
2314 *demo_p++ = 0;
2315
2316 if (demo_p != target)
2317 I_Error("G_WriteOptions: GAME_OPTION_SIZE is too small");
2318
2319 return target;
2320}
2321
2322/* Same, but read instead of write
2323 * cph - const byte*'s
2324 */
2325
2326const byte *G_ReadOptions(const byte *demo_p)
2327{
2328 const byte *target = demo_p + GAME_OPTION_SIZE;
2329
2330 monsters_remember = *demo_p++;
2331
2332 variable_friction = *demo_p; // ice & mud
2333 demo_p++;
2334
2335 weapon_recoil = *demo_p; // weapon recoil
2336 demo_p++;
2337
2338 allow_pushers = *demo_p; // MT_PUSH Things
2339 demo_p++;
2340
2341 demo_p++;
2342
2343 player_bobbing = *demo_p; // whether player bobs or not
2344 demo_p++;
2345
2346 // killough 3/6/98: add parameters to savegame, move from demo
2347 respawnparm = *demo_p++;
2348 fastparm = *demo_p++;
2349 nomonsters = *demo_p++;
2350
2351 demo_insurance = *demo_p++; // killough 3/31/98
2352
2353 // killough 3/26/98: Added rngseed to demos; 3/31/98: moved here
2354
2355 rngseed = *demo_p++ & 0xff;
2356 rngseed <<= 8;
2357 rngseed += *demo_p++ & 0xff;
2358 rngseed <<= 8;
2359 rngseed += *demo_p++ & 0xff;
2360 rngseed <<= 8;
2361 rngseed += *demo_p++ & 0xff;
2362
2363 // Options new to v2.03
2364 if (mbf_features)
2365 {
2366 monster_infighting = *demo_p++; // killough 7/19/98
2367
2368#ifdef DOGS
2369 dogs = *demo_p++; // killough 7/19/98
2370#else
2371 demo_p++;
2372#endif
2373
2374 demo_p += 2;
2375
2376 distfriend = *demo_p++ << 8; // killough 8/8/98
2377 distfriend+= *demo_p++;
2378
2379 monster_backing = *demo_p++; // killough 9/8/98
2380
2381 monster_avoid_hazards = *demo_p++; // killough 9/9/98
2382
2383 monster_friction = *demo_p++; // killough 10/98
2384
2385 help_friends = *demo_p++; // killough 9/9/98
2386
2387#ifdef DOGS
2388 dog_jumping = *demo_p++; // killough 10/98
2389#else
2390 demo_p++;
2391#endif
2392
2393 monkeys = *demo_p++;
2394
2395 { // killough 10/98: a compatibility vector now
2396 int i;
2397 for (i=0; i < COMP_TOTAL; i++)
2398 comp[i] = *demo_p++;
2399 }
2400
2401 forceOldBsp = *demo_p++; // cph 2002/07/20
2402 }
2403 else /* defaults for versions <= 2.02 */
2404 {
2405 /* cph - comp[] has already been set up right by G_Compatibility */
2406
2407 monster_infighting = 1; // killough 7/19/98
2408
2409 monster_backing = 0; // killough 9/8/98
2410
2411 monster_avoid_hazards = 0; // killough 9/9/98
2412
2413 monster_friction = 0; // killough 10/98
2414
2415 help_friends = 0; // killough 9/9/98
2416
2417#ifdef DOGS
2418 dogs = 0; // killough 7/19/98
2419 dog_jumping = 0; // killough 10/98
2420#endif
2421
2422 monkeys = 0;
2423 }
2424
2425 return target;
2426}
2427
2428void G_BeginRecording (void)
2429{
2430 int i;
2431 byte *demostart, *demo_p;
2432 demostart = demo_p = malloc(1000);
2433
2434 /* cph - 3 demo record formats supported: MBF+, BOOM, and Doom v1.9 */
2435 if (mbf_features) {
2436 { /* Write version code into demo */
2437 unsigned char v=0;
2438 switch(compatibility_level) {
2439 case doom_12_compatibility:
2440 case doom_demo_compatibility:
2441 case doom_compatibility:
2442 case boom_compatibility_compatibility:
2443 case boom_201_compatibility:
2444 case boom_202_compatibility:
2445 case lxdoom_1_compatibility:
2446 case prboom_1_compatibility:
2447 case MAX_COMPATIBILITY_LEVEL:
2448 case mbf_compatibility: v = 204; break;
2449 case prboom_2_compatibility: v = 210; break;
2450 case prboom_3_compatibility: v = 211; break;
2451 }
2452 *demo_p++ = v;
2453 }
2454
2455 // signature
2456 *demo_p++ = 0x1d;
2457 *demo_p++ = 'M';
2458 *demo_p++ = 'B';
2459 *demo_p++ = 'F';
2460 *demo_p++ = 0xe6;
2461 *demo_p++ = '\0';
2462
2463 /* killough 2/22/98: save compatibility flag in new demos
2464 * cph - FIXME? MBF demos will always be not in compat. mode */
2465 *demo_p++ = 0;
2466
2467 *demo_p++ = gameskill;
2468 *demo_p++ = gameepisode;
2469 *demo_p++ = gamemap;
2470 *demo_p++ = deathmatch;
2471 *demo_p++ = consoleplayer;
2472
2473 demo_p = G_WriteOptions(demo_p); // killough 3/1/98: Save game options
2474
2475 for (i=0 ; i<MAXPLAYERS ; i++)
2476 *demo_p++ = playeringame[i];
2477
2478 // killough 2/28/98:
2479 // We always store at least MIN_MAXPLAYERS bytes in demo, to
2480 // support enhancements later w/o losing demo compatibility
2481
2482 for (; i<MIN_MAXPLAYERS; i++)
2483 *demo_p++ = 0;
2484
2485 } else if (compatibility_level > doom_compatibility) {
2486 byte v=0, c=0; /* Nominally, version and compatibility bits */
2487 switch (compatibility_level) {
2488 case boom_compatibility_compatibility: v = 202, c = 1; break;
2489 case boom_201_compatibility: v = 201; c = 0; break;
2490 case boom_202_compatibility: v = 202, c = 0; break;
2491 default: I_Error("G_BeginRecording: Boom compatibility level unrecognised?");
2492 }
2493 *demo_p++ = v;
2494
2495 // signature
2496 *demo_p++ = 0x1d;
2497 *demo_p++ = 'B';
2498 *demo_p++ = 'o';
2499 *demo_p++ = 'o';
2500 *demo_p++ = 'm';
2501 *demo_p++ = 0xe6;
2502
2503 /* CPhipps - save compatibility level in demos */
2504 *demo_p++ = c;
2505
2506 *demo_p++ = gameskill;
2507 *demo_p++ = gameepisode;
2508 *demo_p++ = gamemap;
2509 *demo_p++ = deathmatch;
2510 *demo_p++ = consoleplayer;
2511
2512 demo_p = G_WriteOptions(demo_p); // killough 3/1/98: Save game options
2513
2514 for (i=0 ; i<MAXPLAYERS ; i++)
2515 *demo_p++ = playeringame[i];
2516
2517 // killough 2/28/98:
2518 // We always store at least MIN_MAXPLAYERS bytes in demo, to
2519 // support enhancements later w/o losing demo compatibility
2520
2521 for (; i<MIN_MAXPLAYERS; i++)
2522 *demo_p++ = 0;
2523 } else { // cph - write old v1.9 demos (might even sync)
2524 longtics = M_CheckParm("-longtics");
2525 *demo_p++ = longtics ? 111 : 109; // v1.9 has best chance of syncing these
2526 *demo_p++ = gameskill;
2527 *demo_p++ = gameepisode;
2528 *demo_p++ = gamemap;
2529 *demo_p++ = deathmatch;
2530 *demo_p++ = respawnparm;
2531 *demo_p++ = fastparm;
2532 *demo_p++ = nomonsters;
2533 *demo_p++ = consoleplayer;
2534 for (i=0; i<4; i++) // intentionally hard-coded 4 -- killough
2535 *demo_p++ = playeringame[i];
2536 }
2537
2538 if (write(demofd, demostart, demo_p-demostart) != (signed)(size_t)(demo_p-demostart))
2539 I_Error("G_BeginRecording: Error writing demo header");
2540 free(demostart);
2541}
2542
2543
2544//
2545// G_PlayDemo
2546//
2547
2548static const char *defdemoname;
2549
2550void G_DeferedPlayDemo (const char* name)
2551{
2552 defdemoname = name;
2553 gameaction = ga_playdemo;
2554}
2555
2556static int demolumpnum = -1;
2557
2558static const byte* G_ReadDemoHeader(const byte *demo_p)
2559{
2560 skill_t skill;
2561 int i, episode, map;
2562 int demover;
2563 const byte *option_p = NULL; /* killough 11/98 */
2564
2565 basetic = gametic; // killough 9/29/98
2566
2567 // killough 2/22/98, 2/28/98: autodetect old demos and act accordingly.
2568 // Old demos turn on demo_compatibility => compatibility; new demos load
2569 // compatibility flag, and other flags as well, as a part of the demo.
2570
2571 demover = *demo_p++;
2572
2573 if (demover < 200) // Autodetect old demos
2574 {
2575 compatibility_level = doom_demo_compatibility;
2576 if (demover >= 111) longtics = 1;
2577
2578 G_Compatibility();
2579
2580 // killough 3/2/98: force these variables to be 0 in demo_compatibility
2581
2582 variable_friction = 0;
2583
2584 weapon_recoil = 0;
2585
2586 allow_pushers = 0;
2587
2588 monster_infighting = 1; // killough 7/19/98
2589
2590#ifdef DOGS
2591 dogs = 0; // killough 7/19/98
2592 dog_jumping = 0; // killough 10/98
2593#endif
2594
2595 monster_backing = 0; // killough 9/8/98
2596
2597 monster_avoid_hazards = 0; // killough 9/9/98
2598
2599 monster_friction = 0; // killough 10/98
2600 help_friends = 0; // killough 9/9/98
2601 monkeys = 0;
2602
2603 // killough 3/6/98: rearrange to fix savegame bugs (moved fastparm,
2604 // respawnparm, nomonsters flags to G_LoadOptions()/G_SaveOptions())
2605
2606 if ((skill=demover) >= 100) // For demos from versions >= 1.4
2607 {
2608 skill = *demo_p++;
2609 episode = *demo_p++;
2610 map = *demo_p++;
2611 deathmatch = *demo_p++;
2612 respawnparm = *demo_p++;
2613 fastparm = *demo_p++;
2614 nomonsters = *demo_p++;
2615 consoleplayer = *demo_p++;
2616 }
2617 else
2618 {
2619 episode = *demo_p++;
2620 map = *demo_p++;
2621 deathmatch = respawnparm = fastparm =
2622 nomonsters = consoleplayer = 0;
2623 }
2624 }
2625 else // new versions of demos
2626 {
2627 demo_p += 6; // skip signature;
2628 switch (demover) {
2629 case 200: /* BOOM */
2630 case 201:
2631 if (!*demo_p++)
2632 compatibility_level = boom_201_compatibility;
2633 else
2634 compatibility_level = boom_compatibility_compatibility;
2635 break;
2636 case 202:
2637 if (!*demo_p++)
2638 compatibility_level = boom_202_compatibility;
2639 else
2640 compatibility_level = boom_compatibility_compatibility;
2641 break;
2642 case 203:
2643 /* LxDoom or MBF - determine from signature
2644 * cph - load compatibility level */
2645 switch (demobuffer[2]) {
2646 case 'B': /* LxDoom */
2647 /* cph - DEMOSYNC - LxDoom demos recorded in compatibility modes support dropped */
2648 compatibility_level = lxdoom_1_compatibility;
2649 break;
2650 case 'M':
2651 compatibility_level = mbf_compatibility;
2652 demo_p++;
2653 break;
2654 }
2655 break;
2656 case 210:
2657 compatibility_level = prboom_2_compatibility;
2658 demo_p++;
2659 break;
2660 case 211:
2661 compatibility_level = prboom_3_compatibility;
2662 demo_p++;
2663 break;
2664 }
2665 G_Compatibility();
2666 skill = *demo_p++;
2667 episode = *demo_p++;
2668 map = *demo_p++;
2669 deathmatch = *demo_p++;
2670 consoleplayer = *demo_p++;
2671
2672 /* killough 11/98: save option pointer for below */
2673 if (mbf_features)
2674 option_p = demo_p;
2675
2676 demo_p = G_ReadOptions(demo_p); // killough 3/1/98: Read game options
2677
2678 if (demover == 200) // killough 6/3/98: partially fix v2.00 demos
2679 demo_p += 128-GAME_OPTION_SIZE;
2680 }
2681
2682// printf( "G_DoPlayDemo: playing demo with %s compatibility\n",
2683// comp_lev_str[compatibility_level]);
2684
2685 if (demo_compatibility) // only 4 players can exist in old demos
2686 {
2687 for (i=0; i<4; i++) // intentionally hard-coded 4 -- killough
2688 playeringame[i] = *demo_p++;
2689 for (;i < MAXPLAYERS; i++)
2690 playeringame[i] = 0;
2691 }
2692 else
2693 {
2694 for (i=0 ; i < MAXPLAYERS; i++)
2695 playeringame[i] = *demo_p++;
2696 demo_p += MIN_MAXPLAYERS - MAXPLAYERS;
2697 }
2698
2699 if (playeringame[1])
2700 {
2701 netgame = true;
2702 netdemo = true;
2703 }
2704
2705 if (gameaction != ga_loadgame) { /* killough 12/98: support -loadgame */
2706 G_InitNew(skill, episode, map);
2707 }
2708
2709 for (i=0; i<MAXPLAYERS;i++) // killough 4/24/98
2710 players[i].cheats = 0;
2711
2712 return demo_p;
2713}
2714
2715void G_DoPlayDemo(void)
2716{
2717 char basename[9];
2718
2719 ExtractFileBase(defdemoname,basename); // killough
2720 basename[8] = 0;
2721 demobuffer = demo_p = W_CacheLumpNum(demolumpnum = W_GetNumForName(basename));
2722 /* cph - store lump number for unlocking later */
2723
2724 demo_p = G_ReadDemoHeader(demo_p);
2725
2726 gameaction = ga_nothing;
2727 usergame = false;
2728
2729 demoplayback = true;
2730}
2731
2732//
2733// G_TimeDemo
2734//
2735
2736void G_TimeDemo(const char *name) // CPhipps - const char*
2737{
2738 timingdemo = true;
2739 singletics = true;
2740 defdemoname = name;
2741 gameaction = ga_playdemo;
2742}
2743
2744
2745/* G_CheckDemoStatus
2746 *
2747 * Called after a death or level completion to allow demos to be cleaned up
2748 * Returns true if a new demo loop action will take place
2749 */
2750
2751boolean G_CheckDemoStatus (void)
2752{
2753 if (demorecording)
2754 {
2755 demorecording = false;
2756 write(demofd, &DEMOMARKER, 1);
2757 close(demofd);
2758 I_Error("G_CheckDemoStatus: Demo recorded");
2759 return false; // killough
2760 }
2761
2762 if (timingdemo)
2763 {
2764// int endtime = I_GetTime_RealTime ();
2765 int endtime = I_GetTime ();
2766 // killough -- added fps information and made it work for longer demos:
2767 unsigned realtics = endtime-starttime;
2768 int fd=open("/games/doom/timedemo.txt",O_WRONLY | O_CREAT);
2769 fprintf (fd,"Timed %d gametics in %d realtics = %d frames per second",
2770 (unsigned) gametic, realtics,
2771 (unsigned) gametic * (double) TICRATE / realtics);
2772 close(fd);
2773 I_Error ("%d gametics in %d realtics",
2774 (unsigned) gametic,realtics,
2775 (unsigned) gametic * (double) TICRATE / realtics);
2776 return false;
2777 }
2778
2779 if (demoplayback)
2780 {
2781 if (singledemo)
2782 I_Error("Done Playing Demo");
2783 return false;
2784// exit(0); // killough
2785
2786 if (demolumpnum != -1) {
2787 // cph - unlock the demo lump
2788 W_UnlockLumpNum(demolumpnum);
2789 demolumpnum = -1;
2790 }
2791 G_ReloadDefaults(); // killough 3/1/98
2792 netgame = false; // killough 3/29/98
2793 deathmatch = false;
2794 D_AdvanceDemo ();
2795 return true;
2796 }
2797 return false;
2798}
2799
2800// killough 1/22/98: this is a "Doom printf" for messages. I've gotten
2801// tired of using players->message=... and so I've added this dprintf.
2802//
2803// killough 3/6/98: Made limit static to allow z_zone functions to call
2804// this function, without calling realloc(), which seems to cause problems.
2805
2806#define MAX_MESSAGE_SIZE 1024
2807static char msg[MAX_MESSAGE_SIZE];
2808
2809// CPhipps - renamed to doom_printf to avoid name collision with glibc
2810void doom_printf(const char *s, ...)
2811{
2812 va_list v;
2813 va_start(v,s);
2814 vsnprintf(msg,sizeof(msg),s,v); /* print message in buffer */
2815 va_end(v);
2816 players[consoleplayer].message = msg; // set new message
2817}
2818