1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: g_game.c 1566 2020-12-19 06:22:58Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 1998-2016 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 //
20 // $Log: g_game.c,v $
21 // Revision 1.50  2004/09/12 19:40:05  darkwolf95
22 // additional chex quest 1 support
23 //
24 // Revision 1.49  2004/07/27 08:19:35  exl
25 // New fmod, fs functions, bugfix or 2, patrol nodes
26 //
27 // Revision 1.48  2003/11/22 00:49:33  darkwolf95
28 //
29 // Revision 1.47  2003/11/22 00:22:09  darkwolf95
30 // get rid of FS hud pics on level exit and new game, also added exl's fix for clearing hub variables on new game
31 //
32 // Revision 1.46  2003/03/22 22:35:59  hurdler
33 //
34 // Revision 1.45  2002/09/27 16:40:08  tonyd
35 // First commit of acbot
36 //
37 // Revision 1.44  2002/08/24 22:42:02  hurdler
38 // Apply Robert Hogberg patches
39 //
40 // Revision 1.43  2001/12/26 17:24:46  hurdler
41 // Update Linux version
42 //
43 // Revision 1.42  2001/12/15 18:41:35  hurdler
44 // small commit, mainly splitscreen fix
45 //
46 // Revision 1.41  2001/08/20 20:40:39  metzgermeister
47 //
48 // Revision 1.40  2001/08/20 18:34:18  bpereira
49 // glide ligthing and map30 bug
50 //
51 // Revision 1.39  2001/08/12 15:21:04  bpereira
52 // see my log
53 //
54 // Revision 1.38  2001/08/02 19:15:59  bpereira
55 // fix player reset in secret level of doom2
56 //
57 // Revision 1.37  2001/07/16 22:35:40  bpereira
58 // - fixed crash of e3m8 in heretic
59 // - fixed crosshair not drawed bug
60 //
61 // Revision 1.36  2001/05/16 21:21:14  bpereira
62 // Revision 1.35  2001/05/03 21:22:25  hurdler
63 //
64 // Revision 1.34  2001/04/17 22:26:07  calumr
65 // Initial Mac add
66 //
67 // Revision 1.33  2001/04/01 17:35:06  bpereira
68 // Revision 1.32  2001/03/03 06:17:33  bpereira
69 // Revision 1.31  2001/02/24 13:35:19  bpereira
70 // Revision 1.30  2001/02/10 12:27:13  bpereira
71 //
72 // Revision 1.29  2001/01/25 22:15:41  bpereira
73 // added heretic support
74 //
75 // Revision 1.28  2000/11/26 20:36:14  hurdler
76 // Adding autorun2
77 //
78 // Revision 1.27  2000/11/11 13:59:45  bpereira
79 // Revision 1.26  2000/11/06 20:52:15  bpereira
80 // Revision 1.25  2000/11/04 16:23:42  bpereira
81 // Revision 1.24  2000/11/02 19:49:35  bpereira
82 //
83 // Revision 1.23  2000/11/02 17:50:06  stroggonmeth
84 // Big 3Dfloors & FraggleScript commit!!
85 //
86 // Revision 1.22  2000/10/21 08:43:28  bpereira
87 // Revision 1.21  2000/10/09 14:03:31  crashrl
88 // Revision 1.20  2000/10/08 13:30:00  bpereira
89 //
90 // Revision 1.19  2000/10/07 20:36:13  crashrl
91 // Added deathmatch team-start-sectors via sector/line-tag and linedef-type 1000-1031
92 //
93 // Revision 1.18  2000/10/01 10:18:17  bpereira
94 // Revision 1.17  2000/09/28 20:57:14  bpereira
95 // Revision 1.16  2000/08/31 14:30:55  bpereira
96 // Revision 1.15  2000/08/10 14:08:48  hurdler
97 // Revision 1.14  2000/04/30 10:30:10  bpereira
98 // Revision 1.13  2000/04/23 16:19:52  bpereira
99 //
100 // Revision 1.12  2000/04/19 10:56:51  hurdler
101 // commited for exe release and tag only
102 //
103 // Revision 1.11  2000/04/16 18:38:07  bpereira
104 //
105 // Revision 1.10  2000/04/11 19:07:23  stroggonmeth
106 // Finished my logs, fixed a crashing bug.
107 //
108 // Revision 1.9  2000/04/07 23:11:17  metzgermeister
109 // added mouse move
110 //
111 // Revision 1.8  2000/04/06 20:40:22  hurdler
112 // Mostly remove warnings under windows
113 //
114 // Revision 1.7  2000/04/04 00:32:45  stroggonmeth
115 // Initial Boom compatability plus few misc changes all around.
116 //
117 // Revision 1.6  2000/03/29 19:39:48  bpereira
118 //
119 // Revision 1.5  2000/03/23 22:54:00  metzgermeister
120 // added support for HOME/.legacy under Linux
121 //
122 // Revision 1.4  2000/02/27 16:30:28  hurdler
123 // dead player bug fix + add allowmlook <yes|no>
124 //
125 // Revision 1.3  2000/02/27 00:42:10  hurdler
126 // Revision 1.2  2000/02/26 00:28:42  hurdler
127 // Mostly bug fix (see borislog.txt 23-2-2000, 24-2-2000)
128 //
129 //
130 // DESCRIPTION:
131 //      game loop functions, events handling
132 //
133 //-----------------------------------------------------------------------------
134 
135 
136 // [WDJ] To show the demo version on the console
137 #define SHOW_DEMOVERSION
138 #define DEBUG_DEMO
139 // Stick with one demo version because of other ports, and have separate
140 // fields record DoomLegacy specific version and enables.
141 // This only changes the demo header, not the content.
142 // Writes demoversion 143 and up, and can read any DoomLegacy demo.
143 // Older DoomLegacy demos are demo versions 111..143.
144 
145 #include "doomincl.h"
146 #include "doomstat.h"
147 #include "command.h"
148 #include "console.h"
149 #include "dstrings.h"
150 
151 #include "d_main.h"
152 #include "d_net.h"
153 #include "d_netcmd.h"
154 #include "f_finale.h"
155 #include "p_setup.h"
156 #include "p_saveg.h"
157 
158 #include "i_system.h"
159 
160 #include "wi_stuff.h"
161 #include "am_map.h"
162 #include "m_random.h"
163 #include "p_local.h"
164 #include "p_tick.h"
165 
166 // SKY handling - still the wrong place.
167 #include "r_data.h"
168 #include "r_draw.h"
169 #include "r_main.h"
170 #include "r_sky.h"
171 #include "r_things.h"
172 
173 #include "s_sound.h"
174 
175 #include "g_game.h"
176 #include "g_input.h"
177 
178 //added:16-01-98:quick hack test of rocket trails
179 #include "p_fab.h"
180 #include "m_cheat.h"
181 #include "m_misc.h"
182 #include "m_menu.h"
183 #include "m_argv.h"
184 
185 #include "hu_stuff.h"
186 
187 #include "st_stuff.h"
188 
189 #include "keys.h"
190 #include "i_joy.h"
191 #include "w_wad.h"
192 #include "z_zone.h"
193 
194 #include "i_video.h"
195 #include "p_inter.h"
196 #include "p_info.h"
197 #include "byteptr.h"
198 #include "t_script.h"
199 
200 #include "b_game.h"	//added by AC for acbot
201 
202 
203 
204 
205 boolean G_CheckDemoStatus (void);
206 void    G_ReadDemoTiccmd (ticcmd_t* cmd,int playernum);
207 void    G_WriteDemoTiccmd (ticcmd_t* cmd,int playernum);
208 void    G_InitNew (skill_e skill, const char* mapname, boolean resetplayer);
209 
210 void    G_DoCompleted (void);
211 void    G_DoVictory (void);
212 void    G_DoWorldDone (void);
213 
214 
215 // demoversion the 'dynamic' version number, this should be == game VERSION
216 // when playing back demos, 'demoversion' receives the version number of the
217 // demo. At each change to the game play, demoversion is compared to
218 // the game version, if it's older, the changes are not done, and the older
219 // code is used for compatibility.
220 //
221 byte            demoversion;  // engine behavior version
222 uint16_t        demoversion_rev;  // demoversion and revision
223 
224 // Determined by menu selection, or demo.
225 skill_e         gameskill;
226 byte            gameepisode;  // current game episode number  1..4
227 byte            gamemap;      // current game map number 1..31
228 char            game_map_filename[MAX_WADPATH];      // an external wad filename
229 
230 
231 // Determined by gamemode and wad.
232 gamemode_e  gamemode = indetermined;   // Game Mode - identify IWAD as shareware, retail etc.
233 
234 // [WDJ] Enables for fast (test for zero) feature tests in the engine.
235 // Byte is efficient and fast to test for 0/1, int is not.
236 // EN_xxx are enables, value = 0/1.
237 // EV_xxx are enum,    when value = 0..255, use a byte for efficiency.
238 
239 // These are set from gamemode.  Still use gamemode in the main setup.
240 // Set by gamemode, but may be enabled by demos too.
241 byte  EN_doom_etc;  // doom, boom, mbf, common behavior  (not heretic, hexen, strife)
242 byte  EN_boom;  // Boom features (boom demo compatibility=0)
243 byte  EN_mbf;   // MBF (Marines Best Friend) enable (similar prboom mbf_features)
244 byte  EV_legacy; // DoomLegacy version, 0 when some other demo.
245 
246 // Raven: Heretic, Hexen, and Strife may be Raven, but code reader
247 // should not need to know that.  Keep names explicit for easy code reading.
248 byte  EN_heretic_hexen;  // common features
249 byte  EN_heretic;
250 byte  EN_hexen;
251 byte  EN_strife;
252 
253 // Secondary features.
254 // [WDJ] Prevent demo from altering user game settings.
255 // When a cv_ value range is less than 256, test the EV field (ON/OFF/ENUM).
256 // Demo settings may change the cv_xxx.EV fields or EN_xxx variables.
257 // The cv user settings will be restored after the demo by
258 // CV_Restore_User_Settings (setting .EV from .value).
259 // Derive EN_ enables for special code logic.  Need to be set in DemoAdapt
260 // so they are set properly for games and demos.
261 // Boom
262 byte  EN_variable_friction;  // Boom demo flag, Heretic, and Legacy.
263 byte  EN_pushers;
264 byte  EN_skull_bounce_fix;  // !comp[comp_soul]
265 byte  EN_skull_bounce_floor; // PrBoom has this enabled by comp level.
266 byte  EN_boom_physics; // !comp[comp_model]
267 byte  EN_blazing_double_sound; // comp[comp_blazing]
268 byte  EN_vile_revive_bug; // comp[comp_vile]
269 byte  EN_sleeping_sarg_bug;  // fixed PrBoom 4, no comp
270 byte  EN_doorlight; // !comp[comp_doorlight]
271 byte  EN_invul_god; // !comp[comp_god]
272 byte  EN_boom_floor; // !comp[comp_floors]
273 byte  EN_doom_movestep_bug; // comp[comp_moveblock]
274 // MBF  (1998-2000)
275 byte  EN_mbf_pursuit;   // !comp[comp_pursuit]
276 byte  EN_mbf_telefrag;  // !comp[comp_telefrag]
277 fixed_t EV_mbf_distfriend;
278 // Heretic, Hexen
279 byte  EN_inventory;
280 
281 #ifdef DOGS
282 byte  extra_dog_count = 0;
283 static   uint16_t  extra_dog_respawn = 0;  // save on extra tests
284 #define  EXTRA_DOG_RESPAWN_TIME   (5 * TICRATE)
285 #endif
286 
287 // Demo playback enables
288 static char * playdemo_name = NULL;  // malloc
289 static byte  EN_demotic_109;  // old demo tic format
290 static byte  EN_boom_longtics;  // 16 bit boom angle in demo tic
291 
292 
293 
294 // [WDJ] PrBoom compatibility enum, to read Boom demo.
295 enum {
296   comp_telefrag,
297   comp_dropoff,
298   comp_vile,
299   comp_pain,
300   comp_skull,
301   comp_blazing,
302   comp_doorlight,
303   comp_model,
304   comp_god,
305   comp_falloff,
306   comp_floors,
307   comp_skymap,
308   comp_pursuit,
309   comp_doorstuck,
310   comp_staylift,
311   comp_zombie,
312   comp_stairs,
313   comp_infcheat,
314   comp_zerotags,
315   comp_moveblock,
316   comp_respawn,
317   comp_sound,
318   comp_666,
319   comp_soul,
320   comp_maskedanim,
321   COMP_NUM,
322   COMP_TOTAL=32
323 };
324 
325 
326 // -------------------------------------------
327 // Boom and MBF compatibility flags
328 //   cv or EN : DoomLegacy handling of the demo flags.
329 // demo_compatibility  === (compatibility_level < boom_compatibility_compatibility)
330 //   EN_boom =  (compatibility_level >= boom_compatibility_compatibility)
331 // compatibility === (compatibility_level <= boom_compatibility_compatibility)
332 // mbf_features === (compatibility_level >= mbf_compatibility)
333 //   EN_mbf = (compatibility_level >= mbf_compatibility)
334 // compatibility flag in demo - turn off Boom features
335 //   EN_boom = ! compatibility // but is demoversion specific
336 // comp_telefrag - monsters used to telefrag only on MAP30,
337 //   now they do it for spawners only.
338 //   EN_telefrag = ! comp[comp_telefrag] // demo support only
339 // comp_dropoff - MBF encourages things to drop off of overhangs
340 //   cv_mbf_dropoff = ! comp[comp_dropoff]
341 // comp_vile - original Doom archville bugs like ghosts
342 //   EN_vile_revive_bug = comp[comp_vile]
343 // comp_pain - original Doom limits Pain Elementals from spawning too many skulls
344 //   EN_skull_limit = comp[comp_pain]
345 // comp_skull - original Doom let skulls be spit through walls by Pain Elementals
346 //   EN_old_pain_spawn = comp[comp_skull]
347 // comp_blazing - original Doom duplicated blazing door sound
348 //   EN_blazing_double_sound = comp[comp_blazing]
349 // comp_doorlight - MBF made door lighting changes more gradual
350 //   EN_doorlight = ! comp[comp_doorlight]
351 // comp_model - improvements to the game physics
352 //   EN_boom_physics = ! comp[comp_model]
353 // comp_god - fixes to God mode
354 // comp_falloff - MBF encourages things to drop off of overhangs
355 //   cv_mbf_falloff = ! comp[comp_falloff]
356 // comp_floors - fixes for moving floors bugs
357 // comp_skymap - original Doom invul skymap colormap
358 // comp_pursuit - MBF AI change, limited pursuit?
359 //   cv_mbf_pursuit = ! comp[comp_pursuit]
360 // comp_doorstuck - monsters stuck in doors fix
361 //   cv_mbf_doorstuck = ! comp[comp_doorstuck]
362 // comp_staylift - MBF AI change, monsters try to stay on lifts
363 //   cv_mbf_staylift = ! comp[comp_staylift]
364 // comp_zombie - prevent dead players triggering stuff
365 // comp_stairs - see p_floor.c
366 // comp_infcheat - FIXME
367 // comp_zerotags - allow zero tags in wads
368 // comp_moveblock - enables keygrab and mancubi shots going thru walls
369 //   EN_doom_movestep_bug = comp[comp_moveblock]
370 // comp_respawn - objects which aren't on the map at game start respawn at (0,0)
371 //   cph - this is the inverse of comp_respawnfix from eternity.
372 // comp_sound - see s_sound.c
373 // comp_666 - enables tag 666 in non-ExM8 levels
374 // comp_soul - enables lost souls bouncing (see P_ZMovement)
375 //   EN_skull_bounce_fix = ! comp[comp_soul] // normally on
376 // comp_maskedanim - 2s mid textures don't animate
377 
378 #if 0
379   static const struct {
380     complevel_t fix; // level at which fix/change was introduced
381     complevel_t opt; // level at which fix/change was made optional
382   } levels[] = {
383     // comp_doorstuck - monsters stuck in doors fix
384     { boom_202_compatibility, mbf_compatibility },
385 
386     // comp_vile - original Doom archville bugs like ghosts
387     // comp_pain - original Doom limits Pain Elementals from spawning too many skulls
388     // comp_skull - original Doom let skulls be spit through walls by Pain Elementals
389     // comp_blazing - original Doom duplicated blazing door sound
390     // comp_doorlight - MBF made door lighting changes more gradual
391     // comp_model - improvements to the game physics
392     // comp_god - fixes to God mode
393     // comp_skymap
394     // comp_zerotags - allow zero tags in wads */
395     { boom_compatibility (==boom_201_compatibility), mbf_compatibility },
396 
397     // comp_floors - fixes for moving floors bugs
398     // comp_stairs - see p_floor.c
399     { boom_compatibility_compatibility (==boom_200_compatibility), mbf_compatibility },
400 
401     // comp_telefrag - monsters used to telefrag only on MAP30, now they do it for spawners only
402     // comp_dropoff - MBF encourages things to drop off of overhangs
403     // comp_falloff - MBF encourages things to drop off of overhangs
404     // comp_pursuit - MBF AI change, limited pursuit?
405     // comp_staylift - MBF AI change, monsters try to stay on lifts
406     // comp_infcheat - FIXME
407     { mbf_compatibility, mbf_compatibility },
408 
409     // comp_zombie - prevent dead players triggering stuff
410     { lxdoom_1_compatibility, mbf_compatibility },
411     // comp_moveblock - enables keygrab and mancubi shots going thru walls
412     { lxdoom_1_compatibility, prboom_2_compatibility },
413     // comp_respawn - objects which aren't on the map at game start respawn at (0,0)
414     { prboom_2_compatibility, prboom_2_compatibility },
415     // comp_sound - see s_sound.c
416     { boom_compatibility_compatibility (==boom_200_compatibility), prboom_3_compatibility },
417     // comp_666 - enables tag 666 in non-ExM8 levels
418     { ultdoom_compatibility, prboom_4_compatibility },
419     // comp_soul - enables lost souls bouncing (see P_ZMovement)
420     { prboom_4_compatibility, prboom_4_compatibility },
421     // comp_maskedanim - 2s mid textures don't animate
422     { doom_1666_compatibility, prboom_4_compatibility },
423   };
424 #endif
425 
426 
427 // Engine state
428 gamestate_e     gamestate = GS_NULL;
429 gameaction_e    gameaction;
430 byte            paused;                 // multiple pause bits
431 
432 boolean         netgame;                // only true if packets are broadcast
433 boolean         multiplayer;
434 
435 // players and bots
436 byte            num_game_players = 0;      // number of actual players, from playeringame, incl bots
437 byte            playeringame[MAXPLAYERS];  // player active
438 byte            player_state[MAXPLAYERS];  // from where, and pending player
439 player_t        players[MAXPLAYERS];
440 
441 // [WDJ] Whenever assign to these must update the _ptr too.
442 // They are not changed anywhere as often as players[] appears in IF stmts.
443 int             consoleplayer;          // player taking events and displaying
444 int             displayplayer;          // view being displayed
445 int             displayplayer2 = -1;    // for splitscreen, -1 when not in use
446 int             statusbarplayer;        // player who's statusbar is displayed
447                                         // (for spying with F12)
448 
449 // [WDJ] Simplify every test against a player ptr, and splitscreen
450 player_t *      consoleplayer_ptr = &players[0];
451 player_t *      displayplayer_ptr = &players[0];
452 player_t *      displayplayer2_ptr = NULL;  // NULL when not in use
453 
454 tic_t           gametic;
455 tic_t           levelstarttic;          // gametic at level start
456 // [WDJ] Derived from PrBoom basetic.
457 // A tic that always starts at 0, and only runs while the demo runs.
458 tic_t           game_comp_tic;  // gametic - basetic
459 
460 
461 // [WDJ] Keep rarely used state information separate so cache usage is cleaner.
462 
463 // Support
464 boolean         precache = true;        // if true, load all graphics at start
465 boolean         gameplay_msg = false;   // enable game play message control
466 boolean         modifiedgame;           // Set if homebrew PWAD stuff has been added.
467 language_t      language = english;     // Language.
468 
469 // Intermission state
470 int             totalkills, totalitems, totalsecret;
471 wb_start_t      wminfo;                 // parms for world map / intermission
472 
473 // Demo state
474 #define DEMONAME_LEN  32
475 char            demoname[DEMONAME_LEN+5];
476 boolean         demorecording;
477 boolean         demoplayback;
478 byte*           demobuffer;
479 byte*           demo_p;
480 byte*           demoend;
481 boolean         singledemo;             // quit after playing a demo from cmdline
482 
483 // Timing demo state
484 boolean         timingdemo;             // if true, exit with report on completion
485 boolean         nodrawers;              // for comparative timing purposes
486 boolean         noblit;                 // for comparative timing purposes
487 tic_t           demostarttime;          // for comparative timing purposes
488 
489 
490 
491 void ShowMessage_OnChange(void);
492 void AllowTurbo_OnChange(void);
493 
494 CV_PossibleValue_t showmessages_cons_t[]={{0,"Off"},{1,"Minimal"},{2,"Play"},{3,"Verbose"},{4,"Debug"},{5,"Dev"},{0,NULL}};
495 CV_PossibleValue_t pickupflash_cons_t[]   ={{0,"Off"},{1,"Status"},{2,"Half"},{3,"Vanilla"},{0,NULL}};
496 
497 // [0]=main player [1]=splitscreen player
498 consvar_t cv_autorun[2] = {
499   {"autorun"     ,"0",CV_SAVE,CV_OnOff},
500   {"autorun2"    ,"0",CV_SAVE,CV_OnOff}
501 };
502 consvar_t cv_alwaysfreelook[2] = {
503   {"alwaysmlook" ,"0",CV_SAVE,CV_OnOff},
504   {"alwaysmlook2","0",CV_SAVE,CV_OnOff}
505 };
506 consvar_t cv_mouse_move[2] = {
507   {"mousemove"   ,"1",CV_SAVE,CV_OnOff},
508   {"mousemove2"  ,"1",CV_SAVE,CV_OnOff}
509 };
510 
511 consvar_t cv_mouse_invert     = {"invertmouse" ,"0",CV_SAVE,CV_OnOff};
512 #ifdef MOUSE2
513 consvar_t cv_mouse2_invert    = {"invertmouse2","0",CV_SAVE,CV_OnOff};
514 #endif
515 
516 CV_PossibleValue_t joy_deadzone_cons_t[]={{0,"MIN"},{20,"INC"},{2000,"MAX"},{0,NULL}};
517 consvar_t cv_joy_deadzone     = {"joydeadzone" ,"800",CV_SAVE,joy_deadzone_cons_t};
518 
519 consvar_t cv_showmessages     = {"showmessages","2",CV_SAVE | CV_CALL | CV_NOINIT,showmessages_cons_t,ShowMessage_OnChange};
520 consvar_t cv_pickupflash      = {"pickupflash" ,"1",CV_SAVE, pickupflash_cons_t};
521 consvar_t cv_weapon_recoil    = {"weaponrecoil","0",CV_SAVE | CV_NETVAR, CV_OnOff};  // Boom weapon recoil
522 
523 consvar_t cv_allowturbo       = {"allowturbo"  ,"0",CV_NETVAR | CV_CALL, CV_YesNo, AllowTurbo_OnChange};
524 consvar_t cv_allowjump        = {"allowjump"   ,"1",CV_NETVAR,CV_YesNo};
525 consvar_t cv_allowautoaim     = {"allowautoaim","1",CV_NETVAR,CV_YesNo};
526 //SoM: 3/28/2000: Working rocket jumping.
527 consvar_t cv_allowrocketjump  = {"allowrocketjump","0",CV_NETVAR,CV_YesNo};
528 consvar_t cv_allowmlook       = {"allowmlook"  ,"1",CV_NETVAR,CV_YesNo};
529 consvar_t cv_allowexitlevel   = {"allowexitlevel", "1", CV_NETVAR, CV_YesNo, NULL };
530 
531 // oof when hit 2s line (in PrBoom enabled by ! comp_sound)
532 consvar_t cv_oof_2s = {"oof_2s", "0", CV_SAVE|CV_CALL, CV_OnOff, DemoAdapt_p_map};
533 
534 #if MAXPLAYERS>32
535 #error please update "player_name" table using the new value for MAXPLAYERS
536 #endif
537 #if MAXPLAYERNAME!=21
538 #error please update "player_name" table using the new value for MAXPLAYERNAME
539 #endif
540 // changed to 2d array 19990220 by Kin
541 char    player_names[MAXPLAYERS][MAXPLAYERNAME] =
542 {
543     // THESE SHOULD BE AT LEAST MAXPLAYERNAME CHARS
544     "Player 1\0a123456789a\0",
545     "Player 2\0a123456789a\0",
546     "Player 3\0a123456789a\0",
547     "Player 4\0a123456789a\0",
548     "Player 5\0a123456789a\0",        // added 14-1-98 for support 8 players
549     "Player 6\0a123456789a\0",        // added 14-1-98 for support 8 players
550     "Player 7\0a123456789a\0",        // added 14-1-98 for support 8 players
551     "Player 8\0a123456789a\0",        // added 14-1-98 for support 8 players
552     "Player 9\0a123456789a\0",
553     "Player 10\0a123456789\0",
554     "Player 11\0a123456789\0",
555     "Player 12\0a123456789\0",
556     "Player 13\0a123456789\0",
557     "Player 14\0a123456789\0",
558     "Player 15\0a123456789\0",
559     "Player 16\0a123456789\0",
560     "Player 17\0a123456789\0",
561     "Player 18\0a123456789\0",
562     "Player 19\0a123456789\0",
563     "Player 20\0a123456789\0",
564     "Player 21\0a123456789\0",
565     "Player 22\0a123456789\0",
566     "Player 23\0a123456789\0",
567     "Player 24\0a123456789\0",
568     "Player 25\0a123456789\0",
569     "Player 26\0a123456789\0",
570     "Player 27\0a123456789\0",
571     "Player 28\0a123456789\0",
572     "Player 29\0a123456789\0",
573     "Player 30\0a123456789\0",
574     "Player 31\0a123456789\0",
575     "Player 32\0a123456789\0"
576 };
577 
578 
579 // DEATHMATCH
580 void Deathmatch_OnChange(void);
581 
582 // Coop 0: multiple players, coop map objects
583 // Deathmatch 1:  placed weapons respawn immediately
584 // Deathmatch 2:  items respawn
585 // Deathmatch 3:  items respawn, placed weapons respawn immediately
586 // Keep original values for demo and savegame compatibility.
587 CV_PossibleValue_t deathmatch_cons_t[] = {
588   {0x80, "Coop_SP_Map"}, {0x30, "Coop_60"}, {0x20, "Coop_80"}, {0x10, "Coop"}, {0, "Coop_weapons"},
589   {4, "DM"}, {1, "DM_weapons"}, {2, "DM_items"}, {3, "DM_both"}, {0, NULL} };
590 consvar_t cv_deathmatch = { "deathmatch", "0", CV_NETVAR | CV_CALL, deathmatch_cons_t, Deathmatch_OnChange };
591 
592 byte  deathmatch;
593 byte  weapon_persist;         // deathmatch weapon pickup multiple times
594 
595 // deathmatch (0..3)
596 byte  deathmatch_to_itemrespawn[4]    = { 0, 0, 1, 1 };
597 
Deathmatch_OnChange(void)598 void Deathmatch_OnChange(void)
599 {
600     deathmatch = (cv_deathmatch.EV < 0x0F)?  (cv_deathmatch.EV & 0x07) : 0;
601     weapon_persist = 0;
602 
603     if( cv_deathmatch.EV < 4 )
604     {
605         // [WDJ] Server and Client, change other cvar affected by deathmatch, locally.
606         // Do not modify cvar here, and expect NETVAR to update them in clients.
607 
608         // weapon respawn global enable
609         weapon_persist = (cv_deathmatch.EV != 2);  // only the orig modes
610 
611         // itemrespawn for deathmatch 2,3
612         CV_SetParam( &cv_itemrespawn, deathmatch_to_itemrespawn[ deathmatch & 0x03 ] );
613         // itemrespawntime is NETVAR, and will have been distributed at join game.
614 
615         // [WDJ] Respawn weapons in the itemrespawn queue, for weapon_persist.
616         // Fixed code inconsistency: it did not do this when going to coop mode.
617         if( weapon_persist )
618             P_RespawnWeapons();
619     }
620 
621     // give all key to the players
622     if( deathmatch )
623     {
624         int j;
625         for (j = 0; j < MAXPLAYERS; j++)
626         {
627             if (playeringame[j])
628                 players[j].cards = it_allkeys;
629         }
630     }
631 }
632 
633 
634 // TIMELIMIT, FRAGLIMIT
635 
636 void TimeLimit_OnChange(void);
637 consvar_t cv_timelimit = { "timelimit", "0", CV_NETVAR | CV_VALUE | CV_CALL | CV_NOINIT, CV_Unsigned, TimeLimit_OnChange };
638 
639 uint32_t  timelimit_tics = 0;
640 
TimeLimit_OnChange(void)641 void TimeLimit_OnChange(void)
642 {
643     // CV_VALUE, may be too large for EV
644     if (cv_timelimit.value)
645     {
646         GenPrintf(EMSG_hud, "Levels will end after %d minute(s).\n", cv_timelimit.value);
647         timelimit_tics = cv_timelimit.value * 60 * TICRATE;
648     }
649     else
650     {
651         GenPrintf(EMSG_hud, "Time limit disabled\n");
652         timelimit_tics = 0;
653     }
654 }
655 
656 void FragLimit_OnChange(void);
657 CV_PossibleValue_t fraglimit_cons_t[] = { {0, "MIN"}, {1000, "MAX"}, {0, NULL} };
658 consvar_t cv_fraglimit = { "fraglimit", "0", CV_NETVAR | CV_VALUE | CV_CALL | CV_NOINIT, fraglimit_cons_t, FragLimit_OnChange };
659 
FragLimit_OnChange(void)660 void FragLimit_OnChange(void)
661 {
662     int i;
663 
664     // CV_VALUE, may be too large for EV
665     if (cv_fraglimit.value > 0)
666     {
667         for (i = 0; i < MAXPLAYERS; i++)
668             P_CheckFragLimit(&players[i]);
669     }
670 }
671 
672 
673 // TEAM STATE
674 
675 team_info_t*  team_info[MAXTEAMS];  // allocated
676 byte  num_teams = 0;
677 
678 // Create the team if it does not exist.
get_team(int team_num)679 team_info_t*  get_team( int team_num )
680 {
681     if( team_num >= MAXTEAMS )
682         return NULL;
683 
684     // Create missing teams
685     while( team_num >= num_teams )
686     {
687         team_info[num_teams] = Z_Malloc( sizeof(team_info_t), PU_STATIC, NULL);
688         team_info[num_teams]->name = NULL;
689         num_teams++;
690     }
691     return team_info[team_num];
692 }
693 
694 // Set the team name.
695 // Create the team if it does not exist.
set_team_name(int team_num,const char * str)696 void  set_team_name( int team_num, const char * str )
697 {
698     // Create the team if it does not exist.
699     // Because of the complexity, for now, will create team at init.
700     get_team( team_num );
701 
702     if( team_num <= num_teams )
703     {
704         char * name = team_info[team_num]->name;
705         if( name )
706             Z_Free( name );
707         name = Z_Malloc( strlen(str)+6, PU_STATIC, NULL );
708         sprintf(name,"%s team", str);
709         team_info[team_num]->name = name;
710     }
711 }
712 
get_team_name(int team_num)713 char * get_team_name( int team_num )
714 {
715     if( team_num <= num_teams )
716     {
717         if( team_info[team_num]->name )
718             return team_info[team_num]->name;
719     }
720     return "Unknown team";
721 }
722 
723 
724 CV_PossibleValue_t teamplay_cons_t[] = { {0, "Off"}, {1, "Color"}, {2, "Skin"}, {3, NULL} };
725 
726 void  TeamPlay_OnChange( void );
727 consvar_t cv_teamplay = { "teamplay", "0", CV_NETVAR | CV_CALL, teamplay_cons_t, TeamPlay_OnChange };
728 consvar_t cv_teamdamage = { "teamdamage", "0", CV_NETVAR, CV_OnOff };
729 
730 
TeamPlay_OnChange(void)731 void TeamPlay_OnChange(void)
732 {
733     int i;
734     // Change the name of the teams
735 
736     if(cv_teamplay.EV == 1)
737     {
738         // color
739         for(i=0; i<NUMSKINCOLORS; i++)
740             set_team_name( i, Color_Names[i]);
741     }
742     else
743     if(cv_teamplay.EV == 2)
744     {
745         // skins
746         for(i=0; i<numskins; i++)
747             set_team_name( i, skins[i]->name);
748     }
749 }
750 
751 
752 // Support
753 
754 // Simplified body queue.  The Doom bodyqueue was way complicated.
755 // A way to have player corpses stay around, but limit how many.
756 mobj_t*   bodyque[BODYQUESIZE];
757 int       bodyqueslot;
758 
759 
760 void*     statcopy;                      // for statistics driver
761 
ShowMessage_OnChange(void)762 void ShowMessage_OnChange(void)
763 {
764     if( !cv_showmessages.EV )
765         CONS_Printf("%s\n",MSGOFF);
766     else
767         CONS_Printf("%s: %s\n",MSGON, cv_showmessages.string );
768 }
769 
770 
771 //  Build an original game map name from episode and map number,
772 //  based on the game mode (doom1, doom2...)
773 //
G_BuildMapName(int episode,int map)774 char* G_BuildMapName (int episode, int map)
775 {
776     static char  mapname[9];    // internal map name (wad resource name)
777 
778     if (gamemode==doom2_commercial)
779         strcpy (mapname, va("MAP%#02d",map));
780     else
781     {
782         mapname[0] = 'E';
783         mapname[1] = '0' + episode;
784         mapname[2] = 'M';
785         mapname[3] = '0' + map;
786         mapname[4] = 0;
787     }
788     return mapname;
789 }
790 
791 
792 //
793 //  Clip the console player mouse aiming to the current view,
794 //  also returns a signed char for the player ticcmd if needed.
795 //  Used whenever the player view pitch is changed manually
796 //
797 //added:22-02-98:
798 //changed:3-3-98: do a angle limitation now
G_ClipAimingPitch(angle_t aiming)799 angle_t G_ClipAimingPitch(angle_t aiming)
800 {
801   int32_t limitangle;
802 
803   //note: the current software mode implementation doesn't have true perspective
804   if (rendermode == render_soft)
805     limitangle = 732<<ANGLETOFINESHIFT;
806   else
807     limitangle = ANG90 - 1;
808 
809   int32_t p = aiming; // into signed to make comparisions simpler
810 
811   if (p > limitangle)
812     p = limitangle;
813   else if (p < -limitangle)
814     p = -limitangle;
815 
816   return p; // back into angle_t (unsigned)
817 }
818 
819 
820 //
821 // G_BuildTiccmd
822 // Builds a ticcmd from all of the available inputs
823 // or reads it from the demo buffer.
824 // If recording a demo, write it out
825 //
826 // set displayplayer2_ptr to build player 2's ticcmd in splitscreen mode
827 //
828 //  [0]=main player, [1]=splitscreen player
829 angle_t localaiming[2];
830 angle_t localangle[2];
831 
832 //added:06-02-98: mouseaiming (looking up/down with the mouse or keyboard)
833 #define KB_LOOKSPEED    (1<<25)
834 #define MAXPLMOVE		(forwardmove[1])
835 #define TURBOTHRESHOLD  0x32
836 #define SLOWTURNTICS    (6*NEWTICRATERATIO)
837 
838 static fixed_t forwardmove[2] = {25/NEWTICRATERATIO, 50/NEWTICRATERATIO};
839 static fixed_t sidemove[2]    = {24/NEWTICRATERATIO, 40/NEWTICRATERATIO};
840 static fixed_t angleturn[3]   = {640, 1280, 320};        // + slow turn
841 
842 
843 // for change this table change also nextweapon func in g_game and P_PlayerThink
844 char extraweapons[8]={wp_chainsaw,-1,wp_supershotgun,-1,-1,-1,-1,-1};
845 byte nextweaponorder[NUMWEAPONS]={wp_fist,wp_chainsaw,wp_pistol,
846      wp_shotgun,wp_supershotgun,wp_chaingun,wp_missile,wp_plasma,wp_bfg};
847 
NextWeapon(player_t * player,int step)848 byte NextWeapon(player_t *player,int step)
849 {
850     byte   w;
851     int    i;
852     for (i=0;i<NUMWEAPONS;i++)
853     {
854         if( player->readyweapon == nextweaponorder[i] )
855         {
856             i = (i+NUMWEAPONS+step)%NUMWEAPONS;
857             break;
858         }
859     }
860     for (;nextweaponorder[i]!=player->readyweapon; i=(i+NUMWEAPONS+step)%NUMWEAPONS)
861     {
862         w = nextweaponorder[i];
863 
864         // skip super shotgun for non-Doom2
865         if (gamemode!=doom2_commercial && w==wp_supershotgun)
866             continue;
867 
868         // skip plasma-bfg in sharware
869         if (gamemode==doom_shareware && (w==wp_plasma || w==wp_bfg))
870             continue;
871 
872         if ( player->weaponowned[w] &&
873              player->ammo[player->weaponinfo[w].ammo] >= player->weaponinfo[w].ammopershoot )
874         {
875             if(w==wp_chainsaw)
876                 return (BT_CHANGE | BT_EXTRAWEAPON | (wp_fist<<BT_WEAPONSHIFT));
877             if(w==wp_supershotgun)
878                 return (BT_CHANGE | BT_EXTRAWEAPON | (wp_shotgun<<BT_WEAPONSHIFT));
879             return (BT_CHANGE | (w<<BT_WEAPONSHIFT));
880         }
881     }
882     return 0;
883 }
884 
BestWeapon(player_t * player)885 byte BestWeapon(player_t *player)
886 {
887     int newweapon = FindBestWeapon(player);
888 
889     if (newweapon == player->readyweapon)
890         return 0;
891 
892     if (newweapon == wp_chainsaw)
893         return (BT_CHANGE | BT_EXTRAWEAPON | (wp_fist<<BT_WEAPONSHIFT));
894 
895     if (newweapon == wp_supershotgun)
896         return (BT_CHANGE | BT_EXTRAWEAPON | (wp_shotgun<<BT_WEAPONSHIFT));
897 
898     return (BT_CHANGE | (newweapon<<BT_WEAPONSHIFT));
899 }
900 
901 // pind : 0,1 for split player identity
902 static
G_InventoryResponder(player_t * ply,byte pind,int gc[num_gamecontrols][2],event_t * ev)903 boolean G_InventoryResponder(player_t *ply, byte pind,
904                              int gc[num_gamecontrols][2], event_t *ev)
905 {
906   // [WDJ] 1/9/2009 Do not get to process any keyup events, unless also saw
907   // the keydown event.  Now other Responders intercepting
908   // the keydown event work correctly.  Specifically heretic will no longer
909   // use up an inventory item when game saving.
910   static byte keyup_armed[2] = {0,0};   // player1, player2
911 
912   // Do not mess with inventory when menu or console are open.
913   if( menuactive || console_open )
914     return false;
915 
916   if (! EN_inventory)
917     return false;
918 
919   switch (ev->type)
920   {
921     case ev_keydown:
922       if( ev->data1 == gc[gc_invprev][0] || ev->data1 == gc[gc_invprev][1] )
923       {
924                 if( ply->st_inventoryTics )
925                 {
926                     ply->inv_ptr--;
927                     if( ply->inv_ptr < 0 )
928                         ply->inv_ptr = 0;
929                     else
930                     {
931                         ply->st_curpos--;
932                         if( ply->st_curpos < 0 )
933                             ply->st_curpos = 0;
934                     }
935                 }
936                 ply->st_inventoryTics = 5*TICRATE;
937                 goto used_key;
938       }
939       else if( ev->data1 == gc[gc_invnext][0] || ev->data1 == gc[gc_invnext][1] )
940       {
941                 if( ply->st_inventoryTics )
942                 {
943                     ply->inv_ptr++;
944                     if( ply->inv_ptr >= ply->inventorySlotNum )
945                     {
946                         ply->inv_ptr--;
947                         if( ply->inv_ptr < 0)
948                             ply->inv_ptr = 0;
949                     }
950                     else
951                     {
952                         ply->st_curpos++;
953                         if( ply->st_curpos > 6 )
954                             ply->st_curpos = 6;
955                     }
956                 }
957                 ply->st_inventoryTics = 5*TICRATE;
958                 goto used_key;
959       }
960       else if( ev->data1 == gc[gc_invuse ][0] || ev->data1 == gc[gc_invuse ][1] ){
961                 goto arm_key;
962       }
963 
964       break;
965 
966     case ev_keyup:
967       if( ev->data1 == gc[gc_invuse ][0] || ev->data1 == gc[gc_invuse ][1] )
968       {
969           if( keyup_armed[pind] )  // [WDJ] Only if the keydown was not intercepted by some other responder
970           {
971               if( ply->st_inventoryTics )
972                  ply->st_inventoryTics = 0;
973               else if( ply->inventory[ply->inv_ptr].count>0 )
974               {
975                   Send_NetXCmd_pind( XD_USEARTIFACT, &ply->inventory[ply->inv_ptr].type, 1, pind);
976               }
977               goto used_key;
978           }
979       }
980       else if( ev->data1 == gc[gc_invprev][0] || ev->data1 == gc[gc_invprev][1] ||
981                ev->data1 == gc[gc_invnext][0] || ev->data1 == gc[gc_invnext][1] )
982           goto used_key;
983       break;
984 
985     default:
986       break; // shut up compiler
987   }
988 
989   keyup_armed[pind] = 0;  // blanket unused
990   return false;
991 
992 arm_key:
993   keyup_armed[pind] = 1;  // ready for keyup event
994   return true;
995 
996 used_key:
997   keyup_armed[pind] = 0;  // used up
998   return true;
999 }
1000 
1001 
1002 
1003 //  pind : player index, [0]=main player, [1]=splitscreen player
G_BuildTiccmd(ticcmd_t * cmd,int realtics,byte pind)1004 void G_BuildTiccmd(ticcmd_t* cmd, int realtics, byte pind)
1005 {
1006     int         i;
1007 
1008     ticcmd_t *base = I_BaseTiccmd ();             // empty, or external driver
1009     memcpy (cmd,base,sizeof(*cmd));
1010 
1011 
1012     player_t *this_player;
1013     int (*gcc)[2];
1014 
1015 #define G_KEY_DOWN(k) (gamekeydown[gcc[(k)][0]] || gamekeydown[gcc[(k)][1]])
1016 #define G_KEY_PRESSED(k) (G_KEY_DOWN(k) || gamekeytapped[gcc[(k)][0]] || gamekeytapped[gcc[(k)][1]])
1017 
1018     angle_t pitch;
1019 
1020     if( pind == 0 )
1021     {
1022       this_player = consoleplayer_ptr;
1023       gcc = gamecontrol;
1024     } else {
1025       this_player = displayplayer2_ptr;
1026       gcc = gamecontrol2;
1027     }
1028     pitch = localaiming[pind];
1029 
1030     // Exit now if locked
1031     if (this_player->locked)
1032       goto done;
1033 
1034     // a little clumsy, but then the g_input.c became a lot simpler!
1035     boolean strafe = G_KEY_DOWN(gc_strafe);
1036     int speed  = G_KEY_DOWN(gc_speed) ^ cv_autorun[pind].EV;
1037 
1038     boolean turnright = G_KEY_DOWN(gc_turnright);
1039     boolean turnleft  = G_KEY_DOWN(gc_turnleft);
1040     boolean mouseaiming = G_KEY_DOWN(gc_mouseaiming) ^ cv_alwaysfreelook[pind].EV;
1041 
1042     int forward = 0, side = 0; // these must not wrap around, so we need bigger ranges than chars
1043 
1044     // strafing and yaw
1045     if (strafe)
1046     {
1047         if (turnright)
1048             side += sidemove[speed];
1049         if (turnleft)
1050             side -= sidemove[speed];
1051     }
1052     else
1053     {
1054       // use two stage accelerative turning
1055       // on the keyboard and joystick
1056       static int  turnheld[2];   // for accelerative turning
1057 
1058       if (turnleft || turnright)
1059         turnheld[pind] += realtics;
1060       else
1061         turnheld[pind] = 0;
1062 
1063       int tspeed = (turnheld[pind] < SLOWTURNTICS) ? 2 : speed;
1064 
1065       if (turnright)
1066         cmd->angleturn -= angleturn[tspeed];
1067       if (turnleft)
1068         cmd->angleturn += angleturn[tspeed];
1069     }
1070 
1071     // forwards/backwards, strafing
1072     if (G_KEY_DOWN(gc_forward))
1073         forward += forwardmove[speed];
1074     if (G_KEY_DOWN(gc_backward))
1075         forward -= forwardmove[speed];
1076     //added:07-02-98: some people strafe left & right with mouse buttons
1077     if (G_KEY_DOWN(gc_straferight))
1078         side += sidemove[speed];
1079     if (G_KEY_DOWN(gc_strafeleft))
1080         side -= sidemove[speed];
1081 
1082     //added:07-02-98: fire with any button/key
1083     if (G_KEY_DOWN(gc_fire))
1084         cmd->buttons |= BT_ATTACK;
1085 
1086     //added:07-02-98: use with any button/key
1087     if (G_KEY_DOWN(gc_use))
1088         cmd->buttons |= BT_USE;
1089 
1090     //added:22-02-98: jump button
1091     if (cv_allowjump.EV && G_KEY_DOWN(gc_jump))
1092         cmd->buttons |= BT_JUMP;
1093 
1094 
1095     //added:07-02-98: any key / button can trigger a weapon
1096     // chainsaw overrides
1097     if (G_KEY_PRESSED(gc_nextweapon))
1098       cmd->buttons |= NextWeapon(this_player,1);
1099     else if (G_KEY_PRESSED(gc_prevweapon))
1100       cmd->buttons |= NextWeapon(this_player,-1);
1101     else if (G_KEY_PRESSED(gc_bestweapon))
1102       cmd->buttons |= BestWeapon(this_player);
1103     else
1104     for (i=gc_weapon1; i<gc_weapon1+NUMWEAPONS-1; i++)
1105     {
1106         if (G_KEY_PRESSED(i))
1107         {
1108             cmd->buttons |= BT_CHANGE | BT_EXTRAWEAPON; // extra by default
1109             cmd->buttons |= (i-gc_weapon1)<<BT_WEAPONSHIFT;
1110             // already have extraweapon in hand switch to the normal one
1111             if (this_player->readyweapon == extraweapons[i-gc_weapon1])
1112                 cmd->buttons &= ~BT_EXTRAWEAPON;
1113             break;
1114         }
1115     }
1116 
1117 
1118     // pitch
1119     static byte  keyboard_look[2]; // true if lookup/down using keyboard
1120 
1121 
1122     // spring back if not using keyboard neither mouselookin'
1123     if (!keyboard_look[pind] && !mouseaiming)
1124         pitch = 0;
1125 
1126     if (G_KEY_DOWN(gc_lookup))
1127     {
1128         pitch += KB_LOOKSPEED;
1129         keyboard_look[pind] = true;
1130     }
1131     else
1132     if (G_KEY_DOWN(gc_lookdown))
1133     {
1134         pitch -= KB_LOOKSPEED;
1135         keyboard_look[pind] = true;
1136     }
1137     else
1138     if (G_KEY_PRESSED(gc_centerview))
1139     {
1140         pitch = 0;
1141         keyboard_look[pind] = false;
1142     }
1143 
1144     // mice
1145 
1146     // mouse look stuff (mouse look is not the same as mouse aim)
1147     if (pind == 0)
1148     {
1149       if (mouseaiming)
1150       {
1151         keyboard_look[pind] = false;
1152 
1153         // looking up/down
1154         if (cv_mouse_invert.EV)
1155             pitch -= mousey<<19;
1156         else
1157             pitch += mousey<<19;
1158       }
1159       else if (cv_mouse_move[0].EV)
1160         forward += mousey;
1161 
1162       if (strafe)
1163         side += mousex*2;
1164       else
1165         cmd->angleturn -= mousex*8;
1166 
1167       mousex = mousey = 0;
1168     }
1169     else
1170     {
1171       if (mouseaiming)
1172       {
1173         keyboard_look[pind] = false;
1174 
1175         // looking up/down
1176 #ifdef MOUSE2
1177         if (cv_mouse2_invert.EV)
1178           pitch -= mouse2y<<19;
1179         else
1180 #endif
1181           pitch += mouse2y<<19;
1182       }
1183       else if (cv_mouse_move[1].EV)
1184         forward += mouse2y;
1185 
1186       if (strafe)
1187         side += mouse2x*2;
1188       else
1189         cmd->angleturn -= mouse2x*8;
1190 
1191       mouse2x = mouse2y = 0;
1192     }
1193 
1194     // Finally the joysticks.
1195     for (i=0; i < num_joybindings; i++)
1196     {
1197       joybinding_t j = joybindings[i];
1198 
1199       if (j.playnum != pind)
1200         continue;
1201 
1202       int joyvalue = I_JoystickGetAxis(j.joynum, j.axisnum);
1203       if( joyvalue >= -cv_joy_deadzone.value && joyvalue <= cv_joy_deadzone.value)  joyvalue = 0; // Deadzone
1204       int value = (int)(j.scale * joyvalue);
1205 
1206       switch (j.action)
1207       {
1208         case ja_pitch:
1209           pitch = value << 16;
1210           break;
1211         case ja_move:
1212           if(speed == 1) value *= 2; // Double value if player is running
1213           forward += value;
1214           break;
1215         case ja_turn:
1216           cmd->angleturn += value;
1217           break;
1218         case ja_strafe:
1219           if(speed == 1) value *= 2; // Double value if player is running
1220           side += value;
1221           break;
1222         default:
1223           break;
1224       }
1225     }
1226 
1227 
1228     // Do not go faster than max. speed
1229     if (forward > MAXPLMOVE)
1230         forward = MAXPLMOVE;
1231     else if (forward < -MAXPLMOVE)
1232         forward = -MAXPLMOVE;
1233     if (side > MAXPLMOVE)
1234         side = MAXPLMOVE;
1235     else if (side < -MAXPLMOVE)
1236         side = -MAXPLMOVE;
1237 
1238     cmd->forwardmove += forward;
1239     cmd->sidemove += side;
1240 
1241     //26/02/2000: added by Hurdler: accept no mlook for network games
1242     if (!cv_allowmlook.EV)
1243         pitch = 0;
1244 
1245     pitch = G_ClipAimingPitch(pitch); // clip pitch to a reasonable sector
1246     cmd->aiming = pitch >> 16; // to short
1247 
1248     // Generated cmd are absolute angles
1249     localangle[pind] += (cmd->angleturn<<16);
1250     cmd->angleturn = localangle[pind] >> 16;
1251     localaiming[pind] = pitch;
1252 
1253     if( gamemode == heretic )
1254     {
1255         if (G_KEY_DOWN(gc_flydown))
1256 #ifdef TICCMD_148
1257             cmd->ticflags |= TC_flydown;
1258         else
1259             cmd->ticflags &= ~TC_flydown;
1260 #else
1261             cmd->angleturn |= BT_FLYDOWN;
1262         else
1263             cmd->angleturn &= ~BT_FLYDOWN;
1264 #endif
1265     }
1266 
1267  done:
1268     memset(gamekeytapped, 0, sizeof(gamekeytapped)); // we're done, reset key-tapping status
1269 #ifdef TICCMD_148
1270     cmd->ticflags |= TC_received;
1271 #endif
1272 }
1273 
1274 
1275 static fixed_t  originalforwardmove[2] = {0x19, 0x32};
1276 static fixed_t  originalsidemove[2] = {0x18, 0x28};
1277 
AllowTurbo_OnChange(void)1278 void AllowTurbo_OnChange(void)
1279 {
1280     if(!cv_allowturbo.EV && netgame)
1281     {
1282         // like turbo 100
1283         forwardmove[0] = originalforwardmove[0];
1284         forwardmove[1] = originalforwardmove[1];
1285         sidemove[0] = originalsidemove[0];
1286         sidemove[1] = originalsidemove[1];
1287     }
1288 }
1289 
1290 //  turbo <10-255>
1291 //
Command_Turbo_f(void)1292 void Command_Turbo_f (void)
1293 {
1294     int     scale = 200;
1295 
1296     if(!cv_allowturbo.EV && netgame)
1297     {
1298         CONS_Printf("This server don't allow turbo\n");
1299         return;
1300     }
1301 
1302     if (COM_Argc()!=2)
1303     {
1304         CONS_Printf("turbo <10-255> : set turbo");
1305         return;
1306     }
1307 
1308     scale = atoi (COM_Argv(1));
1309 
1310     if (scale < 10)
1311         scale = 10;
1312     if (scale > 255)
1313         scale = 255;
1314 
1315     CONS_Printf ("turbo scale: %i%%\n",scale);
1316 
1317     forwardmove[0] = originalforwardmove[0]*scale/100;
1318     forwardmove[1] = originalforwardmove[1]*scale/100;
1319     sidemove[0] = originalsidemove[0]*scale/100;
1320     sidemove[1] = originalsidemove[1]*scale/100;
1321 }
1322 
1323 
1324 //
1325 // G_DoLoadLevel
1326 //
G_DoLoadLevel(boolean resetplayer)1327 void G_DoLoadLevel (boolean resetplayer)
1328 {
1329     int             i;
1330 
1331     levelstarttic = gametic;        // for time calculation
1332     // [WDJ] Derived from PrBoom, gametic demosync.
1333     if( EN_boom && !EN_mbf )
1334         game_comp_tic = 0;  // Boom demos start at tic 0
1335 
1336     gameplay_msg = false;
1337 
1338     // Reset certain attributes
1339     // (should be in resetplayer 'if'?)
1340     fs_fadealpha = 0;
1341     extramovefactor = 0;
1342     jumpgravity = (6*FRACUNIT/NEWTICRATERATIO);  // re-init
1343     consoleplayer_ptr->locked = false;
1344 
1345     if (wipegamestate == GS_LEVEL)
1346         wipegamestate = GS_FORCEWIPE;  // force a wipe
1347 
1348     gamestate = GS_LEVEL;
1349 
1350     for (i=0 ; i<MAXPLAYERS ; i++)
1351     {
1352         if( resetplayer || (playeringame[i] && players[i].playerstate == PST_DEAD))
1353             players[i].playerstate = PST_REBORN;
1354         memset (players[i].frags,0,sizeof(players[i].frags));
1355         players[i].addfrags = 0;
1356     }
1357 
1358 #ifdef DOGS
1359     extra_dog_count = 0;
1360 #endif
1361 
1362     // game_map_filename is external wad
1363     if (!P_SetupLevel (gameepisode, gamemap, gameskill, game_map_filename ))
1364     {
1365         // fail so reset game stuff
1366         Command_ExitGame_f();
1367         return;
1368     }
1369 
1370     // [WDJ] Some demos specify a console player that does not exist.
1371     // This happens before demoplayback is set.
1372     // Have not been able to determine anything better to do.
1373     if ( consoleplayer_ptr->weaponinfo == NULL )
1374     {
1375         G_AddPlayer( consoleplayer );  // to prevent segfaults
1376         playeringame[consoleplayer] = true;  // needs an mobj
1377     }
1378 
1379     if( ! demoplayback )  // because demo sets it too
1380     {
1381         displayplayer = consoleplayer;          // view the guy you are playing
1382         displayplayer_ptr = consoleplayer_ptr;
1383     }
1384 
1385     if(!cv_splitscreen.EV)
1386     {
1387         // [WDJ] Changed to a testable off for player 2
1388         displayplayer2 = -1;
1389         displayplayer2_ptr = NULL;  // use as test for player2 active
1390     }
1391 
1392     DemoAdapt_bots();
1393     if( server )
1394     {
1395         B_Regulate_Bots( cv_bots.EV );
1396     }
1397 
1398     gameaction = ga_nothing;
1399 #ifdef PARANOIA
1400     Z_CheckHeap (-2);
1401 #endif
1402 
1403     if (camera.chase)
1404         P_ResetCamera ( displayplayer_ptr );
1405 
1406     // clear cmd building stuff
1407     memset(gamekeydown, 0, sizeof(gamekeydown));
1408     memset(gamekeytapped, 0, sizeof(gamekeytapped));
1409     mousex = mousey = mouse2x = mouse2y = 0;
1410     I_StartupMouse( true );  // play mode
1411 
1412     // [WDJ] In case demo is from other than player1 (from prboom, killough)
1413     ST_Start();
1414     // clear hud messages remains (usually from game startup)
1415     HU_Clear_FSPics();
1416     CON_Clear_HUD ();
1417 
1418     gameplay_msg = true;
1419 }
1420 
1421 //
1422 // G_Responder
1423 //  Get info needed to make ticcmd_ts for the players.
1424 //
1425 // return true if event is acted upon
G_Responder(event_t * ev)1426 boolean G_Responder (event_t* ev)
1427 {
1428     // allow spy mode changes even during the demo
1429     if (gamestate == GS_LEVEL && ev->type == ev_keydown
1430         && ev->data1 == KEY_F12
1431         && ( singledemo || ! deathmatch ) )
1432     {
1433         // spy mode
1434         do
1435         {
1436             displayplayer++;
1437             if (displayplayer == MAXPLAYERS)
1438                 displayplayer = 0;
1439         } while (!playeringame[displayplayer] && displayplayer != consoleplayer);
1440         displayplayer_ptr = &players[displayplayer];
1441 
1442         //added:16-01-98:change statusbar also if playingback demo
1443         if( singledemo )
1444             ST_Change_DemoView ();
1445 
1446         //added:11-04-98: tell who's the view
1447         GenPrintf(EMSG_hud, "Viewpoint : %s\n", player_names[displayplayer]);
1448         goto handled;
1449     }
1450 
1451     // any other key pops up menu if in demos
1452     if (gameaction == ga_nothing && !singledemo &&
1453         (demoplayback || gamestate == GS_DEMOSCREEN) )
1454     {
1455         if (ev->type == ev_keydown)
1456         {
1457             M_StartControlPanel ();
1458             goto handled;
1459         }
1460         goto rejected;
1461     }
1462 
1463     if (gamestate == GS_LEVEL)
1464     {
1465 #if 0
1466         if (devparm && ev->type == ev_keydown && ev->data1 == ';')
1467         {
1468             // added Boris : test different player colors
1469             consoleplayer_ptr->skincolor = (consoleplayer_ptr->skincolor+1) % NUMSKINCOLORS;
1470             consoleplayer_ptr->mo->flags |= (consoleplayer_ptr->skincolor)<<MF_TRANSSHIFT;
1471             G_DeathMatchSpawnPlayer (0);
1472             goto handled;
1473         }
1474 #endif
1475         if(!multiplayer)
1476         {
1477            if( cht_Responder (ev))
1478               goto handled;
1479         }
1480         if (HU_Responder (ev))
1481             goto handled; // chat ate the event
1482         if (ST_Responder (ev))
1483             goto handled; // status window ate it
1484         if (AM_Responder (ev))
1485             goto handled; // automap ate it
1486         if (G_InventoryResponder (consoleplayer_ptr, 0, gamecontrol, ev))
1487             goto handled;
1488         if (displayplayer2_ptr
1489             && G_InventoryResponder (displayplayer2_ptr, 1, gamecontrol2, ev))
1490             goto handled;
1491         //added:07-02-98: map the event (key/mouse/joy) to a gamecontrol
1492     }
1493 
1494     if (gamestate == GS_FINALE)
1495     {
1496         if (F_Responder (ev))
1497             goto handled;  // finale ate the event
1498     }
1499 
1500 
1501     // update keys current state
1502     G_MapEventsToControls (ev);
1503 
1504     switch (ev->type)
1505     {
1506       case ev_keydown:
1507         if (ev->data1 == KEY_PAUSE  // keyboard
1508             || ev->data1 == gamecontrol[gc_pause][0] || ev->data1 == gamecontrol[gc_pause][1] )  // joystick
1509         {
1510             COM_BufAddText("pause\n");
1511             goto handled;
1512         }
1513         goto handled;
1514 
1515       case ev_keyup:
1516         goto rejected;   // always let key up events filter down
1517 
1518       case ev_mouse:
1519         goto handled;  // eat events
1520 
1521       default:
1522         break;
1523     }
1524 
1525 rejected:
1526     return false;
1527 
1528 handled:
1529     return true;
1530 }
1531 
1532 
1533 //
1534 // G_Ticker
1535 // Make ticcmd_ts for the players.
1536 //
G_Ticker(void)1537 void G_Ticker (void)
1538 {
1539     int         i;
1540     int         buf;
1541     ticcmd_t*   cmd;
1542 
1543     // do player reborns if needed
1544     if( gamestate == GS_LEVEL )
1545     {
1546         for (i=0 ; i<MAXPLAYERS ; i++)
1547         {
1548             if (playeringame[i])
1549             {
1550                 if( players[i].playerstate == PST_REBORN )
1551                     G_DoReborn (i);
1552                 if( players[i].st_inventoryTics )
1553                     players[i].st_inventoryTics--;
1554             }
1555         }
1556 #ifdef DOGS
1557         // [WDJ] MBF dogs, extension for DoomLegacy.
1558         if( extra_dog_respawn )  // coop respawn
1559         {
1560             extra_dog_respawn--;  // counter
1561             if( extra_dog_respawn == 0 )
1562             {
1563                 G_SpawnExtraDog( NULL );
1564                 if( extra_dog_count < cv_mbf_dogs.EV )
1565                     extra_dog_count = EXTRA_DOG_RESPAWN_TIME;
1566             }
1567         }
1568 #endif
1569     }
1570 
1571     // Do things to change the game state.
1572     while (gameaction != ga_nothing)
1573     {
1574         switch (gameaction)
1575         {
1576             case ga_completed:
1577                 G_DoCompleted ();
1578                 G_Start_Intermission();
1579 #ifdef WAIT_GAME_START_INTERMISSION
1580                 // This must be after G_Start_Intermission because it overrides
1581                 // wait_game_start_timer that is set to default for deathmatch.
1582                 if( server )
1583                     SV_Add_game_start_waiting_players( 0 );
1584 #endif
1585                 break;
1586             case ga_worlddone:
1587                 G_DoWorldDone ();
1588                 break;
1589             case ga_playdemo:
1590                 G_DoPlayDemo( playdemo_name );
1591                 break;
1592             case ga_nothing:
1593                 break;
1594             default:
1595                 // [WDJ] Softer recovery.
1596                 I_SoftError("GAME: gameaction = %d\n", gameaction);
1597                 gameaction = ga_nothing;
1598                 break;
1599         }
1600     }
1601 
1602     // [WDJ] From PrBoom, MBF, EternityEngine
1603     // killough 9/29/98: Skip some commands while pausing during demo playback,
1604     // or while the menu is active.
1605     //
1606     // Do not increment game_comp_tic and skip processing if a demo being played back
1607     // is paused or if the menu is active while a non-net game is being played,
1608     // to maintain sync while allowing pauses.
1609     //
1610     // P_Ticker() does not stop netgames if a menu is activated, so
1611     // we do not need to stop if a menu is pulled up during netgames.
1612     if( (paused & 0x02)
1613         || (!demoplayback && menuactive && !netgame ) )
1614     {
1615        goto main_actions;
1616     }
1617 
1618     game_comp_tic++;  // For revenant tracers and RNG -- we must maintain sync
1619 
1620     buf = gametic%BACKUPTICS;
1621 
1622     // read/write demo and check turbo cheat
1623     for (i=0 ; i<MAXPLAYERS ; i++)
1624     {
1625         // BP: i==0 for playback of demos 1.29 now new players is added with xcmd
1626         if ((playeringame[i] || i==0) && !dedicated)
1627         {
1628             cmd = &players[i].cmd;
1629 
1630             // All clients run the bots, identically.
1631             if (players[i].bot)	//added by AC for acbot
1632                 B_BuildTiccmd(&players[i], &netcmds[buf][i]);
1633 
1634             if (demoplayback)
1635             {
1636                 G_ReadDemoTiccmd (cmd,i);
1637             }
1638             else
1639             {
1640                 // Copy the network ticcmd (and indirectly the local ticcmd) to the player.
1641                 memcpy (cmd, &netcmds[buf][i], sizeof(ticcmd_t));
1642             }
1643 
1644             if (demorecording)
1645                 G_WriteDemoTiccmd (cmd,i);
1646 
1647             // check for turbo cheats
1648             if (cmd->forwardmove > TURBOTHRESHOLD
1649                 && !(gametic % (32*NEWTICRATERATIO)) && ((gametic / (32*NEWTICRATERATIO))&3) == i )
1650             {
1651 #define TURBO_MSG_LEN  80
1652                 static char turbomessage[TURBO_MSG_LEN];
1653                 snprintf (turbomessage, TURBO_MSG_LEN-1, "%s is turbo!", player_names[i]);
1654                 turbomessage[TURBO_MSG_LEN-1] = 0;
1655                 consoleplayer_ptr->message = turbomessage;
1656             }
1657         }
1658     }
1659 
1660 main_actions:
1661 
1662     // do main actions
1663     switch (gamestate)
1664     {
1665       case GS_LEVEL:
1666         //IO_Color(0,255,0,0);
1667         // Apply player cmds to players, then run thinkers and level animations for this gametic.
1668         P_Ticker ();             // tic the game
1669         //IO_Color(0,0,255,0);
1670         ST_Ticker ();
1671         AM_Ticker ();
1672         HU_Ticker ();
1673         break;
1674 
1675       case GS_INTERMISSION:
1676         WI_Ticker ();
1677         break;
1678 
1679       case GS_FINALE:
1680         F_Ticker ();
1681         break;
1682 
1683       case GS_DEMOSCREEN:
1684         D_PageTicker ();
1685         break;
1686 
1687       case GS_WAITINGPLAYERS:
1688       case GS_DEDICATEDSERVER:
1689       case GS_NULL:
1690       default:
1691         // do nothing
1692         gameplay_msg = false;
1693         break;
1694     }
1695 }
1696 
1697 
1698 // Spawn Test
1699 // [WDJ] Use a temporary mobj to test position.
1700 // This can be modified to suit,
1701 // and being static does not leave dangling ptrs into stack space.
1702 // Need flags, flags2, radius, height.
1703 static mobj_t  tstobj;
1704 
1705 //  testtype: determines the test mobj size, how much radius is needed
1706 // Return false when an collision with an existing object would occur.
1707 static
test_spot_unoccupied(mobjtype_t testtype,fixed_t x,fixed_t y,fixed_t z)1708 boolean  test_spot_unoccupied( mobjtype_t testtype, fixed_t x, fixed_t y, fixed_t z )
1709 {
1710     // [WDJ] Test spawn spot for any player or blocking object.
1711     // Needed for spawning off of map spawn spots, and for multiple players.
1712     // Spawn test position is already set.
1713     // MF_SOLID is required to test against objects.
1714     // MF_PICKUP is off to prevent picking up objects.
1715     // MF2_PASSMOBJ is off, as in Heretic.
1716     tstobj.x = x;
1717     tstobj.y = y;
1718     tstobj.z = z;
1719     tstobj.player = NULL;  // so cannot take damage during test
1720     tstobj.type = testtype;
1721     tstobj.info = &mobjinfo[testtype];
1722     tstobj.radius = tstobj.info->radius;
1723     tstobj.height = tstobj.info->height;
1724     tstobj.flags2 = tstobj.info->flags2 & ~MF2_PASSMOBJ;
1725     tstobj.flags = MF_SOLID|MF_SHOOTABLE|MF_DROPOFF;
1726     return  P_CheckPosition( &tstobj, x, y );
1727 }
1728 
1729 
1730 boolean  G_Player_SpawnSpot( int playernum, mapthing_t* spot );
1731 
1732 
1733 // Extra dynamic spawn spots
1734 // Kept static because mobj will keep a ptr to it, and NULL is not good either.
1735 mapthing_t extra_coop_spawn;  // roving coop spawn
1736 // Static spawn index, so if a player gets a difficult spawn,
1737 // it does not repeat every spawn.
1738 static int32_t spind = 1;
1739 
1740 
1741 static
scatter_spawn(mobjtype_t spawn_type,int playernum,mapthing_t * spot)1742 boolean  scatter_spawn( mobjtype_t spawn_type, int playernum, mapthing_t * spot )
1743 {
1744     int i;
1745 
1746     extra_coop_spawn = * spot;  // copy, will be modified
1747     tstobj.x = spot->x<<16;  // spot map location
1748     tstobj.y = spot->y<<16;
1749 
1750     for(i=255; i>0; i--)
1751     {
1752         spind += 83;  // scatter the pattern with prime 83
1753         // The low 8 bits of rv will cycle through all patterns in 256 iter.
1754         // Range (-15..15) * (player_radius + 2)
1755         extra_coop_spawn.x = spot->x + (((spind & 0x0F) - 8) * 18);
1756         extra_coop_spawn.y = spot->y + ((((spind >> 4) & 0x0F) - 8) * 18);
1757 
1758         // Not allowed to cross any blocking lines, to keep it out of the void.
1759         if( P_CheckCrossLine( &tstobj, extra_coop_spawn.x<<16, extra_coop_spawn.y<<16 ) )  continue;
1760 
1761         if( spawn_type == MT_PLAYER )
1762         {
1763             if (G_Player_SpawnSpot(playernum, &extra_coop_spawn) )
1764                 return true;
1765         }
1766 #ifdef DOGS
1767         else
1768         {
1769             if (G_SpawnExtraDog(&extra_coop_spawn) )
1770                 return true;
1771         }
1772 #endif
1773     }
1774     return false;
1775 }
1776 
1777 
1778 
1779 #ifdef DOGS
1780 // [WDJ] Spawn dogs.
1781 // MBF spawns extra dogs at map load, only for single player.
1782 // DoomLegacy also spawns dogs for Coop.
1783 
1784 // Do not modify level map spot due to reusing them during coop.
1785 // A player may join and use a spot that was previously used for a dog.
1786 static mapthing_t  extra_dog_spot;
1787 
1788 // Spawn extra player dog.
1789 // These are dogs that start as player spots, limited by cv_mbf_dogs.
1790 // Dogs that are level map objects spawn normally, as monsters or friends.
G_SpawnExtraDog(mapthing_t * spot)1791 boolean  G_SpawnExtraDog( mapthing_t * spot )
1792 {
1793     fixed_t       x, y;
1794     sector_t *    sec;
1795 
1796     // Spawn a dog on one of the unused player starts, up to cv_mbf_dogs.
1797     // Avoid multiple dogs in case of multiple starts, using playerstart.
1798     // Extra playerstarts will already be voodoo doll spots.
1799     if( extra_dog_count >= cv_mbf_dogs.EV )  goto no_more_dogs;
1800 
1801     if( deathmatch )  goto no_more_dogs;
1802 
1803     if( spot == NULL )
1804     {
1805         // [WDJ] Have run out of start spots.
1806         // This is a recursive call.
1807         int i;
1808         // Try to spawn near one of the other player spots.
1809         for (i=0 ; i<MAXPLAYERS ; i++)
1810         {
1811             if( playerstarts[i] == NULL )  continue;
1812             if( scatter_spawn( MT_DOG, 0, playerstarts[i] )  )
1813                 return true;
1814         }
1815         return false;
1816     }
1817 
1818     // Avoid modify of level map things.
1819     memcpy( &extra_dog_spot, spot, sizeof(mapthing_t) );
1820 
1821     // killough 10/98: force it to be a friend
1822     // [WDJ] Make sure it is not blocked (MTF_MPSPAWN, MTF_NODM, MTF_NOCOOP).
1823     extra_dog_spot.options |= MTF_FRIEND | 0x07;
1824     extra_dog_spot.options &= ~(MTF_MPSPAWN | MTF_NODM | MTF_NOCOOP);
1825 
1826     // haleyjd 9/22/99: deh, bex substitution
1827     extra_dog_spot.type = ( helper_MT < ENDDOOM_MT )? helper_MT : MT_DOG;
1828 
1829     // [WDJ] Test spawn spot for any player or blocking object.
1830     // Use tstobj, so do not need player mobj.  There may not be one.
1831     // The spawn spot location.
1832     x = extra_dog_spot.x << FRACBITS;
1833     y = extra_dog_spot.y << FRACBITS;
1834     sec = R_PointInSubsector(x,y)->sector;
1835     if( ! test_spot_unoccupied( extra_dog_spot.type, x, y, sec->floorheight ) )
1836         return false;
1837 
1838     // SpawnMapthing keeps a reference to the spawn spot in the mobj.
1839     P_SpawnMapthing( &extra_dog_spot );
1840 
1841     extra_dog_count ++;
1842     return true;
1843 
1844 no_more_dogs:
1845     extra_dog_respawn = 0;
1846     return false;
1847 }
1848 
1849 // To deal with respawning dog issues.
G_KillDog(mobj_t * mo)1850 void  G_KillDog( mobj_t * mo )
1851 {
1852     if( mo->spawnpoint != &extra_dog_spot )  return;
1853 
1854     if( !(mo->flags & MF_FRIEND) )  return;
1855 
1856     if( multiplayer && ( ! deathmatch )  // coop respawn
1857         && !cv_respawnmonsters.EV  )  // not otherwise respawned
1858     {
1859         extra_dog_count --;
1860         if( extra_dog_respawn == 0 )
1861             extra_dog_respawn = EXTRA_DOG_RESPAWN_TIME;
1862     }
1863 }
1864 
1865 #endif
1866 
1867 //
1868 // PLAYER STRUCTURE FUNCTIONS
1869 // also see P_SpawnPlayer in P_Things
1870 //
1871 
1872 //
1873 // G_InitPlayer
1874 // Called at the start.
1875 // Called by the game initialization functions.
1876 //
1877 /* BP:UNUSED !
1878 void G_InitPlayer (int player)
1879 {
1880     player_t*   p;
1881 
1882     // set up the saved info
1883     p = &players[player];
1884 
1885     // clear everything else to defaults
1886     G_PlayerReborn (player);
1887 }
1888 */
1889 
1890 
1891 //
1892 // G_PlayerFinishLevel
1893 //  Can when a player completes a level.
1894 //
G_PlayerFinishLevel(int player)1895 void G_PlayerFinishLevel (int player)
1896 {
1897     player_t*  p;
1898     mobj_t * pmo;
1899     int        i;
1900 
1901     p = &players[player];
1902     pmo = p->mo;
1903     for(i=0; i<p->inventorySlotNum; i++)
1904     {
1905         if( p->inventory[i].count>1)
1906             p->inventory[i].count = 1;
1907     }
1908     if( ! deathmatch )
1909     {
1910         for(i = 0; i < MAXARTECONT; i++)
1911             P_PlayerUseArtifact(p, arti_fly);
1912     }
1913     memset (p->powers, 0, sizeof (p->powers));
1914     if( gamemode == heretic )
1915         p->weaponinfo = wpnlev1info;    // cancel power weapons
1916     else
1917         p->weaponinfo = doomweaponinfo;
1918     p->cards = 0;
1919     if( pmo )
1920         pmo->flags &= ~MF_SHADOW;       // cancel invisibility
1921     p->extralight = 0;                  // cancel gun flashes
1922     p->fixedcolormap = 0;               // cancel ir gogles
1923     p->damagecount = 0;                 // no palette changes
1924     p->bonuscount = 0;
1925     p->health_pickup = 0;
1926     p->armor_pickup = 0;
1927     p->weapon_pickup = 0;
1928     p->ammo_pickup = 0;
1929     p->key_pickup = 0;
1930     p->aiming = 0;  // reset freelook
1931 
1932     if(p->chickenTics)
1933     {
1934         if( pmo )
1935             p->readyweapon = pmo->special1; // Restore weapon
1936         p->chickenTics = 0;
1937     }
1938     p->rain1 = NULL;
1939     p->rain2 = NULL;
1940 }
1941 
1942 
1943 // added 2-2-98 for hacking with dehacked patch
1944 int initial_health=100; //MAXHEALTH;
1945 int initial_bullets=50;
1946 
1947 void VerifFavoritWeapon (player_t *player);
1948 
1949 //
1950 // G_PlayerReborn
1951 // Called after a player dies
1952 // almost everything is cleared and initialized
1953 //
1954 // Called by P_SpawnPlayer when PST_REBORN.
G_PlayerReborn(int player)1955 void G_PlayerReborn (int player)
1956 {
1957     player_t*   p;
1958     int         i;
1959     uint16_t    frags[MAXPLAYERS];
1960     uint16_t    addfrags;
1961     int         killcount;
1962     int         itemcount;
1963     int         secretcount;
1964 
1965     //from Boris
1966     int         skincolor;
1967     char        favoritweapon[NUMWEAPONS];
1968     boolean     originalweaponswitch;
1969     boolean     autoaim;
1970     int         skin;   //Fab: keep same skin
1971 #ifdef CLIENTPREDICTION2
1972     mobj_t      *spirit;
1973 #endif
1974     bot_t*      bot;	//added by AC for acbot
1975 
1976     memcpy (frags,players[player].frags,sizeof(frags));
1977     addfrags = players[player].addfrags;
1978     killcount = players[player].killcount;
1979     itemcount = players[player].itemcount;
1980     secretcount = players[player].secretcount;
1981 
1982     //from Boris
1983     skincolor = players[player].skincolor;
1984     originalweaponswitch = players[player].originalweaponswitch;
1985     memcpy (favoritweapon,players[player].favoritweapon,NUMWEAPONS);
1986     autoaim   = players[player].autoaim_toggle;
1987     skin = players[player].skin;
1988 #ifdef CLIENTPREDICTION2
1989     spirit = players[player].spirit;
1990 #endif
1991     bot = players[player].bot;	//added by AC for acbot
1992 
1993     p = &players[player];
1994     memset (p, 0, sizeof(*p));
1995 
1996     memcpy (players[player].frags, frags, sizeof(players[player].frags));
1997     players[player].addfrags=addfrags;
1998     players[player].killcount = killcount;
1999     players[player].itemcount = itemcount;
2000     players[player].secretcount = secretcount;
2001 
2002     // save player config truth reborn
2003     players[player].skincolor = skincolor;
2004     players[player].originalweaponswitch = originalweaponswitch;
2005     memcpy (players[player].favoritweapon,favoritweapon,NUMWEAPONS);
2006     players[player].autoaim_toggle = autoaim;
2007     players[player].skin = skin;
2008 #ifdef CLIENTPREDICTION2
2009     players[player].spirit = spirit;
2010 #endif
2011     players[player].bot = bot;	//added by AC for acbot
2012 
2013     p->usedown = p->attackdown = true;  // don't do anything immediately
2014     p->playerstate = PST_LIVE;
2015     p->health = initial_health;
2016     if( gamemode == heretic )
2017     {
2018         // wand start
2019         p->weaponinfo = wpnlev1info;
2020         p->readyweapon = p->pendingweapon = wp_goldwand;
2021         p->weaponowned[wp_staff] = true;
2022         p->weaponowned[wp_goldwand] = true;
2023         p->ammo[am_goldwand] = 50;
2024     }
2025     else
2026     {
2027         // pistol start
2028         p->weaponinfo = doomweaponinfo;
2029         p->readyweapon = p->pendingweapon = wp_pistol;
2030         p->weaponowned[wp_fist] = true;
2031         p->weaponowned[wp_pistol] = true;
2032         p->ammo[am_clip] = initial_bullets;
2033     }
2034 
2035     // Boris stuff
2036     if(!p->originalweaponswitch)
2037         VerifFavoritWeapon(p);
2038     //eof Boris
2039 
2040     for (i=0 ; i<NUMAMMO ; i++)
2041         p->maxammo[i] = maxammo[i];
2042 }
2043 
2044 
2045 //
2046 // G_Player_SpawnSpot
2047 // Returns false if the player cannot be respawned
2048 // at the given mapthing_t spot because something is occupying it
2049 // Generate spawn fog.
2050 // Spawn player at the spot.
2051 //
2052 //  playernum : the player to check and spawn
2053 //  spot : the level map spawn spot
2054 // Return true when spawn spot is clear.
G_Player_SpawnSpot(int playernum,mapthing_t * spot)2055 boolean  G_Player_SpawnSpot( int playernum, mapthing_t* spot )
2056 {
2057     fixed_t       x, y;
2058     subsector_t*  ss;
2059     sector_t *    ssec;
2060     mobj_t*       mo;
2061     player_t *    player = & players[playernum];
2062 
2063     // added 25-4-98 : maybe there is no player start
2064     if(!spot || spot->type<0)   goto failexit;
2065     if( ! player )   goto failexit;
2066 
2067     // [WDJ] kill bob momentum or player will keep bobbing at spawn spot
2068     player->bob_momx = player->bob_momy = 0;
2069     player->aiming = 0;  // reset freelook
2070 
2071     // The spawn spot location
2072     x = spot->x << FRACBITS;
2073     y = spot->y << FRACBITS;
2074 
2075 #if 0
2076     // First spawn with no checks, is only kept because it is in Doom.
2077     // This does not work after any player moves an inch,
2078     // such as joining a network game.
2079     // Using static tstobj, tests now can be done without player->mo.
2080     if ((gametic == levelstarttic) && (player->mo == NULL))
2081     {
2082         int i;
2083         // first spawn of level, before corpses
2084         // Not all player[i].mo are init yet, see P_SetupLevel().
2085         for (i=0 ; i<playernum ; i++)
2086         {
2087             // Check if another player is on this spot.
2088             // added 15-1-98 check if player is in game (mistake from id)
2089             if (playeringame[i]
2090                 && players[i].mo->x == x
2091                 && players[i].mo->y == y)
2092                 goto failexit;
2093         }
2094         // No fog, and no spawn sound.
2095         goto silent_spawn;
2096     }
2097 #endif
2098 
2099     ss = R_PointInSubsector (x,y);
2100     ssec = ss->sector;
2101 
2102     // check for respawn in team-sector.
2103     // Dogs do not know how to play teams.
2104     if(ssec->teamstartsec)
2105     {
2106         if(cv_teamplay.EV == 1)
2107         {
2108             // color
2109             if(player->skincolor!=(ssec->teamstartsec-1)) // -1 because wanted to know when it is set
2110                 goto failexit;
2111         }
2112         else
2113         if(cv_teamplay.EV == 2)
2114         {
2115             // skins
2116             if(player->skin!=(ssec->teamstartsec-1)) // -1 because wanted to know when it is set
2117                 goto failexit;
2118         }
2119     }
2120 
2121     // [WDJ] Test spawn spot for any player or blocking object.
2122     // Use tstobj, so do not need player mobj.  There may not be one.
2123     if( ! test_spot_unoccupied( MT_PLAYER, x, y, ssec->floorheight ) )
2124         goto failexit;
2125 
2126     // Spawn Spot accepted. Start spawn process.
2127 
2128     // If there is no corpse, there is no respawn fog nor sound.
2129     if( player->mo == NULL )   goto silent_spawn;
2130 
2131     // Flush an old corpse from queue, if needed.
2132     if (bodyqueslot >= BODYQUESIZE)
2133         P_RemoveMobj (bodyque[bodyqueslot%BODYQUESIZE]);
2134     // Put player mobj in the body queue.
2135     bodyque[bodyqueslot%BODYQUESIZE] = player->mo;
2136     bodyqueslot++;
2137 
2138     // spawn a teleport fog
2139     // [WDJ] Note prboom fix doc (cph 2001/04/05), of Vanilla Doom bug found by Ville Vuorinen.
2140     // Signed mapthing_t angle would create table lookup using negative index.
2141     // Current prboom emulates buggy code that accesses outside of finecosine,
2142     // finecosine[-4096] -> finetangent[2048].
2143     // This code fixes the bug.
2144     // Unsigned fine angle worked, but better to use angle_t conversion too,
2145     // which is done by wad_to_angle returning unsigned angle_t.
2146     int angf = ANGLE_TO_FINE( wad_to_angle(spot->angle) );
2147 
2148     mo = P_SpawnMobj (x+20*finecosine[angf], y+20*finesine[angf],
2149                       ssec->floorheight, MT_TFOG);
2150 
2151     //added:16-01-98:consoleplayer -> displayplayer (hear snds from viewpt)
2152     // removed 9-12-98: why not ????
2153     if ( displayplayer_ptr->viewz != 1 )
2154         S_StartObjSound(mo, sfx_telept);  // don't start sound on first frame
2155 
2156 silent_spawn:
2157     // Spawn the player at the spawn spot.
2158     P_SpawnPlayer (spot, playernum);
2159     return true;
2160 
2161 failexit:
2162     return false;
2163 }
2164 
2165 
2166 //
2167 // G_DeathMatchSpawnPlayer
2168 // Spawns a player at one of the random death match spots
2169 // called at level load and each death
2170 //
2171 // Return true when spawned.
G_DeathMatchSpawnPlayer(int playernum)2172 boolean G_DeathMatchSpawnPlayer (int playernum)
2173 {
2174     int  i,j,n;
2175 
2176     if( !numdmstarts )
2177     {
2178         I_SoftError("No deathmatch start in this map!");
2179         return false;
2180     }
2181 
2182     if( EV_legacy < 123 )
2183         n=20;  // Doom, Boom
2184     else
2185         n=64;
2186 
2187     // Random select a deathmatch spot.  Try n times for an unoccupied one.
2188     for (j=0 ; j<n ; j++)
2189     {
2190         i = PP_Random(pr_dmspawn) % numdmstarts;
2191         if( G_Player_SpawnSpot( playernum, deathmatchstarts[i]) )
2192             return true;
2193     }
2194 
2195     if(demoversion<113)
2196     {
2197         // Doom method of last recourse.
2198         // no good spot, so the player will probably get stuck
2199         P_SpawnPlayer (playerstarts[playernum], playernum);
2200         return true;
2201     }
2202 
2203     // [WDJ] Spawn at random offsets from the last random deathmatch spawn location.
2204     // This allows more players than spawn spots.
2205     if( deathmatchstarts[i] )
2206     {
2207         if( scatter_spawn( MT_PLAYER, playernum, deathmatchstarts[i] )  )
2208             return true;
2209     }
2210 
2211     return false;
2212 }
2213 
2214 // Will always spawn the player somewhere.
G_CoopSpawnPlayer(int playernum)2215 void G_CoopSpawnPlayer (int playernum)
2216 {
2217     mapthing_t * coop_spawn = playerstarts[playernum];
2218     int i;
2219 
2220     // Check for the COOP player spot unoccupied.
2221     if( G_Player_SpawnSpot(playernum, coop_spawn) )
2222         return;
2223 
2224     // Try to spawn at one of the other players spots.
2225     for (i=0 ; i<MAXPLAYERS ; i++)
2226     {
2227         if( G_Player_SpawnSpot( playernum, playerstarts[i]) )
2228             return;
2229     }
2230 
2231     if(demoversion<113)
2232     {
2233         // Doom method of last recourse.
2234         P_SpawnPlayer (coop_spawn, playernum);
2235         return;
2236     }
2237 
2238     // [WDJ] Spawn at random offsets from the coop_spawn location.
2239     // This allows more players than spawn spots.
2240     if( coop_spawn == NULL )
2241         coop_spawn = playerstarts[0];  // first player start should always exist
2242 
2243     if( coop_spawn )
2244     {
2245         if( scatter_spawn( MT_PLAYER, playernum, coop_spawn )  )
2246             return;
2247     }
2248 
2249     // Try to use a deathmatch spot.
2250     // No message about deathmatch starts in coop mode.
2251     if( numdmstarts && ( ! deathmatch ) )
2252     {
2253         if( G_DeathMatchSpawnPlayer( playernum )  )
2254             return;
2255     }
2256 
2257     // Probably will spawn within someone.
2258     P_SpawnPlayer (coop_spawn, playernum);
2259 }
2260 
2261 //
2262 // G_DoReborn
2263 //
2264 // Called from: P_SetupLevel, G_Ticker
G_DoReborn(int playernum)2265 void G_DoReborn (int playernum)
2266 {
2267     player_t*  player = &players[playernum];
2268 
2269     // boris comment : this test is like 'single player game'
2270     //                 all this kind of hiden variable must be removed
2271     if( (! multiplayer) && (! deathmatch) )
2272     {
2273         // reload the level from scratch
2274         G_DoLoadLevel (true);
2275     }
2276     else
2277     {
2278         // respawn at the start
2279 
2280         // first dissasociate the corpse
2281         if(player->mo)
2282         {
2283             player->mo->player = NULL;
2284             player->mo->flags2 &= ~MF2_DONTDRAW;
2285         }
2286         // spawn at random spot if in death match
2287         if( deathmatch )
2288         {
2289             if(G_DeathMatchSpawnPlayer (playernum))
2290                return;
2291             // use coop spots too if deathmatch spots occupied
2292         }
2293 
2294         G_CoopSpawnPlayer (playernum);
2295     }
2296 }
2297 
G_AddPlayer(int playernum)2298 void G_AddPlayer( int playernum )
2299 {
2300     player_t *p=&players[playernum];
2301 
2302     p->playerstate = PST_REBORN;
2303     memset(p->inventory, 0, sizeof( p->inventory ));
2304     p->inventorySlotNum = 0;
2305     p->inv_ptr = 0;
2306     p->st_curpos = 0;
2307     p->st_inventoryTics = 0;
2308 
2309     if( gamemode == heretic )
2310         p->weaponinfo = wpnlev1info;
2311     else
2312         p->weaponinfo = doomweaponinfo;
2313 }
2314 
2315 // [WDJ] Par times can now be modified.
2316 // DOOM Par Times
2317 int pars[4][10] =
2318 {
2319     {0},
2320     {0,30,75,120,90,165,180,180,30,165},
2321     {0,90,90,90,120,90,360,240,30,170},
2322     {0,90,45,90,150,90,90,165,30,135}
2323 };
2324 
2325 // DOOM II Par Times
2326 int cpars[32] =
2327 {
2328     30,90,120,120,90,150,120,120,270,90,        //  1-10
2329     210,150,150,150,210,150,420,150,210,150,    // 11-20
2330     240,150,180,150,150,300,330,420,300,180,    // 21-30
2331     120,30                                      // 31-32
2332 };
2333 
2334 
2335 //
2336 // G_DoCompleted
2337 //
2338 boolean         secretexit;
2339 
G_ExitLevel(void)2340 void G_ExitLevel (void)
2341 {
2342     if( gamestate==GS_LEVEL )
2343     {
2344         secretexit = false;
2345         gameaction = ga_completed;
2346     }
2347 }
2348 
2349 // Here's for the german edition.
G_SecretExitLevel(void)2350 void G_SecretExitLevel (void)
2351 {
2352     // IF NO WOLF3D LEVELS, NO SECRET EXIT!
2353     if ( (gamemode == doom2_commercial)
2354       && ( ! VALID_LUMP( W_CheckNumForName("map31") ) ))
2355         secretexit = false;
2356     else
2357         secretexit = true;
2358     gameaction = ga_completed;
2359 }
2360 
G_DoCompleted(void)2361 void G_DoCompleted (void)
2362 {
2363     int  i;
2364 
2365     gameaction = ga_nothing;
2366 
2367     for (i=0 ; i<MAXPLAYERS ; i++)
2368     {
2369         if (playeringame[i])
2370             G_PlayerFinishLevel (i);        // take away cards and stuff
2371     }
2372 
2373     if (automapactive)
2374         AM_Stop ();
2375 
2376     automapactive = false;
2377 }
2378 
G_Start_Intermission(void)2379 void G_Start_Intermission( void )
2380 {
2381     int  i;
2382 
2383     if (gamemode != doom2_commercial)
2384     {
2385         switch(gamemap)
2386         {
2387           case 8:
2388             //BP add comment : no intermission screen
2389             if( deathmatch )
2390                 wminfo.lev_next = 0;
2391             else
2392             {
2393                 // also for heretic
2394                 // disconnect from network
2395                 CL_Reset();
2396                 F_StartFinale();
2397                 return;
2398             }
2399             break; // [WDJ] 4/11/2012  map 8 is not secret level, and prboom and boom do not fall thru here.
2400           case 9:
2401             for (i=0 ; i<MAXPLAYERS ; i++)
2402                 players[i].didsecret = true;
2403             break;
2404         }
2405     }
2406     //DarkWolf95: September 11, 2004: More chex stuff
2407     if (gamemode == chexquest1)
2408     {
2409         if( !modifiedgame && gamemap == 5 )  // original chexquest ends at E1M5
2410         {
2411             if( deathmatch )
2412                 wminfo.lev_next = 0;
2413             else
2414             {
2415                 CL_Reset();
2416                 F_StartFinale();
2417                 return;
2418             }
2419         }
2420     }
2421 
2422     if(!dedicated)
2423         wminfo.didsecret = consoleplayer_ptr->didsecret;
2424     // 0 based
2425     wminfo.epsd = gameepisode -1;
2426     wminfo.lev_prev = gamemap -1;
2427 
2428     // go to next level
2429     // wminfo.lev_next is 0 biased, unlike gamemap
2430     wminfo.lev_next = gamemap;
2431 
2432     // overwrite next level in some cases
2433     if (gamemode == doom2_commercial)
2434     {
2435         if (secretexit)
2436         {
2437             switch(gamemap)
2438             {
2439               case 15 : wminfo.lev_next = 30; break;
2440               case 31 : wminfo.lev_next = 31; break;
2441               default : wminfo.lev_next = 15;break;
2442             }
2443         }
2444         else
2445         {
2446             switch(gamemap)
2447             {
2448               case 31:
2449               case 32: wminfo.lev_next = 15; break;
2450               default: wminfo.lev_next = gamemap;
2451             }
2452         }
2453     }
2454     else
2455     if (gamemode == heretic)
2456     {
2457         static const int afterSecret[5] = { 7, 5, 5, 5, 4 };
2458         if (secretexit)
2459             wminfo.lev_next = 8;    // go to secret level
2460         else if (gamemap == 9)
2461             wminfo.lev_next = afterSecret[gameepisode-1]-1;
2462     }
2463     else
2464     {
2465         if (secretexit)
2466             wminfo.lev_next = 8;    // go to secret level
2467         else if (gamemap == 9)
2468         {
2469             // returning from secret level
2470             switch (gameepisode)
2471             {
2472               case 1 :  wminfo.lev_next = 3; break;
2473               case 2 :  wminfo.lev_next = 5; break;
2474               case 3 :  wminfo.lev_next = 6; break;
2475               case 4 :  wminfo.lev_next = 2; break;
2476               default : wminfo.lev_next = 0; break;
2477             }
2478         }
2479         else
2480         {
2481             if (gamemap == 8)
2482                 wminfo.lev_next = 0; // wrap around in deathmatch
2483         }
2484     }
2485 
2486     wminfo.maxkills = totalkills;
2487     wminfo.maxitems = totalitems;
2488     wminfo.maxsecret = totalsecret;
2489     wminfo.maxfrags = 0;
2490     if( info_partime != -1)
2491         wminfo.partime = TICRATE*info_partime;
2492     else if ( gamemode == doom2_commercial )
2493         wminfo.partime = TICRATE*cpars[gamemap-1];
2494     else
2495         wminfo.partime = TICRATE*pars[gameepisode][gamemap];
2496     wminfo.pnum = consoleplayer;
2497 
2498     for (i=0 ; i<MAXPLAYERS ; i++)
2499     {
2500         wminfo.plyr[i].in = playeringame[i];
2501         wminfo.plyr[i].skills = players[i].killcount;
2502         wminfo.plyr[i].sitems = players[i].itemcount;
2503         wminfo.plyr[i].ssecret = players[i].secretcount;
2504         wminfo.plyr[i].stime = leveltime;
2505         memcpy (wminfo.plyr[i].frags, players[i].frags
2506                 , sizeof(wminfo.plyr[i].frags));
2507         wminfo.plyr[i].addfrags = players[i].addfrags;
2508     }
2509 
2510     if (statcopy)
2511         memcpy (statcopy, &wminfo, sizeof(wminfo));
2512 
2513     gamestate = GS_INTERMISSION;
2514 
2515     WI_Start (&wminfo);
2516 }
2517 
2518 
2519 //
2520 // G_NextLevel (WorldDone)
2521 //
2522 // init next level or go to the final scene
2523 // called by end of intermision screen (wi_stuff)
G_NextLevel(void)2524 void G_NextLevel (void)
2525 {
2526     gameaction = ga_worlddone;
2527     if (secretexit)
2528         consoleplayer_ptr->didsecret = true;
2529 
2530     if ( gamemode == doom2_commercial)
2531     {
2532         if( deathmatch )
2533         {
2534             if( gamemap == 30 )
2535                 wminfo.lev_next = 0; // wrap around in deathmatch
2536         }
2537         else
2538         {
2539             switch (gamemap)
2540             {
2541             case 15:
2542             case 31:
2543                 if (!secretexit)
2544                     break;
2545             case 6:
2546             case 11:
2547             case 20:
2548             case 30:
2549                 if( gamemap == 30 )
2550                     CL_Reset(); // end of game disconnect from server
2551                 gameaction = ga_nothing;
2552                 F_StartFinale ();
2553                 break;
2554             }
2555         }
2556     }
2557 }
2558 
G_DoWorldDone(void)2559 void G_DoWorldDone (void)
2560 {
2561     if( demoversion<129 )
2562     {
2563         gamemap = wminfo.lev_next+1;
2564         G_DoLoadLevel (true);
2565     }
2566     else
2567     {
2568         // not in demo because demo have the mapcommand on it
2569         if(server && !demoplayback)
2570         {
2571             if( ! deathmatch )
2572             {
2573                 // don't reset player between maps
2574                 COM_BufAddText (va("map \"%s\" -noresetplayers\n",G_BuildMapName(gameepisode, wminfo.lev_next+1)));
2575             }
2576             else
2577             {
2578                 // resetplayer in deathmatch for more equality
2579                 COM_BufAddText (va("map \"%s\"\n",G_BuildMapName(gameepisode, wminfo.lev_next+1)));
2580             }
2581         }
2582     }
2583 
2584     gameaction = ga_nothing;
2585 }
2586 
2587 
2588 // compose menu message from strings
2589 static
compose_message(const char * str1,const char * str2)2590 void compose_message( const char * str1, const char * str2 )
2591 {
2592     char msgtemp[128];
2593     if( str2 == NULL )  str2 = "";
2594     sprintf( msgtemp, "%s %s\n\nPress ESC\n", str1, str2 );
2595     M_SimpleMessage ( msgtemp );
2596 }
2597 
2598 
2599 extern char  savegamedir[SAVESTRINGSIZE];
2600 char  savegamename[MAX_WADPATH];
2601 
2602 // Must be able to handle 99 savegame slots, even when
2603 // not SAVEGAME99, so net game saves are universally accepted.
G_Savegame_Name(char * namebuf,int slot)2604 void G_Savegame_Name( /*OUT*/ char * namebuf, /*IN*/ int slot )
2605 {
2606 #ifdef SAVEGAMEDIR
2607     sprintf(namebuf, savegamename, savegamedir, slot);
2608 #else
2609     sprintf(namebuf, savegamename, slot);
2610 #endif
2611 #ifdef SMIF_PC_DOS
2612     if( slot > 9 )
2613     {
2614         // shorten name to 8 char
2615         int ln = strlen( namebuf );
2616         memmove( &namebuf[ln-4], &namebuf[ln-3], 4 );
2617     }
2618 #endif
2619 }
2620 
2621 //
2622 // G_InitFromSavegame
2623 // Can be called by the startup code or the menu task.
2624 //
2625 // Called from menu M_LoadSelect from M_Responder,
2626 // and from D_Main code for -loadgame command line switch.
G_Load_Game(int slot)2627 void G_Load_Game (int slot)
2628 {
2629     // [WDJ] will handle 99 slots
2630     COM_BufAddText(va("load %d\n",slot));
2631     // net command call to G_DoLoadGame
2632 }
2633 
2634 // Called from network command, sent from G_LoadGame
2635 // Reads the save game file.
G_DoLoadGame(int slot)2636 void G_DoLoadGame (int slot)
2637 {
2638     char        savename[255];
2639     savegame_info_t   sginfo;  // read header info
2640 
2641     G_Savegame_Name( savename, slot );
2642 
2643     if( P_Savegame_Readfile( savename ) < 0 )  goto cannot_read_file;
2644     // file is open and savebuffer allocated
2645 
2646     if( ! P_Savegame_Read_header( &sginfo, 0 ) )  goto load_header_failed;
2647     if( ! sginfo.have_game )  goto wrong_game;
2648     if( ! sginfo.have_wad )  goto wrong_wad;
2649 
2650     D_DisableDemo();  // turn off demos and keeps them off
2651 
2652     //added:27-02-98: reset the game version
2653     G_setup_VERSION();
2654 
2655     paused        = 0;
2656     automapactive = false;
2657 
2658     // dearchive all the modifications
2659     P_Savegame_Load_game(); // read game data in savebuffer, defer error test
2660     if( P_Savegame_Closefile( 0 ) < 0 )  goto load_failed;
2661     // savegame buffer deallocated, and file closed
2662 
2663 #ifdef BASETIC_DEMOSYNC
2664 # if 0
2665     // [WDJ] PrBoom savegames indirectly save the gametic.
2666     // I see no need for a demo fix to affect savegames.
2667     // killough 11/98: load revenant tracer state
2668     basetic = gametic - *save_p++;
2669 # endif
2670 #endif
2671 
2672     gameaction = ga_nothing;
2673     gamestate = GS_LEVEL;
2674 
2675     displayplayer = consoleplayer;
2676     displayplayer_ptr = consoleplayer_ptr;
2677 
2678     // done
2679     multiplayer = playeringame[1];
2680     if(playeringame[1] && !netgame)
2681         CV_SetValue(&cv_splitscreen,1);
2682 
2683     if (setsizeneeded)
2684         R_ExecuteSetViewSize ();
2685 
2686     // draw the pattern into the back screen
2687     R_FillBackScreen ();
2688     CON_ToggleOff ();
2689     return;
2690 
2691 cannot_read_file:
2692     CONS_Printf ("Couldn't read file %s", savename);
2693     goto failed_exit;
2694 
2695 load_header_failed:
2696     compose_message( sginfo.msg, NULL );
2697     goto failed_exit;
2698 
2699 wrong_game:
2700     compose_message( "savegame requires game:", sginfo.game );
2701     goto failed_exit;
2702 
2703 wrong_wad:
2704     compose_message( "savegame requires wad:", sginfo.wad );
2705     goto failed_exit;
2706 
2707 load_failed:
2708     M_SimpleMessage("savegame file corrupted\n\nPress ESC\n" );
2709     Command_ExitGame_f();
2710 failed_exit:
2711     P_Savegame_Error_Closefile();  // to dealloate buffer
2712     // were not playing, but server got started by sending load message
2713     if( gamestate == GS_WAITINGPLAYERS )
2714     {
2715         // [WDJ] fix ALLREADYPLAYING message, so that still not playing
2716         Command_ExitGame_f();
2717     }
2718     return;
2719 }
2720 
2721 //
2722 // G_SaveGame
2723 // Called by the menu task.
2724 // Description is a 24 byte text string
2725 //
2726 // Called from menu M_DoSave from M_Responder.
G_Save_Game(int slot,const char * description)2727 void G_Save_Game ( int   slot, const char* description )
2728 {
2729     // Solo player has server, net player without server cannot save.
2730     if (server)
2731     {
2732         // [WDJ] will handle 99 slots
2733         COM_BufAddText(va("save %d \"%s\"\n",slot,description));
2734         // Net command call to G_DoSaveGame
2735     }
2736 }
2737 
2738 // Called from network command sent from G_SaveGame.
2739 // Writes the save game file.
G_DoSaveGame(int savegameslot,const char * savedescription)2740 void G_DoSaveGame (int   savegameslot, const char* savedescription)
2741 {
2742     char        savename[256];
2743 
2744     gameaction = ga_nothing;
2745 
2746     G_Savegame_Name( savename, savegameslot );
2747 
2748     gameaction = ga_nothing;
2749 
2750     if( P_Savegame_Writefile( savename ) < 0 )  return;
2751 
2752     P_Savegame_Write_header( savedescription, 0 );
2753     P_Savegame_Save_game();  // Write game data to savegame buffer.
2754 
2755     if( P_Savegame_Closefile( 1 ) < 0 )  return;
2756 
2757     gameaction = ga_nothing;
2758 
2759     consoleplayer_ptr->message = GGSAVED;
2760 
2761     // draw the pattern into the back screen
2762     R_FillBackScreen ();
2763     ST_Drawer( 1 );	// [WDJ] refresh status background without global flags
2764 }
2765 
2766 
2767 //
2768 // G_InitNew
2769 //  Can be called by the startup code or the menu task,
2770 //  consoleplayer, displayplayer, playeringame[] should be set.
2771 //
2772 // Boris comment : single player start game
2773 // Called by SF_StartSkill, M_ChooseSkill, M_VerifyNightmare
2774 // Called by cht_Responder on clev, CheatWarpFunc
G_DeferedInitNew(skill_e skill,const char * mapname,boolean StartSplitScreenGame)2775 void G_DeferedInitNew (skill_e skill, const char* mapname, boolean StartSplitScreenGame)
2776 {
2777     paused = 0;
2778 
2779     if( demoplayback )
2780         COM_BufAddText ("stopdemo\n");  // invokes G_CheckDemoStatus
2781 
2782     D_DisableDemo();  // turn off demos and keeps them off
2783 
2784     G_setup_VERSION(); // [WDJ] should be after demo is stopped
2785 
2786     // this leave the actual game if needed
2787     SV_StartSinglePlayerServer();
2788 
2789     // Setup before start of game.
2790     COM_BufAddText (va("splitscreen %d;deathmatch 0;fastmonsters 0;"
2791                        "respawnmonsters 0;timelimit 0;fraglimit 0\n",
2792                        StartSplitScreenGame));
2793 
2794     COM_BufAddText (va("map \"%s\" -skill %d -monsters 1\n",mapname,skill+1));
2795 }
2796 
2797 //
2798 // This is the map command interpretation something like Command_Map_f
2799 //
2800 // called at : map cmd execution, doloadgame, doplaydemo
G_InitNew(skill_e skill,const char * mapname,boolean resetplayer)2801 void G_InitNew (skill_e skill, const char* mapname, boolean resetplayer)
2802 {
2803     //added:27-02-98: disable selected features for compatibility with
2804     //                older demos, plus reset new features as default
2805     if(!G_Downgrade (demoversion))
2806     {
2807         CONS_Printf("Cannot Downgrade engine\n");
2808         CL_Reset();
2809         D_StartTitle();
2810         return;
2811     }
2812 
2813     if (paused)
2814     {
2815         paused = 0;
2816         S_ResumeSound ();
2817     }
2818 
2819     if (skill > sk_nightmare)
2820         skill = sk_nightmare;
2821 
2822     M_ClearRandom ();
2823 
2824     if( server && skill == sk_nightmare )
2825     {
2826         // NETVAR, not saved
2827 #if 1
2828         CV_SetParam(&cv_respawnmonsters,1);
2829         CV_SetParam(&cv_fastmonsters,1);
2830 #else
2831         CV_SetValue(&cv_respawnmonsters,1);
2832         CV_SetValue(&cv_fastmonsters,1);
2833 #endif
2834     }
2835 
2836     // for internal maps only
2837     if (FIL_CheckExtension(mapname))
2838     {
2839         // external map file
2840         strncpy (game_map_filename, mapname, MAX_WADPATH-1);
2841         game_map_filename[MAX_WADPATH-1] = 0;
2842         // dummy values, to be set by P_SetupLevel.
2843         gameepisode = 1;
2844         gamemap = 1;
2845     }
2846     else
2847     {
2848         // internal game map
2849         // well this  check is useless because it is done before (d_netcmd.c::command_map_f)
2850         // but in case of for demos....
2851         if( ! VALID_LUMP( W_CheckNumForName(mapname) ) )
2852         {
2853             CONS_Printf("\2Internal game map '%s' not found\n"
2854                         "(use .wad extension for external maps)\n",mapname);
2855             Command_ExitGame_f();
2856             return;
2857         }
2858 
2859         game_map_filename[0] = 0;             // means not an external wad file
2860         if (gamemode==doom2_commercial)       //doom2
2861         {
2862             gamemap = atoi(mapname+3);  // get xx out of MAPxx
2863             gameepisode = 1;
2864         }
2865         else
2866         {
2867             gamemap = mapname[3]-'0';           // ExMy
2868             gameepisode = mapname[1]-'0';
2869         }
2870     }
2871 
2872     gameskill      = skill;
2873     playerdeadview = false;
2874     automapactive  = false;
2875 
2876     G_DoLoadLevel (resetplayer);
2877 }
2878 
2879 
2880 // Sets defaults according to current master EN_
2881 // Do not call after a Demo has set these settings.
2882 static
G_gamemode_EN_defaults(void)2883 void G_gamemode_EN_defaults( void )
2884 {
2885     // Fixes and buggy.
2886     // Heretic never fixed this, but PrBoom did.  Default to fixed.
2887     EN_skull_bounce_fix = 1;  // Off only for old demos, incl Legacy demos.
2888     EN_skull_bounce_floor = 1;
2889     EN_catch_respawn_0 = 1;
2890     // Boom
2891     EN_pushers = EN_boom;
2892     EN_doorlight = EN_boom;
2893     EN_invul_god = EN_boom;
2894     EN_boom_physics = EN_boom;
2895     EN_boom_floor = EN_boom | EN_heretic;
2896     EN_blazing_double_sound = 0;
2897     EN_vile_revive_bug = 0;
2898     EN_sleeping_sarg_bug = 0;
2899     EN_skull_limit = 0;
2900     EN_old_pain_spawn = 0;
2901     EN_doom_movestep_bug = 0;
2902     // MBF
2903     EN_mbf_telefrag = EN_mbf | EN_heretic;
2904 }
2905 
2906 
2907 // [WDJ] Set the gamemode, and all EN_ that are dependent upon it.
2908 // Done here to be near G_Downgrade.
G_set_gamemode(byte new_gamemode)2909 void G_set_gamemode( byte new_gamemode )
2910 {
2911     gamemode = new_gamemode;
2912     EV_legacy = VERSION;  // current DoomLegacy version
2913     // Legacy defaults.
2914     EN_doom_etc = 1;
2915     EN_boom = 1;
2916     EN_mbf = 1;
2917     // Doom and doom-like defaults.
2918     EN_heretic = EN_hexen = EN_strife = 0;
2919     EN_heretic_hexen = 0;
2920     EN_inventory = 0;
2921     // Hexen and Strife are setup even though not implemented yet,
2922     // as placeholders and to prevent bad assumptions.
2923     switch( gamemode )
2924     {
2925      case heretic:
2926       EN_heretic = 1;
2927       goto not_doom;
2928      case hexen:
2929       EN_hexen = 1;
2930       goto not_doom;
2931      case strife:
2932       EN_strife = 1;
2933       goto not_doom;
2934      case chexquest1:  // is doom
2935      default:
2936       break;
2937     }
2938     goto finish;
2939 
2940 not_doom:
2941     EN_heretic_hexen = EN_heretic || EN_hexen;
2942     EN_doom_etc = EN_mbf = EN_boom = 0;
2943     EN_inventory = 1;
2944 
2945 finish:
2946     G_gamemode_EN_defaults();
2947     return;
2948 }
2949 
2950 
2951 // Sets defaults according to current master EN_
2952 // Do not call after a Demo has set these settings.
2953 // Called after getting demoversion, EN_doom, etc, but before setting
2954 // individual demo settings.
2955 static
G_demo_defaults(void)2956 void G_demo_defaults( void )
2957 {
2958     // For DoomLegacy demos, generally, after version 1.44 when DoomLegacy got a capability,
2959     // it got a flag in the demo header.  Default them to off.
2960     friction_model = FR_orig;
2961     monster_infight = INFT_infight;  // Default is to infight, DEH can turn it off.
2962     voodoo_mode = VM_vanilla;
2963     cv_viewheight.EV = 41; // vanilla viewheight
2964     cv_solidcorpse.EV = 0;
2965     cv_instadeath.EV = 0;  // Die
2966     cv_monstergravity.EV = 0;
2967     cv_monbehavior.EV = 0;  // Vanilla
2968     cv_monsterfriction.EV = 0; // Vanilla
2969     EN_skull_bounce_fix = 0;  // Vanilla and DoomLegacy < 1.47
2970     EN_catch_respawn_0 = 0;
2971 
2972     // Boom
2973     cv_rndsoundpitch.EV = EN_boom;  // normal in Boom, calls M_Random
2974     EN_pushers = EN_boom;
2975     // introduced Boom 2.00 without demo flag
2976     EN_doom_movestep_bug = EN_doom_etc && ! EN_boom;
2977     EN_variable_friction =
2978     EN_boom_floor = EN_boom && (demoversion >= 200);
2979     // introduced Boom 2.01 without demo flag
2980     EN_boom_physics =
2981     EN_doorlight =
2982     EN_invul_god =
2983     cv_invul_skymap.EV =
2984     cv_zerotags.EV = EN_boom && (demoversion >= 201);  // 0=Vanilla, 1=Boom
2985     EN_blazing_double_sound =
2986     EN_skull_limit =
2987     EN_old_pain_spawn =
2988     EN_vile_revive_bug = EN_doom_etc && !( EN_boom && (demoversion >= 201));  // fixed Boom 2.01
2989     // introduced Boom 2.02 without demo flag
2990     cv_doorstuck.EV = EN_boom && (demoversion >= 202);  // Boom 2.02
2991     // introduced Boom 2.04
2992     EN_sleeping_sarg_bug = EN_doom_etc && (demoversion < 204);  // fixed Boom 2.04
2993 
2994     // MBF
2995     cv_mbf_dropoff.EV = EN_mbf;
2996     cv_mbf_falloff.EV = EN_mbf;
2997     cv_mbf_monster_avoid_hazard.EV = EN_mbf;
2998     cv_mbf_monster_backing.EV = EN_mbf;
2999     cv_mbf_pursuit.EV = EN_mbf;
3000     cv_mbf_staylift.EV = EN_mbf;
3001     cv_mbf_help_friend.EV = EN_mbf;
3002     cv_mbf_monkeys.EV = EN_mbf;
3003 #ifdef DOGS
3004     cv_mbf_dogs.EV = EN_mbf;
3005     cv_mbf_dog_jumping.EV = EN_mbf;
3006 #endif
3007 
3008 #ifdef DOORDELAY_CONTROL
3009     adj_ticks_per_sec = 35; // default
3010 #endif
3011 }
3012 
3013 
3014 static
G_restore_user_settings(void)3015 void G_restore_user_settings( void )
3016 {
3017     // Force some restore to invoke CV_CALL functions.
3018     cv_monbehavior.EV = 255;  // infight
3019     cv_voodoo_mode.EV = 255;
3020 #ifdef DOORDELAY_CONTROL
3021     cv_doordelay.EV = 255;
3022 #endif
3023 
3024     cv_mbf_distfriend.EV = ~cv_mbf_distfriend.value;  // forced mismatch
3025 
3026     // Restore all modifed cvar
3027     CV_Restore_User_Settings();  //  Set EV = value
3028 }
3029 
3030 
3031 
3032 //added:03-02-98:
3033 //
3034 //  'Downgrade' the game engine so that it is compatible with older demo
3035 //   versions. This will probably get harder and harder with each new
3036 //   'feature' that we add to the game. This will stay until it cannot
3037 //   be done a 'clean' way, then we'll have to forget about old demos..
3038 //
3039 // demoversion is usually set before this is called
G_Downgrade(int version)3040 boolean G_Downgrade(int version)
3041 {
3042     int i;
3043 
3044     if (verbose > 1)
3045     {
3046         GenPrintf(EMSG_info|EMSG_all,"Downgrade to version: %i\n", version);
3047     }
3048 
3049     if (version<109)
3050         return false;
3051 
3052     // always true now, might be false in the future, if couldn't
3053     // go backward and disable all the features...
3054     demoversion = version;
3055 
3056     if( version<130 )
3057     {
3058         mobjinfo[MT_BLOOD].radius = 20*FRACUNIT;
3059         mobjinfo[MT_BLOOD].height = 16*FRACUNIT;
3060         mobjinfo[MT_BLOOD].flags  = MF_NOBLOCKMAP;
3061     }
3062     else
3063     {
3064         mobjinfo[MT_BLOOD].radius = 3*FRACUNIT;
3065         mobjinfo[MT_BLOOD].height = 0*FRACUNIT;
3066         mobjinfo[MT_BLOOD].flags  = 0;
3067     }
3068 
3069     // smoke trails for skull head attack since v1.25
3070     if (version<125)
3071     {
3072         states[S_ROCKET].action.acv = NULL;
3073 
3074         states[S_SKULL_ATK3].action.acv = NULL;
3075         states[S_SKULL_ATK4].action.acv = NULL;
3076     }
3077     else
3078     {
3079         //activate rocket trails by default
3080         states[S_ROCKET].action.acv     = A_SmokeTrailer;
3081 
3082         // smoke trails behind the skull heads
3083         states[S_SKULL_ATK3].action.acv = A_SmokeTrailer;
3084         states[S_SKULL_ATK4].action.acv = A_SmokeTrailer;
3085     }
3086 
3087     if(version <= 109)
3088     {
3089         // disable rocket trails
3090         states[S_ROCKET].action.acv = NULL; //NULL like in Doom2 v1.9
3091 
3092         // Boris : for older demos, initialize the new skincolor value
3093         //         also disable the new preferred weapons order.
3094         for(i=0;i<4;i++)
3095         {
3096             players[i].skincolor = i % NUMSKINCOLORS;
3097             players[i].originalweaponswitch=true;
3098         }//eof Boris
3099     }
3100 
3101     if(version <= 111 || version >= 200)
3102     {
3103         //added:16-02-98: make sure autoaim is used for older
3104         //                demos not using mouse aiming
3105         cv_allowautoaim.EV = 9;  // force autoaim
3106         for(i=0;i<MAXPLAYERS;i++)
3107             players[i].autoaim_toggle = true;
3108     }
3109 
3110     // PrBoom has this enabled by comp level.
3111     // It is only off for old Doom demos.
3112     EN_skull_bounce_floor = EN_skull_bounce_fix
3113        || ( version > 109 && version < 212 );
3114 
3115     //SoM: 3/17/2000: Demo compatability
3116     // EN_boom has been loaded from Boom demo compatiblity.
3117     if(gamemode == heretic)
3118     {
3119         EN_boom = 0;  // expected to be OFF
3120         EN_pushers = 0;
3121         EN_variable_friction = 1;  // Needed for ICE E2M4
3122         EN_sleeping_sarg_bug = 0;
3123     }
3124     else if(version < 129 || ! EN_boom )
3125     {
3126         // Boom demo_compatibility mode  (boom demo version < 200)
3127         EN_pushers = 0;
3128         EN_variable_friction = 0;
3129         EN_sleeping_sarg_bug = 1;
3130     }
3131     else if( version < 200 )
3132     {
3133         // Legacy
3134         // Flags loaded by (Boom, MBF, prboom) demos, but not others.
3135         EN_pushers = 1;	// of Boom 2.02
3136         EN_variable_friction = 1;  // of Boom 2.02
3137         EN_sleeping_sarg_bug = EV_legacy < 144;
3138     }
3139 
3140     if( !demoplayback || friction_model == FR_orig )
3141     {
3142         friction_model =
3143            (gamemode == heretic)? FR_heretic
3144          : (gamemode == hexen)? FR_hexen
3145          : (version <= 132)? FR_orig  // older legacy demos, and doom demos
3146          : (version <= 143)? FR_boom  // old legacy demos
3147          : (version > 200) ?
3148            (
3149               (version <= 202)? FR_boom  // boom 200, 201, 202
3150             : (version == 203)? FR_mbf
3151             : FR_prboom  // prboom
3152            )
3153          : FR_legacy;  // new model, default
3154     }
3155 
3156 #if 0
3157     // EN_boom_invul_skymap, was not enabled for DoomLegacy before 1.47,
3158     // but does not affect demo sync.  Is a matter of preference.
3159     if( version < 147 )
3160         cv_invul_skymap.EV = 0;
3161 #endif
3162 
3163 #if 0
3164     // [WDJ]
3165     // TODO:
3166     // auto weapon change on pickup
3167     if( demoplayback )
3168     {
3169         // values that are set by the demo
3170     }
3171     else
3172     {
3173     }
3174 #endif
3175 
3176     DemoAdapt_p_user();  // local enables of p_user
3177     DemoAdapt_p_mobj();  // local enables of p_mobj
3178     DemoAdapt_p_enemy(); // local enables of p_enemy
3179     DemoAdapt_p_fab();   // local enables of p_fab
3180     DemoAdapt_p_floor(); // local enables of p_floor, TNT MAP30 fix
3181     DemoAdapt_p_map();   // local enables of p_map
3182     DemoAdapt_bots();    // local enables of bots
3183     return true;
3184 }
3185 
3186 // Make it easy to setup the VERSION play, without mistakes.
G_setup_VERSION(void)3187 void G_setup_VERSION( void )
3188 {
3189 #ifdef PARANOIA
3190     if( !EN_mbf )
3191     {
3192         GenPrintf( EMSG_warn, "Setup_VERSION: EN_mbf=0, possibly after demo\n" );
3193         G_set_gamemode( gamemode );  // restore EN set by gamemode
3194     }
3195 #endif
3196     // Reset demoversion to normal
3197     EV_legacy = VERSION;
3198     G_Downgrade( VERSION );
3199 }
3200 
3201 
3202 
3203 //
3204 // DEMO RECORDING
3205 //
3206 
3207 #define ZT_FWD          0x01
3208 #define ZT_SIDE         0x02
3209 #define ZT_ANGLE        0x04
3210 #define ZT_BUTTONS      0x08
3211 #define ZT_AIMING       0x10
3212 #define ZT_CHAT         0x20    // no more used
3213 #define ZT_EXTRADATA    0x40
3214 #define DEMOMARKER      0x80    // demoend
3215 
3216 // SERVER_ID appears in Demo 1.48
3217 #if SERVER_PID < MAXPLAYERS
3218 #  error "SERVER_PID must be > MAXPLAYERS"
3219 #endif
3220 
3221 ticcmd_t oldcmd[MAXPLAYERS];
3222 
3223 // Only called when demoplayback.
G_ReadDemoTiccmd(ticcmd_t * cmd,int playernum)3224 void G_ReadDemoTiccmd (ticcmd_t* cmd,int playernum)
3225 {
3226     if( (*demo_p == DEMOMARKER) || (demo_p > demoend) )
3227     {
3228         // end of demo data stream
3229         G_CheckDemoStatus ();
3230         return;
3231     }
3232 
3233     if( EN_demotic_109 )  // vanilla demo tic format
3234     {
3235         // Doom, Boom, MBF, prboom demo
3236         cmd->forwardmove = READCHAR(demo_p);
3237         cmd->sidemove = READCHAR(demo_p);
3238         if( EN_boom_longtics )
3239         {
3240             cmd->angleturn = READ16(demo_p);
3241         }
3242         else
3243         {
3244             cmd->angleturn = READBYTE(demo_p)<<8;
3245         }
3246         cmd->buttons = READBYTE(demo_p);
3247         // demo does not have
3248         cmd->aiming = 0;
3249     }
3250     else
3251     {
3252         // DoomLegacy advanced demos
3253         char ziptic=*demo_p++;  // bit flags for ZT_
3254 
3255         if(ziptic & ZT_FWD)
3256             oldcmd[playernum].forwardmove = READCHAR(demo_p);
3257 
3258         if(ziptic & ZT_SIDE)
3259             oldcmd[playernum].sidemove = READCHAR(demo_p);
3260 
3261         if(ziptic & ZT_ANGLE)
3262         {
3263             if(demoversion<125)
3264                 oldcmd[playernum].angleturn = READBYTE(demo_p)<<8;
3265             else
3266                 oldcmd[playernum].angleturn = READ16(demo_p);
3267         }
3268 
3269         if(ziptic & ZT_BUTTONS)
3270             oldcmd[playernum].buttons = READBYTE(demo_p);
3271 
3272         if(ziptic & ZT_AIMING)
3273         {
3274             if(demoversion<128)
3275                 oldcmd[playernum].aiming = READCHAR(demo_p);
3276             else
3277                 oldcmd[playernum].aiming = READ16(demo_p);
3278         }
3279 
3280         if(ziptic & ZT_CHAT)
3281             demo_p++;
3282 
3283         if(ziptic & ZT_EXTRADATA)
3284             ReadLmpExtraData(&demo_p,playernum);
3285 #if 0
3286 // only used to clear textcmd, which is now done in TryRunTics
3287         else
3288             ReadLmpExtraData(0,playernum);
3289 #endif
3290 
3291         memcpy(cmd,&(oldcmd[playernum]),sizeof(ticcmd_t));
3292     }
3293 }
3294 
3295 
G_WriteDemoTiccmd(ticcmd_t * cmd,int playernum)3296 void G_WriteDemoTiccmd (ticcmd_t* cmd,int playernum)
3297 {
3298     char ziptic=0;
3299     byte *ziptic_p;
3300 
3301     ziptic_p=demo_p++;  // the ziptic
3302                         // write at the end of this function
3303 
3304     if(cmd->forwardmove != oldcmd[playernum].forwardmove)
3305     {
3306         *demo_p++ = cmd->forwardmove;
3307         oldcmd[playernum].forwardmove = cmd->forwardmove;
3308         ziptic|=ZT_FWD;
3309     }
3310 
3311     if(cmd->sidemove != oldcmd[playernum].sidemove)
3312     {
3313         *demo_p++ = cmd->sidemove;
3314         oldcmd[playernum].sidemove=cmd->sidemove;
3315         ziptic|=ZT_SIDE;
3316     }
3317 
3318     if(cmd->angleturn != oldcmd[playernum].angleturn)
3319     {
3320         WRITE16(demo_p,cmd->angleturn);
3321         oldcmd[playernum].angleturn=cmd->angleturn;
3322         ziptic|=ZT_ANGLE;
3323     }
3324 
3325     if(cmd->buttons != oldcmd[playernum].buttons)
3326     {
3327         *demo_p++ = cmd->buttons;
3328         oldcmd[playernum].buttons=cmd->buttons;
3329         ziptic|=ZT_BUTTONS;
3330     }
3331 
3332     if(cmd->aiming != oldcmd[playernum].aiming)
3333     {
3334         WRITE16(demo_p,cmd->aiming);
3335         oldcmd[playernum].aiming=cmd->aiming;
3336         ziptic|=ZT_AIMING;
3337     }
3338 
3339     if(AddLmpExtradata(&demo_p,playernum))
3340         ziptic|=ZT_EXTRADATA;
3341 
3342     *ziptic_p=ziptic;
3343     //added:16-02-98: attention here for the ticcmd size!
3344     // latest demos with mouse aiming byte in ticcmd
3345     if (ziptic_p > demoend - (5*MAXPLAYERS))
3346     {
3347         G_CheckDemoStatus ();   // no more space
3348         return;
3349     }
3350 
3351 //  don't work in network the consistency is not copyed in the cmd
3352 //    demo_p = ziptic_p;
3353 //    G_ReadDemoTiccmd (cmd,playernum);         // make SURE it is exactly the same
3354 }
3355 
3356 
3357 
3358 //
3359 // G_RecordDemo
3360 //
G_RecordDemo(const char * name)3361 void G_RecordDemo (const char* name)
3362 {
3363     int             i;
3364     int             maxsize;
3365 
3366     strncpy (demoname, name, DEMONAME_LEN);
3367     demoname[DEMONAME_LEN-1] = 0;
3368     strcat (demoname, ".lmp");
3369     maxsize = 0x20000;
3370     i = M_CheckParm ("-maxdemo");
3371     if (i && i<myargc-1)
3372         maxsize = atoi(myargv[i+1])*1024;
3373     demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL);
3374     demoend = demobuffer + maxsize;
3375 
3376     demorecording = true;
3377 }
3378 
3379 
G_BeginRecording(void)3380 void G_BeginRecording (void)
3381 {
3382     int             i;
3383     int rec_version = VERSION;
3384 
3385 #if 0
3386     // If ever need to record something other than VERSION
3387     // make sure they agree this time.
3388     if( rec_version != VERSION )
3389     {
3390         G_Downgrade( rec_version );
3391     }
3392 #endif
3393 
3394     demo_p = demobuffer;
3395 
3396     // DoomLegacy version 1.44, 1.45, and after will all use demo header 144,
3397     // The actual DoomLegacy version is recorded in its header fields.
3398     // Do not change this header, except to add new fields in the empty space.
3399 
3400     // write DL format (demo144) header
3401     *demo_p++ = 144;   // Mark all DoomLegacy demo as version 144.
3402     *demo_p++ = 'D';   // "DL" for DoomLegacy
3403     *demo_p++ = 'L';
3404     *demo_p++ = 1;     // non-zero format version (demo144_format)
3405                        // 0 would be an older version with new header.
3406     *demo_p++ = VERSION;  // version of doomlegacy that recorded it.
3407     *demo_p++ = rec_version;  // actual DoomLegacy demoversion recorded
3408     *demo_p++ = REVISION;     // demo subversion, when needed
3409     *demo_p++ = gameskill;
3410     *demo_p++ = gameepisode;
3411     *demo_p++ = gamemap;
3412     // Save EV value to get correct values when invoked by command line or saved game.
3413     *demo_p++ = cv_deathmatch.EV;
3414     *demo_p++ = cv_respawnmonsters.EV;
3415     *demo_p++ = cv_fastmonsters.EV;
3416     *demo_p++ = nomonsters;
3417     *demo_p++ = consoleplayer;
3418     *demo_p++ = cv_timelimit.value;      // just to be compatible with old demo (no more used)
3419     *demo_p++ = multiplayer;             // 1..31
3420 
3421     for (i=0 ; i<MAXPLAYERS ; i++)
3422     {
3423         if(playeringame[i])
3424           *demo_p++ = 1;
3425         else
3426           *demo_p++ = 0;
3427     }
3428 
3429     byte * demo_p_next = demo_p + 64;
3430     // more settings that affect playback
3431     *demo_p++ = cv_solidcorpse.EV;
3432 #ifdef DOORDELAY_CONTROL
3433     *demo_p++ = adj_ticks_per_sec;  // doordelay, 0 is not default
3434 #else
3435     *demo_p++ = 0; 	// no doordelay
3436 #endif
3437     *demo_p++ = 0x40 + voodoo_mode;  // 0 is not default
3438     *demo_p++ = cv_instadeath.EV;  // voodoo doll instadeath, 0 is default
3439     *demo_p++ = cv_monsterfriction.EV;
3440     *demo_p++ = friction_model;
3441     *demo_p++ = cv_rndsoundpitch.EV;  // uses M_Random
3442     *demo_p++ = cv_monbehavior.EV;
3443     *demo_p++ = cv_doorstuck.EV;
3444     *demo_p++ = cv_monstergravity.EV;
3445     // 10
3446     // Boom and MBF derived controls.
3447     *demo_p++ = cv_monster_remember.EV;
3448     *demo_p++ = cv_weapon_recoil.EV;
3449     *demo_p++ = cv_invul_skymap.EV;
3450     *demo_p++ = cv_zerotags.EV;
3451     *demo_p++ = cv_mbf_dropoff.EV;
3452     *demo_p++ = cv_mbf_falloff.EV;
3453     *demo_p++ = cv_mbf_pursuit.EV;
3454     *demo_p++ = cv_mbf_monster_avoid_hazard.EV;
3455     *demo_p++ = cv_mbf_monster_backing.EV;
3456     *demo_p++ = cv_mbf_staylift.EV;
3457     *demo_p++ = cv_mbf_help_friend.EV;
3458     *demo_p++ = (cv_mbf_distfriend.value >> 8);  // MSB
3459     *demo_p++ = cv_mbf_distfriend.value & 0xFF;  // LSB
3460     *demo_p++ = cv_mbf_monkeys.EV;
3461 #ifdef DOGS
3462     *demo_p++ = cv_mbf_dogs.EV;
3463     *demo_p++ = cv_mbf_dog_jumping.EV;
3464 #else
3465     *demo_p++ = 0;
3466     *demo_p++ = 0;
3467 #endif
3468     // 26
3469     *demo_p++ = (cv_respawnmonsterstime.value >> 8);  // MSB
3470     *demo_p++ = cv_respawnmonsterstime.value & 0xFF;  // LSB
3471     *demo_p++ = (cv_itemrespawntime.value >> 8);  // MSB
3472     *demo_p++ = cv_itemrespawntime.value & 0xFF;  // LSB
3473     // 30
3474     *demo_p++ = (game_comp_tic >> 24); // MSB
3475     *demo_p++ = (game_comp_tic >> 16);
3476     *demo_p++ = (game_comp_tic >> 8);
3477     *demo_p++ = game_comp_tic & 0xFF; // LSB
3478 
3479     // empty space
3480     while( demo_p < demo_p_next )  *demo_p++ = 0;
3481 
3482     *demo_p++ = 0x55;   // Sync mark, start of data
3483     memset(oldcmd,0,sizeof(oldcmd));
3484 }
3485 
3486 
3487 // The following are set by DemoAdapt:
3488 //  voodoo_mode,_doordelay;  // see DemoAdapt_p_fab
3489 
3490 // The following are init by starting a game (demos cannot occur during game):
3491 // deathmatch, multiplayer, nomonsters, respawnmonsters, fastmonsters
3492 // timelimit.
3493 // Timelimit is NOT saved to config.
3494 
3495 // The following are set by G_Downgrade and/or G_DoPlayDemo:
3496 // EN_variable_friction, EN_pushers
3497 
3498 static byte      pdss_settings_valid = 0;
3499 static uint16_t  pdss_respawnmonsterstime;
3500 static uint16_t  pdss_itemrespawntime;
3501 
playdemo_save_settings(void)3502 void playdemo_save_settings( void )
3503 {
3504     // Still have a few settings that need save, restore.
3505     if( pdss_settings_valid == 0 )
3506     {
3507         pdss_settings_valid = 1;
3508         pdss_respawnmonsterstime = cv_respawnmonsterstime.value;
3509         pdss_itemrespawntime = cv_itemrespawntime.value;
3510     }
3511 }
3512 
playdemo_restore_settings(void)3513 void playdemo_restore_settings( void )
3514 {
3515     if( pdss_settings_valid )
3516     {
3517         cv_respawnmonsterstime.value = pdss_respawnmonsterstime;
3518         cv_itemrespawntime.value = pdss_itemrespawntime;
3519 
3520         // only restore when demo has changed some settings
3521         G_restore_user_settings();
3522     }
3523     pdss_settings_valid = 0;  // so user can change settings between demos
3524 }
3525 
3526 
3527 //
3528 // G_PlayDemo
3529 //
3530 // Called by D_DoAdvanceDemo to start a demo
3531 // Called by D_DoomMain to play a command line demo
3532 // Called by G_TimeDemo to play and time a demo
G_DeferedPlayDemo(const char * name)3533 void G_DeferedPlayDemo (const char* name)
3534 {
3535     // [WDJ] All as one string, or else it executes partial string
3536 //    COM_BufAddText(va("playdemo \"%s\"\n", name));
3537 
3538     // Using console command adds extra tics that cause sync problems.
3539     // Copy the demo name, as some are from stack buffers.
3540     if( playdemo_name == NULL )
3541     {
3542         playdemo_name = (char*) malloc(MAX_WADPATH);
3543     }
3544     strncpy( playdemo_name, name, MAX_WADPATH-1);     // parameter
3545     playdemo_name[MAX_WADPATH-1] = '\0';
3546 
3547     gameaction = ga_playdemo;  // play demo after finishing this
3548 }
3549 
3550 
3551 //
3552 //  Start a demo from a .LMP file or from a wad resource (eg: DEMO1)
3553 //
3554 // Called from SF_PlayDemo, fragglescript plays a demo lump
3555 // Called from Command_Playdemo_f, command play demo file or lump
G_DoPlayDemo(const char * defdemoname)3556 void G_DoPlayDemo (const char *defdemoname)
3557 {
3558     skill_e skill;
3559     lumpnum_t  lmp;
3560     int   i, episode, map;
3561     int   demo_size;
3562     int   num_players = 4;
3563     boolean boomdemo = 0;
3564     byte  demo144_format = 0;
3565     byte  boom_compatibility_mode = 0;  // Boom 2.00 compatibility flag
3566     byte  boom_compatibility_level = 0;
3567 
3568     playdemo_save_settings();  // [WDJ] Save user settings.
3569 
3570     // Enables that might be set directly by the demo.
3571     // Defaults
3572     EN_boom = 0;
3573     EN_mbf = 0;
3574     EV_legacy = 0;
3575     // Default demo tic read
3576     EN_demotic_109 = 0;
3577     EN_boom_longtics = 0;
3578 
3579     // [WDJ] Adapted from PrBoom, keep some old demos in sync.
3580     game_comp_tic = 0;
3581 
3582 //
3583 // load demo file / resource
3584 //
3585 
3586     //it's an internal demo
3587     strncpy (demoname, defdemoname, DEMONAME_LEN);
3588     demoname[DEMONAME_LEN-1] = 0;
3589 
3590     lmp = W_CheckNumForName(defdemoname);
3591     if( VALID_LUMP( lmp ) )
3592     {
3593         // lump
3594         demobuffer = demo_p = W_CacheLumpNum (lmp, PU_STATIC);
3595         demo_size = W_LumpLength( lmp );
3596     }
3597     else
3598     {
3599         // external file
3600         FIL_DefaultExtension(demoname,".lmp");
3601         demo_size = FIL_ReadFile (demoname, &demobuffer);
3602         if ( demo_size <= 0 )
3603         {
3604             GenPrintf(EMSG_warn, "\2ERROR: couldn't open file '%s'.\n", demoname);
3605             goto no_demo;
3606         }
3607         demo_p = demobuffer;
3608     }
3609     demoend = demo_p + demo_size - 1;
3610 
3611 //
3612 // read demo header
3613 //
3614 
3615     gameaction = ga_nothing;
3616     demoversion = READBYTE(demo_p);
3617     // header[0]: byte : demo version
3618     // 101 = Strife 1.01  (unsupported)
3619     // 104 = Doom 1.4 beta (unsupported)
3620     // 105 = Doom 1.5 beta (unsupported)
3621     // 106 = Doom 1.6 beta, 1.666 (unsupported)
3622     // 107 = Doom2 1.7, 1.7a (unsupported)
3623     // 108 = Doom 1.8, Doom2 1.8 (unsupported)
3624     // 109 = Doom 1.9, Doom2 1.9
3625     // 110 = Doom, published source code
3626     // 111..143 = Legacy
3627     // 144 = Legacy DL format
3628     // 200 = Boom 2.00	(supported badly, no sync)
3629     // 201 = Boom 2.01  (supported badly, no sync)
3630     // 202 = Boom 2.02  (supported badly, no sync)
3631     // 203 = LxDoom or MBF  (supported badly, no sync)
3632     // 210..214 = prboom (supported badly, no sync)
3633     // Do not have version: Hexen, Heretic, Doom 1.2 and before
3634     if( demoversion < 109 )
3635         goto bad_demo_version;
3636 
3637     if( demoversion < 111 )
3638     {
3639         EN_demotic_109 = 1;  // vanilla demo tic format
3640     }
3641     else if( demoversion >= 111 && demoversion <= 144 )
3642     {
3643         // DoomLegacy Demos
3644         if( demoversion == 144 )  // DoomLegacy demo DL format number
3645         {
3646             if( READBYTE(demo_p) != 'D' )  goto broken_header;
3647             if( READBYTE(demo_p) != 'L' )  goto broken_header;
3648             demo144_format = *demo_p++;  // DL format num, (1)
3649             demo_p++;  // recording legacy version number
3650             demoversion = READBYTE(demo_p);  // DoomLegacy DL demoversion number
3651 	    byte rev = READBYTE(demo_p);
3652             demoversion_rev = VERREV(demoversion, rev);  // with revision
3653             // maybe DL header on old demo
3654             if( demoversion < 111 )  goto broken_header;
3655             if( demoversion < 143 )  demo144_format = 0;
3656         }
3657         EV_legacy = demoversion;  // is a DoomLegacy version
3658         // Older demos (which could have had a demo144_format header put on them)
3659         EN_demotic_109 = (demoversion < 112);  // vanilla demo tic format
3660         EN_boom = (demoversion >= 129);
3661         // [WDJ] enable of "Marine's Best Friend" feature emulation
3662         EN_mbf = (demoversion >= 147);
3663     }
3664     else if (demoversion >= 200 && demoversion <= 214)
3665     {
3666         // Boom, MBF, and prboom headers
3667         // Used by FreeDoom
3668 
3669         // Read the "Boom" or "MBF" header line
3670         // Signature starts with 0x1d, end with 0xe6, padded to 6 bytes.
3671         // Signature Boom:  0x1d 'B' 'o' 'o' 'm'  0xe6
3672         // Signature MBF:   0x1d 'M' 'B' 'F' 0xe6 0x00
3673         if( *demo_p == 0x1d )
3674         {
3675             // Read signature into header buf and terminate as string.
3676             byte header[10];
3677             for ( i=0; i<5; i++ )
3678             {
3679                 header[i] = demo_p[i+1];
3680                 if( header[i] == 0xe6 )  break;
3681             }
3682             header[i] = 0;
3683             demo_p += 6;  // signature is always 6 bytes
3684 
3685             boom_compatibility_level = demoversion;  // default
3686             if( (demoversion == 203) && (header[0] == 'B') )
3687             {
3688                 // LxDoom
3689                 boom_compatibility_mode = 0;
3690                 boom_compatibility_level = 200;  // LxDoom not supported
3691                 EN_boom = 1;
3692 #ifdef DEBUG_DEMO
3693                 debug_Printf( " LxDoom demo\n" );
3694 #endif
3695             }
3696             else
3697             {
3698                 // MBF and prboom header have compatibility flag,
3699                 // which in newer demos will be ignored.
3700                 boom_compatibility_mode = *demo_p++;
3701                 EN_boom = ! boom_compatibility_mode;
3702 #ifdef DEBUG_DEMO
3703                 debug_Printf( " Boom demo\n" );
3704 #endif
3705             }
3706 
3707             // Complicated decoding for various boom versions.
3708             if( boom_compatibility_mode )
3709             {
3710                 if(demoversion <= 202)
3711                 {
3712                     boom_compatibility_level = 200;
3713                 }
3714             }
3715 
3716             EN_demotic_109 = 1;
3717             if( demoversion == 214 )
3718             {
3719                 EN_boom_longtics = 1;
3720             }
3721 
3722             EN_mbf = EN_boom && (header[0] == 'M') && (boom_compatibility_level >= 203);
3723 #ifdef DEBUG_DEMO
3724             debug_Printf( " demo header: %s.\n", header );
3725             debug_Printf( " compatibility 0x%x.\n", boom_compatibility_mode );
3726             debug_Printf( " compatibility_level 0x%x.\n", boom_compatibility_level );
3727             debug_Printf( " EN_boom %i  EN_mbf %i.\n", EN_boom, EN_mbf );
3728 #endif
3729             boomdemo = 1;
3730         }
3731         else
3732         {
3733             goto broken_header;
3734         }
3735     }
3736     else
3737     {
3738         goto bad_demo_version;
3739     }
3740 
3741 
3742 #ifdef SHOW_DEMOVERSION
3743     CONS_Printf( "Demo Version %i.\n", (int)demoversion );
3744 #endif
3745 #ifdef DEBUG_DEMO
3746     debug_Printf( "Demo version %i.\n", (int)demoversion );
3747 #endif
3748 
3749     if (demoversion < VERSION)
3750         CONS_Printf ("\2Demo is from an older game version\n");
3751 
3752     if( demo_p > demoend )  goto broken_header;
3753 
3754     G_demo_defaults();  // Per EN_boom, EN_mbf
3755 
3756     // header[1]: byte: skill level 0..4
3757     skill       = *demo_p++;
3758     // header[2]: byte: Doom episode 1..3, Doom2 and above use 1
3759     episode     = *demo_p++;
3760     // header[3]: byte: map level 1..32
3761     map         = *demo_p++;
3762 #ifdef DEBUG_DEMO
3763     debug_Printf( " skill %i.\n", (int)skill );
3764     debug_Printf( " episode %i.\n", (int)episode );
3765     debug_Printf( " map %i.\n", (int)map );
3766 #endif
3767     // header[4]: byte: play mode 0..2
3768     //   0 = single player or coop
3769     //   1 = deathmatch
3770     //   2 = alt deathmatch
3771 #ifdef DEBUG_DEMO
3772     debug_Printf( " play mode/deathmatch %i.\n", (int)demo_p[0] );
3773 #endif
3774     if (demoversion < 127 || demo144_format || boomdemo)
3775     {
3776         // store it, using the console will set it too late
3777         cv_deathmatch.EV = *demo_p++;
3778         Deathmatch_OnChange();
3779     }
3780     else
3781         demo_p++;  // old legacy demo, ignore deathmatch
3782 
3783     if( ! boomdemo )
3784     {
3785 #ifdef DEBUG_DEMO
3786         debug_Printf( " respawn %i.\n", (int)demo_p[1] );
3787         debug_Printf( " fast monsters %i.\n", (int)demo_p[2] );
3788 #endif
3789         // header[5]: byte: respawn boolean
3790         if (demoversion < 128 || demo144_format)
3791         {
3792             // store it, using the console will set it too late
3793             cv_respawnmonsters.EV = *demo_p++;
3794         }
3795         else
3796             demo_p++;  // legacy demo, ignore respawnmonsters
3797 
3798         // header[6]: byte: fast boolean
3799         if (demoversion < 128 || demo144_format)
3800         {
3801             // store it, using the console will set it too late
3802             cv_fastmonsters.EV = *demo_p++;
3803             cv_fastmonsters.func();
3804         }
3805         else
3806             demo_p++;  // legacy demo, ignore fastmonsters
3807 
3808         // header[7]: byte: no monsters present boolean
3809         nomonsters  = *demo_p++;
3810 #ifdef DEBUG_DEMO
3811         debug_Printf( " no monsters %i.\n", (int)nomonsters );
3812 #endif
3813         cv_rndsoundpitch.EV = 0;
3814     }
3815 
3816     // header[8]: byte: viewing player 0..3, 0=player1
3817     //added:08-02-98: added displayplayer because the status bar links
3818     // to the display player when playing back a demo.
3819     displayplayer = consoleplayer = *demo_p++;
3820     displayplayer_ptr = consoleplayer_ptr = &players[consoleplayer];  // [WDJ]
3821 
3822 #ifdef DEBUG_DEMO
3823     debug_Printf( " viewing player %i.\n",  (int)displayplayer );
3824 #endif
3825 
3826     //  support old v1.9 demos with ONLY 4 PLAYERS ! Man! what a shame!!!
3827     if( demoversion==109 )
3828     {
3829         // header[9..12]: byte: player[1..4] present boolean
3830         num_players = 4;
3831     }
3832     else if( boomdemo )
3833     {
3834         cv_rndsoundpitch.EV = EN_boom;  // normal in Boom, call M_Random
3835 
3836         // Boom ReadOptions
3837         // [WDJ] according to prboom
3838         // [0] monsters remember
3839         // [1] variable friction
3840         // [2] weapon recoil
3841         // [3] allow pushers
3842         // [4] ??
3843         // [5] player bobbing
3844         // [6] respawn
3845         // [7] fast monsters
3846         // [8] no monsters
3847         cv_monster_remember.EV = demo_p[0];
3848         EN_variable_friction = demo_p[1];
3849         cv_weapon_recoil.EV = demo_p[2];
3850         EN_pushers = demo_p[3];
3851 #ifdef DEBUG_DEMO
3852         debug_Printf( " respawn %i.\n", (int)demo_p[6] );
3853         debug_Printf( " fast monsters %i.\n", (int)demo_p[7] );
3854 #endif
3855         cv_respawnmonsters.EV = demo_p[6];  // respawn monsters, boolean
3856         cv_fastmonsters.EV = demo_p[7]; // fast monsters, boolean
3857         cv_fastmonsters.func();
3858         nomonsters = demo_p[8];  // nomonsters, boolean
3859 #ifdef DEBUG_DEMO
3860         debug_Printf( " no monsters %i.\n", (int)nomonsters );
3861 #endif
3862         // [9] demo insurance
3863         // [10..13] random number seed
3864         //   When demo insurance, Boom has random number generator per usage,
3865         //   all initialized from this seed.  DoomLegacy does not have this.
3866         // Seed is not needed for the standard random number generators.
3867         if( demo_p[9] )
3868           debug_Printf( " demo insurance RNG, not implemented.\n" );
3869 
3870         if( demoversion >= 203 ) // MBF and prboom
3871         {
3872             // [14] monster infighting
3873             // [15] dogs
3874             // [16..17] ??
3875             // [18..19] distfriend
3876             // [20] monster backing
3877             // [21] monster avoid hazards
3878             // [22] monster friction
3879             // [23] help friends
3880             // [24] dog jumping
3881             // [25] monkeys
3882             // [26..57] comp vector x32
3883             // [58] force old BSP
3884             // monster_infight from demo is 0/1
3885             // Feature enables 1=ON, Do not notify NET
3886             cv_monbehavior.EV = demo_p[14]? 2:5; // (infight:off)
3887 #ifdef DOGS
3888             cv_mbf_dogs.EV = demo_p[15];
3889 #endif
3890             EV_mbf_distfriend = ((demo_p[18]<<8) + demo_p[19]) << FRACBITS;
3891             cv_mbf_monster_backing.EV = demo_p[20];
3892             cv_mbf_monster_avoid_hazard.EV = demo_p[21];
3893             // Pass EN_monster_friction, and flag cv_monsterfriction
3894             EN_monster_friction = demo_p[22];
3895             cv_monsterfriction.EV = 0x80;  // MBF, Vanilla;
3896             cv_mbf_help_friend.EV = demo_p[23];
3897 #ifdef DOGS
3898             cv_mbf_dog_jumping.EV = demo_p[24];
3899 #endif
3900             cv_mbf_monkeys.EV = demo_p[25];
3901             // comp vector at [26],  1=old demo compatibility
3902             byte * comp = demo_p + 26;
3903             EN_mbf_telefrag = ! comp[comp_telefrag];
3904             cv_mbf_dropoff.EV = ! comp[comp_dropoff];
3905             EN_vile_revive_bug = comp[comp_vile];  // Vanilla
3906             EN_skull_limit = comp[comp_pain];
3907             EN_old_pain_spawn = comp[comp_skull];
3908             EN_blazing_double_sound = comp[comp_blazing];  // Vanilla
3909             EN_doorlight = ! comp[comp_doorlight];
3910             EN_boom_physics = ! comp[comp_model];
3911             EN_invul_god = ! comp[comp_god];
3912             cv_mbf_falloff.EV = ! comp[comp_falloff];
3913             EN_boom_floor = ! comp[comp_floors];
3914             cv_invul_skymap.EV = ! comp[comp_pursuit];  // 0=Vanilla, 1=Boom
3915             cv_mbf_pursuit.EV = ! comp[comp_pursuit];
3916             cv_doorstuck.EV = comp[comp_doorstuck]? 0:2; // Vanilla : MBF
3917             cv_mbf_staylift.EV = ! comp[comp_staylift];
3918             EN_catch_respawn_0 = ! comp[comp_respawn];
3919             EN_skull_bounce_fix = ! comp[comp_soul];
3920             cv_zerotags.EV = ! comp[comp_zerotags]; // 0=Vanilla, 1=Boom
3921             EN_doom_movestep_bug = comp[comp_moveblock];  // 1=Vanilla
3922         }
3923         else
3924         {
3925             // Boom, not MBF
3926             cv_doorstuck.EV = EN_boom;  // 1=Boom
3927         }
3928 
3929         demo_p += (demoversion == 200)? 256 : 64;  // option area size
3930 
3931 
3932         // byte: player[1..32] present boolean
3933         // Boom saved room for 32 players even though only supported 4
3934         num_players = (boom_compatibility_level < 200)? 4 : 32;
3935 
3936         if( boom_compatibility_mode )
3937         {
3938 #ifdef DEBUG_DEMO
3939             debug_Printf( " Boom demo imitating Doom2\n" );
3940 #endif
3941             demoversion = 110;  // imitate non-Boom demo
3942         }
3943     }
3944     else
3945     {
3946 #ifdef DEBUG_DEMO
3947        debug_Printf( " time limit %i.\n", (int)*demo_p );
3948 #endif
3949         if(demoversion<128)
3950         {
3951            // Here is a byte, but user value may exceed a byte.
3952            cv_timelimit.value = *demo_p++;
3953            cv_timelimit.func();
3954         }
3955         else
3956             demo_p++;
3957 
3958         if (demoversion<113)
3959         {
3960             // header[9..16]: byte: player[1..8] present boolean
3961             num_players = 8;
3962         }
3963         else
3964         {
3965             // header[17]: byte: multiplayer boolean
3966             if( demoversion>=131 ) {
3967                 multiplayer = *demo_p++;
3968 #ifdef DEBUG_DEMO
3969                 debug_Printf( " multi-player %i.\n", (int)multiplayer );
3970 #endif
3971             }
3972 
3973             // header[18..50]: byte: player[1..32] present boolean
3974             num_players = 32;
3975         }
3976     }
3977 
3978     if( demo_p > demoend )  goto broken_header;
3979 
3980 #if MAXPLAYERS>32
3981 #error Please add support for old lmps
3982 #endif
3983 
3984     // Read players in game.
3985     memset( playeringame, 0, sizeof(playeringame) );
3986     for (i=0 ; i<num_players ; i++)
3987     {
3988         playeringame[i] = *demo_p++;
3989 #ifdef DEBUG_DEMO
3990         if( playeringame[i] )
3991              debug_Printf( "   player %i\n", i+1 );
3992 #endif
3993     }
3994 
3995     // FIXME: do a proper test here
3996     if( demoversion<131 )
3997         multiplayer = playeringame[1];
3998 
3999     if( demo_p > demoend )  goto broken_header;
4000 
4001     // [WDJ]
4002     if( demo144_format )
4003     {
4004         byte * demo_p_next = demo_p + ((demoversion < 147)? 32 : 64);
4005         // more settings that affect playback
4006         cv_solidcorpse.EV = *demo_p++;
4007 #ifdef DOORDELAY_CONTROL
4008         adj_ticks_per_sec = *demo_p++;  // 0 is not default
4009         if( adj_ticks_per_sec < 20 )  adj_ticks_per_sec = 35;  // default
4010 #else
4011         demo_p++; 	// no doordelay
4012 #endif
4013         if( *demo_p >= 0x40 )  // Voodoo doll control
4014            voodoo_mode = *demo_p++ - 0x40;  // 0 is not default
4015         else
4016            voodoo_mode = VM_auto;  // default
4017         cv_instadeath.EV = *demo_p++;  // voodoo doll instadeath, 0 is default
4018         cv_monsterfriction.EV = *demo_p++;
4019         friction_model = *demo_p++;
4020         cv_rndsoundpitch.EV = *demo_p++;  // uses M_Random
4021         cv_monbehavior.EV = *demo_p++;
4022         if( demoversion_rev < VERREV(148,5) )
4023         {
4024 	    // Previous versions implemented full infight.
4025             if( cv_monbehavior.EV == 2 )  cv_monbehavior.EV = 6;
4026             else if( cv_monbehavior.EV == 4 )  cv_monbehavior.EV = 7;
4027 	}
4028         cv_doorstuck.EV = *demo_p++;
4029         cv_monstergravity.EV = *demo_p++;
4030         // Boom and MBF derived controls.
4031         cv_monster_remember.EV = *demo_p++;
4032         cv_weapon_recoil.EV = *demo_p++;
4033         cv_invul_skymap.EV = *demo_p++;
4034         cv_zerotags.EV = *demo_p++;
4035         cv_mbf_dropoff.EV = *demo_p++;
4036         cv_mbf_falloff.EV = *demo_p++;
4037         cv_mbf_pursuit.EV = *demo_p++;
4038         cv_mbf_monster_avoid_hazard.EV = *demo_p++;
4039         cv_mbf_monster_backing.EV = *demo_p++;
4040         cv_mbf_staylift.EV = *demo_p++;
4041         cv_mbf_help_friend.EV = *demo_p++;
4042         EV_mbf_distfriend = ((demo_p[0]<<8) + demo_p[1]) << FRACBITS;
4043         demo_p += 2;
4044         cv_mbf_monkeys.EV = *demo_p++;
4045 #ifdef DOGS
4046         cv_mbf_dogs.EV = *demo_p++;
4047         cv_mbf_dog_jumping.EV = *demo_p++;
4048 #else
4049         demo_p++;
4050         demo_p++;
4051 #endif
4052         cv_respawnmonsterstime.value = (demo_p[0]<<8) + demo_p[1];
4053         demo_p += 2;
4054         cv_itemrespawntime.value = (demo_p[0]<<8) + demo_p[1];
4055         demo_p += 2;
4056         game_comp_tic = (((((demo_p[0]<<8) + demo_p[1])<<8) + demo_p[2])<<8) + demo_p[3];
4057         demo_p += 4;
4058 
4059         demo_p = demo_p_next;  // skip rest of settings
4060         if( *demo_p++ != 0x55 )  goto broken_header;  // Sync mark, start of data
4061     }
4062 
4063     if( demo_p > demoend )  goto kill_demo;
4064 
4065     memset(oldcmd,0,sizeof(oldcmd));
4066 
4067     demoplayback = true;
4068 
4069     // don't spend a lot of time in loadlevel
4070     if(demoversion<127 || boomdemo)
4071     {
4072         precache = false;
4073         G_InitNew (skill, G_BuildMapName(episode, map),true);
4074         precache = true;
4075     }
4076     else
4077     {
4078         // wait map command in the demo
4079 //        gamestate = wipegamestate = GS_WAITINGPLAYERS;  // will display waiting counter
4080 //        gamestate = wipegamestate = GS_DEMOSCREEN;  // will advance to next demo
4081         gamestate = wipegamestate = GS_NULL;
4082     }
4083 
4084     CON_ToggleOff (); // may be also done at the end of map command
4085     return;
4086 
4087 
4088 bad_demo_version:
4089     GenPrintf(EMSG_warn, "\2ERROR: Incompatible demo (version %d). Legacy supports demo versions 109-%d.\n", demoversion, VERSION);
4090     goto kill_demo;
4091 
4092 broken_header:
4093 #ifdef DEBUG_DEMO
4094     debug_Printf( " broken demo header\n" );
4095 #endif
4096 
4097 kill_demo:
4098     Z_Free (demobuffer);
4099     playdemo_restore_settings();
4100     G_set_gamemode( gamemode );  // restore EN set by gamemode
4101     G_setup_VERSION();
4102 no_demo:
4103     gameaction = ga_nothing;
4104     return;
4105 }
4106 
4107 //
4108 // G_TimeDemo
4109 //             NOTE: name is a full filename for external demos
4110 //
4111 static byte EV_restore_cv_vidwait = 0;
4112 
G_TimeDemo(const char * name)4113 void G_TimeDemo (const char* name)
4114 {
4115     nodrawers = M_CheckParm ("-nodraw");
4116     noblit = M_CheckParm ("-noblit");
4117     EV_restore_cv_vidwait = cv_vidwait.EV;
4118     if( cv_vidwait.EV )
4119         CV_Set( &cv_vidwait, "0");
4120     timingdemo = true;
4121     singletics = true;
4122     framecount = 0;
4123     demostarttime = I_GetTime ();
4124     G_DeferedPlayDemo (name);
4125 }
4126 
4127 
G_DoneLevelLoad(void)4128 void G_DoneLevelLoad(void)
4129 {
4130     CONS_Printf("Load Level in %f sec\n",(float)(I_GetTime()-demostarttime)/TICRATE);
4131     framecount = 0;
4132     demostarttime = I_GetTime ();
4133 }
4134 
4135 
4136 // Called after a death or level completion to allow demos to be cleaned up
4137 // reset engine variable set for the demos
4138 // called from stopdemo command, map command, and g_checkdemoStatus.
G_StopDemo(void)4139 void G_StopDemo(void)
4140 {
4141     Z_Free (demobuffer);
4142     demoplayback  = false;
4143     timingdemo = false;
4144     singletics = false;
4145 
4146     playdemo_restore_settings();  // [WDJ] restore user settings
4147     G_set_gamemode( gamemode );  // restore EN set by gamemode
4148     G_setup_VERSION();
4149 
4150     gamestate=wipegamestate=GS_NULL;
4151     SV_StopServer();
4152 //    SV_StartServer();
4153     SV_ResetServer();
4154 
4155     // cleanup
4156     if( playdemo_name )
4157     {
4158         free(playdemo_name);
4159         playdemo_name = NULL;
4160     }
4161 }
4162 
4163 // Called by G_DeferedInitNew, G_ReadDemoTiccmd, G_WriteDemoTiccmd
4164 // return value is not used by any caller
G_CheckDemoStatus(void)4165 boolean G_CheckDemoStatus (void)
4166 {
4167     if (timingdemo)
4168     {
4169         int time;
4170         float f1,f2;
4171         time = I_GetTime () - demostarttime;
4172         if(!time) return true;
4173         G_StopDemo ();
4174         timingdemo = false;
4175         f1=time;
4176         f2=framecount*TICRATE;
4177         CONS_Printf ("timed %i gametics in %i realtics\n"
4178                      "%f seconds, %f avg fps\n"
4179                      ,leveltime,time,f1/TICRATE,f2/f1);
4180         if( EV_restore_cv_vidwait != cv_vidwait.EV )
4181             CV_SetValue(&cv_vidwait, EV_restore_cv_vidwait);
4182         D_AdvanceDemo ();
4183         return true;
4184     }
4185 
4186     if (demoplayback)
4187     {
4188         if (singledemo)
4189             I_Quit();  // No return
4190         G_StopDemo();
4191         D_AdvanceDemo ();
4192         return true;
4193     }
4194 
4195     if (demorecording)
4196     {
4197         *demo_p++ = DEMOMARKER;
4198         FIL_WriteFile (demoname, demobuffer, demo_p - demobuffer);
4199         Z_Free (demobuffer);
4200         demorecording = false;
4201 
4202         GenPrintf(EMSG_hud, "\2Demo %s recorded\n", demoname);
4203         return true;
4204     }
4205 
4206     return false;
4207 }
4208