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