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