1 /*
2  * Created by Hassan "Karajorma" Kazmi for the FreeSpace2 Source Code Project.
3  * You may not sell or otherwise commercially exploit the source or things you
4  * create based on the source.
5  *
6  * This file is in charge of the "game_settings.tbl", colloquially referred to
7  * as the "mod table", and contains many misc FSO specific settings.
8  */
9 
10 #include "gamesnd/eventmusic.h"
11 #include "def_files/def_files.h"
12 #include "globalincs/version.h"
13 #include "localization/localize.h"
14 #include "mission/missioncampaign.h"
15 #include "mission/missionload.h"
16 #include "mission/missionmessage.h"
17 #include "missionui/fictionviewer.h"
18 #include "mod_table/mod_table.h"
19 #include "parse/parselo.h"
20 #include "sound/sound.h"
21 #include "playerman/player.h"
22 
23 int Directive_wait_time;
24 bool True_loop_argument_sexps;
25 bool Fixed_turret_collisions;
26 bool Damage_impacted_subsystem_first;
27 bool Cutscene_camera_displays_hud;
28 bool Alternate_chaining_behavior;
29 int Default_ship_select_effect;
30 int Default_weapon_select_effect;
31 int Default_fiction_viewer_ui;
32 bool Enable_external_shaders;
33 bool Enable_external_default_scripts;
34 int Default_detail_level;
35 bool Full_color_head_anis;
36 bool Weapons_inherit_parent_collision_group;
37 bool Flight_controls_follow_eyepoint_orientation;
38 int FS2NetD_port;
39 float Briefing_window_FOV;
40 bool Disable_hc_message_ani;
41 bool Red_alert_applies_to_delayed_ships;
42 bool Beams_use_damage_factors;
43 float Generic_pain_flash_factor;
44 float Shield_pain_flash_factor;
45 gameversion::version Targetted_version; // Defaults to retail
46 SCP_string Window_title;
47 bool Unicode_text_mode;
48 bool Use_tabled_strings_for_default_language;
49 bool Dont_preempt_training_voice;
50 SCP_string Movie_subtitle_font;
51 bool Enable_scripts_in_fred; // By default FRED does not initialize the scripting system
52 SCP_string Window_icon_path;
53 bool Disable_built_in_translations;
54 bool Weapon_shockwaves_respect_huge;
55 bool Using_in_game_options;
56 float Dinky_shockwave_default_multiplier;
57 bool Shockwaves_always_damage_bombs;
58 bool Shockwaves_damage_all_obj_types_once;
59 std::tuple<ubyte, ubyte, ubyte> Arc_color_damage_p1;
60 std::tuple<ubyte, ubyte, ubyte> Arc_color_damage_p2;
61 std::tuple<ubyte, ubyte, ubyte> Arc_color_damage_s1;
62 std::tuple<ubyte, ubyte, ubyte> Arc_color_emp_p1;
63 std::tuple<ubyte, ubyte, ubyte> Arc_color_emp_p2;
64 std::tuple<ubyte, ubyte, ubyte> Arc_color_emp_s1;
65 bool Use_engine_wash_intensity;
66 bool Framerate_independent_turning; // an in-depth explanation how this flag is supposed to work can be found in #2740 PR description
67 bool Ai_respect_tabled_turntime_rotdamp;
68 bool Swarmers_lead_targets;
69 bool Chase_view_default;
70 SCP_vector<gr_capability> Required_render_ext;
71 float Weapon_SS_Threshold_Turret_Inaccuracy;
72 bool Render_player_mflash;
73 bool Neb_affects_beams;
74 bool Neb_affects_weapons;
75 bool Neb_affects_particles;
76 bool Neb_affects_fireballs;
77 std::tuple<float, float, float, float> Shadow_distances;
78 std::tuple<float, float, float, float> Shadow_distances_cockpit;
79 bool Custom_briefing_icons_always_override_standard_icons;
80 
81 SCP_vector<std::pair<SCP_string, gr_capability>> req_render_ext_pairs = {
82 	std::make_pair("BPTC Texture Compression", CAPABILITY_BPTC)
83 };
84 
parse_mod_table(const char * filename)85 void parse_mod_table(const char *filename)
86 {
87 	// SCP_vector<SCP_string> lines;
88 
89 	try
90 	{
91 		if (filename == NULL)
92 			read_file_text_from_default(defaults_get_file("game_settings.tbl"));
93 		else
94 			read_file_text(filename, CF_TYPE_TABLES);
95 
96 		reset_parse();
97 
98 		// start parsing
99 		optional_string("#GAME SETTINGS");
100 
101 		if (optional_string("$Minimum version:") || optional_string("$Target Version:")) {
102 			Targetted_version = gameversion::parse_version();
103 
104 			mprintf(("Game Settings Table: Parsed target version of %s\n", gameversion::format_version(Targetted_version).c_str()));
105 
106 			if (!gameversion::check_at_least(Targetted_version)) {
107 				Error(LOCATION, "This modification needs at least version %s of FreeSpace Open. However, the current is only %s!",
108 					gameversion::format_version(Targetted_version).c_str(),
109 					gameversion::format_version(gameversion::get_executable_version()).c_str());
110 			}
111 		}
112 
113 		if (optional_string("$Window title:")) {
114 			stuff_string(Window_title, F_NAME);
115 		}
116 
117 		if (optional_string("$Window icon:")) {
118 			stuff_string(Window_icon_path, F_NAME);
119 		}
120 
121 		if (optional_string("$Unicode mode:")) {
122 			stuff_boolean(&Unicode_text_mode);
123 
124 			mprintf(("Game Settings Table: Unicode mode: %s\n", Unicode_text_mode ? "yes" : "no"));
125 		}
126 
127 		optional_string("#LOCALIZATION SETTINGS");
128 
129 		if (optional_string("$Use tabled strings for the default language:")) {
130 			stuff_boolean(&Use_tabled_strings_for_default_language);
131 
132 			mprintf(("Game Settings Table: Use tabled strings (translations) for the default language: %s\n", Use_tabled_strings_for_default_language ? "yes" : "no"));
133 		}
134 
135 		if (optional_string("$Don't pre-empt training message voice:")) {
136 			stuff_boolean(&Dont_preempt_training_voice);
137 
138 			mprintf(("Game Settings Table: %sre-empting training message voice\n", Dont_preempt_training_voice ? "Not p" : "P"));
139 		}
140 
141 		optional_string("#CAMPAIGN SETTINGS");
142 
143 		if (optional_string("$Default Campaign File Name:")) {
144 			char temp[MAX_FILENAME_LEN];
145 			stuff_string(temp, F_NAME, MAX_FILENAME_LEN);
146 
147 			// remove extension?
148 			if (drop_extension(temp)) {
149 				mprintf(("Game Settings Table: Removed extension on default campaign file name %s\n", temp));
150 			}
151 
152 			// check length
153 			size_t maxlen = (MAX_FILENAME_LEN - 4);
154 			auto len = strlen(temp);
155 			if (len > maxlen) {
156 				error_display(0, "Token too long: [%s].  Length = " SIZE_T_ARG ".  Max is " SIZE_T_ARG ".", temp, len, maxlen);
157 				temp[maxlen] = 0;
158 			}
159 
160 			strcpy_s(Default_campaign_file_name, temp);
161 		}
162 
163 		if (optional_string("#Ignored Campaign File Names")) {
164 			SCP_string campaign_name;
165 
166 			while (optional_string("$Campaign File Name:")) {
167 				stuff_string(campaign_name, F_NAME);
168 
169 				// remove extension?
170 				if (drop_extension(campaign_name)) {
171 					mprintf(("Game Settings Table: Removed extension on ignored campaign file name %s\n", campaign_name.c_str()));
172 				}
173 
174 				// we want case-insensitive matching, so make this lowercase
175 				SCP_tolower(campaign_name);
176 
177 				Ignored_campaigns.push_back(campaign_name);
178 			}
179 		}
180 
181 		// Note: this feature does not ignore missions that are contained in campaigns
182 		if (optional_string("#Ignored Mission File Names")) {
183 			SCP_string mission_name;
184 
185 			while (optional_string("$Mission File Name:")) {
186 				stuff_string(mission_name, F_NAME);
187 
188 				// remove extension?
189 				if (drop_extension(mission_name)) {
190 					mprintf(("Game Settings Table: Removed extension on ignored mission file name %s\n", mission_name.c_str()));
191 				}
192 
193 				// we want case-insensitive matching, so make this lowercase
194 				SCP_tolower(mission_name);
195 
196 				Ignored_missions.push_back(mission_name);
197 			}
198 		}
199 
200 		if (optional_string("$Red-alert applies to delayed ships:")) {
201 			stuff_boolean(&Red_alert_applies_to_delayed_ships);
202 			if (Red_alert_applies_to_delayed_ships) {
203 				mprintf(("Game Settings Table: Red-alert stats will be loaded for ships that arrive later in missions\n"));
204 			}
205 			else {
206 				mprintf(("Game Settings Table: Red-alert stats will NOT be loaded for ships that arrive later in missions (this is retail behavior)\n"));
207 			}
208 		}
209 
210 		optional_string("#HUD SETTINGS");
211 
212 		// how long should the game wait before displaying a directive?
213 		if (optional_string("$Directive Wait Time:")) {
214 			stuff_int(&Directive_wait_time);
215 		}
216 
217 		if (optional_string("$Cutscene camera displays HUD:")) {
218 			stuff_boolean(&Cutscene_camera_displays_hud);
219 		}
220 		// compatibility
221 		if (optional_string("$Cutscene camera disables HUD:")) {
222 			mprintf(("Game Settings Table: \"$$Cutscene camera disables HUD\" is deprecated in favor of \"$Cutscene camera displays HUD\"\n"));
223 			bool temp;
224 			stuff_boolean(&temp);
225 			Cutscene_camera_displays_hud = !temp;
226 		}
227 
228 		if (optional_string("$Full color head animations:")) {
229 			stuff_boolean(&Full_color_head_anis);
230 		}
231 		// compatibility
232 		if (optional_string("$Color head animations with hud colors:")) {
233 			mprintf(("Game Settings Table: \"$Color head animations with hud colors\" is deprecated in favor of \"$Full color head animations\"\n"));
234 			bool temp;
235 			stuff_boolean(&temp);
236 			Full_color_head_anis = !temp;
237 		}
238 
239 		optional_string("#SEXP SETTINGS");
240 
241 		if (optional_string("$Loop SEXPs Then Arguments:")) {
242 			stuff_boolean(&True_loop_argument_sexps);
243 			if (True_loop_argument_sexps) {
244 				mprintf(("Game Settings Table: Using Reversed Loops For SEXP Arguments\n"));
245 			}
246 			else {
247 				mprintf(("Game Settings Table: Using Standard Loops For SEXP Arguments\n"));
248 			}
249 		}
250 
251 		if (optional_string("$Use Alternate Chaining Behavior:")) {
252 			stuff_boolean(&Alternate_chaining_behavior);
253 			if (Alternate_chaining_behavior) {
254 				mprintf(("Game Settings Table: Using alternate event chaining behavior\n"));
255 			}
256 			else {
257 				mprintf(("Game Settings Table: Using standard event chaining behavior\n"));
258 			}
259 		}
260 
261 		optional_string("#GRAPHICS SETTINGS");
262 
263 		if (optional_string("$Enable External Shaders:")) {
264 			stuff_boolean(&Enable_external_shaders);
265 			if (Enable_external_shaders) {
266 				mprintf(("Game Settings Table: External shaders are enabled\n"));
267 			} else {
268 				mprintf(("Game Settings Table: External shaders are DISABLED\n"));
269 			}
270 		}
271 
272 		if (optional_string("$Default Detail Level:")) {
273 			int detail_level;
274 
275 			stuff_int(&detail_level);
276 
277 			mprintf(("Game Settings Table: Setting default detail level to %i of %i-%i\n", detail_level, 0, NUM_DEFAULT_DETAIL_LEVELS - 1));
278 
279 			if (detail_level < 0 || detail_level > NUM_DEFAULT_DETAIL_LEVELS - 1) {
280 				error_display(0, "Invalid detail level: %i, setting to %i", detail_level, Default_detail_level);
281 			}
282 			else {
283 				Default_detail_level = detail_level;
284 			}
285 		}
286 
287 		if (optional_string("$Briefing Window FOV:")) {
288 			float fov;
289 
290 			stuff_float(&fov);
291 
292 			mprintf(("Game Settings Table: Setting briefing window FOV from %f to %f\n", Briefing_window_FOV, fov));
293 
294 			Briefing_window_FOV = fov;
295 		}
296 
297 		if (optional_string("$Generic Pain Flash Factor:")) {
298 			stuff_float(&Generic_pain_flash_factor);
299 			if (Generic_pain_flash_factor != 1.0f)
300 				mprintf(("Game Settings Table: Setting generic pain flash factor to %.2f\n", Generic_pain_flash_factor));
301 		}
302 
303 		if (optional_string("$Shield Pain Flash Factor:")) {
304 			stuff_float(&Shield_pain_flash_factor);
305 			if (Shield_pain_flash_factor != 0.0f)
306 				 mprintf(("Game Settings Table: Setting shield pain flash factor to %.2f\n", Shield_pain_flash_factor));
307 		}
308 
309 		if (optional_string("$BMPMAN Slot Limit:")) {
310 			int tmp;
311 			stuff_int(&tmp);
312 
313 			mprintf(("Game Settings Table: $BMPMAN Slot Limit is deprecated and should be removed. It is not needed anymore.\n"));
314 		}
315 
316 		if (optional_string("$EMP Arc Color:")) {
317 			if (optional_string("+Primary Color Option 1:")) {
318 				int rgb[3];
319 				stuff_int_list(rgb, 3);
320 				if ((rgb[0] >= 0 && rgb[0] <= 255) && (rgb[1] >= 0 && rgb[1] <= 255) && (rgb[2] >= 0 && rgb[2] <= 255)) {
321 					Arc_color_emp_p1 = std::make_tuple(static_cast<ubyte>(rgb[0]), static_cast<ubyte>(rgb[1]), static_cast<ubyte>(rgb[2]));
322 				} else {
323 					error_display(0, "$EMP Arc Color: +Primary Color Option 1 is %i, %i, %i. "
324 						"One or more of these values is not within the range of 0-255. Assuming default color.", rgb[0], rgb[1], rgb[2]);
325 				}
326 			}
327 			if (optional_string("+Primary Color Option 2:")) {
328 				int rgb[3];
329 				stuff_int_list(rgb, 3);
330 				if ((rgb[0] >= 0 && rgb[0] <= 255) && (rgb[1] >= 0 && rgb[1] <= 255) && (rgb[2] >= 0 && rgb[2] <= 255)) {
331 					Arc_color_emp_p2 = std::make_tuple(static_cast<ubyte>(rgb[0]), static_cast<ubyte>(rgb[1]), static_cast<ubyte>(rgb[2]));
332 				} else {
333 					error_display(0, "$EMP Arc Color: +Primary Color Option 2 is %i, %i, %i. "
334 					    "One or more of these values is not within the range of 0-255. Assuming default color.", rgb[0], rgb[1], rgb[2]);
335 				}
336 			}
337 			if (optional_string("+Secondary Color Option 1:")) {
338 				int rgb[3];
339 				stuff_int_list(rgb, 3);
340 				if ((rgb[0] >= 0 && rgb[0] <= 255) && (rgb[1] >= 0 && rgb[1] <= 255) && (rgb[2] >= 0 && rgb[2] <= 255)) {
341 					Arc_color_emp_s1 = std::make_tuple(static_cast<ubyte>(rgb[0]), static_cast<ubyte>(rgb[1]), static_cast<ubyte>(rgb[2]));
342 			    } else {
343 				    error_display(0,"$EMP Arc Color: +Secondary Color Option 1 is %i, %i, %i. "
344 					    "One or more of these values is not within the range of 0-255. Assuming default color.", rgb[0], rgb[1], rgb[2]);
345 			    }
346 		    }
347 		}
348 
349 		if (optional_string("$Damage Arc Color:")) {
350 			if (optional_string("+Primary Color Option 1:")) {
351 				int rgb[3];
352 				stuff_int_list(rgb, 3);
353 				if ((rgb[0] >= 0 && rgb[0] <= 255) && (rgb[1] >= 0 && rgb[1] <= 255) && (rgb[2] >= 0 && rgb[2] <= 255)) {
354 					Arc_color_damage_p1 = std::make_tuple(static_cast<ubyte>(rgb[0]), static_cast<ubyte>(rgb[1]), static_cast<ubyte>(rgb[2]));
355 		        } else {
356 			        error_display(0, "Damage Arc Color: +Primary Color Option 1 is %i, %i, %i. "
357 					    "One or more of these values is not within the range of 0-255. Assuming default color.", rgb[0], rgb[1], rgb[2]);
358 		        }
359 	        }
360 			if (optional_string("+Primary Color Option 2:")) {
361 				int rgb[3];
362 				stuff_int_list(rgb, 3);
363 				if ((rgb[0] >= 0 && rgb[0] <= 255) && (rgb[1] >= 0 && rgb[1] <= 255) && (rgb[2] >= 0 && rgb[2] <= 255)) {
364 					Arc_color_damage_p2 = std::make_tuple(static_cast<ubyte>(rgb[0]), static_cast<ubyte>(rgb[1]), static_cast<ubyte>(rgb[2]));
365 	            } else {
366 		            error_display(0, "$Damage Arc Color: +Primary Color Option 2 is %i, %i, %i. "
367 					    "One or more of these values is not within the range of 0-255. Assuming default color.", rgb[0], rgb[1], rgb[2]);
368 	            }
369 			}
370 			if (optional_string("+Secondary Color Option 1:")) {
371 				int rgb[3];
372 				stuff_int_list(rgb, 3);
373 				if ((rgb[0] >= 0 && rgb[0] <= 255) && (rgb[1] >= 0 && rgb[1] <= 255) && (rgb[2] >= 0 && rgb[2] <= 255)) {
374 					Arc_color_damage_s1 = std::make_tuple(static_cast<ubyte>(rgb[0]), static_cast<ubyte>(rgb[1]), static_cast<ubyte>(rgb[2]));
375 	            } else {
376 		            error_display(0, "$Damage Arc Color: +Secondary Color Option 1 is %i, %i, %i. "
377 					    "One or more of these values is not within the range of 0-255. Assuming default color.", rgb[0], rgb[1], rgb[2]);
378 	            }
379 			}
380 		}
381 
382 		if (optional_string("$Requires Rendering Feature:")) {
383 			SCP_vector<SCP_string> ext_strings;
384 			stuff_string_list(ext_strings);
385 
386 			for (auto& ext_str : ext_strings) {
387 				auto ext = std::find_if(req_render_ext_pairs.begin(), req_render_ext_pairs.end(),
388 								[ext_str](const std::pair<SCP_string, gr_capability> &ext_pair) { return !stricmp(ext_pair.first.c_str(), ext_str.c_str()); });
389 				if (ext != req_render_ext_pairs.end()) {
390 					Required_render_ext.push_back(ext->second);
391 				}
392 			}
393 		}
394 
395 		if (optional_string("$Render player muzzle flashes in cockpit:")) {
396 			stuff_boolean(&Render_player_mflash);
397 		}
398 
399 		if (optional_string("$Beams affected by nebula visibility:")) {
400 			stuff_boolean(&Neb_affects_beams);
401 		}
402 
403 		if (optional_string("$Weapons affected by nebula visibility:")) {
404 			stuff_boolean(&Neb_affects_weapons);
405 		}
406 
407 		if (optional_string("$Particles affected by nebula visibility:")) {
408 			stuff_boolean(&Neb_affects_particles);
409 		}
410 
411 		if (optional_string("$Fireballs affected by nebula visibility:")) {
412 			stuff_boolean(&Neb_affects_fireballs);
413 		}
414 
415 		if (optional_string("$Shadow Cascade Distances:")) {
416 			float dis[4];
417 			stuff_float_list(dis, 4);
418 			if ((dis[0] >= 0) && (dis[1] > dis[0]) && (dis[2] > dis[1]) && (dis[3] > dis[2])) {
419 				Shadow_distances = std::make_tuple((dis[0]), (dis[1]), (dis[2]), (dis[3]));
420 			} else {
421 				error_display(0, "$Shadow Cascade Distances are %f, %f, %f, %f. One or more are < 0, and/or values are not increasing. Assuming default distances.", dis[0], dis[1], dis[2], dis[3]);
422 			}
423 		}
424 
425 		if (optional_string("$Shadow Cascade Distances Cockpit:")) {
426 			float dis[4];
427 			stuff_float_list(dis, 4);
428 			if ((dis[0] >= 0) && (dis[1] > dis[0]) && (dis[2] > dis[1]) && (dis[3] > dis[2])) {
429 				Shadow_distances_cockpit = std::make_tuple((dis[0]), (dis[1]), (dis[2]), (dis[3]));
430 			}
431 			else {
432 				error_display(0, "$Shadow Cascade Distances Cockpit are %f, %f, %f, %f. One or more are < 0, and/or values are not increasing. Assuming default distances.", dis[0], dis[1], dis[2], dis[3]);
433 			}
434 		}
435 
436 		optional_string("#NETWORK SETTINGS");
437 
438 		if (optional_string("$FS2NetD port:")) {
439 			stuff_int(&FS2NetD_port);
440 			if (FS2NetD_port)
441 				mprintf(("Game Settings Table: FS2NetD connecting to port %i\n", FS2NetD_port));
442 		}
443 
444 		optional_string("#SOUND SETTINGS");
445 
446 		if (optional_string("$Default Sound Volume:")) {
447 			stuff_float(&Master_sound_volume);
448 		}
449 
450 		if (optional_string("$Default Music Volume:")) {
451 			stuff_float(&Master_event_music_volume);
452 		}
453 
454 		if (optional_string("$Default Voice Volume:")) {
455 			stuff_float(&Master_voice_volume);
456 		}
457 
458 		optional_string("#FRED SETTINGS");
459 
460 		if (optional_string("$Disable Hard Coded Message Head Ani Files:")) {
461 			stuff_boolean(&Disable_hc_message_ani);
462 			if (Disable_hc_message_ani) {
463 				mprintf(("Game Settings Table: FRED - Disabling Hard Coded Message Ani Files\n"));
464 			}
465 			else {
466 				mprintf(("Game Settings Table: FRED - Using Hard Coded Message Ani Files\n"));
467 
468 			}
469 		}
470 
471 		if (optional_string("$Enable scripting in FRED:")) {
472 			stuff_boolean(&Enable_scripts_in_fred);
473 			if (Enable_scripts_in_fred) {
474 				mprintf(("Game Settings Table: FRED - Scripts will be executed when running FRED.\n"));
475 			}
476 			else {
477 				mprintf(("Game Settings Table: FRED - Scripts will not be executed when running FRED.\n"));
478 			}
479 		}
480 
481 		optional_string("#OTHER SETTINGS");
482 
483 		if (optional_string("$Fixed Turret Collisions:")) {
484 			stuff_boolean(&Fixed_turret_collisions);
485 		}
486 
487 		if (optional_string("$Damage Impacted Subsystem First:")) {
488 			stuff_boolean(&Damage_impacted_subsystem_first);
489 		}
490 
491 		if (optional_string("$Default ship select effect:")) {
492 			char effect[NAME_LENGTH];
493 			stuff_string(effect, F_NAME, NAME_LENGTH);
494 			if (!stricmp(effect, "FS2"))
495 				Default_ship_select_effect = 2;
496 			else if (!stricmp(effect, "FS1"))
497 				Default_ship_select_effect = 1;
498 			else if (!stricmp(effect, "off"))
499 				Default_ship_select_effect = 0;
500 		}
501 
502 		if (optional_string("$Default weapon select effect:")) {
503 			char effect[NAME_LENGTH];
504 			stuff_string(effect, F_NAME, NAME_LENGTH);
505 			if (!stricmp(effect, "FS2"))
506 				Default_weapon_select_effect = 2;
507 			else if (!stricmp(effect, "FS1"))
508 				Default_weapon_select_effect = 1;
509 			else if (!stricmp(effect, "off"))
510 				Default_weapon_select_effect = 0;
511 		}
512 
513 		if (optional_string("$Weapons inherit parent collision group:")) {
514 			stuff_boolean(&Weapons_inherit_parent_collision_group);
515 			if (Weapons_inherit_parent_collision_group)
516 				mprintf(("Game Settings Table: Weapons inherit parent collision group\n"));
517 		}
518 
519 		if (optional_string("$Flight controls follow eyepoint orientation:")) {
520 			stuff_boolean(&Flight_controls_follow_eyepoint_orientation);
521 			if (Flight_controls_follow_eyepoint_orientation)
522 				mprintf(("Game Settings Table: Flight controls follow eyepoint orientation\n"));
523 		}
524 
525 		if (optional_string("$Beams Use Damage Factors:")) {
526 			stuff_boolean(&Beams_use_damage_factors);
527 			if (Beams_use_damage_factors) {
528 				mprintf(("Game Settings Table: Beams will use Damage Factors\n"));
529 			}
530 			else {
531 				mprintf(("Game Settings Table: Beams will ignore Damage Factors (retail behavior)\n"));
532 			}
533 		}
534 
535 		if (optional_string("$Default fiction viewer UI:")) {
536 			char ui_name[NAME_LENGTH];
537 			stuff_string(ui_name, F_NAME, NAME_LENGTH);
538 			if (!stricmp(ui_name, "auto"))
539 				Default_fiction_viewer_ui = -1;
540 			else {
541 				int ui_index = fiction_viewer_ui_name_to_index(ui_name);
542 				if (ui_index >= 0)
543 					Default_fiction_viewer_ui = ui_index;
544 				else
545 					error_display(0, "Unrecognized fiction viewer UI: %s", ui_name);
546 			}
547 		}
548 
549 		if (optional_string("$Movie subtitle font:")) {
550 			// Fonts have not been parsed at this point so we can't validate the font name here
551 			stuff_string(Movie_subtitle_font, F_NAME);
552 		}
553 
554 		if (optional_string("$Disable built-in translations:")) {
555 			stuff_boolean(&Disable_built_in_translations);
556 		}
557 
558 		if (optional_string("$Weapon shockwave damage respects huge ship flags:")) {
559 			stuff_boolean(&Weapon_shockwaves_respect_huge);
560 		}
561 
562 		if (optional_string("$Enable external default scripts:")) {
563 			stuff_boolean(&Enable_external_default_scripts);
564 
565 			if (Enable_external_default_scripts) {
566 				mprintf(("Game Settings Table: Enabled external default scripts.\n"));
567 			} else {
568 				mprintf(("Game Settings Table: Disabled external default scripts.\n"));
569 			}
570 		}
571 
572 		if (optional_string("$Player warpout speed:")) {
573 			stuff_float(&Player_warpout_speed);
574 		}
575 
576 		if (optional_string("$Target warpout match percent:")) {
577 			stuff_float(&Target_warpout_match_percent);
578 		}
579 
580 		if (optional_string("$Minimum player warpout time:")) {
581 			stuff_float(&Minimum_player_warpout_time);
582 		}
583 
584 		if (optional_string("$Enable in-game options:")) {
585 			stuff_boolean(&Using_in_game_options);
586 
587 			if (Using_in_game_options) {
588 				mprintf(("Game Settings Table: Using in-game options system.\n"));
589 			} else {
590 				mprintf(("Game Settings Table: Not using in-game options system.\n"));
591 			}
592 		}
593 
594 		if (optional_string("$Dinky Shockwave Default Multiplier:")) {
595 			stuff_float(&Dinky_shockwave_default_multiplier);
596 			if (Dinky_shockwave_default_multiplier != 1.0f) {
597 				mprintf(("Game Settings Table: Setting default dinky shockwave multiplier to %.2f.\n", Dinky_shockwave_default_multiplier));
598 			}
599 		}
600 
601 		if (optional_string("$Shockwaves Always Damage Bombs:")) {
602 			stuff_boolean(&Shockwaves_always_damage_bombs);
603 		}
604 
605 		if (optional_string("$Shockwaves Damage All Object Types Once:")) {
606 			stuff_boolean(&Shockwaves_damage_all_obj_types_once);
607 		}
608 
609 		if (optional_string("$Use Engine Wash Intensity:")) {
610 			stuff_boolean(&Use_engine_wash_intensity);
611 		}
612 
613 		if (optional_string("$Swarmers Lead Targets:")) {
614 			stuff_boolean(&Swarmers_lead_targets);
615 		}
616 
617 		if (optional_string("$Damage Threshold for Weapons Subsystems to Trigger Turret Inaccuracy:")) {
618 			float weapon_ss_threshold;
619 			stuff_float(&weapon_ss_threshold);
620 			if ( (weapon_ss_threshold >= 0.0f) && (weapon_ss_threshold <= 1.0f) ) {
621 				Weapon_SS_Threshold_Turret_Inaccuracy = weapon_ss_threshold;
622 			} else {
623 				mprintf(("Game Settings Table: '$Damage Threshold for Weapons Subsystems to Trigger Turret Inaccuracy:' value of %.2f is not between 0 and 1. Using default value of 0.70.\n", weapon_ss_threshold));
624 			}
625 		}
626 
627 		if (optional_string("$AI use framerate independent turning:")) {
628 			stuff_boolean(&Framerate_independent_turning);
629 		}
630 
631 		if (optional_string("+AI respect tabled turn time and rotdamp:")) {
632 			stuff_boolean(&Ai_respect_tabled_turntime_rotdamp);
633 			if (!Framerate_independent_turning) {
634 				Warning(LOCATION, "\'AI respect tabled turn time and rotdamp\' requires \'AI use framerate independent turning\' in order to function.\n");
635 			}
636 		}
637 
638 		if (optional_string("$Player starts in third person/chase view by default:")) {
639 			stuff_boolean(&Chase_view_default);
640 		}
641 
642 		if (optional_string("$Custom briefing icons always override standard icons:")) {
643 			stuff_boolean(&Custom_briefing_icons_always_override_standard_icons);
644 		}
645 
646 		required_string("#END");
647 	}
648 	catch (const parse::ParseException& e)
649 	{
650 		mprintf(("TABLES: Unable to parse '%s'!  Error message = %s.\n", (filename) ? filename : "<default game_settings.tbl>", e.what()));
651 		return;
652 	}
653 }
654 
mod_table_init()655 void mod_table_init()
656 {
657 	mod_table_reset();
658 
659 	// first parse the default table
660 	parse_mod_table(NULL);
661 
662 	// if a mod.tbl exists read it
663 	if (cf_exists_full("game_settings.tbl", CF_TYPE_TABLES)) {
664 		parse_mod_table("game_settings.tbl");
665 	}
666 
667 	// parse any modular tables
668 	parse_modular_table("*-mod.tbm", parse_mod_table);
669 }
670 
mod_supports_version(int major,int minor,int build)671 bool mod_supports_version(int major, int minor, int build)
672 {
673 	return Targetted_version >= gameversion::version(major, minor, build, 0);
674 }
675 
mod_table_reset()676 void mod_table_reset()
677 {
678 	Directive_wait_time = 3000;
679 	True_loop_argument_sexps = false;
680 	Fixed_turret_collisions = false;
681 	Damage_impacted_subsystem_first = false;
682 	Cutscene_camera_displays_hud = false;
683 	Alternate_chaining_behavior = false;
684 	Default_ship_select_effect = 2;
685 	Default_weapon_select_effect = 2;
686 	Default_fiction_viewer_ui = -1;
687 	Enable_external_shaders = false;
688 	Enable_external_default_scripts             = false;
689 	Default_detail_level = 3; // "very high" seems a reasonable default in 2012 -zookeeper
690 	Full_color_head_anis = false;
691 	Weapons_inherit_parent_collision_group = false;
692 	Flight_controls_follow_eyepoint_orientation = false;
693 	FS2NetD_port = 0;
694 	Briefing_window_FOV = 0.29375f;
695 	Disable_hc_message_ani = false;
696 	Red_alert_applies_to_delayed_ships = false;
697 	Beams_use_damage_factors = false;
698 	Generic_pain_flash_factor = 1.0f;
699 	Shield_pain_flash_factor = 0.0f;
700 	Targetted_version = gameversion::version(2, 0, 0, 0); // Defaults to retail
701 	Window_title = "";
702 	Unicode_text_mode = false;
703 	Use_tabled_strings_for_default_language = false;
704 	Dont_preempt_training_voice = false;
705 	Movie_subtitle_font = "font01.vf";
706 	Enable_scripts_in_fred = false;
707 	Window_icon_path = "app_icon_sse";
708 	Disable_built_in_translations = false;
709 	Weapon_shockwaves_respect_huge = false;
710 	Using_in_game_options = false;
711 	Dinky_shockwave_default_multiplier = 1.0f;
712 	Shockwaves_always_damage_bombs = false;
713 	Shockwaves_damage_all_obj_types_once = false;
714 	Arc_color_damage_p1 = std::make_tuple(static_cast<ubyte>(64), static_cast<ubyte>(64), static_cast<ubyte>(225));
715 	Arc_color_damage_p2 = std::make_tuple(static_cast<ubyte>(128), static_cast<ubyte>(128), static_cast<ubyte>(255));
716 	Arc_color_damage_s1 = std::make_tuple(static_cast<ubyte>(200), static_cast<ubyte>(200), static_cast<ubyte>(255));
717 	Arc_color_emp_p1 = std::make_tuple(static_cast<ubyte>(64), static_cast<ubyte>(64), static_cast<ubyte>(5));
718 	Arc_color_emp_p2 = std::make_tuple(static_cast<ubyte>(128), static_cast<ubyte>(128), static_cast<ubyte>(10));
719 	Arc_color_emp_s1 = std::make_tuple(static_cast<ubyte>(255), static_cast<ubyte>(255), static_cast<ubyte>(10));
720 	Use_engine_wash_intensity = false;
721 	Framerate_independent_turning = true;
722 	Ai_respect_tabled_turntime_rotdamp = false;
723 	Chase_view_default = false;
724 	Swarmers_lead_targets = false;
725 	Required_render_ext.clear();
726 	Weapon_SS_Threshold_Turret_Inaccuracy = 0.7f; // Defaults to retail value of 0.7 --wookieejedi
727 	Render_player_mflash = false;
728 	Neb_affects_beams = false;
729 	Neb_affects_weapons = false;
730 	Neb_affects_particles = false;
731 	Neb_affects_fireballs = false;
732 	Shadow_distances = std::make_tuple(200.0f, 600.0f, 2500.0f, 8000.0f); // Default values tuned by Swifty and added here by wookieejedi
733 	Shadow_distances_cockpit = std::make_tuple(0.25f, 0.75f, 1.5f, 3.0f); // Default values tuned by wookieejedi and added here by Lafiel
734 	Custom_briefing_icons_always_override_standard_icons = false;
735 }
736