Update the discussion of themeing in the manual, and put a note in the wps tags appen...
[kugel-rb.git] / apps / plugins / doom / g_game.c
blobd4445254f0b54d999816069215adc50760682da4
1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
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
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.
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.
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.
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.
33 *-----------------------------------------------------------------------------
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"
50 #include "d_main.h"
52 #include "wi_stuff.h"
53 #include "hu_stuff.h"
54 #include "st_stuff.h"
55 #include "am_map.h"
57 // Needs access to LFB.
58 #include "v_video.h"
60 #include "w_wad.h"
61 #include "r_main.h"
62 #include "s_sound.h"
64 // Data.
65 #include "dstrings.h"
66 #include "sounds.h"
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"
74 #include "rockmacros.h"
76 #define SAVEGAMESIZE 0x20000
77 #define SAVESTRINGSIZE 24
79 static size_t savegamesize = SAVEGAMESIZE; // killough
80 static boolean netdemo;
81 static const byte *demobuffer; /* cph - only used for playback */
82 static int demofd; /* cph - record straight to file */
83 static const byte *demo_p;
84 static short consistancy[MAXPLAYERS][BACKUPTICS];
86 gameaction_t gameaction;
87 gamestate_t gamestate;
88 skill_t gameskill;
89 boolean respawnmonsters;
90 int gameepisode;
91 int gamemap;
92 boolean paused;
93 // CPhipps - moved *_loadgame vars here
94 static boolean forced_loadgame = false;
95 static boolean command_loadgame = false;
97 boolean usergame; // ok to save / end game
98 boolean timingdemo; // if true, exit with report on completion
99 boolean fastdemo; // if true, run at full speed -- killough
100 boolean nodrawers; // for comparative timing purposes
101 boolean noblit; // for comparative timing purposes
102 int starttime; // for comparative timing purposes
103 boolean deathmatch; // only if started as net death
104 boolean netgame; // only true if packets are broadcast
105 boolean playeringame[MAXPLAYERS];
106 player_t players[MAXPLAYERS];
107 int consoleplayer; // player taking events and displaying
108 int displayplayer; // view being displayed
109 int gametic;
110 int levelstarttic; // gametic at level start
111 extern int basetic; /* killough 9/29/98: for demo sync */
112 int totalkills, totallive, totalitems, totalsecret; // for intermission
113 boolean demorecording;
114 boolean demoplayback;
115 boolean singledemo; // quit after playing a demo from cmdline
116 wbstartstruct_t wminfo; // parms for world map / intermission
117 boolean haswolflevels = false;// jff 4/18/98 wolf levels present
118 static byte *savebuffer; // CPhipps - static
119 int autorun = false; // always running? // phares
120 int totalleveltimes; // CPhipps - total time for all completed levels
121 int longtics;
124 // controls (have defaults)
126 int key_right;
127 int key_left;
128 int key_up;
129 int key_down;
130 int key_menu_right; // phares 3/7/98
131 int key_menu_left; // |
132 int key_menu_up; // V
133 int key_menu_down;
134 int key_menu_backspace; // ^
135 int key_menu_escape; // |
136 int key_menu_enter; // phares 3/7/98
137 int key_strafeleft;
138 int key_straferight;
139 int key_fire;
140 int key_use;
141 int key_strafe;
142 int key_speed;
143 int key_escape = KEY_ESCAPE; // phares 4/13/98
144 int key_weapon;
146 int key_savegame; // phares
147 int key_loadgame; // |
148 int key_autorun; // V
149 int key_reverse;
150 int key_zoomin;
151 int key_zoomout;
153 int key_reverse;
155 int key_chat;
156 int key_backspace;
157 int key_enter;
158 int key_map_right;
159 int key_map_left;
160 int key_map_up;
161 int key_map_down;
162 int key_map_zoomin;
163 int key_map_zoomout;
164 int key_map;
165 int key_map_gobig;
166 int key_map_follow;
167 int key_map_mark;
168 int key_map_clear;
169 int key_map_grid;
170 int key_map_overlay; // cph - map overlay
171 int key_map_rotate; // cph - map rotation
172 int key_help = KEY_F1; // phares 4/13/98
173 int key_soundvolume;
174 int key_hud;
175 int key_quicksave;
176 int key_endgame;
177 int key_messages;
178 int key_quickload;
179 int key_quit;
180 int key_gamma;
181 int key_spy;
182 int key_pause;
183 int key_setup;
184 int destination_keys[MAXPLAYERS];
185 int key_weapontoggle;
186 int key_weapon1;
187 int key_weapon2;
188 int key_weapon3;
189 int key_weapon4;
190 int key_weapon5;
191 int key_weapon6;
192 int key_weapon7; // ^
193 int key_weapon8; // |
194 int key_weapon9; // phares
196 int key_screenshot; // killough 2/22/98: screenshot key
197 int mousebfire;
198 int mousebstrafe;
199 int mousebforward;
200 int joybfire;
201 int joybstrafe;
202 int joybuse;
203 int joybspeed;
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
211 fixed_t forwardmove[2] = {0x19, 0x32};
212 fixed_t sidemove[2] = {0x18, 0x28};
213 fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
215 // CPhipps - made lots of key/button state vars static
216 static boolean gamekeydown[NUMKEYS];
217 static int turnheld; // for accelerative turning
219 static boolean mousearray[4];
220 static boolean *mousebuttons = &mousearray[1]; // allow [-1]
222 // mouse values are used once
223 static int mousex;
224 static int mousey;
225 static unsigned int dclicktime;
226 static unsigned int dclickstate;
227 static unsigned int dclicks;
228 static unsigned int dclicktime2;
229 static unsigned int dclickstate2;
230 static unsigned int dclicks2;
232 // joystick values are repeated
233 static int joyxmove;
234 static int joyymove;
235 static boolean joyarray[5];
236 static boolean *joybuttons = &joyarray[1]; // allow [-1]
238 // Game events info
239 static buttoncode_t special_event; // Event triggered by local player, to send
240 static byte savegameslot; // Slot to load if gameaction == ga_loadgame
241 char savedescription[SAVEDESCLEN]; // Description to save in savegame if gameaction == ga_savegame
243 //jff 3/24/98 declare startskill external, define defaultskill here
244 extern skill_t startskill; //note 0-based
245 int defaultskill; //note 1-based
247 // killough 2/8/98: make corpse queue variable in size
248 int bodyqueslot, bodyquesize; // killough 2/8/98
249 mobj_t **bodyque = 0; // phares 8/10/98
251 void* statcopy; // for statistics driver
253 static void G_DoSaveGame (boolean menu);
254 static const byte* G_ReadDemoHeader(const byte* demo_p);
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
262 static inline signed char fudgef(signed char b)
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;
271 static inline signed short fudgea(signed short b)
273 if (!b || !demo_compatibility || !longtics) return b;
274 b |= 1; if (b>2) b-=2;
275 return b;
278 void G_BuildTiccmd(ticcmd_t* cmd)
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];
291 strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
292 || joybuttons[joybstrafe];
293 speed = autorun || gamekeydown[key_speed] || joybuttons[joybspeed]; // phares
295 forward = side = 0;
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;
305 if (turnheld < SLOWTURNTICS)
306 tspeed = 2; // slow turn
307 else
308 tspeed = speed;
310 // turn 180 degrees in one keystroke? // phares
311 // |
312 if (gamekeydown[key_reverse]) // V
314 cmd->angleturn += QUICKREVERSE; // ^
315 gamekeydown[key_reverse] = false; // |
316 } // phares
318 // let movement keys cancel each other out
320 if (strafe)
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];
331 else
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];
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];
356 // buttons
357 cmd->chatchar = HU_dequeueChatChar();
359 if (gamekeydown[key_fire] || mousebuttons[mousebfire] ||
360 joybuttons[joybfire])
361 cmd->buttons |= BT_ATTACK;
363 if (gamekeydown[key_use] || joybuttons[joybuse])
365 cmd->buttons |= BT_USE;
366 // clear double clicks if hit use button
367 dclicks = 0;
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
374 // killough 3/22/98:
376 // Perform automatic weapons switch here rather than in p_pspr.c,
377 // except in demo_compatibility mode.
379 // killough 3/26/98, 4/2/98: fix autoswitch when no weapons are left
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])
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 )
392 newweapon=wpcheck;
393 break;
395 if(players[consoleplayer].weaponowned[wp_chainsaw]&&newweapon==0)
396 newweapon=1;
398 else
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;
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.
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.
424 if (!demo_compatibility)
426 const player_t *player = &players[consoleplayer];
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.
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;
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;
454 // killough 2/8/98, 3/22/98 -- end of weapon selection changes
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;
461 if (newweapon != wp_nochange)
463 cmd->buttons |= BT_CHANGE;
464 cmd->buttons |= newweapon<<BT_WEAPONSHIFT;
467 // mouse
468 if (mousebuttons[mousebforward])
469 forward += forwardmove[speed];
471 // forward double click
472 if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
474 dclickstate = mousebuttons[mousebforward];
475 if (dclickstate)
476 dclicks++;
477 if (dclicks == 2)
479 cmd->buttons |= BT_USE;
480 dclicks = 0;
482 else
483 dclicktime = 0;
485 else
486 if ((dclicktime += ticdup) > 20)
488 dclicks = 0;
489 dclickstate = 0;
492 // strafe double click
494 bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe];
495 if (bstrafe != dclickstate2 && dclicktime2 > 1 )
497 dclickstate2 = bstrafe;
498 if (dclickstate2)
499 dclicks2++;
500 if (dclicks2 == 2)
502 cmd->buttons |= BT_USE;
503 dclicks2 = 0;
505 else
506 dclicktime2 = 0;
508 else
509 if ((dclicktime2 += ticdup) > 20)
511 dclicks2 = 0;
512 dclickstate2 = 0;
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 */
520 mousex = mousey = 0;
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;
531 cmd->forwardmove += fudgef(forward);
532 cmd->sidemove += side;
533 cmd->angleturn = fudgea(cmd->angleturn);
535 // CPhipps - special events (game new/load/save/pause)
536 if (special_event & BT_SPECIAL) {
537 cmd->buttons = special_event;
538 special_event = 0;
543 // G_RestartLevel
546 void G_RestartLevel(void)
548 special_event = BT_SPECIAL | (BTS_RESTARTLEVEL & BT_SPECIALMASK);
551 #include "z_bmalloc.h"
553 // G_DoLoadLevel
555 extern gamestate_t wipegamestate;
557 static void G_DoLoadLevel (void)
559 int i;
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.
567 skyflatnum = R_FlatNumForName ( SKYFLATNAME );
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
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");
582 else //jff 3/27/98 and lets not forget about DOOM and Ultimate DOOM huh?
583 switch (gameepisode)
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
599 levelstarttic = gametic; // for time calculation
601 if (!demo_compatibility && !mbf_features) // killough 9/29/98
602 basetic = gametic;
604 if (wipegamestate == GS_LEVEL)
605 wipegamestate = -1; // force a wipe
607 gamestate = GS_LEVEL;
609 for (i=0 ; i<MAXPLAYERS ; i++)
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));
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.
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;
628 P_SetupLevel (gameepisode, gamemap, 0, gameskill);
629 displayplayer = consoleplayer; // view the guy you are playing
630 gameaction = ga_nothing;
631 Z_CheckHeap ();
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));
641 // killough 5/13/98: in case netdemo has consoleplayer other than green
642 ST_Start();
643 HU_Start();
645 // killough: make -timedemo work on multilevel demos
646 // Move to end of function to minimize noise -- killough 2/22/98:
648 if (timingdemo)
650 static int first=1;
651 if (first)
653 starttime = I_GetTime ();
654 first=0;
660 // G_Responder
661 // Get info needed to make ticcmd_ts for the players.
663 boolean G_Responder (event_t* ev)
665 // allow spy mode changes even during the demo
666 // killough 2/22/98: even during DM demo
668 // killough 11/98: don't autorepeat spy mode switch
670 #if 0
671 if (ev->data1 == key_spy && netgame && (demoplayback || !deathmatch) &&
672 gamestate == GS_LEVEL)
674 if (ev->type == ev_keyup)
675 gamekeydown[key_spy] = false;
676 if (ev->type == ev_keydown && !gamekeydown[key_spy])
678 gamekeydown[key_spy] = true;
679 do // spy mode
680 if (++displayplayer >= MAXPLAYERS)
681 displayplayer = 0;
682 while (!playeringame[displayplayer] && displayplayer!=consoleplayer);
684 ST_Start(); // killough 3/7/98: switch status bar views too
685 HU_Start();
686 S_UpdateSounds(players[displayplayer].mo);
688 return true;
690 #endif
692 // any other key pops up menu if in demos
694 // killough 8/2/98: enable automap in -timedemo demos
696 // killough 9/29/98: make any key pop up menu regardless of
697 // which kind of demo, and allow other events during playback
699 if (gameaction == ga_nothing && (demoplayback || gamestate == GS_DEMOSCREEN))
701 // killough 9/29/98: allow user to pause demos during playback
702 if (ev->type == ev_keydown && ev->data1 == key_pause)
704 if (paused ^= 2)
705 S_PauseSound();
706 else
707 S_ResumeSound();
708 return true;
710 // killough 10/98:
711 // Don't pop up menu, if paused in middle
712 // of demo playback, or if automap active.
713 // Don't suck up keys, which may be cheats
715 return gamestate == GS_DEMOSCREEN &&
716 !(paused & 2) && !(automapmode & am_active) &&
717 ((ev->type == ev_keydown) ||
718 (ev->type == ev_mouse && ev->data1) ||
719 (ev->type == ev_joystick && ev->data1)) ?
720 M_StartControlPanel(), true : false;
723 if (gamestate == GS_FINALE && F_Responder(ev))
724 return true; // finale ate the event
726 switch (ev->type)
728 case ev_keydown:
729 if (ev->data1 == key_pause) // phares
731 special_event = BT_SPECIAL | (BTS_PAUSE & BT_SPECIALMASK);
732 return true;
734 if (ev->data1 <NUMKEYS)
735 gamekeydown[ev->data1] = true;
736 return true; // eat key down events
738 case ev_keyup:
739 if (ev->data1 <NUMKEYS)
740 gamekeydown[ev->data1] = false;
741 return false; // always let key up events filter down
743 case ev_mouse:
744 mousebuttons[0] = ev->data1 & 1;
745 mousebuttons[1] = ev->data1 & 2;
746 mousebuttons[2] = ev->data1 & 4;
747 mousex = ev->data2*(mouseSensitivity+5)/10;
748 mousey = ev->data3*(mouseSensitivity+5)/10;
749 return true; // eat events
751 case ev_joystick:
752 joybuttons[0] = ev->data1 & 1;
753 joybuttons[1] = ev->data1 & 2;
754 joybuttons[2] = ev->data1 & 4;
755 joybuttons[3] = ev->data1 & 8;
756 joyxmove = ev->data2;
757 joyymove = ev->data3;
758 return true; // eat events
760 default:
761 break;
763 return false;
767 // G_Ticker
768 // Make ticcmd_ts for the players.
770 extern int mapcolor_me;
772 void G_Ticker (void)
774 int i;
775 static gamestate_t prevgamestate;
777 P_MapStart();
778 // do player reborns if needed
779 for (i=0 ; i<MAXPLAYERS ; i++)
780 if (playeringame[i] && players[i].playerstate == PST_REBORN)
781 G_DoReborn (i);
782 P_MapEnd();
784 // do things to change the game state
785 while (gameaction != ga_nothing)
787 switch (gameaction)
789 case ga_loadlevel:
790 // force players to be initialized on level reload
791 for (i=0 ; i<MAXPLAYERS ; i++)
792 players[i].playerstate = PST_REBORN;
793 G_DoLoadLevel ();
794 break;
795 case ga_newgame:
796 G_DoNewGame ();
797 break;
798 case ga_loadgame:
799 G_DoLoadGame ();
800 break;
801 case ga_savegame:
802 G_DoSaveGame (false);
803 break;
804 case ga_playdemo:
805 G_DoPlayDemo ();
806 break;
807 case ga_completed:
808 G_DoCompleted ();
809 break;
810 case ga_victory:
811 F_StartFinale ();
812 break;
813 case ga_worlddone:
814 G_DoWorldDone ();
815 break;
816 case ga_nothing:
817 break;
821 if (paused & 2 || (!demoplayback && menuactive && !netgame))
822 basetic++; // For revenant tracers and RNG -- we must maintain sync
823 else
825 // get commands, check consistancy, and build new consistancy check
826 int buf = (gametic/ticdup)%BACKUPTICS;
828 for (i=0 ; i<MAXPLAYERS ; i++)
830 if (playeringame[i])
832 ticcmd_t *cmd = &players[i].cmd;
834 memcpy(cmd, &netcmds[i][buf], sizeof *cmd);
836 if (demoplayback)
837 G_ReadDemoTiccmd (cmd);
838 if (demorecording)
839 G_WriteDemoTiccmd (cmd);
841 // check for turbo cheats
842 // killough 2/14/98, 2/20/98 -- only warn in netgames and demos
844 if ((netgame || demoplayback) && cmd->forwardmove > TURBOTHRESHOLD &&
845 !(gametic&31) && ((gametic>>5)&3) == i )
847 extern char *player_names[];
848 /* cph - don't use sprintf, use doom_printf */
849 doom_printf ("%s is turbo!", player_names[i]);
852 if (netgame && !netdemo && !(gametic%ticdup) )
854 if (gametic > BACKUPTICS
855 && consistancy[i][buf] != cmd->consistancy)
856 I_Error("G_Ticker: Consistency failure (%i should be %i)",
857 cmd->consistancy, consistancy[i][buf]);
858 if (players[i].mo)
859 consistancy[i][buf] = players[i].mo->x;
860 else
861 consistancy[i][buf] = 0; // killough 2/14/98
866 // check for special buttons
867 for (i=0 ; i<MAXPLAYERS ; i++)
869 if (playeringame[i])
871 if (players[i].cmd.buttons & BT_SPECIAL)
873 switch (players[i].cmd.buttons & BT_SPECIALMASK)
875 case BTS_PAUSE:
876 paused ^= 1;
877 if (paused)
878 S_PauseSound ();
879 else
880 S_ResumeSound ();
881 break;
883 case BTS_SAVEGAME:
884 if (!savedescription[0])
885 strcpy(savedescription, "NET GAME");
886 savegameslot =
887 (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
888 gameaction = ga_savegame;
889 break;
891 // CPhipps - remote loadgame request
892 case BTS_LOADGAME:
893 savegameslot =
894 (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
895 gameaction = ga_loadgame;
896 forced_loadgame = netgame; // Force if a netgame
897 command_loadgame = false;
898 break;
900 // CPhipps - Restart the level
901 case BTS_RESTARTLEVEL:
902 if (demoplayback || (compatibility_level < lxdoom_1_compatibility))
903 break; // CPhipps - Ignore in demos or old games
904 gameaction = ga_loadlevel;
905 break;
907 players[i].cmd.buttons = 0;
913 // cph - if the gamestate changed, we may need to clean up the old gamestate
914 if (gamestate != prevgamestate) {
915 switch (prevgamestate) {
916 case GS_INTERMISSION:
917 WI_End();
918 default:
919 break;
921 prevgamestate = gamestate;
924 // do main actions
925 switch (gamestate)
927 case GS_LEVEL:
928 P_Ticker ();
929 ST_Ticker ();
930 AM_Ticker ();
931 HU_Ticker ();
932 break;
934 case GS_INTERMISSION:
935 WI_Ticker ();
936 break;
938 case GS_FINALE:
939 F_Ticker ();
940 break;
942 case GS_DEMOSCREEN:
943 D_PageTicker ();
944 break;
950 // PLAYER STRUCTURE FUNCTIONS
951 // also see P_SpawnPlayer in P_Things
955 // G_PlayerFinishLevel
956 // Can when a player completes a level.
958 void G_PlayerFinishLevel(int player)
960 player_t *p = &players[player];
961 memset(p->powers, 0, sizeof (p->powers));
962 memset(p->cards, 0, sizeof (p->cards));
963 p->mo->flags &= ~MF_SHADOW; // cancel invisibility
964 p->extralight = 0; // cancel gun flashes
965 p->fixedcolormap = 0; // cancel ir gogles
966 p->damagecount = 0; // no palette changes
967 p->bonuscount = 0;
970 // CPhipps - G_SetPlayerColour
971 // Player colours stuff
973 // G_SetPlayerColour
975 #include "r_draw.h"
976 extern byte playernumtotrans[MAXPLAYERS];
978 void G_ChangedPlayerColour(int pn, int cl)
980 int i;
982 if (!netgame) return;
984 mapcolor_plyr[pn] = cl;
986 // Rebuild colour translation tables accordingly
987 R_InitTranslationTables();
988 // Change translations on existing player mobj's
989 for (i=0; i<MAXPLAYERS; i++) {
990 if ((gamestate == GS_LEVEL) && playeringame[i] && (players[i].mo != NULL)) {
991 players[i].mo->flags &= ~MF_TRANSLATION;
992 players[i].mo->flags |= playernumtotrans[i] << MF_TRANSSHIFT;
998 // G_PlayerReborn
999 // Called after a player dies
1000 // almost everything is cleared and initialized
1002 void G_PlayerReborn (int player)
1004 player_t *p;
1005 int i;
1006 int frags[MAXPLAYERS];
1007 int killcount;
1008 int itemcount;
1009 int secretcount;
1011 memcpy (frags, players[player].frags, sizeof frags);
1012 killcount = players[player].killcount;
1013 itemcount = players[player].itemcount;
1014 secretcount = players[player].secretcount;
1016 p = &players[player];
1018 // killough 3/10/98,3/21/98: preserve cheats across idclev
1020 int cheats = p->cheats;
1021 memset (p, 0, sizeof(*p));
1022 p->cheats = cheats;
1025 memcpy(players[player].frags, frags, sizeof(players[player].frags));
1026 players[player].killcount = killcount;
1027 players[player].itemcount = itemcount;
1028 players[player].secretcount = secretcount;
1030 p->usedown = p->attackdown = true; // don't do anything immediately
1031 p->playerstate = PST_LIVE;
1032 p->health = MAXHEALTH;
1033 p->readyweapon = p->pendingweapon = wp_pistol;
1034 p->weaponowned[wp_fist] = true;
1035 p->weaponowned[wp_pistol] = true;
1036 p->ammo[am_clip] = 50;
1038 for (i=0 ; i<NUMAMMO ; i++)
1039 p->maxammo[i] = maxammo[i];
1044 // G_CheckSpot
1045 // Returns false if the player cannot be respawned
1046 // at the given mapthing_t spot
1047 // because something is occupying it
1050 void P_SpawnPlayer(mapthing_t *mthing);
1052 boolean G_CheckSpot(int playernum, mapthing_t *mthing)
1054 fixed_t x,y;
1055 subsector_t *ss;
1056 int i;
1058 if (!players[playernum].mo)
1060 // first spawn of level, before corpses
1061 for (i=0 ; i<playernum ; i++)
1062 if (players[i].mo->x == mthing->x << FRACBITS
1063 && players[i].mo->y == mthing->y << FRACBITS)
1064 return false;
1065 return true;
1068 x = mthing->x << FRACBITS;
1069 y = mthing->y << FRACBITS;
1071 // killough 4/2/98: fix bug where P_CheckPosition() uses a non-solid
1072 // corpse to detect collisions with other players in DM starts
1074 // Old code:
1075 // if (!P_CheckPosition (players[playernum].mo, x, y))
1076 // return false;
1078 players[playernum].mo->flags |= MF_SOLID;
1079 i = P_CheckPosition(players[playernum].mo, x, y);
1080 players[playernum].mo->flags &= ~MF_SOLID;
1081 if (!i)
1082 return false;
1084 // flush an old corpse if needed
1085 // killough 2/8/98: make corpse queue have an adjustable limit
1086 // killough 8/1/98: Fix bugs causing strange crashes
1088 if (bodyquesize > 0)
1090 static mobj_t **bodyque;
1091 static int queuesize;
1092 if (queuesize < bodyquesize)
1094 bodyque = realloc(bodyque, bodyquesize*sizeof*bodyque);
1095 memset(bodyque+queuesize, 0,
1096 (bodyquesize-queuesize)*sizeof*bodyque);
1097 queuesize = bodyquesize;
1099 if (bodyqueslot >= bodyquesize)
1100 P_RemoveMobj(bodyque[bodyqueslot % bodyquesize]);
1101 bodyque[bodyqueslot++ % bodyquesize] = players[playernum].mo;
1103 else
1104 if (!bodyquesize)
1105 P_RemoveMobj(players[playernum].mo);
1107 // spawn a teleport fog
1108 ss = R_PointInSubsector (x,y);
1109 { // Teleport fog at respawn point
1110 fixed_t xa=0,ya=0;
1111 int an;
1112 mobj_t *mo;
1114 /* BUG: an can end up negative, because mthing->angle is (signed) short.
1115 * We have to emulate original Doom's behaviour, deferencing past the start
1116 * of the array, into the previous array (finetangent) */
1117 an = ( ANG45 * ((signed)mthing->angle/45) ) >> ANGLETOFINESHIFT;
1118 switch (an) {
1119 case -4096: xa = finetangent[2048]; // finecosine[-4096]
1120 ya = finetangent[0]; // finesine[-4096]
1121 break;
1122 case -3072: xa = finetangent[3072]; // finecosine[-3072]
1123 ya = finetangent[1024]; // finesine[-3072]
1124 break;
1125 case -2048: xa = finesine[0]; // finecosine[-2048]
1126 ya = finetangent[2048]; // finesine[-2048]
1127 break;
1128 case -1024: xa = finesine[1024]; // finecosine[-1024]
1129 ya = finetangent[3072]; // finesine[-1024]
1130 break;
1131 case 1024:
1132 case 2048:
1133 case 3072:
1134 case 4096:
1135 case 0: xa = finecosine[an];
1136 ya = finesine[an];
1137 break;
1138 default: I_Error("G_CheckSpot: unexpected angle %d\n",an);
1142 mo = P_SpawnMobj(x+20*xa, y+20*ya, ss->sector->floorheight, MT_TFOG);
1144 if (players[consoleplayer].viewz != 1)
1145 S_StartSound(mo, sfx_telept); // don't start sound on first frame
1148 return true;
1153 // G_DeathMatchSpawnPlayer
1154 // Spawns a player at one of the random death match spots
1155 // called at level load and each death
1157 void G_DeathMatchSpawnPlayer (int playernum)
1159 int j, selections = deathmatch_p - deathmatchstarts;
1161 if (selections < MAXPLAYERS)
1162 I_Error("G_DeathMatchSpawnPlayer: Only %i deathmatch spots, %d required",
1163 selections, MAXPLAYERS);
1165 for (j=0 ; j<20 ; j++)
1167 int i = P_Random(pr_dmspawn) % selections;
1168 if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
1170 deathmatchstarts[i].type = playernum+1;
1171 P_SpawnPlayer (&deathmatchstarts[i]);
1172 return;
1176 // no good spot, so the player will probably get stuck
1177 P_SpawnPlayer (&playerstarts[playernum]);
1181 // G_DoReborn
1184 void G_DoReborn (int playernum)
1186 if (!netgame)
1187 gameaction = ga_loadlevel; // reload the level from scratch
1188 (void)playernum;
1189 #if 0
1190 else
1191 { // respawn at the start
1192 int i;
1194 // first dissasociate the corpse
1195 players[playernum].mo->player = NULL;
1197 // spawn at random spot if in death match
1198 if (deathmatch)
1200 G_DeathMatchSpawnPlayer (playernum);
1201 return;
1204 if (G_CheckSpot (playernum, &playerstarts[playernum]) )
1206 P_SpawnPlayer (&playerstarts[playernum]);
1207 return;
1210 // try to spawn at one of the other players spots
1211 for (i=0 ; i<MAXPLAYERS ; i++)
1213 if (G_CheckSpot (playernum, &playerstarts[i]) )
1215 playerstarts[i].type = playernum+1; // fake as other player
1216 P_SpawnPlayer (&playerstarts[i]);
1217 playerstarts[i].type = i+1; // restore
1218 return;
1220 // he's going to be inside something. Too bad.
1222 P_SpawnPlayer (&playerstarts[playernum]);
1224 #endif
1227 // DOOM Par Times
1228 int pars[4][10] =
1230 {0},
1231 {0,30,75,120,90,165,180,180,30,165},
1232 {0,90,90,90,120,90,360,240,30,170},
1233 {0,90,45,90,150,90,90,165,30,135}
1236 // DOOM II Par Times
1237 int cpars[32] =
1239 30,90,120,120,90,150,120,120,270,90, // 1-10
1240 210,150,150,150,210,150,420,150,210,150, // 11-20
1241 240,150,180,150,150,300,330,420,300,180, // 21-30
1242 120,30 // 31-32
1245 static boolean secretexit;
1247 void G_ExitLevel (void)
1249 secretexit = false;
1250 gameaction = ga_completed;
1253 // Here's for the german edition.
1254 // IF NO WOLF3D LEVELS, NO SECRET EXIT!
1256 void G_SecretExitLevel (void)
1258 if (gamemode!=commercial || haswolflevels)
1259 secretexit = true;
1260 else
1261 secretexit = false;
1262 gameaction = ga_completed;
1266 // G_DoCompleted
1269 void G_DoCompleted (void)
1271 int i;
1273 gameaction = ga_nothing;
1275 for (i=0; i<MAXPLAYERS; i++)
1276 if (playeringame[i])
1277 G_PlayerFinishLevel(i); // take away cards and stuff
1279 if (automapmode & am_active)
1280 AM_Stop();
1282 if (gamemode != commercial) // kilough 2/7/98
1283 switch(gamemap)
1285 // cph - Remove ExM8 special case, so it gets summary screen displayed
1286 case 9:
1287 for (i=0 ; i<MAXPLAYERS ; i++)
1288 players[i].didsecret = true;
1289 break;
1292 wminfo.didsecret = players[consoleplayer].didsecret;
1293 wminfo.epsd = gameepisode -1;
1294 wminfo.last = gamemap -1;
1296 // wminfo.next is 0 biased, unlike gamemap
1297 if (gamemode == commercial)
1299 if (secretexit)
1300 switch(gamemap)
1302 case 15:
1303 wminfo.next = 30; break;
1304 case 31:
1305 wminfo.next = 31; break;
1307 else
1308 switch(gamemap)
1310 case 31:
1311 case 32:
1312 wminfo.next = 15; break;
1313 default:
1314 wminfo.next = gamemap;
1317 else
1319 if (secretexit)
1320 wminfo.next = 8; // go to secret level
1321 else
1322 if (gamemap == 9)
1324 // returning from secret level
1325 switch (gameepisode)
1327 case 1:
1328 wminfo.next = 3;
1329 break;
1330 case 2:
1331 wminfo.next = 5;
1332 break;
1333 case 3:
1334 wminfo.next = 6;
1335 break;
1336 case 4:
1337 wminfo.next = 2;
1338 break;
1341 else
1342 wminfo.next = gamemap; // go to next level
1345 wminfo.maxkills = totalkills;
1346 wminfo.maxitems = totalitems;
1347 wminfo.maxsecret = totalsecret;
1348 wminfo.maxfrags = 0;
1350 if ( gamemode == commercial )
1351 wminfo.partime = TICRATE*cpars[gamemap-1];
1352 else
1353 wminfo.partime = TICRATE*pars[gameepisode][gamemap];
1355 wminfo.pnum = consoleplayer;
1357 for (i=0 ; i<MAXPLAYERS ; i++)
1359 wminfo.plyr[i].in = playeringame[i];
1360 wminfo.plyr[i].skills = players[i].killcount;
1361 wminfo.plyr[i].sitems = players[i].itemcount;
1362 wminfo.plyr[i].ssecret = players[i].secretcount;
1363 wminfo.plyr[i].stime = leveltime;
1364 memcpy (wminfo.plyr[i].frags, players[i].frags,
1365 sizeof(wminfo.plyr[i].frags));
1368 /* cph - modified so that only whole seconds are added to the totalleveltimes
1369 * value; so our total is compatible with the "naive" total of just adding
1370 * the times in seconds shown for each level. Also means our total time
1371 * will agree with Compet-n.
1373 wminfo.totaltimes = (totalleveltimes += (leveltime - leveltime%35));
1375 gamestate = GS_INTERMISSION;
1376 automapmode &= ~am_active;
1378 if (statcopy)
1379 memcpy (statcopy, &wminfo, sizeof(wminfo));
1381 WI_Start (&wminfo);
1386 // G_WorldDone
1389 void G_WorldDone (void)
1391 gameaction = ga_worlddone;
1393 if (secretexit)
1394 players[consoleplayer].didsecret = true;
1396 if (gamemode == commercial)
1398 switch (gamemap)
1400 case 15:
1401 case 31:
1402 if (!secretexit)
1403 break;
1404 case 6:
1405 case 11:
1406 case 20:
1407 case 30:
1408 F_StartFinale ();
1409 break;
1412 else if (gamemap == 8)
1413 gameaction = ga_victory; // cph - after ExM8 summary screen, show victory stuff
1416 void G_DoWorldDone (void)
1418 idmusnum = -1; //jff 3/17/98 allow new level's music to be loaded
1419 gamestate = GS_LEVEL;
1420 gamemap = wminfo.next+1;
1421 G_DoLoadLevel();
1422 gameaction = ga_nothing;
1423 AM_clearMarks(); //jff 4/12/98 clear any marks on the automap
1426 // killough 2/28/98: A ridiculously large number
1427 // of players, the most you'll ever need in a demo
1428 // or savegame. This is used to prevent problems, in
1429 // case more players in a game are supported later.
1431 #define MIN_MAXPLAYERS 32
1433 extern boolean setsizeneeded;
1434 void R_ExecuteSetViewSize(void);
1436 //CPhipps - savename variable redundant
1438 /* killough 12/98:
1439 * This function returns a signature for the current wad.
1440 * It is used to distinguish between wads, for the purposes
1441 * of savegame compatibility warnings, and options lookups.
1444 static uint_64_t G_UpdateSignature(uint_64_t s, const char *name)
1446 int i, lump = W_CheckNumForName(name);
1447 if (lump != -1 && (i = lump+10) < numlumps)
1450 int size = W_LumpLength(i);
1451 const byte *p = W_CacheLumpNum(i);
1452 while (size--)
1453 s <<= 1, s += *p++;
1454 W_UnlockLumpNum(i);
1456 while (--i > lump);
1457 return s;
1460 static uint_64_t G_Signature(void)
1462 static uint_64_t s = 0;
1463 static boolean computed = false;
1464 char name[9];
1465 int episode, map;
1467 if (!computed) {
1468 computed = true;
1469 if (gamemode == commercial)
1470 for (map = haswolflevels ? 32 : 30; map; map--)
1471 snprintf(name, sizeof(name), "map%02d", map), s = G_UpdateSignature(s, name);
1472 else
1473 for (episode = gamemode==retail ? 4 :
1474 gamemode==shareware ? 1 : 3; episode; episode--)
1475 for (map = 9; map; map--)
1476 snprintf(name, sizeof(name), "E%dM%d", episode, map), s = G_UpdateSignature(s, name);
1478 return s;
1482 // killough 5/15/98: add forced loadgames, which allow user to override checks
1485 void G_ForcedLoadGame(void)
1487 // CPhipps - net loadgames are always forced, so we only reach here
1488 // in single player
1489 gameaction = ga_loadgame;
1490 forced_loadgame = true;
1493 // killough 3/16/98: add slot info
1494 // killough 5/15/98: add command-line
1495 void G_LoadGame(int slot, boolean command)
1497 if (!demoplayback && !command) {
1498 // CPhipps - handle savegame filename in G_DoLoadGame
1499 // - Delay load so it can be communicated in net game
1500 // - store info in special_event
1501 special_event = BT_SPECIAL | (BTS_LOADGAME & BT_SPECIALMASK) |
1502 ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK);
1503 forced_loadgame = netgame; // CPhipps - always force load netgames
1504 } else {
1505 // Do the old thing, immediate load
1506 gameaction = ga_loadgame;
1507 forced_loadgame = false;
1508 savegameslot = slot;
1509 demoplayback = false;
1511 command_loadgame = command;
1514 // killough 5/15/98:
1515 // Consistency Error when attempting to load savegame.
1517 static void G_LoadGameErr(const char *msg)
1519 (void) msg; // Need to fix, but right now we're ignoring a forced load
1520 Z_Free(savebuffer); // Free the savegame buffer
1521 // M_ForcedLoadGame(msg); // Print message asking for 'Y' to force
1522 if (command_loadgame) // If this was a command-line -loadgame
1524 D_StartTitle(); // Start the title screen
1525 gamestate = GS_DEMOSCREEN; // And set the game state accordingly
1529 // CPhipps - size of version header
1530 #define VERSIONSIZE 16
1532 const char * comp_lev_str[MAX_COMPATIBILITY_LEVEL] =
1533 { "doom v1.2", "demo", "doom", "\"boom compatibility\"", "boom v2.01", "boom v2.02", "lxdoom v1.3.2+",
1534 "MBF", "PrBoom 2.03beta", "PrBoom v2.1.0-2.1.1",
1535 "Current PrBoom" };
1537 static const struct {
1538 unsigned int comp_level;
1539 const char* ver_printf;
1540 int version;
1541 } version_headers[] = {
1542 { prboom_1_compatibility, "PrBoom %d", 260},
1543 /* cph - we don't need a new version_header for prboom_3_comp/v2.1.1, since
1544 * the file format is unchanged. */
1545 { prboom_3_compatibility, "PrBoom %d", 210}
1548 static const size_t num_version_headers = sizeof(version_headers) / sizeof(version_headers[0]);
1550 void G_DoLoadGame(void)
1552 int length, i;
1553 // CPhipps - do savegame filename stuff here
1554 char name[100+1]; // killough 3/22/98
1555 int savegame_compatibility = -1;
1557 G_SaveGameName(name,sizeof(name),savegameslot, demoplayback);
1559 gameaction = ga_nothing;
1561 length = M_ReadFile(name, &savebuffer);
1562 save_p = savebuffer + SAVESTRINGSIZE;
1564 // CPhipps - read the description field, compare with supported ones
1565 for (i=0; (size_t)i<num_version_headers; i++) {
1566 char vcheck[VERSIONSIZE];
1567 // killough 2/22/98: "proprietary" version string :-)
1568 snprintf (vcheck,sizeof(vcheck), version_headers[i].ver_printf, version_headers[i].version);
1570 if (!strncmp(save_p, vcheck, VERSIONSIZE)) {
1571 savegame_compatibility = version_headers[i].comp_level;
1572 i = num_version_headers;
1575 if (savegame_compatibility == -1) {
1576 if (forced_loadgame) {
1577 savegame_compatibility = MAX_COMPATIBILITY_LEVEL-1;
1578 } else {
1579 G_LoadGameErr("Unrecognised savegame version!\nAre you sure? (y/n) ");
1580 return;
1584 save_p += VERSIONSIZE;
1586 // CPhipps - always check savegames even when forced,
1587 // only print a warning if forced
1588 { // killough 3/16/98: check lump name checksum (independent of order)
1589 uint_64_t checksum = 0;
1591 checksum = G_Signature();
1593 if (memcmp(&checksum, save_p, sizeof checksum)) {
1594 if (!forced_loadgame) {
1595 char *msg = malloc(strlen(save_p + sizeof checksum) + 128);
1596 strcpy(msg,"Incompatible Savegame!!!\n");
1597 if (save_p[sizeof checksum])
1598 strcat(strcat(msg,"Wads expected:\n\n"), save_p + sizeof checksum);
1599 strcat(msg, "\nAre you sure?");
1600 G_LoadGameErr(msg);
1601 free(msg);
1602 return;
1603 } else
1604 printf("G_DoLoadGame: Incompatible savegame\n");
1606 save_p += sizeof checksum;
1609 save_p += strlen(save_p)+1;
1611 /* cph - FIXME - compatibility flag? */
1612 compatibility_level = savegame_compatibility;
1613 save_p++;
1615 gameskill = *save_p++;
1616 gameepisode = *save_p++;
1617 gamemap = *save_p++;
1619 for (i=0 ; i<MAXPLAYERS ; i++)
1620 playeringame[i] = *save_p++;
1621 save_p += MIN_MAXPLAYERS-MAXPLAYERS; // killough 2/28/98
1623 idmusnum = *save_p++; // jff 3/17/98 restore idmus music
1624 if (idmusnum==255) idmusnum=-1; // jff 3/18/98 account for unsigned byte
1626 /* killough 3/1/98: Read game options
1627 * killough 11/98: move down to here
1629 save_p = (char*)G_ReadOptions(save_p);
1631 // load a base level
1632 G_InitNew (gameskill, gameepisode, gamemap);
1634 /* get the times - killough 11/98: save entire word */
1635 memcpy(&leveltime, save_p, sizeof leveltime);
1636 save_p += sizeof leveltime;
1638 /* cph - total episode time */
1639 if (compatibility_level >= prboom_2_compatibility) {
1640 memcpy(&totalleveltimes, save_p, sizeof totalleveltimes);
1641 save_p += sizeof totalleveltimes;
1643 else totalleveltimes = 0;
1645 // killough 11/98: load revenant tracer state
1646 basetic = gametic - *save_p++;
1648 // dearchive all the modifications
1649 P_UnArchivePlayers ();
1650 P_UnArchiveWorld ();
1651 P_UnArchiveThinkers ();
1652 P_UnArchiveSpecials ();
1653 P_UnArchiveRNG (); // killough 1/18/98: load RNG information
1654 P_UnArchiveMap (); // killough 1/22/98: load automap information
1656 if (*save_p != 0xe6)
1657 I_Error ("G_DoLoadGame: Bad savegame");
1659 // done
1660 Z_Free (savebuffer);
1662 if (setsizeneeded)
1663 R_ExecuteSetViewSize ();
1665 // draw the pattern into the back screen
1666 R_FillBackScreen ();
1668 /* killough 12/98: support -recordfrom and -loadgame -playdemo */
1669 if (!command_loadgame)
1670 singledemo = false; /* Clear singledemo flag if loading from menu */
1671 else
1672 if (singledemo) {
1673 gameaction = ga_loadgame; /* Mark that we're loading a game before demo */
1674 G_DoPlayDemo(); /* This will detect it and won't reinit level */
1675 } else /* Command line + record means it's a recordfrom */
1676 if (demorecording)
1677 G_BeginRecording();
1681 // G_SaveGame
1682 // Called by the menu task.
1683 // Description is a 24 byte text string
1686 void G_SaveGame(int slot, char *description)
1688 strcpy(savedescription, description);
1689 if (demoplayback) {
1690 /* cph - We're doing a user-initiated save game while a demo is
1691 * running so, go outside normal mechanisms
1693 savegameslot = slot;
1694 G_DoSaveGame(true);
1696 // CPhipps - store info in special_event
1697 special_event = BT_SPECIAL | (BTS_SAVEGAME & BT_SPECIALMASK) |
1698 ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK);
1699 #ifdef HAVE_NET
1700 D_NetSendMisc(nm_savegamename, strlen(savedescription)+1, savedescription);
1701 #endif
1704 // Check for overrun and realloc if necessary -- Lee Killough 1/22/98
1705 void CheckSaveGame(size_t size)
1707 size_t pos = save_p - savebuffer;
1708 size += 1024; // breathing room
1709 if (pos+size > savegamesize)
1710 save_p = (savebuffer = realloc(savebuffer,
1711 savegamesize += (size+1023) & ~1023)) + pos;
1714 /* killough 3/22/98: form savegame name in one location
1715 * (previously code was scattered around in multiple places)
1716 * cph - Avoid possible buffer overflow problems by passing
1717 * size to this function and using snprintf */
1719 void G_SaveGameName(char *name, size_t size, int slot, boolean demoplayback)
1721 const char* sgn = demoplayback ? "demosav" : SAVEGAMENAME;
1722 snprintf (name, size, "%s%d.dsg", sgn, slot);
1725 static void G_DoSaveGame (boolean menu)
1727 char name[100+1];
1728 char name2[VERSIONSIZE];
1729 char *description;
1730 int length, i;
1732 gameaction = ga_nothing; // cph - cancel savegame at top of this function,
1733 // in case later problems cause a premature exit
1735 G_SaveGameName(name,sizeof(name),savegameslot, demoplayback && !menu);
1737 description = savedescription;
1739 save_p = savebuffer = malloc(savegamesize);
1741 CheckSaveGame(SAVESTRINGSIZE+VERSIONSIZE+sizeof(unsigned long));
1742 memcpy (save_p, description, SAVESTRINGSIZE);
1743 save_p += SAVESTRINGSIZE;
1744 memset (name2,0,sizeof(name2));
1746 // CPhipps - scan for the version header
1747 for (i=0; (size_t)i<num_version_headers; i++)
1748 if (version_headers[i].comp_level == compatibility_level) {
1749 // killough 2/22/98: "proprietary" version string :-)
1750 snprintf (name2,sizeof(name2),version_headers[i].ver_printf,version_headers[i].version);
1751 memcpy (save_p, name2, VERSIONSIZE);
1752 i = num_version_headers+1;
1755 if ((size_t)i == num_version_headers) {
1756 doom_printf("No savegame signature known for\nthis compatibility level\n"
1757 "%d/%d, %d registered", compatibility_level,
1758 MAX_COMPATIBILITY_LEVEL, (signed)num_version_headers);
1759 free(savebuffer); // cph - free data
1760 return;
1763 save_p += VERSIONSIZE;
1765 { /* killough 3/16/98, 12/98: store lump name checksum */
1766 uint_64_t checksum = G_Signature();
1767 memcpy(save_p, &checksum, sizeof checksum);
1768 save_p += sizeof checksum;
1771 // killough 3/16/98: store pwad filenames in savegame
1773 // CPhipps - changed for new wadfiles handling
1774 int i = 0;
1775 for (*save_p = 0; (size_t)i<numwadfiles; i++)
1777 const char *const w = wadfiles[i].name;
1778 CheckSaveGame(strlen(w)+2);
1779 strcat(strcat(save_p, w), "\n");
1781 save_p += strlen(save_p)+1;
1784 CheckSaveGame(GAME_OPTION_SIZE+MIN_MAXPLAYERS+10);
1786 /* cph - FIXME? - Save compatibility level */
1787 *save_p++ = 0;
1789 *save_p++ = gameskill;
1790 *save_p++ = gameepisode;
1791 *save_p++ = gamemap;
1793 for (i=0 ; i<MAXPLAYERS ; i++)
1794 *save_p++ = playeringame[i];
1796 for (;i<MIN_MAXPLAYERS;i++) // killough 2/28/98
1797 *save_p++ = 0;
1799 *save_p++ = idmusnum; // jff 3/17/98 save idmus state
1801 save_p = G_WriteOptions(save_p); // killough 3/1/98: save game options
1803 /* cph - FIXME - endianness? */
1804 /* killough 11/98: save entire word */
1805 memcpy(save_p, &leveltime, sizeof leveltime);
1806 save_p += sizeof leveltime;
1808 /* cph - total episode time */
1809 if (compatibility_level >= prboom_2_compatibility) {
1810 memcpy(save_p, &totalleveltimes, sizeof totalleveltimes);
1811 save_p += sizeof totalleveltimes;
1813 else totalleveltimes = 0;
1815 // killough 11/98: save revenant tracer state
1816 *save_p++ = (gametic-basetic) & 255;
1818 // killough 3/22/98: add Z_CheckHeap after each call to ensure consistency
1819 Z_CheckHeap();
1820 P_ArchivePlayers();
1821 Z_CheckHeap();
1823 // phares 9/13/98: Move mobj_t->index out of P_ArchiveThinkers so the
1824 // indices can be used by P_ArchiveWorld when the sectors are saved.
1825 // This is so we can save the index of the mobj_t of the thinker that
1826 // caused a sound, referenced by sector_t->soundtarget.
1827 P_ThinkerToIndex();
1829 P_ArchiveWorld();
1830 Z_CheckHeap();
1831 P_ArchiveThinkers();
1833 // phares 9/13/98: Move index->mobj_t out of P_ArchiveThinkers, simply
1834 // for symmetry with the P_ThinkerToIndex call above.
1836 P_IndexToThinker();
1838 Z_CheckHeap();
1839 P_ArchiveSpecials();
1840 P_ArchiveRNG(); // killough 1/18/98: save RNG information
1841 Z_CheckHeap();
1842 P_ArchiveMap(); // killough 1/22/98: save automap information
1844 *save_p++ = 0xe6; // consistancy marker
1846 length = save_p - savebuffer;
1848 Z_CheckHeap();
1849 doom_printf( "%s", M_WriteFile(name, savebuffer, length)
1850 ? GGSAVED /* Ty - externalised */
1851 : "Game save failed!"); // CPhipps - not externalised
1853 free(savebuffer); // killough
1854 savebuffer = save_p = NULL;
1856 savedescription[0] = 0;
1859 static skill_t d_skill;
1860 static int d_episode;
1861 static int d_map;
1863 void G_DeferedInitNew(skill_t skill, int episode, int map)
1865 d_skill = skill;
1866 d_episode = episode;
1867 d_map = map;
1868 gameaction = ga_newgame;
1871 extern int variable_friction;
1872 extern int default_variable_friction; // ice & mud
1874 extern int weapon_recoil, default_weapon_recoil; // weapon recoil
1876 extern int allow_pushers;
1877 extern int default_allow_pushers; // MT_PUSH Things
1879 extern int player_bobbing;
1880 extern int default_player_bobbing; // whether player bobs or not
1882 extern int monsters_remember, default_monsters_remember;
1884 /* cph -
1885 * G_Compatibility
1887 * Initialises the comp[] array based on the compatibility_level
1888 * For reference, MBF did:
1889 * for (i=0; i < COMP_TOTAL; i++)
1890 * comp[i] = compatibility;
1892 * Instead, we have a lookup table showing at what version a fix was
1893 * introduced.
1896 void G_Compatibility(void)
1898 static const complevel_t fix_levels[COMP_NUM] = {
1899 mbf_compatibility, /* comp_telefrag - monsters used to telefrag only
1900 * on MAP30, now they do it for spawners only */
1901 mbf_compatibility, /* comp_dropoff - MBF encourages things to drop
1902 * off of overhangs */
1903 boom_compatibility,/* comp_vile - original Doom archville bugs like
1904 * ghosts */
1905 boom_compatibility,/* comp_pain - original Doom limits Pain Elements
1906 * from spawning too many skulls */
1907 boom_compatibility,/* comp_skull - original Doom let skulls be spit
1908 * through walls by Pain Elementals */
1909 boom_compatibility,/* comp_blazing - original Doom duplicated
1910 * blazing door sound */
1911 mbf_compatibility, /* comp_doorlight - MBF made door lighting changes
1912 * more gradual */
1913 boom_compatibility,/* comp_model - improvements to the game physics */
1914 boom_compatibility,/* comp_god - fixes to God mode */
1915 mbf_compatibility, /* comp_falloff - MBF encourages things to drop
1916 * off of overhangs */
1917 boom_compatibility_compatibility,
1918 /* comp_floors - fixes for moving floors bugs */
1919 boom_compatibility,/* comp_skymap */
1920 mbf_compatibility, /* comp_pursuit - MBF AI change, limited pursuit? */
1921 boom_compatibility,/* comp_doorstuck - monsters stuck in doors fix */
1922 mbf_compatibility, /* comp_staylift - MBF AI change, monsters try
1923 * to stay on lifts */
1924 lxdoom_1_compatibility, /* comp_zombie - prevent dead players
1925 * triggering stuff */
1926 boom_compatibility_compatibility, /* comp_stairs - see p_floor.c */
1927 mbf_compatibility, /* comp_infcheat - FIXME */
1928 boom_compatibility,/* comp_zerotags - allow zero tags in wads */
1929 lxdoom_1_compatibility, /* comp_moveblock - enables keygrab and
1930 * mancubi shots going thru walls */
1931 prboom_2_compatibility, /* comp_respawn - objects which aren't on the map
1932 * at game start respawn at (0,0) */
1933 boom_compatibility_compatibility, /* comp_sound - see s_sound.c */
1935 int i;
1936 for (i=0; i<COMP_NUM; i++)
1937 comp[i] = compatibility_level < fix_levels[i];
1938 for (; i<COMP_TOTAL; i++) comp[i] = 1;
1941 #ifdef DOGS
1942 /* killough 7/19/98: Marine's best friend :) */
1943 static int G_GetHelpers(void)
1945 int j = M_CheckParm ("-dog");
1947 if (!j)
1948 j = M_CheckParm ("-dogs");
1949 return j ? j+1 < myargc ? atoi(myargv[j+1]) : 1 : default_dogs;
1951 #endif
1954 // killough 3/1/98: function to reload all the default parameter
1955 // settings before a new game begins
1957 void G_ReloadDefaults(void)
1959 // killough 3/1/98: Initialize options based on config file
1960 // (allows functions above to load different values for demos
1961 // and savegames without messing up defaults).
1963 weapon_recoil = default_weapon_recoil; // weapon recoil
1965 player_bobbing = default_player_bobbing; // whether player bobs or not
1967 variable_friction = allow_pushers = true;
1969 monsters_remember = default_monsters_remember; // remember former enemies
1971 monster_infighting = default_monster_infighting; // killough 7/19/98
1973 #ifdef DOGS
1974 dogs = netgame ? 0 : G_GetHelpers(); // killough 7/19/98
1975 dog_jumping = default_dog_jumping;
1976 #endif
1978 distfriend = default_distfriend; // killough 8/8/98
1980 monster_backing = default_monster_backing; // killough 9/8/98
1982 monster_avoid_hazards = default_monster_avoid_hazards; // killough 9/9/98
1984 monster_friction = default_monster_friction; // killough 10/98
1986 help_friends = default_help_friends; // killough 9/9/98
1988 monkeys = default_monkeys;
1990 // jff 1/24/98 reset play mode to command line spec'd version
1991 // killough 3/1/98: moved to here
1992 // respawnparm = clrespawnparm;
1993 // fastparm = clfastparm;
1994 // nomonsters = clnomonsters;
1996 //jff 3/24/98 set startskill from defaultskill in config file, unless
1997 // it has already been set by a -skill parameter
1998 if (startskill==sk_none)
1999 startskill = (skill_t)(defaultskill-1);
2001 demoplayback = false;
2002 singledemo = false; // killough 9/29/98: don't stop after 1 demo
2003 netdemo = false;
2005 // killough 2/21/98:
2006 memset(playeringame+1, 0, sizeof(*playeringame)*(MAXPLAYERS-1));
2008 consoleplayer = 0;
2010 compatibility_level = default_compatibility_level;
2012 int i = M_CheckParm("-complevel");
2013 if (i && (1+i) < myargc) compatibility_level = atoi(myargv[i+1]);
2015 if (compatibility_level == (complevel_t)-1)
2016 compatibility_level = MAX_COMPATIBILITY_LEVEL-1;
2018 if (mbf_features)
2019 memcpy(comp, default_comp, sizeof comp);
2020 else
2021 G_Compatibility();
2023 // killough 3/31/98, 4/5/98: demo sync insurance
2024 demo_insurance = default_demo_insurance == 1;
2026 rngseed += 1 + gametic; // CPhipps
2027 // rngseed += I_GetRandomTimeSeed() + gametic; // CPhipps
2030 void G_DoNewGame (void)
2032 G_ReloadDefaults(); // killough 3/1/98
2033 netgame = false; // killough 3/1/98
2034 deathmatch = false;
2035 G_InitNew (d_skill, d_episode, d_map);
2036 gameaction = ga_nothing;
2038 //jff 4/26/98 wake up the status bar in case were coming out of a DM demo
2039 ST_Start();
2042 // killough 4/10/98: New function to fix bug which caused Doom
2043 // lockups when idclev was used in conjunction with -fast.
2045 void G_SetFastParms(int fast_pending)
2047 static int fast = 0; // remembers fast state
2048 int i;
2049 if (fast != fast_pending) { /* only change if necessary */
2050 if ((fast = fast_pending))
2052 for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++)
2053 if (states[i].tics != 1 || demo_compatibility) // killough 4/10/98
2054 states[i].tics >>= 1; // don't change 1->0 since it causes cycles
2055 mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT;
2056 mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT;
2057 mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT;
2059 else
2061 for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++)
2062 states[i].tics <<= 1;
2063 mobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT;
2064 mobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT;
2065 mobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT;
2070 // The sky texture to be used instead of the F_SKY1 dummy.
2071 extern int skytexture;
2074 // G_InitNew
2075 // Can be called by the startup code or the menu task,
2076 // consoleplayer, displayplayer, playeringame[] should be set.
2079 void G_InitNew(skill_t skill, int episode, int map)
2081 int i;
2083 if (paused)
2085 paused = false;
2086 S_ResumeSound();
2089 if (skill > sk_nightmare)
2090 skill = sk_nightmare;
2092 if (episode < 1)
2093 episode = 1;
2095 if (gamemode == retail)
2097 if (episode > 4)
2098 episode = 4;
2100 else
2101 if (gamemode == shareware)
2103 if (episode > 1)
2104 episode = 1; // only start episode 1 on shareware
2106 else
2107 if (episode > 3)
2108 episode = 3;
2110 if (map < 1)
2111 map = 1;
2112 if (map > 9 && gamemode != commercial)
2113 map = 9;
2115 G_SetFastParms(fastparm || skill == sk_nightmare); // killough 4/10/98
2117 M_ClearRandom();
2119 respawnmonsters = skill == sk_nightmare || respawnparm;
2121 // force players to be initialized upon first level load
2122 for (i=0 ; i<MAXPLAYERS ; i++)
2123 players[i].playerstate = PST_REBORN;
2125 usergame = true; // will be set false if a demo
2126 paused = false;
2127 automapmode &= ~am_active;
2128 gameepisode = episode;
2129 gamemap = map;
2130 gameskill = skill;
2132 totalleveltimes = 0; // cph
2134 //jff 4/16/98 force marks on automap cleared every new level start
2135 AM_clearMarks();
2137 G_DoLoadLevel ();
2142 // DEMO RECORDING
2145 unsigned char DEMOMARKER=0x80;
2147 void G_ReadDemoTiccmd (ticcmd_t* cmd)
2149 if (*demo_p == DEMOMARKER)
2150 G_CheckDemoStatus(); // end of demo data stream
2151 else
2153 cmd->forwardmove = ((signed char)*demo_p++);
2154 cmd->sidemove = ((signed char)*demo_p++);
2155 if (!longtics) {
2156 cmd->angleturn = ((unsigned char)*demo_p++)<<8;
2157 } else {
2158 unsigned int lowbyte = (unsigned char)*demo_p++;
2159 cmd->angleturn = (((signed int)(*demo_p++))<<8) + lowbyte;
2161 cmd->buttons = (unsigned char)*demo_p++;
2165 /* Demo limits removed -- killough
2166 * cph - record straight to file
2168 void G_WriteDemoTiccmd (ticcmd_t* cmd)
2170 char buf[5];
2171 char *p = buf;
2173 *p++ = cmd->forwardmove;
2174 *p++ = cmd->sidemove;
2175 if (!longtics) {
2176 *p++ = (cmd->angleturn+128)>>8;
2177 } else {
2178 signed short a = cmd->angleturn;
2179 *p++ = a & 0xff;
2180 *p++ = (a >> 8) & 0xff;
2182 *p++ = cmd->buttons;
2183 if (write(demofd, buf, p-buf) != p-buf)
2184 I_Error("G_WriteDemoTiccmd: error writing demo");
2186 /* cph - alias demo_p to it so we can read it back */
2187 demo_p = buf;
2188 G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same
2192 // G_RecordDemo
2195 void G_RecordDemo (const char* name)
2197 char demoname[100];
2198 usergame = false;
2199 AddDefaultExtension(strcpy(demoname, name), ".lmp"); // 1/18/98 killough
2200 demorecording = true;
2201 /* cph - Record demos straight to file
2202 * If file already exists, try to continue existing demo
2204 if (fileexists(demoname)) {
2205 demofd = open(demoname, O_WRONLY | O_APPEND);
2206 } else {
2207 demofd = open(demoname, O_WRONLY | O_RDONLY);
2208 if (demofd) {
2209 int slot = -1;
2210 int rc;
2212 byte buf[200];
2213 size_t len;
2214 read(demofd, buf, sizeof(buf));
2216 len = G_ReadDemoHeader(buf) - buf;
2217 lseek(demofd, len, SEEK_SET);
2219 do {
2220 byte buf[4];
2221 rc = read(demofd, buf, sizeof(buf));
2222 if (buf[0] == DEMOMARKER) break;
2223 if (buf[3] & BT_SPECIAL)
2224 if ((buf[3] & BT_SPECIALMASK) == BTS_SAVEGAME)
2225 slot = (buf[3] & BTS_SAVEMASK)>>BTS_SAVESHIFT;
2226 } while (rc == /* sizeof(buf) is out of scope here */ 4 );
2227 if (slot == -1) I_Error("G_RecordDemo: No save in demo, can't continue");
2228 lseek(demofd, -rc, SEEK_CUR);
2229 G_LoadGame(slot, false);
2230 autostart = false;
2233 if (!demofd) I_Error("G_RecordDemo: failed to open %s", name);
2236 // These functions are used to read and write game-specific options in demos
2237 // and savegames so that demo sync is preserved and savegame restoration is
2238 // complete. Not all options (for example "compatibility"), however, should
2239 // be loaded and saved here. It is extremely important to use the same
2240 // positions as before for the variables, so if one becomes obsolete, the
2241 // byte(s) should still be skipped over or padded with 0's.
2242 // Lee Killough 3/1/98
2244 extern int forceOldBsp;
2246 byte *G_WriteOptions(byte *demo_p)
2248 byte *target = demo_p + GAME_OPTION_SIZE;
2250 *demo_p++ = monsters_remember; // part of monster AI
2252 *demo_p++ = variable_friction; // ice & mud
2254 *demo_p++ = weapon_recoil; // weapon recoil
2256 *demo_p++ = allow_pushers; // MT_PUSH Things
2258 *demo_p++ = 0;
2260 *demo_p++ = player_bobbing; // whether player bobs or not
2262 // killough 3/6/98: add parameters to savegame, move around some in demos
2263 *demo_p++ = respawnparm;
2264 *demo_p++ = fastparm;
2265 *demo_p++ = nomonsters;
2267 *demo_p++ = demo_insurance; // killough 3/31/98
2269 // killough 3/26/98: Added rngseed. 3/31/98: moved here
2270 *demo_p++ = (byte)((rngseed >> 24) & 0xff);
2271 *demo_p++ = (byte)((rngseed >> 16) & 0xff);
2272 *demo_p++ = (byte)((rngseed >> 8) & 0xff);
2273 *demo_p++ = (byte)( rngseed & 0xff);
2275 // Options new to v2.03 begin here
2277 *demo_p++ = monster_infighting; // killough 7/19/98
2279 #ifdef DOGS
2280 *demo_p++ = dogs; // killough 7/19/98
2281 #else
2282 *demo_p++ = 0;
2283 #endif
2285 *demo_p++ = 0;
2286 *demo_p++ = 0;
2288 *demo_p++ = (distfriend >> 8) & 0xff; // killough 8/8/98
2289 *demo_p++ = distfriend & 0xff; // killough 8/8/98
2291 *demo_p++ = monster_backing; // killough 9/8/98
2293 *demo_p++ = monster_avoid_hazards; // killough 9/9/98
2295 *demo_p++ = monster_friction; // killough 10/98
2297 *demo_p++ = help_friends; // killough 9/9/98
2299 #ifdef DOGS
2300 *demo_p++ = dog_jumping;
2301 #else
2302 *demo_p++ = 0;
2303 #endif
2305 *demo_p++ = monkeys;
2307 { // killough 10/98: a compatibility vector now
2308 int i;
2309 for (i=0; i < COMP_TOTAL; i++)
2310 *demo_p++ = comp[i] != 0;
2313 *demo_p++ = (compatibility_level >= prboom_2_compatibility) && forceOldBsp; // cph 2002/07/20
2315 //----------------
2316 // Padding at end
2317 //----------------
2318 while (demo_p < target)
2319 *demo_p++ = 0;
2321 if (demo_p != target)
2322 I_Error("G_WriteOptions: GAME_OPTION_SIZE is too small");
2324 return target;
2327 /* Same, but read instead of write
2328 * cph - const byte*'s
2331 const byte *G_ReadOptions(const byte *demo_p)
2333 const byte *target = demo_p + GAME_OPTION_SIZE;
2335 monsters_remember = *demo_p++;
2337 variable_friction = *demo_p; // ice & mud
2338 demo_p++;
2340 weapon_recoil = *demo_p; // weapon recoil
2341 demo_p++;
2343 allow_pushers = *demo_p; // MT_PUSH Things
2344 demo_p++;
2346 demo_p++;
2348 player_bobbing = *demo_p; // whether player bobs or not
2349 demo_p++;
2351 // killough 3/6/98: add parameters to savegame, move from demo
2352 respawnparm = *demo_p++;
2353 fastparm = *demo_p++;
2354 nomonsters = *demo_p++;
2356 demo_insurance = *demo_p++; // killough 3/31/98
2358 // killough 3/26/98: Added rngseed to demos; 3/31/98: moved here
2360 rngseed = *demo_p++ & 0xff;
2361 rngseed <<= 8;
2362 rngseed += *demo_p++ & 0xff;
2363 rngseed <<= 8;
2364 rngseed += *demo_p++ & 0xff;
2365 rngseed <<= 8;
2366 rngseed += *demo_p++ & 0xff;
2368 // Options new to v2.03
2369 if (mbf_features)
2371 monster_infighting = *demo_p++; // killough 7/19/98
2373 #ifdef DOGS
2374 dogs = *demo_p++; // killough 7/19/98
2375 #else
2376 demo_p++;
2377 #endif
2379 demo_p += 2;
2381 distfriend = *demo_p++ << 8; // killough 8/8/98
2382 distfriend+= *demo_p++;
2384 monster_backing = *demo_p++; // killough 9/8/98
2386 monster_avoid_hazards = *demo_p++; // killough 9/9/98
2388 monster_friction = *demo_p++; // killough 10/98
2390 help_friends = *demo_p++; // killough 9/9/98
2392 #ifdef DOGS
2393 dog_jumping = *demo_p++; // killough 10/98
2394 #else
2395 demo_p++;
2396 #endif
2398 monkeys = *demo_p++;
2400 { // killough 10/98: a compatibility vector now
2401 int i;
2402 for (i=0; i < COMP_TOTAL; i++)
2403 comp[i] = *demo_p++;
2406 forceOldBsp = *demo_p++; // cph 2002/07/20
2408 else /* defaults for versions <= 2.02 */
2410 /* cph - comp[] has already been set up right by G_Compatibility */
2412 monster_infighting = 1; // killough 7/19/98
2414 monster_backing = 0; // killough 9/8/98
2416 monster_avoid_hazards = 0; // killough 9/9/98
2418 monster_friction = 0; // killough 10/98
2420 help_friends = 0; // killough 9/9/98
2422 #ifdef DOGS
2423 dogs = 0; // killough 7/19/98
2424 dog_jumping = 0; // killough 10/98
2425 #endif
2427 monkeys = 0;
2430 return target;
2433 void G_BeginRecording (void)
2435 int i;
2436 byte *demostart, *demo_p;
2437 demostart = demo_p = malloc(1000);
2439 /* cph - 3 demo record formats supported: MBF+, BOOM, and Doom v1.9 */
2440 if (mbf_features) {
2441 { /* Write version code into demo */
2442 unsigned char v=0;
2443 switch(compatibility_level) {
2444 case doom_12_compatibility:
2445 case doom_demo_compatibility:
2446 case doom_compatibility:
2447 case boom_compatibility_compatibility:
2448 case boom_201_compatibility:
2449 case boom_202_compatibility:
2450 case lxdoom_1_compatibility:
2451 case prboom_1_compatibility:
2452 case MAX_COMPATIBILITY_LEVEL:
2453 case mbf_compatibility: v = 204; break;
2454 case prboom_2_compatibility: v = 210; break;
2455 case prboom_3_compatibility: v = 211; break;
2457 *demo_p++ = v;
2460 // signature
2461 *demo_p++ = 0x1d;
2462 *demo_p++ = 'M';
2463 *demo_p++ = 'B';
2464 *demo_p++ = 'F';
2465 *demo_p++ = 0xe6;
2466 *demo_p++ = '\0';
2468 /* killough 2/22/98: save compatibility flag in new demos
2469 * cph - FIXME? MBF demos will always be not in compat. mode */
2470 *demo_p++ = 0;
2472 *demo_p++ = gameskill;
2473 *demo_p++ = gameepisode;
2474 *demo_p++ = gamemap;
2475 *demo_p++ = deathmatch;
2476 *demo_p++ = consoleplayer;
2478 demo_p = G_WriteOptions(demo_p); // killough 3/1/98: Save game options
2480 for (i=0 ; i<MAXPLAYERS ; i++)
2481 *demo_p++ = playeringame[i];
2483 // killough 2/28/98:
2484 // We always store at least MIN_MAXPLAYERS bytes in demo, to
2485 // support enhancements later w/o losing demo compatibility
2487 for (; i<MIN_MAXPLAYERS; i++)
2488 *demo_p++ = 0;
2490 } else if (compatibility_level > doom_compatibility) {
2491 byte v=0, c=0; /* Nominally, version and compatibility bits */
2492 switch (compatibility_level) {
2493 case boom_compatibility_compatibility: v = 202, c = 1; break;
2494 case boom_201_compatibility: v = 201; c = 0; break;
2495 case boom_202_compatibility: v = 202, c = 0; break;
2496 default: I_Error("G_BeginRecording: Boom compatibility level unrecognised?");
2498 *demo_p++ = v;
2500 // signature
2501 *demo_p++ = 0x1d;
2502 *demo_p++ = 'B';
2503 *demo_p++ = 'o';
2504 *demo_p++ = 'o';
2505 *demo_p++ = 'm';
2506 *demo_p++ = 0xe6;
2508 /* CPhipps - save compatibility level in demos */
2509 *demo_p++ = c;
2511 *demo_p++ = gameskill;
2512 *demo_p++ = gameepisode;
2513 *demo_p++ = gamemap;
2514 *demo_p++ = deathmatch;
2515 *demo_p++ = consoleplayer;
2517 demo_p = G_WriteOptions(demo_p); // killough 3/1/98: Save game options
2519 for (i=0 ; i<MAXPLAYERS ; i++)
2520 *demo_p++ = playeringame[i];
2522 // killough 2/28/98:
2523 // We always store at least MIN_MAXPLAYERS bytes in demo, to
2524 // support enhancements later w/o losing demo compatibility
2526 for (; i<MIN_MAXPLAYERS; i++)
2527 *demo_p++ = 0;
2528 } else { // cph - write old v1.9 demos (might even sync)
2529 longtics = M_CheckParm("-longtics");
2530 *demo_p++ = longtics ? 111 : 109; // v1.9 has best chance of syncing these
2531 *demo_p++ = gameskill;
2532 *demo_p++ = gameepisode;
2533 *demo_p++ = gamemap;
2534 *demo_p++ = deathmatch;
2535 *demo_p++ = respawnparm;
2536 *demo_p++ = fastparm;
2537 *demo_p++ = nomonsters;
2538 *demo_p++ = consoleplayer;
2539 for (i=0; i<4; i++) // intentionally hard-coded 4 -- killough
2540 *demo_p++ = playeringame[i];
2543 if (write(demofd, demostart, demo_p-demostart) != (signed)(size_t)(demo_p-demostart))
2544 I_Error("G_BeginRecording: Error writing demo header");
2545 free(demostart);
2550 // G_PlayDemo
2553 static const char *defdemoname;
2555 void G_DeferedPlayDemo (const char* name)
2557 defdemoname = name;
2558 gameaction = ga_playdemo;
2561 static int demolumpnum = -1;
2563 static const byte* G_ReadDemoHeader(const byte *demo_p)
2565 skill_t skill;
2566 int i, episode, map;
2567 int demover;
2568 const byte *option_p = NULL; /* killough 11/98 */
2570 basetic = gametic; // killough 9/29/98
2572 // killough 2/22/98, 2/28/98: autodetect old demos and act accordingly.
2573 // Old demos turn on demo_compatibility => compatibility; new demos load
2574 // compatibility flag, and other flags as well, as a part of the demo.
2576 demover = *demo_p++;
2578 if (demover < 200) // Autodetect old demos
2580 compatibility_level = doom_demo_compatibility;
2581 if (demover >= 111) longtics = 1;
2583 G_Compatibility();
2585 // killough 3/2/98: force these variables to be 0 in demo_compatibility
2587 variable_friction = 0;
2589 weapon_recoil = 0;
2591 allow_pushers = 0;
2593 monster_infighting = 1; // killough 7/19/98
2595 #ifdef DOGS
2596 dogs = 0; // killough 7/19/98
2597 dog_jumping = 0; // killough 10/98
2598 #endif
2600 monster_backing = 0; // killough 9/8/98
2602 monster_avoid_hazards = 0; // killough 9/9/98
2604 monster_friction = 0; // killough 10/98
2605 help_friends = 0; // killough 9/9/98
2606 monkeys = 0;
2608 // killough 3/6/98: rearrange to fix savegame bugs (moved fastparm,
2609 // respawnparm, nomonsters flags to G_LoadOptions()/G_SaveOptions())
2611 if ((skill=demover) >= 100) // For demos from versions >= 1.4
2613 skill = *demo_p++;
2614 episode = *demo_p++;
2615 map = *demo_p++;
2616 deathmatch = *demo_p++;
2617 respawnparm = *demo_p++;
2618 fastparm = *demo_p++;
2619 nomonsters = *demo_p++;
2620 consoleplayer = *demo_p++;
2622 else
2624 episode = *demo_p++;
2625 map = *demo_p++;
2626 deathmatch = respawnparm = fastparm =
2627 nomonsters = consoleplayer = 0;
2630 else // new versions of demos
2632 demo_p += 6; // skip signature;
2633 switch (demover) {
2634 case 200: /* BOOM */
2635 case 201:
2636 if (!*demo_p++)
2637 compatibility_level = boom_201_compatibility;
2638 else
2639 compatibility_level = boom_compatibility_compatibility;
2640 break;
2641 case 202:
2642 if (!*demo_p++)
2643 compatibility_level = boom_202_compatibility;
2644 else
2645 compatibility_level = boom_compatibility_compatibility;
2646 break;
2647 case 203:
2648 /* LxDoom or MBF - determine from signature
2649 * cph - load compatibility level */
2650 switch (demobuffer[2]) {
2651 case 'B': /* LxDoom */
2652 /* cph - DEMOSYNC - LxDoom demos recorded in compatibility modes support dropped */
2653 compatibility_level = lxdoom_1_compatibility;
2654 break;
2655 case 'M':
2656 compatibility_level = mbf_compatibility;
2657 demo_p++;
2658 break;
2660 break;
2661 case 210:
2662 compatibility_level = prboom_2_compatibility;
2663 demo_p++;
2664 break;
2665 case 211:
2666 compatibility_level = prboom_3_compatibility;
2667 demo_p++;
2668 break;
2670 G_Compatibility();
2671 skill = *demo_p++;
2672 episode = *demo_p++;
2673 map = *demo_p++;
2674 deathmatch = *demo_p++;
2675 consoleplayer = *demo_p++;
2677 /* killough 11/98: save option pointer for below */
2678 if (mbf_features)
2679 option_p = demo_p;
2681 demo_p = G_ReadOptions(demo_p); // killough 3/1/98: Read game options
2683 if (demover == 200) // killough 6/3/98: partially fix v2.00 demos
2684 demo_p += 128-GAME_OPTION_SIZE;
2687 // printf( "G_DoPlayDemo: playing demo with %s compatibility\n",
2688 // comp_lev_str[compatibility_level]);
2690 if (demo_compatibility) // only 4 players can exist in old demos
2692 for (i=0; i<4; i++) // intentionally hard-coded 4 -- killough
2693 playeringame[i] = *demo_p++;
2694 for (;i < MAXPLAYERS; i++)
2695 playeringame[i] = 0;
2697 else
2699 for (i=0 ; i < MAXPLAYERS; i++)
2700 playeringame[i] = *demo_p++;
2701 demo_p += MIN_MAXPLAYERS - MAXPLAYERS;
2704 if (playeringame[1])
2706 netgame = true;
2707 netdemo = true;
2710 if (gameaction != ga_loadgame) { /* killough 12/98: support -loadgame */
2711 G_InitNew(skill, episode, map);
2714 for (i=0; i<MAXPLAYERS;i++) // killough 4/24/98
2715 players[i].cheats = 0;
2717 return demo_p;
2720 void G_DoPlayDemo(void)
2722 char basename[9];
2724 ExtractFileBase(defdemoname,basename); // killough
2725 basename[8] = 0;
2726 demobuffer = demo_p = W_CacheLumpNum(demolumpnum = W_GetNumForName(basename));
2727 /* cph - store lump number for unlocking later */
2729 demo_p = G_ReadDemoHeader(demo_p);
2731 gameaction = ga_nothing;
2732 usergame = false;
2734 demoplayback = true;
2738 // G_TimeDemo
2741 void G_TimeDemo(const char *name) // CPhipps - const char*
2743 timingdemo = true;
2744 singletics = true;
2745 defdemoname = name;
2746 gameaction = ga_playdemo;
2750 /* G_CheckDemoStatus
2752 * Called after a death or level completion to allow demos to be cleaned up
2753 * Returns true if a new demo loop action will take place
2756 boolean G_CheckDemoStatus (void)
2758 if (demorecording)
2760 demorecording = false;
2761 write(demofd, &DEMOMARKER, 1);
2762 close(demofd);
2763 I_Error("G_CheckDemoStatus: Demo recorded");
2764 return false; // killough
2767 if (timingdemo)
2769 // int endtime = I_GetTime_RealTime ();
2770 int endtime = I_GetTime ();
2771 // killough -- added fps information and made it work for longer demos:
2772 unsigned realtics = endtime-starttime;
2773 int fd=open(GAMEBASE "timedemo.txt",O_WRONLY | O_CREAT);
2774 fdprintf (fd,"Timed %d gametics in %d realtics = %d frames per second",
2775 (unsigned) gametic, realtics,
2776 (unsigned) gametic * TICRATE/ realtics);
2777 close(fd);
2778 I_Error ("%d gametics in %d realtics",
2779 (unsigned) gametic,realtics,
2780 (unsigned) gametic * TICRATE / realtics);
2781 return false;
2784 if (demoplayback)
2786 if (singledemo)
2787 I_Error("Done Playing Demo");
2788 return false;
2789 // exit(0); // killough
2791 if (demolumpnum != -1) {
2792 // cph - unlock the demo lump
2793 W_UnlockLumpNum(demolumpnum);
2794 demolumpnum = -1;
2796 G_ReloadDefaults(); // killough 3/1/98
2797 netgame = false; // killough 3/29/98
2798 deathmatch = false;
2799 D_AdvanceDemo ();
2800 return true;
2802 return false;
2805 // killough 1/22/98: this is a "Doom printf" for messages. I've gotten
2806 // tired of using players->message=... and so I've added this dprintf.
2808 // killough 3/6/98: Made limit static to allow z_zone functions to call
2809 // this function, without calling realloc(), which seems to cause problems.
2811 #define MAX_MESSAGE_SIZE 1024
2812 static char msg[MAX_MESSAGE_SIZE];
2814 // CPhipps - renamed to doom_printf to avoid name collision with glibc
2815 void doom_printf(const char *s, ...)
2817 va_list v;
2818 va_start(v,s);
2819 vsnprintf(msg,sizeof(msg),s,v); /* print message in buffer */
2820 va_end(v);
2821 players[consoleplayer].message = msg; // set new message