1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  g_game.c
12 /// \brief game loop functions, events handling
13 
14 #include "doomdef.h"
15 #include "console.h"
16 #include "d_main.h"
17 #include "d_player.h"
18 #include "d_clisrv.h"
19 #include "f_finale.h"
20 #include "p_setup.h"
21 #include "p_saveg.h"
22 #include "i_system.h"
23 #include "am_map.h"
24 #include "m_random.h"
25 #include "p_local.h"
26 #include "r_draw.h"
27 #include "r_main.h"
28 #include "s_sound.h"
29 #include "g_game.h"
30 #include "g_demo.h"
31 #include "m_cheat.h"
32 #include "m_misc.h"
33 #include "m_menu.h"
34 #include "m_argv.h"
35 #include "hu_stuff.h"
36 #include "st_stuff.h"
37 #include "z_zone.h"
38 #include "i_video.h"
39 #include "byteptr.h"
40 #include "i_joy.h"
41 #include "r_local.h"
42 #include "r_skins.h"
43 #include "y_inter.h"
44 #include "v_video.h"
45 #include "lua_hook.h"
46 #include "b_bot.h"
47 #include "m_cond.h" // condition sets
48 
49 #include "lua_hud.h"
50 
51 gameaction_t gameaction;
52 gamestate_t gamestate = GS_NULL;
53 UINT8 ultimatemode = false;
54 
55 boolean botingame;
56 UINT8 botskin;
57 UINT16 botcolor;
58 
59 JoyType_t Joystick;
60 JoyType_t Joystick2;
61 
62 // 1024 bytes is plenty for a savegame
63 #define SAVEGAMESIZE (1024)
64 
65 char gamedatafilename[64] = "gamedata.dat";
66 char timeattackfolder[64] = "main";
67 char customversionstring[32] = "\0";
68 
69 static void G_DoCompleted(void);
70 static void G_DoStartContinue(void);
71 static void G_DoContinued(void);
72 static void G_DoWorldDone(void);
73 
74 char   mapmusname[7]; // Music name
75 UINT16 mapmusflags; // Track and reset bit
76 UINT32 mapmusposition; // Position to jump to
77 
78 INT16 gamemap = 1;
79 UINT32 maptol;
80 UINT8 globalweather = 0;
81 INT32 curWeather = PRECIP_NONE;
82 INT32 cursaveslot = 0; // Auto-save 1p savegame slot
83 //INT16 lastmapsaved = 0; // Last map we auto-saved at
84 INT16 lastmaploaded = 0; // Last map the game loaded
85 UINT8 gamecomplete = 0;
86 
87 marathonmode_t marathonmode = 0;
88 tic_t marathontime = 0;
89 
90 UINT8 numgameovers = 0; // for startinglives balance
91 SINT8 startinglivesbalance[maxgameovers+1] = {3, 5, 7, 9, 12, 15, 20, 25, 30, 40, 50, 75, 99, 0x7F};
92 
93 UINT16 mainwads = 0;
94 boolean modifiedgame; // Set if homebrew PWAD stuff has been added.
95 boolean savemoddata = false;
96 UINT8 paused;
97 UINT8 modeattacking = ATTACKING_NONE;
98 boolean disableSpeedAdjust = false;
99 boolean imcontinuing = false;
100 boolean runemeraldmanager = false;
101 UINT16 emeraldspawndelay = 60*TICRATE;
102 
103 // menu demo things
104 UINT8  numDemos      = 0;
105 UINT32 demoDelayTime = 15*TICRATE;
106 UINT32 demoIdleTime  = 3*TICRATE;
107 
108 boolean netgame; // only true if packets are broadcast
109 boolean multiplayer;
110 boolean playeringame[MAXPLAYERS];
111 boolean addedtogame;
112 player_t players[MAXPLAYERS];
113 
114 INT32 consoleplayer; // player taking events and displaying
115 INT32 displayplayer; // view being displayed
116 INT32 secondarydisplayplayer; // for splitscreen
117 
118 tic_t gametic;
119 tic_t levelstarttic; // gametic at level start
120 UINT32 ssspheres; // old special stage
121 INT16 lastmap; // last level you were at (returning from special stages)
122 tic_t timeinmap; // Ticker for time spent in level (used for levelcard display)
123 
124 INT16 spstage_start, spmarathon_start;
125 INT16 sstage_start, sstage_end, smpstage_start, smpstage_end;
126 
127 INT16 titlemap = 0;
128 boolean hidetitlepics = false;
129 INT16 bootmap; //bootmap for loading a map on startup
130 
131 INT16 tutorialmap = 0; // map to load for tutorial
132 boolean tutorialmode = false; // are we in a tutorial right now?
133 INT32 tutorialgcs = gcs_custom; // which control scheme is loaded?
134 INT32 tutorialusemouse = 0; // store cv_usemouse user value
135 INT32 tutorialfreelook = 0; // store cv_alwaysfreelook user value
136 INT32 tutorialmousemove = 0; // store cv_mousemove user value
137 INT32 tutorialanalog = 0; // store cv_analog[0] user value
138 
139 boolean looptitle = false;
140 
141 UINT16 skincolor_redteam = SKINCOLOR_RED;
142 UINT16 skincolor_blueteam = SKINCOLOR_BLUE;
143 UINT16 skincolor_redring = SKINCOLOR_SALMON;
144 UINT16 skincolor_bluering = SKINCOLOR_CORNFLOWER;
145 
146 tic_t countdowntimer = 0;
147 boolean countdowntimeup = false;
148 boolean exitfadestarted = false;
149 
150 cutscene_t *cutscenes[128];
151 textprompt_t *textprompts[MAX_PROMPTS];
152 
153 INT16 nextmapoverride;
154 UINT8 skipstats;
155 
156 // Pointers to each CTF flag
157 mobj_t *redflag;
158 mobj_t *blueflag;
159 // Pointers to CTF spawn location
160 mapthing_t *rflagpoint;
161 mapthing_t *bflagpoint;
162 
163 struct quake quake;
164 
165 // Map Header Information
166 mapheader_t* mapheaderinfo[NUMMAPS] = {NULL};
167 
168 static boolean exitgame = false;
169 static boolean retrying = false;
170 static boolean retryingmodeattack = false;
171 
172 UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage.
173 
174 UINT16 emeralds;
175 INT32 luabanks[NUM_LUABANKS];
176 UINT32 token; // Number of tokens collected in a level
177 UINT32 tokenlist; // List of tokens collected
178 boolean gottoken; // Did you get a token? Used for end of act
179 INT32 tokenbits; // Used for setting token bits
180 
181 // Old Special Stage
182 INT32 sstimer; // Time allotted in the special stage
183 
184 tic_t totalplaytime;
185 boolean gamedataloaded = false;
186 
187 // Time attack data for levels
188 // These are dynamically allocated for space reasons now
189 recorddata_t *mainrecords[NUMMAPS]   = {NULL};
190 nightsdata_t *nightsrecords[NUMMAPS] = {NULL};
191 UINT8 mapvisited[NUMMAPS];
192 
193 // Temporary holding place for nights data for the current map
194 nightsdata_t ntemprecords;
195 
196 UINT32 bluescore, redscore; // CTF and Team Match team scores
197 
198 // ring count... for PERFECT!
199 INT32 nummaprings = 0;
200 
201 // Elminates unnecessary searching.
202 boolean CheckForBustableBlocks;
203 boolean CheckForBouncySector;
204 boolean CheckForQuicksand;
205 boolean CheckForMarioBlocks;
206 boolean CheckForFloatBob;
207 boolean CheckForReverseGravity;
208 
209 // Powerup durations
210 UINT16 invulntics = 20*TICRATE;
211 UINT16 sneakertics = 20*TICRATE;
212 UINT16 flashingtics = 3*TICRATE;
213 UINT16 tailsflytics = 8*TICRATE;
214 UINT16 underwatertics = 30*TICRATE;
215 UINT16 spacetimetics = 11*TICRATE + (TICRATE/2);
216 UINT16 extralifetics = 4*TICRATE;
217 UINT16 nightslinktics = 2*TICRATE;
218 
219 INT32 gameovertics = 11*TICRATE;
220 
221 UINT8 ammoremovaltics = 2*TICRATE;
222 
223 UINT8 use1upSound = 0;
224 UINT8 maxXtraLife = 2; // Max extra lives from rings
225 UINT8 useContinues = 0; // Set to 1 to enable continues outside of no-save scenarioes
226 
227 UINT8 introtoplay;
228 UINT8 creditscutscene;
229 UINT8 useBlackRock = 1;
230 
231 // Emerald locations
232 mobj_t *hunt1;
233 mobj_t *hunt2;
234 mobj_t *hunt3;
235 
236 UINT32 countdown, countdown2; // for racing
237 
238 fixed_t gravity;
239 
240 INT16 autobalance; //for CTF team balance
241 INT16 teamscramble; //for CTF team scramble
242 INT16 scrambleplayers[MAXPLAYERS]; //for CTF team scramble
243 INT16 scrambleteams[MAXPLAYERS]; //for CTF team scramble
244 INT16 scrambletotal; //for CTF team scramble
245 INT16 scramblecount; //for CTF team scramble
246 
247 INT32 cheats; //for multiplayer cheat commands
248 
249 tic_t hidetime;
250 
251 // Grading
252 UINT32 timesBeaten;
253 UINT32 timesBeatenWithEmeralds;
254 UINT32 timesBeatenUltimate;
255 
256 typedef struct joystickvector2_s
257 {
258 	INT32 xaxis;
259 	INT32 yaxis;
260 } joystickvector2_t;
261 
262 boolean precache = true; // if true, load all graphics at start
263 
264 INT16 prevmap, nextmap;
265 
266 static UINT8 *savebuffer;
267 
268 // Analog Control
269 static void UserAnalog_OnChange(void);
270 static void UserAnalog2_OnChange(void);
271 static void Analog_OnChange(void);
272 static void Analog2_OnChange(void);
273 static void DirectionChar_OnChange(void);
274 static void DirectionChar2_OnChange(void);
275 static void AutoBrake_OnChange(void);
276 static void AutoBrake2_OnChange(void);
277 void SendWeaponPref(void);
278 void SendWeaponPref2(void);
279 
280 static CV_PossibleValue_t crosshair_cons_t[] = {{0, "Off"}, {1, "Cross"}, {2, "Angle"}, {3, "Point"}, {0, NULL}};
281 static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"},
282 {1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"},
283 #if JOYAXISSET > 1
284 {3, "Z-Axis"}, {4, "X-Rudder"}, {-3, "Z-Axis-"}, {-4, "X-Rudder-"},
285 #endif
286 #if JOYAXISSET > 2
287 {5, "Y-Rudder"}, {6, "Z-Rudder"}, {-5, "Y-Rudder-"}, {-6, "Z-Rudder-"},
288 #endif
289 #if JOYAXISSET > 3
290 {7, "U-Axis"}, {8, "V-Axis"}, {-7, "U-Axis-"}, {-8, "V-Axis-"},
291 #endif
292  {0, NULL}};
293 #if JOYAXISSET > 4
294 "More Axis Sets"
295 #endif
296 
297 // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler.
298 
299 // it automatically becomes compact with 20+ players, but if you like it, I guess you can turn that on!
300 consvar_t cv_compactscoreboard= CVAR_INIT ("compactscoreboard", "Off", CV_SAVE, CV_OnOff, NULL);
301 
302 // chat timer thingy
303 static CV_PossibleValue_t chattime_cons_t[] = {{5, "MIN"}, {999, "MAX"}, {0, NULL}};
304 consvar_t cv_chattime = CVAR_INIT ("chattime", "8", CV_SAVE, chattime_cons_t, NULL);
305 
306 // chatwidth
307 static CV_PossibleValue_t chatwidth_cons_t[] = {{64, "MIN"}, {300, "MAX"}, {0, NULL}};
308 consvar_t cv_chatwidth = CVAR_INIT ("chatwidth", "150", CV_SAVE, chatwidth_cons_t, NULL);
309 
310 // chatheight
311 static CV_PossibleValue_t chatheight_cons_t[] = {{6, "MIN"}, {22, "MAX"}, {0, NULL}};
312 consvar_t cv_chatheight= CVAR_INIT ("chatheight", "8", CV_SAVE, chatheight_cons_t, NULL);
313 
314 // chat notifications (do you want to hear beeps? I'd understand if you didn't.)
315 consvar_t cv_chatnotifications= CVAR_INIT ("chatnotifications", "On", CV_SAVE, CV_OnOff, NULL);
316 
317 // chat spam protection (why would you want to disable that???)
318 consvar_t cv_chatspamprotection= CVAR_INIT ("chatspamprotection", "On", CV_SAVE, CV_OnOff, NULL);
319 
320 // minichat text background
321 consvar_t cv_chatbacktint = CVAR_INIT ("chatbacktint", "On", CV_SAVE, CV_OnOff, NULL);
322 
323 // old shit console chat. (mostly exists for stuff like terminal, not because I cared if anyone liked the old chat.)
324 static CV_PossibleValue_t consolechat_cons_t[] = {{0, "Window"}, {1, "Console"}, {2, "Window (Hidden)"}, {0, NULL}};
325 consvar_t cv_consolechat = CVAR_INIT ("chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL);
326 
327 // Pause game upon window losing focus
328 consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL);
329 
330 consvar_t cv_crosshair = CVAR_INIT ("crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL);
331 consvar_t cv_crosshair2 = CVAR_INIT ("crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL);
332 consvar_t cv_invertmouse = CVAR_INIT ("invertmouse", "Off", CV_SAVE, CV_OnOff, NULL);
333 consvar_t cv_alwaysfreelook = CVAR_INIT ("alwaysmlook", "On", CV_SAVE, CV_OnOff, NULL);
334 consvar_t cv_invertmouse2 = CVAR_INIT ("invertmouse2", "Off", CV_SAVE, CV_OnOff, NULL);
335 consvar_t cv_alwaysfreelook2 = CVAR_INIT ("alwaysmlook2", "On", CV_SAVE, CV_OnOff, NULL);
336 consvar_t cv_chasefreelook = CVAR_INIT ("chasemlook", "Off", CV_SAVE, CV_OnOff, NULL);
337 consvar_t cv_chasefreelook2 = CVAR_INIT ("chasemlook2", "Off", CV_SAVE, CV_OnOff, NULL);
338 consvar_t cv_mousemove = CVAR_INIT ("mousemove", "Off", CV_SAVE, CV_OnOff, NULL);
339 consvar_t cv_mousemove2 = CVAR_INIT ("mousemove2", "Off", CV_SAVE, CV_OnOff, NULL);
340 
341 // previously "analog", "analog2", "useranalog", and "useranalog2", invalidating 2.1-era copies of config.cfg
342 // changed because it'd be nice to see people try out our actually good controls with gamepads now autobrake exists
343 consvar_t cv_analog[2] = {
344 	CVAR_INIT ("sessionanalog", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, Analog_OnChange),
345 	CVAR_INIT ("sessionanalog2", "Off", CV_CALL|CV_NOSHOWHELP, CV_OnOff, Analog2_OnChange),
346 };
347 consvar_t cv_useranalog[2] = {
348 	CVAR_INIT ("configanalog", "Off", CV_SAVE|CV_CALL|CV_NOSHOWHELP, CV_OnOff, UserAnalog_OnChange),
349 	CVAR_INIT ("configanalog2", "Off", CV_SAVE|CV_CALL|CV_NOSHOWHELP, CV_OnOff, UserAnalog2_OnChange),
350 };
351 
352 // deez New User eXperiences
353 static CV_PossibleValue_t directionchar_cons_t[] = {{0, "Camera"}, {1, "Movement"}, {2, "Simple Locked"}, {0, NULL}};
354 consvar_t cv_directionchar[2] = {
355 	CVAR_INIT ("directionchar", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar_OnChange),
356 	CVAR_INIT ("directionchar2", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar2_OnChange),
357 };
358 consvar_t cv_autobrake = CVAR_INIT ("autobrake", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake_OnChange);
359 consvar_t cv_autobrake2 = CVAR_INIT ("autobrake2", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake2_OnChange);
360 
361 // hi here's some new controls
362 static CV_PossibleValue_t zerotoone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}};
363 consvar_t cv_cam_shiftfacing[2] = {
364 	CVAR_INIT ("cam_shiftfacingchar", "0.33", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
365 	CVAR_INIT ("cam2_shiftfacingchar", "0.33", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
366 };
367 consvar_t cv_cam_turnfacing[2] = {
368 	CVAR_INIT ("cam_turnfacingchar", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
369 	CVAR_INIT ("cam2_turnfacingchar", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
370 };
371 consvar_t cv_cam_turnfacingability[2] = {
372 	CVAR_INIT ("cam_turnfacingability", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
373 	CVAR_INIT ("cam2_turnfacingability", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
374 };
375 consvar_t cv_cam_turnfacingspindash[2] = {
376 	CVAR_INIT ("cam_turnfacingspindash", "0.5", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
377 	CVAR_INIT ("cam2_turnfacingspindash", "0.5", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
378 };
379 consvar_t cv_cam_turnfacinginput[2] = {
380 	CVAR_INIT ("cam_turnfacinginput", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
381 	CVAR_INIT ("cam2_turnfacinginput", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL),
382 };
383 
384 static CV_PossibleValue_t centertoggle_cons_t[] = {{0, "Hold"}, {1, "Toggle"}, {2, "Sticky Hold"}, {0, NULL}};
385 consvar_t cv_cam_centertoggle[2] = {
386 	CVAR_INIT ("cam_centertoggle", "Hold", CV_SAVE, centertoggle_cons_t, NULL),
387 	CVAR_INIT ("cam2_centertoggle", "Hold", CV_SAVE, centertoggle_cons_t, NULL),
388 };
389 
390 static CV_PossibleValue_t lockedinput_cons_t[] = {{0, "Strafe"}, {1, "Turn"}, {0, NULL}};
391 consvar_t cv_cam_lockedinput[2] = {
392 	CVAR_INIT ("cam_lockedinput", "Strafe", CV_SAVE, lockedinput_cons_t, NULL),
393 	CVAR_INIT ("cam2_lockedinput", "Strafe", CV_SAVE, lockedinput_cons_t, NULL),
394 };
395 
396 static CV_PossibleValue_t lockedassist_cons_t[] = {
397 	{0, "Off"},
398 	{LOCK_BOSS, "Bosses"},
399 	{LOCK_BOSS|LOCK_ENEMY, "Enemies"},
400 	{LOCK_BOSS|LOCK_INTERESTS, "Interests"},
401 	{LOCK_BOSS|LOCK_ENEMY|LOCK_INTERESTS, "Full"},
402 	{0, NULL}
403 };
404 consvar_t cv_cam_lockonboss[2] = {
405 	CVAR_INIT ("cam_lockaimassist", "Bosses", CV_SAVE, lockedassist_cons_t, NULL),
406 	CVAR_INIT ("cam2_lockaimassist", "Bosses", CV_SAVE, lockedassist_cons_t, NULL),
407 };
408 
409 typedef enum
410 {
411 	AXISNONE = 0,
412 	AXISTURN,
413 	AXISMOVE,
414 	AXISLOOK,
415 	AXISSTRAFE,
416 
417 	AXISDIGITAL, // axes below this use digital deadzone
418 
419 	AXISJUMP,
420 	AXISSPIN,
421 	AXISFIRE,
422 	AXISFIRENORMAL,
423 } axis_input_e;
424 
425 consvar_t cv_turnaxis = CVAR_INIT ("joyaxis_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL);
426 consvar_t cv_moveaxis = CVAR_INIT ("joyaxis_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL);
427 consvar_t cv_sideaxis = CVAR_INIT ("joyaxis_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL);
428 consvar_t cv_lookaxis = CVAR_INIT ("joyaxis_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL);
429 consvar_t cv_jumpaxis = CVAR_INIT ("joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL);
430 consvar_t cv_spinaxis = CVAR_INIT ("joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL);
431 consvar_t cv_fireaxis = CVAR_INIT ("joyaxis_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL);
432 consvar_t cv_firenaxis = CVAR_INIT ("joyaxis_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL);
433 consvar_t cv_deadzone = CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL);
434 consvar_t cv_digitaldeadzone = CVAR_INIT ("joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL);
435 
436 consvar_t cv_turnaxis2 = CVAR_INIT ("joyaxis2_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL);
437 consvar_t cv_moveaxis2 = CVAR_INIT ("joyaxis2_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL);
438 consvar_t cv_sideaxis2 = CVAR_INIT ("joyaxis2_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL);
439 consvar_t cv_lookaxis2 = CVAR_INIT ("joyaxis2_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL);
440 consvar_t cv_jumpaxis2 = CVAR_INIT ("joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL);
441 consvar_t cv_spinaxis2 = CVAR_INIT ("joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL);
442 consvar_t cv_fireaxis2 = CVAR_INIT ("joyaxis2_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL);
443 consvar_t cv_firenaxis2 = CVAR_INIT ("joyaxis2_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL);
444 consvar_t cv_deadzone2 = CVAR_INIT ("joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL);
445 consvar_t cv_digitaldeadzone2 = CVAR_INIT ("joy_digdeadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL);
446 
447 player_t *seenplayer; // player we're aiming at right now
448 
449 // now automatically allocated in D_RegisterClientCommands
450 // so that it doesn't have to be updated depending on the value of MAXPLAYERS
451 char player_names[MAXPLAYERS][MAXPLAYERNAME+1];
452 
453 INT32 player_name_changes[MAXPLAYERS];
454 
455 INT16 rw_maximums[NUM_WEAPONS] =
456 {
457 	800, // MAX_INFINITY
458 	400, // MAX_AUTOMATIC
459 	100, // MAX_BOUNCE
460 	50,  // MAX_SCATTER
461 	100, // MAX_GRENADE
462 	50,  // MAX_EXPLOSION
463 	50   // MAX_RAIL
464 };
465 
466 // Allocation for time and nights data
G_AllocMainRecordData(INT16 i)467 void G_AllocMainRecordData(INT16 i)
468 {
469 	if (!mainrecords[i])
470 		mainrecords[i] = Z_Malloc(sizeof(recorddata_t), PU_STATIC, NULL);
471 	memset(mainrecords[i], 0, sizeof(recorddata_t));
472 }
473 
G_AllocNightsRecordData(INT16 i)474 void G_AllocNightsRecordData(INT16 i)
475 {
476 	if (!nightsrecords[i])
477 		nightsrecords[i] = Z_Malloc(sizeof(nightsdata_t), PU_STATIC, NULL);
478 	memset(nightsrecords[i], 0, sizeof(nightsdata_t));
479 }
480 
481 // MAKE SURE YOU SAVE DATA BEFORE CALLING THIS
G_ClearRecords(void)482 void G_ClearRecords(void)
483 {
484 	INT16 i;
485 	for (i = 0; i < NUMMAPS; ++i)
486 	{
487 		if (mainrecords[i])
488 		{
489 			Z_Free(mainrecords[i]);
490 			mainrecords[i] = NULL;
491 		}
492 		if (nightsrecords[i])
493 		{
494 			Z_Free(nightsrecords[i]);
495 			nightsrecords[i] = NULL;
496 		}
497 	}
498 }
499 
500 // For easy retrieval of records
G_GetBestScore(INT16 map)501 UINT32 G_GetBestScore(INT16 map)
502 {
503 	if (!mainrecords[map-1])
504 		return 0;
505 
506 	return mainrecords[map-1]->score;
507 }
508 
G_GetBestTime(INT16 map)509 tic_t G_GetBestTime(INT16 map)
510 {
511 	if (!mainrecords[map-1] || mainrecords[map-1]->time <= 0)
512 		return (tic_t)UINT32_MAX;
513 
514 	return mainrecords[map-1]->time;
515 }
516 
G_GetBestRings(INT16 map)517 UINT16 G_GetBestRings(INT16 map)
518 {
519 	if (!mainrecords[map-1])
520 		return 0;
521 
522 	return mainrecords[map-1]->rings;
523 }
524 
G_GetBestNightsScore(INT16 map,UINT8 mare)525 UINT32 G_GetBestNightsScore(INT16 map, UINT8 mare)
526 {
527 	if (!nightsrecords[map-1])
528 		return 0;
529 
530 	return nightsrecords[map-1]->score[mare];
531 }
532 
G_GetBestNightsTime(INT16 map,UINT8 mare)533 tic_t G_GetBestNightsTime(INT16 map, UINT8 mare)
534 {
535 	if (!nightsrecords[map-1] || nightsrecords[map-1]->time[mare] <= 0)
536 		return (tic_t)UINT32_MAX;
537 
538 	return nightsrecords[map-1]->time[mare];
539 }
540 
G_GetBestNightsGrade(INT16 map,UINT8 mare)541 UINT8 G_GetBestNightsGrade(INT16 map, UINT8 mare)
542 {
543 	if (!nightsrecords[map-1])
544 		return 0;
545 
546 	return nightsrecords[map-1]->grade[mare];
547 }
548 
549 // For easy adding of NiGHTS records
G_AddTempNightsRecords(UINT32 pscore,tic_t ptime,UINT8 mare)550 void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare)
551 {
552 	ntemprecords.score[mare] = pscore;
553 	ntemprecords.grade[mare] = P_GetGrade(pscore, gamemap, mare - 1);
554 	ntemprecords.time[mare] = ptime;
555 
556 	// Update nummares
557 	// Note that mare "0" is overall, mare "1" is the first real mare
558 	if (ntemprecords.nummares < mare)
559 		ntemprecords.nummares = mare;
560 }
561 
562 //
563 // G_UpdateRecordReplays
564 //
565 // Update replay files/data, etc. for Record Attack
566 // See G_SetNightsRecords for NiGHTS Attack.
567 //
G_UpdateRecordReplays(void)568 static void G_UpdateRecordReplays(void)
569 {
570 	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
571 	char *gpath;
572 	char lastdemo[256], bestdemo[256];
573 	UINT8 earnedEmblems;
574 
575 	// Record new best time
576 	if (!mainrecords[gamemap-1])
577 		G_AllocMainRecordData(gamemap-1);
578 
579 	if (players[consoleplayer].score > mainrecords[gamemap-1]->score)
580 		mainrecords[gamemap-1]->score = players[consoleplayer].score;
581 
582 	if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
583 		mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
584 
585 	if ((UINT16)(players[consoleplayer].rings) > mainrecords[gamemap-1]->rings)
586 		mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
587 
588 	// Save demo!
589 	bestdemo[255] = '\0';
590 	lastdemo[255] = '\0';
591 	G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].rings));
592 	G_CheckDemoStatus();
593 
594 	I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
595 	I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
596 
597 	if ((gpath = malloc(glen)) == NULL)
598 		I_Error("Out of memory for replay filepath\n");
599 
600 	sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
601 	snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
602 
603 	if (FIL_FileExists(lastdemo))
604 	{
605 		UINT8 *buf;
606 		size_t len = FIL_ReadFile(lastdemo, &buf);
607 
608 		snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
609 		if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
610 		{ // Better time, save this demo.
611 			if (FIL_FileExists(bestdemo))
612 				remove(bestdemo);
613 			FIL_WriteFile(bestdemo, buf, len);
614 			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
615 		}
616 
617 		snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
618 		if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
619 		{ // Better score, save this demo.
620 			if (FIL_FileExists(bestdemo))
621 				remove(bestdemo);
622 			FIL_WriteFile(bestdemo, buf, len);
623 			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
624 		}
625 
626 		snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
627 		if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<2)))
628 		{ // Better rings, save this demo.
629 			if (FIL_FileExists(bestdemo))
630 				remove(bestdemo);
631 			FIL_WriteFile(bestdemo, buf, len);
632 			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW MOST RINGS!"), M_GetText("Saved replay as"), bestdemo);
633 		}
634 
635 		//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
636 
637 		Z_Free(buf);
638 	}
639 	free(gpath);
640 
641 	// Check emblems when level data is updated
642 	if ((earnedEmblems = M_CheckLevelEmblems()))
643 		CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
644 
645 	// Update timeattack menu's replay availability.
646 	Nextmap_OnChange();
647 }
648 
G_SetNightsRecords(void)649 void G_SetNightsRecords(void)
650 {
651 	INT32 i;
652 	UINT32 totalscore = 0;
653 	tic_t totaltime = 0;
654 	UINT8 earnedEmblems;
655 
656 	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
657 	char *gpath;
658 	char lastdemo[256], bestdemo[256];
659 
660 	if (!ntemprecords.nummares)
661 		return;
662 
663 	// Set overall
664 	{
665 		UINT8 totalrank = 0, realrank = 0;
666 
667 		for (i = 1; i <= ntemprecords.nummares; ++i)
668 		{
669 			totalscore += ntemprecords.score[i];
670 			totalrank += ntemprecords.grade[i];
671 			totaltime += ntemprecords.time[i];
672 		}
673 
674 		// Determine overall grade
675 		realrank = (UINT8)((FixedDiv((fixed_t)totalrank << FRACBITS, ntemprecords.nummares << FRACBITS) + (FRACUNIT/2)) >> FRACBITS);
676 
677 		// You need ALL rainbow As to get a rainbow A overall
678 		if (realrank == GRADE_S && (totalrank / ntemprecords.nummares) != GRADE_S)
679 			realrank = GRADE_A;
680 
681 		ntemprecords.score[0] = totalscore;
682 		ntemprecords.grade[0] = realrank;
683 		ntemprecords.time[0] = totaltime;
684 	}
685 
686 	// Now take all temp records and put them in the actual records
687 	{
688 		nightsdata_t *maprecords;
689 
690 		if (!nightsrecords[gamemap-1])
691 			G_AllocNightsRecordData(gamemap-1);
692 		maprecords = nightsrecords[gamemap-1];
693 
694 		if (maprecords->nummares != ntemprecords.nummares)
695 			maprecords->nummares = ntemprecords.nummares;
696 
697 		for (i = 0; i < ntemprecords.nummares + 1; ++i)
698 		{
699 			if (maprecords->score[i] < ntemprecords.score[i])
700 				maprecords->score[i] = ntemprecords.score[i];
701 			if (maprecords->grade[i] < ntemprecords.grade[i])
702 				maprecords->grade[i] = ntemprecords.grade[i];
703 			if (!maprecords->time[i] || maprecords->time[i] > ntemprecords.time[i])
704 				maprecords->time[i] = ntemprecords.time[i];
705 		}
706 	}
707 
708 	memset(&ntemprecords, 0, sizeof(nightsdata_t));
709 
710 	// Save demo!
711 	bestdemo[255] = '\0';
712 	lastdemo[255] = '\0';
713 	G_SetDemoTime(totaltime, totalscore, 0);
714 	G_CheckDemoStatus();
715 
716 	I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
717 	I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
718 
719 	if ((gpath = malloc(glen)) == NULL)
720 		I_Error("Out of memory for replay filepath\n");
721 
722 	sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
723 	snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
724 
725 	if (FIL_FileExists(lastdemo))
726 	{
727 		UINT8 *buf;
728 		size_t len = FIL_ReadFile(lastdemo, &buf);
729 
730 		snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);;
731 		if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
732 		{ // Better time, save this demo.
733 			if (FIL_FileExists(bestdemo))
734 				remove(bestdemo);
735 			FIL_WriteFile(bestdemo, buf, len);
736 			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
737 		}
738 
739 		snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
740 		if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
741 		{ // Better score, save this demo.
742 			if (FIL_FileExists(bestdemo))
743 				remove(bestdemo);
744 			FIL_WriteFile(bestdemo, buf, len);
745 			CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
746 		}
747 
748 		//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
749 
750 		Z_Free(buf);
751 	}
752 	free(gpath);
753 
754 	if ((earnedEmblems = M_CheckLevelEmblems()))
755 		CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
756 
757 	// If the mare count changed, this will update the score display
758 	Nextmap_OnChange();
759 }
760 
761 // for consistency among messages: this modifies the game and removes savemoddata.
G_SetGameModified(boolean silent)762 void G_SetGameModified(boolean silent)
763 {
764 	if (modifiedgame && !savemoddata)
765 		return;
766 
767 	modifiedgame = true;
768 	savemoddata = false;
769 
770 	if (!silent)
771 		CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to record statistics.\n"));
772 
773 	// If in record attack recording, cancel it.
774 	if (modeattacking)
775 		M_EndModeAttackRun();
776 	else if (marathonmode)
777 		Command_ExitGame_f();
778 }
779 
780 /** Builds an original game map name from a map number.
781   * The complexity is due to MAPA0-MAPZZ.
782   *
783   * \param map Map number.
784   * \return Pointer to a static buffer containing the desired map name.
785   * \sa M_MapNumber
786   */
G_BuildMapName(INT32 map)787 const char *G_BuildMapName(INT32 map)
788 {
789 	static char mapname[10] = "MAPXX"; // internal map name (wad resource name)
790 
791 	I_Assert(map > 0);
792 	I_Assert(map <= NUMMAPS);
793 
794 	if (map < 100)
795 		sprintf(&mapname[3], "%.2d", map);
796 	else
797 	{
798 		mapname[3] = (char)('A' + (char)((map - 100) / 36));
799 		if ((map - 100) % 36 < 10)
800 			mapname[4] = (char)('0' + (char)((map - 100) % 36));
801 		else
802 			mapname[4] = (char)('A' + (char)((map - 100) % 36) - 10);
803 		mapname[5] = '\0';
804 	}
805 
806 	return mapname;
807 }
808 
809 /** Clips the console player's mouse aiming to the current view.
810   * Used whenever the player view is changed manually.
811   *
812   * \param aiming Pointer to the vertical angle to clip.
813   * \return Short version of the clipped angle for building a ticcmd.
814   */
G_ClipAimingPitch(INT32 * aiming)815 INT16 G_ClipAimingPitch(INT32 *aiming)
816 {
817 	INT32 limitangle;
818 
819 	limitangle = ANGLE_90 - 1;
820 
821 	if (*aiming > limitangle)
822 		*aiming = limitangle;
823 	else if (*aiming < -limitangle)
824 		*aiming = -limitangle;
825 
826 	return (INT16)((*aiming)>>16);
827 }
828 
G_SoftwareClipAimingPitch(INT32 * aiming)829 INT16 G_SoftwareClipAimingPitch(INT32 *aiming)
830 {
831 	INT32 limitangle;
832 
833 	// note: the current software mode implementation doesn't have true perspective
834 	limitangle = ANGLE_90 - ANG10; // Some viewing fun, but not too far down...
835 
836 	if (*aiming > limitangle)
837 		*aiming = limitangle;
838 	else if (*aiming < -limitangle)
839 		*aiming = -limitangle;
840 
841 	return (INT16)((*aiming)>>16);
842 }
843 
JoyAxis(axis_input_e axissel)844 static INT32 JoyAxis(axis_input_e axissel)
845 {
846 	INT32 retaxis;
847 	INT32 axisval;
848 	boolean flp = false;
849 
850 	//find what axis to get
851 	switch (axissel)
852 	{
853 		case AXISTURN:
854 			axisval = cv_turnaxis.value;
855 			break;
856 		case AXISMOVE:
857 			axisval = cv_moveaxis.value;
858 			break;
859 		case AXISLOOK:
860 			axisval = cv_lookaxis.value;
861 			break;
862 		case AXISSTRAFE:
863 			axisval = cv_sideaxis.value;
864 			break;
865 		case AXISJUMP:
866 			axisval = cv_jumpaxis.value;
867 			break;
868 		case AXISSPIN:
869 			axisval = cv_spinaxis.value;
870 			break;
871 		case AXISFIRE:
872 			axisval = cv_fireaxis.value;
873 			break;
874 		case AXISFIRENORMAL:
875 			axisval = cv_firenaxis.value;
876 			break;
877 		default:
878 			return 0;
879 	}
880 
881 	if (axisval < 0) //odd -axises
882 	{
883 		axisval = -axisval;
884 		flp = true;
885 	}
886 	if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None
887 		return 0;
888 
889 	if (axisval%2)
890 	{
891 		axisval /= 2;
892 		retaxis = joyxmove[axisval];
893 	}
894 	else
895 	{
896 		axisval--;
897 		axisval /= 2;
898 		retaxis = joyymove[axisval];
899 	}
900 
901 	if (retaxis < (-JOYAXISRANGE))
902 		retaxis = -JOYAXISRANGE;
903 	if (retaxis > (+JOYAXISRANGE))
904 		retaxis = +JOYAXISRANGE;
905 
906 	if (!Joystick.bGamepadStyle && axissel > AXISDIGITAL)
907 	{
908 		const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone.value) >> FRACBITS;
909 		if (-jdeadzone < retaxis && retaxis < jdeadzone)
910 			return 0;
911 	}
912 
913 	if (flp) retaxis = -retaxis; //flip it around
914 	return retaxis;
915 }
916 
Joy2Axis(axis_input_e axissel)917 static INT32 Joy2Axis(axis_input_e axissel)
918 {
919 	INT32 retaxis;
920 	INT32 axisval;
921 	boolean flp = false;
922 
923 	//find what axis to get
924 	switch (axissel)
925 	{
926 		case AXISTURN:
927 			axisval = cv_turnaxis2.value;
928 			break;
929 		case AXISMOVE:
930 			axisval = cv_moveaxis2.value;
931 			break;
932 		case AXISLOOK:
933 			axisval = cv_lookaxis2.value;
934 			break;
935 		case AXISSTRAFE:
936 			axisval = cv_sideaxis2.value;
937 			break;
938 		case AXISJUMP:
939 			axisval = cv_jumpaxis2.value;
940 			break;
941 		case AXISSPIN:
942 			axisval = cv_spinaxis2.value;
943 			break;
944 		case AXISFIRE:
945 			axisval = cv_fireaxis2.value;
946 			break;
947 		case AXISFIRENORMAL:
948 			axisval = cv_firenaxis2.value;
949 			break;
950 		default:
951 			return 0;
952 	}
953 
954 
955 	if (axisval < 0) //odd -axises
956 	{
957 		axisval = -axisval;
958 		flp = true;
959 	}
960 
961 	if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None
962 		return 0;
963 
964 	if (axisval%2)
965 	{
966 		axisval /= 2;
967 		retaxis = joy2xmove[axisval];
968 	}
969 	else
970 	{
971 		axisval--;
972 		axisval /= 2;
973 		retaxis = joy2ymove[axisval];
974 	}
975 
976 	if (retaxis < (-JOYAXISRANGE))
977 		retaxis = -JOYAXISRANGE;
978 	if (retaxis > (+JOYAXISRANGE))
979 		retaxis = +JOYAXISRANGE;
980 
981 	if (!Joystick2.bGamepadStyle && axissel > AXISDIGITAL)
982 	{
983 		const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone2.value) >> FRACBITS;
984 		if (-jdeadzone < retaxis && retaxis < jdeadzone)
985 			return 0;
986 	}
987 
988 	if (flp) retaxis = -retaxis; //flip it around
989 	return retaxis;
990 }
991 
992 
993 #define PlayerJoyAxis(p, ax) ((p) == 1 ? JoyAxis(ax) : Joy2Axis(ax))
994 
995 // Take a magnitude of two axes, and adjust it to take out the deadzone
996 // Will return a value between 0 and JOYAXISRANGE
G_BasicDeadZoneCalculation(INT32 magnitude,fixed_t deadZone)997 static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone)
998 {
999 	const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT;
1000 	INT32 deadzoneAppliedValue = 0;
1001 	INT32 adjustedMagnitude = abs(magnitude);
1002 
1003 	if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%...
1004 		return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0
1005 	else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone
1006 	{
1007 		adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE);
1008 
1009 		adjustedMagnitude -= jdeadzone;
1010 
1011 		deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone);
1012 	}
1013 
1014 	return deadzoneAppliedValue;
1015 }
1016 
1017 // Get the actual sensible radial value for a joystick axis when accounting for a deadzone
G_HandleAxisDeadZone(UINT8 splitnum,joystickvector2_t * joystickvector)1018 static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvector)
1019 {
1020 	INT32 gamepadStyle = Joystick.bGamepadStyle;
1021 	fixed_t deadZone = cv_deadzone.value;
1022 
1023 	if (splitnum == 1)
1024 	{
1025 		gamepadStyle = Joystick2.bGamepadStyle;
1026 		deadZone = cv_deadzone2.value;
1027 	}
1028 
1029 	// When gamepadstyle is "true" the values are just -1, 0, or 1. This is done in the interface code.
1030 	if (!gamepadStyle)
1031 	{
1032 		// Get the total magnitude of the 2 axes
1033 		INT32 magnitude = (joystickvector->xaxis * joystickvector->xaxis) + (joystickvector->yaxis * joystickvector->yaxis);
1034 		INT32 normalisedXAxis;
1035 		INT32 normalisedYAxis;
1036 		INT32 normalisedMagnitude;
1037 		double dMagnitude = sqrt((double)magnitude);
1038 		magnitude = (INT32)dMagnitude;
1039 
1040 		// Get the normalised xy values from the magnitude
1041 		normalisedXAxis = (joystickvector->xaxis * magnitude) / JOYAXISRANGE;
1042 		normalisedYAxis = (joystickvector->yaxis * magnitude) / JOYAXISRANGE;
1043 
1044 		// Apply the deadzone to the magnitude to give a correct value between 0 and JOYAXISRANGE
1045 		normalisedMagnitude = G_BasicDeadZoneCalculation(magnitude, deadZone);
1046 
1047 		// Apply the deadzone to the xy axes
1048 		joystickvector->xaxis = (normalisedXAxis * normalisedMagnitude) / JOYAXISRANGE;
1049 		joystickvector->yaxis = (normalisedYAxis * normalisedMagnitude) / JOYAXISRANGE;
1050 
1051 		// Cap the values so they don't go above the correct maximum
1052 		joystickvector->xaxis = min(joystickvector->xaxis, JOYAXISRANGE);
1053 		joystickvector->xaxis = max(joystickvector->xaxis, -JOYAXISRANGE);
1054 		joystickvector->yaxis = min(joystickvector->yaxis, JOYAXISRANGE);
1055 		joystickvector->yaxis = max(joystickvector->yaxis, -JOYAXISRANGE);
1056 	}
1057 }
1058 
1059 //
1060 // G_BuildTiccmd
1061 // Builds a ticcmd from all of the available inputs
1062 // or reads it from the demo buffer.
1063 // If recording a demo, write it out
1064 //
1065 // set secondaryplayer true to build player 2's ticcmd in splitscreen mode
1066 //
1067 INT32 localaiming, localaiming2;
1068 angle_t localangle, localangle2;
1069 
1070 static fixed_t forwardmove[2] = {25<<FRACBITS>>16, 50<<FRACBITS>>16};
1071 static fixed_t sidemove[2] = {25<<FRACBITS>>16, 50<<FRACBITS>>16}; // faster!
1072 static fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
1073 
1074 INT16 ticcmd_oldangleturn[2];
1075 boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player
1076 mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object?
G_BuildTiccmd(ticcmd_t * cmd,INT32 realtics,UINT8 ssplayer)1077 void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
1078 {
1079 	boolean forcestrafe = false;
1080 	boolean forcefullinput = false;
1081 	INT32 tspeed, forward, side, axis, strafeaxis, moveaxis, turnaxis, lookaxis, i;
1082 
1083 	joystickvector2_t movejoystickvector, lookjoystickvector;
1084 
1085 	const INT32 speed = 1;
1086 	// these ones used for multiple conditions
1087 	boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming;
1088 	boolean strafeisturn; // Simple controls only
1089 	player_t *player = &players[ssplayer == 2 ? secondarydisplayplayer : consoleplayer];
1090 	camera_t *thiscam = ((ssplayer == 1 || player->bot == 2) ? &camera : &camera2);
1091 	angle_t *myangle = (ssplayer == 1 ? &localangle : &localangle2);
1092 	INT32 *myaiming = (ssplayer == 1 ? &localaiming : &localaiming2);
1093 
1094 	angle_t drawangleoffset = (player->powers[pw_carry] == CR_ROLLOUT) ? ANGLE_180 : 0;
1095 	INT32 chasecam, chasefreelook, alwaysfreelook, usejoystick, invertmouse, turnmultiplier, mousemove;
1096 	controlstyle_e controlstyle = G_ControlStyle(ssplayer);
1097 	INT32 *mx; INT32 *my; INT32 *mly;
1098 
1099 	static INT32 turnheld[2]; // for accelerative turning
1100 	static boolean keyboard_look[2]; // true if lookup/down using keyboard
1101 	static boolean resetdown[2]; // don't cam reset every frame
1102 	static boolean joyaiming[2]; // check the last frame's value if we need to reset the camera
1103 
1104 	// simple mode vars
1105 	static boolean zchange[2]; // only switch z targets once per press
1106 	static fixed_t tta_factor[2] = {FRACUNIT, FRACUNIT}; // disables turn-to-angle when manually turning camera until movement happens
1107 	boolean centerviewdown = false;
1108 
1109 	UINT8 forplayer = ssplayer-1;
1110 
1111 	if (ssplayer == 1)
1112 	{
1113 		chasecam = cv_chasecam.value;
1114 		chasefreelook = cv_chasefreelook.value;
1115 		alwaysfreelook = cv_alwaysfreelook.value;
1116 		usejoystick = cv_usejoystick.value;
1117 		invertmouse = cv_invertmouse.value;
1118 		turnmultiplier = cv_cam_turnmultiplier.value;
1119 		mousemove = cv_mousemove.value;
1120 		mx = &mousex;
1121 		my = &mousey;
1122 		mly = &mlooky;
1123 		G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
1124 	}
1125 	else
1126 	{
1127 		chasecam = cv_chasecam2.value;
1128 		chasefreelook = cv_chasefreelook2.value;
1129 		alwaysfreelook = cv_alwaysfreelook2.value;
1130 		usejoystick = cv_usejoystick2.value;
1131 		invertmouse = cv_invertmouse2.value;
1132 		turnmultiplier = cv_cam2_turnmultiplier.value;
1133 		mousemove = cv_mousemove2.value;
1134 		mx = &mouse2x;
1135 		my = &mouse2y;
1136 		mly = &mlook2y;
1137 		G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver
1138 	}
1139 
1140 	strafeisturn = controlstyle == CS_SIMPLE && ticcmd_centerviewdown[forplayer] &&
1141 		((cv_cam_lockedinput[forplayer].value && !ticcmd_ztargetfocus[forplayer]) || (player->pflags & PF_STARTDASH)) &&
1142 		!player->climbing && player->powers[pw_carry] != CR_MINECART;
1143 
1144 	// why build a ticcmd if we're paused?
1145 	// Or, for that matter, if we're being reborn.
1146 	// ...OR if we're blindfolded. No looking into the floor.
1147 	if (paused || P_AutoPause() || (gamestate == GS_LEVEL && (player->playerstate == PST_REBORN || ((gametyperules & GTR_TAG)
1148 	&& (leveltime < hidetime * TICRATE) && (player->pflags & PF_TAGIT)))))
1149 	{//@TODO splitscreen player
1150 		cmd->angleturn = ticcmd_oldangleturn[forplayer];
1151 		cmd->aiming = G_ClipAimingPitch(myaiming);
1152 		return;
1153 	}
1154 
1155 	turnright = PLAYERINPUTDOWN(ssplayer, gc_turnright);
1156 	turnleft = PLAYERINPUTDOWN(ssplayer, gc_turnleft);
1157 
1158 	straferkey = PLAYERINPUTDOWN(ssplayer, gc_straferight);
1159 	strafelkey = PLAYERINPUTDOWN(ssplayer, gc_strafeleft);
1160 	movefkey = PLAYERINPUTDOWN(ssplayer, gc_forward);
1161 	movebkey = PLAYERINPUTDOWN(ssplayer, gc_backward);
1162 
1163 	if (strafeisturn)
1164 	{
1165 		turnright |= straferkey;
1166 		turnleft |= strafelkey;
1167 		straferkey = strafelkey = false;
1168 	}
1169 
1170 	mouseaiming = (PLAYERINPUTDOWN(ssplayer, gc_mouseaiming)) ^
1171 		((chasecam && !player->spectator) ? chasefreelook : alwaysfreelook);
1172 	analogjoystickmove = usejoystick && !Joystick.bGamepadStyle;
1173 	gamepadjoystickmove = usejoystick && Joystick.bGamepadStyle;
1174 
1175 	thisjoyaiming = (chasecam && !player->spectator) ? chasefreelook : alwaysfreelook;
1176 
1177 	// Reset the vertical look if we're no longer joyaiming
1178 	if (!thisjoyaiming && joyaiming[forplayer])
1179 		*myaiming = 0;
1180 	joyaiming[forplayer] = thisjoyaiming;
1181 
1182 	turnaxis = PlayerJoyAxis(ssplayer, AXISTURN);
1183 	if (strafeisturn)
1184 		turnaxis += PlayerJoyAxis(ssplayer, AXISSTRAFE);
1185 	lookaxis = PlayerJoyAxis(ssplayer, AXISLOOK);
1186 	lookjoystickvector.xaxis = turnaxis;
1187 	lookjoystickvector.yaxis = lookaxis;
1188 	G_HandleAxisDeadZone(forplayer, &lookjoystickvector);
1189 
1190 	if (gamepadjoystickmove && lookjoystickvector.xaxis != 0)
1191 	{
1192 		turnright = turnright || (lookjoystickvector.xaxis > 0);
1193 		turnleft = turnleft || (lookjoystickvector.xaxis < 0);
1194 	}
1195 	forward = side = 0;
1196 
1197 	// use two stage accelerative turning
1198 	// on the keyboard and joystick
1199 	if (turnleft || turnright)
1200 		turnheld[forplayer] += realtics;
1201 	else
1202 		turnheld[forplayer] = 0;
1203 
1204 	if (turnheld[forplayer] < SLOWTURNTICS)
1205 		tspeed = 2; // slow turn
1206 	else
1207 		tspeed = speed;
1208 
1209 	// let movement keys cancel each other out
1210 	if (controlstyle == CS_LMAOGALOG) // Analog
1211 	{
1212 		if (turnright)
1213 			cmd->angleturn = (INT16)(cmd->angleturn - angleturn[tspeed]);
1214 		if (turnleft)
1215 			cmd->angleturn = (INT16)(cmd->angleturn + angleturn[tspeed]);
1216 	}
1217 	if (twodlevel
1218 		|| (player->mo && (player->mo->flags2 & MF2_TWOD))
1219 		|| (!demoplayback && (player->pflags & PF_SLIDING)))
1220 			forcefullinput = true;
1221 	if (twodlevel
1222 		|| (player->mo && (player->mo->flags2 & MF2_TWOD))
1223 		|| (!demoplayback && ((player->powers[pw_carry] == CR_NIGHTSMODE)
1224 		|| (player->pflags & (PF_SLIDING|PF_FORCESTRAFE))))) // Analog
1225 			forcestrafe = true;
1226 	if (forcestrafe)
1227 	{
1228 		if (turnright)
1229 			side += sidemove[speed];
1230 		if (turnleft)
1231 			side -= sidemove[speed];
1232 
1233 		if (analogjoystickmove && lookjoystickvector.xaxis != 0)
1234 		{
1235 			// JOYAXISRANGE is supposed to be 1023 (divide by 1024)
1236 			side += ((lookjoystickvector.xaxis * sidemove[1]) >> 10);
1237 		}
1238 	}
1239 	else if (controlstyle == CS_LMAOGALOG) // Analog
1240 	{
1241 		if (turnright)
1242 			cmd->buttons |= BT_CAMRIGHT;
1243 		if (turnleft)
1244 			cmd->buttons |= BT_CAMLEFT;
1245 	}
1246 	else
1247 	{
1248 		if (turnright && turnleft);
1249 		else if (turnright)
1250 			cmd->angleturn = (INT16)(cmd->angleturn - ((angleturn[tspeed] * turnmultiplier)>>FRACBITS));
1251 		else if (turnleft)
1252 			cmd->angleturn = (INT16)(cmd->angleturn + ((angleturn[tspeed] * turnmultiplier)>>FRACBITS));
1253 
1254 		if (analogjoystickmove && lookjoystickvector.xaxis != 0)
1255 		{
1256 			// JOYAXISRANGE should be 1023 (divide by 1024)
1257 			cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 10) * turnmultiplier)>>FRACBITS)); // ANALOG!
1258 		}
1259 
1260 		if (turnright || turnleft || abs(cmd->angleturn) > angleturn[2])
1261 			tta_factor[forplayer] = 0; // suspend turn to angle
1262 	}
1263 
1264 	strafeaxis = strafeisturn ? 0 : PlayerJoyAxis(ssplayer, AXISSTRAFE);
1265 	moveaxis = PlayerJoyAxis(ssplayer, AXISMOVE);
1266 	movejoystickvector.xaxis = strafeaxis;
1267 	movejoystickvector.yaxis = moveaxis;
1268 	G_HandleAxisDeadZone(forplayer, &movejoystickvector);
1269 
1270 	if (gamepadjoystickmove && movejoystickvector.xaxis != 0)
1271 	{
1272 		if (movejoystickvector.xaxis > 0)
1273 			side += sidemove[speed];
1274 		else if (movejoystickvector.xaxis < 0)
1275 			side -= sidemove[speed];
1276 	}
1277 	else if (analogjoystickmove && movejoystickvector.xaxis != 0)
1278 	{
1279 		// JOYAXISRANGE is supposed to be 1023 (divide by 1024)
1280 		side += ((movejoystickvector.xaxis * sidemove[1]) >> 10);
1281 	}
1282 
1283 	// forward with key or button
1284 	if (movefkey || (gamepadjoystickmove && movejoystickvector.yaxis < 0)
1285 		|| ((player->powers[pw_carry] == CR_NIGHTSMODE)
1286 			&& (PLAYERINPUTDOWN(ssplayer, gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0))))
1287 		forward = forwardmove[speed];
1288 	if (movebkey || (gamepadjoystickmove && movejoystickvector.yaxis > 0)
1289 		|| ((player->powers[pw_carry] == CR_NIGHTSMODE)
1290 			&& (PLAYERINPUTDOWN(ssplayer, gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0))))
1291 		forward -= forwardmove[speed];
1292 
1293 	if (analogjoystickmove && movejoystickvector.yaxis != 0)
1294 		forward -= ((movejoystickvector.yaxis * forwardmove[1]) >> 10); // ANALOG!
1295 
1296 	// some people strafe left & right with mouse buttons
1297 	// those people are weird
1298 	if (straferkey)
1299 		side += sidemove[speed];
1300 	if (strafelkey)
1301 		side -= sidemove[speed];
1302 
1303 	if (PLAYERINPUTDOWN(ssplayer, gc_weaponnext))
1304 		cmd->buttons |= BT_WEAPONNEXT; // Next Weapon
1305 	if (PLAYERINPUTDOWN(ssplayer, gc_weaponprev))
1306 		cmd->buttons |= BT_WEAPONPREV; // Previous Weapon
1307 
1308 #if NUM_WEAPONS > 10
1309 "Add extra inputs to g_input.h/gamecontrols_e"
1310 #endif
1311 	//use the four avaliable bits to determine the weapon.
1312 	cmd->buttons &= ~BT_WEAPONMASK;
1313 	for (i = 0; i < NUM_WEAPONS; ++i)
1314 		if (PLAYERINPUTDOWN(ssplayer, gc_wepslot1 + i))
1315 		{
1316 			cmd->buttons |= (UINT16)(i + 1);
1317 			break;
1318 		}
1319 
1320 	// fire with any button/key
1321 	axis = PlayerJoyAxis(ssplayer, AXISFIRE);
1322 	if (PLAYERINPUTDOWN(ssplayer, gc_fire) || (usejoystick && axis > 0))
1323 		cmd->buttons |= BT_ATTACK;
1324 
1325 	// fire normal with any button/key
1326 	axis = PlayerJoyAxis(ssplayer, AXISFIRENORMAL);
1327 	if (PLAYERINPUTDOWN(ssplayer, gc_firenormal) || (usejoystick && axis > 0))
1328 		cmd->buttons |= BT_FIRENORMAL;
1329 
1330 	if (PLAYERINPUTDOWN(ssplayer, gc_tossflag))
1331 		cmd->buttons |= BT_TOSSFLAG;
1332 
1333 	// Lua scriptable buttons
1334 	if (PLAYERINPUTDOWN(ssplayer, gc_custom1))
1335 		cmd->buttons |= BT_CUSTOM1;
1336 	if (PLAYERINPUTDOWN(ssplayer, gc_custom2))
1337 		cmd->buttons |= BT_CUSTOM2;
1338 	if (PLAYERINPUTDOWN(ssplayer, gc_custom3))
1339 		cmd->buttons |= BT_CUSTOM3;
1340 
1341 	// use with any button/key
1342 	axis = PlayerJoyAxis(ssplayer, AXISSPIN);
1343 	if (PLAYERINPUTDOWN(ssplayer, gc_spin) || (usejoystick && axis > 0))
1344 		cmd->buttons |= BT_SPIN;
1345 
1346 	// Centerview can be a toggle in simple mode!
1347 	{
1348 		static boolean last_centerviewdown[2], centerviewhold[2]; // detect taps for toggle behavior
1349 		boolean down = PLAYERINPUTDOWN(ssplayer, gc_centerview);
1350 
1351 		if (!(controlstyle == CS_SIMPLE && cv_cam_centertoggle[forplayer].value))
1352 			centerviewdown = down;
1353 		else
1354 		{
1355 			if (down && !last_centerviewdown[forplayer])
1356 				centerviewhold[forplayer] = !centerviewhold[forplayer];
1357 			last_centerviewdown[forplayer] = down;
1358 
1359 			if (cv_cam_centertoggle[forplayer].value == 2 && !down && !ticcmd_ztargetfocus[forplayer])
1360 				centerviewhold[forplayer] = false;
1361 
1362 			centerviewdown = centerviewhold[forplayer];
1363 		}
1364 	}
1365 
1366 	if (centerviewdown)
1367 	{
1368 		if (controlstyle == CS_SIMPLE && !ticcmd_centerviewdown[forplayer] && !G_RingSlingerGametype())
1369 		{
1370 			CV_SetValue(&cv_directionchar[forplayer], 2);
1371 			cmd->angleturn = (INT16)((player->mo->angle - *myangle) >> 16);
1372 			*myaiming = 0;
1373 
1374 			if (cv_cam_lockonboss[forplayer].value)
1375 				P_SetTarget(&ticcmd_ztargetfocus[forplayer], P_LookForFocusTarget(player, NULL, 0, cv_cam_lockonboss[forplayer].value));
1376 		}
1377 
1378 		ticcmd_centerviewdown[forplayer] = true;
1379 	}
1380 	else if (ticcmd_centerviewdown[forplayer])
1381 	{
1382 		if (controlstyle == CS_SIMPLE)
1383 		{
1384 			P_SetTarget(&ticcmd_ztargetfocus[forplayer], NULL);
1385 			CV_SetValue(&cv_directionchar[forplayer], 1);
1386 		}
1387 
1388 		ticcmd_centerviewdown[forplayer] = false;
1389 	}
1390 
1391 	if (ticcmd_ztargetfocus[forplayer])
1392 	{
1393 		if (
1394 			P_MobjWasRemoved(ticcmd_ztargetfocus[forplayer]) ||
1395 			!ticcmd_ztargetfocus[forplayer]->health ||
1396 			(ticcmd_ztargetfocus[forplayer]->flags2 & MF2_FRET) ||
1397 			(ticcmd_ztargetfocus[forplayer]->type == MT_EGGMOBILE3 && !ticcmd_ztargetfocus[forplayer]->movecount) // Sea Egg is moving around underground and shouldn't be tracked
1398 		)
1399 			P_SetTarget(&ticcmd_ztargetfocus[forplayer], NULL);
1400 		else
1401 		{
1402 			mobj_t *newtarget = NULL;
1403 			if (zchange[forplayer])
1404 			{
1405 				if (!turnleft && !turnright && abs(cmd->angleturn) < angleturn[0])
1406 					zchange[forplayer] = false;
1407 			}
1408 			else if (turnleft || cmd->angleturn > angleturn[0])
1409 			{
1410 				zchange[forplayer] = true;
1411 				newtarget = P_LookForFocusTarget(player, ticcmd_ztargetfocus[forplayer], 1, cv_cam_lockonboss[forplayer].value);
1412 			}
1413 			else if (turnright || cmd->angleturn < -angleturn[0])
1414 			{
1415 				zchange[forplayer] = true;
1416 				newtarget = P_LookForFocusTarget(player, ticcmd_ztargetfocus[forplayer], -1, cv_cam_lockonboss[forplayer].value);
1417 			}
1418 
1419 			if (newtarget)
1420 				P_SetTarget(&ticcmd_ztargetfocus[forplayer], newtarget);
1421 
1422 			// I assume this is netgame-safe because gunslinger spawns this for only the local player...... *sweats intensely*
1423 			newtarget = P_SpawnMobj(ticcmd_ztargetfocus[forplayer]->x, ticcmd_ztargetfocus[forplayer]->y, ticcmd_ztargetfocus[forplayer]->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
1424 			P_SetTarget(&newtarget->target, ticcmd_ztargetfocus[forplayer]);
1425 
1426 			if (P_AproxDistance(
1427 				player->mo->x - ticcmd_ztargetfocus[forplayer]->x,
1428 				player->mo->y - ticcmd_ztargetfocus[forplayer]->y
1429 			) > 50*player->mo->scale)
1430 			{
1431 				INT32 anglediff = R_PointToAngle2(player->mo->x, player->mo->y, ticcmd_ztargetfocus[forplayer]->x, ticcmd_ztargetfocus[forplayer]->y) - *myangle;
1432 				const INT32 maxturn = ANG10/2;
1433 				anglediff /= 4;
1434 
1435 				if (anglediff > maxturn)
1436 					anglediff = maxturn;
1437 				else if (anglediff < -maxturn)
1438 					anglediff = -maxturn;
1439 
1440 				cmd->angleturn = (INT16)(cmd->angleturn + (anglediff >> 16));
1441 			}
1442 		}
1443 	}
1444 
1445 	if (ticcmd_centerviewdown[forplayer] && controlstyle == CS_SIMPLE)
1446 		controlstyle = CS_LEGACY;
1447 
1448 	if (PLAYERINPUTDOWN(ssplayer, gc_camreset))
1449 	{
1450 		if (thiscam->chase && !resetdown[forplayer])
1451 			P_ResetCamera(&players[ssplayer == 1 ? displayplayer : secondarydisplayplayer], thiscam);
1452 
1453 		resetdown[forplayer] = true;
1454 	}
1455 	else
1456 		resetdown[forplayer] = false;
1457 
1458 
1459 	// jump button
1460 	axis = PlayerJoyAxis(ssplayer, AXISJUMP);
1461 	if (PLAYERINPUTDOWN(ssplayer, gc_jump) || (usejoystick && axis > 0))
1462 		cmd->buttons |= BT_JUMP;
1463 
1464 	// player aiming shit, ahhhh...
1465 	{
1466 		INT32 player_invert = invertmouse ? -1 : 1;
1467 		INT32 screen_invert =
1468 			(player->mo && (player->mo->eflags & MFE_VERTICALFLIP)
1469 			 && (!thiscam->chase || player->pflags & PF_FLIPCAM)) //because chasecam's not inverted
1470 			 ? -1 : 1; // set to -1 or 1 to multiply
1471 		 INT32 configlookaxis = ssplayer == 1 ? cv_lookaxis.value : cv_lookaxis2.value;
1472 
1473 		// mouse look stuff (mouse look is not the same as mouse aim)
1474 		if (mouseaiming)
1475 		{
1476 			keyboard_look[forplayer] = false;
1477 
1478 			// looking up/down
1479 			*myaiming += (*mly<<19)*player_invert*screen_invert;
1480 		}
1481 
1482 		if (analogjoystickmove && joyaiming[forplayer] && lookjoystickvector.yaxis != 0 && configlookaxis != 0)
1483 			*myaiming += (lookjoystickvector.yaxis<<16) * screen_invert;
1484 
1485 		// spring back if not using keyboard neither mouselookin'
1486 		if (!keyboard_look[forplayer] && configlookaxis == 0 && !joyaiming[forplayer] && !mouseaiming)
1487 			*myaiming = 0;
1488 
1489 		if (!(player->powers[pw_carry] == CR_NIGHTSMODE))
1490 		{
1491 			if (PLAYERINPUTDOWN(ssplayer, gc_lookup) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0))
1492 			{
1493 				*myaiming += KB_LOOKSPEED * screen_invert;
1494 				keyboard_look[forplayer] = true;
1495 			}
1496 			else if (PLAYERINPUTDOWN(ssplayer, gc_lookdown) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0))
1497 			{
1498 				*myaiming -= KB_LOOKSPEED * screen_invert;
1499 				keyboard_look[forplayer] = true;
1500 			}
1501 			else if (ticcmd_centerviewdown[forplayer])
1502 				*myaiming = 0;
1503 		}
1504 
1505 		// accept no mlook for network games
1506 		if (!cv_allowmlook.value)
1507 			*myaiming = 0;
1508 
1509 		cmd->aiming = G_ClipAimingPitch(myaiming);
1510 	}
1511 
1512 	if (!mouseaiming && mousemove)
1513 		forward += *my;
1514 
1515 	if ((!demoplayback && (player->pflags & PF_SLIDING))) // Analog for mouse
1516 		side += *mx*2;
1517 	else if (controlstyle == CS_LMAOGALOG)
1518 	{
1519 		if (*mx)
1520 		{
1521 			if (*mx > 0)
1522 				cmd->buttons |= BT_CAMRIGHT;
1523 			else
1524 				cmd->buttons |= BT_CAMLEFT;
1525 		}
1526 	}
1527 	else
1528 		cmd->angleturn = (INT16)(cmd->angleturn - (*mx*8));
1529 
1530 	*mx = *my = *mly = 0;
1531 
1532 	if (forward > MAXPLMOVE)
1533 		forward = MAXPLMOVE;
1534 	else if (forward < -MAXPLMOVE)
1535 		forward = -MAXPLMOVE;
1536 	if (side > MAXPLMOVE)
1537 		side = MAXPLMOVE;
1538 	else if (side < -MAXPLMOVE)
1539 		side = -MAXPLMOVE;
1540 
1541 	// No additional acceleration when moving forward/backward and strafing simultaneously.
1542 	// do this AFTER we cap to MAXPLMOVE so people can't find ways to cheese around this.
1543 	if (!forcefullinput && forward && side)
1544 	{
1545 		angle_t angle = R_PointToAngle2(0, 0, side << FRACBITS, forward << FRACBITS);
1546 		INT32 maxforward = abs(P_ReturnThrustY(NULL, angle, MAXPLMOVE));
1547 		INT32 maxside = abs(P_ReturnThrustX(NULL, angle, MAXPLMOVE));
1548 		forward = max(min(forward, maxforward), -maxforward);
1549 		side = max(min(side, maxside), -maxside);
1550 	}
1551 
1552 	//Silly hack to make 2d mode *somewhat* playable with no chasecam.
1553 	if ((twodlevel || (player->mo && player->mo->flags2 & MF2_TWOD)) && !thiscam->chase)
1554 	{
1555 		INT32 temp = forward;
1556 		forward = side;
1557 		side = temp;
1558 	}
1559 
1560 	cmd->forwardmove = (SINT8)(cmd->forwardmove + forward);
1561 	cmd->sidemove = (SINT8)(cmd->sidemove + side);
1562 
1563 	if (player->bot == 1) { // Tailsbot for P2
1564 		if (!player->powers[pw_tailsfly] && (cmd->forwardmove || cmd->sidemove || cmd->buttons))
1565 		{
1566 			player->bot = 2; // A player-controlled bot. Returns to AI when it respawns.
1567 			CV_SetValue(&cv_analog[1], true);
1568 		}
1569 		else
1570 		{
1571 			G_CopyTiccmd(cmd,  I_BaseTiccmd2(), 1); // empty, or external driver
1572 			B_BuildTiccmd(player, cmd);
1573 		}
1574 		B_HandleFlightIndicator(player);
1575 	}
1576 	else if (player->bot == 2)
1577 		// Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy
1578 		cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
1579 
1580 	*myangle += (cmd->angleturn<<16);
1581 
1582 	if (controlstyle == CS_LMAOGALOG) {
1583 		angle_t angle;
1584 
1585 		if (player->awayviewtics)
1586 			angle = player->awayviewmobj->angle;
1587 		else
1588 			angle = thiscam->angle;
1589 
1590 		cmd->angleturn = (INT16)((angle - (ticcmd_oldangleturn[forplayer] << 16)) >> 16);
1591 	}
1592 	else
1593 	{
1594 		// Adjust camera angle by player input
1595 		if (controlstyle == CS_SIMPLE && !forcestrafe && thiscam->chase && !turnheld[forplayer] && !ticcmd_centerviewdown[forplayer] && !player->climbing && player->powers[pw_carry] != CR_MINECART)
1596 		{
1597 			fixed_t camadjustfactor = cv_cam_turnfacinginput[forplayer].value;
1598 
1599 			if (camadjustfactor)
1600 			{
1601 				fixed_t sine = FINESINE((R_PointToAngle2(0, 0, player->rmomx, player->rmomy) - localangle)>>ANGLETOFINESHIFT);
1602 				fixed_t factor;
1603 				INT16 camadjust;
1604 
1605 				if ((sine > 0) == (cmd->sidemove > 0))
1606 					sine = 0; // Prevent jerking right when braking from going left, or vice versa
1607 
1608 				factor = min(40, FixedMul(player->speed, abs(sine))*2 / FRACUNIT);
1609 
1610 				camadjust = (cmd->sidemove * factor * camadjustfactor) >> 16;
1611 
1612 				*myangle -= camadjust << 16;
1613 				cmd->angleturn = (INT16)(cmd->angleturn - camadjust);
1614 			}
1615 
1616 			if (ticcmd_centerviewdown[forplayer] && (cv_cam_lockedinput[forplayer].value || (player->pflags & PF_STARTDASH)))
1617 				cmd->sidemove = 0;
1618 		}
1619 
1620 		// Adjust camera angle to face player direction, depending on circumstances
1621 		// Nothing happens if cam left/right are held, so you can hold both to lock the camera in one direction
1622 		if (controlstyle == CS_SIMPLE && !forcestrafe && thiscam->chase && !turnheld[forplayer] && !ticcmd_centerviewdown[forplayer] && player->powers[pw_carry] != CR_MINECART)
1623 		{
1624 			fixed_t camadjustfactor;
1625 			boolean alt = false; // Reduce intensity on diagonals and prevent backwards movement from turning the camera
1626 
1627 			if (player->pflags & PF_GLIDING)
1628 				camadjustfactor = cv_cam_turnfacingability[forplayer].value/4;
1629 			else if (player->pflags & PF_STARTDASH)
1630 				camadjustfactor = cv_cam_turnfacingspindash[forplayer].value/4;
1631 			else
1632 			{
1633 				alt = true;
1634 				camadjustfactor = cv_cam_turnfacing[forplayer].value/8;
1635 			}
1636 
1637 			camadjustfactor = FixedMul(camadjustfactor, max(FRACUNIT - player->speed, min(player->speed/18, FRACUNIT)));
1638 
1639 			camadjustfactor = FixedMul(camadjustfactor, tta_factor[forplayer]);
1640 
1641 			if (tta_factor[forplayer] < FRACUNIT && (cmd->forwardmove || cmd->sidemove || tta_factor[forplayer] >= FRACUNIT/3))
1642 				tta_factor[forplayer] += FRACUNIT>>5;
1643 			else if (tta_factor[forplayer] && tta_factor[forplayer] < FRACUNIT/3)
1644 				tta_factor[forplayer] -= FRACUNIT>>5;
1645 
1646 			if (camadjustfactor)
1647 			{
1648 				angle_t controlangle;
1649 				INT32 anglediff;
1650 				INT16 camadjust;
1651 
1652 				if ((cmd->forwardmove || cmd->sidemove) && !(player->pflags & PF_SPINNING))
1653 					controlangle = *myangle + R_PointToAngle2(0, 0, cmd->forwardmove << FRACBITS, -cmd->sidemove << FRACBITS);
1654 				else
1655 					controlangle = player->drawangle + drawangleoffset;
1656 
1657 				anglediff = controlangle - *myangle;
1658 
1659 				if (alt)
1660 				{
1661 					fixed_t sine = FINESINE((angle_t) (anglediff)>>ANGLETOFINESHIFT);
1662 					sine = abs(sine);
1663 
1664 					if (abs(anglediff) > ANGLE_90)
1665 						sine = max(0, sine*3 - 2*FRACUNIT); // At about 135 degrees, this will stop turning
1666 
1667 					anglediff = FixedMul(anglediff, sine);
1668 				}
1669 
1670 				camadjust = FixedMul(anglediff, camadjustfactor) >> 16;
1671 
1672 				*myangle += camadjust << 16;
1673 				cmd->angleturn = (INT16)(cmd->angleturn + camadjust);
1674 			}
1675 		}
1676 	}
1677 
1678 	// At this point, cmd doesn't contain the final angle yet,
1679 	// So we need to temporarily transform it so Lua scripters
1680 	// don't need to handle it differently than in other hooks.
1681 	if (addedtogame && gamestate == GS_LEVEL)
1682 	{
1683 		INT16 extra = ticcmd_oldangleturn[forplayer] - player->oldrelangleturn;
1684 		INT16 origangle = cmd->angleturn;
1685 		INT16 orighookangle = (INT16)(origangle + player->angleturn + extra);
1686 		INT16 origaiming = cmd->aiming;
1687 
1688 		cmd->angleturn = orighookangle;
1689 
1690 		LUAh_PlayerCmd(player, cmd);
1691 
1692 		extra = cmd->angleturn - orighookangle;
1693 		cmd->angleturn = origangle + extra;
1694 		*myangle += extra << 16;
1695 		*myaiming += (cmd->aiming - origaiming) << 16;
1696 	}
1697 
1698 	//Reset away view if a command is given.
1699 	if (ssplayer == 1 && (cmd->forwardmove || cmd->sidemove || cmd->buttons)
1700 		&& displayplayer != consoleplayer)
1701 	{
1702 		// Call ViewpointSwitch hooks here.
1703 		// The viewpoint was forcibly changed.
1704 		LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
1705 		displayplayer = consoleplayer;
1706 	}
1707 
1708 	cmd->angleturn = (INT16)(cmd->angleturn + ticcmd_oldangleturn[forplayer]);
1709 	ticcmd_oldangleturn[forplayer] = cmd->angleturn;
1710 }
1711 
G_CopyTiccmd(ticcmd_t * dest,const ticcmd_t * src,const size_t n)1712 ticcmd_t *G_CopyTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
1713 {
1714 	return M_Memcpy(dest, src, n*sizeof(*src));
1715 }
1716 
G_MoveTiccmd(ticcmd_t * dest,const ticcmd_t * src,const size_t n)1717 ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
1718 {
1719 	size_t i;
1720 	for (i = 0; i < n; i++)
1721 	{
1722 		dest[i].forwardmove = src[i].forwardmove;
1723 		dest[i].sidemove = src[i].sidemove;
1724 		dest[i].angleturn = SHORT(src[i].angleturn);
1725 		dest[i].aiming = (INT16)SHORT(src[i].aiming);
1726 		dest[i].buttons = (UINT16)SHORT(src[i].buttons);
1727 	}
1728 	return dest;
1729 }
1730 
1731 // User has designated that they want
1732 // analog ON, so tell the game to stop
1733 // fudging with it.
UserAnalog_OnChange(void)1734 static void UserAnalog_OnChange(void)
1735 {
1736 	if (cv_useranalog[0].value)
1737 		CV_SetValue(&cv_analog[0], 1);
1738 	else
1739 		CV_SetValue(&cv_analog[0], 0);
1740 }
1741 
UserAnalog2_OnChange(void)1742 static void UserAnalog2_OnChange(void)
1743 {
1744 	if (botingame)
1745 		return;
1746 	if (cv_useranalog[1].value)
1747 		CV_SetValue(&cv_analog[1], 1);
1748 	else
1749 		CV_SetValue(&cv_analog[1], 0);
1750 }
1751 
Analog_OnChange(void)1752 static void Analog_OnChange(void)
1753 {
1754 	if (!cv_cam_dist.string)
1755 		return;
1756 
1757 	// cameras are not initialized at this point
1758 
1759 	if (!cv_chasecam.value && cv_analog[0].value) {
1760 		CV_SetValue(&cv_analog[0], 0);
1761 		return;
1762 	}
1763 
1764 	SendWeaponPref();
1765 }
1766 
Analog2_OnChange(void)1767 static void Analog2_OnChange(void)
1768 {
1769 	if (!(splitscreen || botingame) || !cv_cam2_dist.string)
1770 		return;
1771 
1772 	// cameras are not initialized at this point
1773 
1774 	if (!cv_chasecam2.value && cv_analog[1].value) {
1775 		CV_SetValue(&cv_analog[1], 0);
1776 		return;
1777 	}
1778 
1779 	SendWeaponPref2();
1780 }
1781 
DirectionChar_OnChange(void)1782 static void DirectionChar_OnChange(void)
1783 {
1784 	SendWeaponPref();
1785 }
1786 
DirectionChar2_OnChange(void)1787 static void DirectionChar2_OnChange(void)
1788 {
1789 	SendWeaponPref2();
1790 }
1791 
AutoBrake_OnChange(void)1792 static void AutoBrake_OnChange(void)
1793 {
1794 	SendWeaponPref();
1795 }
1796 
AutoBrake2_OnChange(void)1797 static void AutoBrake2_OnChange(void)
1798 {
1799 	SendWeaponPref2();
1800 }
1801 
1802 //
1803 // G_DoLoadLevel
1804 //
G_DoLoadLevel(boolean resetplayer)1805 void G_DoLoadLevel(boolean resetplayer)
1806 {
1807 	INT32 i;
1808 
1809 	// Make sure objectplace is OFF when you first start the level!
1810 	OP_ResetObjectplace();
1811 	demosynced = true;
1812 
1813 	levelstarttic = gametic; // for time calculation
1814 
1815 	if (wipegamestate == GS_LEVEL)
1816 		wipegamestate = -1; // force a wipe
1817 
1818 	if (gamestate == GS_INTERMISSION)
1819 		Y_EndIntermission();
1820 
1821 	// cleanup
1822 	if (titlemapinaction == TITLEMAP_LOADING)
1823 	{
1824 		if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR)
1825 		{
1826 			titlemap = 0; // let's not infinite recursion ok
1827 			Command_ExitGame_f();
1828 			return;
1829 		}
1830 
1831 		titlemapinaction = TITLEMAP_RUNNING;
1832 	}
1833 	else
1834 		titlemapinaction = TITLEMAP_OFF;
1835 
1836 	G_SetGamestate(GS_LEVEL);
1837 	I_UpdateMouseGrab();
1838 
1839 	for (i = 0; i < MAXPLAYERS; i++)
1840 	{
1841 		if (resetplayer || (playeringame[i] && players[i].playerstate == PST_DEAD))
1842 			players[i].playerstate = PST_REBORN;
1843 	}
1844 
1845 	// Setup the level.
1846 	if (!P_LoadLevel(false, false)) // this never returns false?
1847 	{
1848 		// fail so reset game stuff
1849 		Command_ExitGame_f();
1850 		return;
1851 	}
1852 
1853 	P_FindEmerald();
1854 
1855 	displayplayer = consoleplayer; // view the guy you are playing
1856 	if (!splitscreen && !botingame)
1857 		secondarydisplayplayer = consoleplayer;
1858 
1859 	gameaction = ga_nothing;
1860 #ifdef PARANOIA
1861 	Z_CheckHeap(-2);
1862 #endif
1863 
1864 	if (camera.chase)
1865 		P_ResetCamera(&players[displayplayer], &camera);
1866 	if (camera2.chase && splitscreen)
1867 		P_ResetCamera(&players[secondarydisplayplayer], &camera2);
1868 
1869 	// clear cmd building stuff
1870 	memset(gamekeydown, 0, sizeof (gamekeydown));
1871 	for (i = 0;i < JOYAXISSET; i++)
1872 	{
1873 		joyxmove[i] = joyymove[i] = 0;
1874 		joy2xmove[i] = joy2ymove[i] = 0;
1875 	}
1876 	mousex = mousey = 0;
1877 	mouse2x = mouse2y = 0;
1878 
1879 	// clear hud messages remains (usually from game startup)
1880 	CON_ClearHUD();
1881 }
1882 
1883 //
1884 // Start the title card.
1885 //
G_StartTitleCard(void)1886 void G_StartTitleCard(void)
1887 {
1888 	// The title card has been disabled for this map.
1889 	// Oh well.
1890 	if (!G_IsTitleCardAvailable())
1891 	{
1892 		WipeStageTitle = false;
1893 		return;
1894 	}
1895 
1896 	// clear the hud
1897 	CON_ClearHUD();
1898 
1899 	// prepare status bar
1900 	ST_startTitleCard();
1901 
1902 	// start the title card
1903 	WipeStageTitle = (!titlemapinaction);
1904 }
1905 
1906 //
1907 // Run the title card before fading in to the level.
1908 //
G_PreLevelTitleCard(void)1909 void G_PreLevelTitleCard(void)
1910 {
1911 #ifndef NOWIPE
1912 	tic_t starttime = I_GetTime();
1913 	tic_t endtime = starttime + (PRELEVELTIME*NEWTICRATERATIO);
1914 	tic_t nowtime = starttime;
1915 	tic_t lasttime = starttime;
1916 	while (nowtime < endtime)
1917 	{
1918 		// draw loop
1919 		while (!((nowtime = I_GetTime()) - lasttime))
1920 			I_Sleep();
1921 		lasttime = nowtime;
1922 
1923 		ST_runTitleCard();
1924 		ST_preLevelTitleCardDrawer();
1925 		I_FinishUpdate(); // page flip or blit buffer
1926 
1927 		if (moviemode)
1928 			M_SaveFrame();
1929 		if (takescreenshot) // Only take screenshots after drawing.
1930 			M_DoScreenShot();
1931 	}
1932 	if (!cv_showhud.value)
1933 		wipestyleflags = WSF_CROSSFADE;
1934 #endif
1935 }
1936 
1937 static boolean titlecardforreload = false;
1938 
1939 //
1940 // Returns true if the current level has a title card.
1941 //
G_IsTitleCardAvailable(void)1942 boolean G_IsTitleCardAvailable(void)
1943 {
1944 	// The current level header explicitly disabled the title card.
1945 	UINT16 titleflag = LF_NOTITLECARDFIRST;
1946 
1947 	if (modeattacking != ATTACKING_NONE)
1948 		titleflag = LF_NOTITLECARDRECORDATTACK;
1949 	else if (titlecardforreload)
1950 		titleflag = LF_NOTITLECARDRESPAWN;
1951 
1952 	if (mapheaderinfo[gamemap-1]->levelflags & titleflag)
1953 		return false;
1954 
1955 	// The current gametype doesn't have a title card.
1956 	if (gametyperules & GTR_NOTITLECARD)
1957 		return false;
1958 
1959 	// The current level has no name.
1960 	if (!mapheaderinfo[gamemap-1]->lvlttl[0])
1961 		return false;
1962 
1963 	// The title card is available.
1964 	return true;
1965 }
1966 
1967 INT32 pausedelay = 0;
1968 boolean pausebreakkey = false;
1969 static INT32 camtoggledelay, camtoggledelay2 = 0;
1970 
1971 //
1972 // G_Responder
1973 // Get info needed to make ticcmd_ts for the players.
1974 //
G_Responder(event_t * ev)1975 boolean G_Responder(event_t *ev)
1976 {
1977 	// any other key pops up menu if in demos
1978 	if (gameaction == ga_nothing && !singledemo &&
1979 		((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN))
1980 	{
1981 		if (ev->type == ev_keydown && ev->data1 != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE))
1982 		{
1983 			M_StartControlPanel();
1984 			return true;
1985 		}
1986 		return false;
1987 	}
1988 	else if (demoplayback && titledemo)
1989 	{
1990 		// Title demo uses intro responder
1991 		if (F_IntroResponder(ev))
1992 		{
1993 			// stop the title demo
1994 			G_CheckDemoStatus();
1995 			return true;
1996 		}
1997 		return false;
1998 	}
1999 
2000 	if (gamestate == GS_LEVEL)
2001 	{
2002 		if (HU_Responder(ev))
2003 			return true; // chat ate the event
2004 		if (AM_Responder(ev))
2005 			return true; // automap ate it
2006 		// map the event (key/mouse/joy) to a gamecontrol
2007 	}
2008 	// Intro
2009 	else if (gamestate == GS_INTRO)
2010 	{
2011 		if (F_IntroResponder(ev))
2012 		{
2013 			D_StartTitle();
2014 			return true;
2015 		}
2016 	}
2017 	else if (gamestate == GS_CUTSCENE)
2018 	{
2019 		if (HU_Responder(ev))
2020 			return true; // chat ate the event
2021 
2022 		if (F_CutsceneResponder(ev))
2023 		{
2024 			D_StartTitle();
2025 			return true;
2026 		}
2027 	}
2028 	else if (gamestate == GS_CREDITS || gamestate == GS_ENDING) // todo: keep ending here?
2029 	{
2030 		if (HU_Responder(ev))
2031 			return true; // chat ate the event
2032 
2033 		if (F_CreditResponder(ev))
2034 		{
2035 			// Skip credits for everyone
2036 			if (! netgame)
2037 				F_StartGameEvaluation();
2038 			else if (server || IsPlayerAdmin(consoleplayer))
2039 				SendNetXCmd(XD_EXITLEVEL, NULL, 0);
2040 			return true;
2041 		}
2042 	}
2043 	else if (gamestate == GS_CONTINUING)
2044 	{
2045 		if (F_ContinueResponder(ev))
2046 			return true;
2047 	}
2048 	// Demo End
2049 	else if (gamestate == GS_GAMEEND)
2050 		return true;
2051 	else if (gamestate == GS_INTERMISSION || gamestate == GS_EVALUATION)
2052 		if (HU_Responder(ev))
2053 			return true; // chat ate the event
2054 
2055 	// allow spy mode changes even during the demo
2056 	if (gamestate == GS_LEVEL && ev->type == ev_keydown
2057 		&& (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1]))
2058 	{
2059 		// ViewpointSwitch Lua hook.
2060 		UINT8 canSwitchView = 0;
2061 
2062 		if (splitscreen || !netgame)
2063 			displayplayer = consoleplayer;
2064 		else
2065 		{
2066 			// spy mode
2067 			do
2068 			{
2069 				displayplayer++;
2070 				if (displayplayer == MAXPLAYERS)
2071 					displayplayer = 0;
2072 
2073 				if (!playeringame[displayplayer])
2074 					continue;
2075 
2076 				// Call ViewpointSwitch hooks here.
2077 				canSwitchView = LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], false);
2078 				if (canSwitchView == 1) // Set viewpoint to this player
2079 					break;
2080 				else if (canSwitchView == 2) // Skip this player
2081 					continue;
2082 
2083 				if (players[displayplayer].spectator)
2084 					continue;
2085 
2086 				if (G_GametypeHasTeams())
2087 				{
2088 					if (players[consoleplayer].ctfteam
2089 					 && players[displayplayer].ctfteam != players[consoleplayer].ctfteam)
2090 						continue;
2091 				}
2092 				else if (gametyperules & GTR_HIDEFROZEN)
2093 				{
2094 					if (players[consoleplayer].pflags & PF_TAGIT)
2095 						continue;
2096 				}
2097 				// Other Tag-based gametypes?
2098 				else if (G_TagGametype())
2099 				{
2100 					if (!players[consoleplayer].spectator
2101 					 && (players[consoleplayer].pflags & PF_TAGIT) != (players[displayplayer].pflags & PF_TAGIT))
2102 						continue;
2103 				}
2104 				else if (G_GametypeHasSpectators() && G_RingSlingerGametype())
2105 				{
2106 					if (!players[consoleplayer].spectator)
2107 						continue;
2108 				}
2109 
2110 				break;
2111 			} while (displayplayer != consoleplayer);
2112 
2113 			// change statusbar also if playing back demo
2114 			if (singledemo)
2115 				ST_changeDemoView();
2116 
2117 			// tell who's the view
2118 			CONS_Printf(M_GetText("Viewpoint: %s\n"), player_names[displayplayer]);
2119 
2120 			return true;
2121 		}
2122 	}
2123 
2124 	// update keys current state
2125 	G_MapEventsToControls(ev);
2126 
2127 	switch (ev->type)
2128 	{
2129 		case ev_keydown:
2130 			if (ev->data1 == gamecontrol[gc_pause][0]
2131 				|| ev->data1 == gamecontrol[gc_pause][1]
2132 				|| ev->data1 == KEY_PAUSE)
2133 			{
2134 				if (modeattacking && !demoplayback && (gamestate == GS_LEVEL))
2135 				{
2136 					pausebreakkey = (ev->data1 == KEY_PAUSE);
2137 					if (menuactive || pausedelay < 0 || leveltime < 2)
2138 						return true;
2139 
2140 					if (pausedelay < 1+(NEWTICRATE/2))
2141 						pausedelay = 1+(NEWTICRATE/2);
2142 					else if (++pausedelay > 1+(NEWTICRATE/2)+(NEWTICRATE/3))
2143 					{
2144 						G_SetModeAttackRetryFlag();
2145 						return true;
2146 					}
2147 					pausedelay++; // counteract subsequent subtraction this frame
2148 				}
2149 				else
2150 				{
2151 					INT32 oldpausedelay = pausedelay;
2152 					pausedelay = (NEWTICRATE/7);
2153 					if (!oldpausedelay)
2154 					{
2155 						// command will handle all the checks for us
2156 						COM_ImmedExecute("pause");
2157 						return true;
2158 					}
2159 				}
2160 			}
2161 			if (ev->data1 == gamecontrol[gc_camtoggle][0]
2162 				|| ev->data1 == gamecontrol[gc_camtoggle][1])
2163 			{
2164 				if (!camtoggledelay)
2165 				{
2166 					camtoggledelay = NEWTICRATE / 7;
2167 					CV_SetValue(&cv_chasecam, cv_chasecam.value ? 0 : 1);
2168 				}
2169 			}
2170 			if (ev->data1 == gamecontrolbis[gc_camtoggle][0]
2171 				|| ev->data1 == gamecontrolbis[gc_camtoggle][1])
2172 			{
2173 				if (!camtoggledelay2)
2174 				{
2175 					camtoggledelay2 = NEWTICRATE / 7;
2176 					CV_SetValue(&cv_chasecam2, cv_chasecam2.value ? 0 : 1);
2177 				}
2178 			}
2179 			return true;
2180 
2181 		case ev_keyup:
2182 			return false; // always let key up events filter down
2183 
2184 		case ev_mouse:
2185 			return true; // eat events
2186 
2187 		case ev_joystick:
2188 			return true; // eat events
2189 
2190 		case ev_joystick2:
2191 			return true; // eat events
2192 
2193 
2194 		default:
2195 			break;
2196 	}
2197 
2198 	return false;
2199 }
2200 
2201 //
2202 // G_Ticker
2203 // Make ticcmd_ts for the players.
2204 //
G_Ticker(boolean run)2205 void G_Ticker(boolean run)
2206 {
2207 	UINT32 i;
2208 	INT32 buf;
2209 
2210 	// see also SCR_DisplayMarathonInfo
2211 	if ((marathonmode & (MA_INIT|MA_INGAME)) == MA_INGAME && gamestate == GS_LEVEL)
2212 		marathontime++;
2213 
2214 	P_MapStart();
2215 	// do player reborns if needed
2216 	if (gamestate == GS_LEVEL)
2217 	{
2218 		// Or, alternatively, retry.
2219 		if (!(netgame || multiplayer) && G_GetRetryFlag())
2220 		{
2221 			G_ClearRetryFlag();
2222 
2223 			if (modeattacking)
2224 			{
2225 				pausedelay = INT32_MIN;
2226 				M_ModeAttackRetry(0);
2227 			}
2228 			else
2229 			{
2230 				// Costs a life to retry ... unless the player in question is dead already, or you haven't even touched the first starpost in marathon run.
2231 				if (marathonmode && gamemap == spmarathon_start && !players[consoleplayer].starposttime)
2232 				{
2233 					player_t *p = &players[consoleplayer];
2234 					marathonmode |= MA_INIT;
2235 					marathontime = 0;
2236 
2237 					numgameovers = tokenlist = token = 0;
2238 					countdown = countdown2 = exitfadestarted = 0;
2239 
2240 					p->playerstate = PST_REBORN;
2241 					p->starpostx = p->starposty = p->starpostz = 0;
2242 
2243 					p->lives = startinglivesbalance[0];
2244 					p->continues = 1;
2245 
2246 					p->score = 0;
2247 
2248 					// The latter two should clear by themselves, but just in case
2249 					p->pflags &= ~(PF_TAGIT|PF_GAMETYPEOVER|PF_FULLSTASIS);
2250 
2251 					// Clear cheatcodes too, just in case.
2252 					p->pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);
2253 
2254 					p->xtralife = 0;
2255 
2256 					// Reset unlockable triggers
2257 					unlocktriggers = 0;
2258 
2259 					emeralds = 0;
2260 
2261 					memset(&luabanks, 0, sizeof(luabanks));
2262 				}
2263 				else if (G_GametypeUsesLives() && players[consoleplayer].playerstate == PST_LIVE && players[consoleplayer].lives != INFLIVES)
2264 					players[consoleplayer].lives -= 1;
2265 
2266 				G_DoReborn(consoleplayer);
2267 			}
2268 		}
2269 
2270 		for (i = 0; i < MAXPLAYERS; i++)
2271 			if (playeringame[i] && players[i].playerstate == PST_REBORN)
2272 				G_DoReborn(i);
2273 	}
2274 	P_MapEnd();
2275 
2276 	// do things to change the game state
2277 	while (gameaction != ga_nothing)
2278 		switch (gameaction)
2279 		{
2280 			case ga_completed: G_DoCompleted(); break;
2281 			case ga_startcont: G_DoStartContinue(); break;
2282 			case ga_continued: G_DoContinued(); break;
2283 			case ga_worlddone: G_DoWorldDone(); break;
2284 			case ga_nothing: break;
2285 			default: I_Error("gameaction = %d\n", gameaction);
2286 		}
2287 
2288 	buf = gametic % BACKUPTICS;
2289 
2290 	for (i = 0; i < MAXPLAYERS; i++)
2291 	{
2292 		if (playeringame[i])
2293 		{
2294 			INT16 received;
2295 
2296 			G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
2297 
2298 			received = (players[i].cmd.angleturn & TICCMD_RECEIVED);
2299 
2300 			players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
2301 			players[i].oldrelangleturn = players[i].cmd.angleturn;
2302 			if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
2303 				P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
2304 			else
2305 				players[i].cmd.angleturn = players[i].angleturn;
2306 
2307 			players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
2308 			players[i].cmd.angleturn |= received;
2309 		}
2310 	}
2311 
2312 	// do main actions
2313 	switch (gamestate)
2314 	{
2315 		case GS_LEVEL:
2316 			if (titledemo)
2317 				F_TitleDemoTicker();
2318 			P_Ticker(run); // tic the game
2319 			ST_Ticker(run);
2320 			F_TextPromptTicker();
2321 			AM_Ticker();
2322 			HU_Ticker();
2323 			break;
2324 
2325 		case GS_INTERMISSION:
2326 			if (run)
2327 				Y_Ticker();
2328 			HU_Ticker();
2329 			break;
2330 
2331 		case GS_TIMEATTACK:
2332 			F_MenuPresTicker(run);
2333 			break;
2334 
2335 		case GS_INTRO:
2336 			if (run)
2337 				F_IntroTicker();
2338 			break;
2339 
2340 		case GS_ENDING:
2341 			if (run)
2342 				F_EndingTicker();
2343 			HU_Ticker();
2344 			break;
2345 
2346 		case GS_CUTSCENE:
2347 			if (run)
2348 				F_CutsceneTicker();
2349 			HU_Ticker();
2350 			break;
2351 
2352 		case GS_GAMEEND:
2353 			if (run)
2354 				F_GameEndTicker();
2355 			break;
2356 
2357 		case GS_EVALUATION:
2358 			if (run)
2359 				F_GameEvaluationTicker();
2360 			HU_Ticker();
2361 			break;
2362 
2363 		case GS_CONTINUING:
2364 			if (run)
2365 				F_ContinueTicker();
2366 			break;
2367 
2368 		case GS_CREDITS:
2369 			if (run)
2370 				F_CreditTicker();
2371 			HU_Ticker();
2372 			break;
2373 
2374 		case GS_TITLESCREEN:
2375 			if (titlemapinaction) P_Ticker(run); // then intentionally fall through
2376 			/* FALLTHRU */
2377 		case GS_WAITINGPLAYERS:
2378 			F_MenuPresTicker(run);
2379 			F_TitleScreenTicker(run);
2380 			break;
2381 
2382 		case GS_DEDICATEDSERVER:
2383 		case GS_NULL:
2384 			break; // do nothing
2385 	}
2386 
2387 	if (run)
2388 	{
2389 		if (pausedelay && pausedelay != INT32_MIN)
2390 		{
2391 			if (pausedelay > 0)
2392 				pausedelay--;
2393 			else
2394 				pausedelay++;
2395 		}
2396 
2397 		if (camtoggledelay)
2398 			camtoggledelay--;
2399 
2400 		if (camtoggledelay2)
2401 			camtoggledelay2--;
2402 
2403 		if (gametic % NAMECHANGERATE == 0)
2404 		{
2405 			memset(player_name_changes, 0, sizeof player_name_changes);
2406 		}
2407 	}
2408 }
2409 
2410 //
2411 // PLAYER STRUCTURE FUNCTIONS
2412 // also see P_SpawnPlayer in P_Things
2413 //
2414 
2415 //
2416 // G_PlayerFinishLevel
2417 // Called when a player completes a level.
2418 //
G_PlayerFinishLevel(INT32 player)2419 static inline void G_PlayerFinishLevel(INT32 player)
2420 {
2421 	player_t *p;
2422 
2423 	p = &players[player];
2424 
2425 	memset(p->powers, 0, sizeof (p->powers));
2426 	p->ringweapons = 0;
2427 
2428 	p->mo->flags2 &= ~MF2_SHADOW; // cancel invisibility
2429 	P_FlashPal(p, 0, 0); // Resets
2430 	p->starpostscale = 0;
2431 	p->starpostangle = 0;
2432 	p->starposttime = 0;
2433 	p->starpostx = 0;
2434 	p->starposty = 0;
2435 	p->starpostz = 0;
2436 	p->starpostnum = 0;
2437 
2438 	if (rendermode == render_soft)
2439 		V_SetPaletteLump(GetPalette()); // Reset the palette
2440 }
2441 
2442 //
2443 // G_PlayerReborn
2444 // Called after a player dies. Almost everything is cleared and initialized.
2445 //
G_PlayerReborn(INT32 player,boolean betweenmaps)2446 void G_PlayerReborn(INT32 player, boolean betweenmaps)
2447 {
2448 	player_t *p;
2449 	INT32 score;
2450 	INT32 lives;
2451 	INT32 continues;
2452 	fixed_t camerascale;
2453 	fixed_t shieldscale;
2454 	UINT8 charability;
2455 	UINT8 charability2;
2456 	fixed_t normalspeed;
2457 	fixed_t runspeed;
2458 	UINT8 thrustfactor;
2459 	UINT8 accelstart;
2460 	UINT8 acceleration;
2461 	INT32 charflags;
2462 	INT32 pflags;
2463 	UINT32 thokitem;
2464 	UINT32 spinitem;
2465 	UINT32 revitem;
2466 	UINT32 followitem;
2467 	fixed_t actionspd;
2468 	fixed_t mindash;
2469 	fixed_t maxdash;
2470 	INT32 ctfteam;
2471 	INT32 starposttime;
2472 	INT16 starpostx;
2473 	INT16 starposty;
2474 	INT16 starpostz;
2475 	INT32 starpostnum;
2476 	INT32 starpostangle;
2477 	fixed_t starpostscale;
2478 	fixed_t jumpfactor;
2479 	fixed_t height;
2480 	fixed_t spinheight;
2481 	INT32 exiting;
2482 	tic_t dashmode;
2483 	INT16 numboxes;
2484 	INT16 totalring;
2485 	UINT8 laps;
2486 	UINT8 mare;
2487 	UINT16 skincolor;
2488 	INT32 skin;
2489 	UINT32 availabilities;
2490 	tic_t jointime;
2491 	tic_t quittime;
2492 	boolean spectator;
2493 	boolean outofcoop;
2494 	INT16 bot;
2495 	SINT8 pity;
2496 	INT16 rings;
2497 	INT16 spheres;
2498 	INT16 playerangleturn;
2499 	INT16 oldrelangleturn;
2500 
2501 	score = players[player].score;
2502 	lives = players[player].lives;
2503 	continues = players[player].continues;
2504 	ctfteam = players[player].ctfteam;
2505 	exiting = players[player].exiting;
2506 	jointime = players[player].jointime;
2507 	quittime = players[player].quittime;
2508 	spectator = players[player].spectator;
2509 	outofcoop = players[player].outofcoop;
2510 	pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER));
2511 	playerangleturn = players[player].angleturn;
2512 	oldrelangleturn = players[player].oldrelangleturn;
2513 
2514 	if (!betweenmaps)
2515 		pflags |= (players[player].pflags & PF_FINISHED);
2516 
2517 	// As long as we're not in multiplayer, carry over cheatcodes from map to map
2518 	if (!(netgame || multiplayer))
2519 		pflags |= (players[player].pflags & (PF_GODMODE|PF_NOCLIP|PF_INVIS));
2520 
2521 	dashmode = players[player].dashmode;
2522 
2523 	numboxes = players[player].numboxes;
2524 	laps = players[player].laps;
2525 	totalring = players[player].totalring;
2526 
2527 	skincolor = players[player].skincolor;
2528 	skin = players[player].skin;
2529 	availabilities = players[player].availabilities;
2530 	camerascale = players[player].camerascale;
2531 	shieldscale = players[player].shieldscale;
2532 	charability = players[player].charability;
2533 	charability2 = players[player].charability2;
2534 	normalspeed = players[player].normalspeed;
2535 	runspeed = players[player].runspeed;
2536 	thrustfactor = players[player].thrustfactor;
2537 	accelstart = players[player].accelstart;
2538 	acceleration = players[player].acceleration;
2539 	charflags = players[player].charflags;
2540 
2541 	starposttime = players[player].starposttime;
2542 	starpostx = players[player].starpostx;
2543 	starposty = players[player].starposty;
2544 	starpostz = players[player].starpostz;
2545 	starpostnum = players[player].starpostnum;
2546 	starpostangle = players[player].starpostangle;
2547 	starpostscale = players[player].starpostscale;
2548 	jumpfactor = players[player].jumpfactor;
2549 	height = players[player].height;
2550 	spinheight = players[player].spinheight;
2551 	thokitem = players[player].thokitem;
2552 	spinitem = players[player].spinitem;
2553 	revitem = players[player].revitem;
2554 	followitem = players[player].followitem;
2555 	actionspd = players[player].actionspd;
2556 	mindash = players[player].mindash;
2557 	maxdash = players[player].maxdash;
2558 
2559 	mare = players[player].mare;
2560 	bot = players[player].bot;
2561 	pity = players[player].pity;
2562 
2563 	if (betweenmaps || !G_IsSpecialStage(gamemap))
2564 	{
2565 		rings = (ultimatemode ? 0 : mapheaderinfo[gamemap-1]->startrings);
2566 		spheres = 0;
2567 	}
2568 	else
2569 	{
2570 		rings = players[player].rings;
2571 		spheres = players[player].spheres;
2572 	}
2573 
2574 	p = &players[player];
2575 	memset(p, 0, sizeof (*p));
2576 
2577 	p->score = score;
2578 	p->lives = lives;
2579 	p->continues = continues;
2580 	p->pflags = pflags;
2581 	p->ctfteam = ctfteam;
2582 	p->jointime = jointime;
2583 	p->quittime = quittime;
2584 	p->spectator = spectator;
2585 	p->outofcoop = outofcoop;
2586 	p->angleturn = playerangleturn;
2587 	p->oldrelangleturn = oldrelangleturn;
2588 
2589 	// save player config truth reborn
2590 	p->skincolor = skincolor;
2591 	p->skin = skin;
2592 	p->availabilities = availabilities;
2593 	p->camerascale = camerascale;
2594 	p->shieldscale = shieldscale;
2595 	p->charability = charability;
2596 	p->charability2 = charability2;
2597 	p->normalspeed = normalspeed;
2598 	p->runspeed = runspeed;
2599 	p->thrustfactor = thrustfactor;
2600 	p->accelstart = accelstart;
2601 	p->acceleration = acceleration;
2602 	p->charflags = charflags;
2603 	p->thokitem = thokitem;
2604 	p->spinitem = spinitem;
2605 	p->revitem = revitem;
2606 	p->followitem = followitem;
2607 	p->actionspd = actionspd;
2608 	p->mindash = mindash;
2609 	p->maxdash = maxdash;
2610 
2611 	p->starposttime = starposttime;
2612 	p->starpostx = starpostx;
2613 	p->starposty = starposty;
2614 	p->starpostz = starpostz;
2615 	p->starpostnum = starpostnum;
2616 	p->starpostangle = starpostangle;
2617 	p->starpostscale = starpostscale;
2618 	p->jumpfactor = jumpfactor;
2619 	p->height = height;
2620 	p->spinheight = spinheight;
2621 	p->exiting = exiting;
2622 
2623 	p->dashmode = dashmode;
2624 
2625 	p->numboxes = numboxes;
2626 	p->laps = laps;
2627 	p->totalring = totalring;
2628 
2629 	p->mare = mare;
2630 	if (bot)
2631 		p->bot = 1; // reset to AI-controlled
2632 	p->pity = pity;
2633 	p->rings = rings;
2634 	p->spheres = spheres;
2635 
2636 	// Don't do anything immediately
2637 	p->pflags |= PF_SPINDOWN;
2638 	p->pflags |= PF_ATTACKDOWN;
2639 	p->pflags |= PF_JUMPDOWN;
2640 
2641 	p->playerstate = PST_LIVE;
2642 	p->panim = PA_IDLE; // standing animation
2643 
2644 	//if ((netgame || multiplayer) && !p->spectator) -- moved into P_SpawnPlayer to account for forced changes there
2645 		//p->powers[pw_flashing] = flashingtics-1; // Babysitting deterrent
2646 
2647 	// Check to make sure their color didn't change somehow...
2648 	if (G_GametypeHasTeams())
2649 	{
2650 		if (p->ctfteam == 1 && p->skincolor != skincolor_redteam)
2651 		{
2652 			if (p == &players[consoleplayer])
2653 				CV_SetValue(&cv_playercolor, skincolor_redteam);
2654 			else if (p == &players[secondarydisplayplayer])
2655 				CV_SetValue(&cv_playercolor2, skincolor_redteam);
2656 		}
2657 		else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam)
2658 		{
2659 			if (p == &players[consoleplayer])
2660 				CV_SetValue(&cv_playercolor, skincolor_blueteam);
2661 			else if (p == &players[secondarydisplayplayer])
2662 				CV_SetValue(&cv_playercolor2, skincolor_blueteam);
2663 		}
2664 	}
2665 
2666 	if (betweenmaps)
2667 		return;
2668 
2669 	if (p-players == consoleplayer)
2670 	{
2671 		if (mapmusflags & MUSIC_RELOADRESET)
2672 		{
2673 			strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7);
2674 			mapmusname[6] = 0;
2675 			mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK);
2676 			mapmusposition = mapheaderinfo[gamemap-1]->muspos;
2677 		}
2678 
2679 		// This is in S_Start, but this was not here previously.
2680 		// if (RESETMUSIC)
2681 		// 	S_StopMusic();
2682 		S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
2683 	}
2684 
2685 	if (gametyperules & GTR_EMERALDHUNT)
2686 		P_FindEmerald(); // scan for emeralds to hunt for
2687 
2688 	// If NiGHTS, find lowest mare to start with.
2689 	p->mare = P_FindLowestMare();
2690 
2691 	CONS_Debug(DBG_NIGHTS, M_GetText("Current mare is %d\n"), p->mare);
2692 
2693 	if (p->mare == 255)
2694 		p->mare = 0;
2695 }
2696 
2697 //
2698 // G_CheckSpot
2699 // Returns false if the player cannot be respawned
2700 // at the given mapthing_t spot
2701 // because something is occupying it
2702 //
G_CheckSpot(INT32 playernum,mapthing_t * mthing)2703 static boolean G_CheckSpot(INT32 playernum, mapthing_t *mthing)
2704 {
2705 	fixed_t x;
2706 	fixed_t y;
2707 	INT32 i;
2708 
2709 	// maybe there is no player start
2710 	if (!mthing)
2711 		return false;
2712 
2713 	if (!players[playernum].mo)
2714 	{
2715 		// first spawn of level
2716 		for (i = 0; i < playernum; i++)
2717 			if (playeringame[i] && players[i].mo
2718 				&& players[i].mo->x == mthing->x << FRACBITS
2719 				&& players[i].mo->y == mthing->y << FRACBITS)
2720 			{
2721 				return false;
2722 			}
2723 		return true;
2724 	}
2725 
2726 	x = mthing->x << FRACBITS;
2727 	y = mthing->y << FRACBITS;
2728 
2729 	if (!P_CheckPosition(players[playernum].mo, x, y))
2730 		return false;
2731 
2732 	return true;
2733 }
2734 
2735 //
2736 // G_SpawnPlayer
2737 // Spawn a player in a spot appropriate for the gametype --
2738 // or a not-so-appropriate spot, if it initially fails
2739 // due to a lack of starts open or something.
2740 //
G_SpawnPlayer(INT32 playernum)2741 void G_SpawnPlayer(INT32 playernum)
2742 {
2743 	if (!playeringame[playernum])
2744 		return;
2745 
2746 	P_SpawnPlayer(playernum);
2747 	G_MovePlayerToSpawnOrStarpost(playernum);
2748 	LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :)
2749 }
2750 
G_MovePlayerToSpawnOrStarpost(INT32 playernum)2751 void G_MovePlayerToSpawnOrStarpost(INT32 playernum)
2752 {
2753 	if (players[playernum].starposttime)
2754 		P_MovePlayerToStarpost(playernum);
2755 	else
2756 		P_MovePlayerToSpawn(playernum, G_FindMapStart(playernum));
2757 }
2758 
G_FindCTFStart(INT32 playernum)2759 mapthing_t *G_FindCTFStart(INT32 playernum)
2760 {
2761 	INT32 i,j;
2762 
2763 	if (!numredctfstarts && !numbluectfstarts) //why even bother, eh?
2764 	{
2765 		if ((gametyperules & GTR_TEAMFLAGS) && (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer)))
2766 			CONS_Alert(CONS_WARNING, M_GetText("No CTF starts in this map!\n"));
2767 		return NULL;
2768 	}
2769 
2770 	if ((!players[playernum].ctfteam && numredctfstarts && (!numbluectfstarts || P_RandomChance(FRACUNIT/2))) || players[playernum].ctfteam == 1) //red
2771 	{
2772 		if (!numredctfstarts)
2773 		{
2774 			if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2775 				CONS_Alert(CONS_WARNING, M_GetText("No Red Team starts in this map!\n"));
2776 			return NULL;
2777 		}
2778 
2779 		for (j = 0; j < 32; j++)
2780 		{
2781 			i = P_RandomKey(numredctfstarts);
2782 			if (G_CheckSpot(playernum, redctfstarts[i]))
2783 				return redctfstarts[i];
2784 		}
2785 
2786 		if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2787 			CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Red Team starts!\n"));
2788 		return NULL;
2789 	}
2790 	else if (!players[playernum].ctfteam || players[playernum].ctfteam == 2) //blue
2791 	{
2792 		if (!numbluectfstarts)
2793 		{
2794 			if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2795 				CONS_Alert(CONS_WARNING, M_GetText("No Blue Team starts in this map!\n"));
2796 			return NULL;
2797 		}
2798 
2799 		for (j = 0; j < 32; j++)
2800 		{
2801 			i = P_RandomKey(numbluectfstarts);
2802 			if (G_CheckSpot(playernum, bluectfstarts[i]))
2803 				return bluectfstarts[i];
2804 		}
2805 		if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2806 			CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Blue Team starts!\n"));
2807 		return NULL;
2808 	}
2809 	//should never be reached but it gets stuff to shut up
2810 	return NULL;
2811 }
2812 
G_FindMatchStart(INT32 playernum)2813 mapthing_t *G_FindMatchStart(INT32 playernum)
2814 {
2815 	INT32 i, j;
2816 
2817 	if (numdmstarts)
2818 	{
2819 		for (j = 0; j < 64; j++)
2820 		{
2821 			i = P_RandomKey(numdmstarts);
2822 			if (G_CheckSpot(playernum, deathmatchstarts[i]))
2823 				return deathmatchstarts[i];
2824 		}
2825 		if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2826 			CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Deathmatch starts!\n"));
2827 		return NULL;
2828 	}
2829 
2830 	if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2831 		CONS_Alert(CONS_WARNING, M_GetText("No Deathmatch starts in this map!\n"));
2832 	return NULL;
2833 }
2834 
G_FindCoopStart(INT32 playernum)2835 mapthing_t *G_FindCoopStart(INT32 playernum)
2836 {
2837 	if (numcoopstarts)
2838 	{
2839 		//if there's 6 players in a map with 3 player starts, this spawns them 1/2/3/1/2/3.
2840 		if (G_CheckSpot(playernum, playerstarts[playernum % numcoopstarts]))
2841 			return playerstarts[playernum % numcoopstarts];
2842 
2843 		//Don't bother checking to see if the player 1 start is open.
2844 		//Just spawn there.
2845 		return playerstarts[0];
2846 	}
2847 
2848 	if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2849 		CONS_Alert(CONS_WARNING, M_GetText("No Co-op starts in this map!\n"));
2850 	return NULL;
2851 }
2852 
2853 // Find a Co-op start, or fallback into other types of starts.
G_FindCoopStartOrFallback(INT32 playernum)2854 static inline mapthing_t *G_FindCoopStartOrFallback(INT32 playernum)
2855 {
2856 	mapthing_t *spawnpoint = NULL;
2857 	if (!(spawnpoint = G_FindCoopStart(playernum)) // find a Co-op start
2858 	&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
2859 		spawnpoint = G_FindCTFStart(playernum); // fallback
2860 	return spawnpoint;
2861 }
2862 
2863 // Find a Match start, or fallback into other types of starts.
G_FindMatchStartOrFallback(INT32 playernum)2864 static inline mapthing_t *G_FindMatchStartOrFallback(INT32 playernum)
2865 {
2866 	mapthing_t *spawnpoint = NULL;
2867 	if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start
2868 	&& !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start
2869 		spawnpoint = G_FindCoopStart(playernum); // fallback
2870 	return spawnpoint;
2871 }
2872 
G_FindMapStart(INT32 playernum)2873 mapthing_t *G_FindMapStart(INT32 playernum)
2874 {
2875 	mapthing_t *spawnpoint;
2876 
2877 	if (!playeringame[playernum])
2878 		return NULL;
2879 
2880 	// -- Spectators --
2881 	// Order in platform gametypes: Coop->DM->CTF
2882 	// And, with deathmatch starts: DM->CTF->Coop
2883 	if (players[playernum].spectator)
2884 	{
2885 		// In platform gametypes, spawn in Co-op starts first
2886 		// Overriden by GTR_DEATHMATCHSTARTS.
2887 		if (G_PlatformGametype() && !(gametyperules & GTR_DEATHMATCHSTARTS))
2888 			spawnpoint = G_FindCoopStartOrFallback(playernum);
2889 		else
2890 			spawnpoint = G_FindMatchStartOrFallback(playernum);
2891 	}
2892 
2893 	// -- CTF --
2894 	// Order: CTF->DM->Coop
2895 	else if ((gametyperules & (GTR_TEAMFLAGS|GTR_TEAMS)) && players[playernum].ctfteam)
2896 	{
2897 		if (!(spawnpoint = G_FindCTFStart(playernum)) // find a CTF start
2898 		&& !(spawnpoint = G_FindMatchStart(playernum))) // find a DM start
2899 			spawnpoint = G_FindCoopStart(playernum); // fallback
2900 	}
2901 
2902 	// -- DM/Tag/CTF-spectator/etc --
2903 	// Order: DM->CTF->Coop
2904 	else if (G_TagGametype() ? (!(players[playernum].pflags & PF_TAGIT)) : (gametyperules & GTR_DEATHMATCHSTARTS))
2905 		spawnpoint = G_FindMatchStartOrFallback(playernum);
2906 
2907 	// -- Other game modes --
2908 	// Order: Coop->DM->CTF
2909 	else
2910 		spawnpoint = G_FindCoopStartOrFallback(playernum);
2911 
2912 	//No spawns found. ANYWHERE.
2913 	if (!spawnpoint)
2914 	{
2915 		if (nummapthings)
2916 		{
2917 			if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2918 				CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the first mapthing!\n"));
2919 			spawnpoint = &mapthings[0];
2920 		}
2921 		else
2922 		{
2923 			if (playernum == consoleplayer || (splitscreen && playernum == secondarydisplayplayer))
2924 				CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the origin!\n"));
2925 		}
2926 	}
2927 
2928 	return spawnpoint;
2929 }
2930 
2931 // Go back through all the projectiles and remove all references to the old
2932 // player mobj, replacing them with the new one.
G_ChangePlayerReferences(mobj_t * oldmo,mobj_t * newmo)2933 void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo)
2934 {
2935 	thinker_t *th;
2936 	mobj_t *mo2;
2937 
2938 	I_Assert((oldmo != NULL) && (newmo != NULL));
2939 
2940 	// scan all thinkers
2941 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
2942 	{
2943 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
2944 			continue;
2945 
2946 		mo2 = (mobj_t *)th;
2947 
2948 		if (!(mo2->flags & MF_MISSILE))
2949 			continue;
2950 
2951 		if (mo2->target == oldmo)
2952 		{
2953 			P_SetTarget(&mo2->target, newmo);
2954 			mo2->flags2 |= MF2_BEYONDTHEGRAVE; // this mobj belongs to a player who has reborn
2955 		}
2956 	}
2957 }
2958 
2959 //
2960 // G_DoReborn
2961 //
G_DoReborn(INT32 playernum)2962 void G_DoReborn(INT32 playernum)
2963 {
2964 	player_t *player = &players[playernum];
2965 	boolean resetlevel = false;
2966 	INT32 i;
2967 
2968 	if (modeattacking)
2969 	{
2970 		M_EndModeAttackRun();
2971 		return;
2972 	}
2973 
2974 	// Make sure objectplace is OFF when you first start the level!
2975 	OP_ResetObjectplace();
2976 
2977 	if (player->bot && playernum != consoleplayer)
2978 	{ // Bots respawn next to their master.
2979 		mobj_t *oldmo = NULL;
2980 
2981 		// first dissasociate the corpse
2982 		if (player->mo)
2983 		{
2984 			oldmo = player->mo;
2985 			// Don't leave your carcass stuck 10-billion feet in the ground!
2986 			P_RemoveMobj(player->mo);
2987 		}
2988 
2989 		B_RespawnBot(playernum);
2990 		if (oldmo)
2991 			G_ChangePlayerReferences(oldmo, players[playernum].mo);
2992 
2993 		return;
2994 	}
2995 
2996 	if (countdowntimeup || (!(netgame || multiplayer) && (gametyperules & GTR_CAMPAIGN)))
2997 		resetlevel = true;
2998 	else if ((G_GametypeUsesCoopLives() || G_GametypeUsesCoopStarposts()) && (netgame || multiplayer) && !G_IsSpecialStage(gamemap))
2999 	{
3000 		boolean notgameover = true;
3001 
3002 		if (G_GametypeUsesCoopLives() && (cv_cooplives.value != 0 && player->lives <= 0)) // consider game over first
3003 		{
3004 			for (i = 0; i < MAXPLAYERS; i++)
3005 			{
3006 				if (!playeringame[i])
3007 					continue;
3008 				if (players[i].exiting || players[i].lives > 0)
3009 					break;
3010 			}
3011 
3012 			if (i == MAXPLAYERS)
3013 			{
3014 				notgameover = false;
3015 				if (!countdown2)
3016 				{
3017 					// They're dead, Jim.
3018 					//nextmapoverride = spstage_start;
3019 					nextmapoverride = gamemap;
3020 					countdown2 = TICRATE;
3021 					skipstats = 2;
3022 
3023 					for (i = 0; i < MAXPLAYERS; i++)
3024 					{
3025 						if (playeringame[i])
3026 							players[i].score = 0;
3027 					}
3028 
3029 					//emeralds = 0;
3030 					tokenbits = 0;
3031 					tokenlist = 0;
3032 					token = 0;
3033 				}
3034 			}
3035 		}
3036 
3037 		if (G_GametypeUsesCoopStarposts() && (notgameover && cv_coopstarposts.value == 2))
3038 		{
3039 			for (i = 0; i < MAXPLAYERS; i++)
3040 			{
3041 				if (!playeringame[i])
3042 					continue;
3043 
3044 				if (players[i].playerstate != PST_DEAD && !players[i].spectator && players[i].mo && players[i].mo->health)
3045 					break;
3046 			}
3047 			if (i == MAXPLAYERS)
3048 				resetlevel = true;
3049 		}
3050 	}
3051 
3052 	if (resetlevel)
3053 	{
3054 		// reload the level from scratch
3055 		if (countdowntimeup)
3056 		{
3057 			for (i = 0; i < MAXPLAYERS; i++)
3058 			{
3059 				if (!playeringame[i])
3060 					continue;
3061 				players[i].starpostscale = 0;
3062 				players[i].starpostangle = 0;
3063 				players[i].starposttime = 0;
3064 				players[i].starpostx = 0;
3065 				players[i].starposty = 0;
3066 				players[i].starpostz = 0;
3067 				players[i].starpostnum = 0;
3068 			}
3069 		}
3070 		if (!countdowntimeup && (mapheaderinfo[gamemap-1]->levelflags & LF_NORELOAD) && !(marathonmode & MA_INIT))
3071 		{
3072 			P_RespawnThings();
3073 
3074 			for (i = 0; i < MAXPLAYERS; i++)
3075 			{
3076 				if (!playeringame[i])
3077 					continue;
3078 				players[i].playerstate = PST_REBORN;
3079 				P_ClearStarPost(players[i].starpostnum);
3080 			}
3081 
3082 			// Do a wipe
3083 			wipegamestate = -1;
3084 			wipestyleflags = WSF_CROSSFADE;
3085 
3086 			if (camera.chase)
3087 				P_ResetCamera(&players[displayplayer], &camera);
3088 			if (camera2.chase && splitscreen)
3089 				P_ResetCamera(&players[secondarydisplayplayer], &camera2);
3090 
3091 			// clear cmd building stuff
3092 			memset(gamekeydown, 0, sizeof (gamekeydown));
3093 			for (i = 0; i < JOYAXISSET; i++)
3094 			{
3095 				joyxmove[i] = joyymove[i] = 0;
3096 				joy2xmove[i] = joy2ymove[i] = 0;
3097 			}
3098 			mousex = mousey = 0;
3099 			mouse2x = mouse2y = 0;
3100 
3101 			// clear hud messages remains (usually from game startup)
3102 			CON_ClearHUD();
3103 
3104 			// Starpost support
3105 			for (i = 0; i < MAXPLAYERS; i++)
3106 			{
3107 				if (!playeringame[i])
3108 					continue;
3109 				G_SpawnPlayer(i);
3110 			}
3111 
3112 			// restore time in netgame (see also p_setup.c)
3113 			if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
3114 			{
3115 				// is this a hack? maybe
3116 				tic_t maxstarposttime = 0;
3117 				for (i = 0; i < MAXPLAYERS; i++)
3118 				{
3119 					if (playeringame[i] && players[i].starposttime > maxstarposttime)
3120 						maxstarposttime = players[i].starposttime;
3121 				}
3122 				leveltime = maxstarposttime;
3123 			}
3124 		}
3125 		else
3126 		{
3127 			LUAh_MapChange(gamemap);
3128 			titlecardforreload = true;
3129 			G_DoLoadLevel(true);
3130 			titlecardforreload = false;
3131 			if (metalrecording)
3132 				G_BeginMetal();
3133 			return;
3134 		}
3135 	}
3136 	else
3137 	{
3138 		// respawn at the start
3139 		mobj_t *oldmo = NULL;
3140 
3141 		// Not resetting map, so return to level music
3142 		if (!countdown2
3143 		&& player->lives <= 0
3144 		&& cv_cooplives.value == 1) // not allowed for life steal because no way to come back from zero group lives without addons, which should call this anyways
3145 			P_RestoreMultiMusic(player);
3146 
3147 		// first dissasociate the corpse
3148 		if (player->mo)
3149 		{
3150 			oldmo = player->mo;
3151 			// Don't leave your carcass stuck 10-billion feet in the ground!
3152 			P_RemoveMobj(player->mo);
3153 		}
3154 
3155 		G_SpawnPlayer(playernum);
3156 		if (oldmo)
3157 			G_ChangePlayerReferences(oldmo, players[playernum].mo);
3158 	}
3159 }
3160 
G_AddPlayer(INT32 playernum)3161 void G_AddPlayer(INT32 playernum)
3162 {
3163 	INT32 countplayers = 0, notexiting = 0;
3164 
3165 	player_t *p = &players[playernum];
3166 
3167 	// Go through the current players and make sure you have the latest starpost set
3168 	if (G_PlatformGametype() && (netgame || multiplayer))
3169 	{
3170 		INT32 i;
3171 		for (i = 0; i < MAXPLAYERS; i++)
3172 		{
3173 			if (!playeringame[i])
3174 				continue;
3175 
3176 			if (players[i].bot) // ignore dumb, stupid tails
3177 				continue;
3178 
3179 			countplayers++;
3180 
3181 			if (!players[i].exiting)
3182 				notexiting++;
3183 
3184 			if (!(cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (p->starpostnum < players[i].starpostnum)))
3185 				continue;
3186 
3187 			p->starpostscale = players[i].starpostscale;
3188 			p->starposttime = players[i].starposttime;
3189 			p->starpostx = players[i].starpostx;
3190 			p->starposty = players[i].starposty;
3191 			p->starpostz = players[i].starpostz;
3192 			p->starpostangle = players[i].starpostangle;
3193 			p->starpostnum = players[i].starpostnum;
3194 		}
3195 	}
3196 
3197 	p->playerstate = PST_REBORN;
3198 
3199 	p->height = mobjinfo[MT_PLAYER].height;
3200 
3201 	if (G_GametypeUsesLives() || ((netgame || multiplayer) && (gametyperules & GTR_FRIENDLY)))
3202 		p->lives = cv_startinglives.value;
3203 
3204 	if ((countplayers && !notexiting) || G_IsSpecialStage(gamemap))
3205 		P_DoPlayerExit(p);
3206 }
3207 
G_EnoughPlayersFinished(void)3208 boolean G_EnoughPlayersFinished(void)
3209 {
3210 	UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
3211 	INT32 total = 0;
3212 	INT32 exiting = 0;
3213 	INT32 i;
3214 
3215 	for (i = 0; i < MAXPLAYERS; i++)
3216 	{
3217 		if (!playeringame[i] || players[i].spectator || players[i].bot)
3218 			continue;
3219 		if (players[i].quittime > 30 * TICRATE)
3220 			continue;
3221 		if (players[i].lives <= 0)
3222 			continue;
3223 
3224 		total++;
3225 		if ((players[i].pflags & PF_FINISHED) || players[i].exiting)
3226 			exiting++;
3227 	}
3228 
3229 	if (exiting)
3230 		return exiting * 4 / total >= numneeded;
3231 	else
3232 		return false;
3233 }
3234 
G_ExitLevel(void)3235 void G_ExitLevel(void)
3236 {
3237 	if (gamestate == GS_LEVEL)
3238 	{
3239 		gameaction = ga_completed;
3240 		lastdraw = true;
3241 
3242 		// If you want your teams scrambled on map change, start the process now.
3243 		// The teams will scramble at the start of the next round.
3244 		if (cv_scrambleonchange.value && G_GametypeHasTeams())
3245 		{
3246 			if (server)
3247 				CV_SetValue(&cv_teamscramble, cv_scrambleonchange.value);
3248 		}
3249 
3250 		if (!(gametyperules & (GTR_FRIENDLY|GTR_CAMPAIGN)))
3251 			CONS_Printf(M_GetText("The round has ended.\n"));
3252 
3253 		// Remove CEcho text on round end.
3254 		HU_ClearCEcho();
3255 	}
3256 	else if (gamestate == GS_ENDING)
3257 	{
3258 		F_StartCredits();
3259 	}
3260 	else if (gamestate == GS_CREDITS)
3261 	{
3262 		F_StartGameEvaluation();
3263 	}
3264 }
3265 
3266 // See also the enum GameType in doomstat.h
3267 const char *Gametype_Names[NUMGAMETYPES] =
3268 {
3269 	"Co-op", // GT_COOP
3270 	"Competition", // GT_COMPETITION
3271 	"Race", // GT_RACE
3272 
3273 	"Match", // GT_MATCH
3274 	"Team Match", // GT_TEAMMATCH
3275 
3276 	"Tag", // GT_TAG
3277 	"Hide & Seek", // GT_HIDEANDSEEK
3278 
3279 	"CTF" // GT_CTF
3280 };
3281 
3282 // For dehacked
3283 const char *Gametype_ConstantNames[NUMGAMETYPES] =
3284 {
3285 	"GT_COOP", // GT_COOP
3286 	"GT_COMPETITION", // GT_COMPETITION
3287 	"GT_RACE", // GT_RACE
3288 
3289 	"GT_MATCH", // GT_MATCH
3290 	"GT_TEAMMATCH", // GT_TEAMMATCH
3291 
3292 	"GT_TAG", // GT_TAG
3293 	"GT_HIDEANDSEEK", // GT_HIDEANDSEEK
3294 
3295 	"GT_CTF" // GT_CTF
3296 };
3297 
3298 // Gametype rules
3299 UINT32 gametypedefaultrules[NUMGAMETYPES] =
3300 {
3301 	// Co-op
3302 	GTR_CAMPAIGN|GTR_LIVES|GTR_FRIENDLY|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES|GTR_CUTSCENES,
3303 	// Competition
3304 	GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
3305 	// Race
3306 	GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
3307 
3308 	// Match
3309 	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY,
3310 	// Team Match
3311 	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
3312 
3313 	// Tag
3314 	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
3315 	// Hide and Seek
3316 	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_STARTCOUNTDOWN|GTR_HIDEFROZEN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
3317 
3318 	// CTF
3319 	GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
3320 };
3321 
3322 //
3323 // G_SetGametype
3324 //
3325 // Set a new gametype, also setting gametype rules accordingly. Yay!
3326 //
G_SetGametype(INT16 gtype)3327 void G_SetGametype(INT16 gtype)
3328 {
3329 	gametype = gtype;
3330 	gametyperules = gametypedefaultrules[gametype];
3331 }
3332 
3333 //
3334 // G_AddGametype
3335 //
3336 // Add a gametype. Returns the new gametype number.
3337 //
G_AddGametype(UINT32 rules)3338 INT16 G_AddGametype(UINT32 rules)
3339 {
3340 	INT16 newgtype = gametypecount;
3341 	gametypecount++;
3342 
3343 	// Set gametype rules.
3344 	gametypedefaultrules[newgtype] = rules;
3345 	Gametype_Names[newgtype] = "???";
3346 
3347 	// Update gametype_cons_t accordingly.
3348 	G_UpdateGametypeSelections();
3349 
3350 	return newgtype;
3351 }
3352 
3353 //
3354 // G_AddGametypeConstant
3355 //
3356 // Self-explanatory. Filters out "bad" characters.
3357 //
G_AddGametypeConstant(INT16 gtype,const char * newgtconst)3358 void G_AddGametypeConstant(INT16 gtype, const char *newgtconst)
3359 {
3360 	size_t r = 0; // read
3361 	size_t w = 0; // write
3362 	char *gtconst = Z_Calloc(strlen(newgtconst) + 4, PU_STATIC, NULL);
3363 	char *tmpconst = Z_Calloc(strlen(newgtconst) + 1, PU_STATIC, NULL);
3364 
3365 	// Copy the gametype name.
3366 	strcpy(tmpconst, newgtconst);
3367 
3368 	// Make uppercase.
3369 	strupr(tmpconst);
3370 
3371 	// Prepare to write the new constant string now.
3372 	strcpy(gtconst, "GT_");
3373 
3374 	// Remove characters that will not be allowed in the constant string.
3375 	for (; r < strlen(tmpconst); r++)
3376 	{
3377 		boolean writechar = true;
3378 		char rc = tmpconst[r];
3379 		switch (rc)
3380 		{
3381 			// Space, at sign and question mark
3382 			case ' ':
3383 			case '@':
3384 			case '?':
3385 			// Used for operations
3386 			case '+':
3387 			case '-':
3388 			case '*':
3389 			case '/':
3390 			case '%':
3391 			case '^':
3392 			case '&':
3393 			case '!':
3394 			// Part of Lua's syntax
3395 			case '#':
3396 			case '=':
3397 			case '~':
3398 			case '<':
3399 			case '>':
3400 			case '(':
3401 			case ')':
3402 			case '{':
3403 			case '}':
3404 			case '[':
3405 			case ']':
3406 			case ':':
3407 			case ';':
3408 			case ',':
3409 			case '.':
3410 				writechar = false;
3411 				break;
3412 		}
3413 		if (writechar)
3414 		{
3415 			gtconst[3 + w] = rc;
3416 			w++;
3417 		}
3418 	}
3419 
3420 	// Free the temporary string.
3421 	Z_Free(tmpconst);
3422 
3423 	// Finally, set the constant string.
3424 	Gametype_ConstantNames[gtype] = gtconst;
3425 }
3426 
3427 //
3428 // G_UpdateGametypeSelections
3429 //
3430 // Updates gametype_cons_t.
3431 //
G_UpdateGametypeSelections(void)3432 void G_UpdateGametypeSelections(void)
3433 {
3434 	INT32 i;
3435 	for (i = 0; i < gametypecount; i++)
3436 	{
3437 		gametype_cons_t[i].value = i;
3438 		gametype_cons_t[i].strvalue = Gametype_Names[i];
3439 	}
3440 	gametype_cons_t[NUMGAMETYPES].value = 0;
3441 	gametype_cons_t[NUMGAMETYPES].strvalue = NULL;
3442 }
3443 
3444 //
3445 // G_SetGametypeDescription
3446 //
3447 // Set a description for the specified gametype.
3448 // (Level platter)
3449 //
G_SetGametypeDescription(INT16 gtype,char * descriptiontext,UINT8 leftcolor,UINT8 rightcolor)3450 void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolor, UINT8 rightcolor)
3451 {
3452 	if (descriptiontext != NULL)
3453 		strncpy(gametypedesc[gtype].notes, descriptiontext, 441);
3454 	gametypedesc[gtype].col[0] = leftcolor;
3455 	gametypedesc[gtype].col[1] = rightcolor;
3456 }
3457 
3458 // Gametype rankings
3459 INT16 gametyperankings[NUMGAMETYPES] =
3460 {
3461 	GT_COOP,
3462 	GT_COMPETITION,
3463 	GT_RACE,
3464 
3465 	GT_MATCH,
3466 	GT_TEAMMATCH,
3467 
3468 	GT_TAG,
3469 	GT_HIDEANDSEEK,
3470 
3471 	GT_CTF,
3472 };
3473 
3474 // Gametype to TOL (Type Of Level)
3475 UINT32 gametypetol[NUMGAMETYPES] =
3476 {
3477 	TOL_COOP, // Co-op
3478 	TOL_COMPETITION, // Competition
3479 	TOL_RACE, // Race
3480 
3481 	TOL_MATCH, // Match
3482 	TOL_MATCH, // Team Match
3483 
3484 	TOL_TAG, // Tag
3485 	TOL_TAG, // Hide and Seek
3486 
3487 	TOL_CTF, // CTF
3488 };
3489 
3490 tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = {
3491 	{"SOLO",TOL_SP},
3492 	{"SP",TOL_SP},
3493 	{"SINGLEPLAYER",TOL_SP},
3494 	{"SINGLE",TOL_SP},
3495 
3496 	{"COOP",TOL_COOP},
3497 	{"CO-OP",TOL_COOP},
3498 
3499 	{"COMPETITION",TOL_COMPETITION},
3500 	{"RACE",TOL_RACE},
3501 
3502 	{"MATCH",TOL_MATCH},
3503 	{"TAG",TOL_TAG},
3504 	{"CTF",TOL_CTF},
3505 
3506 	{"2D",TOL_2D},
3507 	{"MARIO",TOL_MARIO},
3508 	{"NIGHTS",TOL_NIGHTS},
3509 	{"OLDBRAK",TOL_ERZ3},
3510 	{"ERZ3",TOL_ERZ3},
3511 
3512 	{"XMAS",TOL_XMAS},
3513 	{"CHRISTMAS",TOL_XMAS},
3514 	{"WINTER",TOL_XMAS},
3515 
3516 	{NULL, 0}
3517 };
3518 
3519 UINT32 lastcustomtol = (TOL_XMAS<<1);
3520 
3521 //
3522 // G_AddTOL
3523 //
3524 // Adds a type of level.
3525 //
G_AddTOL(UINT32 newtol,const char * tolname)3526 void G_AddTOL(UINT32 newtol, const char *tolname)
3527 {
3528 	INT32 i;
3529 	for (i = 0; TYPEOFLEVEL[i].name; i++)
3530 		;
3531 
3532 	TYPEOFLEVEL[i].name = Z_StrDup(tolname);
3533 	TYPEOFLEVEL[i].flag = newtol;
3534 }
3535 
3536 //
3537 // G_AddGametypeTOL
3538 //
3539 // Assigns a type of level to a gametype.
3540 //
G_AddGametypeTOL(INT16 gtype,UINT32 newtol)3541 void G_AddGametypeTOL(INT16 gtype, UINT32 newtol)
3542 {
3543 	gametypetol[gtype] = newtol;
3544 }
3545 
3546 //
3547 // G_GetGametypeByName
3548 //
3549 // Returns the number for the given gametype name string, or -1 if not valid.
3550 //
G_GetGametypeByName(const char * gametypestr)3551 INT32 G_GetGametypeByName(const char *gametypestr)
3552 {
3553 	INT32 i;
3554 
3555 	for (i = 0; i < gametypecount; i++)
3556 		if (!stricmp(gametypestr, Gametype_Names[i]))
3557 			return i;
3558 
3559 	return -1; // unknown gametype
3560 }
3561 
3562 //
3563 // G_IsSpecialStage
3564 //
3565 // Returns TRUE if
3566 // the given map is a special stage.
3567 //
G_IsSpecialStage(INT32 mapnum)3568 boolean G_IsSpecialStage(INT32 mapnum)
3569 {
3570 	if (modeattacking == ATTACKING_RECORD)
3571 		return false;
3572 	if (mapnum >= sstage_start && mapnum <= sstage_end)
3573 		return true;
3574 	if (mapnum >= smpstage_start && mapnum <= smpstage_end)
3575 		return true;
3576 
3577 	return false;
3578 }
3579 
3580 //
3581 // G_GametypeUsesLives
3582 //
3583 // Returns true if the current gametype uses
3584 // the lives system.  False otherwise.
3585 //
G_GametypeUsesLives(void)3586 boolean G_GametypeUsesLives(void)
3587 {
3588 	// Coop, Competitive
3589 	if ((gametyperules & GTR_LIVES)
3590 	 && !(modeattacking || metalrecording) // No lives in Time Attack
3591 	 && !G_IsSpecialStage(gamemap)
3592 	 && !(maptol & TOL_NIGHTS)) // No lives in NiGHTS
3593 		return true;
3594 	return false;
3595 }
3596 
3597 //
3598 // G_GametypeUsesCoopLives
3599 //
3600 // Returns true if the current gametype uses
3601 // the cooplives CVAR.  False otherwise.
3602 //
G_GametypeUsesCoopLives(void)3603 boolean G_GametypeUsesCoopLives(void)
3604 {
3605 	return (gametyperules & (GTR_LIVES|GTR_FRIENDLY)) == (GTR_LIVES|GTR_FRIENDLY);
3606 }
3607 
3608 //
3609 // G_GametypeUsesCoopStarposts
3610 //
3611 // Returns true if the current gametype uses
3612 // the coopstarposts CVAR.  False otherwise.
3613 //
G_GametypeUsesCoopStarposts(void)3614 boolean G_GametypeUsesCoopStarposts(void)
3615 {
3616 	return (gametyperules & GTR_FRIENDLY);
3617 }
3618 
3619 //
3620 // G_GametypeHasTeams
3621 //
3622 // Returns true if the current gametype uses
3623 // Red/Blue teams.  False otherwise.
3624 //
G_GametypeHasTeams(void)3625 boolean G_GametypeHasTeams(void)
3626 {
3627 	return (gametyperules & GTR_TEAMS);
3628 }
3629 
3630 //
3631 // G_GametypeHasSpectators
3632 //
3633 // Returns true if the current gametype supports
3634 // spectators.  False otherwise.
3635 //
G_GametypeHasSpectators(void)3636 boolean G_GametypeHasSpectators(void)
3637 {
3638 	return (gametyperules & GTR_SPECTATORS);
3639 }
3640 
3641 //
3642 // G_RingSlingerGametype
3643 //
3644 // Returns true if the current gametype supports firing rings.
3645 // ANY gametype can be a ringslinger gametype, just flick a switch.
3646 //
G_RingSlingerGametype(void)3647 boolean G_RingSlingerGametype(void)
3648 {
3649 	return ((gametyperules & GTR_RINGSLINGER) || (cv_ringslinger.value));
3650 }
3651 
3652 //
3653 // G_PlatformGametype
3654 //
3655 // Returns true if a gametype is a more traditional platforming-type.
3656 //
G_PlatformGametype(void)3657 boolean G_PlatformGametype(void)
3658 {
3659 	return (!(gametyperules & GTR_RINGSLINGER));
3660 }
3661 
3662 //
3663 // G_CoopGametype
3664 //
3665 // Returns true if a gametype is a Co-op gametype.
3666 //
G_CoopGametype(void)3667 boolean G_CoopGametype(void)
3668 {
3669 	return ((gametyperules & (GTR_FRIENDLY|GTR_CAMPAIGN)) == (GTR_FRIENDLY|GTR_CAMPAIGN));
3670 }
3671 
3672 //
3673 // G_TagGametype
3674 //
3675 // For Jazz's Tag/HnS modes that have a lot of special cases..
3676 //
G_TagGametype(void)3677 boolean G_TagGametype(void)
3678 {
3679 	return (gametyperules & GTR_TAG);
3680 }
3681 
3682 //
3683 // G_CompetitionGametype
3684 //
3685 // For gametypes that are race gametypes, and have lives.
3686 //
G_CompetitionGametype(void)3687 boolean G_CompetitionGametype(void)
3688 {
3689 	return ((gametyperules & GTR_RACE) && (gametyperules & GTR_LIVES));
3690 }
3691 
3692 /** Get the typeoflevel flag needed to indicate support of a gametype.
3693   * In single-player, this always returns TOL_SP.
3694   * \param gametype The gametype for which support is desired.
3695   * \return The typeoflevel flag to check for that gametype.
3696   * \author Graue <graue@oceanbase.org>
3697   */
G_TOLFlag(INT32 pgametype)3698 UINT32 G_TOLFlag(INT32 pgametype)
3699 {
3700 	if (!multiplayer)
3701 		return TOL_SP;
3702 	return gametypetol[pgametype];
3703 }
3704 
3705 /** Select a random map with the given typeoflevel flags.
3706   * If no map has those flags, this arbitrarily gives you map 1.
3707   * \param tolflags The typeoflevel flags to insist on. Other bits may
3708   *                 be on too, but all of these must be on.
3709   * \return A random map with those flags, 1-based, or 1 if no map
3710   *         has those flags.
3711   * \author Graue <graue@oceanbase.org>
3712   */
RandMap(UINT32 tolflags,INT16 pprevmap)3713 static INT16 RandMap(UINT32 tolflags, INT16 pprevmap)
3714 {
3715 	INT16 *okmaps = Z_Malloc(NUMMAPS * sizeof(INT16), PU_STATIC, NULL);
3716 	INT32 numokmaps = 0;
3717 	INT16 ix;
3718 
3719 	// Find all the maps that are ok and and put them in an array.
3720 	for (ix = 0; ix < NUMMAPS; ix++)
3721 		if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags
3722 		 && ix != pprevmap // Don't pick the same map.
3723 		 && (dedicated || !M_MapLocked(ix+1)) // Don't pick locked maps.
3724 		)
3725 			okmaps[numokmaps++] = ix;
3726 
3727 	if (numokmaps == 0)
3728 		ix = 0; // Sorry, none match. You get MAP01.
3729 	else
3730 		ix = okmaps[M_RandomKey(numokmaps)];
3731 
3732 	Z_Free(okmaps);
3733 
3734 	return ix;
3735 }
3736 
3737 //
3738 // G_UpdateVisited
3739 //
G_UpdateVisited(void)3740 static void G_UpdateVisited(void)
3741 {
3742 	boolean spec = G_IsSpecialStage(gamemap);
3743 	// Update visitation flags?
3744 	if ((!modifiedgame || savemoddata) // Not modified
3745 		&& !multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode
3746 		&& !(spec && stagefailed)) // Not failed the special stage
3747 	{
3748 		UINT8 earnedEmblems;
3749 
3750 		// Update visitation flags
3751 		mapvisited[gamemap-1] |= MV_BEATEN;
3752 		// eh, what the hell
3753 		if (ultimatemode)
3754 			mapvisited[gamemap-1] |= MV_ULTIMATE;
3755 		// may seem incorrect but IS possible in what the main game uses as mp special stages, and nummaprings will be -1 in NiGHTS
3756 		if (nummaprings > 0 && players[consoleplayer].rings >= nummaprings)
3757 		{
3758 			mapvisited[gamemap-1] |= MV_PERFECT;
3759 			if (modeattacking)
3760 				mapvisited[gamemap-1] |= MV_PERFECTRA;
3761 		}
3762 		if (!spec)
3763 		{
3764 			// not available to special stages because they can only really be done in one order in an unmodified game, so impossible for first six and trivial for seventh
3765 			if (ALL7EMERALDS(emeralds))
3766 				mapvisited[gamemap-1] |= MV_ALLEMERALDS;
3767 		}
3768 
3769 		if (modeattacking == ATTACKING_RECORD)
3770 			G_UpdateRecordReplays();
3771 		else if (modeattacking == ATTACKING_NIGHTS)
3772 			G_SetNightsRecords();
3773 
3774 		if ((earnedEmblems = M_CompletionEmblems()))
3775 			CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
3776 	}
3777 }
3778 
CanSaveLevel(INT32 mapnum)3779 static boolean CanSaveLevel(INT32 mapnum)
3780 {
3781 	// You can never save in a special stage.
3782 	if (G_IsSpecialStage(mapnum))
3783 		return false;
3784 
3785 	// If the game is complete for this save slot, then any level can save!
3786 	if (gamecomplete)
3787 		return true;
3788 
3789 	// Be kind with Marathon Mode live event backups.
3790 	if (marathonmode)
3791 		return true;
3792 
3793 	// Any levels that have the savegame flag can save normally.
3794 	return (mapheaderinfo[mapnum-1] && (mapheaderinfo[mapnum-1]->levelflags & LF_SAVEGAME));
3795 }
3796 
G_HandleSaveLevel(void)3797 static void G_HandleSaveLevel(void)
3798 {
3799 	// do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c
3800 	if (nextmap >= 1100-1)
3801 	{
3802 		if (!gamecomplete)
3803 			gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
3804 		if (cursaveslot > 0)
3805 		{
3806 			if (marathonmode)
3807 			{
3808 				// don't keep a backup around when the run is done!
3809 				if (FIL_FileExists(liveeventbackup))
3810 					remove(liveeventbackup);
3811 				cursaveslot = 0;
3812 			}
3813 			else if ((!modifiedgame || savemoddata) && !(netgame || multiplayer || ultimatemode || demorecording || metalrecording || modeattacking))
3814 				G_SaveGame((UINT32)cursaveslot, spstage_start);
3815 		}
3816 	}
3817 	// and doing THIS here means you don't lose your progress if you close the game mid-intermission
3818 	else if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking)
3819 		&& (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(lastmap+1))
3820 		G_SaveGame((UINT32)cursaveslot, lastmap+1); // not nextmap+1 to route around special stages
3821 }
3822 
3823 //
3824 // G_DoCompleted
3825 //
G_DoCompleted(void)3826 static void G_DoCompleted(void)
3827 {
3828 	INT32 i;
3829 	boolean spec = G_IsSpecialStage(gamemap);
3830 
3831 	tokenlist = 0; // Reset the list
3832 
3833 	if (modeattacking && pausedelay)
3834 		pausedelay = 0;
3835 
3836 	gameaction = ga_nothing;
3837 
3838 	if (metalplayback)
3839 		G_StopMetalDemo();
3840 	if (metalrecording)
3841 		G_StopMetalRecording(false);
3842 
3843 	for (i = 0; i < MAXPLAYERS; i++)
3844 		if (playeringame[i])
3845 			G_PlayerFinishLevel(i); // take away cards and stuff
3846 
3847 	if (automapactive)
3848 		AM_Stop();
3849 
3850 	S_StopSounds();
3851 
3852 	prevmap = (INT16)(gamemap-1);
3853 
3854 	// go to next level
3855 	// nextmap is 0-based, unlike gamemap
3856 	if (nextmapoverride != 0)
3857 		nextmap = (INT16)(nextmapoverride-1);
3858 	else if (marathonmode && mapheaderinfo[gamemap-1]->marathonnext)
3859 		nextmap = (INT16)(mapheaderinfo[gamemap-1]->marathonnext-1);
3860 	else
3861 	{
3862 		nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
3863 		if (marathonmode && nextmap == spmarathon_start-1)
3864 			nextmap = 1100-1; // No infinite loop for you
3865 	}
3866 
3867 	// If nextmap is actually going to get used, make sure it points to
3868 	// a map of the proper gametype -- skip levels that don't support
3869 	// the current gametype. (Helps avoid playing boss levels in Race,
3870 	// for instance).
3871 	if (!spec || nextmapoverride)
3872 	{
3873 		if (nextmap >= 0 && nextmap < NUMMAPS)
3874 		{
3875 			register INT16 cm = nextmap;
3876 			UINT32 tolflag = G_TOLFlag(gametype);
3877 			UINT8 visitedmap[(NUMMAPS+7)/8];
3878 
3879 			memset(visitedmap, 0, sizeof (visitedmap));
3880 
3881 			while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag))
3882 			{
3883 				visitedmap[cm/8] |= (1<<(cm&7));
3884 				if (!mapheaderinfo[cm])
3885 					cm = -1; // guarantee error execution
3886 				else if (marathonmode && mapheaderinfo[cm]->marathonnext)
3887 					cm = (INT16)(mapheaderinfo[cm]->marathonnext-1);
3888 				else
3889 					cm = (INT16)(mapheaderinfo[cm]->nextlevel-1);
3890 
3891 				if (cm >= NUMMAPS || cm < 0) // out of range (either 1100ish or error)
3892 				{
3893 					cm = nextmap; //Start the loop again so that the error checking below is executed.
3894 
3895 					//Make sure the map actually exists before you try to go to it!
3896 					if ((W_CheckNumForName(G_BuildMapName(cm + 1)) == LUMPERROR))
3897 					{
3898 						CONS_Alert(CONS_ERROR, M_GetText("Next map given (MAP %d) doesn't exist! Reverting to MAP01.\n"), cm+1);
3899 						cm = 0;
3900 						break;
3901 					}
3902 				}
3903 
3904 				if (visitedmap[cm/8] & (1<<(cm&7))) // smells familiar
3905 				{
3906 					// We got stuck in a loop, came back to the map we started on
3907 					// without finding one supporting the current gametype.
3908 					// Thus, print a warning, and just use this map anyways.
3909 					CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1);
3910 					break;
3911 				}
3912 			}
3913 			nextmap = cm;
3914 		}
3915 
3916 		// wrap around in race
3917 		if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
3918 			nextmap = (INT16)(spstage_start-1);
3919 
3920 		if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1)
3921 			I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
3922 
3923 		if (!spec)
3924 			lastmap = nextmap; // Remember last map for when you come out of the special stage.
3925 	}
3926 
3927 	if ((gottoken = ((gametyperules & GTR_SPECIALSTAGES) && token)))
3928 	{
3929 		token--;
3930 
3931 		for (i = 0; i < 7; i++)
3932 			if (!(emeralds & (1<<i)))
3933 			{
3934 				nextmap = ((netgame || multiplayer) ? smpstage_start : sstage_start) + i - 1; // to special stage!
3935 				break;
3936 			}
3937 
3938 		if (i == 7)
3939 		{
3940 			gottoken = false;
3941 			token = 0;
3942 		}
3943 	}
3944 
3945 	if (spec && !gottoken && !nextmapoverride)
3946 		nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001
3947 
3948 	automapactive = false;
3949 
3950 	if (!(gametyperules & GTR_CAMPAIGN))
3951 	{
3952 		if (cv_advancemap.value == 0) // Stay on same map.
3953 			nextmap = prevmap;
3954 		else if (cv_advancemap.value == 2) // Go to random map.
3955 			nextmap = RandMap(G_TOLFlag(gametype), prevmap);
3956 	}
3957 
3958 	// We are committed to this map now.
3959 	// We may as well allocate its header if it doesn't exist
3960 	// (That is, if it's a real map)
3961 	if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
3962 		P_AllocMapHeader(nextmap);
3963 
3964 	// If the current gametype has no intermission screen set, then don't start it.
3965 	Y_DetermineIntermissionType();
3966 
3967 	if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed) || (intertype == int_none))
3968 	{
3969 		G_UpdateVisited();
3970 		G_HandleSaveLevel();
3971 		G_AfterIntermission();
3972 	}
3973 	else
3974 	{
3975 		G_SetGamestate(GS_INTERMISSION);
3976 		Y_StartIntermission();
3977 		Y_LoadIntermissionData();
3978 		G_UpdateVisited();
3979 		G_HandleSaveLevel();
3980 	}
3981 }
3982 
3983 // See also F_EndCutscene, the only other place which handles intra-map/ending transitions
G_AfterIntermission(void)3984 void G_AfterIntermission(void)
3985 {
3986 	Y_CleanupScreenBuffer();
3987 
3988 	if (modeattacking)
3989 	{
3990 		M_EndModeAttackRun();
3991 		return;
3992 	}
3993 
3994 	if (gamecomplete == 2) // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission
3995 		gamecomplete = 1;
3996 
3997 	HU_ClearCEcho();
3998 
3999 	if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene.
4000 		F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
4001 	else
4002 	{
4003 		if (nextmap < 1100-1)
4004 			G_NextLevel();
4005 		else
4006 			G_EndGame();
4007 	}
4008 }
4009 
4010 //
4011 // G_NextLevel (WorldDone)
4012 //
4013 // init next level or go to the final scene
4014 // called by end of intermission screen (y_inter)
4015 //
G_NextLevel(void)4016 void G_NextLevel(void)
4017 {
4018 	gameaction = ga_worlddone;
4019 }
4020 
G_DoWorldDone(void)4021 static void G_DoWorldDone(void)
4022 {
4023 	if (server)
4024 	{
4025 		if (gametyperules & GTR_CAMPAIGN)
4026 			// don't reset player between maps
4027 			D_MapChange(nextmap+1, gametype, ultimatemode, false, 0, false, false);
4028 		else
4029 			// resetplayer in match/chaos/tag/CTF/race for more equality
4030 			D_MapChange(nextmap+1, gametype, ultimatemode, true, 0, false, false);
4031 	}
4032 
4033 	gameaction = ga_nothing;
4034 }
4035 
4036 //
4037 // G_UseContinue
4038 //
G_UseContinue(void)4039 void G_UseContinue(void)
4040 {
4041 	if (gamestate == GS_LEVEL && !netgame && !multiplayer)
4042 	{
4043 		gameaction = ga_startcont;
4044 		lastdraw = true;
4045 	}
4046 }
4047 
G_DoStartContinue(void)4048 static void G_DoStartContinue(void)
4049 {
4050 	I_Assert(!netgame && !multiplayer);
4051 
4052 	G_PlayerFinishLevel(consoleplayer); // take away cards and stuff
4053 
4054 	F_StartContinue();
4055 	gameaction = ga_nothing;
4056 }
4057 
4058 //
4059 // G_Continue
4060 //
4061 // re-init level, used by continue and possibly countdowntimeup
4062 //
G_Continue(void)4063 void G_Continue(void)
4064 {
4065 	if (!netgame && !multiplayer)
4066 		gameaction = ga_continued;
4067 }
4068 
G_DoContinued(void)4069 static void G_DoContinued(void)
4070 {
4071 	player_t *pl = &players[consoleplayer];
4072 	I_Assert(!netgame && !multiplayer);
4073 	I_Assert(pl->continues > 0);
4074 
4075 	if (pl->continues)
4076 		pl->continues--;
4077 
4078 	// Reset score
4079 	pl->score = 0;
4080 
4081 	// Allow tokens to come back
4082 	tokenlist = 0;
4083 	token = 0;
4084 
4085 	if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && (!modifiedgame || savemoddata) && cursaveslot > 0)
4086 		G_SaveGameOver((UINT32)cursaveslot, true);
4087 
4088 	// Reset # of lives
4089 	pl->lives = (ultimatemode) ? 1 : startinglivesbalance[numgameovers];
4090 
4091 	D_MapChange(gamemap, gametype, ultimatemode, false, 0, false, false);
4092 
4093 	gameaction = ga_nothing;
4094 }
4095 
4096 //
4097 // G_EndGame (formerly Y_EndGame)
4098 // Frankly this function fits better in g_game.c than it does in y_inter.c
4099 //
4100 // ...Gee, (why) end the game?
4101 // Because G_AfterIntermission and F_EndCutscene would
4102 // both do this exact same thing *in different ways* otherwise,
4103 // which made it so that you could only unlock Ultimate mode
4104 // if you had a cutscene after the final level and crap like that.
4105 // This function simplifies it so only one place has to be updated
4106 // when something new is added.
G_EndGame(void)4107 void G_EndGame(void)
4108 {
4109 	// Only do evaluation and credits in coop games.
4110 	if (gametyperules & GTR_CUTSCENES)
4111 	{
4112 		if (nextmap == 1103-1) // end game with ending
4113 		{
4114 			F_StartEnding();
4115 			return;
4116 		}
4117 		if (nextmap == 1102-1) // end game with credits
4118 		{
4119 			F_StartCredits();
4120 			return;
4121 		}
4122 		if (nextmap == 1101-1) // end game with evaluation
4123 		{
4124 			F_StartGameEvaluation();
4125 			return;
4126 		}
4127 	}
4128 
4129 	// 1100 or competitive multiplayer, so go back to title screen.
4130 	D_StartTitle();
4131 }
4132 
4133 //
4134 // G_LoadGameSettings
4135 //
4136 // Sets a tad of default info we need.
G_LoadGameSettings(void)4137 void G_LoadGameSettings(void)
4138 {
4139 	// defaults
4140 	spstage_start = spmarathon_start = 1;
4141 	sstage_start = 50;
4142 	sstage_end = 56; // 7 special stages in vanilla SRB2
4143 	sstage_end++; // plus one weirdo
4144 	smpstage_start = 60;
4145 	smpstage_end = 66; // 7 multiplayer special stages too
4146 
4147 	// initialize free sfx slots for skin sounds
4148 	S_InitRuntimeSounds();
4149 }
4150 
4151 // G_LoadGameData
4152 // Loads the main data file, which stores information such as emblems found, etc.
G_LoadGameData(void)4153 void G_LoadGameData(void)
4154 {
4155 	size_t length;
4156 	INT32 i, j;
4157 	UINT8 modded = false;
4158 	UINT8 rtemp;
4159 
4160 	//For records
4161 	UINT32 recscore;
4162 	tic_t  rectime;
4163 	UINT16 recrings;
4164 
4165 	UINT8 recmares;
4166 	INT32 curmare;
4167 
4168 	// Clear things so previously read gamedata doesn't transfer
4169 	// to new gamedata
4170 	G_ClearRecords(); // main and nights records
4171 	M_ClearSecrets(); // emblems, unlocks, maps visited, etc
4172 	totalplaytime = 0; // total play time (separate from all)
4173 
4174 	if (M_CheckParm("-nodata"))
4175 		return; // Don't load.
4176 
4177 	// Allow saving of gamedata beyond this point
4178 	gamedataloaded = true;
4179 
4180 	if (M_CheckParm("-gamedata") && M_IsNextParm())
4181 	{
4182 		strlcpy(gamedatafilename, M_GetNextParm(), sizeof gamedatafilename);
4183 	}
4184 
4185 	if (M_CheckParm("-resetdata"))
4186 		return; // Don't load (essentially, reset).
4187 
4188 	length = FIL_ReadFile(va(pandf, srb2home, gamedatafilename), &savebuffer);
4189 	if (!length) // Aw, no game data. Their loss!
4190 		return;
4191 
4192 	save_p = savebuffer;
4193 
4194 	// Version check
4195 	if (READUINT32(save_p) != 0xFCAFE211)
4196 	{
4197 		const char *gdfolder = "the SRB2 folder";
4198 		if (strcmp(srb2home,"."))
4199 			gdfolder = srb2home;
4200 
4201 		Z_Free(savebuffer);
4202 		save_p = NULL;
4203 		I_Error("Game data is from another version of SRB2.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
4204 	}
4205 
4206 	totalplaytime = READUINT32(save_p);
4207 
4208 	modded = READUINT8(save_p);
4209 
4210 	// Aha! Someone's been screwing with the save file!
4211 	if ((modded && !savemoddata))
4212 		goto datacorrupt;
4213 	else if (modded != true && modded != false)
4214 		goto datacorrupt;
4215 
4216 	// TODO put another cipher on these things? meh, I don't care...
4217 	for (i = 0; i < NUMMAPS; i++)
4218 		if ((mapvisited[i] = READUINT8(save_p)) > MV_MAX)
4219 			goto datacorrupt;
4220 
4221 	// To save space, use one bit per collected/achieved/unlocked flag
4222 	for (i = 0; i < MAXEMBLEMS;)
4223 	{
4224 		rtemp = READUINT8(save_p);
4225 		for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
4226 			emblemlocations[j+i].collected = ((rtemp >> j) & 1);
4227 		i += j;
4228 	}
4229 	for (i = 0; i < MAXEXTRAEMBLEMS;)
4230 	{
4231 		rtemp = READUINT8(save_p);
4232 		for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
4233 			extraemblems[j+i].collected = ((rtemp >> j) & 1);
4234 		i += j;
4235 	}
4236 	for (i = 0; i < MAXUNLOCKABLES;)
4237 	{
4238 		rtemp = READUINT8(save_p);
4239 		for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
4240 			unlockables[j+i].unlocked = ((rtemp >> j) & 1);
4241 		i += j;
4242 	}
4243 	for (i = 0; i < MAXCONDITIONSETS;)
4244 	{
4245 		rtemp = READUINT8(save_p);
4246 		for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
4247 			conditionSets[j+i].achieved = ((rtemp >> j) & 1);
4248 		i += j;
4249 	}
4250 
4251 	timesBeaten = READUINT32(save_p);
4252 	timesBeatenWithEmeralds = READUINT32(save_p);
4253 	timesBeatenUltimate = READUINT32(save_p);
4254 
4255 	// Main records
4256 	for (i = 0; i < NUMMAPS; ++i)
4257 	{
4258 		recscore = READUINT32(save_p);
4259 		rectime  = (tic_t)READUINT32(save_p);
4260 		recrings = READUINT16(save_p);
4261 		save_p++; // compat
4262 
4263 		if (recrings > 10000 || recscore > MAXSCORE)
4264 			goto datacorrupt;
4265 
4266 		if (recscore || rectime || recrings)
4267 		{
4268 			G_AllocMainRecordData((INT16)i);
4269 			mainrecords[i]->score = recscore;
4270 			mainrecords[i]->time = rectime;
4271 			mainrecords[i]->rings = recrings;
4272 		}
4273 	}
4274 
4275 	// Nights records
4276 	for (i = 0; i < NUMMAPS; ++i)
4277 	{
4278 		if ((recmares = READUINT8(save_p)) == 0)
4279 			continue;
4280 
4281 		G_AllocNightsRecordData((INT16)i);
4282 
4283 		for (curmare = 0; curmare < (recmares+1); ++curmare)
4284 		{
4285 			nightsrecords[i]->score[curmare] = READUINT32(save_p);
4286 			nightsrecords[i]->grade[curmare] = READUINT8(save_p);
4287 			nightsrecords[i]->time[curmare] = (tic_t)READUINT32(save_p);
4288 
4289 			if (nightsrecords[i]->grade[curmare] > GRADE_S)
4290 				goto datacorrupt;
4291 		}
4292 
4293 		nightsrecords[i]->nummares = recmares;
4294 	}
4295 
4296 	// done
4297 	Z_Free(savebuffer);
4298 	save_p = NULL;
4299 
4300 	// Silent update unlockables in case they're out of sync with conditions
4301 	M_SilentUpdateUnlockablesAndEmblems();
4302 
4303 	return;
4304 
4305 	// Landing point for corrupt gamedata
4306 	datacorrupt:
4307 	{
4308 		const char *gdfolder = "the SRB2 folder";
4309 		if (strcmp(srb2home,"."))
4310 			gdfolder = srb2home;
4311 
4312 		Z_Free(savebuffer);
4313 		save_p = NULL;
4314 
4315 		I_Error("Corrupt game data file.\nDelete %s(maybe in %s) and try again.", gamedatafilename, gdfolder);
4316 	}
4317 }
4318 
4319 // G_SaveGameData
4320 // Saves the main data file, which stores information such as emblems found, etc.
G_SaveGameData(void)4321 void G_SaveGameData(void)
4322 {
4323 	size_t length;
4324 	INT32 i, j;
4325 	UINT8 btemp;
4326 
4327 	INT32 curmare;
4328 
4329 	if (!gamedataloaded)
4330 		return; // If never loaded (-nodata), don't save
4331 
4332 	save_p = savebuffer = (UINT8 *)malloc(GAMEDATASIZE);
4333 	if (!save_p)
4334 	{
4335 		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for saving game data\n"));
4336 		return;
4337 	}
4338 
4339 	if (modifiedgame && !savemoddata)
4340 	{
4341 		free(savebuffer);
4342 		save_p = savebuffer = NULL;
4343 		return;
4344 	}
4345 
4346 	// Version test
4347 	WRITEUINT32(save_p, 0xFCAFE211);
4348 
4349 	WRITEUINT32(save_p, totalplaytime);
4350 
4351 	btemp = (UINT8)(savemoddata || modifiedgame);
4352 	WRITEUINT8(save_p, btemp);
4353 
4354 	// TODO put another cipher on these things? meh, I don't care...
4355 	for (i = 0; i < NUMMAPS; i++)
4356 		WRITEUINT8(save_p, (mapvisited[i] & MV_MAX));
4357 
4358 	// To save space, use one bit per collected/achieved/unlocked flag
4359 	for (i = 0; i < MAXEMBLEMS;)
4360 	{
4361 		btemp = 0;
4362 		for (j = 0; j < 8 && j+i < MAXEMBLEMS; ++j)
4363 			btemp |= (emblemlocations[j+i].collected << j);
4364 		WRITEUINT8(save_p, btemp);
4365 		i += j;
4366 	}
4367 	for (i = 0; i < MAXEXTRAEMBLEMS;)
4368 	{
4369 		btemp = 0;
4370 		for (j = 0; j < 8 && j+i < MAXEXTRAEMBLEMS; ++j)
4371 			btemp |= (extraemblems[j+i].collected << j);
4372 		WRITEUINT8(save_p, btemp);
4373 		i += j;
4374 	}
4375 	for (i = 0; i < MAXUNLOCKABLES;)
4376 	{
4377 		btemp = 0;
4378 		for (j = 0; j < 8 && j+i < MAXUNLOCKABLES; ++j)
4379 			btemp |= (unlockables[j+i].unlocked << j);
4380 		WRITEUINT8(save_p, btemp);
4381 		i += j;
4382 	}
4383 	for (i = 0; i < MAXCONDITIONSETS;)
4384 	{
4385 		btemp = 0;
4386 		for (j = 0; j < 8 && j+i < MAXCONDITIONSETS; ++j)
4387 			btemp |= (conditionSets[j+i].achieved << j);
4388 		WRITEUINT8(save_p, btemp);
4389 		i += j;
4390 	}
4391 
4392 	WRITEUINT32(save_p, timesBeaten);
4393 	WRITEUINT32(save_p, timesBeatenWithEmeralds);
4394 	WRITEUINT32(save_p, timesBeatenUltimate);
4395 
4396 	// Main records
4397 	for (i = 0; i < NUMMAPS; i++)
4398 	{
4399 		if (mainrecords[i])
4400 		{
4401 			WRITEUINT32(save_p, mainrecords[i]->score);
4402 			WRITEUINT32(save_p, mainrecords[i]->time);
4403 			WRITEUINT16(save_p, mainrecords[i]->rings);
4404 		}
4405 		else
4406 		{
4407 			WRITEUINT32(save_p, 0);
4408 			WRITEUINT32(save_p, 0);
4409 			WRITEUINT16(save_p, 0);
4410 		}
4411 		WRITEUINT8(save_p, 0); // compat
4412 	}
4413 
4414 	// NiGHTS records
4415 	for (i = 0; i < NUMMAPS; i++)
4416 	{
4417 		if (!nightsrecords[i] || !nightsrecords[i]->nummares)
4418 		{
4419 			WRITEUINT8(save_p, 0);
4420 			continue;
4421 		}
4422 
4423 		WRITEUINT8(save_p, nightsrecords[i]->nummares);
4424 
4425 		for (curmare = 0; curmare < (nightsrecords[i]->nummares + 1); ++curmare)
4426 		{
4427 			WRITEUINT32(save_p, nightsrecords[i]->score[curmare]);
4428 			WRITEUINT8(save_p, nightsrecords[i]->grade[curmare]);
4429 			WRITEUINT32(save_p, nightsrecords[i]->time[curmare]);
4430 		}
4431 	}
4432 
4433 	length = save_p - savebuffer;
4434 
4435 	FIL_WriteFile(va(pandf, srb2home, gamedatafilename), savebuffer, length);
4436 	free(savebuffer);
4437 	save_p = savebuffer = NULL;
4438 }
4439 
4440 #define VERSIONSIZE 16
4441 
4442 //
4443 // G_InitFromSavegame
4444 // Can be called by the startup code or the menu task.
4445 //
G_LoadGame(UINT32 slot,INT16 mapoverride)4446 void G_LoadGame(UINT32 slot, INT16 mapoverride)
4447 {
4448 	size_t length;
4449 	char vcheck[VERSIONSIZE];
4450 	char savename[255];
4451 
4452 	// memset savedata to all 0, fixes calling perfectly valid saves corrupt because of bots
4453 	memset(&savedata, 0, sizeof(savedata));
4454 
4455 #ifdef SAVEGAME_OTHERVERSIONS
4456 	//Oh christ.  The force load response needs access to mapoverride too...
4457 	startonmapnum = mapoverride;
4458 #endif
4459 
4460 	if (marathonmode)
4461 		strcpy(savename, liveeventbackup);
4462 	else
4463 		sprintf(savename, savegamename, slot);
4464 
4465 	length = FIL_ReadFile(savename, &savebuffer);
4466 	if (!length)
4467 	{
4468 		CONS_Printf(M_GetText("Couldn't read file %s\n"), savename);
4469 		return;
4470 	}
4471 
4472 	save_p = savebuffer;
4473 
4474 	memset(vcheck, 0, sizeof (vcheck));
4475 	sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
4476 	if (strcmp((const char *)save_p, (const char *)vcheck))
4477 	{
4478 #ifdef SAVEGAME_OTHERVERSIONS
4479 		M_StartMessage(M_GetText("Save game from different version.\nYou can load this savegame, but\nsaving afterwards will be disabled.\n\nDo you want to continue anyway?\n\n(Press 'Y' to confirm)\n"),
4480 		               M_ForceLoadGameResponse, MM_YESNO);
4481 		//Freeing done by the callback function of the above message
4482 #else
4483 		M_ClearMenus(true); // so ESC backs out to title
4484 		M_StartMessage(M_GetText("Save game from different version\n\nPress ESC\n"), NULL, MM_NOTHING);
4485 		Command_ExitGame_f();
4486 		Z_Free(savebuffer);
4487 		save_p = savebuffer = NULL;
4488 
4489 		// no cheating!
4490 		memset(&savedata, 0, sizeof(savedata));
4491 #endif
4492 		return; // bad version
4493 	}
4494 	save_p += VERSIONSIZE;
4495 
4496 //	if (demoplayback) // reset game engine
4497 //		G_StopDemo();
4498 
4499 //	paused = false;
4500 //	automapactive = false;
4501 
4502 	// dearchive all the modifications
4503 	if (!P_LoadGame(mapoverride))
4504 	{
4505 		M_ClearMenus(true); // so ESC backs out to title
4506 		M_StartMessage(M_GetText("Savegame file corrupted\n\nPress ESC\n"), NULL, MM_NOTHING);
4507 		Command_ExitGame_f();
4508 		Z_Free(savebuffer);
4509 		save_p = savebuffer = NULL;
4510 
4511 		// no cheating!
4512 		memset(&savedata, 0, sizeof(savedata));
4513 		return;
4514 	}
4515 	if (marathonmode)
4516 	{
4517 		marathontime = READUINT32(save_p);
4518 		marathonmode |= READUINT8(save_p);
4519 	}
4520 
4521 	// done
4522 	Z_Free(savebuffer);
4523 	save_p = savebuffer = NULL;
4524 
4525 //	gameaction = ga_nothing;
4526 //	G_SetGamestate(GS_LEVEL);
4527 	displayplayer = consoleplayer;
4528 	multiplayer = splitscreen = false;
4529 
4530 //	G_DeferedInitNew(sk_medium, G_BuildMapName(1), 0, 0, 1);
4531 	if (setsizeneeded)
4532 		R_ExecuteSetViewSize();
4533 
4534 	M_ClearMenus(true);
4535 	CON_ToggleOff();
4536 }
4537 
4538 //
4539 // G_SaveGame
4540 // Saves your game.
4541 //
G_SaveGame(UINT32 slot,INT16 mapnum)4542 void G_SaveGame(UINT32 slot, INT16 mapnum)
4543 {
4544 	boolean saved;
4545 	char savename[256] = "";
4546 	const char *backup;
4547 
4548 	if (marathonmode)
4549 		strcpy(savename, liveeventbackup);
4550 	else
4551 		sprintf(savename, savegamename, slot);
4552 	backup = va("%s",savename);
4553 
4554 	gameaction = ga_nothing;
4555 	{
4556 		char name[VERSIONSIZE];
4557 		size_t length;
4558 
4559 		save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
4560 		if (!save_p)
4561 		{
4562 			CONS_Alert(CONS_ERROR, M_GetText("No more free memory for saving game data\n"));
4563 			return;
4564 		}
4565 
4566 		memset(name, 0, sizeof (name));
4567 		sprintf(name, (marathonmode ? "back-up %d" : "version %d"), VERSION);
4568 		WRITEMEM(save_p, name, VERSIONSIZE);
4569 
4570 		P_SaveGame(mapnum);
4571 		if (marathonmode)
4572 		{
4573 			UINT32 writetime = marathontime;
4574 			if (!(marathonmode & MA_INGAME))
4575 				writetime += TICRATE*5; // live event backup penalty because we don't know how long it takes to get to the next map
4576 			WRITEUINT32(save_p, writetime);
4577 			WRITEUINT8(save_p, (marathonmode & ~MA_INIT));
4578 		}
4579 
4580 		length = save_p - savebuffer;
4581 		saved = FIL_WriteFile(backup, savebuffer, length);
4582 		free(savebuffer);
4583 		save_p = savebuffer = NULL;
4584 	}
4585 
4586 	gameaction = ga_nothing;
4587 
4588 	if (cv_debug && saved)
4589 		CONS_Printf(M_GetText("Game saved.\n"));
4590 	else if (!saved)
4591 		CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
4592 }
4593 
4594 #define BADSAVE goto cleanup;
4595 #define CHECKPOS if (save_p >= end_p) BADSAVE
G_SaveGameOver(UINT32 slot,boolean modifylives)4596 void G_SaveGameOver(UINT32 slot, boolean modifylives)
4597 {
4598 	boolean saved = false;
4599 	size_t length;
4600 	char vcheck[VERSIONSIZE];
4601 	char savename[255];
4602 	const char *backup;
4603 
4604 	if (marathonmode)
4605 		strcpy(savename, liveeventbackup);
4606 	else
4607 		sprintf(savename, savegamename, slot);
4608 	backup = va("%s",savename);
4609 
4610 	length = FIL_ReadFile(savename, &savebuffer);
4611 	if (!length)
4612 	{
4613 		CONS_Printf(M_GetText("Couldn't read file %s\n"), savename);
4614 		return;
4615 	}
4616 
4617 	{
4618 		char temp[sizeof(timeattackfolder)];
4619 		UINT8 *end_p = savebuffer + length;
4620 		UINT8 *lives_p;
4621 		SINT8 pllives;
4622 
4623 		save_p = savebuffer;
4624 		// Version check
4625 		memset(vcheck, 0, sizeof (vcheck));
4626 		sprintf(vcheck, (marathonmode ? "back-up %d" : "version %d"), VERSION);
4627 		if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE
4628 		save_p += VERSIONSIZE;
4629 
4630 		// P_UnArchiveMisc()
4631 		(void)READINT16(save_p);
4632 		CHECKPOS
4633 		(void)READUINT16(save_p); // emeralds
4634 		CHECKPOS
4635 		READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to
4636 		if (strcmp(temp, timeattackfolder)) BADSAVE
4637 
4638 		// P_UnArchivePlayer()
4639 		CHECKPOS
4640 		(void)READUINT16(save_p);
4641 		CHECKPOS
4642 
4643 		WRITEUINT8(save_p, numgameovers);
4644 		CHECKPOS
4645 
4646 		lives_p = save_p;
4647 		pllives = READSINT8(save_p); // lives
4648 		CHECKPOS
4649 		if (modifylives && pllives < startinglivesbalance[numgameovers])
4650 		{
4651 			pllives = startinglivesbalance[numgameovers];
4652 			WRITESINT8(lives_p, pllives);
4653 		}
4654 
4655 		(void)READINT32(save_p); // Score
4656 		CHECKPOS
4657 		(void)READINT32(save_p); // continues
4658 
4659 		// File end marker check
4660 		CHECKPOS
4661 		switch (READUINT8(save_p))
4662 		{
4663 			case 0xb7:
4664 				{
4665 					UINT8 i, banksinuse;
4666 					CHECKPOS
4667 					banksinuse = READUINT8(save_p);
4668 					CHECKPOS
4669 					if (banksinuse > NUM_LUABANKS)
4670 						BADSAVE
4671 					for (i = 0; i < banksinuse; i++)
4672 					{
4673 						(void)READINT32(save_p);
4674 						CHECKPOS
4675 					}
4676 					if (READUINT8(save_p) != 0x1d)
4677 						BADSAVE
4678 				}
4679 			case 0x1d:
4680 				break;
4681 			default:
4682 				BADSAVE
4683 		}
4684 
4685 		// done
4686 		saved = FIL_WriteFile(backup, savebuffer, length);
4687 	}
4688 
4689 cleanup:
4690 	if (cv_debug && saved)
4691 		CONS_Printf(M_GetText("Game saved.\n"));
4692 	else if (!saved)
4693 		CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
4694 	Z_Free(savebuffer);
4695 	save_p = savebuffer = NULL;
4696 
4697 }
4698 #undef CHECKPOS
4699 #undef BADSAVE
4700 
4701 //
4702 // G_DeferedInitNew
4703 // Can be called by the startup code or the menu task,
4704 // consoleplayer, displayplayer, playeringame[] should be set.
4705 //
G_DeferedInitNew(boolean pultmode,const char * mapname,INT32 pickedchar,boolean SSSG,boolean FLS)4706 void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS)
4707 {
4708 	UINT16 color = skins[pickedchar].prefcolor;
4709 	paused = false;
4710 
4711 	if (demoplayback)
4712 		COM_BufAddText("stopdemo\n");
4713 	G_FreeGhosts(); // TODO: do we actually need to do this?
4714 
4715 	// this leave the actual game if needed
4716 	SV_StartSinglePlayerServer();
4717 
4718 	if (savedata.lives > 0)
4719 	{
4720 		if ((botingame = ((botskin = savedata.botskin) != 0)))
4721 			botcolor = skins[botskin-1].prefcolor;
4722 	}
4723 	else if (splitscreen != SSSG)
4724 	{
4725 		splitscreen = SSSG;
4726 		SplitScreen_OnChange();
4727 	}
4728 
4729 	color = skins[pickedchar].prefcolor;
4730 	SetPlayerSkinByNum(consoleplayer, pickedchar);
4731 	CV_StealthSet(&cv_skin, skins[pickedchar].name);
4732 	CV_StealthSetValue(&cv_playercolor, color);
4733 
4734 	if (mapname)
4735 		D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pultmode, true, 1, false, FLS);
4736 }
4737 
4738 //
4739 // This is the map command interpretation something like Command_Map_f
4740 //
4741 // called at: map cmd execution, doloadgame, doplaydemo
G_InitNew(UINT8 pultmode,const char * mapname,boolean resetplayer,boolean skipprecutscene,boolean FLS)4742 void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS)
4743 {
4744 	INT32 i;
4745 
4746 	Y_CleanupScreenBuffer();
4747 
4748 	if (paused)
4749 	{
4750 		paused = false;
4751 		S_ResumeAudio();
4752 	}
4753 
4754 	if (netgame || multiplayer) // Nice try, haxor.
4755 		pultmode = false;
4756 
4757 	if (!demoplayback && !netgame) // Netgame sets random seed elsewhere, demo playback sets seed just before us!
4758 		P_SetRandSeed(M_RandomizedSeed()); // Use a more "Random" random seed
4759 
4760 	if (resetplayer)
4761 	{
4762 		// Clear a bunch of variables
4763 		numgameovers = tokenlist = token = sstimer = redscore = bluescore = lastmap = 0;
4764 		countdown = countdown2 = exitfadestarted = 0;
4765 
4766 		for (i = 0; i < MAXPLAYERS; i++)
4767 		{
4768 			players[i].playerstate = PST_REBORN;
4769 			players[i].starpostscale = players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0;
4770 			players[i].starpostx = players[i].starposty = players[i].starpostz = 0;
4771 
4772 			if (netgame || multiplayer)
4773 			{
4774 				if (!FLS || (players[i].lives < 1))
4775 					players[i].lives = cv_startinglives.value;
4776 				players[i].continues = 0;
4777 			}
4778 			else
4779 			{
4780 				players[i].lives = (pultmode) ? 1 : startinglivesbalance[0];
4781 				players[i].continues = (pultmode) ? 0 : 1;
4782 			}
4783 
4784 			if (!((netgame || multiplayer) && (FLS)))
4785 				players[i].score = 0;
4786 
4787 			// The latter two should clear by themselves, but just in case
4788 			players[i].pflags &= ~(PF_TAGIT|PF_GAMETYPEOVER|PF_FULLSTASIS);
4789 
4790 			// Clear cheatcodes too, just in case.
4791 			players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);
4792 
4793 			players[i].xtralife = 0;
4794 		}
4795 
4796 		// Reset unlockable triggers
4797 		unlocktriggers = 0;
4798 
4799 		// clear itemfinder, just in case
4800 		if (!dedicated)	// except in dedicated servers, where it is not registered and can actually I_Error debug builds
4801 			CV_StealthSetValue(&cv_itemfinder, 0);
4802 	}
4803 
4804 	// internal game map
4805 	// well this check is useless because it is done before (d_netcmd.c::command_map_f)
4806 	// but in case of for demos....
4807 	if (W_CheckNumForName(mapname) == LUMPERROR)
4808 	{
4809 		I_Error("Internal game map '%s' not found\n", mapname);
4810 		Command_ExitGame_f();
4811 		return;
4812 	}
4813 
4814 	gamemap = (INT16)M_MapNumber(mapname[3], mapname[4]); // get xx out of MAPxx
4815 
4816 	// gamemap changed; we assume that its map header is always valid,
4817 	// so make it so
4818 	if(!mapheaderinfo[gamemap-1])
4819 		P_AllocMapHeader(gamemap-1);
4820 
4821 	maptol = mapheaderinfo[gamemap-1]->typeoflevel;
4822 	globalweather = mapheaderinfo[gamemap-1]->weather;
4823 
4824 	// Don't carry over custom music change to another map.
4825 	mapmusflags |= MUSIC_RELOADRESET;
4826 
4827 	ultimatemode = pultmode;
4828 	automapactive = false;
4829 	imcontinuing = false;
4830 
4831 	if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking && !(marathonmode & MA_NOCUTSCENES)) // Start a custom cutscene.
4832 		F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer);
4833 	else
4834 		G_DoLoadLevel(resetplayer);
4835 
4836 	if (netgame)
4837 	{
4838 		char *title = G_BuildMapTitle(gamemap);
4839 
4840 		CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
4841 		if (title)
4842 		{
4843 			CONS_Printf(": %s", title);
4844 			Z_Free(title);
4845 		}
4846 		CONS_Printf("\"\n");
4847 	}
4848 }
4849 
4850 
G_BuildMapTitle(INT32 mapnum)4851 char *G_BuildMapTitle(INT32 mapnum)
4852 {
4853 	char *title = NULL;
4854 
4855 	if (!mapheaderinfo[mapnum-1])
4856 		P_AllocMapHeader(mapnum-1);
4857 
4858 	if (strcmp(mapheaderinfo[mapnum-1]->lvlttl, ""))
4859 	{
4860 		size_t len = 1;
4861 		const char *zonetext = NULL;
4862 		const UINT8 actnum = mapheaderinfo[mapnum-1]->actnum;
4863 
4864 		len += strlen(mapheaderinfo[mapnum-1]->lvlttl);
4865 		if (!(mapheaderinfo[mapnum-1]->levelflags & LF_NOZONE))
4866 		{
4867 			zonetext = M_GetText("Zone");
4868 			len += strlen(zonetext) + 1;	// ' ' + zonetext
4869 		}
4870 		if (actnum > 0)
4871 			len += 1 + 11;			// ' ' + INT32
4872 
4873 		title = Z_Malloc(len, PU_STATIC, NULL);
4874 
4875 		sprintf(title, "%s", mapheaderinfo[mapnum-1]->lvlttl);
4876 		if (zonetext) sprintf(title + strlen(title), " %s", zonetext);
4877 		if (actnum > 0) sprintf(title + strlen(title), " %d", actnum);
4878 	}
4879 
4880 	return title;
4881 }
4882 
measurekeywords(mapsearchfreq_t * fr,struct searchdim ** dimp,UINT8 * cuntp,const char * s,const char * q,boolean wanttable)4883 static void measurekeywords(mapsearchfreq_t *fr,
4884 		struct searchdim **dimp, UINT8 *cuntp,
4885 		const char *s, const char *q, boolean wanttable)
4886 {
4887 	char *qp;
4888 	char *sp;
4889 	if (wanttable)
4890 		(*dimp) = Z_Realloc((*dimp), 255 * sizeof (struct searchdim),
4891 				PU_STATIC, NULL);
4892 	for (qp = strtok(va("%s", q), " ");
4893 			qp && fr->total < 255;
4894 			qp = strtok(0, " "))
4895 	{
4896 		if (( sp = strcasestr(s, qp) ))
4897 		{
4898 			if (wanttable)
4899 			{
4900 				(*dimp)[(*cuntp)].pos = sp - s;
4901 				(*dimp)[(*cuntp)].siz = strlen(qp);
4902 			}
4903 			(*cuntp)++;
4904 			fr->total++;
4905 		}
4906 	}
4907 	if (wanttable)
4908 		(*dimp) = Z_Realloc((*dimp), (*cuntp) * sizeof (struct searchdim),
4909 				PU_STATIC, NULL);
4910 }
4911 
writesimplefreq(mapsearchfreq_t * fr,INT32 * frc,INT32 mapnum,UINT8 pos,UINT8 siz)4912 static void writesimplefreq(mapsearchfreq_t *fr, INT32 *frc,
4913 		INT32 mapnum, UINT8 pos, UINT8 siz)
4914 {
4915 	fr[(*frc)].mapnum = mapnum;
4916 	fr[(*frc)].matchd = ZZ_Alloc(sizeof (struct searchdim));
4917 	fr[(*frc)].matchd[0].pos = pos;
4918 	fr[(*frc)].matchd[0].siz = siz;
4919 	fr[(*frc)].matchc = 1;
4920 	fr[(*frc)].total = 1;
4921 	(*frc)++;
4922 }
4923 
G_FindMap(const char * mapname,char ** foundmapnamep,mapsearchfreq_t ** freqp,INT32 * freqcp)4924 INT32 G_FindMap(const char *mapname, char **foundmapnamep,
4925 		mapsearchfreq_t **freqp, INT32 *freqcp)
4926 {
4927 	INT32 newmapnum = 0;
4928 	INT32 mapnum;
4929 	INT32 apromapnum = 0;
4930 
4931 	size_t      mapnamelen;
4932 	char   *realmapname = NULL;
4933 	char   *newmapname = NULL;
4934 	char   *apromapname = NULL;
4935 	char   *aprop = NULL;
4936 
4937 	mapsearchfreq_t *freq;
4938 	boolean wanttable;
4939 	INT32 freqc;
4940 	UINT8 frequ;
4941 
4942 	INT32 i;
4943 
4944 	mapnamelen = strlen(mapname);
4945 
4946 	/* Count available maps; how ugly. */
4947 	for (i = 0, freqc = 0; i < NUMMAPS; ++i)
4948 	{
4949 		if (mapheaderinfo[i])
4950 			freqc++;
4951 	}
4952 
4953 	freq = ZZ_Calloc(freqc * sizeof (mapsearchfreq_t));
4954 
4955 	wanttable = !!( freqp );
4956 
4957 	freqc = 0;
4958 	for (i = 0, mapnum = 1; i < NUMMAPS; ++i, ++mapnum)
4959 		if (mapheaderinfo[i])
4960 	{
4961 		if (!( realmapname = G_BuildMapTitle(mapnum) ))
4962 			continue;
4963 
4964 		aprop = realmapname;
4965 
4966 		/* Now that we found a perfect match no need to fucking guess. */
4967 		if (strnicmp(realmapname, mapname, mapnamelen) == 0)
4968 		{
4969 			if (wanttable)
4970 			{
4971 				writesimplefreq(freq, &freqc, mapnum, 0, mapnamelen);
4972 			}
4973 			if (newmapnum == 0)
4974 			{
4975 				newmapnum = mapnum;
4976 				newmapname = realmapname;
4977 				realmapname = 0;
4978 				Z_Free(apromapname);
4979 				if (!wanttable)
4980 					break;
4981 			}
4982 		}
4983 		else
4984 		if (apromapnum == 0 || wanttable)
4985 		{
4986 			/* LEVEL 1--match keywords verbatim */
4987 			if (( aprop = strcasestr(realmapname, mapname) ))
4988 			{
4989 				if (wanttable)
4990 				{
4991 					writesimplefreq(freq, &freqc,
4992 							mapnum, aprop - realmapname, mapnamelen);
4993 				}
4994 				if (apromapnum == 0)
4995 				{
4996 					apromapnum = mapnum;
4997 					apromapname = realmapname;
4998 					realmapname = 0;
4999 				}
5000 			}
5001 			else/* ...match individual keywords */
5002 			{
5003 				freq[freqc].mapnum = mapnum;
5004 				measurekeywords(&freq[freqc],
5005 						&freq[freqc].matchd, &freq[freqc].matchc,
5006 						realmapname, mapname, wanttable);
5007 				measurekeywords(&freq[freqc],
5008 						&freq[freqc].keywhd, &freq[freqc].keywhc,
5009 						mapheaderinfo[i]->keywords, mapname, wanttable);
5010 				if (freq[freqc].total)
5011 					freqc++;
5012 			}
5013 		}
5014 
5015 		Z_Free(realmapname);/* leftover old name */
5016 	}
5017 
5018 	if (newmapnum == 0)/* no perfect match--try a substring */
5019 	{
5020 		newmapnum = apromapnum;
5021 		newmapname = apromapname;
5022 	}
5023 
5024 	if (newmapnum == 0)/* calculate most queries met! */
5025 	{
5026 		frequ = 0;
5027 		for (i = 0; i < freqc; ++i)
5028 		{
5029 			if (freq[i].total > frequ)
5030 			{
5031 				frequ = freq[i].total;
5032 				newmapnum = freq[i].mapnum;
5033 			}
5034 		}
5035 		if (newmapnum)
5036 		{
5037 			newmapname = G_BuildMapTitle(newmapnum);
5038 		}
5039 	}
5040 
5041 	if (freqp)
5042 		(*freqp) = freq;
5043 	else
5044 		Z_Free(freq);
5045 
5046 	if (freqcp)
5047 		(*freqcp) = freqc;
5048 
5049 	if (foundmapnamep)
5050 		(*foundmapnamep) = newmapname;
5051 	else
5052 		Z_Free(newmapname);
5053 
5054 	return newmapnum;
5055 }
5056 
G_FreeMapSearch(mapsearchfreq_t * freq,INT32 freqc)5057 void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc)
5058 {
5059 	INT32 i;
5060 	for (i = 0; i < freqc; ++i)
5061 	{
5062 		Z_Free(freq[i].matchd);
5063 	}
5064 	Z_Free(freq);
5065 }
5066 
G_FindMapByNameOrCode(const char * mapname,char ** realmapnamep)5067 INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
5068 {
5069 	boolean usemapcode = false;
5070 
5071 	INT32 newmapnum;
5072 
5073 	size_t mapnamelen;
5074 
5075 	char *p;
5076 
5077 	mapnamelen = strlen(mapname);
5078 
5079 	if (mapnamelen == 2)/* maybe two digit code */
5080 	{
5081 		if (( newmapnum = M_MapNumber(mapname[0], mapname[1]) ))
5082 			usemapcode = true;
5083 	}
5084 	else if (mapnamelen == 5 && strnicmp(mapname, "MAP", 3) == 0)
5085 	{
5086 		if (( newmapnum = M_MapNumber(mapname[3], mapname[4]) ))
5087 			usemapcode = true;
5088 	}
5089 
5090 	if (!usemapcode)
5091 	{
5092 		/* Now detect map number in base 10, which no one asked for. */
5093 		newmapnum = strtol(mapname, &p, 10);
5094 		if (*p == '\0')/* we got it */
5095 		{
5096 			if (newmapnum < 1 || newmapnum > NUMMAPS)
5097 			{
5098 				CONS_Alert(CONS_ERROR, M_GetText("Invalid map number %d.\n"), newmapnum);
5099 				return 0;
5100 			}
5101 			usemapcode = true;
5102 		}
5103 		else
5104 		{
5105 			newmapnum = G_FindMap(mapname, realmapnamep, NULL, NULL);
5106 		}
5107 	}
5108 
5109 	if (usemapcode)
5110 	{
5111 		/* we can't check mapheaderinfo for this hahahaha */
5112 		if (W_CheckNumForName(G_BuildMapName(newmapnum)) == LUMPERROR)
5113 			return 0;
5114 
5115 		if (realmapnamep)
5116 			(*realmapnamep) = G_BuildMapTitle(newmapnum);
5117 	}
5118 
5119 	return newmapnum;
5120 }
5121 
5122 //
5123 // G_SetGamestate
5124 //
5125 // Use this to set the gamestate, please.
5126 //
G_SetGamestate(gamestate_t newstate)5127 void G_SetGamestate(gamestate_t newstate)
5128 {
5129 	gamestate = newstate;
5130 }
5131 
5132 /* These functions handle the exitgame flag. Before, when the user
5133    chose to end a game, it happened immediately, which could cause
5134    crashes if the game was in the middle of something. Now, a flag
5135    is set, and the game can then be stopped when it's safe to do
5136    so.
5137 */
5138 
5139 // Used as a callback function.
G_SetExitGameFlag(void)5140 void G_SetExitGameFlag(void)
5141 {
5142 	exitgame = true;
5143 }
5144 
G_ClearExitGameFlag(void)5145 void G_ClearExitGameFlag(void)
5146 {
5147 	exitgame = false;
5148 }
5149 
G_GetExitGameFlag(void)5150 boolean G_GetExitGameFlag(void)
5151 {
5152 	return exitgame;
5153 }
5154 
5155 // Same deal with retrying.
G_SetRetryFlag(void)5156 void G_SetRetryFlag(void)
5157 {
5158 	retrying = true;
5159 }
5160 
G_ClearRetryFlag(void)5161 void G_ClearRetryFlag(void)
5162 {
5163 	retrying = false;
5164 }
5165 
G_GetRetryFlag(void)5166 boolean G_GetRetryFlag(void)
5167 {
5168 	return retrying;
5169 }
5170 
G_SetModeAttackRetryFlag(void)5171 void G_SetModeAttackRetryFlag(void)
5172 {
5173 	retryingmodeattack = true;
5174 	G_SetRetryFlag();
5175 }
5176 
G_ClearModeAttackRetryFlag(void)5177 void G_ClearModeAttackRetryFlag(void)
5178 {
5179 	retryingmodeattack = false;
5180 }
5181 
G_GetModeAttackRetryFlag(void)5182 boolean G_GetModeAttackRetryFlag(void)
5183 {
5184 	return retryingmodeattack;
5185 }
5186 
5187 // Time utility functions
G_TicsToHours(tic_t tics)5188 INT32 G_TicsToHours(tic_t tics)
5189 {
5190 	return tics/(3600*TICRATE);
5191 }
5192 
G_TicsToMinutes(tic_t tics,boolean full)5193 INT32 G_TicsToMinutes(tic_t tics, boolean full)
5194 {
5195 	if (full)
5196 		return tics/(60*TICRATE);
5197 	else
5198 		return tics/(60*TICRATE)%60;
5199 }
5200 
G_TicsToSeconds(tic_t tics)5201 INT32 G_TicsToSeconds(tic_t tics)
5202 {
5203 	return (tics/TICRATE)%60;
5204 }
5205 
G_TicsToCentiseconds(tic_t tics)5206 INT32 G_TicsToCentiseconds(tic_t tics)
5207 {
5208 	return (INT32)((tics%TICRATE) * (100.00f/TICRATE));
5209 }
5210 
G_TicsToMilliseconds(tic_t tics)5211 INT32 G_TicsToMilliseconds(tic_t tics)
5212 {
5213 	return (INT32)((tics%TICRATE) * (1000.00f/TICRATE));
5214 }
5215 
5216