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