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