1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: g_game.cpp 4688 2014-03-25 16:55:35Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 1998-2006 by Randy Heit (ZDoom).
8 // Copyright (C) 2006-2014 by The Odamex Team.
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License
12 // as published by the Free Software Foundation; either version 2
13 // of the License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // DESCRIPTION:
21 // G_GAME
22 //
23 //-----------------------------------------------------------------------------
24
25 #include "version.h"
26 #include "minilzo.h"
27 #include "m_alloc.h"
28 #include "doomdef.h"
29 #include "doomstat.h"
30 #include "d_netinf.h"
31 #include "z_zone.h"
32 #include "f_finale.h"
33 #include "m_argv.h"
34 #include "m_fileio.h"
35 #include "m_misc.h"
36 #include "m_menu.h"
37 #include "m_random.h"
38 #include "i_system.h"
39 #include "i_input.h"
40 #include "hardware.h"
41 #include "p_setup.h"
42 #include "p_saveg.h"
43 #include "p_tick.h"
44 #include "d_main.h"
45 #include "wi_stuff.h"
46 #include "hu_stuff.h"
47 #include "st_stuff.h"
48 #include "am_map.h"
49 #include "c_console.h"
50 #include "c_cvars.h"
51 #include "c_bind.h"
52 #include "c_dispatch.h"
53 #include "v_video.h"
54 #include "w_wad.h"
55 #include "p_local.h"
56 #include "s_sound.h"
57 #include "gstrings.h"
58 #include "r_data.h"
59 #include "r_sky.h"
60 #include "r_draw.h"
61 #include "g_game.h"
62 #include "g_level.h"
63 #include "cl_main.h"
64 #include "cl_demo.h"
65 #include "gi.h"
66 #include "hu_mousegraph.h"
67
68 #ifdef _XBOX
69 #include "i_xbox.h"
70 #endif
71
72 #include <math.h> // for pow()
73
74 #include <sstream>
75
76 #define SAVESTRINGSIZE 24
77
78 #define TURN180_TICKS 9 // [RH] # of ticks to complete a turn180
79
80 BOOL G_CheckDemoStatus (void);
81 void G_ReadDemoTiccmd ();
82 void G_WriteDemoTiccmd ();
83 void G_PlayerReborn (player_t &player);
84
85 void G_DoNewGame (void);
86 void G_DoLoadGame (void);
87 void G_DoCompleted (void);
88 void G_DoVictory (void);
89 void G_DoWorldDone (void);
90 void G_DoSaveGame (void);
91
92 bool C_DoNetDemoKey(event_t *ev);
93 bool C_DoSpectatorKey(event_t *ev);
94
95 void CL_QuitCommand();
96
97 EXTERN_CVAR (sv_skill)
98 EXTERN_CVAR (novert)
99 EXTERN_CVAR (sv_monstersrespawn)
100 EXTERN_CVAR (sv_itemsrespawn)
101 EXTERN_CVAR (sv_weaponstay)
102 EXTERN_CVAR (sv_keepkeys)
103 EXTERN_CVAR (co_nosilentspawns)
104 EXTERN_CVAR (co_allowdropoff)
105
106 EXTERN_CVAR (chasedemo)
107
108 gameaction_t gameaction;
109 gamestate_t gamestate = GS_STARTUP;
110 BOOL respawnmonsters;
111
112 BOOL paused;
113 BOOL sendpause; // send a pause event next tic
114 BOOL sendsave; // send a save event next tic
115 BOOL usergame; // ok to save / end game
116 BOOL sendcenterview; // send a center view event next tic
117
118 bool timingdemo; // if true, exit with report on completion
119 bool nodrawers; // for comparative timing purposes
120 bool noblit; // for comparative timing purposes
121
122 BOOL viewactive;
123
124 // Describes if a network game is being played
125 BOOL network_game;
126 // Use only for demos, it is a old variable for the old network code
127 BOOL netgame;
128 // Describes if this is a multiplayer game or not
129 BOOL multiplayer;
130 // The player vector, contains all player information
131 Players players;
132
133 byte consoleplayer_id; // player taking events and displaying
134 byte displayplayer_id; // view being displayed
135 int gametic;
136 bool singleplayerjustdied = false; // Nes - When it's okay for single-player servers to reload.
137
138 enum demoversion_t
139 {
140 LMP_DOOM_1_9,
141 LMP_DOOM_1_9_1, // longtics hack
142 }demoversion;
143
144 #define DOOM_1_4_DEMO 0x68
145 #define DOOM_1_5_DEMO 0x69
146 #define DOOM_1_6_DEMO 0x6A
147 #define DOOM_1_7_DEMO 0x6B
148 #define DOOM_1_8_DEMO 0x6C
149 #define DOOM_1_9_DEMO 0x6D
150 #define DOOM_1_9p_DEMO 0x6E
151 #define DOOM_1_9_1_DEMO 0x6F
152
153 EXTERN_CVAR(sv_nomonsters)
EXTERN_CVAR(sv_fastmonsters)154 EXTERN_CVAR(sv_fastmonsters)
155 EXTERN_CVAR(sv_freelook)
156 EXTERN_CVAR(sv_allowjump)
157 EXTERN_CVAR(co_realactorheight)
158 EXTERN_CVAR(co_zdoomphys)
159 EXTERN_CVAR(co_fixweaponimpacts)
160 EXTERN_CVAR(co_blockmapfix)
161 EXTERN_CVAR (dynresval) // [Toke - Mouse] Dynamic Resolution Value
162 EXTERN_CVAR (dynres_state) // [Toke - Mouse] Dynamic Resolution on/off
163 EXTERN_CVAR (m_filter)
164 EXTERN_CVAR (hud_mousegraph)
165 EXTERN_CVAR (cl_predictpickup)
166
167 CVAR_FUNC_IMPL(cl_mouselook)
168 {
169 // Nes - center the view
170 AddCommandString("centerview");
171
172 // Nes - update skies
173 R_InitSkyMap ();
174 }
175
176 char demoname[256];
177 BOOL demorecording;
178 BOOL demoplayback;
179 BOOL democlassic;
180 BOOL demonew; // [RH] Only used around G_InitNew for demos
181
182 extern bool simulated_connection;
183
184 int iffdemover;
185 byte* demobuffer;
186 byte *demo_p, *demo_e;
187 size_t maxdemosize;
188 byte* zdemformend; // end of FORM ZDEM chunk
189 byte* zdembodyend; // end of ZDEM BODY chunk
190 BOOL singledemo; // quit after playing a demo from cmdline
191 int demostartgametic;
192 FILE *recorddemo_fp;
193
194 BOOL precache = true; // if true, load all graphics at start
195
196 wbstartstruct_t wminfo; // parms for world map / intermission
197
198 byte* savebuffer;
199
200
201 #define MAXPLMOVE (forwardmove[1])
202
203 #define TURBOTHRESHOLD 12800
204
205 float normforwardmove[2] = {0x19, 0x32}; // [RH] For setting turbo from console
206 float normsidemove[2] = {0x18, 0x28}; // [RH] Ditto
207
208 fixed_t forwardmove[2], sidemove[2];
209 fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
210 fixed_t flyspeed[2] = {1*256, 3*256};
211 int lookspeed[2] = {450, 512};
212
213 #define SLOWTURNTICS 6
214
215 EXTERN_CVAR (cl_run)
216
217 EXTERN_CVAR (mouse_type)
218 EXTERN_CVAR (invertmouse)
219 EXTERN_CVAR (lookstrafe)
220 EXTERN_CVAR (mouse_acceleration)
221 EXTERN_CVAR (mouse_threshold)
222 EXTERN_CVAR (m_pitch)
223 EXTERN_CVAR (m_yaw)
224 EXTERN_CVAR (m_forward)
225 EXTERN_CVAR (m_side)
226
227 int turnheld; // for accelerative turning
228
229 // mouse values are used once
230 int mousex;
231 int mousey;
232
233 // [Toke - Mouse] new mouse stuff
234 int mousexleft;
235 int mouseydown;
236
237 // Joystick values are repeated
238 // Store a value for each of the analog axis controls -- Hyper_Eye
239 int joyforward;
240 int joystrafe;
241 int joyturn;
242 int joylook;
243
244 EXTERN_CVAR (joy_forwardaxis)
245 EXTERN_CVAR (joy_strafeaxis)
246 EXTERN_CVAR (joy_turnaxis)
247 EXTERN_CVAR (joy_lookaxis)
248 EXTERN_CVAR (joy_sensitivity)
249 EXTERN_CVAR (joy_invert)
250 EXTERN_CVAR (joy_freelook)
251
252 int savegameslot;
253 char savedescription[32];
254
consoleplayer()255 player_t &consoleplayer()
256 {
257 return idplayer(consoleplayer_id);
258 }
259
displayplayer()260 player_t &displayplayer()
261 {
262 return idplayer(displayplayer_id);
263 }
264
listenplayer()265 player_t &listenplayer()
266 {
267 return displayplayer();
268 }
269
270 // [RH] Name of screenshot file to generate (usually NULL)
271 std::string shotfile;
272
273 // [Fly] don't allow to change turbo in csDoom
G_SetDefaultTurbo(void)274 void G_SetDefaultTurbo (void)
275 {
276 forwardmove[0] = (int)(normforwardmove[0]);
277 forwardmove[1] = (int)(normforwardmove[1]);
278 sidemove[0] = (int)(normsidemove[0]);
279 sidemove[1] = (int)(normsidemove[1]);
280 }
281
282
283 /* [RH] Impulses: Temporary hack to get weapon changing
284 * working with keybindings until I can get the
285 * inventory system working.
286 *
287 * So this turned out to not be so temporary. It *will*
288 * change, though.
289 */
290 int Impulse;
291
BEGIN_COMMAND(impulse)292 BEGIN_COMMAND (impulse)
293 {
294 if (argc > 1)
295 Impulse = atoi (argv[1]);
296 }
297 END_COMMAND (impulse)
298
BEGIN_COMMAND(centerview)299 BEGIN_COMMAND (centerview)
300 {
301 sendcenterview = true;
302 }
303 END_COMMAND (centerview)
304
BEGIN_COMMAND(pause)305 BEGIN_COMMAND (pause)
306 {
307 sendpause = true;
308 }
309 END_COMMAND (pause)
310
BEGIN_COMMAND(turnspeeds)311 BEGIN_COMMAND (turnspeeds)
312 {
313 if (argc == 1)
314 {
315 Printf (PRINT_HIGH, "Current turn speeds: %ld %ld %ld\n",
316 angleturn[0], angleturn[1], angleturn[2]);
317 }
318 else
319 {
320 size_t i;
321 for (i = 1; i <= 3 && i < argc; i++)
322 angleturn[i-1] = atoi (argv[i]);
323
324 if (i <= 2)
325 angleturn[1] = angleturn[0] * 2;
326 if (i <= 3)
327 angleturn[2] = angleturn[0] / 2;
328 }
329 }
330 END_COMMAND (turnspeeds)
331
332 static int turntick;
BEGIN_COMMAND(turn180)333 BEGIN_COMMAND (turn180)
334 {
335 turntick = TURN180_TICKS;
336 }
337 END_COMMAND (turn180)
338
339 weapontype_t P_GetNextWeapon(player_t *player, bool forward);
BEGIN_COMMAND(weapnext)340 BEGIN_COMMAND (weapnext)
341 {
342 weapontype_t newweapon = P_GetNextWeapon(&consoleplayer(), true);
343 if (newweapon != wp_nochange)
344 Impulse = int(newweapon) + 50;
345 }
346 END_COMMAND (weapnext)
347
BEGIN_COMMAND(weapprev)348 BEGIN_COMMAND (weapprev)
349 {
350 weapontype_t newweapon = P_GetNextWeapon(&consoleplayer(), false);
351 if (newweapon != wp_nochange)
352 Impulse = int(newweapon) + 50;
353 }
354 END_COMMAND (weapprev)
355
356 extern constate_e ConsoleState;
357
358 //
359 // G_BuildTiccmd
360 // Builds a ticcmd from all of the available inputs
361 // or reads it from the demo buffer.
362 // If recording a demo, write it out
363 //
G_BuildTiccmd(ticcmd_t * cmd)364 void G_BuildTiccmd (ticcmd_t *cmd)
365 {
366 int strafe;
367 int speed;
368 int tspeed;
369 int forward;
370 int side;
371 int look;
372 int fly;
373
374 ticcmd_t *base;
375
376 base = I_BaseTiccmd (); // empty, or external driver
377
378 memcpy (cmd,base,sizeof(*cmd));
379
380 strafe = Actions[ACTION_STRAFE];
381 speed = Actions[ACTION_SPEED];
382 if (cl_run)
383 speed ^= 1;
384
385 forward = side = look = fly = 0;
386
387 // GhostlyDeath -- USE takes us out of spectator mode
388 if ((&consoleplayer())->spectator && Actions[ACTION_USE] && connected)
389 {
390 AddCommandString("join");
391 }
392
393 // [RH] only use two stage accelerative turning on the keyboard
394 // and not the joystick, since we treat the joystick as
395 // the analog device it is.
396 if ((Actions[ACTION_LEFT]) || (Actions[ACTION_RIGHT]))
397 turnheld += 1;
398 else
399 turnheld = 0;
400
401 if (turnheld < SLOWTURNTICS)
402 tspeed = 2; // slow turn
403 else
404 tspeed = speed;
405
406 // let movement keys cancel each other out
407 if (strafe)
408 {
409 if (Actions[ACTION_RIGHT])
410 side += sidemove[speed];
411 if (Actions[ACTION_LEFT])
412 side -= sidemove[speed];
413 }
414 else
415 {
416 if (Actions[ACTION_RIGHT])
417 cmd->yaw -= angleturn[tspeed];
418 if (Actions[ACTION_LEFT])
419 cmd->yaw += angleturn[tspeed];
420 }
421
422 // Joystick analog strafing -- Hyper_Eye
423 side += (int)(((float)joystrafe / (float)SHRT_MAX) * sidemove[speed]);
424
425 if (Actions[ACTION_LOOKUP])
426 look += lookspeed[speed];
427 if (Actions[ACTION_LOOKDOWN])
428 look -= lookspeed[speed];
429
430 if (Actions[ACTION_MOVEUP])
431 fly += flyspeed[speed];
432 if (Actions[ACTION_MOVEDOWN])
433 fly -= flyspeed[speed];
434
435 if (Actions[ACTION_KLOOK])
436 {
437 if (Actions[ACTION_FORWARD])
438 look += lookspeed[speed];
439 if (Actions[ACTION_BACK])
440 look -= lookspeed[speed];
441 }
442 else
443 {
444 if (Actions[ACTION_FORWARD])
445 forward += forwardmove[speed];
446 if (Actions[ACTION_BACK])
447 forward -= forwardmove[speed];
448 }
449
450 // Joystick analog look -- Hyper_Eye
451 if(joy_freelook && sv_freelook)
452 {
453 if (joy_invert)
454 look += (int)(((float)joylook / (float)SHRT_MAX) * lookspeed[speed]);
455 else
456 look -= (int)(((float)joylook / (float)SHRT_MAX) * lookspeed[speed]);
457 }
458
459 if (Actions[ACTION_MOVERIGHT])
460 side += sidemove[speed];
461 if (Actions[ACTION_MOVELEFT])
462 side -= sidemove[speed];
463
464 // buttons
465 if (Actions[ACTION_ATTACK] && ConsoleState == c_up && !headsupactive) // john - only add attack when console up
466 cmd->buttons |= BT_ATTACK;
467
468 if (Actions[ACTION_USE])
469 cmd->buttons |= BT_USE;
470
471 if (Actions[ACTION_JUMP])
472 cmd->buttons |= BT_JUMP;
473
474 // [RH] Handle impulses. If they are between 1 and 7,
475 // they get sent as weapon change events.
476 if (Impulse >= 1 && Impulse <= 8)
477 {
478 cmd->buttons |= BT_CHANGE;
479 cmd->buttons |= (Impulse - 1) << BT_WEAPONSHIFT;
480 }
481 else
482 {
483 cmd->impulse = Impulse;
484 }
485 Impulse = 0;
486
487 // [SL] 2012-03-31 - Let the server know when the client is predicting a
488 // weapon change due to a weapon pickup
489 if (!serverside && cl_predictpickup)
490 {
491 if (!cmd->impulse && !(cmd->buttons & BT_CHANGE) &&
492 consoleplayer().pendingweapon != wp_nochange)
493 cmd->impulse = 50 + static_cast<int>(consoleplayer().pendingweapon);
494 }
495
496 if (strafe || lookstrafe)
497 side += (int)(((float)joyturn / (float)SHRT_MAX) * sidemove[speed]);
498 else
499 cmd->yaw -= (short)((((float)joyturn / (float)SHRT_MAX) * angleturn[1]) * (joy_sensitivity / 10));
500
501 if (Actions[ACTION_MLOOK])
502 {
503 if (joy_invert)
504 look += (int)(((float)joyforward / (float)SHRT_MAX) * lookspeed[speed]);
505 else
506 look -= (int)(((float)joyforward / (float)SHRT_MAX) * lookspeed[speed]);
507 }
508 else
509 {
510 forward -= (int)(((float)joyforward / (float)SHRT_MAX) * forwardmove[speed]);
511 }
512
513 if ((Actions[ACTION_MLOOK]) || (cl_mouselook && sv_freelook))
514 {
515 int val;
516
517 val = (int)((float)(mousey * 16) * m_pitch);
518 if (invertmouse)
519 look -= val;
520 else
521 look += val;
522 }
523 else
524 {
525 if (novert == 0) // [Toke - Mouse] acts like novert.exe
526 {
527 forward += (int)((float)mousey * m_forward);
528 }
529 }
530
531 if (sendcenterview)
532 {
533 sendcenterview = false;
534 look = CENTERVIEW;
535 }
536 else
537 {
538 if (look > 32767)
539 look = 32767;
540 else if (look < -32767)
541 look = -32767;
542 }
543
544 if (strafe || lookstrafe)
545 side += (int)((float)mousex * m_side);
546 else
547 cmd->yaw -= (int)((float)(mousex*0x8) * m_yaw) / ticdup;
548
549 mousex = mousey = 0;
550
551 if (forward > MAXPLMOVE)
552 forward = MAXPLMOVE;
553 else if (forward < -MAXPLMOVE)
554 forward = -MAXPLMOVE;
555 if (side > MAXPLMOVE)
556 side = MAXPLMOVE;
557 else if (side < -MAXPLMOVE)
558 side = -MAXPLMOVE;
559
560 cmd->forwardmove += forward;
561 cmd->sidemove += side;
562 cmd->pitch = look;
563 cmd->upmove = fly;
564
565 // special buttons
566 if (sendpause)
567 {
568 sendpause = false;
569 cmd->buttons = BT_SPECIAL | BTS_PAUSE;
570 }
571
572 if (sendsave)
573 {
574 sendsave = false;
575 cmd->buttons = BT_SPECIAL | BTS_SAVEGAME | (savegameslot<<BTS_SAVESHIFT);
576 }
577
578 cmd->forwardmove <<= 8;
579 cmd->sidemove <<= 8;
580
581 // [RH] 180-degree turn overrides all other yaws
582 if (turntick) {
583 turntick--;
584 cmd->yaw = (ANG180 / TURN180_TICKS) >> 16;
585 }
586 }
587
588
589 // [RH] Spy mode has been separated into two console commands.
590 // One goes forward; the other goes backward.
591 // denis - todo - should this be used somewhere?
592 /*static void ChangeSpy (void)
593 {
594 consoleplayer().camera = players[displayplayer].mo;
595 S_UpdateSounds(consoleplayer().camera);
596 ST_Start(); // killough 3/7/98: switch status bar views too
597 }*/
598
599
G_ConvertMouseSettings(int old_type,int new_type)600 void G_ConvertMouseSettings(int old_type, int new_type)
601 {
602 if (old_type == new_type)
603 return;
604
605 if (new_type == MOUSE_ODAMEX)
606 new_type = MOUSE_DOOM;
607
608 // first convert to ZDoom settings
609 if (old_type == MOUSE_DOOM)
610 {
611 mouse_sensitivity.Set((mouse_sensitivity + 5.0f) / 40.0f);
612 m_pitch.Set(m_pitch * 4.0f);
613 }
614 else if (old_type == MOUSE_ODAMEX)
615 {
616 mouse_sensitivity.Set(mouse_sensitivity / 40.0f);
617 m_pitch.Set(m_pitch * 4.0f);
618 }
619
620 // convert to the destination type
621 if (new_type == MOUSE_DOOM)
622 {
623 mouse_sensitivity.Set((mouse_sensitivity * 40.0f) - 5.0f);
624 m_pitch.Set(m_pitch * 0.25f);
625 }
626 else if (new_type == MOUSE_ODAMEX)
627 {
628 mouse_sensitivity.Set(mouse_sensitivity * 40.0f);
629 m_pitch.Set(m_pitch * 0.25f);
630 }
631 }
632
G_DoomMouseScaleX(float x)633 float G_DoomMouseScaleX(float x)
634 {
635 return (x * (mouse_sensitivity + 5.0f) / 10.0f);
636 }
637
G_DoomMouseScaleY(float y)638 float G_DoomMouseScaleY(float y)
639 {
640 return G_DoomMouseScaleX(y); // identical scaling for x and y
641 }
642
G_ZDoomDIMouseScaleX(float x)643 float G_ZDoomDIMouseScaleX(float x)
644 {
645 return (x * 4.0f * mouse_sensitivity);
646 }
647
G_ZDoomDIMouseScaleY(float y)648 float G_ZDoomDIMouseScaleY(float y)
649 {
650 return (y * mouse_sensitivity);
651 }
652
G_ProcessMouseMovementEvent(const event_t * ev)653 void G_ProcessMouseMovementEvent(const event_t *ev)
654 {
655 static float fprevx = 0.0f, fprevy = 0.0f;
656 float fmousex = (float)ev->data2;
657 float fmousey = (float)ev->data3;
658
659 if (mouse_acceleration > 0.0f)
660 {
661 // apply mouse acceleration (from Chocolate Doom)
662 if (fmousex > mouse_threshold)
663 fmousex = (fmousex - mouse_threshold) * mouse_acceleration + mouse_threshold;
664 else if (fmousex < -mouse_threshold)
665 fmousex = (fmousex + mouse_threshold) * mouse_acceleration - mouse_threshold;
666 if (fmousey > mouse_threshold)
667 fmousey = (fmousey - mouse_threshold) * mouse_acceleration + mouse_threshold;
668 else if (fmousey < -mouse_threshold)
669 fmousey = (fmousey + mouse_threshold) * mouse_acceleration - mouse_threshold;
670 }
671
672 if (m_filter)
673 {
674 // smooth out the mouse input
675 fmousex = (fmousex + fprevx) / 2.0f;
676 fmousey = (fmousey + fprevy) / 2.0f;
677 }
678
679 fprevx = fmousex;
680 fprevy = fmousey;
681
682 if (mouse_type == MOUSE_ZDOOM_DI)
683 {
684 fmousex = G_ZDoomDIMouseScaleX(fmousex);
685 fmousey = G_ZDoomDIMouseScaleY(fmousey);
686 }
687 else
688 {
689 fmousex = G_DoomMouseScaleX(fmousex);
690 fmousey = G_DoomMouseScaleY(fmousey);
691 }
692
693 if (dynres_state)
694 {
695 // add some funky exponential sensitivity
696 if (fmousex < 0.0f)
697 fmousex = -pow(-fmousex, dynresval);
698 else
699 fmousex = pow(fmousex, dynresval);
700
701 if (fmousey < 0.0f)
702 fmousey = -pow(-fmousey, dynresval);
703 else
704 fmousey = pow(fmousey, dynresval);
705 }
706
707 mousex = (int)fmousex;
708 mousey = (int)fmousey;
709 }
710
711 //
712 // G_Responder
713 // Get info needed to make ticcmd_ts for the players.
714 //
G_Responder(event_t * ev)715 BOOL G_Responder (event_t *ev)
716 {
717 // any other key pops up menu if in demos
718 // [RH] But only if the key isn't bound to a "special" command
719 if (gameaction == ga_nothing &&
720 (demoplayback || gamestate == GS_DEMOSCREEN))
721 {
722 const char *cmd = C_GetBinding (ev->data1);
723
724 if (ev->type == ev_keydown)
725 {
726
727 if (!cmd || (
728 strnicmp (cmd, "menu_", 5) &&
729 stricmp (cmd, "toggleconsole") &&
730 stricmp (cmd, "sizeup") &&
731 stricmp (cmd, "sizedown") &&
732 stricmp (cmd, "togglemap") &&
733 stricmp (cmd, "spynext") &&
734 stricmp (cmd, "chase") &&
735 stricmp (cmd, "+showscores") &&
736 stricmp (cmd, "bumpgamma") &&
737 stricmp (cmd, "screenshot") &&
738 stricmp (cmd, "stepmode") &&
739 stricmp (cmd, "step")))
740 {
741 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
742 M_StartControlPanel ();
743 return true;
744 }
745 else
746 {
747 return C_DoKey (ev);
748 }
749 }
750 if (cmd && cmd[0] == '+')
751 return C_DoKey (ev);
752
753 return false;
754 }
755
756 if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)
757 {
758 if (C_DoNetDemoKey(ev)) // netdemo playback ate the event
759 return true;
760 if (C_DoSpectatorKey(ev))
761 return true;
762
763 if (HU_Responder (ev))
764 return true; // chat ate the event
765 if (ST_Responder (ev))
766 return true; // status window ate it
767 if (!viewactive)
768 if (AM_Responder (ev))
769 return true; // automap ate it
770 }
771 else if (gamestate == GS_FINALE)
772 {
773 if (F_Responder (ev))
774 return true; // finale ate the event
775 }
776
777 switch (ev->type)
778 {
779 case ev_keydown:
780 if (C_DoKey (ev))
781 return true;
782 break;
783
784 case ev_keyup:
785 C_DoKey (ev);
786 break;
787
788 // [Toke - Mouse] New mouse code
789 case ev_mouse:
790 G_ProcessMouseMovementEvent(ev);
791
792 if (hud_mousegraph)
793 mousegraph.append(mousex, mousey);
794
795 break;
796
797 case ev_joystick:
798 if(ev->data1 == 0) // Axis Movement
799 {
800 if(ev->data2 == joy_strafeaxis) // Strafe
801 joystrafe = ev->data3;
802 else if(ev->data2 == joy_forwardaxis) // Move
803 joyforward = ev->data3;
804 else if(ev->data2 == joy_turnaxis) // Turn
805 joyturn = ev->data3;
806 else if(ev->data2 == joy_lookaxis) // Look
807 joylook = ev->data3;
808 else
809 break; // The default case will be to treat the analog control as a button -- Hyper_Eye
810 }
811
812 break;
813
814 }
815
816 // [RH] If the view is active, give the automap a chance at
817 // the events *last* so that any bound keys get precedence.
818
819 if (gamestate == GS_LEVEL && viewactive)
820 return AM_Responder (ev);
821
822 if (ev->type == ev_keydown ||
823 ev->type == ev_mouse ||
824 ev->type == ev_joystick)
825 return true;
826 else
827 return false;
828 }
829
830
831 int netin;
832 int netout;
833 int outrate;
834
BEGIN_COMMAND(netstat)835 BEGIN_COMMAND(netstat)
836 {
837 Printf (PRINT_HIGH, "in = %d out = %d", netin, netout);
838 }
839 END_COMMAND(netstat)
840
841 void P_MovePlayer (player_t *player);
842 void P_CalcHeight (player_t *player);
843 void P_DeathThink (player_t *player);
844 void CL_SimulateWorld();
845 //
846 // G_Ticker
847 // Make ticcmd_ts for the players.
848 //
849 extern DCanvas *page;
850 extern int connecttimeout;
851
G_Ticker(void)852 void G_Ticker (void)
853 {
854 int buf;
855
856 // do player reborns if needed
857 if(serverside)
858 for (Players::iterator it = players.begin();it != players.end();++it)
859 {
860 if (it->ingame() && (it->playerstate == PST_REBORN || it->playerstate == PST_ENTER))
861 G_DoReborn(*it);
862 }
863
864 // do things to change the game state
865 while (gameaction != ga_nothing)
866 {
867 switch (gameaction)
868 {
869 case ga_loadlevel:
870 G_DoLoadLevel (-1);
871 break;
872 case ga_fullresetlevel:
873 gameaction = ga_nothing;
874 break;
875 case ga_resetlevel:
876 gameaction = ga_nothing;
877 break;
878 case ga_newgame:
879 G_DoNewGame ();
880 break;
881 case ga_loadgame:
882 G_DoLoadGame ();
883 break;
884 case ga_savegame:
885 G_DoSaveGame ();
886 break;
887 case ga_playdemo:
888 G_DoPlayDemo ();
889 break;
890 case ga_completed:
891 G_DoCompleted ();
892 break;
893 case ga_victory:
894 gameaction = ga_nothing;
895 break;
896 case ga_worlddone:
897 G_DoWorldDone ();
898 break;
899 case ga_screenshot:
900 I_ScreenShot(shotfile);
901 gameaction = ga_nothing;
902 break;
903 case ga_fullconsole:
904 C_FullConsole ();
905 gameaction = ga_nothing;
906 break;
907 case ga_nothing:
908 break;
909 }
910 C_AdjustBottom ();
911 }
912
913 // get commands
914 buf = gametic%BACKUPTICS;
915 memcpy (&consoleplayer().cmd, &consoleplayer().netcmds[buf], sizeof(ticcmd_t));
916
917 static int realrate = 0;
918 int packet_size;
919
920 if (demoplayback)
921 G_ReadDemoTiccmd(); // play all player commands
922 if (demorecording)
923 G_WriteDemoTiccmd(); // read in all player commands
924
925 if(netdemo.isPlaying())
926 {
927 netdemo.readMessages(&net_message);
928 }
929
930 if (connected && !simulated_connection)
931 {
932 while ((packet_size = NET_GetPacket()) )
933 {
934 // denis - don't accept candy from strangers
935 if(!NET_CompareAdr(serveraddr, net_from))
936 break;
937
938 realrate += packet_size;
939 last_received = gametic;
940 noservermsgs = false;
941
942 CL_ReadPacketHeader();
943
944 if (netdemo.isRecording())
945 netdemo.capture(&net_message);
946
947 CL_ParseCommands();
948
949 if (gameaction == ga_fullconsole) // Host_EndGame was called
950 return;
951 }
952
953 if (!(gametic%TICRATE))
954 {
955 netin = realrate;
956 realrate = 0;
957 }
958
959 CL_SaveCmd(); // save console commands
960 if (!noservermsgs)
961 CL_SendCmd(); // send console commands to the server
962
963 if (!(gametic%TICRATE))
964 {
965 netout = outrate;
966 outrate = 0;
967 }
968
969 if (gametic - last_received > 65)
970 noservermsgs = true;
971 }
972 else if (NET_GetPacket() && !simulated_connection)
973 {
974 // denis - don't accept candy from strangers
975 if((gamestate == GS_DOWNLOAD || gamestate == GS_CONNECTING)
976 && NET_CompareAdr(serveraddr, net_from))
977 {
978 if (netdemo.isRecording())
979 netdemo.capture(&net_message);
980
981 int type = MSG_ReadLong();
982
983 if(type == CHALLENGE)
984 {
985 CL_PrepareConnect();
986 }
987 else if(type == 0)
988 {
989 if (!CL_Connect())
990 memset (&serveraddr, 0, sizeof(serveraddr));
991
992 connecttimeout = 0;
993 }
994 else
995 {
996 // we are already connected to this server, quit first
997 MSG_WriteMarker(&net_buffer, clc_disconnect);
998 NET_SendPacket(net_buffer, serveraddr);
999 }
1000 }
1001 }
1002
1003 if (netdemo.isRecording())
1004 netdemo.writeMessages();
1005
1006 // check for special buttons
1007 if(serverside && consoleplayer().ingame())
1008 {
1009 player_t &player = consoleplayer();
1010
1011 if (player.cmd.buttons & BT_SPECIAL)
1012 {
1013 switch (player.cmd.buttons & BT_SPECIALMASK)
1014 {
1015 case BTS_PAUSE:
1016 paused ^= 1;
1017 if (paused)
1018 S_PauseSound ();
1019 else
1020 S_ResumeSound ();
1021 break;
1022
1023 case BTS_SAVEGAME:
1024 if (!savedescription[0])
1025 strcpy (savedescription, "NET GAME");
1026 savegameslot = (player.cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT;
1027 gameaction = ga_savegame;
1028 break;
1029 }
1030 }
1031 }
1032
1033 // do main actions
1034 switch (gamestate)
1035 {
1036 case GS_LEVEL:
1037 if(clientside && !serverside)
1038 {
1039 if (!consoleplayer().mo)
1040 {
1041 // [SL] 2011-12-14 - Spawn message from server has not arrived
1042 // yet. Fake it and hope it arrives soon.
1043 AActor *mobj = new AActor (0, 0, 0, MT_PLAYER);
1044 mobj->flags &= ~MF_SOLID;
1045 mobj->flags2 |= MF2_DONTDRAW;
1046 consoleplayer().mo = mobj->ptr();
1047 consoleplayer().mo->player = &consoleplayer();
1048 G_PlayerReborn(consoleplayer());
1049 DPrintf("Did not receive spawn for consoleplayer.\n");
1050 }
1051
1052 CL_SimulateWorld();
1053 CL_PredictWorld();
1054 }
1055 P_Ticker ();
1056 ST_Ticker ();
1057 AM_Ticker ();
1058 break;
1059
1060 case GS_INTERMISSION:
1061 ST_Ticker ();
1062 WI_Ticker ();
1063 break;
1064
1065 case GS_FINALE:
1066 F_Ticker ();
1067 break;
1068
1069 case GS_DEMOSCREEN:
1070 D_PageTicker ();
1071 break;
1072
1073 default:
1074 break;
1075 }
1076 }
1077
1078
1079 //
1080 // PLAYER STRUCTURE FUNCTIONS
1081 // also see P_SpawnPlayer in P_Mobj
1082 //
1083
1084 //
1085 // G_PlayerFinishLevel
1086 // Call when a player completes a level.
1087 //
G_PlayerFinishLevel(player_t & player)1088 void G_PlayerFinishLevel (player_t &player)
1089 {
1090 player_t *p;
1091
1092 p = &player;
1093
1094 memset (p->powers, 0, sizeof (p->powers));
1095 memset (p->cards, 0, sizeof (p->cards));
1096
1097 if(p->mo)
1098 p->mo->flags &= ~MF_SHADOW; // cancel invisibility
1099
1100 p->extralight = 0; // cancel gun flashes
1101 p->fixedcolormap = 0; // cancel ir goggles
1102 p->damagecount = 0; // no palette changes
1103 p->bonuscount = 0;
1104 }
1105
1106
1107 //
1108 // G_PlayerReborn
1109 // Called after a player dies
1110 // almost everything is cleared and initialized
1111 //
G_PlayerReborn(player_t & p)1112 void G_PlayerReborn (player_t &p) // [Toke - todo] clean this function
1113 {
1114 size_t i;
1115 for (i = 0; i < NUMAMMO; i++)
1116 {
1117 p.maxammo[i] = maxammo[i];
1118 p.ammo[i] = 0;
1119 }
1120 for (i = 0; i < NUMWEAPONS; i++)
1121 p.weaponowned[i] = false;
1122 if (!sv_keepkeys)
1123 {
1124 for (i = 0; i < NUMCARDS; i++)
1125 p.cards[i] = false;
1126 }
1127 for (i = 0; i < NUMPOWERS; i++)
1128 p.powers[i] = false;
1129 for (i = 0; i < NUMFLAGS; i++)
1130 p.flags[i] = false;
1131 p.backpack = false;
1132
1133 p.usedown = p.attackdown = true; // don't do anything immediately
1134 p.playerstate = PST_LIVE;
1135 p.health = deh.StartHealth; // [RH] Used to be MAXHEALTH
1136 p.armortype = 0;
1137 p.armorpoints = 0;
1138 p.readyweapon = p.pendingweapon = wp_pistol;
1139 p.weaponowned[wp_fist] = true;
1140 p.weaponowned[wp_pistol] = true;
1141 p.ammo[am_clip] = deh.StartBullets; // [RH] Used to be 50
1142
1143 p.death_time = 0;
1144 p.tic = 0;
1145 }
1146
1147 //
1148 // G_CheckSpot
1149 // Returns false if the player cannot be respawned
1150 // at the given mapthing2_t spot
1151 // because something is occupying it
1152 //
1153 void P_SpawnPlayer (player_t &player, mapthing2_t* mthing);
1154
G_CheckSpot(player_t & player,mapthing2_t * mthing)1155 bool G_CheckSpot (player_t &player, mapthing2_t *mthing)
1156 {
1157 fixed_t x;
1158 fixed_t y;
1159 fixed_t z, oldz;
1160 unsigned an;
1161 AActor* mo;
1162 size_t i;
1163 fixed_t xa,ya;
1164
1165 x = mthing->x << FRACBITS;
1166 y = mthing->y << FRACBITS;
1167 z = mthing->z << FRACBITS;
1168
1169 z = P_FloorHeight(x, y);
1170
1171 if (!player.mo)
1172 {
1173 // first spawn of level, before corpses
1174 for (Players::iterator it = players.begin();it != players.end();++it)
1175 {
1176 if (&player == &*it)
1177 continue;
1178
1179 if (it->mo && it->mo->x == x && it->mo->y == y)
1180 return false;
1181 }
1182 return true;
1183 }
1184
1185 oldz = player.mo->z; // [RH] Need to save corpse's z-height
1186 player.mo->z = z; // [RH] Checks are now full 3-D
1187
1188 // killough 4/2/98: fix bug where P_CheckPosition() uses a non-solid
1189 // corpse to detect collisions with other players in DM starts
1190 //
1191 // Old code:
1192 // if (!P_CheckPosition (players[playernum].mo, x, y))
1193 // return false;
1194
1195 player.mo->flags |= MF_SOLID;
1196 i = P_CheckPosition(player.mo, x, y);
1197 player.mo->flags &= ~MF_SOLID;
1198 player.mo->z = oldz; // [RH] Restore corpse's height
1199 if (!i)
1200 return false;
1201
1202 // spawn a teleport fog
1203 if (!player.spectator) // ONLY IF THEY ARE NOT A SPECTATOR
1204 {
1205 // emulate out-of-bounds access to finecosine / finesine tables
1206 // which cause west-facing player spawns to have the spawn-fog
1207 // and its sound located off the map in vanilla Doom.
1208
1209 // borrowed from Eternity Engine
1210
1211 // haleyjd: There was a weird bug with this statement:
1212 //
1213 // an = (ANG45 * (mthing->angle/45)) >> ANGLETOFINESHIFT;
1214 //
1215 // Even though this code stores the result into an unsigned variable, most
1216 // compilers seem to ignore that fact in the optimizer and use the resulting
1217 // value directly in a lea instruction. This causes the signed mapthing_t
1218 // angle value to generate an out-of-bounds access into the fine trig
1219 // lookups. In vanilla, this accesses the finetangent table and other parts
1220 // of the finesine table, and the result is what I call the "ninja spawn,"
1221 // which is missing the fog and sound, as it spawns somewhere out in the
1222 // far reaches of the void.
1223
1224 if (co_nosilentspawns)
1225 {
1226 an = ( ANG45 * ((unsigned int)mthing->angle/45) ) >> ANGLETOFINESHIFT;
1227 xa = finecosine[an];
1228 ya = finesine[an];
1229 }
1230 else
1231 {
1232 angle_t mtangle = (angle_t)(mthing->angle / 45);
1233
1234 an = ANG45 * mtangle;
1235
1236 switch(mtangle)
1237 {
1238 case 4: // 180 degrees (0x80000000 >> 19 == -4096)
1239 xa = finetangent[2048];
1240 ya = finetangent[0];
1241 break;
1242 case 5: // 225 degrees (0xA0000000 >> 19 == -3072)
1243 xa = finetangent[3072];
1244 ya = finetangent[1024];
1245 break;
1246 case 6: // 270 degrees (0xC0000000 >> 19 == -2048)
1247 xa = finesine[0];
1248 ya = finetangent[2048];
1249 break;
1250 case 7: // 315 degrees (0xE0000000 >> 19 == -1024)
1251 xa = finesine[1024];
1252 ya = finetangent[3072];
1253 break;
1254 default: // everything else works properly
1255 xa = finecosine[an >> ANGLETOFINESHIFT];
1256 ya = finesine[an >> ANGLETOFINESHIFT];
1257 break;
1258 }
1259 }
1260
1261 mo = new AActor (x+20*xa, y+20*ya, z, MT_TFOG);
1262
1263 if (level.time)
1264 S_Sound (mo, CHAN_VOICE, "misc/teleport", 1, ATTN_NORM); // don't start sound on first frame
1265 }
1266
1267 return true;
1268 }
1269
1270
1271 //
1272 // G_DeathMatchSpawnPlayer
1273 // Spawns a player at one of the random death match spots
1274 // called at level load and each death
1275 //
1276
1277 // [RH] Returns the distance of the closest player to the given mapthing2_t.
1278 // denis - todo - should this be used somewhere?
1279 /*static fixed_t PlayersRangeFromSpot (mapthing2_t *spot)
1280 {
1281 fixed_t closest = MAXINT;
1282 fixed_t distance;
1283
1284 for (size_t i = 0; i < players.size(); i++)
1285 {
1286 if (!players[i].ingame() || !players[i].mo || players[i].health <= 0)
1287 continue;
1288
1289 distance = P_AproxDistance (players[i].mo->x - spot->x * FRACUNIT,
1290 players[i].mo->y - spot->y * FRACUNIT);
1291
1292 if (distance < closest)
1293 closest = distance;
1294 }
1295
1296 return closest;
1297 }
1298
1299 // [RH] Select the deathmatch spawn spot farthest from everyone.
1300 static mapthing2_t *SelectFarthestDeathmatchSpot (int selections)
1301 {
1302 fixed_t bestdistance = 0;
1303 mapthing2_t *bestspot = NULL;
1304 int i;
1305
1306 for (i = 0; i < selections; i++)
1307 {
1308 fixed_t distance = PlayersRangeFromSpot (&deathmatchstarts[i]);
1309
1310 if (distance > bestdistance)
1311 {
1312 bestdistance = distance;
1313 bestspot = &deathmatchstarts[i];
1314 }
1315 }
1316
1317 return bestspot;
1318 }
1319
1320 */
1321
1322 // [RH] Select a deathmatch spawn spot at random (original mechanism)
SelectRandomDeathmatchSpot(player_t & player,int selections)1323 static mapthing2_t *SelectRandomDeathmatchSpot (player_t &player, int selections)
1324 {
1325 int i = 0, j;
1326
1327 for (j = 0; j < 20; j++)
1328 {
1329 i = P_Random () % selections;
1330 if (G_CheckSpot (player, &deathmatchstarts[i]) )
1331 {
1332 return &deathmatchstarts[i];
1333 }
1334 }
1335
1336 // [RH] return a spot anyway, since we allow telefragging when a player spawns
1337 return &deathmatchstarts[i];
1338 }
1339
G_DeathMatchSpawnPlayer(player_t & player)1340 void G_DeathMatchSpawnPlayer (player_t &player)
1341 {
1342 int selections;
1343 mapthing2_t *spot;
1344
1345 if(!serverside || sv_gametype == GM_COOP)
1346 return;
1347
1348 //if (!ctfmode)
1349 {
1350 selections = deathmatch_p - deathmatchstarts;
1351 // [RH] We can get by with just 1 deathmatch start
1352 if (selections < 1)
1353 I_Error ("No deathmatch starts");
1354
1355 // [Toke - dmflags] Old location of DF_SPAWN_FARTHEST
1356 spot = SelectRandomDeathmatchSpot (player, selections);
1357
1358 if (!spot && !playerstarts.empty())
1359 {
1360 // no good spot, so the player will probably get stuck
1361 spot = &playerstarts[player.id%playerstarts.size()];
1362 }
1363 else
1364 {
1365 if (player.id < 4)
1366 spot->type = player.id+1;
1367 else
1368 spot->type = player.id+4001-4; // [RH] > 4 players
1369 }
1370
1371
1372 }
1373 /*else
1374 {
1375 selections = 0;
1376
1377 if (player.userinfo.team == TEAM_BLUE) // [Toke - CTF - starts]
1378 {
1379 selections = blueteam_p - blueteamstarts;
1380
1381 if (selections < 1)
1382 I_Error ("No blue team starts");
1383 }
1384
1385 if (player.userinfo.team == TEAM_RED) // [Toke - CTF - starts]
1386 {
1387 selections = redteam_p - redteamstarts;
1388
1389 if (selections < 1)
1390 I_Error ("No red team starts");
1391 }
1392
1393 if (selections < 1)
1394 I_Error ("No team starts");
1395
1396 spot = CTF_SelectTeamPlaySpot (player, selections); // [Toke - Teams]
1397
1398 if (!spot && !playerstarts.empty())
1399 spot = &playerstarts[player.id%playerstarts.size()];
1400
1401 else
1402 {
1403 if (player.id < 4)
1404 spot->type = player.id+1;
1405 else
1406 spot->type = player.id+4001-4;
1407 }
1408 }*/
1409
1410 P_SpawnPlayer (player, spot);
1411 }
1412
1413 //
1414 // G_DoReborn
1415 //
G_DoReborn(player_t & player)1416 void G_DoReborn (player_t &player)
1417 {
1418 if(!serverside)
1419 return;
1420
1421 if (!multiplayer)
1422 {
1423 // reload the level from scratch
1424 gameaction = ga_loadlevel;
1425 return;
1426 }
1427
1428 // respawn at the start
1429 // first disassociate the corpse
1430 if (player.mo)
1431 player.mo->player = NULL;
1432
1433 // spawn at random spot if in death match
1434 if (sv_gametype != GM_COOP)
1435 {
1436 G_DeathMatchSpawnPlayer (player);
1437 return;
1438 }
1439
1440 if(playerstarts.empty())
1441 I_Error("No player starts");
1442
1443 unsigned int playernum = player.id - 1;
1444
1445 if (G_CheckSpot (player, &playerstarts[playernum%playerstarts.size()]) )
1446 {
1447 P_SpawnPlayer (player, &playerstarts[playernum%playerstarts.size()]);
1448 return;
1449 }
1450
1451 // try to spawn at one of the other players' spots
1452 for (size_t i = 0; i < playerstarts.size(); i++)
1453 {
1454 if (G_CheckSpot (player, &playerstarts[i]) )
1455 {
1456 P_SpawnPlayer (player, &playerstarts[i]);
1457 return;
1458 }
1459 }
1460
1461 // he's going to be inside something. Too bad.
1462 P_SpawnPlayer (player, &playerstarts[playernum%playerstarts.size()]);
1463 }
1464
1465
G_ScreenShot(char * filename)1466 void G_ScreenShot (char *filename)
1467 {
1468 // SoM: THIS CRASHES A LOT
1469 if(filename && *filename)
1470 shotfile = filename;
1471 else
1472 shotfile = "";
1473
1474 gameaction = ga_screenshot;
1475 }
1476
1477
1478
1479 //
1480 // G_InitFromSavegame
1481 // Can be called by the startup code or the menu task.
1482 //
1483 char savename[256];
1484
G_LoadGame(char * name)1485 void G_LoadGame (char* name)
1486 {
1487 strcpy (savename, name);
1488 gameaction = ga_loadgame;
1489 }
1490
1491
G_DoLoadGame(void)1492 void G_DoLoadGame (void)
1493 {
1494 unsigned int i;
1495 char text[16];
1496
1497 gameaction = ga_nothing;
1498
1499 FILE *stdfile = fopen (savename, "rb");
1500 if (stdfile == NULL)
1501 {
1502 Printf (PRINT_HIGH, "Could not read savegame '%s'\n", savename);
1503 return;
1504 }
1505
1506 fseek (stdfile, SAVESTRINGSIZE, SEEK_SET); // skip the description field
1507 fread (text, 16, 1, stdfile);
1508 if (strncmp (text, SAVESIG, 16))
1509 {
1510 Printf (PRINT_HIGH, "Savegame '%s' is from a different version\n", savename);
1511
1512 fclose(stdfile);
1513
1514 return;
1515 }
1516 fread (text, 8, 1, stdfile);
1517 text[8] = 0;
1518
1519 /*bglobal.RemoveAllBots (true);*/
1520
1521 FLZOFile savefile (stdfile, FFile::EReading);
1522
1523 if (!savefile.IsOpen ())
1524 I_Error ("Savegame '%s' is corrupt\n", savename);
1525
1526 Printf (PRINT_HIGH, "Loading savegame '%s'...\n", savename);
1527
1528 FArchive arc (savefile);
1529
1530 {
1531 byte vars[4096], *vars_p;
1532 unsigned int len;
1533 vars_p = vars;
1534 len = arc.ReadCount ();
1535 arc.Read (vars, len);
1536 cvar_t::C_ReadCVars (&vars_p);
1537 }
1538
1539 // dearchive all the modifications
1540 G_SerializeSnapshots (arc);
1541 P_SerializeRNGState (arc);
1542 P_SerializeACSDefereds (arc);
1543
1544 CL_QuitNetGame();
1545
1546 netgame = false;
1547 multiplayer = false;
1548
1549 // load a base level
1550 savegamerestore = true; // Use the player actors in the savegame
1551 serverside = true;
1552 G_InitNew (text);
1553 displayplayer_id = consoleplayer_id = 1;
1554 savegamerestore = false;
1555
1556 arc >> level.time;
1557
1558
1559 for (i = 0; i < NUM_WORLDVARS; i++)
1560 arc >> ACS_WorldVars[i];
1561
1562 for (i = 0; i < NUM_GLOBALVARS; i++)
1563 arc >> ACS_GlobalVars[i];
1564
1565 arc >> text[9];
1566
1567 arc.Close ();
1568
1569 if (text[9] != 0x1d)
1570 I_Error ("Bad savegame");
1571 }
1572
1573
1574 //
1575 // G_SaveGame
1576 // Called by the menu task.
1577 // Description is a 24 byte text string
1578 //
G_SaveGame(int slot,char * description)1579 void G_SaveGame (int slot, char *description)
1580 {
1581 savegameslot = slot;
1582 strcpy (savedescription, description);
1583 sendsave = true;
1584 }
1585
G_BuildSaveName(std::string & name,int slot)1586 void G_BuildSaveName (std::string &name, int slot)
1587 {
1588 std::stringstream ssName;
1589
1590 #ifdef _XBOX
1591 std::string path = xbox_GetSavePath(name, slot);
1592 #else
1593 std::string path = I_GetUserFileName ((const char *)name.c_str());
1594 #endif
1595
1596 ssName << path;
1597 ssName << "odasv";
1598 ssName << slot;
1599 ssName << ".ods";
1600
1601 name = ssName.str();
1602 }
1603
G_DoSaveGame(void)1604 void G_DoSaveGame (void)
1605 {
1606 std::string name;
1607 char *description;
1608 int i;
1609
1610 G_SnapshotLevel ();
1611
1612 G_BuildSaveName (name, savegameslot);
1613 description = savedescription;
1614
1615 FILE *stdfile = fopen (name.c_str(), "wb");
1616
1617 if (stdfile == NULL)
1618 {
1619 return;
1620 }
1621
1622 #ifdef _XBOX
1623 xbox_WriteSaveMeta(name.substr(0, name.rfind(PATHSEPCHAR)), description);
1624 #endif
1625
1626 Printf (PRINT_HIGH, "Saving game to '%s'...\n", name.c_str());
1627
1628 fwrite (description, SAVESTRINGSIZE, 1, stdfile);
1629 fwrite (SAVESIG, 16, 1, stdfile);
1630 fwrite (level.mapname, 8, 1, stdfile);
1631
1632 FLZOFile savefile (stdfile, FFile::EWriting, true);
1633 FArchive arc (savefile);
1634
1635 {
1636 byte vars[4096], *vars_p;
1637 vars_p = vars;
1638
1639 cvar_t::C_WriteCVars (&vars_p, CVAR_SERVERINFO);
1640 arc.WriteCount (vars_p - vars);
1641 arc.Write (vars, vars_p - vars);
1642 }
1643
1644 G_SerializeSnapshots (arc);
1645 P_SerializeRNGState (arc);
1646 P_SerializeACSDefereds (arc);
1647
1648 arc << level.time;
1649
1650 for (i = 0; i < NUM_WORLDVARS; i++)
1651 arc << ACS_WorldVars[i];
1652
1653 for (i = 0; i < NUM_GLOBALVARS; i++)
1654 arc << ACS_GlobalVars[i];
1655
1656
1657 arc << (BYTE)0x1d; // consistancy marker
1658
1659 gameaction = ga_nothing;
1660 savedescription[0] = 0;
1661
1662 Printf (PRINT_HIGH, "%s\n", GStrings(GGSAVED));
1663 arc.Close ();
1664
1665 if (level.info->snapshot != NULL)
1666 {
1667 delete level.info->snapshot;
1668 level.info->snapshot = NULL;
1669 }
1670 }
1671
1672
1673
1674
1675 //
1676 // DEMO RECORDING
1677 //
1678 #define DEMOMARKER 0x80
1679 #define DEMOSTOP 0x07
1680
G_ReadDemoTiccmd()1681 void G_ReadDemoTiccmd()
1682 {
1683 if (demoversion == LMP_DOOM_1_9 || demoversion == LMP_DOOM_1_9_1)
1684 {
1685 int demostep = (demoversion == LMP_DOOM_1_9_1) ? 5 : 4;
1686
1687 for (Players::iterator it = players.begin(); it != players.end(); ++it)
1688 {
1689 if ((demo_e - demo_p < demostep) || (*demo_p == DEMOMARKER))
1690 {
1691 // end of demo data stream
1692 G_CheckDemoStatus();
1693 return;
1694 }
1695
1696 it->cmd.forwardmove = ((signed char)*demo_p++) << 8;
1697 it->cmd.sidemove = ((signed char)*demo_p++) << 8;
1698
1699 if (demoversion == LMP_DOOM_1_9)
1700 {
1701 it->cmd.yaw = ((unsigned char)*demo_p++) << 8;
1702 }
1703 else
1704 {
1705 it->cmd.yaw = ((unsigned short)*demo_p++);
1706 it->cmd.yaw |= ((unsigned short)*demo_p++) << 8;
1707 }
1708 it->cmd.buttons = (unsigned char)*demo_p++;
1709 }
1710 }
1711 }
1712
1713 //
1714 // G_WriteDemoTiccmd
1715 //
G_WriteDemoTiccmd()1716 void G_WriteDemoTiccmd ()
1717 {
1718 byte demo_tmp[8];
1719
1720 int demostep = (demoversion == LMP_DOOM_1_9_1) ? 5 : 4;
1721
1722 for (Players::iterator it = players.begin(); it != players.end(); ++it)
1723 {
1724 byte* demo_p = demo_tmp;
1725
1726 *demo_p++ = it->cmd.forwardmove >> 8;
1727 *demo_p++ = it->cmd.sidemove >> 8;
1728
1729 // If this is a longtics demo, record in higher resolution
1730 if (LMP_DOOM_1_9_1 == demoversion)
1731 {
1732 *demo_p++ = (it->cmd.yaw & 0xff);
1733 *demo_p++ = (it->cmd.yaw >> 8) & 0xff;
1734 }
1735 else
1736 {
1737 *demo_p++ = it->cmd.yaw >> 8;
1738 it->cmd.yaw = ((unsigned char)*(demo_p - 1)) << 8;
1739 }
1740
1741 *demo_p++ = it->cmd.buttons;
1742
1743 fwrite(demo_tmp, demostep, 1, recorddemo_fp);
1744 }
1745 }
1746
1747 //
1748 // G_RecordDemo
1749 //
G_RecordDemo(const std::string & mapname,const std::string & basedemoname)1750 bool G_RecordDemo(const std::string& mapname, const std::string& basedemoname)
1751 {
1752 std::string demoname = basedemoname + ".lmp";
1753
1754 if (recorddemo_fp)
1755 {
1756 fclose(recorddemo_fp);
1757 recorddemo_fp = NULL;
1758 }
1759
1760 recorddemo_fp = fopen(demoname.c_str(), "w");
1761
1762 if (!recorddemo_fp)
1763 {
1764 Printf(PRINT_HIGH, "Could not open file %s for writing\n", demoname.c_str());
1765 return false;
1766 }
1767
1768 CL_QuitNetGame();
1769
1770 usergame = false;
1771 demorecording = true;
1772 demostartgametic = gametic;
1773
1774 players.clear();
1775 players.push_back(player_t());
1776 players.back().playerstate = PST_REBORN;
1777 players.back().id = 1;
1778
1779 player_t &con = idplayer(1);
1780 consoleplayer_id = displayplayer_id = con.id;
1781
1782 serverside = true;
1783
1784 bool monstersrespawn = sv_monstersrespawn.asInt();
1785 bool fastmonsters = sv_fastmonsters.asInt();
1786 bool nomonsters = sv_nomonsters.asInt();
1787
1788 // [SL] 2014-01-07 - Backup any cvars that need to be set to default to
1789 // ensure demo compatibility. CVAR_SERVERINFO cvars is a handy superset
1790 // of those cvars
1791 cvar_t::C_BackupCVars(CVAR_SERVERINFO);
1792 cvar_t::C_SetCVarsToDefaults(CVAR_SERVERINFO);
1793
1794 sv_monstersrespawn.Set(monstersrespawn);
1795 sv_fastmonsters.Set(fastmonsters);
1796 sv_nomonsters.Set(nomonsters);
1797
1798 G_InitNew(mapname.c_str());
1799
1800 byte demo_tmp[32];
1801 demo_p = demo_tmp;
1802
1803 // Save the right version code for this demo
1804 if (demoversion == LMP_DOOM_1_9_1)
1805 *demo_p++ = DOOM_1_9_1_DEMO;
1806 else
1807 *demo_p++ = DOOM_1_9_DEMO;
1808
1809 democlassic = true;
1810
1811 int episode, mapid;
1812 if (gameinfo.flags & GI_MAPxx)
1813 {
1814 episode = 1;
1815 mapid = level.levelnum;
1816 }
1817 else
1818 {
1819 // convert levelnum from form of 24 to episode=3, mapid=4
1820 episode = 1 + level.levelnum / 10;
1821 mapid = level.levelnum % 10;
1822 }
1823
1824 *demo_p++ = sv_skill.asInt() - 1;
1825 *demo_p++ = episode;
1826 *demo_p++ = mapid;
1827 *demo_p++ = 0; // coop gametype only (actually single-player only)
1828 *demo_p++ = sv_monstersrespawn.asInt();
1829 *demo_p++ = sv_fastmonsters.asInt();
1830 *demo_p++ = sv_nomonsters.asInt();
1831 *demo_p++ = 0; // player 1 POV
1832
1833 *demo_p++ = 1; // player 1 is present
1834 *demo_p++ = 0; // player 2 is not present
1835 *demo_p++ = 0; // player 3 is not present
1836 *demo_p++ = 0; // player 4 is not present
1837
1838 fwrite(demo_tmp, 13, 1, recorddemo_fp);
1839
1840 return true;
1841 }
1842
1843
1844 //
1845 // G_PlayDemo
1846 //
1847
1848 std::string defdemoname;
1849
G_DeferedPlayDemo(const char * name)1850 void G_DeferedPlayDemo (const char *name)
1851 {
1852 defdemoname = name;
1853 gameaction = ga_playdemo;
1854 }
1855
G_RecordCommand(int argc,char ** argv,demoversion_t ver)1856 static void G_RecordCommand(int argc, char** argv, demoversion_t ver)
1857 {
1858 if (argc > 2)
1859 {
1860 demoversion = ver;
1861
1862 if (gamestate != GS_STARTUP)
1863 {
1864 //G_CheckDemoStatus();
1865 G_RecordDemo(argv[1], argv[2]);
1866 }
1867 else
1868 {
1869 strncpy(startmap, argv[1], 8);
1870 demorecordfile = argv[2];
1871 autostart = true;
1872 autorecord = true;
1873 }
1874 }
1875 else
1876 {
1877 Printf(PRINT_HIGH, "Usage: record%s map file\n",
1878 ver == LMP_DOOM_1_9_1 ? "longtics" : "vanilla");
1879 }
1880 }
1881
BEGIN_COMMAND(recordvanilla)1882 BEGIN_COMMAND(recordvanilla)
1883 {
1884 G_RecordCommand(argc, argv, LMP_DOOM_1_9);
1885 }
1886 END_COMMAND(recordvanilla)
1887
BEGIN_COMMAND(recordlongtics)1888 BEGIN_COMMAND(recordlongtics)
1889 {
1890 G_RecordCommand(argc, argv, LMP_DOOM_1_9_1);
1891 }
1892 END_COMMAND(recordlongtics)
1893
BEGIN_COMMAND(stopdemo)1894 BEGIN_COMMAND(stopdemo)
1895 {
1896 G_CheckDemoStatus ();
1897 }
1898 END_COMMAND(stopdemo)
1899
BEGIN_COMMAND(playdemo)1900 BEGIN_COMMAND(playdemo)
1901 {
1902 if(argc > 1)
1903 {
1904 extern bool lastWadRebootSuccess;
1905 if(lastWadRebootSuccess)
1906 {
1907 G_DeferedPlayDemo(argv[1]);
1908 }
1909 else
1910 {
1911 Printf(PRINT_HIGH, "Cannot play demo because WAD didn't load\n");
1912 Printf(PRINT_HIGH, "Use the 'wad' command\n");
1913 }
1914 }
1915 else
1916 Printf(PRINT_HIGH, "Usage: playdemo lumpname or file\n");
1917 }
1918 END_COMMAND(playdemo)
1919
BEGIN_COMMAND(streamdemo)1920 BEGIN_COMMAND(streamdemo)
1921 {
1922 if(argc > 1)
1923 {
1924 G_DeferedPlayDemo(argv[1]);
1925 G_DoPlayDemo(true);
1926 }
1927 else
1928 {
1929 Printf(PRINT_HIGH, "Usage: streamdemo lumpname or file\n");
1930 }
1931 }
END_COMMAND(streamdemo)1932 END_COMMAND(streamdemo)
1933
1934
1935 //
1936 // G_DoPlayDemo
1937 //
1938 // Plays the vanilla LMP demo defdemoname (either in the loaded WAD files
1939 // or as an external file).
1940 //
1941 // If justStreamInput is true, the recorded game parameters will be ignored.
1942 // This is used to stream input to the client while connected to a server
1943 // for regression testing purposes.
1944 //
1945 void G_DoPlayDemo(bool justStreamInput)
1946 {
1947 if (!justStreamInput)
1948 CL_QuitNetGame();
1949
1950 gameaction = ga_nothing;
1951 int bytelen;
1952
1953 int demolump = W_CheckNumForName(defdemoname.c_str());
1954 if (demolump != -1)
1955 {
1956 demobuffer = demo_p = (byte*)W_CacheLumpNum(demolump, PU_STATIC);
1957 bytelen = W_LumpLength(demolump);
1958 }
1959 else
1960 {
1961 // [RH] Allow for demos not loaded as lumps
1962 FixPathSeparator(defdemoname);
1963 M_AppendExtension(defdemoname, ".lmp");
1964 bytelen = M_ReadFile(defdemoname, &demobuffer);
1965 demo_p = demobuffer;
1966 }
1967
1968 demo_e = demo_p + bytelen;
1969
1970 if (bytelen < 14)
1971 {
1972 if (bytelen)
1973 Z_Free(demobuffer);
1974
1975 Printf(PRINT_HIGH, "DOOM Demo file too short\n");
1976 gameaction = ga_fullconsole;
1977 return;
1978 }
1979
1980 if (demo_p[0] == DOOM_1_4_DEMO ||
1981 demo_p[0] == DOOM_1_5_DEMO ||
1982 demo_p[0] == DOOM_1_6_DEMO ||
1983 demo_p[0] == DOOM_1_7_DEMO ||
1984 demo_p[0] == DOOM_1_8_DEMO ||
1985 demo_p[0] == DOOM_1_9_DEMO ||
1986 demo_p[0] == DOOM_1_9p_DEMO ||
1987 demo_p[0] == DOOM_1_9_1_DEMO)
1988 {
1989 Printf(PRINT_HIGH, "Playing DOOM demo %s\n", defdemoname.c_str());
1990
1991 democlassic = true;
1992 demostartgametic = gametic;
1993 demoversion = *demo_p++ == DOOM_1_9_1_DEMO ? LMP_DOOM_1_9_1 : LMP_DOOM_1_9;
1994
1995 byte skill = *demo_p++ + 1;
1996
1997 byte episode = *demo_p++;
1998 byte map = *demo_p++;
1999 char mapname[32];
2000 if (gameinfo.flags & GI_MAPxx)
2001 sprintf(mapname, "MAP%02d", map);
2002 else
2003 sprintf(mapname, "E%dM%d", episode, map);
2004
2005 int deathmatch = *demo_p++;
2006 bool monstersrespawn = *demo_p++;
2007 bool fastmonsters = *demo_p++;
2008 bool nomonsters = *demo_p++;
2009 byte who = *demo_p++;
2010
2011 if (!justStreamInput)
2012 players.clear();
2013
2014 for (size_t i = 0 ; i < MAXPLAYERS_VANILLA; i++)
2015 {
2016 if (*demo_p++ && !justStreamInput)
2017 {
2018 players.push_back(player_t());
2019 player_t* player = &players.back();
2020 player->playerstate = PST_REBORN;
2021 player->id = i + 1;
2022 }
2023 }
2024
2025 if (!justStreamInput)
2026 {
2027 player_t &con = idplayer(who + 1);
2028
2029 if (!validplayer(con))
2030 {
2031 Z_Free(demobuffer);
2032 Printf(PRINT_HIGH, "DOOM Demo: invalid console player %d of %d\n", who + 1, players.size());
2033 gameaction = ga_fullconsole;
2034 return;
2035 }
2036
2037 consoleplayer_id = displayplayer_id = con.id;
2038
2039 if (players.size() > 1)
2040 {
2041 netgame = true;
2042 multiplayer = true;
2043 }
2044 else
2045 {
2046 netgame = false;
2047 multiplayer = false;
2048 }
2049
2050 serverside = true;
2051
2052 // [SL] 2012-12-26 - Backup any cvars that need to be set to default to
2053 // ensure demo compatibility. CVAR_SERVERINFO cvars is a handy superset
2054 // of those cvars
2055 cvar_t::C_BackupCVars(CVAR_SERVERINFO);
2056 cvar_t::C_SetCVarsToDefaults(CVAR_SERVERINFO);
2057
2058 sv_skill.Set(skill);
2059 sv_monstersrespawn.Set(monstersrespawn);
2060 sv_fastmonsters.Set(fastmonsters);
2061 sv_nomonsters.Set(nomonsters);
2062
2063 if (deathmatch == 2)
2064 {
2065 // Altdeath
2066 sv_gametype.Set(GM_DM);
2067 sv_weaponstay.Set(0.0f);
2068 sv_itemsrespawn.Set(1.0f);
2069 }
2070 else if (deathmatch == 1)
2071 {
2072 // Classic deathmatch
2073 sv_gametype.Set(GM_DM);
2074 sv_weaponstay.Set(1.0f);
2075 sv_itemsrespawn.Set(0.0f);
2076 }
2077 else
2078 {
2079 // Co-op
2080 sv_gametype.Set(GM_COOP);
2081 sv_weaponstay.Set(1.0f);
2082 sv_itemsrespawn.Set(0.0f);
2083 }
2084
2085 G_InitNew(mapname);
2086
2087 usergame = false;
2088 }
2089
2090 demoplayback = true;
2091
2092 // Set up the colors and names for the demo players
2093 for (Players::iterator it = players.begin(); it != players.end(); ++it)
2094 {
2095 R_BuildClassicPlayerTranslation(it->id, it->id - 1);
2096 it->userinfo.color = translationRGB[it->id][0];
2097
2098 char tmpname[16];
2099 sprintf(tmpname, "Player %i", it->id);
2100 it->userinfo.netname = tmpname;
2101 }
2102 }
2103 else
2104 {
2105 democlassic = false;
2106 Printf(PRINT_HIGH, "Unsupported demo format. If you are trying to play an Odamex " \
2107 "netdemo, please use the netplay command\n");
2108 gameaction = ga_nothing;
2109 }
2110 }
2111
2112 //
2113 // G_TimeDemo
2114 //
G_TimeDemo(const char * name)2115 void G_TimeDemo(const char* name)
2116 {
2117 nodrawers = Args.CheckParm ("-nodraw");
2118 noblit = Args.CheckParm ("-noblit");
2119 timingdemo = true;
2120
2121 defdemoname = name;
2122 gameaction = ga_playdemo;
2123 }
2124
2125 //
2126 // G_CleanupDemo
2127 //
G_CleanupDemo()2128 void G_CleanupDemo()
2129 {
2130 if (demoplayback)
2131 {
2132 Z_Free(demobuffer);
2133
2134 demoplayback = false;
2135 netgame = false;
2136 multiplayer = false;
2137 serverside = false;
2138
2139 cvar_t::C_RestoreCVars(); // [RH] Restore cvars demo might have changed
2140 }
2141
2142 if (demorecording)
2143 {
2144 if (recorddemo_fp)
2145 {
2146 fputc(DEMOSTOP, recorddemo_fp);
2147 fclose(recorddemo_fp);
2148 recorddemo_fp = NULL;
2149 }
2150
2151 cvar_t::C_RestoreCVars(); // [RH] Restore cvars demo might have changed
2152
2153 demorecording = false;
2154 Printf(PRINT_HIGH, "Demo %s recorded\n", demoname);
2155 }
2156 }
2157
2158 /*
2159 ===================
2160 =
2161 = G_CheckDemoStatus
2162 =
2163 = Called after a death or level completion to allow demos to be cleaned up
2164 = Returns true if a new demo loop action will take place
2165 ===================
2166 */
2167
G_CheckDemoStatus(void)2168 BOOL G_CheckDemoStatus (void)
2169 {
2170 if (demoplayback)
2171 {
2172 G_CleanupDemo();
2173
2174 extern bool demotest;
2175 if (demotest)
2176 {
2177 AActor *mo = idplayer(1).mo;
2178
2179 if (mo)
2180 Printf(PRINT_HIGH, "demotest:%x %x %x %x\n", mo->angle, mo->x, mo->y, mo->z);
2181 else
2182 Printf(PRINT_HIGH, "demotest:no player\n");
2183 }
2184
2185 if (singledemo || timingdemo)
2186 {
2187 if (timingdemo)
2188 {
2189 extern dtime_t starttime;
2190 dtime_t endtime = I_MSTime() - starttime;
2191 int realtics = endtime * TICRATE / 1000;
2192 float fps = float(gametic * TICRATE) / realtics;
2193
2194 Printf(PRINT_HIGH, "timed %i gametics in %i realtics (%.1f fps)\n",
2195 gametic, realtics, fps);
2196
2197 // exit the application
2198 CL_QuitCommand();
2199 return false;
2200 }
2201 else
2202 Printf (PRINT_HIGH, "Demo ended.\n");
2203
2204 gameaction = ga_fullconsole;
2205 timingdemo = false;
2206 return false;
2207 }
2208
2209 D_AdvanceDemo ();
2210 return true;
2211 }
2212
2213 if (demorecording)
2214 {
2215 G_CleanupDemo();
2216 }
2217
2218 return false;
2219 }
2220
2221
2222 VERSION_CONTROL (g_game_cpp, "$Id: g_game.cpp 4688 2014-03-25 16:55:35Z dr_sean $")
2223