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