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