Add weapon cycling bindings for mouse and joystick buttons. Add weapon cycling bindi...
[chocolate-doom.git] / src / g_game.c
blobbf9560db760dca48fbfcd7562cbd038ea7a7fd09
1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 // 02111-1307, USA.
22 // DESCRIPTION: none
24 //-----------------------------------------------------------------------------
28 #include <string.h>
29 #include <stdlib.h>
30 #include <math.h>
32 #include "doomdef.h"
33 #include "doomstat.h"
35 #include "deh_main.h"
36 #include "deh_misc.h"
38 #include "z_zone.h"
39 #include "f_finale.h"
40 #include "m_argv.h"
41 #include "m_misc.h"
42 #include "m_menu.h"
43 #include "m_random.h"
44 #include "i_system.h"
45 #include "i_timer.h"
46 #include "i_video.h"
48 #include "p_setup.h"
49 #include "p_saveg.h"
50 #include "p_tick.h"
52 #include "d_main.h"
54 #include "wi_stuff.h"
55 #include "hu_stuff.h"
56 #include "st_stuff.h"
57 #include "am_map.h"
59 // Needs access to LFB.
60 #include "v_video.h"
62 #include "w_wad.h"
64 #include "p_local.h"
66 #include "s_sound.h"
68 // Data.
69 #include "dstrings.h"
70 #include "sounds.h"
72 // SKY handling - still the wrong place.
73 #include "r_data.h"
74 #include "r_sky.h"
78 #include "g_game.h"
81 #define SAVEGAMESIZE 0x2c000
85 boolean G_CheckDemoStatus (void);
86 void G_ReadDemoTiccmd (ticcmd_t* cmd);
87 void G_WriteDemoTiccmd (ticcmd_t* cmd);
88 void G_PlayerReborn (int player);
89 void G_InitNew (skill_t skill, int episode, int map);
91 void G_DoReborn (int playernum);
93 void G_DoLoadLevel (void);
94 void G_DoNewGame (void);
95 void G_DoLoadGame (void);
96 void G_DoPlayDemo (void);
97 void G_DoCompleted (void);
98 void G_DoVictory (void);
99 void G_DoWorldDone (void);
100 void G_DoSaveGame (void);
102 // Gamestate the last time G_Ticker was called.
104 gamestate_t oldgamestate;
106 gameaction_t gameaction;
107 gamestate_t gamestate;
108 skill_t gameskill;
109 boolean respawnmonsters;
110 int gameepisode;
111 int gamemap;
113 // If non-zero, exit the level after this number of minutes.
115 int timelimit;
117 boolean paused;
118 boolean sendpause; // send a pause event next tic
119 boolean sendsave; // send a save event next tic
120 boolean usergame; // ok to save / end game
122 boolean timingdemo; // if true, exit with report on completion
123 boolean nodrawers; // for comparative timing purposes
124 boolean noblit; // for comparative timing purposes
125 int starttime; // for comparative timing purposes
127 boolean viewactive;
129 boolean deathmatch; // only if started as net death
130 boolean netgame; // only true if packets are broadcast
131 boolean playeringame[MAXPLAYERS];
132 player_t players[MAXPLAYERS];
134 boolean turbodetected[MAXPLAYERS];
136 int consoleplayer; // player taking events and displaying
137 int displayplayer; // view being displayed
138 int gametic;
139 int levelstarttic; // gametic at level start
140 int totalkills, totalitems, totalsecret; // for intermission
142 char demoname[32];
143 boolean demorecording;
144 boolean longtics; // cph's doom 1.91 longtics hack
145 boolean lowres_turn; // low resolution turning for longtics
146 boolean demoplayback;
147 boolean netdemo;
148 byte* demobuffer;
149 byte* demo_p;
150 byte* demoend;
151 boolean singledemo; // quit after playing a demo from cmdline
153 boolean precache = true; // if true, load all graphics at start
155 boolean testcontrols = false; // Invoked by setup to test controls
157 wbstartstruct_t wminfo; // parms for world map / intermission
159 byte consistancy[MAXPLAYERS][BACKUPTICS];
163 // Controls
165 int key_right = KEY_RIGHTARROW;
166 int key_left = KEY_LEFTARROW;
168 int key_up = KEY_UPARROW;
169 int key_down = KEY_DOWNARROW;
170 int key_strafeleft = ',';
171 int key_straferight = '.';
172 int key_fire = KEY_RCTRL;
173 int key_use = ' ';
174 int key_strafe = KEY_RALT;
175 int key_speed = KEY_RSHIFT;
177 int key_weapon1 = '1';
178 int key_weapon2 = '2';
179 int key_weapon3 = '3';
180 int key_weapon4 = '4';
181 int key_weapon5 = '5';
182 int key_weapon6 = '6';
183 int key_weapon7 = '7';
184 int key_weapon8 = '8';
185 int key_prevweapon = 0;
186 int key_nextweapon = 0;
188 int key_pause = KEY_PAUSE;
189 int key_demo_quit = 'q';
190 int key_spy = KEY_F12;
192 int mousebfire = 0;
193 int mousebstrafe = 1;
194 int mousebforward = 2;
196 int mousebstrafeleft = -1;
197 int mousebstraferight = -1;
198 int mousebbackward = -1;
199 int mousebuse = -1;
201 int mousebprevweapon = -1;
202 int mousebnextweapon = -1;
204 // Control whether if a mouse button is double clicked, it acts like
205 // "use" has been pressed
207 int dclick_use = 1;
209 int joybfire = 0;
210 int joybstrafe = 1;
211 int joybuse = 3;
212 int joybspeed = 2;
213 int joybstrafeleft = -1;
214 int joybstraferight = -1;
216 int joybprevweapon = -1;
217 int joybnextweapon = -1;
219 // fraggle: Disallow mouse and joystick movement to cause forward/backward
220 // motion. Specified with the '-novert' command line parameter.
221 // This is an int to allow saving to config file
223 int novert = 0;
227 #define MAXPLMOVE (forwardmove[1])
229 #define TURBOTHRESHOLD 0x32
231 fixed_t forwardmove[2] = {0x19, 0x32};
232 fixed_t sidemove[2] = {0x18, 0x28};
233 fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
235 static int *weapon_keys[] = {
236 &key_weapon1,
237 &key_weapon2,
238 &key_weapon3,
239 &key_weapon4,
240 &key_weapon5,
241 &key_weapon6,
242 &key_weapon7,
243 &key_weapon8
246 // Set to -1 or +1 to switch to the previous or next weapon.
248 static int next_weapon = 0;
250 // Used for prev/next weapon keys.
252 static const struct
254 weapontype_t weapon;
255 weapontype_t weapon_num;
256 } weapon_order_table[] = {
257 { wp_fist, wp_fist },
258 { wp_chainsaw, wp_fist },
259 { wp_pistol, wp_pistol },
260 { wp_shotgun, wp_shotgun },
261 { wp_supershotgun, wp_shotgun },
262 { wp_chaingun, wp_chaingun },
263 { wp_missile, wp_missile },
264 { wp_plasma, wp_plasma },
265 { wp_bfg, wp_bfg }
268 #define SLOWTURNTICS 6
270 #define NUMKEYS 256
271 #define MAX_JOY_BUTTONS 20
272 #define MAX_MOUSE_BUTTONS 3
274 static boolean gamekeydown[NUMKEYS];
275 static int turnheld; // for accelerative turning
277 static boolean mousearray[MAX_MOUSE_BUTTONS + 1];
278 static boolean *mousebuttons = &mousearray[1]; // allow [-1]
280 // mouse values are used once
281 int mousex;
282 int mousey;
284 static int dclicktime;
285 static boolean dclickstate;
286 static int dclicks;
287 static int dclicktime2;
288 static boolean dclickstate2;
289 static int dclicks2;
291 // joystick values are repeated
292 static int joyxmove;
293 static int joyymove;
294 static boolean joyarray[MAX_JOY_BUTTONS + 1];
295 static boolean *joybuttons = &joyarray[1]; // allow [-1]
297 static int savegameslot;
298 static char savedescription[32];
300 static int testcontrols_mousespeed;
302 #define BODYQUESIZE 32
304 mobj_t* bodyque[BODYQUESIZE];
305 int bodyqueslot;
307 int vanilla_savegame_limit = 1;
308 int vanilla_demo_limit = 1;
311 #define MOUSE_SPEED_BOX_WIDTH 16
312 #define COLOR_RED 0xb0
313 #define COLOR_BLACK 0x00
314 #define COLOR_WHITE 0x04
315 #define COLOR_YELLOW 0xe7
317 void G_DrawMouseSpeedBox(void)
319 extern int usemouse;
320 int i;
321 int box_x, box_y;
322 int original_speed;
323 int x, y;
324 int redline_x;
325 int linelen;
326 char *lumpname;
327 int color;
329 // If the mouse is turned off or acceleration is turned off, don't
330 // draw the box at all.
332 if (!usemouse || fabs(mouse_acceleration - 1) < 0.01)
334 return;
337 // Calculate box position
339 box_x = SCREENWIDTH - MOUSE_SPEED_BOX_WIDTH * 8;
340 box_y = SCREENHEIGHT - 9;
342 // Draw the box.
344 x = box_x;
346 for (i=0; i<MOUSE_SPEED_BOX_WIDTH; ++i)
348 if (i == 0)
350 lumpname = "M_LSLEFT";
352 else if (i == MOUSE_SPEED_BOX_WIDTH - 1)
354 lumpname = "M_LSRGHT";
356 else
358 lumpname = "M_LSCNTR";
361 V_DrawPatchDirect(x, box_y, 0, W_CacheLumpName(DEH_String(lumpname),
362 PU_CACHE));
363 x += 8;
366 // Calculate the position of the red line. This is 1/3 of the way
367 // along the box.
369 redline_x = (MOUSE_SPEED_BOX_WIDTH / 3) * 8;
371 // Undo acceleration and get back the original mouse speed
373 if (testcontrols_mousespeed < mouse_threshold)
375 original_speed = testcontrols_mousespeed;
377 else
379 original_speed = testcontrols_mousespeed - mouse_threshold;
380 original_speed = (int) (original_speed / mouse_acceleration);
381 original_speed += mouse_threshold;
384 // Calculate line length
386 linelen = (original_speed * redline_x) / mouse_threshold;
388 // Draw horizontal "thermometer"
390 for (x=0; x<(MOUSE_SPEED_BOX_WIDTH - 1) * 8; ++x)
392 if (x < linelen)
394 if (x < redline_x)
396 color = COLOR_WHITE;
398 else
400 color = COLOR_YELLOW;
403 else
405 color = COLOR_BLACK;
408 screens[0][(box_y - 4) * SCREENWIDTH + box_x + x + 1] = color;
411 // Draw red line
413 for (y=box_y - 8; y<box_y; ++y)
415 screens[0][y * SCREENWIDTH + box_x + redline_x] = COLOR_RED;
419 int G_CmdChecksum (ticcmd_t* cmd)
421 size_t i;
422 int sum = 0;
424 for (i=0 ; i< sizeof(*cmd)/4 - 1 ; i++)
425 sum += ((int *)cmd)[i];
427 return sum;
430 static boolean WeaponSelectable(weapontype_t weapon)
432 // Can't select a weapon if we don't own it.
434 if (!players[consoleplayer].weaponowned[weapon])
436 return false;
439 // Can't select the fist if we have the chainsaw, unless
440 // we also have the berserk pack.
442 if (weapon == wp_fist
443 && players[consoleplayer].weaponowned[wp_chainsaw]
444 && !players[consoleplayer].powers[pw_strength])
446 return false;
449 return true;
452 static int G_NextWeapon(int direction)
454 weapontype_t weapon;
455 int i;
457 // Find index in the table.
459 if (players[consoleplayer].pendingweapon == wp_nochange)
461 weapon = players[consoleplayer].readyweapon;
463 else
465 weapon = players[consoleplayer].pendingweapon;
468 for (i=0; i<arrlen(weapon_order_table); ++i)
470 if (weapon_order_table[i].weapon == weapon)
472 break;
476 // Switch weapon.
480 i += direction;
481 i = (i + arrlen(weapon_order_table)) % arrlen(weapon_order_table);
482 } while (!WeaponSelectable(weapon_order_table[i].weapon));
484 return weapon_order_table[i].weapon_num;
488 // G_BuildTiccmd
489 // Builds a ticcmd from all of the available inputs
490 // or reads it from the demo buffer.
491 // If recording a demo, write it out
493 void G_BuildTiccmd (ticcmd_t* cmd)
495 int i;
496 boolean strafe;
497 boolean bstrafe;
498 int speed;
499 int tspeed;
500 int forward;
501 int side;
503 memset(cmd, 0, sizeof(ticcmd_t));
505 cmd->consistancy =
506 consistancy[consoleplayer][maketic%BACKUPTICS];
508 strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
509 || joybuttons[joybstrafe];
511 // fraggle: support the old "joyb_speed = 31" hack which
512 // allowed an autorun effect
514 speed = key_speed >= NUMKEYS
515 || joybspeed >= MAX_JOY_BUTTONS
516 || gamekeydown[key_speed]
517 || joybuttons[joybspeed];
519 forward = side = 0;
521 // use two stage accelerative turning
522 // on the keyboard and joystick
523 if (joyxmove < 0
524 || joyxmove > 0
525 || gamekeydown[key_right]
526 || gamekeydown[key_left])
527 turnheld += ticdup;
528 else
529 turnheld = 0;
531 if (turnheld < SLOWTURNTICS)
532 tspeed = 2; // slow turn
533 else
534 tspeed = speed;
536 // let movement keys cancel each other out
537 if (strafe)
539 if (gamekeydown[key_right])
541 // fprintf(stderr, "strafe right\n");
542 side += sidemove[speed];
544 if (gamekeydown[key_left])
546 // fprintf(stderr, "strafe left\n");
547 side -= sidemove[speed];
549 if (joyxmove > 0)
550 side += sidemove[speed];
551 if (joyxmove < 0)
552 side -= sidemove[speed];
555 else
557 if (gamekeydown[key_right])
558 cmd->angleturn -= angleturn[tspeed];
559 if (gamekeydown[key_left])
560 cmd->angleturn += angleturn[tspeed];
561 if (joyxmove > 0)
562 cmd->angleturn -= angleturn[tspeed];
563 if (joyxmove < 0)
564 cmd->angleturn += angleturn[tspeed];
567 if (gamekeydown[key_up])
569 // fprintf(stderr, "up\n");
570 forward += forwardmove[speed];
572 if (gamekeydown[key_down])
574 // fprintf(stderr, "down\n");
575 forward -= forwardmove[speed];
578 if (joyymove < 0)
579 forward += forwardmove[speed];
580 if (joyymove > 0)
581 forward -= forwardmove[speed];
583 if (gamekeydown[key_strafeleft]
584 || joybuttons[joybstrafeleft]
585 || mousebuttons[mousebstrafeleft])
587 side -= sidemove[speed];
590 if (gamekeydown[key_straferight]
591 || joybuttons[joybstraferight]
592 || mousebuttons[mousebstraferight])
594 side += sidemove[speed];
597 // buttons
598 cmd->chatchar = HU_dequeueChatChar();
600 if (gamekeydown[key_fire] || mousebuttons[mousebfire]
601 || joybuttons[joybfire])
602 cmd->buttons |= BT_ATTACK;
604 if (gamekeydown[key_use]
605 || joybuttons[joybuse]
606 || mousebuttons[mousebuse])
608 cmd->buttons |= BT_USE;
609 // clear double clicks if hit use button
610 dclicks = 0;
613 // If the previous or next weapon button is pressed, the
614 // next_weapon variable is set to change weapons when
615 // we generate a ticcmd. Choose a new weapon.
617 if (next_weapon != 0)
619 i = G_NextWeapon(next_weapon);
620 cmd->buttons |= BT_CHANGE;
621 cmd->buttons |= i << BT_WEAPONSHIFT;
622 next_weapon = 0;
624 else
626 // Check weapon keys.
628 for (i=0; i<arrlen(weapon_keys); ++i)
630 int key = *weapon_keys[i];
632 if (gamekeydown[key])
634 cmd->buttons |= BT_CHANGE;
635 cmd->buttons |= i<<BT_WEAPONSHIFT;
636 break;
641 // mouse
642 if (mousebuttons[mousebforward])
644 forward += forwardmove[speed];
646 if (mousebuttons[mousebbackward])
648 forward -= forwardmove[speed];
651 if (dclick_use)
653 // forward double click
654 if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1 )
656 dclickstate = mousebuttons[mousebforward];
657 if (dclickstate)
658 dclicks++;
659 if (dclicks == 2)
661 cmd->buttons |= BT_USE;
662 dclicks = 0;
664 else
665 dclicktime = 0;
667 else
669 dclicktime += ticdup;
670 if (dclicktime > 20)
672 dclicks = 0;
673 dclickstate = 0;
677 // strafe double click
678 bstrafe =
679 mousebuttons[mousebstrafe]
680 || joybuttons[joybstrafe];
681 if (bstrafe != dclickstate2 && dclicktime2 > 1 )
683 dclickstate2 = bstrafe;
684 if (dclickstate2)
685 dclicks2++;
686 if (dclicks2 == 2)
688 cmd->buttons |= BT_USE;
689 dclicks2 = 0;
691 else
692 dclicktime2 = 0;
694 else
696 dclicktime2 += ticdup;
697 if (dclicktime2 > 20)
699 dclicks2 = 0;
700 dclickstate2 = 0;
705 // fraggle: allow disabling mouse y movement
707 if (!novert)
709 forward += mousey;
712 if (strafe)
713 side += mousex*2;
714 else
715 cmd->angleturn -= mousex*0x8;
717 if (mousex == 0)
719 // No movement in the previous frame
721 testcontrols_mousespeed = 0;
724 mousex = mousey = 0;
726 if (forward > MAXPLMOVE)
727 forward = MAXPLMOVE;
728 else if (forward < -MAXPLMOVE)
729 forward = -MAXPLMOVE;
730 if (side > MAXPLMOVE)
731 side = MAXPLMOVE;
732 else if (side < -MAXPLMOVE)
733 side = -MAXPLMOVE;
735 cmd->forwardmove += forward;
736 cmd->sidemove += side;
738 // special buttons
739 if (sendpause)
741 sendpause = false;
742 cmd->buttons = BT_SPECIAL | BTS_PAUSE;
745 if (sendsave)
747 sendsave = false;
748 cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT);
751 // low-res turning
753 if (lowres_turn)
755 static signed short carry = 0;
756 signed short desired_angleturn;
758 desired_angleturn = cmd->angleturn + carry;
760 // round angleturn to the nearest 256 unit boundary
761 // for recording demos with single byte values for turn
763 cmd->angleturn = (desired_angleturn + 128) & 0xff00;
765 // Carry forward the error from the reduced resolution to the
766 // next tic, so that successive small movements can accumulate.
768 carry = desired_angleturn - cmd->angleturn;
774 // G_DoLoadLevel
776 extern gamestate_t wipegamestate;
778 void G_DoLoadLevel (void)
780 int i;
782 // Set the sky map.
783 // First thing, we have a dummy sky texture name,
784 // a flat. The data is in the WAD only because
785 // we look for an actual index, instead of simply
786 // setting one.
788 skyflatnum = R_FlatNumForName(DEH_String(SKYFLATNAME));
790 levelstarttic = gametic; // for time calculation
792 if (wipegamestate == GS_LEVEL)
793 wipegamestate = -1; // force a wipe
795 gamestate = GS_LEVEL;
797 for (i=0 ; i<MAXPLAYERS ; i++)
799 turbodetected[i] = false;
800 if (playeringame[i] && players[i].playerstate == PST_DEAD)
801 players[i].playerstate = PST_REBORN;
802 memset (players[i].frags,0,sizeof(players[i].frags));
805 P_SetupLevel (gameepisode, gamemap, 0, gameskill);
806 displayplayer = consoleplayer; // view the guy you are playing
807 gameaction = ga_nothing;
808 Z_CheckHeap ();
810 // clear cmd building stuff
812 memset (gamekeydown, 0, sizeof(gamekeydown));
813 joyxmove = joyymove = 0;
814 mousex = mousey = 0;
815 sendpause = sendsave = paused = false;
816 memset (mousebuttons, 0, sizeof(mousebuttons));
817 memset (joybuttons, 0, sizeof(joybuttons));
819 if (testcontrols)
821 players[consoleplayer].message = "Press escape to quit.";
825 static void SetJoyButtons(unsigned int buttons_mask)
827 int i;
829 for (i=0; i<MAX_JOY_BUTTONS; ++i)
831 int button_on = (buttons_mask & (1 << i)) != 0;
833 // Detect button press:
835 if (!joybuttons[i] && button_on)
837 // Weapon cycling:
839 if (i == joybprevweapon)
841 next_weapon = -1;
843 else if (i == joybnextweapon)
845 next_weapon = 1;
849 joybuttons[i] = button_on;
853 static void SetMouseButtons(unsigned int buttons_mask)
855 int i;
857 for (i=0; i<MAX_MOUSE_BUTTONS; ++i)
859 unsigned int button_on = (buttons_mask & (1 << i)) != 0;
861 // Detect button press:
863 if (!mousebuttons[i] && button_on)
865 if (i == mousebprevweapon)
867 next_weapon = -1;
869 else if (i == mousebnextweapon)
871 next_weapon = 1;
875 mousebuttons[i] = button_on;
880 // G_Responder
881 // Get info needed to make ticcmd_ts for the players.
883 boolean G_Responder (event_t* ev)
885 // allow spy mode changes even during the demo
886 if (gamestate == GS_LEVEL && ev->type == ev_keydown
887 && ev->data1 == key_spy && (singledemo || !deathmatch) )
889 // spy mode
892 displayplayer++;
893 if (displayplayer == MAXPLAYERS)
894 displayplayer = 0;
895 } while (!playeringame[displayplayer] && displayplayer != consoleplayer);
896 return true;
899 // any other key pops up menu if in demos
900 if (gameaction == ga_nothing && !singledemo &&
901 (demoplayback || gamestate == GS_DEMOSCREEN)
904 if (ev->type == ev_keydown ||
905 (ev->type == ev_mouse && ev->data1) ||
906 (ev->type == ev_joystick && ev->data1) )
908 M_StartControlPanel ();
909 return true;
911 return false;
914 if (gamestate == GS_LEVEL)
916 #if 0
917 if (devparm && ev->type == ev_keydown && ev->data1 == ';')
919 G_DeathMatchSpawnPlayer (0);
920 return true;
922 #endif
923 if (HU_Responder (ev))
924 return true; // chat ate the event
925 if (ST_Responder (ev))
926 return true; // status window ate it
927 if (AM_Responder (ev))
928 return true; // automap ate it
931 if (gamestate == GS_FINALE)
933 if (F_Responder (ev))
934 return true; // finale ate the event
937 if (testcontrols && ev->type == ev_mouse)
939 // If we are invoked by setup to test the controls, save the
940 // mouse speed so that we can display it on-screen.
941 // Perform a low pass filter on this so that the thermometer
942 // appears to move smoothly.
944 testcontrols_mousespeed = abs(ev->data2);
947 // If the next/previous weapon keys are pressed, set the next_weapon
948 // variable to change weapons when the next ticcmd is generated.
950 if (ev->type == ev_keydown && ev->data1 == key_prevweapon)
952 next_weapon = -1;
954 else if (ev->type == ev_keydown && ev->data1 == key_nextweapon)
956 next_weapon = 1;
959 switch (ev->type)
961 case ev_keydown:
962 if (ev->data1 == key_pause)
964 sendpause = true;
966 else if (ev->data1 <NUMKEYS)
968 gamekeydown[ev->data1] = true;
971 return true; // eat key down events
973 case ev_keyup:
974 if (ev->data1 <NUMKEYS)
975 gamekeydown[ev->data1] = false;
976 return false; // always let key up events filter down
978 case ev_mouse:
979 SetMouseButtons(ev->data1);
980 mousex = ev->data2*(mouseSensitivity+5)/10;
981 mousey = ev->data3*(mouseSensitivity+5)/10;
982 return true; // eat events
984 case ev_joystick:
985 SetJoyButtons(ev->data1);
986 joyxmove = ev->data2;
987 joyymove = ev->data3;
988 return true; // eat events
990 default:
991 break;
994 return false;
1000 // G_Ticker
1001 // Make ticcmd_ts for the players.
1003 void G_Ticker (void)
1005 int i;
1006 int buf;
1007 ticcmd_t* cmd;
1009 // do player reborns if needed
1010 for (i=0 ; i<MAXPLAYERS ; i++)
1011 if (playeringame[i] && players[i].playerstate == PST_REBORN)
1012 G_DoReborn (i);
1014 // do things to change the game state
1015 while (gameaction != ga_nothing)
1017 switch (gameaction)
1019 case ga_loadlevel:
1020 G_DoLoadLevel ();
1021 break;
1022 case ga_newgame:
1023 G_DoNewGame ();
1024 break;
1025 case ga_loadgame:
1026 G_DoLoadGame ();
1027 break;
1028 case ga_savegame:
1029 G_DoSaveGame ();
1030 break;
1031 case ga_playdemo:
1032 G_DoPlayDemo ();
1033 break;
1034 case ga_completed:
1035 G_DoCompleted ();
1036 break;
1037 case ga_victory:
1038 F_StartFinale ();
1039 break;
1040 case ga_worlddone:
1041 G_DoWorldDone ();
1042 break;
1043 case ga_screenshot:
1044 V_ScreenShot ();
1045 players[consoleplayer].message = DEH_String("screen shot");
1046 gameaction = ga_nothing;
1047 break;
1048 case ga_nothing:
1049 break;
1053 // get commands, check consistancy,
1054 // and build new consistancy check
1055 buf = (gametic/ticdup)%BACKUPTICS;
1057 for (i=0 ; i<MAXPLAYERS ; i++)
1059 if (playeringame[i])
1061 cmd = &players[i].cmd;
1063 memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t));
1065 if (demoplayback)
1066 G_ReadDemoTiccmd (cmd);
1067 if (demorecording)
1068 G_WriteDemoTiccmd (cmd);
1070 // check for turbo cheats
1072 // check ~ 4 seconds whether to display the turbo message.
1073 // store if the turbo threshold was exceeded in any tics
1074 // over the past 4 seconds. offset the checking period
1075 // for each player so messages are not displayed at the
1076 // same time.
1078 if (cmd->forwardmove > TURBOTHRESHOLD)
1080 turbodetected[i] = true;
1083 if ((gametic & 31) == 0
1084 && ((gametic >> 5) % MAXPLAYERS) == i
1085 && turbodetected[i])
1087 static char turbomessage[80];
1088 extern char *player_names[4];
1089 sprintf (turbomessage, "%s is turbo!",player_names[i]);
1090 players[consoleplayer].message = turbomessage;
1091 turbodetected[i] = false;
1094 if (netgame && !netdemo && !(gametic%ticdup) )
1096 if (gametic > BACKUPTICS
1097 && consistancy[i][buf] != cmd->consistancy)
1099 I_Error ("consistency failure (%i should be %i)",
1100 cmd->consistancy, consistancy[i][buf]);
1102 if (players[i].mo)
1103 consistancy[i][buf] = players[i].mo->x;
1104 else
1105 consistancy[i][buf] = rndindex;
1110 // check for special buttons
1111 for (i=0 ; i<MAXPLAYERS ; i++)
1113 if (playeringame[i])
1115 if (players[i].cmd.buttons & BT_SPECIAL)
1117 switch (players[i].cmd.buttons & BT_SPECIALMASK)
1119 case BTS_PAUSE:
1120 paused ^= 1;
1121 if (paused)
1122 S_PauseSound ();
1123 else
1124 S_ResumeSound ();
1125 break;
1127 case BTS_SAVEGAME:
1128 if (!savedescription[0])
1129 strcpy (savedescription, "NET GAME");
1130 savegameslot =
1131 (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
1132 gameaction = ga_savegame;
1133 break;
1139 // Have we just finished displaying an intermission screen?
1141 if (oldgamestate == GS_INTERMISSION && gamestate != GS_INTERMISSION)
1143 WI_End();
1146 oldgamestate = gamestate;
1148 // do main actions
1149 switch (gamestate)
1151 case GS_LEVEL:
1152 P_Ticker ();
1153 ST_Ticker ();
1154 AM_Ticker ();
1155 HU_Ticker ();
1156 break;
1158 case GS_INTERMISSION:
1159 WI_Ticker ();
1160 break;
1162 case GS_FINALE:
1163 F_Ticker ();
1164 break;
1166 case GS_DEMOSCREEN:
1167 D_PageTicker ();
1168 break;
1174 // PLAYER STRUCTURE FUNCTIONS
1175 // also see P_SpawnPlayer in P_Things
1179 // G_InitPlayer
1180 // Called at the start.
1181 // Called by the game initialization functions.
1183 void G_InitPlayer (int player)
1185 player_t* p;
1187 // set up the saved info
1188 p = &players[player];
1190 // clear everything else to defaults
1191 G_PlayerReborn (player);
1198 // G_PlayerFinishLevel
1199 // Can when a player completes a level.
1201 void G_PlayerFinishLevel (int player)
1203 player_t* p;
1205 p = &players[player];
1207 memset (p->powers, 0, sizeof (p->powers));
1208 memset (p->cards, 0, sizeof (p->cards));
1209 p->mo->flags &= ~MF_SHADOW; // cancel invisibility
1210 p->extralight = 0; // cancel gun flashes
1211 p->fixedcolormap = 0; // cancel ir gogles
1212 p->damagecount = 0; // no palette changes
1213 p->bonuscount = 0;
1218 // G_PlayerReborn
1219 // Called after a player dies
1220 // almost everything is cleared and initialized
1222 void G_PlayerReborn (int player)
1224 player_t* p;
1225 int i;
1226 int frags[MAXPLAYERS];
1227 int killcount;
1228 int itemcount;
1229 int secretcount;
1231 memcpy (frags,players[player].frags,sizeof(frags));
1232 killcount = players[player].killcount;
1233 itemcount = players[player].itemcount;
1234 secretcount = players[player].secretcount;
1236 p = &players[player];
1237 memset (p, 0, sizeof(*p));
1239 memcpy (players[player].frags, frags, sizeof(players[player].frags));
1240 players[player].killcount = killcount;
1241 players[player].itemcount = itemcount;
1242 players[player].secretcount = secretcount;
1244 p->usedown = p->attackdown = true; // don't do anything immediately
1245 p->playerstate = PST_LIVE;
1246 p->health = deh_initial_health; // Use dehacked value
1247 p->readyweapon = p->pendingweapon = wp_pistol;
1248 p->weaponowned[wp_fist] = true;
1249 p->weaponowned[wp_pistol] = true;
1250 p->ammo[am_clip] = deh_initial_bullets;
1252 for (i=0 ; i<NUMAMMO ; i++)
1253 p->maxammo[i] = maxammo[i];
1258 // G_CheckSpot
1259 // Returns false if the player cannot be respawned
1260 // at the given mapthing_t spot
1261 // because something is occupying it
1263 void P_SpawnPlayer (mapthing_t* mthing);
1265 boolean
1266 G_CheckSpot
1267 ( int playernum,
1268 mapthing_t* mthing )
1270 fixed_t x;
1271 fixed_t y;
1272 subsector_t* ss;
1273 unsigned an;
1274 mobj_t* mo;
1275 int i;
1277 if (!players[playernum].mo)
1279 // first spawn of level, before corpses
1280 for (i=0 ; i<playernum ; i++)
1281 if (players[i].mo->x == mthing->x << FRACBITS
1282 && players[i].mo->y == mthing->y << FRACBITS)
1283 return false;
1284 return true;
1287 x = mthing->x << FRACBITS;
1288 y = mthing->y << FRACBITS;
1290 if (!P_CheckPosition (players[playernum].mo, x, y) )
1291 return false;
1293 // flush an old corpse if needed
1294 if (bodyqueslot >= BODYQUESIZE)
1295 P_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]);
1296 bodyque[bodyqueslot%BODYQUESIZE] = players[playernum].mo;
1297 bodyqueslot++;
1299 // spawn a teleport fog
1300 ss = R_PointInSubsector (x,y);
1301 an = ( ANG45 * (((unsigned int) mthing->angle)/45) ) >> ANGLETOFINESHIFT;
1303 mo = P_SpawnMobj (x+20*finecosine[an], y+20*finesine[an]
1304 , ss->sector->floorheight
1305 , MT_TFOG);
1307 if (players[consoleplayer].viewz != 1)
1308 S_StartSound (mo, sfx_telept); // don't start sound on first frame
1310 return true;
1315 // G_DeathMatchSpawnPlayer
1316 // Spawns a player at one of the random death match spots
1317 // called at level load and each death
1319 void G_DeathMatchSpawnPlayer (int playernum)
1321 int i,j;
1322 int selections;
1324 selections = deathmatch_p - deathmatchstarts;
1325 if (selections < 4)
1326 I_Error ("Only %i deathmatch spots, 4 required", selections);
1328 for (j=0 ; j<20 ; j++)
1330 i = P_Random() % selections;
1331 if (G_CheckSpot (playernum, &deathmatchstarts[i]) )
1333 deathmatchstarts[i].type = playernum+1;
1334 P_SpawnPlayer (&deathmatchstarts[i]);
1335 return;
1339 // no good spot, so the player will probably get stuck
1340 P_SpawnPlayer (&playerstarts[playernum]);
1344 // G_DoReborn
1346 void G_DoReborn (int playernum)
1348 int i;
1350 if (!netgame)
1352 // reload the level from scratch
1353 gameaction = ga_loadlevel;
1355 else
1357 // respawn at the start
1359 // first dissasociate the corpse
1360 players[playernum].mo->player = NULL;
1362 // spawn at random spot if in death match
1363 if (deathmatch)
1365 G_DeathMatchSpawnPlayer (playernum);
1366 return;
1369 if (G_CheckSpot (playernum, &playerstarts[playernum]) )
1371 P_SpawnPlayer (&playerstarts[playernum]);
1372 return;
1375 // try to spawn at one of the other players spots
1376 for (i=0 ; i<MAXPLAYERS ; i++)
1378 if (G_CheckSpot (playernum, &playerstarts[i]) )
1380 playerstarts[i].type = playernum+1; // fake as other player
1381 P_SpawnPlayer (&playerstarts[i]);
1382 playerstarts[i].type = i+1; // restore
1383 return;
1385 // he's going to be inside something. Too bad.
1387 P_SpawnPlayer (&playerstarts[playernum]);
1392 void G_ScreenShot (void)
1394 gameaction = ga_screenshot;
1399 // DOOM Par Times
1400 int pars[4][10] =
1402 {0},
1403 {0,30,75,120,90,165,180,180,30,165},
1404 {0,90,90,90,120,90,360,240,30,170},
1405 {0,90,45,90,150,90,90,165,30,135}
1408 // DOOM II Par Times
1409 int cpars[32] =
1411 30,90,120,120,90,150,120,120,270,90, // 1-10
1412 210,150,150,150,210,150,420,150,210,150, // 11-20
1413 240,150,180,150,150,300,330,420,300,180, // 21-30
1414 120,30 // 31-32
1419 // G_DoCompleted
1421 boolean secretexit;
1422 extern char* pagename;
1424 void G_ExitLevel (void)
1426 secretexit = false;
1427 gameaction = ga_completed;
1430 // Here's for the german edition.
1431 void G_SecretExitLevel (void)
1433 // IF NO WOLF3D LEVELS, NO SECRET EXIT!
1434 if ( (gamemode == commercial)
1435 && (W_CheckNumForName("map31")<0))
1436 secretexit = false;
1437 else
1438 secretexit = true;
1439 gameaction = ga_completed;
1442 void G_DoCompleted (void)
1444 int i;
1446 gameaction = ga_nothing;
1448 for (i=0 ; i<MAXPLAYERS ; i++)
1449 if (playeringame[i])
1450 G_PlayerFinishLevel (i); // take away cards and stuff
1452 if (automapactive)
1453 AM_Stop ();
1455 if (gamemode != commercial)
1457 // Chex Quest ends after 5 levels, rather than 8.
1459 if (gameversion == exe_chex)
1461 if (gamemap == 5)
1463 gameaction = ga_victory;
1464 return;
1467 else
1469 switch(gamemap)
1471 case 8:
1472 gameaction = ga_victory;
1473 return;
1474 case 9:
1475 for (i=0 ; i<MAXPLAYERS ; i++)
1476 players[i].didsecret = true;
1477 break;
1482 //#if 0 Hmmm - why?
1483 if ( (gamemap == 8)
1484 && (gamemode != commercial) )
1486 // victory
1487 gameaction = ga_victory;
1488 return;
1491 if ( (gamemap == 9)
1492 && (gamemode != commercial) )
1494 // exit secret level
1495 for (i=0 ; i<MAXPLAYERS ; i++)
1496 players[i].didsecret = true;
1498 //#endif
1501 wminfo.didsecret = players[consoleplayer].didsecret;
1502 wminfo.epsd = gameepisode -1;
1503 wminfo.last = gamemap -1;
1505 // wminfo.next is 0 biased, unlike gamemap
1506 if ( gamemode == commercial)
1508 if (secretexit)
1509 switch(gamemap)
1511 case 15: wminfo.next = 30; break;
1512 case 31: wminfo.next = 31; break;
1514 else
1515 switch(gamemap)
1517 case 31:
1518 case 32: wminfo.next = 15; break;
1519 default: wminfo.next = gamemap;
1522 else
1524 if (secretexit)
1525 wminfo.next = 8; // go to secret level
1526 else if (gamemap == 9)
1528 // returning from secret level
1529 switch (gameepisode)
1531 case 1:
1532 wminfo.next = 3;
1533 break;
1534 case 2:
1535 wminfo.next = 5;
1536 break;
1537 case 3:
1538 wminfo.next = 6;
1539 break;
1540 case 4:
1541 wminfo.next = 2;
1542 break;
1545 else
1546 wminfo.next = gamemap; // go to next level
1549 wminfo.maxkills = totalkills;
1550 wminfo.maxitems = totalitems;
1551 wminfo.maxsecret = totalsecret;
1552 wminfo.maxfrags = 0;
1553 if ( gamemode == commercial )
1554 wminfo.partime = TICRATE*cpars[gamemap-1];
1555 else
1556 wminfo.partime = TICRATE*pars[gameepisode][gamemap];
1557 wminfo.pnum = consoleplayer;
1559 for (i=0 ; i<MAXPLAYERS ; i++)
1561 wminfo.plyr[i].in = playeringame[i];
1562 wminfo.plyr[i].skills = players[i].killcount;
1563 wminfo.plyr[i].sitems = players[i].itemcount;
1564 wminfo.plyr[i].ssecret = players[i].secretcount;
1565 wminfo.plyr[i].stime = leveltime;
1566 memcpy (wminfo.plyr[i].frags, players[i].frags
1567 , sizeof(wminfo.plyr[i].frags));
1570 gamestate = GS_INTERMISSION;
1571 viewactive = false;
1572 automapactive = false;
1574 WI_Start (&wminfo);
1579 // G_WorldDone
1581 void G_WorldDone (void)
1583 gameaction = ga_worlddone;
1585 if (secretexit)
1586 players[consoleplayer].didsecret = true;
1588 if ( gamemode == commercial )
1590 switch (gamemap)
1592 case 15:
1593 case 31:
1594 if (!secretexit)
1595 break;
1596 case 6:
1597 case 11:
1598 case 20:
1599 case 30:
1600 F_StartFinale ();
1601 break;
1606 void G_DoWorldDone (void)
1608 gamestate = GS_LEVEL;
1609 gamemap = wminfo.next+1;
1610 G_DoLoadLevel ();
1611 gameaction = ga_nothing;
1612 viewactive = true;
1618 // G_InitFromSavegame
1619 // Can be called by the startup code or the menu task.
1621 extern boolean setsizeneeded;
1622 void R_ExecuteSetViewSize (void);
1624 char savename[256];
1626 void G_LoadGame (char* name)
1628 strcpy (savename, name);
1629 gameaction = ga_loadgame;
1632 #define VERSIONSIZE 16
1635 void G_DoLoadGame (void)
1637 int savedleveltime;
1639 gameaction = ga_nothing;
1641 save_stream = fopen(savename, "rb");
1643 if (save_stream == NULL)
1645 return;
1648 savegame_error = false;
1650 if (!P_ReadSaveGameHeader())
1652 fclose(save_stream);
1653 return;
1656 savedleveltime = leveltime;
1658 // load a base level
1659 G_InitNew (gameskill, gameepisode, gamemap);
1661 leveltime = savedleveltime;
1663 // dearchive all the modifications
1664 P_UnArchivePlayers ();
1665 P_UnArchiveWorld ();
1666 P_UnArchiveThinkers ();
1667 P_UnArchiveSpecials ();
1669 if (!P_ReadSaveGameEOF())
1670 I_Error ("Bad savegame");
1672 fclose(save_stream);
1674 if (setsizeneeded)
1675 R_ExecuteSetViewSize ();
1677 // draw the pattern into the back screen
1678 R_FillBackScreen ();
1683 // G_SaveGame
1684 // Called by the menu task.
1685 // Description is a 24 byte text string
1687 void
1688 G_SaveGame
1689 ( int slot,
1690 char* description )
1692 savegameslot = slot;
1693 strcpy (savedescription, description);
1694 sendsave = true;
1697 void G_DoSaveGame (void)
1699 char *savegame_file;
1700 char *temp_savegame_file;
1702 temp_savegame_file = P_TempSaveGameFile();
1703 savegame_file = P_SaveGameFile(savegameslot);
1705 // Open the savegame file for writing. We write to a temporary file
1706 // and then rename it at the end if it was successfully written.
1707 // This prevents an existing savegame from being overwritten by
1708 // a corrupted one, or if a savegame buffer overrun occurs.
1710 save_stream = fopen(temp_savegame_file, "wb");
1712 if (save_stream == NULL)
1714 return;
1717 savegame_error = false;
1719 P_WriteSaveGameHeader(savedescription);
1721 P_ArchivePlayers ();
1722 P_ArchiveWorld ();
1723 P_ArchiveThinkers ();
1724 P_ArchiveSpecials ();
1726 P_WriteSaveGameEOF();
1728 // Enforce the same savegame size limit as in Vanilla Doom,
1729 // except if the vanilla_savegame_limit setting is turned off.
1731 if (vanilla_savegame_limit && ftell(save_stream) > SAVEGAMESIZE)
1733 I_Error ("Savegame buffer overrun");
1736 // Finish up, close the savegame file.
1738 fclose(save_stream);
1740 // Now rename the temporary savegame file to the actual savegame
1741 // file, overwriting the old savegame if there was one there.
1743 remove(savegame_file);
1744 rename(temp_savegame_file, savegame_file);
1746 gameaction = ga_nothing;
1747 strcpy(savedescription, "");
1749 players[consoleplayer].message = DEH_String(GGSAVED);
1751 // draw the pattern into the back screen
1752 R_FillBackScreen ();
1757 // G_InitNew
1758 // Can be called by the startup code or the menu task,
1759 // consoleplayer, displayplayer, playeringame[] should be set.
1761 skill_t d_skill;
1762 int d_episode;
1763 int d_map;
1765 void
1766 G_DeferedInitNew
1767 ( skill_t skill,
1768 int episode,
1769 int map)
1771 d_skill = skill;
1772 d_episode = episode;
1773 d_map = map;
1774 gameaction = ga_newgame;
1778 void G_DoNewGame (void)
1780 demoplayback = false;
1781 netdemo = false;
1782 netgame = false;
1783 deathmatch = false;
1784 playeringame[1] = playeringame[2] = playeringame[3] = 0;
1785 respawnparm = false;
1786 fastparm = false;
1787 nomonsters = false;
1788 consoleplayer = 0;
1789 G_InitNew (d_skill, d_episode, d_map);
1790 gameaction = ga_nothing;
1793 // The sky texture to be used instead of the F_SKY1 dummy.
1794 extern int skytexture;
1797 void
1798 G_InitNew
1799 ( skill_t skill,
1800 int episode,
1801 int map )
1803 char *skytexturename;
1804 int i;
1806 if (paused)
1808 paused = false;
1809 S_ResumeSound ();
1813 if (skill > sk_nightmare)
1814 skill = sk_nightmare;
1817 // This was quite messy with SPECIAL and commented parts.
1818 // Supposedly hacks to make the latest edition work.
1819 // It might not work properly.
1820 if (episode < 1)
1821 episode = 1;
1823 if ( gamemode == retail )
1825 if (episode > 4)
1826 episode = 4;
1828 else if ( gamemode == shareware )
1830 if (episode > 1)
1831 episode = 1; // only start episode 1 on shareware
1833 else
1835 if (episode > 3)
1836 episode = 3;
1841 if (map < 1)
1842 map = 1;
1844 if ( (map > 9)
1845 && ( gamemode != commercial) )
1846 map = 9;
1848 M_ClearRandom ();
1850 if (skill == sk_nightmare || respawnparm )
1851 respawnmonsters = true;
1852 else
1853 respawnmonsters = false;
1855 if (fastparm || (skill == sk_nightmare && gameskill != sk_nightmare) )
1857 for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
1858 states[i].tics >>= 1;
1859 mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT;
1860 mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT;
1861 mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT;
1863 else if (skill != sk_nightmare && gameskill == sk_nightmare)
1865 for (i=S_SARG_RUN1 ; i<=S_SARG_PAIN2 ; i++)
1866 states[i].tics <<= 1;
1867 mobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT;
1868 mobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT;
1869 mobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT;
1873 // force players to be initialized upon first level load
1874 for (i=0 ; i<MAXPLAYERS ; i++)
1875 players[i].playerstate = PST_REBORN;
1877 usergame = true; // will be set false if a demo
1878 paused = false;
1879 demoplayback = false;
1880 automapactive = false;
1881 viewactive = true;
1882 gameepisode = episode;
1883 gamemap = map;
1884 gameskill = skill;
1886 viewactive = true;
1888 // Set the sky to use.
1890 // Note: This IS broken, but it is how Vanilla Doom behaves.
1891 // See http://doom.wikia.com/wiki/Sky_never_changes_in_Doom_II.
1893 // Because we set the sky here at the start of a game, not at the
1894 // start of a level, the sky texture never changes unless we
1895 // restore from a saved game. This was fixed before the Doom
1896 // source release, but this IS the way Vanilla DOS Doom behaves.
1898 if (gamemode == commercial)
1900 if (gamemap < 12)
1901 skytexturename = "SKY1";
1902 else if (gamemap < 21)
1903 skytexturename = "SKY2";
1904 else
1905 skytexturename = "SKY3";
1907 else
1909 switch (gameepisode)
1911 default:
1912 case 1:
1913 skytexturename = "SKY1";
1914 break;
1915 case 2:
1916 skytexturename = "SKY2";
1917 break;
1918 case 3:
1919 skytexturename = "SKY3";
1920 break;
1921 case 4: // Special Edition sky
1922 skytexturename = "SKY4";
1923 break;
1927 skytexturename = DEH_String(skytexturename);
1929 skytexture = R_TextureNumForName(skytexturename);
1932 G_DoLoadLevel ();
1937 // DEMO RECORDING
1939 #define DEMOMARKER 0x80
1942 void G_ReadDemoTiccmd (ticcmd_t* cmd)
1944 if (*demo_p == DEMOMARKER)
1946 // end of demo data stream
1947 G_CheckDemoStatus ();
1948 return;
1950 cmd->forwardmove = ((signed char)*demo_p++);
1951 cmd->sidemove = ((signed char)*demo_p++);
1953 // If this is a longtics demo, read back in higher resolution
1955 if (longtics)
1957 cmd->angleturn = *demo_p++;
1958 cmd->angleturn |= (*demo_p++) << 8;
1960 else
1962 cmd->angleturn = ((unsigned char) *demo_p++)<<8;
1965 cmd->buttons = (unsigned char)*demo_p++;
1968 // Increase the size of the demo buffer to allow unlimited demos
1970 static void IncreaseDemoBuffer(void)
1972 int current_length;
1973 byte *new_demobuffer;
1974 byte *new_demop;
1975 int new_length;
1977 // Find the current size
1979 current_length = demoend - demobuffer;
1981 // Generate a new buffer twice the size
1982 new_length = current_length * 2;
1984 new_demobuffer = Z_Malloc(new_length, PU_STATIC, 0);
1985 new_demop = new_demobuffer + (demo_p - demobuffer);
1987 // Copy over the old data
1989 memcpy(new_demobuffer, demobuffer, current_length);
1991 // Free the old buffer and point the demo pointers at the new buffer.
1993 Z_Free(demobuffer);
1995 demobuffer = new_demobuffer;
1996 demo_p = new_demop;
1997 demoend = demobuffer + new_length;
2000 void G_WriteDemoTiccmd (ticcmd_t* cmd)
2002 byte *demo_start;
2004 if (gamekeydown[key_demo_quit]) // press q to end demo recording
2005 G_CheckDemoStatus ();
2007 demo_start = demo_p;
2009 *demo_p++ = cmd->forwardmove;
2010 *demo_p++ = cmd->sidemove;
2012 // If this is a longtics demo, record in higher resolution
2014 if (longtics)
2016 *demo_p++ = (cmd->angleturn & 0xff);
2017 *demo_p++ = (cmd->angleturn >> 8) & 0xff;
2019 else
2021 *demo_p++ = cmd->angleturn >> 8;
2024 *demo_p++ = cmd->buttons;
2026 // reset demo pointer back
2027 demo_p = demo_start;
2029 if (demo_p > demoend - 16)
2031 if (vanilla_demo_limit)
2033 // no more space
2034 G_CheckDemoStatus ();
2035 return;
2037 else
2039 // Vanilla demo limit disabled: unlimited
2040 // demo lengths!
2042 IncreaseDemoBuffer();
2046 G_ReadDemoTiccmd (cmd); // make SURE it is exactly the same
2052 // G_RecordDemo
2054 void G_RecordDemo (char* name)
2056 int i;
2057 int maxsize;
2059 usergame = false;
2060 strcpy (demoname, name);
2061 strcat (demoname, ".lmp");
2062 maxsize = 0x20000;
2065 // @arg <size>
2066 // @category demo
2067 // @vanilla
2069 // Specify the demo buffer size (KiB)
2072 i = M_CheckParm ("-maxdemo");
2073 if (i && i<myargc-1)
2074 maxsize = atoi(myargv[i+1])*1024;
2075 demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL);
2076 demoend = demobuffer + maxsize;
2078 demorecording = true;
2082 void G_BeginRecording (void)
2084 int i;
2087 // @category demo
2089 // Record a high resolution "Doom 1.91" demo.
2092 longtics = M_CheckParm("-longtics") != 0;
2094 // If not recording a longtics demo, record in low res
2096 lowres_turn = !longtics;
2098 demo_p = demobuffer;
2100 // Save the right version code for this demo
2102 if (longtics)
2104 *demo_p++ = DOOM_191_VERSION;
2106 else
2108 *demo_p++ = DOOM_VERSION;
2111 *demo_p++ = gameskill;
2112 *demo_p++ = gameepisode;
2113 *demo_p++ = gamemap;
2114 *demo_p++ = deathmatch;
2115 *demo_p++ = respawnparm;
2116 *demo_p++ = fastparm;
2117 *demo_p++ = nomonsters;
2118 *demo_p++ = consoleplayer;
2120 for (i=0 ; i<MAXPLAYERS ; i++)
2121 *demo_p++ = playeringame[i];
2126 // G_PlayDemo
2129 char* defdemoname;
2131 void G_DeferedPlayDemo (char* name)
2133 defdemoname = name;
2134 gameaction = ga_playdemo;
2137 // Generate a string describing a demo version
2139 static char *DemoVersionDescription(int version)
2141 static char resultbuf[16];
2143 switch (version)
2145 case 104:
2146 return "v1.4";
2147 case 105:
2148 return "v1.5";
2149 case 106:
2150 return "v1.6/v1.666";
2151 case 107:
2152 return "v1.7/v1.7a";
2153 case 108:
2154 return "v1.8";
2155 case 109:
2156 return "v1.9";
2157 default:
2158 break;
2161 // Unknown version. Perhaps this is a pre-v1.4 IWAD? If the version
2162 // byte is in the range 0-4 then it can be a v1.0-v1.2 demo.
2164 if (version >= 0 && version <= 4)
2166 return "v1.0/v1.1/v1.2";
2168 else
2170 sprintf(resultbuf, "%i.%i (unknown)", version / 100, version % 100);
2171 return resultbuf;
2175 void G_DoPlayDemo (void)
2177 skill_t skill;
2178 int i, episode, map;
2179 int demoversion;
2181 gameaction = ga_nothing;
2182 demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC);
2184 demoversion = *demo_p++;
2186 if (demoversion == DOOM_VERSION)
2188 longtics = false;
2190 else if (demoversion == DOOM_191_VERSION)
2192 // demo recorded with cph's modified "v1.91" doom exe
2193 longtics = true;
2195 else
2197 char *message = "Demo is from a different game version!\n"
2198 "(read %i, should be %i)\n"
2199 "\n"
2200 "*** You may need to upgrade your version "
2201 "of Doom to v1.9. ***\n"
2202 " See: http://doomworld.com/files/patches.shtml\n"
2203 " This appears to be %s.";
2205 I_Error(message, demoversion, DOOM_VERSION,
2206 DemoVersionDescription(demoversion));
2209 skill = *demo_p++;
2210 episode = *demo_p++;
2211 map = *demo_p++;
2212 deathmatch = *demo_p++;
2213 respawnparm = *demo_p++;
2214 fastparm = *demo_p++;
2215 nomonsters = *demo_p++;
2216 consoleplayer = *demo_p++;
2218 for (i=0 ; i<MAXPLAYERS ; i++)
2219 playeringame[i] = *demo_p++;
2222 // @category demo
2224 // Play back a demo recorded in a netgame with a single player.
2227 if (playeringame[1] || M_CheckParm("-netdemo") > 0)
2229 netgame = true;
2230 netdemo = true;
2233 // don't spend a lot of time in loadlevel
2234 precache = false;
2235 G_InitNew (skill, episode, map);
2236 precache = true;
2237 starttime = I_GetTime ();
2239 usergame = false;
2240 demoplayback = true;
2244 // G_TimeDemo
2246 void G_TimeDemo (char* name)
2249 // @vanilla
2251 // Disable rendering the screen entirely.
2254 nodrawers = M_CheckParm ("-nodraw");
2257 // @vanilla
2259 // Disable blitting the screen.
2262 noblit = M_CheckParm ("-noblit");
2263 timingdemo = true;
2264 singletics = true;
2266 defdemoname = name;
2267 gameaction = ga_playdemo;
2272 ===================
2274 = G_CheckDemoStatus
2276 = Called after a death or level completion to allow demos to be cleaned up
2277 = Returns true if a new demo loop action will take place
2278 ===================
2281 boolean G_CheckDemoStatus (void)
2283 int endtime;
2285 if (timingdemo)
2287 float fps;
2288 int realtics;
2290 endtime = I_GetTime ();
2291 realtics = endtime - starttime;
2292 fps = ((float) gametic * TICRATE) / realtics;
2294 // Prevent recursive calls
2295 timingdemo = false;
2296 demoplayback = false;
2298 I_Error ("timed %i gametics in %i realtics (%f fps)",
2299 gametic, realtics, fps);
2302 if (demoplayback)
2304 W_ReleaseLumpName(defdemoname);
2305 demoplayback = false;
2306 netdemo = false;
2307 netgame = false;
2308 deathmatch = false;
2309 playeringame[1] = playeringame[2] = playeringame[3] = 0;
2310 respawnparm = false;
2311 fastparm = false;
2312 nomonsters = false;
2313 consoleplayer = 0;
2315 if (singledemo)
2316 I_Quit ();
2317 else
2318 D_AdvanceDemo ();
2320 return true;
2323 if (demorecording)
2325 *demo_p++ = DEMOMARKER;
2326 M_WriteFile (demoname, demobuffer, demo_p - demobuffer);
2327 Z_Free (demobuffer);
2328 demorecording = false;
2329 I_Error ("Demo %s recorded",demoname);
2332 return false;