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