]> icculus.org git repositories - theoddone33/hhexen.git/blob - base/g_game.c
Round 5: Warn those using 1.0 wadfiles to quit it.
[theoddone33/hhexen.git] / base / g_game.c
1
2 //**************************************************************************
3 //**
4 //** g_game.c : Heretic 2 : Raven Software, Corp.
5 //**
6 //** $RCSfile$
7 //** $Revision$
8 //** $Date$
9 //** $Author$
10 //**
11 //**************************************************************************
12
13 #include <string.h>
14 #include "h2def.h"
15 #include "p_local.h"
16 #include "soundst.h"
17
18 #define AM_STARTKEY     9
19
20 // External functions
21
22 extern void R_InitSky(int map);
23 extern void P_PlayerNextArtifact(player_t *player);
24
25 // Functions
26
27 boolean G_CheckDemoStatus (void);
28 void G_ReadDemoTiccmd (ticcmd_t *cmd);
29 void G_WriteDemoTiccmd (ticcmd_t *cmd);
30 void G_InitNew (skill_t skill, int episode, int map);
31
32 void G_DoReborn (int playernum);
33
34 void G_DoLoadLevel(void);
35 void G_DoInitNew(void);
36 void G_DoNewGame(void);
37 void G_DoLoadGame(void);
38 void G_DoPlayDemo(void);
39 void G_DoTeleportNewMap(void);
40 void G_DoCompleted(void);
41 void G_DoVictory(void);
42 void G_DoWorldDone(void);
43 void G_DoSaveGame(void);
44 void G_DoSingleReborn(void);
45
46 void H2_PageTicker(void);
47 void H2_AdvanceDemo(void);
48
49 extern boolean mn_SuicideConsole;
50
51 gameaction_t    gameaction;
52 gamestate_t     gamestate;
53 skill_t         gameskill;
54 //boolean         respawnmonsters;
55 int             gameepisode;
56 int             gamemap;
57 int                              prevmap;
58
59 boolean         paused;
60 boolean         sendpause;              // send a pause event next tic
61 boolean         sendsave;               // send a save event next tic
62 boolean         usergame;               // ok to save / end game
63
64 boolean         timingdemo;             // if true, exit with report on completion
65 int             starttime;              // for comparative timing purposes      
66
67 boolean         viewactive;
68
69 boolean         deathmatch;             // only if started as net death
70 boolean         netgame;                // only true if packets are broadcast
71 boolean         playeringame[MAXPLAYERS];
72 player_t        players[MAXPLAYERS];
73 pclass_t                PlayerClass[MAXPLAYERS];
74
75 // Position indicator for cooperative net-play reborn
76 int RebornPosition;
77
78 int             consoleplayer;          // player taking events and displaying
79 int             displayplayer;          // view being displayed
80 int             gametic;
81 int             levelstarttic;          // gametic at level start
82
83 char            demoname[32];
84 boolean         demorecording;
85 boolean         demoplayback;
86 byte            *demobuffer, *demo_p;
87 boolean         singledemo;             // quit after playing a demo from cmdline
88
89 boolean         precache = true;        // if true, load all graphics at start
90
91 short            consistancy[MAXPLAYERS][BACKUPTICS];
92
93 //
94 // controls (have defaults)
95 //
96 int key_right, key_left, key_up, key_down;
97 int key_strafeleft, key_straferight, key_jump;
98 int key_fire, key_use, key_strafe, key_speed;
99 int     key_flyup, key_flydown, key_flycenter;
100 int     key_lookup, key_lookdown, key_lookcenter;
101 int     key_invleft, key_invright, key_useartifact;
102
103 int mouselook;
104 boolean alwaysrun;
105
106 int mousebfire;
107 int mousebstrafe;
108 int mousebforward;
109 int mousebjump;
110
111 int joybfire;
112 int joybstrafe;
113 int joybuse;
114 int joybspeed;
115 int joybjump;
116
117 int LeaveMap;
118 static int LeavePosition;
119
120 //#define MAXPLMOVE       0x32 // Old Heretic Max move
121
122 fixed_t MaxPlayerMove[NUMCLASSES] = { 0x3C, 0x32, 0x2D,
123                                         0x3D,
124                                         0x31 };
125 fixed_t forwardmove[NUMCLASSES][2] = 
126 {
127         { 0x1D, 0x3C },
128         { 0x19, 0x32 },
129         { 0x16, 0x2E },
130         { 0x17, 0x3D },
131         { 0x18, 0x31 }
132 };
133         
134 fixed_t sidemove[NUMCLASSES][2] = 
135 {
136         { 0x1B, 0x3B },
137         { 0x18, 0x28 },
138         { 0x15, 0x25 },
139         { 0x16, 0x3C },
140         { 0x17, 0x27 }
141 };
142
143 fixed_t angleturn[3] = {640, 1280, 320};     // + slow turn
144 #define SLOWTURNTICS    6
145
146 #define NUMKEYS 256
147 boolean         gamekeydown[NUMKEYS];
148 int             turnheld;                   // for accelerative turning
149 int                              lookheld;
150
151
152 boolean         mousearray[4];
153 boolean         *mousebuttons = &mousearray[1];
154
155         // allow [-1]
156 int             mousex, mousey;             // mouse values are used once
157 int             dclicktime, dclickstate, dclicks;
158 int             dclicktime2, dclickstate2, dclicks2;
159
160 int             joyxmove, joyymove;         // joystick values are repeated
161 boolean         joyarray[5];
162 boolean         *joybuttons = &joyarray[1];     // allow [-1]
163
164 int     savegameslot;
165 char    savedescription[32];
166
167 int inventoryTics;
168
169
170 static skill_t TempSkill;
171 static int TempEpisode;
172 static int TempMap;
173
174 //=============================================================================
175 /*
176 ====================
177 =
178 = G_BuildTiccmd
179 =
180 = Builds a ticcmd from all of the available inputs or reads it from the
181 = demo buffer.
182 = If recording a demo, write it out
183 ====================
184 */
185
186 extern boolean inventory;
187 extern int curpos;
188 extern int inv_ptr;
189
190 extern  int             isCyberPresent;     // is CyberMan present?
191 boolean usearti = true;
192 void I_ReadCyberCmd (ticcmd_t *cmd);
193
194 void G_BuildTiccmd (ticcmd_t *cmd)
195 {
196         int             i;
197         boolean         strafe, bstrafe;
198         int             speed, tspeed, lspeed;
199         int             forward, side;
200         int look, arti;
201         int flyheight;
202         int pClass;
203
204         extern boolean artiskip;
205
206
207         pClass = players[consoleplayer].class;
208         memset (cmd,0,sizeof(*cmd));
209
210 //      cmd->consistancy =
211 //              consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
212
213         cmd->consistancy =
214                 consistancy[consoleplayer][maketic%BACKUPTICS];
215         if (isCyberPresent)
216                 I_ReadCyberCmd (cmd);
217
218 //printf ("cons: %i\n",cmd->consistancy);
219         strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
220                 || joybuttons[joybstrafe];
221
222         speed = gamekeydown[key_speed] || joybuttons[joybspeed]
223                 || joybuttons[joybspeed];
224         if(alwaysrun && !demoplayback && !demorecording)
225         speed = !speed;
226         forward = side = look = arti = flyheight = 0;
227         
228 //
229 // use two stage accelerative turning on the keyboard and joystick
230 //
231         if (joyxmove < 0 || joyxmove > 0 
232         || gamekeydown[key_right] || gamekeydown[key_left])
233                 turnheld += ticdup;
234         else
235                 turnheld = 0;
236         if (turnheld < SLOWTURNTICS)
237                 tspeed = 2;             // slow turn
238         else
239                 tspeed = speed;
240
241         if(gamekeydown[key_lookdown] || gamekeydown[key_lookup])
242         {
243                 lookheld += ticdup;
244         }
245         else
246         {
247                 lookheld = 0;
248         }
249         if(lookheld < SLOWTURNTICS)
250         {
251                 lspeed = 1; // 3;
252         }
253         else
254         {
255                 lspeed = 2; // 5;
256         }
257
258 //
259 // let movement keys cancel each other out
260 //
261         if(strafe)
262         {
263                 if (gamekeydown[key_right])
264                 {
265                         side += sidemove[pClass][speed];
266                 }
267                 if (gamekeydown[key_left])
268                 {
269                         side -= sidemove[pClass][speed];
270                 }
271                 if (joyxmove > 0)
272                 {
273                         side += sidemove[pClass][speed];
274                 }
275                 if (joyxmove < 0)
276                 {
277                         side -= sidemove[pClass][speed];
278                 }
279         }
280         else
281         {
282                 if (gamekeydown[key_right])
283                         cmd->angleturn -= angleturn[tspeed];
284                 if (gamekeydown[key_left])
285                         cmd->angleturn += angleturn[tspeed];
286                 if (joyxmove > 0)
287                         cmd->angleturn -= angleturn[tspeed];
288                 if (joyxmove < 0)
289                         cmd->angleturn += angleturn[tspeed];
290         }
291
292         if (gamekeydown[key_up])
293         {
294                 forward += forwardmove[pClass][speed];
295         }
296         if (gamekeydown[key_down])
297         {
298                 forward -= forwardmove[pClass][speed];
299         }
300         if (joyymove < 0)
301         {
302                 forward += forwardmove[pClass][speed];
303         }
304         if (joyymove > 0)
305         {
306                 forward -= forwardmove[pClass][speed];
307         }
308         if (gamekeydown[key_straferight])
309         {
310                 side += sidemove[pClass][speed];
311         }
312         if (gamekeydown[key_strafeleft])
313         {
314                 side -= sidemove[pClass][speed];
315         }
316
317         // Look up/down/center keys
318         if(gamekeydown[key_lookup])
319         {
320                 look = lspeed;
321         }
322         if(gamekeydown[key_lookdown])
323         {
324                 look = -lspeed;
325         } 
326         if(gamekeydown[key_lookcenter])
327         {
328                 look = TOCENTER;
329         }
330
331         // Fly up/down/drop keys
332         if(gamekeydown[key_flyup])
333         {
334                 flyheight = 5; // note that the actual flyheight will be twice this
335         }
336         if(gamekeydown[key_flydown])
337         {
338                 flyheight = -5;
339         }
340         if(gamekeydown[key_flycenter])
341         {
342                 flyheight = TOCENTER; 
343                 look = TOCENTER; 
344         }
345         // Use artifact key
346         if(gamekeydown[key_useartifact])
347         {
348                 if(gamekeydown[key_speed] && artiskip)
349                 {
350                         if(players[consoleplayer].inventory[inv_ptr].type != arti_none)
351                         { // Skip an artifact
352                                 gamekeydown[key_useartifact] = false;
353                                 P_PlayerNextArtifact(&players[consoleplayer]);                  
354                         }
355                 }
356                 else
357                 {
358                         if(inventory)
359                         {
360                                 players[consoleplayer].readyArtifact =
361                                         players[consoleplayer].inventory[inv_ptr].type;
362                                 inventory = false;
363                                 cmd->arti = 0;
364                                 usearti = false;
365                         }
366                         else if(usearti)
367                         {
368                                 cmd->arti |= 
369                                         players[consoleplayer].inventory[inv_ptr].type&AFLAG_MASK;
370                                 usearti = false;
371                         }
372                 }
373         }
374         if(gamekeydown[key_jump] || mousebuttons[mousebjump]
375                 || joybuttons[joybjump])
376         {
377                 cmd->arti |= AFLAG_JUMP;
378         }
379         if(mn_SuicideConsole)
380         {
381                 cmd->arti |= AFLAG_SUICIDE;
382                 mn_SuicideConsole = false;
383         }
384
385         // Artifact hot keys
386         if(gamekeydown[KEY_BACKSPACE] && !cmd->arti)
387         {
388                 gamekeydown[KEY_BACKSPACE] = false;     // Use one of each artifact
389                 cmd->arti = NUMARTIFACTS;
390         }
391         else if(gamekeydown[KEY_BACKSLASH] && !cmd->arti 
392         && (players[consoleplayer].mo->health < MAXHEALTH))
393         {
394                 gamekeydown[KEY_BACKSLASH] = false;
395                 cmd->arti = arti_health;                                                
396         }
397         else if(gamekeydown[KEY_ZERO] && !cmd->arti)
398         {
399         gamekeydown[KEY_ZERO] = false;
400                 cmd->arti = arti_poisonbag;                                             
401         }
402         else if(gamekeydown[KEY_NINE] && !cmd->arti)
403         {
404                 gamekeydown[KEY_NINE] = false;
405                 cmd->arti = arti_blastradius;                                   
406         }
407         else if(gamekeydown[KEY_EIGHT] && !cmd->arti)
408         {
409                 gamekeydown[KEY_EIGHT] = false;
410                 cmd->arti = arti_teleport;                                              
411         }
412         else if(gamekeydown[KEY_SEVEN] && !cmd->arti)
413         {
414                 gamekeydown[KEY_SEVEN] = false;
415                 cmd->arti = arti_teleportother;                                         
416         }
417         else if(gamekeydown[KEY_SIX] && !cmd->arti)
418         {
419                 gamekeydown[KEY_SIX] = false;
420                 cmd->arti = arti_egg;                                           
421         }
422         else if(gamekeydown[KEY_FIVE] && !cmd->arti
423         && !players[consoleplayer].powers[pw_invulnerability])
424         {
425                 gamekeydown[KEY_FIVE] = false;
426                 cmd->arti = arti_invulnerability;                               
427         }
428
429 //
430 // buttons
431 //
432         cmd->chatchar = CT_dequeueChatChar();
433         if (gamekeydown[key_fire] || mousebuttons[mousebfire]
434                 || joybuttons[joybfire])
435                 cmd->buttons |= BT_ATTACK;
436
437         if (gamekeydown[key_use] || joybuttons[joybuse] )
438         {
439                 cmd->buttons |= BT_USE;
440                 dclicks = 0;                    // clear double clicks if hit use button
441         }
442         
443         for(i = 0; i < NUMWEAPONS; i++)
444         {
445                 if(gamekeydown['1'+i])
446                 {
447                         cmd->buttons |= BT_CHANGE;
448                         cmd->buttons |= i<<BT_WEAPONSHIFT;
449                         break;
450                 }
451         }
452
453 //
454 // mouse
455 //
456 #if 0
457     printf( "%d %d %d %d <%d %d>\n",
458             mousebuttons[mousebfire], mousebuttons[mousebforward],
459             mousebuttons[mousebjump], mousebuttons[mousebstrafe],
460             mousex, mousey );
461 #endif
462         if (mousebuttons[mousebforward])
463         {
464                 forward += forwardmove[pClass][speed];
465         }
466                 
467 //
468 // forward double click
469 //
470         if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
471         {
472                 dclickstate = mousebuttons[mousebforward];
473                 if (dclickstate)
474                         dclicks++;
475                 if (dclicks == 2)
476                 {
477                         cmd->buttons |= BT_USE;
478                         dclicks = 0;
479                 }
480                 else
481                         dclicktime = 0;
482         }
483         else
484         {
485                 dclicktime += ticdup;
486                 if (dclicktime > 20)
487                 {
488                         dclicks = 0;
489                         dclickstate = 0;
490                 }
491         }
492         
493 //
494 // strafe double click
495 //
496         bstrafe = mousebuttons[mousebstrafe]
497 || joybuttons[joybstrafe];
498         if (bstrafe != dclickstate2 && dclicktime2 > 1 )
499         {
500                 dclickstate2 = bstrafe;
501                 if (dclickstate2)
502                         dclicks2++;
503                 if (dclicks2 == 2)
504                 {
505                         cmd->buttons |= BT_USE;
506                         dclicks2 = 0;
507                 }
508                 else
509                         dclicktime2 = 0;
510         }
511         else
512         {
513                 dclicktime2 += ticdup;
514                 if (dclicktime2 > 20)
515                 {
516                         dclicks2 = 0;
517                         dclickstate2 = 0;
518                 }
519         }
520         if (strafe)
521         {
522                 side += mousex*2;
523         }
524         else
525         {
526                 cmd->angleturn -= mousex*0x8;
527         }       
528
529     if( demorecording || demoplayback || (mouselook == 0) )
530     {
531         forward += mousey;
532     }
533     else if( mousey )
534     {
535         // We'll directly change the viewing pitch of the console player.
536         float adj = ((mousey*0x4)<<16) / (float) ANGLE_180*180*110.0/85.0;
537         float newlookdir = 0; /* jim initialiser added to prevent warning */
538
539         adj *= 2;       //Speed up the X11 mlook a little.
540
541         if(mouselook == 1)
542                 newlookdir = players[consoleplayer].lookdir + adj;
543         else if(mouselook == 2)
544                 newlookdir = players[consoleplayer].lookdir - adj;
545
546         // vertical view angle taken from p_user.c line 249.
547         if( newlookdir > 90 )
548             newlookdir = 90;
549         if( newlookdir < -110 )
550             newlookdir = -110;
551
552         players[consoleplayer].lookdir = newlookdir;
553     }
554
555         mousex = mousey = 0;
556         
557         if (forward > MaxPlayerMove[pClass])
558         {
559                 forward = MaxPlayerMove[pClass];
560         }
561         else if (forward < -MaxPlayerMove[pClass])
562         {
563                 forward = -MaxPlayerMove[pClass];
564         }
565         if (side > MaxPlayerMove[pClass])
566         {
567                 side = MaxPlayerMove[pClass];
568         }
569         else if (side < -MaxPlayerMove[pClass])
570         {
571                 side = -MaxPlayerMove[pClass];
572         }
573         if(players[consoleplayer].powers[pw_speed]
574                 && !players[consoleplayer].morphTics)
575         { // Adjust for a player with a speed artifact
576                 forward = (3*forward)>>1;
577                 side = (3*side)>>1;
578         }
579         cmd->forwardmove += forward;
580         cmd->sidemove += side;
581         if(players[consoleplayer].playerstate == PST_LIVE)
582         {
583                 if(look < 0)
584                 {
585                         look += 16;
586                 }
587                 cmd->lookfly = look;
588         }
589         if(flyheight < 0)
590         {
591                 flyheight += 16;
592         }
593         cmd->lookfly |= flyheight<<4;
594
595 //
596 // special buttons
597 //
598         if (sendpause)
599         {
600                 sendpause = false;
601                 cmd->buttons = BT_SPECIAL | BTS_PAUSE;
602         }
603
604         if (sendsave)
605         {
606                 sendsave = false;
607                 cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT);
608         }
609 }
610
611
612 /*
613 ==============
614 =
615 = G_DoLoadLevel
616 =
617 ==============
618 */
619
620 void G_DoLoadLevel (void)
621 {
622         int             i;
623         
624         levelstarttic = gametic;        // for time calculation 
625         gamestate = GS_LEVEL;
626         for (i=0 ; i<MAXPLAYERS ; i++)
627         {
628                 if (playeringame[i] && players[i].playerstate == PST_DEAD)
629                         players[i].playerstate = PST_REBORN;
630                 memset (players[i].frags,0,sizeof(players[i].frags));
631         }
632
633         SN_StopAllSequences();  
634         P_SetupLevel (gameepisode, gamemap, 0, gameskill);   
635         displayplayer = consoleplayer;      // view the guy you are playing   
636         starttime = I_GetTime ();
637         gameaction = ga_nothing;
638         Z_CheckHeap ();
639
640 //
641 // clear cmd building stuff
642 // 
643
644         memset (gamekeydown, 0, sizeof(gamekeydown));
645         joyxmove = joyymove = 0;
646         mousex = mousey = 0;
647         sendpause = sendsave = paused = false;
648         memset (mousebuttons, 0, sizeof(mousebuttons));
649         memset (joybuttons, 0, sizeof(joybuttons));
650 }
651
652
653 /*
654 ===============================================================================
655 =
656 = G_Responder 
657 =
658 = get info needed to make ticcmd_ts for the players
659 =
660 ===============================================================================
661 */
662
663 boolean G_Responder(event_t *ev)
664 {
665         player_t *plr;
666         extern boolean MenuActive;
667
668         plr = &players[consoleplayer];
669         if(ev->type == ev_keyup && ev->data1 == key_useartifact)
670         { // flag to denote that it's okay to use an artifact
671                 if(!inventory)
672                 {
673                         plr->readyArtifact = plr->inventory[inv_ptr].type;
674                 }
675                 usearti = true;
676         }
677
678         // Check for spy mode player cycle
679         if(gamestate == GS_LEVEL && ev->type == ev_keydown
680                 && ev->data1 == KEY_F12 && !deathmatch)
681         { // Cycle the display player
682                 do
683                 {
684                         displayplayer++;
685                         if(displayplayer == MAXPLAYERS)
686                         {
687                                 displayplayer = 0;
688                         }
689                 } while(!playeringame[displayplayer]
690                         && displayplayer != consoleplayer);
691                 return(true);
692         }
693
694         if(CT_Responder(ev))
695         { // Chat ate the event
696                 return(true);
697         }
698         if(gamestate == GS_LEVEL)
699         {
700                 if(SB_Responder(ev))
701                 { // Status bar ate the event
702                         return(true);
703                 }
704                 if(AM_Responder(ev))
705                 { // Automap ate the event
706                         return(true);
707                 }
708         }
709
710         switch(ev->type)
711         {
712                 case ev_keydown:
713                         if(ev->data1 == key_invleft)
714                         {
715                                 inventoryTics = 5*35;
716                                 if(!inventory)
717                                 {
718                                         inventory = true;
719                                         break;
720                                 }
721                                 inv_ptr--;
722                                 if(inv_ptr < 0)
723                                 {
724                                         inv_ptr = 0;
725                                 }
726                                 else
727                                 {
728                                         curpos--;
729                                         if(curpos < 0)
730                                         {
731                                                 curpos = 0;
732                                         }
733                                 }
734                                 return(true);
735                         }
736                         if(ev->data1 == key_invright)
737                         {
738                                 inventoryTics = 5*35;
739                                 if(!inventory)
740                                 {
741                                         inventory = true;
742                                         break;
743                                 }
744                                 inv_ptr++;
745                                 if(inv_ptr >= plr->inventorySlotNum)
746                                 {
747                                         inv_ptr--;
748                                         if(inv_ptr < 0)
749                                                 inv_ptr = 0;
750                                 }
751                                 else
752                                 {
753                                         curpos++;
754                                         if(curpos > 6)
755                                         {
756                                                 curpos = 6;
757                                         }
758                                 }
759                                 return(true);
760                         }
761                         if(ev->data1 == KEY_PAUSE && !MenuActive)
762                         {
763                                 sendpause = true;
764                                 return(true);
765                         }
766                         if(ev->data1 < NUMKEYS)
767                         {
768                                 gamekeydown[ev->data1] = true;
769                         }
770                         return(true); // eat key down events
771
772                 case ev_keyup:
773                         if(ev->data1 < NUMKEYS)
774                         {
775                                 gamekeydown[ev->data1] = false;
776                         }
777                         return(false); // always let key up events filter down
778
779                 case ev_mouse:
780
781                         mousebuttons[0] = ev->data1&1;
782                         mousebuttons[1] = ev->data1&2;
783                         mousebuttons[2] = ev->data1&4;
784                         mousex = ev->data2*(mouseSensitivity+5)/10;
785                         mousey = ev->data3*(mouseSensitivity+5)/10;
786                         return(true); // eat events
787
788                 case ev_joystick:
789                         joybuttons[0] = ev->data1&1;
790                         joybuttons[1] = ev->data1&2;
791                         joybuttons[2] = ev->data1&4;
792                         joybuttons[3] = ev->data1&8;
793                         joyxmove = ev->data2;
794                         joyymove = ev->data3;
795                         return(true); // eat events
796
797                 default:
798                         break;
799         }
800         return(false);
801 }
802
803
804 //==========================================================================
805 //
806 // G_Ticker
807 //
808 //==========================================================================
809
810 void G_Ticker(void)
811 {
812         int i, buf;
813         ticcmd_t *cmd=NULL;
814
815 //
816 // do player reborns if needed
817 //
818         for (i=0 ; i<MAXPLAYERS ; i++)
819                 if (playeringame[i] && players[i].playerstate == PST_REBORN)
820                         G_DoReborn (i);
821
822 //
823 // do things to change the game state
824 //
825         while (gameaction != ga_nothing)
826         {
827                 switch (gameaction)
828                 {
829                         case ga_loadlevel:
830                                 G_DoLoadLevel();
831                                 break;
832                         case ga_initnew:
833                                 G_DoInitNew();
834                                 break;
835                         case ga_newgame:
836                                 G_DoNewGame();
837                                 break;
838                         case ga_loadgame:
839                                 Draw_LoadIcon();
840                                 G_DoLoadGame();
841                                 break;
842                         case ga_savegame:
843                                 Draw_SaveIcon();
844                                 G_DoSaveGame();
845                                 break;
846                         case ga_singlereborn:
847                                 G_DoSingleReborn();
848                                 break;
849                         case ga_playdemo:
850                                 G_DoPlayDemo();
851                                 break;
852                         case ga_screenshot:
853                                 M_ScreenShot();
854                                 gameaction = ga_nothing;
855                                 break;
856                         case ga_leavemap:
857                                 Draw_TeleportIcon();
858                                 G_DoTeleportNewMap();
859                                 break;
860                         case ga_completed:
861                                 G_DoCompleted();
862                                 break;
863                         case ga_worlddone:
864                                 G_DoWorldDone();
865                                 break;
866                         case ga_victory:
867                                 F_StartFinale();
868                                 break;
869                         default:
870                                 break;
871                 }
872         }
873         
874                         
875 //
876 // get commands, check consistancy, and build new consistancy check
877 //
878         //buf = gametic%BACKUPTICS;
879
880
881         buf = (gametic/ticdup)%BACKUPTICS;
882
883         for (i=0 ; i<MAXPLAYERS ; i++)
884                 if (playeringame[i])
885                 {
886                         cmd = &players[i].cmd;
887
888                         memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t));
889
890                         if (demoplayback)
891                                 G_ReadDemoTiccmd (cmd);
892                         if (demorecording)
893                                 G_WriteDemoTiccmd (cmd);
894
895                         if (netgame && !(gametic%ticdup) )
896                         {
897                                 if (gametic > BACKUPTICS
898                                 && consistancy[i][buf] != cmd->consistancy)
899                                 {
900                                         I_Error ("consistency failure (%i should be %i)",cmd->consistancy, consistancy[i][buf]);
901                                 }
902                                 if (players[i].mo)
903                                         consistancy[i][buf] = players[i].mo->x;
904                                 else
905                                         consistancy[i][buf] = rndindex;
906                         }
907                 }
908
909 //
910 // check for special buttons
911 //
912         for (i=0 ; i<MAXPLAYERS ; i++)
913                 if (playeringame[i])
914                 {
915                         if (players[i].cmd.buttons & BT_SPECIAL)
916                         {
917                                 switch (players[i].cmd.buttons & BT_SPECIALMASK)
918                                 {
919                                 case BTS_PAUSE:
920                                         paused ^= 1;
921                                         if(paused)
922                                         {
923                                                 S_PauseSound();
924                                         }
925                                         else
926                                         {
927                                                 S_ResumeSound();
928                                         }
929                                         break;
930                                         
931                                 case BTS_SAVEGAME:
932                                         if (!savedescription[0])
933                                         {
934                                                 if(netgame)
935                                                 {
936                                                         strcpy (savedescription, "NET GAME");
937                                                 }
938                                                 else
939                                                 {
940                                                         strcpy(savedescription, "SAVE GAME");
941                                                 }
942                                         }
943                                         savegameslot = 
944                                                 (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
945                                         gameaction = ga_savegame;
946                                         break;
947                                 }
948                         }
949                 }
950         
951 // turn inventory off after a certain amount of time
952         if(inventory && !(--inventoryTics))
953         {
954                 players[consoleplayer].readyArtifact =
955                         players[consoleplayer].inventory[inv_ptr].type;
956                 inventory = false;
957                 cmd->arti = 0;
958         }
959 //
960 // do main actions
961 //
962 //
963 // do main actions
964 //
965         switch (gamestate)
966         {
967                 case GS_LEVEL:
968                         P_Ticker ();
969                         SB_Ticker ();
970                         AM_Ticker ();
971                         CT_Ticker();
972                         break;
973                 case GS_INTERMISSION:
974                         IN_Ticker ();
975                         break;
976                 case GS_FINALE:
977                         F_Ticker();
978                         break;
979                 case GS_DEMOSCREEN:
980                         H2_PageTicker ();
981                         break;
982         }       
983 }
984
985
986 /*
987 ==============================================================================
988
989                                                 PLAYER STRUCTURE FUNCTIONS
990
991 also see P_SpawnPlayer in P_Things
992 ==============================================================================
993 */
994
995 //==========================================================================
996 //
997 // G_PlayerExitMap
998 //
999 // Called when the player leaves a map.
1000 //
1001 //==========================================================================
1002
1003 void G_PlayerExitMap(int playerNumber)
1004 {
1005         int i;
1006         player_t *player;
1007         int flightPower;
1008
1009         player = &players[playerNumber];
1010
1011 //      if(deathmatch)
1012 //      {
1013 //              // Strip all but one of each type of artifact
1014 //              for(i = 0; i < player->inventorySlotNum; i++)
1015 //              {
1016 //                      player->inventory[i].count = 1;
1017 //              }
1018 //              player->artifactCount = player->inventorySlotNum;
1019 //      }
1020 //      else
1021
1022         // Strip all current powers (retain flight)
1023         flightPower = player->powers[pw_flight];
1024         memset(player->powers, 0, sizeof(player->powers));
1025         player->powers[pw_flight] = flightPower;
1026
1027         if(deathmatch)
1028         {
1029                 player->powers[pw_flight] = 0;
1030         }
1031         else
1032         {
1033                 if(P_GetMapCluster(gamemap) != P_GetMapCluster(LeaveMap))
1034                 { // Entering new cluster
1035                         // Strip all keys
1036                         player->keys = 0;
1037
1038                         // Strip flight artifact
1039                         for(i = 0; i < 25; i++)
1040                         {
1041                                 player->powers[pw_flight] = 0;
1042                                 P_PlayerUseArtifact(player, arti_fly);
1043                         }
1044                         player->powers[pw_flight] = 0;
1045                 }
1046         }
1047
1048         if(player->morphTics)
1049         {
1050                 player->readyweapon = player->mo->special1; // Restore weapon
1051                 player->morphTics = 0;
1052         }
1053         player->messageTics = 0;
1054         player->lookdir = 0;
1055         player->mo->flags &= ~MF_SHADOW; // Remove invisibility
1056         player->extralight = 0; // Remove weapon flashes
1057         player->fixedcolormap = 0; // Remove torch
1058         player->damagecount = 0; // No palette changes
1059         player->bonuscount = 0;
1060         player->poisoncount = 0;
1061         if(player == &players[consoleplayer])
1062         {
1063                 SB_state = -1; // refresh the status bar
1064                 viewangleoffset = 0;
1065         }
1066 }
1067
1068 //==========================================================================
1069 //
1070 // G_PlayerReborn
1071 //
1072 // Called after a player dies.  Almost everything is cleared and
1073 // initialized.
1074 //
1075 //==========================================================================
1076
1077 void G_PlayerReborn(int player)
1078 {
1079         player_t *p;
1080         int frags[MAXPLAYERS];
1081         int killcount, itemcount, secretcount;
1082         uint worldTimer;
1083
1084         memcpy(frags, players[player].frags, sizeof(frags));
1085         killcount = players[player].killcount;
1086         itemcount = players[player].itemcount;
1087         secretcount = players[player].secretcount;
1088         worldTimer = players[player].worldTimer;
1089
1090         p = &players[player];
1091         memset(p, 0, sizeof(*p));
1092
1093         memcpy(players[player].frags, frags, sizeof(players[player].frags));
1094         players[player].killcount = killcount;
1095         players[player].itemcount = itemcount;
1096         players[player].secretcount = secretcount;
1097         players[player].worldTimer = worldTimer;
1098         players[player].class = PlayerClass[player];
1099
1100         p->usedown = p->attackdown = true; // don't do anything immediately
1101         p->playerstate = PST_LIVE;
1102         p->health = MAXHEALTH;
1103         p->readyweapon = p->pendingweapon = WP_FIRST;
1104         p->weaponowned[WP_FIRST] = true;
1105         p->messageTics = 0;
1106         p->lookdir = 0;
1107         localQuakeHappening[player] = false;
1108         if(p == &players[consoleplayer])
1109         {
1110                 SB_state = -1; // refresh the status bar
1111                 inv_ptr = 0; // reset the inventory pointer
1112                 curpos = 0;
1113                 viewangleoffset = 0;
1114         }
1115 }
1116
1117 /*
1118 ====================
1119 =
1120 = G_CheckSpot 
1121 =
1122 = Returns false if the player cannot be respawned at the given mapthing_t spot 
1123 = because something is occupying it
1124 ====================
1125 */
1126
1127 void P_SpawnPlayer (mapthing_t *mthing);
1128
1129 boolean G_CheckSpot (int playernum, mapthing_t *mthing)
1130 {
1131         fixed_t         x,y;
1132         subsector_t *ss;
1133         unsigned        an;
1134         mobj_t      *mo;
1135         
1136         x = mthing->x << FRACBITS;
1137         y = mthing->y << FRACBITS;
1138
1139         players[playernum].mo->flags2 &= ~MF2_PASSMOBJ;
1140         if (!P_CheckPosition (players[playernum].mo, x, y) )
1141         {
1142                 players[playernum].mo->flags2 |= MF2_PASSMOBJ;
1143                 return false;
1144         }
1145         players[playernum].mo->flags2 |= MF2_PASSMOBJ;
1146
1147 // spawn a teleport fog
1148         ss = R_PointInSubsector (x,y);
1149         an = ( ANG45 * (mthing->angle/45) ) >> ANGLETOFINESHIFT;
1150
1151         mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an],
1152                 ss->sector->floorheight+TELEFOGHEIGHT, MT_TFOG);
1153
1154         if (players[consoleplayer].viewz != 1)
1155                 S_StartSound (mo, SFX_TELEPORT);  // don't start sound on first frame
1156
1157         return true;
1158 }
1159
1160 /*
1161 ====================
1162 =
1163 = G_DeathMatchSpawnPlayer
1164 =
1165 = Spawns a player at one of the random death match spots
1166 = called at level load and each death
1167 ====================
1168 */
1169
1170 void G_DeathMatchSpawnPlayer (int playernum)
1171 {
1172         int             i,j;
1173         int             selections;
1174         
1175         selections = deathmatch_p - deathmatchstarts;
1176
1177         // This check has been moved to p_setup.c:P_LoadThings()
1178         //if (selections < 8)
1179         //      I_Error ("Only %i deathmatch spots, 8 required", selections);
1180
1181         for (j=0 ; j<20 ; j++)
1182         {
1183                 i = P_Random() % selections;
1184                 if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
1185                 {
1186                         deathmatchstarts[i].type = playernum+1;
1187                         P_SpawnPlayer (&deathmatchstarts[i]);
1188                         return;
1189                 }
1190         }
1191
1192 // no good spot, so the player will probably get stuck
1193         P_SpawnPlayer (&playerstarts[0][playernum]);
1194 }
1195
1196 //==========================================================================
1197 //
1198 // G_DoReborn
1199 //
1200 //==========================================================================
1201
1202 void G_DoReborn(int playernum)
1203 {
1204         int i;
1205         boolean oldWeaponowned[NUMWEAPONS];
1206         int oldKeys;
1207         int oldPieces;
1208         boolean foundSpot;
1209         int bestWeapon;
1210
1211         if(G_CheckDemoStatus())
1212         {
1213                 return;
1214         }
1215         if(!netgame)
1216         {
1217                 if(SV_RebornSlotAvailable())
1218                 { // Use the reborn code if the slot is available
1219                         gameaction = ga_singlereborn;
1220                 }
1221                 else
1222                 { // Start a new game if there's no reborn info
1223                         gameaction = ga_newgame;
1224                 }
1225         }
1226         else
1227         { // Net-game
1228                 players[playernum].mo->player = NULL; // Dissassociate the corpse
1229
1230                 if(deathmatch)
1231                 { // Spawn at random spot if in death match
1232                         G_DeathMatchSpawnPlayer(playernum);
1233                         return;
1234                 }
1235
1236                 // Cooperative net-play, retain keys and weapons
1237                 oldKeys = players[playernum].keys;
1238                 oldPieces = players[playernum].pieces;
1239                 for(i = 0; i < NUMWEAPONS; i++)
1240                 {
1241                         oldWeaponowned[i] = players[playernum].weaponowned[i];
1242                 }
1243
1244                 foundSpot = false;
1245                 if(G_CheckSpot(playernum,
1246                         &playerstarts[RebornPosition][playernum]))
1247                 { // Appropriate player start spot is open
1248                         P_SpawnPlayer(&playerstarts[RebornPosition][playernum]);
1249                         foundSpot = true;
1250                 }
1251                 else
1252                 {
1253                         // Try to spawn at one of the other player start spots
1254                         for(i = 0; i < MAXPLAYERS; i++)
1255                         {
1256                                 if(G_CheckSpot(playernum, &playerstarts[RebornPosition][i]))
1257                                 { // Found an open start spot
1258
1259                                         // Fake as other player
1260                                         playerstarts[RebornPosition][i].type = playernum+1;
1261                                         P_SpawnPlayer(&playerstarts[RebornPosition][i]);
1262
1263                                         // Restore proper player type
1264                                         playerstarts[RebornPosition][i].type = i+1;
1265         
1266                                         foundSpot = true;
1267                                         break;
1268                                 }
1269                         }
1270                 }
1271
1272                 if(foundSpot == false)
1273                 { // Player's going to be inside something
1274                         P_SpawnPlayer(&playerstarts[RebornPosition][playernum]);
1275                 }
1276
1277                 // Restore keys and weapons
1278                 players[playernum].keys = oldKeys;
1279                 players[playernum].pieces = oldPieces;
1280                 for(bestWeapon = 0, i = 0; i < NUMWEAPONS; i++)
1281                 {
1282                         if(oldWeaponowned[i])
1283                         {
1284                                 bestWeapon = i;
1285                                 players[playernum].weaponowned[i] = true;
1286                         }
1287                 }
1288                 players[playernum].mana[MANA_1] = 25;
1289                 players[playernum].mana[MANA_2] = 25;
1290                 if(bestWeapon)
1291                 { // Bring up the best weapon
1292                         players[playernum].pendingweapon = bestWeapon;
1293                 }
1294         }
1295 }
1296
1297 void G_ScreenShot (void)
1298 {
1299         gameaction = ga_screenshot;
1300 }
1301
1302 //==========================================================================
1303 //
1304 // G_StartNewInit
1305 //
1306 //==========================================================================
1307
1308 void G_StartNewInit(void)
1309 {
1310         SV_InitBaseSlot();
1311         SV_ClearRebornSlot();
1312         P_ACSInitNewGame();
1313         // Default the player start spot group to 0
1314         RebornPosition = 0;
1315 }
1316
1317 //==========================================================================
1318 //
1319 // G_StartNewGame
1320 //
1321 //==========================================================================
1322
1323 void G_StartNewGame(skill_t skill)
1324 {
1325         int realMap;
1326
1327         G_StartNewInit();
1328         realMap = P_TranslateMap(1);
1329         if(realMap == -1)
1330         {
1331                 realMap = 1;
1332         }
1333         G_InitNew(TempSkill, 1, realMap);
1334 }
1335
1336 //==========================================================================
1337 //
1338 // G_TeleportNewMap
1339 //
1340 // Only called by the warp cheat code.  Works just like normal map to map
1341 // teleporting, but doesn't do any interlude stuff.
1342 //
1343 //==========================================================================
1344
1345 void G_TeleportNewMap(int map, int position)
1346 {
1347         gameaction = ga_leavemap;
1348         LeaveMap = map;
1349         LeavePosition = position;
1350 }
1351
1352 //==========================================================================
1353 //
1354 // G_DoTeleportNewMap
1355 //
1356 //==========================================================================
1357
1358 void G_DoTeleportNewMap(void)
1359 {
1360         SV_MapTeleport(LeaveMap, LeavePosition);
1361         gamestate = GS_LEVEL;
1362         gameaction = ga_nothing;
1363         RebornPosition = LeavePosition;
1364 }
1365
1366 /*
1367 boolean secretexit;
1368 void G_ExitLevel (void)
1369 {
1370         secretexit = false;
1371         gameaction = ga_completed;
1372 }
1373 void G_SecretExitLevel (void)
1374 {
1375         secretexit = true;
1376         gameaction = ga_completed;
1377 }
1378 */
1379
1380 //==========================================================================
1381 //
1382 // G_Completed
1383 //
1384 // Starts intermission routine, which is used only during hub exits,
1385 // and DeathMatch games.
1386 //==========================================================================
1387
1388 void G_Completed(int map, int position)
1389 {
1390         gameaction = ga_completed;
1391         LeaveMap = map;
1392         LeavePosition = position;
1393 }
1394
1395 void G_DoCompleted(void)
1396 {
1397         int i;
1398
1399         gameaction = ga_nothing;
1400         if(G_CheckDemoStatus())
1401         {
1402                 return;
1403         }
1404         for(i = 0; i < MAXPLAYERS; i++)
1405         {
1406                 if(playeringame[i])
1407                 {
1408                         G_PlayerExitMap(i);
1409                 }
1410         }
1411         if(LeaveMap == -1 && LeavePosition == -1)
1412         {
1413                 gameaction = ga_victory;
1414                 return;
1415         }
1416         else
1417         {               
1418                 gamestate = GS_INTERMISSION;
1419                 IN_Start();
1420         }
1421
1422 /*
1423         int i;
1424         static int afterSecret[3] = { 7, 5, 5 };
1425
1426         gameaction = ga_nothing;
1427         if(G_CheckDemoStatus())
1428         {
1429                 return;
1430         }
1431         for(i = 0; i < MAXPLAYERS; i++)
1432         {
1433                 if(playeringame[i])
1434                 {
1435                         G_PlayerFinishLevel(i);
1436                 }
1437         }
1438         prevmap = gamemap;
1439         if(secretexit == true)
1440         {
1441                 gamemap = 9;
1442         }
1443         else if(gamemap == 9)
1444         { // Finished secret level
1445                 gamemap = afterSecret[gameepisode-1];
1446         }
1447         else if(gamemap == 8)
1448         {
1449                 gameaction = ga_victory;
1450                 return;
1451         }
1452         else
1453         {
1454                 gamemap++;
1455         }
1456         gamestate = GS_INTERMISSION;
1457         IN_Start();
1458 */
1459 }
1460
1461 //============================================================================
1462 //
1463 // G_WorldDone
1464 //
1465 //============================================================================
1466
1467 void G_WorldDone(void)
1468 {
1469         gameaction = ga_worlddone;
1470 }
1471
1472 //============================================================================
1473 //
1474 // G_DoWorldDone
1475 //
1476 //============================================================================
1477
1478 void G_DoWorldDone(void)
1479 {
1480         gamestate = GS_LEVEL;
1481         G_DoLoadLevel();
1482         gameaction = ga_nothing;
1483         viewactive = true;
1484 }
1485
1486 //==========================================================================
1487 //
1488 // G_DoSingleReborn
1489 //
1490 // Called by G_Ticker based on gameaction.  Loads a game from the reborn
1491 // save slot.
1492 //
1493 //==========================================================================
1494
1495 void G_DoSingleReborn(void)
1496 {
1497         gameaction = ga_nothing;
1498         SV_LoadGame(SV_GetRebornSlot());
1499         SB_SetClassData();
1500 }
1501
1502 //==========================================================================
1503 //
1504 // G_LoadGame
1505 //
1506 // Can be called by the startup code or the menu task.
1507 //
1508 //==========================================================================
1509
1510 static int GameLoadSlot;
1511
1512 void G_LoadGame(int slot)
1513 {
1514         GameLoadSlot = slot;
1515         gameaction = ga_loadgame;
1516 }
1517
1518 //==========================================================================
1519 //
1520 // G_DoLoadGame
1521 //
1522 // Called by G_Ticker based on gameaction.
1523 //
1524 //==========================================================================
1525
1526 void G_DoLoadGame(void)
1527 {
1528         gameaction = ga_nothing;
1529         SV_LoadGame(GameLoadSlot);
1530         if(!netgame)
1531         { // Copy the base slot to the reborn slot
1532                 SV_UpdateRebornSlot();
1533         }
1534         SB_SetClassData();
1535 }
1536
1537 //==========================================================================
1538 //
1539 // G_SaveGame
1540 //
1541 // Called by the menu task.  <description> is a 24 byte text string.
1542 //
1543 //==========================================================================
1544
1545 void G_SaveGame(int slot, char *description)
1546 {
1547         savegameslot = slot;
1548         strcpy(savedescription, description);
1549         sendsave = true;
1550 }
1551
1552 //==========================================================================
1553 //
1554 // G_DoSaveGame
1555 //
1556 // Called by G_Ticker based on gameaction.
1557 //
1558 //==========================================================================
1559
1560 void G_DoSaveGame(void)
1561 {
1562         SV_SaveGame(savegameslot, savedescription);
1563         gameaction = ga_nothing;
1564         savedescription[0] = 0;
1565         P_SetMessage(&players[consoleplayer], TXT_GAMESAVED, true);
1566 }
1567
1568 //==========================================================================
1569 //
1570 // G_DeferredNewGame
1571 //
1572 //==========================================================================
1573
1574 void G_DeferredNewGame(skill_t skill)
1575 {
1576         TempSkill = skill;
1577         gameaction = ga_newgame;
1578 }
1579
1580 //==========================================================================
1581 //
1582 // G_DoNewGame
1583 //
1584 //==========================================================================
1585
1586 void G_DoNewGame(void)
1587 {
1588         G_StartNewGame(TempSkill);
1589         gameaction = ga_nothing;
1590 }
1591
1592 /*
1593 ====================
1594 =
1595 = G_InitNew
1596 =
1597 = Can be called by the startup code or the menu task
1598 = consoleplayer, displayplayer, playeringame[] should be set
1599 ====================
1600 */
1601
1602 void G_DeferedInitNew(skill_t skill, int episode, int map)
1603 {
1604         TempSkill = skill;
1605         TempEpisode = episode;
1606         TempMap = map;
1607         gameaction = ga_initnew;
1608 }
1609
1610 void G_DoInitNew(void)
1611 {
1612         SV_InitBaseSlot();
1613         G_InitNew(TempSkill, TempEpisode, TempMap);
1614         gameaction = ga_nothing;
1615 }
1616
1617 void G_InitNew(skill_t skill, int episode, int map)
1618 {
1619         int i;
1620
1621         if(paused)
1622         {
1623                 paused = false;
1624                 S_ResumeSound();
1625         }
1626         if(skill < sk_baby)
1627         {
1628                 skill = sk_baby;
1629         }
1630         if(skill > sk_nightmare)
1631         {
1632                 skill = sk_nightmare;
1633         }
1634         if(map < 1)
1635         {
1636                 map = 1;
1637         }
1638         if(map > 99)
1639         {
1640                 map = 99;
1641         }
1642         M_ClearRandom();
1643         // Force players to be initialized upon first level load
1644         for(i = 0; i < MAXPLAYERS; i++)
1645         {
1646                 players[i].playerstate = PST_REBORN;
1647                 players[i].worldTimer = 0;
1648         }
1649
1650         // Set up a bunch of globals
1651         usergame = true; // will be set false if a demo
1652         paused = false;
1653         demorecording = false;
1654         demoplayback = false;
1655         viewactive = true;
1656         gameepisode = episode;
1657         gamemap = map;
1658         gameskill = skill;
1659         BorderNeedRefresh = true;
1660
1661         // Initialize the sky
1662         R_InitSky(map);
1663
1664         // Give one null ticcmd_t
1665         //gametic = 0;
1666         //maketic = 1;
1667         //for (i=0 ; i<MAXPLAYERS ; i++)
1668         //      nettics[i] = 1; // one null event for this gametic
1669         //memset (localcmds,0,sizeof(localcmds));
1670         //memset (netcmds,0,sizeof(netcmds));
1671
1672         G_DoLoadLevel();
1673 }
1674
1675 /*
1676 ===============================================================================
1677
1678                                                         DEMO RECORDING
1679
1680 ===============================================================================
1681 */
1682
1683 #define DEMOMARKER      0x80
1684
1685 void G_ReadDemoTiccmd (ticcmd_t *cmd)
1686 {
1687         if (*demo_p == DEMOMARKER)
1688         {       // end of demo data stream
1689                 G_CheckDemoStatus ();
1690                 return;
1691         }
1692         cmd->forwardmove = ((signed char)*demo_p++);
1693         cmd->sidemove = ((signed char)*demo_p++);
1694         cmd->angleturn = ((unsigned char)*demo_p++)<<8;
1695         cmd->buttons = (unsigned char)*demo_p++;
1696         cmd->lookfly = (unsigned char)*demo_p++;
1697         cmd->arti = (unsigned char)*demo_p++;
1698 }
1699
1700 void G_WriteDemoTiccmd (ticcmd_t *cmd)
1701 {
1702         if (gamekeydown['q'])           // press q to end demo recording
1703                 G_CheckDemoStatus ();
1704         *demo_p++ = cmd->forwardmove;
1705         *demo_p++ = cmd->sidemove;
1706         *demo_p++ = cmd->angleturn>>8;
1707         *demo_p++ = cmd->buttons;
1708         *demo_p++ = cmd->lookfly;
1709         *demo_p++ = cmd->arti;
1710         demo_p -= 6;
1711         G_ReadDemoTiccmd (cmd);         // make SURE it is exactly the same
1712 }
1713
1714
1715
1716 /*
1717 ===================
1718 =
1719 = G_RecordDemo
1720 =
1721 ===================
1722 */
1723
1724 void G_RecordDemo (skill_t skill, int numplayers, int episode, int map, char *name)
1725 {
1726         int             i;
1727         
1728         G_InitNew (skill, episode, map);
1729         usergame = false;
1730         strcpy (demoname, name);
1731         strcat (demoname, ".lmp");
1732         demobuffer = demo_p = Z_Malloc (0x20000,PU_STATIC,NULL);
1733         *demo_p++ = skill;
1734         *demo_p++ = episode;
1735         *demo_p++ = map;
1736         
1737         for (i=0 ; i<MAXPLAYERS ; i++)
1738         {
1739                 *demo_p++ = playeringame[i];
1740                 *demo_p++ = PlayerClass[i];
1741         }               
1742         demorecording = true;
1743 }
1744
1745
1746 /*
1747 ===================
1748 =
1749 = G_PlayDemo
1750 =
1751 ===================
1752 */
1753
1754 char    *defdemoname;
1755
1756 void G_DeferedPlayDemo (char *name)
1757 {
1758         defdemoname = name;
1759         gameaction = ga_playdemo;
1760 }
1761
1762 void G_DoPlayDemo (void)
1763 {
1764         skill_t skill;
1765         int             i, episode, map;
1766         
1767         gameaction = ga_nothing;
1768         demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC);
1769         skill = *demo_p++;
1770         episode = *demo_p++;
1771         map = *demo_p++;
1772
1773         for (i=0 ; i<MAXPLAYERS ; i++)
1774         {
1775                 playeringame[i] = *demo_p++;
1776                 PlayerClass[i] = *demo_p++;
1777         }
1778
1779         // Initialize world info, etc.
1780         G_StartNewInit();
1781
1782         precache = false;               // don't spend a lot of time in loadlevel
1783         G_InitNew (skill, episode, map);
1784         precache = true;
1785         usergame = false;
1786         demoplayback = true;
1787 }
1788
1789
1790 /*
1791 ===================
1792 =
1793 = G_TimeDemo
1794 =
1795 ===================
1796 */
1797
1798 void G_TimeDemo (char *name)
1799 {
1800         skill_t skill;
1801         int             episode, map;
1802         
1803         demobuffer = demo_p = W_CacheLumpName (name, PU_STATIC);
1804         skill = *demo_p++;
1805         episode = *demo_p++;
1806         map = *demo_p++;
1807         G_InitNew (skill, episode, map);
1808         usergame = false;
1809         demoplayback = true;
1810         timingdemo = true;
1811         singletics = true;
1812 }
1813
1814
1815 /*
1816 ===================
1817 =
1818 = G_CheckDemoStatus
1819 =
1820 = Called after a death or level completion to allow demos to be cleaned up
1821 = Returns true if a new demo loop action will take place
1822 ===================
1823 */
1824
1825 boolean G_CheckDemoStatus (void)
1826 {
1827         int             endtime;
1828         
1829         if (timingdemo)
1830         {
1831                 endtime = I_GetTime ();
1832                 I_Error ("timed %i gametics in %i realtics",gametic
1833                 , endtime-starttime);
1834         }
1835         
1836         if (demoplayback)
1837         {
1838                 if (singledemo)
1839                         I_Quit ();
1840                         
1841                 Z_ChangeTag (demobuffer, PU_CACHE);
1842                 demoplayback = false;
1843                 H2_AdvanceDemo();
1844                 return true;
1845         }
1846
1847         if (demorecording)
1848         {
1849                 *demo_p++ = DEMOMARKER;
1850                 M_WriteFile (demoname, demobuffer, demo_p - demobuffer);
1851                 Z_Free (demobuffer);
1852                 demorecording = false;
1853                 I_Error ("Demo %s recorded",demoname);
1854         }
1855
1856         return false;
1857 }