1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include <cstdio>
13 #include <cstdlib>
14 #include <cstring>
15 #include <cassert>
16 #include <cstdarg>
17 #include <csetjmp>
18 
19 
20 #include "ai/aigoals.h"
21 #include "asteroid/asteroid.h"
22 #include "bmpman/bmpman.h"
23 #include "cfile/cfile.h"
24 #include "cmdline/cmdline.h"
25 #include "debris/debris.h"
26 #include "gamesnd/eventmusic.h"
27 #include "globalincs/alphacolors.h"
28 #include "globalincs/linklist.h"
29 #include "hud/hudescort.h"
30 #include "hud/hudets.h"
31 #include "hud/hudwingmanstatus.h"
32 #include "iff_defs/iff_defs.h"
33 #include "io/timer.h"
34 #include "jumpnode/jumpnode.h"
35 #include "lighting/lighting.h"
36 #include "localization/localize.h"
37 #include "math/fvi.h"
38 #include "math/staticrand.h"
39 #include "mission/missionbriefcommon.h"
40 #include "mission/missioncampaign.h"
41 #include "mission/missiongoals.h"
42 #include "mission/missionhotkey.h"
43 #include "mission/missionlog.h"
44 #include "mission/missionmessage.h"
45 #include "mission/missionparse.h"
46 #include "missionui/fictionviewer.h"
47 #include "missionui/missioncmdbrief.h"
48 #include "missionui/redalert.h"
49 #include "mod_table/mod_table.h"
50 #include "nebula/neb.h"
51 #include "nebula/neblightning.h"
52 #include "network/multi.h"
53 #include "network/multi_endgame.h"
54 #include "network/multi_respawn.h"
55 #include "network/multimsgs.h"
56 #include "network/multiutil.h"
57 #include "object/objectdock.h"
58 #include "object/parseobjectdock.h"
59 #include "object/objectshield.h"
60 #include "object/waypoint.h"
61 #include "parse/generic_log.h"
62 #include "parse/parselo.h"
63 #include "parse/sexp_container.h"
64 #include "scripting/hook_api.h"
65 #include "scripting/scripting.h"
66 #include "playerman/player.h"
67 #include "popup/popup.h"
68 #include "popup/popupdead.h"
69 #include "ship/ship.h"
70 #include "ship/shipfx.h"
71 #include "ship/shiphit.h"
72 #include "sound/ds.h"
73 #include "starfield/nebula.h"
74 #include "starfield/starfield.h"
75 #include "weapon/weapon.h"
76 #include "tracing/Monitor.h"
77 #include "missionparse.h"
78 
79 
80 LOCAL struct {
81 	char docker[NAME_LENGTH];
82 	char dockee[NAME_LENGTH];
83 	char docker_point[NAME_LENGTH];
84 	char dockee_point[NAME_LENGTH];
85 } Initially_docked[MAX_SHIPS];
86 
87 int Total_initially_docked;
88 
89 mission	The_mission;
90 char Mission_filename[80];
91 
92 int Mission_palette;  // index into Nebula_palette_filenames[] of palette file to use for mission
93 int Nebula_index;  // index into Nebula_filenames[] of nebula to use in mission.
94 int Num_ai_behaviors = MAX_AI_BEHAVIORS;
95 int Num_cargo = 0;
96 int Num_status_names = MAX_STATUS_NAMES;
97 int Num_arrival_names = MAX_ARRIVAL_NAMES;
98 int Num_goal_type_names = MAX_GOAL_TYPE_NAMES;
99 int Num_parse_goals;
100 int Player_starts = 1;
101 int Num_teams;
102 fix Entry_delay_time = 0;
103 
104 int Num_unknown_ship_classes;
105 int Num_unknown_weapon_classes;
106 int Num_unknown_loadout_classes;
107 
108 ushort Current_file_checksum = 0;
109 ushort Last_file_checksum = 0;
110 int    Current_file_length   = 0;
111 
112 // alternate ship type names
113 char Mission_alt_types[MAX_ALT_TYPE_NAMES][NAME_LENGTH];
114 int Mission_alt_type_count = 0;
115 
116 // callsigns
117 char Mission_callsigns[MAX_CALLSIGNS][NAME_LENGTH];
118 int Mission_callsign_count = 0;
119 
120 #define SHIP_WARP_TIME 5.0f		// how many seconds it takes for ship to warp in
121 
122 // the ship arrival list will contain a list of ships that are yet to arrive.  This
123 // list could also include ships that are part of wings!
124 p_object Ship_arrival_list;	// for linked list of ships to arrive later
125 
126 // all the ships that we parse
127 SCP_vector<p_object> Parse_objects;
128 
129 
130 // list for arriving support ship
131 p_object	Support_ship_pobj;
132 p_object *Arriving_support_ship;
133 char Arriving_repair_targets[MAX_AI_GOALS][NAME_LENGTH];
134 int Num_arriving_repair_targets;
135 
136 #define MIN_SUBSYS_STATUS_SIZE		25
137 subsys_status *Subsys_status = NULL;
138 int Subsys_index;
139 int Subsys_status_size;
140 
141 char Mission_parse_storm_name[NAME_LENGTH] = "none";
142 
143 team_data Team_data[MAX_TVT_TEAMS];
144 
145 // variables for player start in single player
146 char		Player_start_shipname[NAME_LENGTH];
147 int		Player_start_shipnum;
148 p_object *Player_start_pobject;
149 
150 // name of all ships to use while parsing a mission (since a ship might be referenced by
151 // something before that ship has even been loaded yet)
152 char Parse_names[MAX_SHIPS + MAX_WINGS][NAME_LENGTH];
153 int Num_parse_names;
154 
155 SCP_vector<texture_replace> Fred_texture_replacements;
156 
157 int Num_path_restrictions;
158 path_restriction_t Path_restrictions[MAX_PATH_RESTRICTIONS];
159 
160 //XSTR:OFF
161 
162 const char *Nebula_filenames[NUM_NEBULAS] = {
163 	"Nebula01",
164 	"Nebula02",
165 	"Nebula03"
166 };
167 
168 const char *Neb2_filenames[NUM_NEBULAS] = {
169 	"Nebfull01",
170 	"Nebfull02",
171 	"Nebfull03"
172 };
173 
174 // Note: Nebula_colors[] and Nebula_palette_filenames are linked via index numbers
175 const char *Nebula_colors[NUM_NEBULA_COLORS] = {
176 	"Red",
177 	"Blue",
178 	"Gold",
179 	"Purple",
180 	"Maroon",
181 	"Green",
182 	"Grey blue",
183 	"Violet",
184 	"Grey Green",
185 };
186 
187 const char *Ai_behavior_names[MAX_AI_BEHAVIORS] = {
188 	"Chase",
189 	"Evade",
190 	"Get behind",
191 	"Stay Near",
192 	"Still",
193 	"Guard",
194 	"Avoid",
195 	"Waypoints",
196 	"Dock",
197 	"None",
198 	"Big Ship",
199 	"Path",
200 	"Be Rearmed",
201 	"Safety",
202 	"Evade Weapon",
203 	"Strafe",
204 	"Play Dead",
205 	"Bay Emerge",
206 	"Bay Depart",
207 	"Sentry Gun",
208 	"Warp Out",
209 };
210 
211 char *Cargo_names[MAX_CARGO];
212 char Cargo_names_buf[MAX_CARGO][NAME_LENGTH];
213 
214 const char *Ship_class_names[MAX_SHIP_CLASSES];		// to be filled in from Ship_info array
215 
216 const char *Icon_names[MIN_BRIEF_ICONS] = {
217 	"Fighter", "Fighter Wing", "Cargo", "Cargo Wing", "Largeship",
218 	"Largeship Wing", "Capital", "Planet", "Asteroid Field", "Waypoint",
219 	"Support Ship", "Freighter(no cargo)", "Freighter(has cargo)",
220 	"Freighter Wing(no cargo)", "Freighter Wing(has cargo)", "Installation",
221 	"Bomber", "Bomber Wing", "Cruiser", "Cruiser Wing", "Unknown", "Unknown Wing",
222 	"Player Fighter", "Player Fighter Wing", "Player Bomber", "Player Bomber Wing",
223 	"Knossos Device", "Transport Wing", "Corvette", "Gas Miner", "Awacs", "Supercap", "Sentry Gun", "Jump Node", "Transport"
224 };
225 
226 const char *Status_desc_names[MAX_STATUS_NAMES] = {
227 	"Shields Critical", "Engines Damaged", "Fully Operational",
228 };
229 
230 const char *Status_type_names[MAX_STATUS_NAMES] = {
231 	"Damaged", "Disabled", "Corroded",
232 };
233 
234 const char *Status_target_names[MAX_STATUS_NAMES] = {
235 	"Weapons", "Engines", "Cable TV",
236 };
237 
238 // definitions for arrival locations for ships/wings
239 const char *Arrival_location_names[MAX_ARRIVAL_NAMES] = {
240 	"Hyperspace", "Near Ship", "In front of ship", "Docking Bay",
241 };
242 
243 const char *Departure_location_names[MAX_DEPARTURE_NAMES] = {
244 	"Hyperspace", "Docking Bay",
245 };
246 
247 const char *Goal_type_names[MAX_GOAL_TYPE_NAMES] = {
248 	"Primary", "Secondary", "Bonus",
249 };
250 
251 const char *Reinforcement_type_names[] = {
252 	"Attack/Protect",
253 	"Repair/Rearm",
254 };
255 
256 const char *Old_game_types[OLD_MAX_GAME_TYPES] = {
257 	"Single Player Only",
258 	"Multiplayer Only",
259 	"Single/Multi Player",
260 	"Training mission"
261 };
262 
263 flag_def_list_new<Mission::Parse_Object_Flags> Parse_object_flags[] = {
264     { "cargo-known",					Mission::Parse_Object_Flags::SF_Cargo_known,			true, false },
265     { "ignore-count",					Mission::Parse_Object_Flags::SF_Ignore_count,			true, false },
266     { "protect-ship",					Mission::Parse_Object_Flags::OF_Protected,				true, false },
267     { "reinforcement",					Mission::Parse_Object_Flags::SF_Reinforcement,			true, false },
268     { "no-shields",						Mission::Parse_Object_Flags::OF_No_shields,				true, false },
269     { "escort",							Mission::Parse_Object_Flags::SF_Escort,					true, false },
270     { "player-start",					Mission::Parse_Object_Flags::OF_Player_start,			true, false },
271     { "no-arrival-music",				Mission::Parse_Object_Flags::SF_No_arrival_music,		true, false },
272     { "no-arrival-warp",				Mission::Parse_Object_Flags::SF_No_arrival_warp,		true, false },
273     { "no-departure-warp",				Mission::Parse_Object_Flags::SF_No_departure_warp,		true, false },
274     { "locked",							Mission::Parse_Object_Flags::SF_Locked,					true, false },
275     { "invulnerable",					Mission::Parse_Object_Flags::OF_Invulnerable,			true, false },
276     { "hidden-from-sensors",			Mission::Parse_Object_Flags::SF_Hidden_from_sensors,	true, false },
277     { "scannable",						Mission::Parse_Object_Flags::SF_Scannable,				true, false },
278     { "kamikaze",						Mission::Parse_Object_Flags::AIF_Kamikaze,				true, false },
279     { "no-dynamic",						Mission::Parse_Object_Flags::AIF_No_dynamic,			true, false },
280     { "red-alert-carry",				Mission::Parse_Object_Flags::SF_Red_alert_store_status, true, false },
281     { "beam-protect-ship",				Mission::Parse_Object_Flags::OF_Beam_protected,			true, false },
282     { "flak-protect-ship",				Mission::Parse_Object_Flags::OF_Flak_protected,			true, false },
283     { "laser-protect-ship",				Mission::Parse_Object_Flags::OF_Laser_protected,		true, false },
284     { "missile-protect-ship",			Mission::Parse_Object_Flags::OF_Missile_protected,		true, false },
285     { "guardian",						Mission::Parse_Object_Flags::SF_Guardian,				true, false },
286     { "special-warp",					Mission::Parse_Object_Flags::Knossos_warp_in,			true, false },
287     { "vaporize",						Mission::Parse_Object_Flags::SF_Vaporize,				true, false },
288     { "stealth",						Mission::Parse_Object_Flags::SF_Stealth,				true, false },
289     { "friendly-stealth-invisible",		Mission::Parse_Object_Flags::SF_Friendly_stealth_invis, true, false },
290     { "don't-collide-invisible",		Mission::Parse_Object_Flags::SF_Dont_collide_invis,		true, false },
291     { "primitive-sensors",				Mission::Parse_Object_Flags::SF_Primitive_sensors,		true, false },
292     { "no-subspace-drive",				Mission::Parse_Object_Flags::SF_No_subspace_drive,		true, false },
293     { "nav-carry-status",				Mission::Parse_Object_Flags::SF_Nav_carry_status,		true, false },
294     { "affected-by-gravity",			Mission::Parse_Object_Flags::SF_Affected_by_gravity,	true, false },
295     { "toggle-subsystem-scanning",		Mission::Parse_Object_Flags::SF_Toggle_subsystem_scanning, true, false },
296     { "targetable-as-bomb",				Mission::Parse_Object_Flags::OF_Targetable_as_bomb,		true, false },
297     { "no-builtin-messages",			Mission::Parse_Object_Flags::SF_No_builtin_messages,	true, false },
298     { "primaries-locked",				Mission::Parse_Object_Flags::SF_Primaries_locked,		true, false },
299     { "secondaries-locked",				Mission::Parse_Object_Flags::SF_Secondaries_locked,		true, false },
300     { "no-death-scream",				Mission::Parse_Object_Flags::SF_No_death_scream,		true, false },
301     { "always-death-scream",			Mission::Parse_Object_Flags::SF_Always_death_scream,	true, false },
302     { "nav-needslink",					Mission::Parse_Object_Flags::SF_Nav_needslink,			true, false },
303     { "hide-ship-name",					Mission::Parse_Object_Flags::SF_Hide_ship_name,			true, false },
304     { "set-class-dynamically",			Mission::Parse_Object_Flags::SF_Set_class_dynamically,	true, false },
305     { "lock-all-turrets",				Mission::Parse_Object_Flags::SF_Lock_all_turrets_initially, true, false },
306     { "afterburners-locked",			Mission::Parse_Object_Flags::SF_Afterburner_locked,		true, false },
307     { "force-shields-on",				Mission::Parse_Object_Flags::OF_Force_shields_on,		true, false },
308     { "immobile",						Mission::Parse_Object_Flags::OF_Immobile,				true, false },
309     { "no-ets",							Mission::Parse_Object_Flags::SF_No_ets,					true, false },
310     { "cloaked",						Mission::Parse_Object_Flags::SF_Cloaked,				true, false },
311     { "ship-locked",					Mission::Parse_Object_Flags::SF_Ship_locked,			true, false },
312     { "weapons-locked",					Mission::Parse_Object_Flags::SF_Weapons_locked,			true, false },
313     { "scramble-messages",				Mission::Parse_Object_Flags::SF_Scramble_messages,		true, false },
314     { "no_collide",						Mission::Parse_Object_Flags::OF_No_collide,				true, false },
315 	{ "no-disabled-self-destruct",		Mission::Parse_Object_Flags::SF_No_disabled_self_destruct, true, false }
316 };
317 
318 const size_t num_parse_object_flags = sizeof(Parse_object_flags) / sizeof(flag_def_list_new<Mission::Parse_Object_Flags>);
319 
320 // These are only the flags that are saved to the mission file.  See the MEF_ #defines.
321 flag_def_list Mission_event_flags[] = {
322 	{ "interval & delay use msecs", MEF_USE_MSECS, 0 },
323 };
324 int Num_mission_event_flags = sizeof(Mission_event_flags) / sizeof(flag_def_list);
325 
326 const char *Mission_event_log_flags[MAX_MISSION_EVENT_LOG_FLAGS] = {
327 	"true",
328 	"false",
329 	"always true",			// disabled
330 	"always false",
331 	"first repeat",
332 	"last repeat",
333 	"first trigger",
334 	"last trigger",
335 	"state change",
336 };
337 
338 
339 //XSTR:ON
340 
341 int Num_reinforcement_type_names = sizeof(Reinforcement_type_names) / sizeof(char *);
342 
343 vec3d Parse_viewer_pos;
344 matrix Parse_viewer_orient;
345 
346 int Loading_screen_bm_index=-1;
347 
348 // definitions for timestamps for eval'ing arrival/departure cues
349 int Mission_arrival_timestamp;
350 int Mission_departure_timestamp;
351 fix Mission_end_time;
352 
353 #define ARRIVAL_TIMESTAMP		2000		// every 2 seconds
354 #define DEPARTURE_TIMESTAMP	2200		// every 2.2 seconds -- just to be a little different
355 
356 // calculates a "unique" file signature as a ushort (checksum) and an int (file length)
357 // the amount of The_mission we're going to checksum
358 // WARNING : do NOT call this function on the server - it will overwrite goals, etc
359 #define MISSION_CHECKSUM_SIZE (NAME_LENGTH + NAME_LENGTH + 4 + DATE_TIME_LENGTH + DATE_TIME_LENGTH)
360 
361 // timers used to limit arrival messages and music
362 #define ARRIVAL_MUSIC_MIN_SEPARATION	60000
363 #define ARRIVAL_MESSAGE_MIN_SEPARATION 30000
364 
365 #define ARRIVAL_MESSAGE_DELAY_MIN		2000
366 #define ARRIVAL_MESSAGE_DELAY_MAX		3000
367 
368 static int Allow_arrival_music_timestamp;
369 static int Allow_arrival_message_timestamp;
370 static int Arrival_message_delay_timestamp;
371 
372 // multi TvT
373 static int Allow_arrival_music_timestamp_m[2];
374 static int Allow_arrival_message_timestamp_m[2];
375 static int Arrival_message_delay_timestamp_m[2];
376 
377 extern fix game_get_overall_frametime();	// for texture animation
378 
379 // local prototypes
380 void parse_player_info2(mission *pm);
381 bool post_process_mission();
382 int allocate_subsys_status();
383 void parse_common_object_data(p_object	*objp);
384 void parse_asteroid_fields(mission *pm);
385 int mission_set_arrival_location(int anchor, int location, int distance, int objnum, int path_mask, vec3d *new_pos, matrix *new_orient);
386 int get_anchor(const char *name);
387 void mission_parse_set_up_initial_docks();
388 void mission_parse_set_arrival_locations();
389 void mission_set_wing_arrival_location( wing *wingp, int num_to_set );
390 void parse_init(bool basic = false);
391 void parse_object_set_handled_flag_helper(p_object *pobjp, p_dock_function_info *infop);
392 void parse_object_clear_all_handled_flags();
393 int parse_object_on_arrival_list(p_object *pobjp);
394 int add_path_restriction();
395 
396 static bool Warned_about_team_out_of_range;
397 
398 // Goober5000
399 void mission_parse_mark_non_arrival(p_object *p_objp);
400 void mission_parse_mark_non_arrival(wing *wingp);
401 void mission_parse_mark_non_arrivals();
402 
403 // Goober5000 - FRED import
404 void convertFSMtoFS2();
405 
406 
407 MONITOR(NumShipArrivals)
408 MONITOR(NumShipDepartures)
409 
410 const std::shared_ptr<scripting::Hook> OnDepartureStartedHook = scripting::Hook::Factory(
411 	"On Departure Started", "Called when a ship starts the departure process.",
412 	{
413 		{"Ship", "ship", "The ship that has began the depture process."},
414 	});
415 
416 // Goober5000
parse_custom_bitmap(const char * expected_string_640,const char * expected_string_1024,char * string_field_640,char * string_field_1024)417 void parse_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, char *string_field_640, char *string_field_1024)
418 {
419 	int found640 = 0, found1024 = 0;
420 	strcpy(string_field_640, "");
421 	strcpy(string_field_1024, "");
422 
423 	// custom mission loading background, or whatever
424 	if (optional_string(expected_string_640))
425 	{
426 		found640 = 1;
427 		stuff_string(string_field_640, F_NAME, MAX_FILENAME_LEN);
428 	}
429 	if (optional_string(expected_string_1024))
430 	{
431 		found1024 = 1;
432 		stuff_string(string_field_1024, F_NAME, MAX_FILENAME_LEN);
433 	}
434 
435 	// error testing
436 	if (Fred_running && (found640) && !(found1024))
437 	{
438 		mprintf(("Mission: found an entry for %s but not a corresponding entry for %s!\n", expected_string_640, expected_string_1024));
439 	}
440 	if (Fred_running && !(found640) && (found1024))
441 	{
442 		mprintf(("Mission: found an entry for %s but not a corresponding entry for %s!\n", expected_string_1024, expected_string_640));
443 	}
444 }
445 
parse_mission_info(mission * pm,bool basic=false)446 void parse_mission_info(mission *pm, bool basic = false)
447 {
448 	char game_string[NAME_LENGTH];
449 
450 	// Goober5000
451 	skip_to_start_of_string("#Mission Info");
452 
453 	required_string("#Mission Info");
454 
455 	required_string("$Version:");
456 	stuff_float(&pm->version);
457 	if (pm->version != MISSION_VERSION)
458 		mprintf(("Older mission, should update it (%.2f<-->%.2f)\n", pm->version, MISSION_VERSION));
459 
460 	required_string("$Name:");
461 	stuff_string(pm->name, F_NAME, NAME_LENGTH);
462 
463 	required_string("$Author:");
464 	stuff_string(pm->author, F_NAME, NAME_LENGTH);
465 
466 	required_string("$Created:");
467 	stuff_string(pm->created, F_DATE, DATE_TIME_LENGTH);
468 
469 	required_string("$Modified:");
470 	stuff_string(pm->modified, F_DATE, DATE_TIME_LENGTH);
471 
472 	required_string("$Notes:");
473 	stuff_string(pm->notes, F_NOTES, NOTES_LENGTH);
474 
475 	if (optional_string("$Mission Desc:"))
476 		stuff_string(pm->mission_desc, F_MULTITEXT, MISSION_DESC_LENGTH);
477 	else
478 		strcpy_s(pm->mission_desc, NOX("No description\n"));
479 
480 	pm->game_type = MISSION_TYPE_SINGLE;				// default to single player only
481 	if ( optional_string("+Game Type:")) {
482 		// HACK HACK HACK -- stuff_string was changed to *not* ignore carriage returns.  Since the
483 		// old style missions may have carriage returns, deal with it here.
484 		ignore_white_space();
485 		stuff_string(game_string, F_NAME, NAME_LENGTH);
486 		for ( int i = 0; i < OLD_MAX_GAME_TYPES; i++ ) {
487 			if ( !stricmp(game_string, Old_game_types[i]) ) {
488 
489 				// this block of code is now old mission compatibility code.  We specify game
490 				// type in a different manner than before.
491 				if ( i == OLD_GAME_TYPE_SINGLE_ONLY )
492 					pm->game_type = MISSION_TYPE_SINGLE;
493 				else if ( i == OLD_GAME_TYPE_MULTI_ONLY )
494 					pm->game_type = MISSION_TYPE_MULTI;
495 				else if ( i == OLD_GAME_TYPE_SINGLE_MULTI )
496 					pm->game_type = (MISSION_TYPE_SINGLE | MISSION_TYPE_MULTI );
497 				else if ( i == OLD_GAME_TYPE_TRAINING )
498 					pm->game_type = MISSION_TYPE_TRAINING;
499 				else
500 					Int3();
501 
502 				if ( pm->game_type & MISSION_TYPE_MULTI )
503 					pm->game_type |= MISSION_TYPE_MULTI_COOP;
504 
505 				break;
506 			}
507 		}
508 	}
509 
510 	if ( optional_string("+Game Type Flags:") ) {
511 		stuff_int(&pm->game_type);
512 	}
513 
514     pm->flags.reset();
515 	if (optional_string("+Flags:")){
516         stuff_flagset(&pm->flags);
517 	}
518 
519 	// nebula mission stuff
520 	Neb2_awacs = -1.0f;
521 	if(optional_string("+NebAwacs:")){
522 		stuff_float(&Neb2_awacs);
523 	}
524 	if(optional_string("+Storm:")){
525 		stuff_string(Mission_parse_storm_name, F_NAME, NAME_LENGTH);
526 
527 		if (!basic)
528 			nebl_set_storm(Mission_parse_storm_name);
529 	}
530 	Neb2_fog_near_mult = 1.0f;
531 	Neb2_fog_far_mult = 1.0f;
532 	if(optional_string("+Fog Near Mult:")){
533 		stuff_float(&Neb2_fog_near_mult);
534 	}
535 	if(optional_string("+Fog Far Mult:")){
536 		stuff_float(&Neb2_fog_far_mult);
537 	}
538 
539 	// Goober5000 - ship contrail speed threshold
540 	pm->contrail_threshold = CONTRAIL_THRESHOLD_DEFAULT;
541 	if (optional_string("$Contrail Speed Threshold:")){
542 		stuff_int(&pm->contrail_threshold);
543 	}
544 
545 	// get the number of players if in a multiplayer mission
546 	pm->num_players = 1;
547 	if ( pm->game_type & MISSION_TYPE_MULTI ) {
548 		if ( optional_string("+Num Players:") ) {
549 			stuff_int( &(pm->num_players) );
550 		}
551 	}
552 
553 	// get the number of respawns
554 	pm->num_respawns = 0;
555 	if ( pm->game_type & MISSION_TYPE_MULTI ) {
556 		if ( optional_string("+Num Respawns:") ){
557 			stuff_int( (int*)&(pm->num_respawns) );
558 		}
559 	}
560 
561 	The_mission.max_respawn_delay = -1;
562 	if ( pm->game_type & MISSION_TYPE_MULTI ) {
563 		if ( optional_string("+Max Respawn Time:") ){
564 			stuff_int( &The_mission.max_respawn_delay );
565 		}
566 	}
567 
568 	if ( optional_string("+Red Alert:")) {
569 		int temp;
570 		stuff_int(&temp);
571 
572         pm->flags.set(Mission::Mission_Flags::Red_alert, temp != 0);
573     }
574 	red_alert_invalidate_timestamp();
575 
576 	if ( optional_string("+Scramble:")) {
577 		int temp;
578 		stuff_int(&temp);
579 
580         pm->flags.set(Mission::Mission_Flags::Scramble, temp != 0);
581 	}
582 
583 	// if we are just requesting basic info then skip everything else.  the reason
584 	// for this is to make sure that we don't modify things outside of the mission struct
585 	// that might not get reset afterwards (like what can happen in the techroom) - taylor
586 	//
587 	// NOTE: this can be dangerous so be sure that any get_mission_info() call (defaults to basic info) will
588 	//       only reference data parsed before this point!! (like current FRED2 and game code does)
589 	if (basic)
590 		return;
591 
592 	// this is part of mission info but derived from the campaign file rather than parsed
593 	extern int debrief_find_persona_index();
594 	pm->debriefing_persona = debrief_find_persona_index();
595 
596 	// set up support ships
597 	pm->support_ships.arrival_location = ARRIVE_AT_LOCATION;
598 	pm->support_ships.arrival_anchor = -1;
599 	pm->support_ships.departure_location = DEPART_AT_LOCATION;
600 	pm->support_ships.departure_anchor = -1;
601 	pm->support_ships.max_hull_repair_val = 0.0f;
602 	pm->support_ships.max_subsys_repair_val = 100.0f;	//ASSUMPTION: full repair capabilities
603 	pm->support_ships.max_support_ships = -1;	// infinite
604 	pm->support_ships.max_concurrent_ships = 1;
605 	pm->support_ships.ship_class = -1;
606 	pm->support_ships.tally = 0;
607 	pm->support_ships.support_available_for_species = 0;
608 
609 	// for each species, store whether support is available
610 	for (int species = 0; species < (int)Species_info.size(); species++)
611 	{
612         for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
613 		{
614 			if ((it->flags[Ship::Info_Flags::Support]) && (it->species == species))
615 			{
616 				pm->support_ships.support_available_for_species |= (1 << species);
617 				break;
618 			}
619 		}
620 	}
621 
622 	if ( optional_string("+Disallow Support:"))
623 	{
624 		int temp;
625 		stuff_int(&temp);
626 
627 		pm->support_ships.max_support_ships = (temp > 0) ? 0 : -1;
628 	}
629 
630 	if ( optional_string("+Hull Repair Ceiling:"))
631 	{
632 		float temp;
633 		stuff_float(&temp);
634 
635 		//ONLY set max_hull_repair_val if the value given is valid -C
636 		if (temp <= 100.0f && temp >= 0.0f) {
637 			pm->support_ships.max_hull_repair_val = temp;
638 		}
639 	}
640 
641 	if ( optional_string("+Subsystem Repair Ceiling:"))
642 	{
643 		float temp;
644 		stuff_float(&temp);
645 
646 		//ONLY set max_subsys_repair_val if the value given is valid -C
647 		if (temp <= 100.0f && temp >= 0.0f) {
648 			pm->support_ships.max_subsys_repair_val = temp;
649 		}
650 	}
651 
652 	if (optional_string("+All Teams Attack")){
653 		Mission_all_attack = 1;
654 	} else {
655 		Mission_all_attack = 0;
656 	}
657 
658 	//	Maybe delay the player's entry.
659 	if (optional_string("+Player Entry Delay:")) {
660 		float	temp;
661 
662 		stuff_float(&temp);
663 		Assert(temp >= 0.0f);
664 		Entry_delay_time = fl2f(temp);
665 	}
666 	else
667 	{
668 		Entry_delay_time = 0;
669 	}
670 
671 	if (optional_string("+Viewer pos:")){
672 		stuff_vec3d(&Parse_viewer_pos);
673 	}
674 
675 	if (optional_string("+Viewer orient:")){
676 		stuff_matrix(&Parse_viewer_orient);
677 	}
678 
679 	// possible squadron reassignment
680 	strcpy_s(pm->squad_name, "");
681 	strcpy_s(pm->squad_filename, "");
682 	if(optional_string("+SquadReassignName:")){
683 		stuff_string(pm->squad_name, F_NAME, NAME_LENGTH);
684 		if(optional_string("+SquadReassignLogo:")){
685 			stuff_string(pm->squad_filename, F_NAME, MAX_FILENAME_LEN);
686 		}
687 	}
688 	// always clear out squad reassignments if not single player
689 	if(Game_mode & GM_MULTIPLAYER){
690 		strcpy_s(pm->squad_name, "");
691 		strcpy_s(pm->squad_filename, "");
692 	}
693 	// reassign the player
694 	else {
695 		if(!Fred_running && (Player != NULL) && (pm->squad_name[0] != '\0') && (Game_mode & GM_CAMPAIGN_MODE)){
696 			mprintf(("Reassigning player to squadron %s\n", pm->squad_name));
697 			player_set_squad(Player, pm->squad_name);
698 			player_set_squad_bitmap(Player, pm->squad_filename, false);
699 		}
700 	}
701 
702 
703 	// wing stuff by Goober5000 ------------------------------------------
704 	// the wing name arrays are initialized in ship_level_init
705 	if (optional_string("$Starting wing names:"))
706 	{
707 		stuff_string_list(Starting_wing_names, MAX_STARTING_WINGS);
708 	}
709 
710 	if (optional_string("$Squadron wing names:"))
711 	{
712 		stuff_string_list(Squadron_wing_names, MAX_SQUADRON_WINGS);
713 	}
714 
715 	if (optional_string("$Team-versus-team wing names:"))
716 	{
717 		stuff_string_list(TVT_wing_names, MAX_TVT_WINGS);
718 	}
719 	// end of wing stuff -------------------------------------------------
720 
721 
722 	// set up the Num_teams variable accoriding to the game_type variable'
723 	Num_teams = 1;				// assume 1
724 
725 	// multiplayer team v. team games have two teams.  If we have three teams, we need to use
726 	// a new mission mode!
727 	if ( (pm->game_type & MISSION_TYPE_MULTI) && (pm->game_type & MISSION_TYPE_MULTI_TEAMS) ){
728 		Num_teams = 2;
729 	}
730 
731 	// Goober5000 - made this into a function since we use much the same technique for the briefing background
732 	parse_custom_bitmap("$Load Screen 640:", "$Load Screen 1024:", pm->loading_screen[GR_640], pm->loading_screen[GR_1024]);
733 
734 	strcpy_s(pm->skybox_model, "");
735 	if (optional_string("$Skybox Model:"))
736 	{
737 		stuff_string(pm->skybox_model, F_NAME, MAX_FILENAME_LEN);
738 	}
739 
740 	vm_set_identity(&pm->skybox_orientation);
741 	if (optional_string("+Skybox Orientation:"))
742 	{
743 		stuff_matrix(&pm->skybox_orientation);
744 	}
745 
746 	if (optional_string("+Skybox Flags:")){
747 		pm->skybox_flags = 0;
748 		stuff_int(&pm->skybox_flags);
749 	}else{
750 		pm->skybox_flags = DEFAULT_NMODEL_FLAGS;
751 	}
752 
753 	// Goober5000 - AI on a per-mission basis
754 	The_mission.ai_profile = &Ai_profiles[Default_ai_profile];
755 	if (optional_string("$AI Profile:"))
756 	{
757 		int index;
758 		char temp[NAME_LENGTH];
759 
760 		stuff_string(temp, F_NAME, NAME_LENGTH);
761 		index = ai_profile_lookup(temp);
762 
763 		if (index >= 0)
764 			The_mission.ai_profile = &Ai_profiles[index];
765 		else
766 			WarningEx(LOCATION, "Mission: %s\nUnknown AI profile %s!", pm->name, temp );
767 	}
768 
769 	Assert( The_mission.ai_profile != NULL );
770 
771 	// Kazan - player use AI at start?
772 	if (pm->flags[Mission::Mission_Flags::Player_start_ai])
773 		Player_use_ai = 1;
774 
775 	pm->sound_environment.id = -1;
776 	if (optional_string("$Sound Environment:")) {
777 		char preset[65] = { '\0' };
778 		stuff_string(preset, F_NAME, sizeof(preset)-1);
779 
780 		int preset_id = ds_eax_get_preset_id(preset);
781 
782 		if (preset_id >= 0) {
783 			sound_env_get(&pm->sound_environment, preset_id);
784 		}
785 
786 		// NOTE: values will be clamped properly when the effect is actually set
787 
788 		if (optional_string("+Volume:")) {
789 			stuff_float(&pm->sound_environment.volume);
790 		}
791 
792 		if (optional_string("+Damping:")) {
793 			stuff_float(&pm->sound_environment.damping);
794 		}
795 
796 		if (optional_string("+Decay Time:")) {
797 			stuff_float(&pm->sound_environment.decay);
798 		}
799 	}
800 }
801 
parse_player_info(mission * pm)802 void parse_player_info(mission *pm)
803 {
804 	char temp[NAME_LENGTH];
805 	Assert(pm != NULL);
806 
807 	// alternate type names begin here
808 	mission_parse_reset_alt();
809 	if(optional_string("#Alternate Types:")){
810 		// read them all in
811 		while(!optional_string("#end")){
812 			required_string("$Alt:");
813 			stuff_string(temp, F_NAME, NAME_LENGTH);
814 
815 			// maybe store it
816 			mission_parse_add_alt(temp);
817 		}
818 	}
819 
820 	// callsigns begin here
821 	mission_parse_reset_callsign();
822 	if(optional_string("#Callsigns:")){
823 		// read them all in
824 		while(!optional_string("#end")){
825 			required_string("$Callsign:");
826 			stuff_string(temp, F_NAME, NAME_LENGTH);
827 
828 			// maybe store it
829 			mission_parse_add_callsign(temp);
830 		}
831 	}
832 
833 	Player_starts = 0;
834 	required_string("#Players");
835 
836 	while (required_string_either("#Objects", "$")){
837 		parse_player_info2(pm);
838 	}
839 }
840 
parse_player_info2(mission * pm)841 void parse_player_info2(mission *pm)
842 {
843 	int nt, i;
844 	SCP_vector<loadout_row> list, list2;
845 	team_data *ptr;
846 
847 	// read in a ship/weapon pool for each team.
848 	for ( nt = 0; nt < Num_teams; nt++ ) {
849 		int num_choices;
850 
851 		ptr = &Team_data[nt];
852 		// get the shipname for single player missions
853 		// MWA -- make this required later!!!!
854 		if ( optional_string("$Starting Shipname:") )
855 			stuff_string( Player_start_shipname, F_NAME, NAME_LENGTH );
856 
857 		required_string("$Ship Choices:");
858 		stuff_loadout_list(list, MISSION_LOADOUT_SHIP_LIST);
859 
860 		num_choices = 0;
861 
862 		// check ship class loadout entries
863 		for (auto &sc : list) {
864 			// in a campaign, see if the player is allowed the ships or not.  Remove them from the
865 			// pool if they are not allowed
866 			if (Game_mode & GM_CAMPAIGN_MODE || (MULTIPLAYER_CLIENT)) {
867 				if ( !Campaign.ships_allowed[sc.index] )
868 					continue;
869 			}
870 			if (sc.index < 0 || sc.index >= ship_info_size())
871 				continue;
872 
873 			ptr->ship_list[num_choices] = sc.index;
874 
875 			// if the list isn't set by a variable leave the variable name empty
876 			if (sc.index_sexp_var == NOT_SET_BY_SEXP_VARIABLE) {
877 				strcpy_s(ptr->ship_list_variables[num_choices], "") ;
878 			}
879 			else {
880 				strcpy_s(ptr->ship_list_variables[num_choices], Sexp_variables[sc.index_sexp_var].variable_name);
881 			}
882 
883 			ptr->ship_count[num_choices] = sc.count;
884 			ptr->loadout_total += sc.count;
885 
886 			// if the list isn't set by a variable leave the variable name empty
887 			if (sc.count_sexp_var == NOT_SET_BY_SEXP_VARIABLE) {
888 				strcpy_s(ptr->ship_count_variables[num_choices], "");
889 			}
890 			else {
891 				strcpy_s(ptr->ship_count_variables[num_choices], Sexp_variables[sc.count_sexp_var].variable_name);
892 			}
893 
894 			num_choices++;
895 		}
896 		ptr->num_ship_choices = num_choices;
897 
898 		ptr->default_ship = -1;
899 		if (optional_string("+Default_ship:")) {
900 			char str[NAME_LENGTH];
901 			stuff_string(str, F_NAME, NAME_LENGTH);
902 			ptr->default_ship = ship_info_lookup(str);
903 			if (-1 == ptr->default_ship) {
904 				WarningEx(LOCATION, "Mission: %s\nUnknown default ship %s!  Defaulting to %s.", pm->name, str, Ship_info[ptr->ship_list[0]].name );
905 				ptr->default_ship = ptr->ship_list[0]; // default to 1st in list
906 			}
907 			// see if the player's default ship is an allowable ship (campaign only). If not, then what
908 			// do we do?  choose the first allowable one?
909 			if (Game_mode & GM_CAMPAIGN_MODE || (MULTIPLAYER_CLIENT)) {
910 				if ( !(Campaign.ships_allowed[ptr->default_ship]) ) {
911 					for (i = 0; i < ship_info_size(); i++ ) {
912 						if ( Campaign.ships_allowed[i] ) {
913 							ptr->default_ship = i;
914 							break;
915 						}
916 					}
917 					Assertion( i < ship_info_size(), "Mission: %s: Could not find a valid default ship.\n", pm->name );
918 				}
919 			}
920 		}
921 
922 		if (ptr->default_ship == -1)  // invalid or not specified, make first in list
923 			ptr->default_ship = ptr->ship_list[0];
924 
925 		required_string("+Weaponry Pool:");
926 		stuff_loadout_list(list2, MISSION_LOADOUT_WEAPON_LIST);
927 
928 		num_choices = 0;
929 
930 		// check weapon class loadout entries
931 		for (auto &wc : list2) {
932 			// in a campaign, see if the player is allowed the weapons or not.  Remove them from the
933 			// pool if they are not allowed
934 			if (Game_mode & GM_CAMPAIGN_MODE || (MULTIPLAYER_CLIENT)) {
935 				if ( !Campaign.weapons_allowed[wc.index] ) {
936 					continue;
937 				}
938 			}
939 			if (wc.index < 0 || wc.index >= weapon_info_size())
940 				continue;
941 
942 			// always allow the pool to be added in FRED, it is a verbal warning
943 			// to let the mission dev know about the problem
944 			if ( !(Weapon_info[wc.index].wi_flags[Weapon::Info_Flags::Player_allowed]) && !Fred_running ) {
945 				WarningEx(LOCATION, "Weapon '%s' in weapon pool isn't allowed on player loadout! Ignoring it ...\n", Weapon_info[wc.index].name);
946 				continue;
947 			}
948 
949 			ptr->weaponry_pool[num_choices] = wc.index;
950 			ptr->weaponry_count[num_choices] = wc.count;
951 
952 			// if the list isn't set by a variable leave the variable name empty
953 			if (wc.index_sexp_var == NOT_SET_BY_SEXP_VARIABLE) {
954 				strcpy_s(ptr->weaponry_pool_variable[num_choices], "");
955 			}
956 			else {
957 				strcpy_s(ptr->weaponry_pool_variable[num_choices], Sexp_variables[wc.index_sexp_var].variable_name);
958 			}
959 
960 			// if the list isn't set by a variable leave the variable name empty
961 			if (wc.count_sexp_var == NOT_SET_BY_SEXP_VARIABLE) {
962 				strcpy_s(ptr->weaponry_amount_variable[num_choices], "");
963 			}
964 			else {
965 				strcpy_s(ptr->weaponry_amount_variable[num_choices], Sexp_variables[wc.count_sexp_var].variable_name);
966 			}
967 
968 			num_choices++;
969 		}
970 		ptr->num_weapon_choices = num_choices;
971 
972 		memset(ptr->weapon_required, 0, MAX_WEAPON_TYPES * sizeof(bool));
973 		if (optional_string("+Required for mission:"))
974 		{
975 			int num_weapons;
976 			int weapon_list_buf[MAX_WEAPON_TYPES];
977 			num_weapons = (int)stuff_int_list(weapon_list_buf, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
978 
979 			for (i = 0; i < num_weapons; i++)
980 				ptr->weapon_required[weapon_list_buf[i]] = true;
981 		}
982 	}
983 
984 	if ( nt != Num_teams )
985 		Error(LOCATION, "Not enough ship/weapon pools for mission.  There are %d teams and only %d pools.", Num_teams, nt);
986 }
987 
parse_cutscenes(mission * pm)988 void parse_cutscenes(mission *pm)
989 {
990 	pm->cutscenes.clear();
991 
992 	if (optional_string("#Cutscenes"))
993 	{
994 		mission_cutscene scene;
995 
996 		while (!optional_string("#end"))
997 		{
998 			// this list should correspond to the MOVIE_* #defines
999 			scene.type = optional_string_one_of(6,
1000 				"$Fiction Viewer Cutscene:",
1001 				"$Command Brief Cutscene:",
1002 				"$Briefing Cutscene:",
1003 				"$Pre-game Cutscene:",
1004 				"$Debriefing Cutscene:",
1005 				"$Campaign End Cutscene:");
1006 
1007 			// no more cutscenes specified?
1008 			if (scene.type < 0)
1009 				break;
1010 
1011 			// get the cutscene file
1012 			stuff_string(scene.filename, F_NAME, NAME_LENGTH);
1013 
1014 			// get the sexp if we have one
1015 			if (optional_string("+formula:"))
1016 				scene.formula = get_sexp_main();
1017 			else
1018 				scene.formula = Locked_sexp_true;
1019 
1020 			// add it
1021 			pm->cutscenes.push_back(scene);
1022 		}
1023 	}
1024 }
1025 
parse_plot_info(mission *)1026 void parse_plot_info(mission * /*pm*/)
1027 {
1028 	if (optional_string("#Plot Info"))
1029 	{
1030 		char dummy_filespec[FILESPEC_LENGTH];
1031 		char dummy_name[NAME_LENGTH];
1032 
1033 		required_string("$Tour:");
1034 		stuff_string(dummy_name, F_NAME, NAME_LENGTH);
1035 
1036 		required_string("$Pre-Briefing Cutscene:");
1037 		stuff_string(dummy_filespec, F_FILESPEC, FILESPEC_LENGTH);
1038 
1039 		required_string("$Pre-Mission Cutscene:");
1040 		stuff_string(dummy_filespec, F_FILESPEC, FILESPEC_LENGTH);
1041 
1042 		required_string("$Next Mission Success:");
1043 		stuff_string(dummy_name, F_NAME, NAME_LENGTH);
1044 
1045 		required_string("$Next Mission Partial:");
1046 		stuff_string(dummy_name, F_NAME, NAME_LENGTH);
1047 
1048 		required_string("$Next Mission Failure:");
1049 		stuff_string(dummy_name, F_NAME, NAME_LENGTH);
1050 	}
1051 }
1052 
parse_briefing_info(mission *)1053 void parse_briefing_info(mission * /*pm*/)
1054 {
1055 	char junk[4096];
1056 
1057 	if ( !optional_string("#Briefing Info") )
1058 		return;
1059 
1060 	required_string("$Briefing Voice 1:");
1061 	stuff_string(junk, F_FILESPEC, sizeof(junk));
1062 
1063 	required_string("$Briefing Text 1:");
1064 	stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1065 
1066 	required_string("$Briefing Voice 2:");
1067 	stuff_string(junk, F_FILESPEC, sizeof(junk));
1068 
1069 	required_string("$Briefing Text 2:");
1070 	stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1071 
1072 	required_string("$Briefing Voice 3:");
1073 	stuff_string(junk, F_FILESPEC, sizeof(junk));
1074 
1075 	required_string("$Briefing Text 3:");
1076 	stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1077 
1078 	required_string("$Debriefing Voice 1:");
1079 	stuff_string(junk, F_FILESPEC, sizeof(junk));
1080 
1081 	required_string("$Debriefing Text 1:");
1082 	stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1083 
1084 	required_string("$Debriefing Voice 2:");
1085 	stuff_string(junk, F_FILESPEC, sizeof(junk));
1086 
1087 	required_string("$Debriefing Text 2:");
1088 	stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1089 
1090 	required_string("$Debriefing Voice 3:");
1091 	stuff_string(junk, F_FILESPEC, sizeof(junk));
1092 
1093 	required_string("$Debriefing Text 3:");
1094 	stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1095 }
1096 
1097 /**
1098  * Parse the event music and briefing music for the mission
1099  */
parse_music(mission * pm,int flags)1100 void parse_music(mission *pm, int flags)
1101 {
1102 	int i, index, num;
1103 	char *ch;
1104 	char temp[NAME_LENGTH];
1105 
1106 	event_music_reset_choices();
1107 
1108 	if ( !optional_string("#Music") ) {
1109 		return;
1110 	}
1111 
1112 	required_string("$Event Music:");
1113 	stuff_string(pm->event_music_name, F_NAME, NAME_LENGTH);
1114 
1115 	// Goober5000
1116 	if (optional_string("$Substitute Event Music:"))
1117 		stuff_string(pm->substitute_event_music_name, F_NAME, NAME_LENGTH);
1118 
1119 	required_string("$Briefing Music:");
1120 	stuff_string(pm->briefing_music_name, F_NAME, NAME_LENGTH);
1121 
1122 	// Goober5000
1123 	if (optional_string("$Substitute Briefing Music:"))
1124 		stuff_string(pm->substitute_briefing_music_name, F_NAME, NAME_LENGTH);
1125 
1126 	// old stuff, apparently
1127 	if (optional_string("$Debriefing Success Music:"))
1128 	{
1129 		stuff_string(temp, F_NAME, NAME_LENGTH);
1130 		index = event_music_get_spooled_music_index(temp);
1131 		if ((index >= 0) && ((Spooled_music[index].flags & SMF_VALID) || Fred_running)) {
1132 			event_music_set_score(SCORE_DEBRIEF_SUCCESS, temp);
1133 		}
1134 	}
1135 
1136 	// not old, just added since it makes sense
1137 	if (optional_string("$Debriefing Average Music:"))
1138 	{
1139 		stuff_string(temp, F_NAME, NAME_LENGTH);
1140 		index = event_music_get_spooled_music_index(temp);
1141 		if ((index >= 0) && ((Spooled_music[index].flags & SMF_VALID) || Fred_running)) {
1142 			event_music_set_score(SCORE_DEBRIEF_AVERAGE, temp);
1143 		}
1144 	}
1145 
1146 	// old stuff
1147 	if (optional_string("$Debriefing Fail Music:"))
1148 	{
1149 		stuff_string(temp, F_NAME, NAME_LENGTH);
1150 		index = event_music_get_spooled_music_index(temp);
1151 		if ((index >= 0) && ((Spooled_music[index].flags & SMF_VALID) || Fred_running)) {
1152 			event_music_set_score(SCORE_DEBRIEF_FAIL, temp);
1153 		}
1154 	}
1155 
1156 	// new stuff
1157 	if (optional_string("$Fiction Viewer Music:"))
1158 	{
1159 		stuff_string(temp, F_NAME, NAME_LENGTH);
1160 		event_music_set_score(SCORE_FICTION_VIEWER, temp);
1161 	}
1162 
1163 	// Goober5000 - if briefing not specified in import, default to BRIEF1
1164 	if (!stricmp(pm->briefing_music_name, "none") && (flags & MPF_IMPORT_FSM))
1165 		strcpy_s(pm->briefing_music_name, "BRIEF1");
1166 
1167 	// Goober5000 - old way of grabbing substitute music, but here for reverse compatibility
1168 	if (optional_string("$Substitute Music:"))
1169 	{
1170 		stuff_string(pm->substitute_event_music_name, F_NAME, NAME_LENGTH, "," );
1171 		Mp++;
1172 		stuff_string(pm->substitute_briefing_music_name, F_NAME, NAME_LENGTH);
1173 	}
1174 
1175 	// Goober5000 - if the mission is being imported, the substitutes are the specified tracks
1176 	// (with FS1 prefixes) and we generate new specified tracks
1177 	if (flags & MPF_IMPORT_FSM)
1178 	{
1179 		// no specified music?
1180 		if (!stricmp(pm->event_music_name, "none"))
1181 			goto done_event_music;
1182 
1183 		// set the FS1 equivalent as the substitute
1184 		strcpy_s(pm->substitute_event_music_name, "FS1-");
1185 		strcat_s(pm->substitute_event_music_name, pm->event_music_name);
1186 
1187 		// if we have Marauder, it's in FS2 as Deuteronomy, so we're done
1188 		if (!stricmp(pm->event_music_name, "7: Marauder") && event_music_get_soundtrack_index("5: Deuteronomy") >= 0)
1189 		{
1190 			strcpy_s(pm->event_music_name, "5: Deuteronomy");
1191 			goto done_event_music;
1192 		}
1193 
1194 		// search for something with the same track number
1195 		strcpy_s(temp, pm->event_music_name);
1196 		ch = strchr(temp, ':');
1197 		if (ch != NULL)
1198 		{
1199 			*(ch + 1) = '\0';
1200 
1201 			for (i = 0; i < Num_soundtracks; i++)
1202 			{
1203 				if (!strncmp(temp, Soundtracks[i].name, strlen(temp)))
1204 				{
1205 					strcpy_s(pm->event_music_name, Soundtracks[i].name);
1206 					goto done_event_music;
1207 				}
1208 			}
1209 		}
1210 
1211 		// last resort: pick a random track out of the 7 FS2 soundtracks
1212 		num = (Num_soundtracks < 7) ? Num_soundtracks : 7;
1213 		strcpy_s(pm->event_music_name, Soundtracks[Random::next(num)].name);
1214 
1215 
1216 done_event_music:
1217 
1218 
1219 		// no specified music?
1220 		if (!stricmp(pm->briefing_music_name, "none"))
1221 			goto done_briefing_music;
1222 
1223 		// set the FS1 equivalent as the substitute
1224 		strcpy_s(pm->substitute_briefing_music_name, "FS1-");
1225 		strcat_s(pm->substitute_briefing_music_name, pm->briefing_music_name);
1226 
1227 		// Choco Mousse is the FS1 title soundtrack, so use Aquitaine in FS2
1228 		if (!stricmp(pm->briefing_music_name, "Choco Mousse") && event_music_get_spooled_music_index("Aquitaine") >= 0)
1229 		{
1230 			strcpy_s(pm->briefing_music_name, "Aquitaine");
1231 			goto done_briefing_music;
1232 		}
1233 
1234 		// we might have a match with the same track number
1235 		if (event_music_get_spooled_music_index(pm->briefing_music_name) >= 0)
1236 			goto done_briefing_music;
1237 
1238 		// last resort: pick a random track out of the first 7 FS2 briefings (the regular ones)...
1239 		num = (Num_music_files < 7) ? Num_music_files : 7;
1240 		strcpy_s(pm->briefing_music_name, Spooled_music[Random::next(num)].name);
1241 
1242 
1243 done_briefing_music:
1244 
1245 		/* NO-OP */ ;
1246 	}
1247 
1248 
1249 	// set the soundtrack, preferring the substitute in FS2 (not FRED!)
1250 	index = event_music_get_soundtrack_index(pm->substitute_event_music_name);
1251 	if ((index >= 0) && (Soundtracks[index].flags & EMF_VALID) && !Fred_running)
1252 	{
1253 		event_music_set_soundtrack(pm->substitute_event_music_name);
1254 	}
1255 	else
1256 	{
1257 		event_music_set_soundtrack(pm->event_music_name);
1258 	}
1259 
1260 	// set the briefing, preferring the substitute in FS2 (not FRED!)
1261 	index = event_music_get_spooled_music_index(pm->substitute_briefing_music_name);
1262 	if ((index >= 0) && (Spooled_music[index].flags & SMF_VALID) && !Fred_running)
1263 	{
1264 		event_music_set_score(SCORE_BRIEFING, pm->substitute_briefing_music_name);
1265 	}
1266 	else
1267 	{
1268 		event_music_set_score(SCORE_BRIEFING, pm->briefing_music_name);
1269 	}
1270 }
1271 
1272 /**
1273  * Parse fiction viewer
1274  */
parse_fiction(mission *)1275 void parse_fiction(mission * /*pm*/)
1276 {
1277 	fiction_viewer_reset();
1278 
1279 	if (optional_string("#Fiction Viewer"))
1280 	{
1281 		bool fiction_viewer_loaded = false;
1282 
1283 		while (check_for_string("$File:"))
1284 		{
1285 			fiction_viewer_stage stage;
1286 			memset(&stage, 0, sizeof(fiction_viewer_stage));
1287 			stage.formula = Locked_sexp_true;
1288 
1289 			required_string("$File:");
1290 			stuff_string(stage.story_filename, F_FILESPEC, MAX_FILENAME_LEN);
1291 
1292 			if (optional_string("$Font:"))
1293 				stuff_string(stage.font_filename, F_FILESPEC, MAX_FILENAME_LEN);
1294 
1295 			if (optional_string("$Voice:"))
1296 				stuff_string(stage.voice_filename, F_FILESPEC, MAX_FILENAME_LEN);
1297 
1298 			if (optional_string("$UI:"))
1299 				stuff_string(stage.ui_name, F_NAME, NAME_LENGTH);
1300 
1301 			parse_custom_bitmap("$Background 640:", "$Background 1024:", stage.background[GR_640], stage.background[GR_1024]);
1302 
1303 			// get the sexp if we have one
1304 			if (optional_string("$Formula:"))
1305 				stage.formula = get_sexp_main();
1306 
1307 			if (strlen(stage.story_filename) > 0)
1308 			{
1309 				// now, store this stage
1310 				Fiction_viewer_stages.push_back(stage);
1311 
1312 				// see if this is the stage we want to display, then display it
1313 				if (!Fred_running && !fiction_viewer_loaded && is_sexp_true(stage.formula))
1314 				{
1315 					fiction_viewer_load((int)(Fiction_viewer_stages.size() - 1));
1316 					fiction_viewer_loaded = true;
1317 				}
1318 			}
1319 			else
1320 			{
1321 				error_display(0, "Fiction viewer story filename may not be empty!");
1322 			}
1323 		}
1324 	}
1325 }
1326 
1327 /**
1328  * Parse command briefing
1329  */
parse_cmd_brief(mission *)1330 void parse_cmd_brief(mission * /*pm*/)
1331 {
1332 	int stage;
1333 
1334 	Assert(!Cur_cmd_brief->num_stages);
1335 	stage = 0;
1336 
1337 	required_string("#Command Briefing");
1338 
1339 	// Yarn - use the same code as for mission loading screens
1340 	parse_custom_bitmap("$Background 640:", "$Background 1024:", Cur_cmd_brief->background[GR_640], Cur_cmd_brief->background[GR_1024]);
1341 
1342 	while (optional_string("$Stage Text:")) {
1343 		Assert(stage < CMD_BRIEF_STAGES_MAX);
1344 		stuff_string(Cur_cmd_brief->stage[stage].text, F_MULTITEXT, NULL);
1345 
1346 		required_string("$Ani Filename:");
1347 		stuff_string(Cur_cmd_brief->stage[stage].ani_filename, F_FILESPEC, MAX_FILENAME_LEN);
1348 		if (optional_string("+Wave Filename:"))
1349 			stuff_string(Cur_cmd_brief->stage[stage].wave_filename, F_FILESPEC, MAX_FILENAME_LEN);
1350 		else
1351 			Cur_cmd_brief->stage[stage].wave_filename[0] = 0;
1352 
1353 		stage++;
1354 	}
1355 
1356 	Cur_cmd_brief->num_stages = stage;
1357 }
1358 
parse_cmd_briefs(mission * pm)1359 void parse_cmd_briefs(mission *pm)
1360 {
1361 	int i;
1362 
1363 	cmd_brief_reset();
1364 	// a hack follows because old missions don't have a command briefing
1365 	if (required_string_either("#Command Briefing", "#Briefing"))
1366 		return;
1367 
1368 	for (i=0; i<Num_teams; i++) {
1369 		Cur_cmd_brief = &Cmd_briefs[i];
1370 		parse_cmd_brief(pm);
1371 	}
1372 }
1373 
1374 /**
1375  * Parse the data required for the mission briefing
1376  *
1377  * NOTE: This updates the global Briefing struct with all the data necessary to drive the briefing
1378  */
parse_briefing(mission *,int flags)1379 void parse_briefing(mission * /*pm*/, int flags)
1380 {
1381 	int nt, i, j, stage_num = 0, icon_num = 0;
1382 	brief_stage *bs;
1383 	brief_icon *bi;
1384 	briefing *bp;
1385 
1386 	char not_used_text[MAX_ICON_TEXT_LEN];
1387 
1388 	brief_reset();
1389 
1390 	// MWA -- 2/3/98.  we can now have multiple briefing and debriefings in a mission
1391 	for ( nt = 0; nt < Num_teams; nt++ ) {
1392 		if ( !optional_string("#Briefing") )
1393 			break;
1394 
1395 		bp = &Briefings[nt];
1396 		required_string("$start_briefing");
1397 
1398 		// Goober5000 - use the same code as for mission loading screens
1399 		parse_custom_bitmap("$briefing_background_640:", "$briefing_background_1024:", bp->background[GR_640], bp->background[GR_1024]);
1400 		parse_custom_bitmap("$ship_select_background_640:", "$ship_select_background_1024:", bp->ship_select_background[GR_640], bp->ship_select_background[GR_1024]);
1401 		parse_custom_bitmap("$weapon_select_background_640:", "$weapon_select_background_1024:", bp->weapon_select_background[GR_640], bp->weapon_select_background[GR_1024]);
1402 
1403 		required_string("$num_stages:");
1404 		stuff_int(&bp->num_stages);
1405 		Assert(bp->num_stages <= MAX_BRIEF_STAGES);
1406 
1407 		stage_num = 0;
1408 		while (required_string_either("$end_briefing", "$start_stage")) {
1409 			required_string("$start_stage");
1410 			Assert(stage_num < MAX_BRIEF_STAGES);
1411 
1412 			if (stage_num >= bp->num_stages) {
1413 				error_display(1,
1414 							  "$num_stages did not match the number of specified stages! %d stages were specified but there is at least one more.",
1415 							  bp->num_stages);
1416 			}
1417 
1418 			bs = &bp->stages[stage_num++];
1419 			required_string("$multi_text");
1420 			stuff_string(bs->text, F_MULTITEXT, NULL);
1421 			required_string("$voice:");
1422 			stuff_string(bs->voice, F_FILESPEC, MAX_FILENAME_LEN);
1423 			required_string("$camera_pos:");
1424 			stuff_vec3d(&bs->camera_pos);
1425 			required_string("$camera_orient:");
1426 			stuff_matrix(&bs->camera_orient);
1427 			required_string("$camera_time:");
1428 			stuff_int(&bs->camera_time);
1429 
1430 			if ( optional_string("$num_lines:") ) {
1431 				stuff_int(&bs->num_lines);
1432 
1433 				if ( Fred_running )	{
1434 					Assert(bs->lines!=NULL);
1435 				} else {
1436 					if ( bs->num_lines > 0 )	{
1437 						bs->lines = (brief_line *)vm_malloc(sizeof(brief_line)*bs->num_lines);
1438 						Assert(bs->lines!=NULL);
1439 					}
1440 				}
1441 
1442 				for (i=0; i<bs->num_lines; i++) {
1443 					required_string("$line_start:");
1444 					stuff_int(&bs->lines[i].start_icon);
1445 					required_string("$line_end:");
1446 					stuff_int(&bs->lines[i].end_icon);
1447 				}
1448 			}
1449 			else {
1450 				bs->num_lines = 0;
1451 			}
1452 
1453 			required_string("$num_icons:");
1454 			stuff_int(&bs->num_icons);
1455 
1456 			if ( Fred_running )	{
1457 				Assert(bs->icons!=NULL);
1458 			} else {
1459 				if ( bs->num_icons > 0 )	{
1460 					bs->icons = (brief_icon *)vm_malloc(sizeof(brief_icon)*bs->num_icons);
1461 					Assert(bs->icons!=NULL);
1462 				}
1463 			}
1464 
1465 			if ( optional_string("$flags:") )
1466 				stuff_int(&bs->flags);
1467 			else
1468 				bs->flags = 0;
1469 
1470 			if ( optional_string("$formula:") )
1471 				bs->formula = get_sexp_main();
1472 			else
1473 				bs->formula = Locked_sexp_true;
1474 
1475 			Assert(bs->num_icons <= MAX_STAGE_ICONS );
1476 
1477 			// static alias stuff - stupid, but it seems to be necessary
1478 			static const char *temp_team_names[MAX_IFFS];
1479 			for (i = 0; i < Num_iffs; i++)
1480 				temp_team_names[i] = Iff_info[i].iff_name;
1481 
1482 			while (required_string_either("$end_stage", "$start_icon"))
1483 			{
1484 				required_string("$start_icon");
1485 				Assert(icon_num < MAX_STAGE_ICONS);
1486 				// Make sure we don't cause a buffer overflow if $num_icons is wrong
1487 				if (icon_num >= bs->num_icons) {
1488 					error_display(1,
1489 								  "$num_icons did not match the number of specified icons! %d icons were specified but there is at least one more.",
1490 								  bs->num_icons);
1491 				}
1492 				bi = &bs->icons[icon_num++];
1493 
1494 				required_string("$type:");
1495 				stuff_int(&bi->type);
1496 
1497 				// Goober5000 - import
1498 				if (flags & MPF_IMPORT_FSM)
1499 				{
1500 					// someone changed the jump node icon to a Knossos, so change it back
1501 					if (bi->type == ICON_KNOSSOS_DEVICE)
1502 						bi->type = ICON_JUMP_NODE;
1503 
1504 					// change largeship to transport
1505 					else if (bi->type == ICON_LARGESHIP)
1506 						bi->type = ICON_TRANSPORT;
1507 
1508 					// ditto
1509 					else if (bi->type == ICON_LARGESHIP_WING)
1510 						bi->type = ICON_TRANSPORT_WING;
1511 				}
1512 
1513 				find_and_stuff("$team:", &bi->team, F_NAME, temp_team_names, Num_iffs, "team name");
1514 
1515 				find_and_stuff("$class:", &bi->ship_class, F_NAME, Ship_class_names, Ship_info.size(), "ship class");
1516 				bi->modelnum = -1;
1517 				bi->model_instance_num = -1;
1518 
1519 				// Goober5000 - import
1520 				if (flags & MPF_IMPORT_FSM)
1521 				{
1522 					// the Faustus is a largeship
1523 					if (!strnicmp(Ship_info[bi->ship_class].name, "GTSC Faustus", 12))
1524 					{
1525 						if (bi->type == ICON_CRUISER)
1526 							bi->type = ICON_LARGESHIP;
1527 
1528 						else if (bi->type == ICON_CRUISER_WING)
1529 							bi->type = ICON_LARGESHIP_WING;
1530 					}
1531 					// the Hades is a supercap
1532 					else if (!strnicmp(Ship_info[bi->ship_class].name, "GTD Hades", 9))
1533 					{
1534 						bi->type = ICON_SUPERCAP;
1535 					}
1536 				}
1537 
1538 				required_string("$pos:");
1539 				stuff_vec3d(&bi->pos);
1540 
1541 				bi->label[0] = 0;
1542 				if (optional_string("$label:"))
1543 					stuff_string(bi->label, F_MESSAGE, MAX_LABEL_LEN);
1544 
1545 				if (optional_string("+id:")) {
1546 					stuff_int(&bi->id);
1547 					if (bi->id >= Cur_brief_id)
1548 						Cur_brief_id = bi->id + 1;
1549 
1550 				} else {
1551 					bi->id = -1;
1552 					for (i=0; i<stage_num-1; i++)
1553 						for (j=0; j < bp->stages[i].num_icons; j++)
1554 						{
1555 							if (!stricmp(bp->stages[i].icons[j].label, bi->label))
1556 								bi->id = bp->stages[i].icons[j].id;
1557 						}
1558 
1559 					if (bi->id < 0)
1560 						bi->id = Cur_brief_id++;
1561 				}
1562 
1563 				bi->flags=0;
1564 				int val;
1565 				required_string("$hlight:");
1566 				stuff_int(&val);
1567 				if ( val>0 ) {
1568 					bi->flags |= BI_HIGHLIGHT;
1569 				}
1570 
1571 				if (optional_string("$mirror:"))
1572 				{
1573 					stuff_int(&val);
1574 					if ( val>0 ) {
1575 						bi->flags |= BI_MIRROR_ICON;
1576 					}
1577 				}
1578 
1579 				if (optional_string("$use wing icon:"))
1580 				{
1581 					stuff_int(&val);
1582 					if ( val>0 ) {
1583 						bi->flags |= BI_USE_WING_ICON;
1584 					}
1585 				}
1586 
1587 				required_string("$multi_text");
1588 				stuff_string(not_used_text, F_MULTITEXT, MAX_ICON_TEXT_LEN);
1589 				required_string("$end_icon");
1590 			} // end while
1591 			if (icon_num != bs->num_icons) {
1592 				error_display(1,
1593 							  "$num_icons did not match the number of specified icons! %d icons were specified but only %d were parsed.",
1594 							  bs->num_icons,
1595 							  icon_num);
1596 			}
1597 			icon_num = 0;
1598 			required_string("$end_stage");
1599 		}	// end while
1600 		if (stage_num != bp->num_stages) {
1601 			error_display(1,
1602 						  "$num_stages did not match the number of specified icons! %d stages were specified but only %d were parsed.",
1603 						  bp->num_stages,
1604 						  stage_num);
1605 		}
1606 		required_string("$end_briefing");
1607 	}
1608 
1609 	if ( nt != Num_teams )
1610 		Error(LOCATION, "Not enough briefings in mission file.  There are %d teams and only %d briefings.", Num_teams, nt );
1611 }
1612 
1613 /**
1614  * Parse the data required for the mission debriefings
1615  */
parse_debriefing_new(mission *)1616 void parse_debriefing_new(mission * /*pm*/)
1617 {
1618 	int				stage_num, nt;
1619 	debriefing		*db;
1620 	debrief_stage	*dbs;
1621 
1622 	debrief_reset();
1623 
1624 	// 2/3/98 -- MWA.  We can now have multiple briefings and debriefings on a team
1625 	for ( nt = 0; nt < Num_teams; nt++ ) {
1626 
1627 		if ( !optional_string("#Debriefing_info") )
1628 			break;
1629 
1630 		stage_num = 0;
1631 
1632 		db = &Debriefings[nt];
1633 
1634 		// Yarn - use the same code as for mission loading screens
1635 		parse_custom_bitmap("$Background 640:", "$Background 1024:", db->background[GR_640], db->background[GR_1024]);
1636 
1637 		required_string("$Num stages:");
1638 		stuff_int(&db->num_stages);
1639 		Assert(db->num_stages <= MAX_DEBRIEF_STAGES);
1640 
1641 		while (required_string_either("#", "$Formula")) {
1642 			Assert(stage_num < MAX_DEBRIEF_STAGES);
1643 			dbs = &db->stages[stage_num++];
1644 			required_string("$Formula:");
1645 			dbs->formula = get_sexp_main();
1646 			required_string("$multi text");
1647 			stuff_string(dbs->text, F_MULTITEXT, NULL);
1648 			required_string("$Voice:");
1649 			stuff_string(dbs->voice, F_FILESPEC, MAX_FILENAME_LEN);
1650 			required_string("$Recommendation text:");
1651 			stuff_string(dbs->recommendation_text, F_MULTITEXT, NULL);
1652 		} // end while
1653 
1654 		Assert(db->num_stages == stage_num);
1655 	}
1656 
1657 	if ( nt != Num_teams )
1658 		Error(LOCATION, "Not enough debriefings for mission.  There are %d teams and only %d debriefings;\n", Num_teams, nt );
1659 }
1660 
position_ship_for_knossos_warpin(object * objp)1661 void position_ship_for_knossos_warpin(object *objp)
1662 {
1663 	ship *shipp = &Ships[objp->instance];
1664 	ship_info *sip = &Ship_info[shipp->ship_info_index];
1665 	object *knossos_objp = nullptr;
1666 	float half_length, min_dist = -1.0f;
1667 	vec3d center_pos, actual_local_center;
1668 	vec3d new_point, new_center_pos, offset;
1669 
1670 	// Assume no valid knossos device
1671 	shipp->special_warpin_objnum = -1;
1672 
1673 	// find closest knossos device (allow multiple knossoses)
1674 	for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
1675 	{
1676 		object *ship_objp = &Objects[so->objnum];
1677 
1678 		if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags[Ship::Info_Flags::Knossos_device])
1679 		{
1680 			// is this the closest?  (can use dist_squared since we're only comparing)
1681 			float dist = vm_vec_dist_squared(&ship_objp->pos, &objp->pos);
1682 			if (min_dist < 0.0f || dist < min_dist)
1683 			{
1684 				knossos_objp = ship_objp;
1685 				min_dist = dist;
1686 			}
1687 		}
1688 	}
1689 
1690 	if (knossos_objp == nullptr)
1691 		return;
1692 
1693 	// set ship special_warpin_objnum
1694 	shipp->special_warpin_objnum = OBJ_INDEX(knossos_objp);
1695 
1696 	// determine the correct center of the model (which may not be the model's origin)
1697 	if (object_is_docked(objp))
1698 		dock_calc_docked_actual_center(&actual_local_center, objp);
1699 	else
1700 		ship_class_get_actual_center(sip, &actual_local_center);
1701 
1702 	// find world position of the center of the ship assembly
1703 	vm_vec_unrotate(&center_pos, &actual_local_center, &objp->orient);
1704 	vm_vec_add2(&center_pos, &objp->pos);
1705 
1706 	// determine the half-length
1707 	if (object_is_docked(objp))
1708 	{
1709 		// we need to get the longitudinal radius of our ship, so find the semilatus rectum along the Z-axis
1710 		half_length = dock_calc_max_semilatus_rectum_parallel_to_axis(objp, Z_AXIS);
1711 	}
1712 	else
1713 		half_length = 0.5f * ship_class_get_length(sip);
1714 
1715 	// position self for warp on plane of device
1716 	float dist = fvi_ray_plane(&new_point, &knossos_objp->pos, &knossos_objp->orient.vec.fvec, &center_pos, &objp->orient.vec.fvec, 0.0f);
1717 	vm_vec_scale_add(&new_center_pos, &center_pos, &objp->orient.vec.fvec, (dist - half_length));
1718 
1719 	// now move the actual ship based on how we moved the center
1720 	vm_vec_sub(&offset, &new_center_pos, &center_pos);
1721 	vm_vec_add2(&objp->pos, &offset);
1722 
1723 	// if ship is HUGE, make it go through the center of the knossos
1724 	if (sip->is_huge_ship())
1725 	{
1726 		vm_vec_sub(&offset, &knossos_objp->pos, &new_point);
1727 		vm_vec_add2(&objp->pos, &offset);
1728 	}
1729 }
1730 
1731 /**
1732  * This is conceptually almost the same as ::obj_move_one_docked_object and is used in the same way.
1733  */
parse_dock_one_docked_object(p_object * pobjp,p_object * parent_pobjp)1734 void parse_dock_one_docked_object(p_object *pobjp, p_object *parent_pobjp)
1735 {
1736 	int dockpoint, parent_dockpoint;
1737 	char *dockpoint_name, *parent_dockpoint_name;
1738 	object *objp, *parent_objp;
1739 
1740 	// get the actual in-game objects that will be docked
1741 	objp = pobjp->created_object;
1742 	parent_objp = parent_pobjp->created_object;
1743 
1744 	// check valid
1745 	if (!objp || !parent_objp)
1746 	{
1747 		Int3();
1748 		return;
1749 	}
1750 
1751 	// get dockpoint names
1752 	dockpoint_name = dock_find_dockpoint_used_by_object(pobjp, parent_pobjp);
1753 	parent_dockpoint_name = dock_find_dockpoint_used_by_object(parent_pobjp, pobjp);
1754 
1755 	// check valid
1756 	if (!dockpoint_name || !parent_dockpoint_name)
1757 	{
1758 		Int3();
1759 		return;
1760 	}
1761 
1762 	// resolve names to dockpoints
1763 	dockpoint = model_find_dock_name_index(Ship_info[Ships[objp->instance].ship_info_index].model_num, dockpoint_name);
1764 	parent_dockpoint = model_find_dock_name_index(Ship_info[Ships[parent_objp->instance].ship_info_index].model_num, parent_dockpoint_name);
1765 
1766 	// check valid
1767 	if ((dockpoint < 0) || (parent_dockpoint < 0))
1768 	{
1769 		if (dockpoint < 0)
1770 			ReleaseWarning(LOCATION, "Dockpoint %s could not be found on model %s", dockpoint_name, model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num)->filename);
1771 		if (parent_dockpoint < 0)
1772 			ReleaseWarning(LOCATION, "Dockpoint %s could not be found on model %s", parent_dockpoint_name, model_get(Ship_info[Ships[parent_objp->instance].ship_info_index].model_num)->filename);
1773 
1774 		return;
1775 	}
1776 
1777 	// dock them
1778 	nprintf(("AI", "Initially docked: %s to parent %s\n", Ships[objp->instance].ship_name, Ships[parent_objp->instance].ship_name));
1779 	ai_dock_with_object(objp, dockpoint, parent_objp, parent_dockpoint, AIDO_DOCK_NOW);
1780 }
1781 
1782 int parse_create_object_sub(p_object *objp);
1783 
1784 // Goober5000
parse_create_docked_object_helper(p_object * pobjp,p_dock_function_info * infop)1785 void parse_create_docked_object_helper(p_object *pobjp, p_dock_function_info *infop)
1786 {
1787 	// create the object
1788 	parse_create_object_sub(pobjp);
1789 
1790 	// remove the object from the arrival list
1791 	// (don't remove the leader, because he'll be removed the usual way)
1792 	if (pobjp != infop->parameter_variables.objp_value)
1793 	{
1794 		// If an object is present at the beginning of the mission, it may not have been put on the arrival
1795 		// list.  This is totally dependent on the order the objects are parsed in the mission file because
1796 		// that is the order in which they are created.  All the objects in a docked group are created
1797 		// simultaneously when and only when the leader is created.
1798 
1799 		// if it's on the list, remove it
1800 		if (parse_object_on_arrival_list(pobjp))
1801 			list_remove(&Ship_arrival_list, pobjp);
1802 	}
1803 }
1804 
1805 /**
1806  * This is a bit tricky because of the way initial docking is now handled.
1807  * Docking groups require special treatment.
1808  */
parse_create_object(p_object * pobjp)1809 int parse_create_object(p_object *pobjp)
1810 {
1811 	object *objp;
1812 
1813 	// if this guy is part of a dock group, create the entire group, starting with the leader
1814 	if (object_is_docked(pobjp))
1815 	{
1816 		p_dock_function_info dfi;
1817 
1818 		// we should only be calling this for dock leaders, because the dock leader
1819 		// governs the creation of his entire group
1820 		Assert((pobjp->flags[Mission::Parse_Object_Flags::SF_Dock_leader]));
1821 
1822 		// if the leader will be destroyed before the mission starts, then *only* create the leader;
1823 		// don't create the rest of the group (this is what retail did)
1824 		if (pobjp->destroy_before_mission_time >= 0)
1825 			return parse_create_object_sub(pobjp);
1826 
1827 		// store the leader as a parameter
1828 		dfi.parameter_variables.objp_value = pobjp;
1829 
1830 		// Just as we couldn't create the parse dock trees until we had parsed them all,
1831 		// we can't dock the in-game objects until we have created them all. :p
1832 
1833 		// create all the objects
1834 		dock_evaluate_all_docked_objects(pobjp, &dfi, parse_create_docked_object_helper);
1835 
1836 		// dock all the objects
1837 		dock_dock_docked_objects(pobjp);
1838 
1839 		// now clear the handled flags
1840 		parse_object_clear_all_handled_flags();
1841 	}
1842 	// create normally
1843 	else
1844 	{
1845 		parse_create_object_sub(pobjp);
1846 	}
1847 
1848 	// get the main object
1849 	objp = pobjp->created_object;
1850 
1851 	// if arriving through knossos, adjust objp->pos to plane of knossos and set object reference
1852 	// special warp is single player only
1853 	if ((pobjp->flags[Mission::Parse_Object_Flags::Knossos_warp_in]) && !(Game_mode & GM_MULTIPLAYER))
1854 	{
1855 		if (!Fred_running)
1856 			position_ship_for_knossos_warpin(objp);
1857 	}
1858 
1859 	// warp it in (moved from parse_create_object_sub)
1860 	if ((Game_mode & GM_IN_MISSION) && (!Fred_running) && (!Game_restoring))
1861 	{
1862 		if ((Ships[objp->instance].wingnum < 0) && (pobjp->arrival_location != ARRIVE_FROM_DOCK_BAY))
1863 		{
1864 			shipfx_warpin_start(objp);
1865 		}
1866 	}
1867 
1868 	// return the main object's objnum
1869 	return OBJ_INDEX(objp);
1870 }
1871 
1872 void parse_bring_in_docked_wing(p_object *p_objp, int wingnum, int shipnum);
1873 
1874 /**
1875  * Given a stuffed p_object struct, create an object and fill in the necessary fields.
1876  * @return object number.
1877  */
parse_create_object_sub(p_object * p_objp)1878 int parse_create_object_sub(p_object *p_objp)
1879 {
1880 	int	i, j, k, objnum, shipnum;
1881 	int anchor_objnum = -1;
1882 	bool brought_in_docked_wing = false;
1883 	ai_info *aip;
1884 	ship_subsys *ptr;
1885 	ship *shipp;
1886 	ship_info *sip;
1887 	subsys_status *sssp;
1888 	ship_weapon *wp;
1889 
1890 	// texture replacements
1891 	polymodel *pm;
1892 
1893 	MONITOR_INC(NumShipArrivals, 1);
1894 
1895 	// base level creation - need ship name in case of duplicate textures
1896 	objnum = ship_create(&p_objp->orient, &p_objp->pos, p_objp->ship_class, p_objp->name);
1897 	Assert(objnum != -1);
1898 	shipnum = Objects[objnum].instance;
1899 
1900 	shipp = &Ships[shipnum];
1901 	sip = &Ship_info[shipp->ship_info_index];
1902 
1903 	// Goober5000 - make the parse object aware of what it was created as
1904 	p_objp->created_object = &Objects[objnum];
1905 
1906 	// Goober5000 - if this object is being created because he's docked to something,
1907 	// and he's in a wing, then mark the wing as having arrived
1908 	if (object_is_docked(p_objp) && !(p_objp->flags[Mission::Parse_Object_Flags::SF_Dock_leader]) && (p_objp->wingnum >= 0))
1909 	{
1910 		if (!Fred_running)
1911 		{
1912 			parse_bring_in_docked_wing(p_objp, p_objp->wingnum, shipnum);
1913 			brought_in_docked_wing = true;
1914 		}
1915 	}
1916 
1917 	shipp->group = p_objp->group;
1918 	shipp->team = p_objp->team;
1919 	shipp->display_name = p_objp->display_name;
1920 	shipp->escort_priority = p_objp->escort_priority;
1921 	shipp->use_special_explosion = p_objp->use_special_explosion;
1922 	shipp->special_exp_damage = p_objp->special_exp_damage;
1923 	shipp->special_exp_blast = p_objp->special_exp_blast;
1924 	shipp->special_exp_inner = p_objp->special_exp_inner;
1925 	shipp->special_exp_outer = p_objp->special_exp_outer;
1926 	shipp->use_shockwave = p_objp->use_shockwave;
1927 	shipp->special_exp_shockwave_speed = p_objp->special_exp_shockwave_speed;
1928 	shipp->special_exp_deathroll_time = p_objp->special_exp_deathroll_time;
1929 
1930 	shipp->special_hitpoints = p_objp->special_hitpoints;
1931 	shipp->special_shield = p_objp->special_shield;
1932 
1933 	for (i=0;i<MAX_IFFS;i++)
1934 	{
1935 		for (j=0;j<MAX_IFFS;j++)
1936 		{
1937 			shipp->ship_iff_color[i][j] = p_objp->alt_iff_color[i][j];
1938 		}
1939 	}
1940 
1941 	shipp->ship_max_shield_strength = p_objp->ship_max_shield_strength;
1942 	shipp->ship_max_hull_strength =  p_objp->ship_max_hull_strength;
1943 	shipp->max_shield_recharge = p_objp->max_shield_recharge;
1944 
1945 	// Goober5000 - ugh, this is really stupid having to do this here; if the
1946 	// ship creation code was better organized this wouldn't be necessary
1947 	if (shipp->special_hitpoints > 0)
1948 	{
1949 		float hull_factor = shipp->ship_max_hull_strength / sip->max_hull_strength;
1950 		ship_subsys *ss;
1951 
1952 		for (ss = GET_FIRST(&shipp->subsys_list); ss != END_OF_LIST(&shipp->subsys_list) && ss != NULL; ss = GET_NEXT(ss))
1953 		{
1954 			ss->max_hits *= hull_factor;
1955 
1956 			if (Fred_running)
1957 				ss->current_hits = 0.0f;
1958 			else
1959 				ss->current_hits = ss->max_hits;
1960 		}
1961 
1962 		ship_recalc_subsys_strength(shipp);
1963 	}
1964 
1965 	// Goober5000 - this is also stupid; this is in ship_set but it needs to be done here because of the special hitpoints mod
1966 	Objects[objnum].hull_strength = shipp->ship_max_hull_strength;
1967 
1968 	shipp->respawn_priority = p_objp->respawn_priority;
1969 
1970 	// if this is a multiplayer dogfight game, and its from a player wing, make it team traitor
1971 	if (MULTI_DOGFIGHT && (p_objp->wingnum >= 0))
1972 	{
1973 		for (i = 0; i < MAX_STARTING_WINGS; i++)
1974 		{
1975 			if (!stricmp(Starting_wing_names[i], Wings[p_objp->wingnum].name))
1976 				shipp->team = Iff_traitor;
1977 		}
1978 	}
1979 
1980 	// alternate stuff
1981 	shipp->alt_type_index = p_objp->alt_type_index;
1982 	shipp->callsign_index = p_objp->callsign_index;
1983 
1984 	// AI stuff.  Note a lot of the AI was already initialized in ship_create.
1985 	aip = &(Ai_info[shipp->ai_index]);
1986 
1987 	aip->ai_class = p_objp->ai_class;
1988 	shipp->weapons.ai_class = p_objp->ai_class;  // Fred uses this instead of above.
1989 	//Fixes a bug where the AI class attributes were not copied if the AI class was set in the mission.
1990 	if (The_mission.ai_profile->flags[AI::Profile_Flags::Fix_ai_class_bug])
1991 		ship_set_new_ai_class(shipp, p_objp->ai_class);
1992 
1993 	aip->behavior = p_objp->behavior;
1994 	aip->mode = aip->behavior;
1995 
1996 	// make sure aim_safety has its submode defined
1997 	if (aip->mode == AIM_SAFETY) {
1998 		aip->submode = AISS_1;
1999 	}
2000 
2001 	shipp->cargo1 = p_objp->cargo1;
2002 
2003 	shipp->arrival_location = p_objp->arrival_location;
2004 	shipp->arrival_distance = p_objp->arrival_distance;
2005 	shipp->arrival_anchor = p_objp->arrival_anchor;
2006 	shipp->arrival_path_mask = p_objp->arrival_path_mask;
2007 	shipp->arrival_cue = p_objp->arrival_cue;
2008 	shipp->arrival_delay = p_objp->arrival_delay;
2009 	shipp->departure_location = p_objp->departure_location;
2010 	shipp->departure_anchor = p_objp->departure_anchor;
2011 	shipp->departure_path_mask = p_objp->departure_path_mask;
2012 	shipp->departure_cue = p_objp->departure_cue;
2013 	shipp->departure_delay = p_objp->departure_delay;
2014 	shipp->wingnum = p_objp->wingnum;
2015 	shipp->hotkey = p_objp->hotkey;
2016 	shipp->score = p_objp->score;
2017 	shipp->assist_score_pct = p_objp->assist_score_pct;
2018 	shipp->persona_index = p_objp->persona_index;
2019 	if (Ship_info[shipp->ship_info_index].uses_team_colors && !p_objp->team_color_setting.empty())
2020 		shipp->team_name = p_objp->team_color_setting;
2021 
2022 	if (p_objp->warpin_params_index >= 0)
2023 		shipp->warpin_params_index = p_objp->warpin_params_index;
2024 	if (p_objp->warpout_params_index >= 0)
2025 		shipp->warpout_params_index = p_objp->warpout_params_index;
2026 
2027 	// now that we have our correct warpout params, set the warp effects
2028 	if (!Fred_running) {
2029 		ship_set_warp_effects(&Objects[objnum]);
2030 	}
2031 
2032 	// reset texture animations
2033 	shipp->base_texture_anim_frametime = game_get_overall_frametime();
2034 
2035 	// handle the replacement textures
2036 	shipp->apply_replacement_textures(p_objp->replacement_textures);
2037 
2038 	// Copy across the alt classes (if any) for FRED
2039 	if (Fred_running) {
2040 		shipp->s_alt_classes = p_objp->alt_classes;
2041 	}
2042 
2043 	// check the parse object's flags for possible things to set on this newly created ship
2044 	resolve_parse_flags(&Objects[objnum], p_objp->flags);
2045 
2046 
2047 	// other flag checks
2048 ////////////////////////
2049 
2050 	// forcing the shields on or off depending on flags -- but only if shield strength supports it
2051 
2052 	// no strength means we can't have shields, period
2053     if (p_objp->ship_max_shield_strength == 0.0f)
2054         Objects[objnum].flags.set(Object::Object_Flags::No_shields);
2055     // force shields on means we have them regardless of other flags; per r5332 this ranks above the next check
2056     else if (p_objp->flags[Mission::Parse_Object_Flags::OF_Force_shields_on])
2057         Objects[objnum].flags.remove(Object::Object_Flags::No_shields);
2058     // intrinsic no-shields means we have them off in-game
2059     else if (!Fred_running && (sip->flags[Ship::Info_Flags::Intrinsic_no_shields]))
2060         Objects[objnum].flags.set(Object::Object_Flags::No_shields);
2061 
2062 	// don't set the flag if the mission is ongoing in a multiplayer situation. This will be set by the players in the
2063 	// game only before the game or during respawning.
2064 	// MWA -- changed the next line to remove the !(Game_mode & GM_MULTIPLAYER).  We shouldn't be setting
2065 	// this flag in single player mode -- it gets set in post process mission.
2066     if ((p_objp->flags[Mission::Parse_Object_Flags::OF_Player_start]) && (Fred_running || ((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION)))) {
2067 		Objects[objnum].flags.set(Object::Object_Flags::Player_ship);
2068 	}
2069 
2070 	// a couple of ai_info flags.  Also, do a reasonable default for the kamikaze damage regardless of
2071 	// whether this flag is set or not
2072 	if (p_objp->flags[Mission::Parse_Object_Flags::AIF_Kamikaze])
2073 	{
2074 		Ai_info[shipp->ai_index].ai_flags.set(AI::AI_Flags::Kamikaze);
2075 		Ai_info[shipp->ai_index].kamikaze_damage = p_objp->kamikaze_damage;
2076 	}
2077 
2078 	if (p_objp->flags[Mission::Parse_Object_Flags::AIF_No_dynamic])
2079 		Ai_info[shipp->ai_index].ai_flags.set(AI::AI_Flags::No_dynamic);
2080 
2081 	if (p_objp->flags[Mission::Parse_Object_Flags::SF_Red_alert_store_status])
2082 	{
2083 		if (!(Game_mode & GM_MULTIPLAYER)) {
2084 			shipp->flags.set(Ship::Ship_Flags::Red_alert_store_status);
2085 		}
2086 	}
2087 
2088 	if (p_objp->flags[Mission::Parse_Object_Flags::Knossos_warp_in])
2089 	{
2090         Objects[objnum].flags.set(Object::Object_Flags::Special_warpin);
2091 		Knossos_warp_ani_used = 1;
2092 	}
2093 
2094 	// set the orders that this ship will accept.  It will have already been set to default from the
2095 	// ship create code, so only set them if the parse object flags say they are unique
2096 	if (p_objp->flags[Mission::Parse_Object_Flags::SF_Use_unique_orders])
2097 	{
2098 		shipp->orders_accepted = p_objp->orders_accepted;
2099 
2100 		// MWA  5/15/98 -- Added the following debug code because some orders that ships
2101 		// will accept were apparently written out incorrectly with Fred.  This Int3() should
2102 		// trap these instances.
2103 #ifndef NDEBUG
2104 		if (Fred_running)
2105 		{
2106 			int default_orders, remaining_orders;
2107 
2108 			default_orders = ship_get_default_orders_accepted(&Ship_info[shipp->ship_info_index]);
2109 			remaining_orders = p_objp->orders_accepted & ~default_orders;
2110 			if (remaining_orders)
2111 			{
2112 				Warning(LOCATION, "Ship %s has orders which it will accept that are\nnot part of default orders accepted.\n\nPlease reedit this ship and change the orders again\n", shipp->ship_name);
2113 			}
2114 		}
2115 #endif
2116 	}
2117 
2118 	if (p_objp->flags[Mission::Parse_Object_Flags::SF_Dock_leader])
2119 		shipp->flags.set(Ship::Ship_Flags::Dock_leader);
2120 
2121 	if (p_objp->flags[Mission::Parse_Object_Flags::SF_Warp_broken])
2122 		shipp->flags.set(Ship::Ship_Flags::Warp_broken);
2123 
2124 	if (p_objp->flags[Mission::Parse_Object_Flags::SF_Warp_never])
2125 		shipp->flags.set(Ship::Ship_Flags::Warp_never);
2126 
2127 	if (p_objp->flags[Mission::Parse_Object_Flags::SF_Has_display_name])
2128 		shipp->flags.set(Ship::Ship_Flags::Has_display_name);
2129 
2130 ////////////////////////
2131 
2132 
2133 	// if ship is in a wing, and the wing's no_warp_effect flag is set, then set the equivalent
2134 	// flag for the ship
2135     if ((shipp->wingnum != -1) && (Wings[shipp->wingnum].flags[Ship::Wing_Flags::No_arrival_warp]))
2136         shipp->flags.set(Ship::Ship_Flags::No_arrival_warp);
2137 
2138     if ((shipp->wingnum != -1) && (Wings[shipp->wingnum].flags[Ship::Wing_Flags::No_departure_warp]))
2139         shipp->flags.set(Ship::Ship_Flags::No_departure_warp);
2140 
2141     // ditto for Kazan
2142     if ((shipp->wingnum != -1) && (Wings[shipp->wingnum].flags[Ship::Wing_Flags::Nav_carry])) {
2143 		shipp->flags.set(Ship::Ship_Flags::Navpoint_carry);
2144 	}
2145 
2146 	// if the wing index and wing pos are set for this parse object, set them for the ship.  This
2147 	// is useful in multiplayer when ships respawn
2148 	shipp->wing_status_wing_index = p_objp->wing_status_wing_index;
2149 	shipp->wing_status_wing_pos = p_objp->wing_status_wing_pos;
2150 
2151 
2152 	// set up the ai_goals for this object -- all ships created here are AI controlled.
2153 	if (p_objp->ai_goals != -1)
2154 	{
2155 		int sexp;
2156 
2157 		// set up the ai goals for this object.
2158 		for (sexp = CDR(p_objp->ai_goals); sexp != -1; sexp = CDR(sexp))
2159 			ai_add_ship_goal_sexp(sexp, AIG_TYPE_EVENT_SHIP, aip);
2160 
2161 		// free the sexpression nodes only for non-wing ships.  wing code will handle its own case
2162 		if (p_objp->wingnum < 0)
2163 			free_sexp2(p_objp->ai_goals);	// free up sexp nodes for reuse, since they aren't needed anymore.
2164 	}
2165 
2166 	Assert(sip->model_num != -1);
2167 
2168 	// initialize subsystem statii here.  The subsystems are given a percentage damaged.  So a percent value
2169 	// of 20% means that the subsystem is 20% damaged (*not* 20% of max hits).  This is opposite the way
2170 	// that the initial velocity/hull strength/shields work
2171 	i = p_objp->subsys_count;
2172 	while (i--)
2173 	{
2174 		sssp = &Subsys_status[p_objp->subsys_index + i];
2175 		if (!stricmp(sssp->name, NOX("Pilot")))
2176 		{
2177 			ptr = nullptr;
2178 			wp = &shipp->weapons;
2179 		}
2180 		else
2181 		{
2182 			ptr = ship_get_subsys(shipp, sssp->name);	// find the subsystem in the ship list that corresponds to the parsed subsystem
2183 			if (!ptr)
2184 			{
2185 				Warning(LOCATION, "Unable to find '%s' in the ship %s (class %s) subsys_list!", sssp->name, shipp->ship_name, sip->name);
2186 				continue;
2187 			}
2188 			wp = &ptr->weapons;
2189 		}
2190 
2191 		if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
2192 		{
2193 			for (j = k = 0; j < MAX_SHIP_PRIMARY_BANKS; ++j)
2194 			{
2195 				// skip over any empty primary banks unless we are in FRED
2196 				if ((sssp->primary_banks[j] >= 0) || Fred_running)
2197 				{
2198 					wp->primary_bank_weapons[k] = sssp->primary_banks[j];
2199 					++k;
2200 				}
2201 			}
2202 
2203 			if (Fred_running)
2204 			{
2205 				// only do this for the Pilot subsystem
2206 				if (!ptr)
2207 					wp->num_primary_banks = sip->num_primary_banks;
2208 			}
2209 			else
2210 				wp->num_primary_banks = k;
2211 		}
2212 
2213 		for (j = 0; j < wp->num_primary_banks; ++j)
2214 		{
2215 			if (Fred_running)
2216 			{
2217 				wp->primary_bank_ammo[j] = sssp->primary_ammo[j];
2218 			}
2219 			else if (wp->primary_bank_weapons[j] >= 0 && Weapon_info[wp->primary_bank_weapons[j]].wi_flags[Weapon::Info_Flags::Ballistic])
2220 			{
2221 				Assertion(Weapon_info[wp->primary_bank_weapons[j]].cargo_size > 0.0f,
2222 					"Primary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
2223 					shipp->ship_name, sssp->name,j,Weapon_info[wp->primary_bank_weapons[j]].name);
2224 				// Pilot subsystem ammo depends on ship ammo capacity
2225 				// in contrast non pilot subsystems depend on the bank capacity of the subsystem
2226 				int ammo_cap;
2227 				if (!ptr) {
2228 					ammo_cap = sip->primary_bank_ammo_capacity[j];
2229 				} else {
2230 					ammo_cap = wp->primary_bank_capacity[j];
2231 				}
2232 				int capacity = (int)std::lround(sssp->primary_ammo[j] / 100.0f * ammo_cap);
2233 				wp->primary_bank_ammo[j] = (int)std::lround(capacity / Weapon_info[wp->primary_bank_weapons[j]].cargo_size);
2234 			}
2235 		}
2236 
2237 		if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
2238 		{
2239 			for (j = k = 0; j < MAX_SHIP_SECONDARY_BANKS; ++j)
2240 			{
2241 				// skip over any empty secondary banks unless we are in FRED
2242 				if ((sssp->secondary_banks[j] >= 0) || Fred_running)
2243 				{
2244 					wp->secondary_bank_weapons[k] = sssp->secondary_banks[j];
2245 					++k;
2246 				}
2247 			}
2248 
2249 			if (Fred_running)
2250 			{
2251 				// only do this for the Pilot subsystem
2252 				if (!ptr)
2253 					wp->num_secondary_banks = sip->num_secondary_banks;
2254 			}
2255 			else
2256 				wp->num_secondary_banks = k;
2257 		}
2258 
2259 		for (j = 0; j < wp->num_secondary_banks; ++j)
2260 		{
2261 			if (Fred_running)
2262 			{
2263 				wp->secondary_bank_ammo[j] = sssp->secondary_ammo[j];
2264 			}
2265 			else if (wp->secondary_bank_weapons[j] >= 0)
2266 			{
2267 				Assertion(Weapon_info[wp->secondary_bank_weapons[j]].cargo_size > 0.0f,
2268 					"Secondary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
2269 					shipp->ship_name, sssp->name, j, Weapon_info[wp->secondary_bank_weapons[j]].name);
2270 				// Pilot subsystem ammo depends on ship ammo capacity
2271 				// in contrast non pilot subsystems depend on the bank capacity of the subsystem
2272 				int ammo_cap;
2273 				if (!ptr) {
2274 					ammo_cap = sip->secondary_bank_ammo_capacity[j];
2275 				} else {
2276 					ammo_cap = wp->secondary_bank_capacity[j];
2277 				}
2278 				int capacity = (int)std::lround(sssp->secondary_ammo[j] / 100.0f * ammo_cap);
2279 				wp->secondary_bank_ammo[j] = (int)std::lround(capacity / Weapon_info[wp->secondary_bank_weapons[j]].cargo_size);
2280 			}
2281 		}
2282 
2283 		// if we are parsing a Pilot subsystem, skip the rest
2284 		if (!ptr)
2285 			continue;
2286 
2287 		if (shipp->flags[Ship::Ship_Flags::Lock_all_turrets_initially])
2288 		{
2289 			// mark all turrets as locked
2290 			if(ptr->system_info->type == SUBSYSTEM_TURRET)
2291 			{
2292 				ptr->weapons.flags.set(Ship::Weapon_Flags::Turret_Lock);
2293 			}
2294 		}
2295 
2296 		if (Fred_running)
2297 		{
2298 			ptr->current_hits = sssp->percent;
2299 			ptr->max_hits = 100.0f;
2300 		}
2301 		else
2302 		{
2303 			ptr->max_hits = ptr->system_info->max_subsys_strength * (shipp->ship_max_hull_strength / sip->max_hull_strength);
2304 
2305 			float new_hits = ptr->max_hits * ((100.0f - sssp->percent) / 100.f);
2306 			if (!(ptr->flags[Ship::Subsystem_Flags::No_aggregate])) {
2307 				shipp->subsys_info[ptr->system_info->type].aggregate_current_hits -= (ptr->max_hits - new_hits);
2308 			}
2309 
2310 			if ((100.0f - sssp->percent) < 0.5)
2311 			{
2312 				ptr->current_hits = 0.0f;
2313 				if (ptr->submodel_instance_1 != nullptr)
2314 					ptr->submodel_instance_1->blown_off = true;
2315 			}
2316 			else
2317 			{
2318 				ptr->current_hits = new_hits;
2319 			}
2320 		}
2321 
2322 		ptr->subsys_cargo_name = sssp->subsys_cargo_name;
2323 
2324 		if (sssp->ai_class != SUBSYS_STATUS_NO_CHANGE)
2325 			ptr->weapons.ai_class = sssp->ai_class;
2326 
2327 		ptr->turret_animation_position = MA_POS_NOT_SET;	// model animation position is not set
2328 		ptr->turret_animation_done_time = 0;
2329 	}
2330 
2331 	// initial hull strength, shields, and velocity are all expressed as a percentage of the max value/
2332 	// so a initial_hull value of 90% means 90% of max.  This way is opposite of how subsystems are dealt
2333 	// with
2334 	if (Fred_running)
2335 	{
2336 		Objects[objnum].phys_info.speed = (float) p_objp->initial_velocity;
2337 		Objects[objnum].hull_strength = (float) p_objp->initial_hull;
2338 		Objects[objnum].shield_quadrant[0] = (float) p_objp->initial_shields;
2339 
2340 	}
2341 	else
2342 	{
2343 		int max_allowed_sparks, num_sparks, iLoop;
2344 
2345 		Objects[objnum].hull_strength = p_objp->initial_hull * shipp->ship_max_hull_strength / 100.0f;
2346 		for (iLoop = 0; iLoop<Objects[objnum].n_quadrants; iLoop++)
2347 		{
2348 			Objects[objnum].shield_quadrant[iLoop] = (float) (shipp->max_shield_recharge * p_objp->initial_shields * shield_get_max_quad(&Objects[objnum]) / 100.0f);
2349 		}
2350 
2351 		// initial velocities now do not apply to ships which warp in after mission starts
2352 		// WMC - Make it apply for ships with IN_PLACE_ANIM type
2353 		// zookeeper - Also make it apply for hyperspace warps
2354 		if (!(Game_mode & GM_IN_MISSION) || (Warp_params[shipp->warpin_params_index].warp_type == WT_IN_PLACE_ANIM || Warp_params[shipp->warpin_params_index].warp_type == WT_HYPERSPACE))
2355 		{
2356 			Objects[objnum].phys_info.speed = (float) p_objp->initial_velocity * sip->max_speed / 100.0f;
2357 			// prev_ramp_vel needs to be in local coordinates
2358 			// set z of prev_ramp_vel to initial velocity
2359 			vm_vec_zero(&Objects[objnum].phys_info.prev_ramp_vel);
2360 			Objects[objnum].phys_info.prev_ramp_vel.xyz.z = Objects[objnum].phys_info.speed;
2361 			// convert to global coordinates and set to ship velocity and desired velocity
2362 			vm_vec_unrotate(&Objects[objnum].phys_info.vel, &Objects[objnum].phys_info.prev_ramp_vel, &Objects[objnum].orient);
2363 			Objects[objnum].phys_info.desired_vel = Objects[objnum].phys_info.vel;
2364 		}
2365 
2366 		// recalculate damage of subsystems
2367 		ship_recalc_subsys_strength(&Ships[shipnum]);
2368 
2369 		// create sparks on a ship whose hull is damaged.  We will create two sparks for every 20%
2370 		// of hull damage done.  100 means no sparks.  between 80 and 100 do two sparks.  60 and 80 is
2371 		// four, etc.
2372 		pm = model_get(sip->model_num);
2373 		max_allowed_sparks = get_max_sparks(&Objects[objnum]);
2374 		num_sparks = (int)((100.0f - p_objp->initial_hull) / 5.0f);
2375 		if (num_sparks > max_allowed_sparks)
2376 			num_sparks = max_allowed_sparks;
2377 
2378 		for (iLoop = 0; iLoop < num_sparks; iLoop++)
2379 		{
2380 			vec3d v1, v2;
2381 
2382 			// DA 10/20/98 - sparks must be chosen on the hull and not any submodel
2383 			submodel_get_two_random_points_better(sip->model_num, pm->detail[0], &v1, &v2);
2384 			ship_hit_sparks_no_rotate(&Objects[objnum], &v1);
2385 		}
2386 	}
2387 
2388 	// in mission, we add a log entry -- set ship positions for ships not in wings, and then do
2389 	// warpin effect
2390 	if ((Game_mode & GM_IN_MISSION) && (!Fred_running))
2391 	{
2392 		mission_log_add_entry(LOG_SHIP_ARRIVED, shipp->ship_name, NULL);
2393 
2394 		if (!Game_restoring)
2395 		{
2396 			// if this ship isn't in a wing, determine its arrival location
2397 			if (shipp->wingnum == -1)
2398 			{
2399 				int location;
2400 
2401 				// multiplayer clients set the arrival location of ships to be at location since their
2402 				// position has already been determined.  Don't actually set the variable since we
2403 				// don't want the warp effect to show if coming from a dock bay.
2404 				location = p_objp->arrival_location;
2405 
2406 				if (MULTIPLAYER_CLIENT)
2407 					location = ARRIVE_AT_LOCATION;
2408 
2409 				anchor_objnum = mission_set_arrival_location(p_objp->arrival_anchor, location, p_objp->arrival_distance, objnum, p_objp->arrival_path_mask, NULL, NULL);
2410 
2411 				// Goober5000 - warpin start moved to parse_create_object
2412 			}
2413 		}
2414 
2415 		// possibly add this ship to a hotkey set
2416 		// Ships can now have both a ship-hotkey and a wing-hotkey -- FSF
2417 		if (shipp->hotkey != -1)
2418 			mission_hotkey_mf_add(shipp->hotkey, shipp->objnum, HOTKEY_MISSION_FILE_ADDED);
2419 		if ((shipp->wingnum != -1) && (Wings[shipp->wingnum].hotkey != -1))
2420 			mission_hotkey_mf_add(Wings[shipp->wingnum].hotkey, shipp->objnum, HOTKEY_MISSION_FILE_ADDED);
2421 
2422 		// possibly add this ship to the hud escort list
2423 		if (shipp->flags[Ship::Ship_Flags::Escort])
2424 			hud_add_remove_ship_escort(objnum, 1);
2425 	}
2426 
2427 	// for multiplayer games, make a call to the network code to assign the object signature
2428 	// of the newly created object.  The network host of the netgame will always assign a signature
2429 	// to a newly created object.  The network signature will get to the clients of the game in
2430 	// different manners depending on whether or not an individual ship or a wing was created.
2431 	if (Game_mode & GM_MULTIPLAYER)
2432 	{
2433 		Objects[objnum].net_signature = p_objp->net_signature;
2434 
2435 		// Goober5000 - for an initially docked group, only send the packet for the dock leader... this is necessary so that the
2436 		// docked hierarchy of objects can be created in the right order on the client side
2437 		if (!object_is_docked(p_objp) || (p_objp->flags[Mission::Parse_Object_Flags::SF_Dock_leader]))
2438 		{
2439 			if ((Game_mode & GM_IN_MISSION) && MULTIPLAYER_MASTER && (p_objp->wingnum == -1))
2440 				send_ship_create_packet(&Objects[objnum], (p_objp == Arriving_support_ship));
2441 		}
2442 		// also add this ship to the multi ship tracking and interpolation struct
2443 		multi_ship_record_add_ship(objnum);
2444 	}
2445 
2446 	// If the ship is in a wing, this will be done in mission_set_wing_arrival_location() instead
2447 	// If the ship is in a wing, but the wing is docked then addition of bool brought_in_docked_wing accounts for that status --wookieejedi
2448 	if (Game_mode & GM_IN_MISSION && ((shipp->wingnum == -1) || (brought_in_docked_wing))) {
2449 		object *anchor_objp = (anchor_objnum >= 0) ? &Objects[anchor_objnum] : nullptr;
2450 
2451 		if (Script_system.IsActiveAction(CHA_ONSHIPARRIVE)) {
2452 			Script_system.SetHookObjects(2, "Ship", &Objects[objnum], "Parent", anchor_objp);
2453 			Script_system.RunCondition(CHA_ONSHIPARRIVE, &Objects[objnum]);
2454 			Script_system.RemHookVars({"Ship", "Parent"});
2455 		}
2456 	}
2457 
2458 	return objnum;
2459 }
2460 
2461 /**
2462  * There are a bunch of assumptions in the code that, in FS2, the wing will be created first, and
2463  * then it will create its component ships.  If a wing arrives because all its ships were docked
2464  * to something else, these assumptions are turned inside out.  So we have to sort of bootstrap
2465  * the creation of the wing by running a subset of the code from parse_wing_create_ships().
2466  */
parse_bring_in_docked_wing(p_object * p_objp,int wingnum,int shipnum)2467 void parse_bring_in_docked_wing(p_object *p_objp, int wingnum, int shipnum)
2468 {
2469 	Assert(!Fred_running);
2470 	Assert(p_objp != NULL);
2471 	Assert(wingnum >= 0);
2472 	Assert(shipnum >= 0);
2473 	int j, index;
2474 	wing *wingp = &Wings[wingnum];
2475 
2476 	// link ship and wing together
2477 	// (do this first because mission log relies on ship_index)
2478 	wingp->ship_index[p_objp->pos_in_wing] = shipnum;
2479 	Ships[shipnum].wingnum = wingnum;
2480 
2481 	// has this wing arrived at all yet?
2482 	if (wingp->current_wave == 0)
2483 	{
2484 		wingp->current_wave++;
2485 		mission_log_add_entry( LOG_WING_ARRIVED, wingp->name, NULL, wingp->current_wave );
2486 		wingp->wave_delay_timestamp = timestamp(-1);
2487 	}
2488 	// how did we get more than one wave here?
2489 	else if (wingp->current_wave > 1)
2490 		Error(LOCATION, "Wing %s was created from docked ships but somehow has more than one wave!", wingp->name);
2491 
2492 	// increment tallies
2493 	wingp->total_arrived_count++;
2494 	wingp->current_count++;
2495 	// make sure we haven't created too many ships
2496 	Assert(wingp->current_count <= MAX_SHIPS_PER_WING);
2497 
2498 	// at this point the wing has arrived, so handle the stuff for this particular ship
2499 
2500 	// set up wingman status index
2501 	hud_wingman_status_set_index(wingp, &Ships[shipnum], p_objp);
2502 
2503 	// copy to parse object
2504 	p_objp->wing_status_wing_index = Ships[shipnum].wing_status_wing_index;
2505 	p_objp->wing_status_wing_pos = Ships[shipnum].wing_status_wing_pos;
2506 
2507 	// set flag if necessary
2508 	for (j = 0; j < MAX_STARTING_WINGS; j++)
2509 	{
2510 		if (!stricmp(Starting_wing_names[j], wingp->name))
2511 			Ships[shipnum].flags[Ship::Ship_Flags::From_player_wing];
2512 	}
2513 
2514 	// handle AI
2515 	ai_info *aip = &Ai_info[Ships[shipnum].ai_index];
2516 	aip->wing = wingnum;
2517 
2518 	if (wingp->flags[Ship::Wing_Flags::No_dynamic])
2519 		aip->ai_flags.set(AI::AI_Flags::No_dynamic);
2520 
2521 	// copy any goals from the wing to the newly created ship
2522 	for (index = 0; index < MAX_AI_GOALS; index++)
2523 	{
2524 		if (wingp->ai_goals[index].ai_mode != AI_GOAL_NONE)
2525 			ai_copy_mission_wing_goal(&wingp->ai_goals[index], aip);
2526 	}
2527 }
2528 
2529 // Goober5000
resolve_parse_flags(object * objp,flagset<Mission::Parse_Object_Flags> & parse_flags)2530 void resolve_parse_flags(object *objp, flagset<Mission::Parse_Object_Flags> &parse_flags)
2531 {
2532     Assert(objp != NULL);
2533     ship *shipp = &Ships[objp->instance];
2534 
2535     if (parse_flags[Mission::Parse_Object_Flags::SF_Cargo_known])
2536         shipp->flags.set(Ship::Ship_Flags::Cargo_revealed);
2537 
2538     if (parse_flags[Mission::Parse_Object_Flags::SF_Ignore_count])
2539         shipp->flags.set(Ship::Ship_Flags::Ignore_count);
2540 
2541     if (parse_flags[Mission::Parse_Object_Flags::OF_Protected])
2542         objp->flags.set(Object::Object_Flags::Protected);
2543 
2544     if (parse_flags[Mission::Parse_Object_Flags::SF_Reinforcement])
2545     {
2546         //Individual ships in wings can't be reinforcements - FUBAR
2547         if (shipp->wingnum >= 0)
2548         {
2549             Warning(LOCATION, "Ship %s is a reinforcement unit but is a member of a wing. Ignoring reinforcement flag.", shipp->ship_name);
2550         }
2551         else
2552         {
2553             shipp->flags.set(Ship::Ship_Flags::Reinforcement);
2554         }
2555     }
2556 
2557     if ((parse_flags[Mission::Parse_Object_Flags::OF_No_shields]) && (parse_flags[Mission::Parse_Object_Flags::OF_Force_shields_on]))
2558     {
2559         Warning(LOCATION, "The parser found a ship with both the \"force-shields-on\" and \"no-shields\" flags; this is inconsistent!");
2560     }
2561     if (parse_flags[Mission::Parse_Object_Flags::OF_No_shields])
2562         objp->flags.set(Object::Object_Flags::No_shields);
2563 
2564     if (parse_flags[Mission::Parse_Object_Flags::SF_Escort])
2565         shipp->flags.set(Ship::Ship_Flags::Escort);
2566 
2567     // P_OF_PLAYER_START is handled in parse_create_object_sub
2568 
2569     if (parse_flags[Mission::Parse_Object_Flags::SF_No_arrival_music])
2570         shipp->flags.set(Ship::Ship_Flags::No_arrival_music);
2571 
2572     if (parse_flags[Mission::Parse_Object_Flags::SF_No_arrival_warp])
2573         shipp->flags.set(Ship::Ship_Flags::No_arrival_warp);
2574 
2575     if (parse_flags[Mission::Parse_Object_Flags::SF_No_departure_warp])
2576         shipp->flags.set(Ship::Ship_Flags::No_departure_warp);
2577 
2578     if (parse_flags[Mission::Parse_Object_Flags::SF_Locked]) {
2579         shipp->flags.set(Ship::Ship_Flags::Ship_locked);
2580         shipp->flags.set(Ship::Ship_Flags::Weapons_locked);
2581     }
2582 
2583     if (parse_flags[Mission::Parse_Object_Flags::OF_Invulnerable])
2584         objp->flags.set(Object::Object_Flags::Invulnerable);
2585 
2586     if (parse_flags[Mission::Parse_Object_Flags::SF_Hidden_from_sensors])
2587         shipp->flags.set(Ship::Ship_Flags::Hidden_from_sensors);
2588 
2589     if (parse_flags[Mission::Parse_Object_Flags::SF_Scannable])
2590         shipp->flags.set(Ship::Ship_Flags::Scannable);
2591 
2592     // P_AIF_KAMIKAZE, P_AIF_NO_DYNAMIC, and P_SF_RED_ALERT_CARRY are handled in parse_create_object_sub
2593 
2594     if (parse_flags[Mission::Parse_Object_Flags::OF_Beam_protected])
2595         objp->flags.set(Object::Object_Flags::Beam_protected);
2596 
2597     if (parse_flags[Mission::Parse_Object_Flags::OF_Flak_protected])
2598         objp->flags.set(Object::Object_Flags::Flak_protected);
2599 
2600     if (parse_flags[Mission::Parse_Object_Flags::OF_Laser_protected])
2601         objp->flags.set(Object::Object_Flags::Laser_protected);
2602 
2603     if (parse_flags[Mission::Parse_Object_Flags::OF_Missile_protected])
2604         objp->flags.set(Object::Object_Flags::Missile_protected);
2605 
2606     if (parse_flags[Mission::Parse_Object_Flags::SF_Guardian])
2607         shipp->ship_guardian_threshold = SHIP_GUARDIAN_THRESHOLD_DEFAULT;
2608 
2609     if (parse_flags[Mission::Parse_Object_Flags::SF_Vaporize])
2610         shipp->flags.set(Ship::Ship_Flags::Vaporize);
2611 
2612     if (parse_flags[Mission::Parse_Object_Flags::SF_Stealth])
2613         shipp->flags.set(Ship::Ship_Flags::Stealth);
2614 
2615     if (parse_flags[Mission::Parse_Object_Flags::SF_Friendly_stealth_invis])
2616         shipp->flags.set(Ship::Ship_Flags::Friendly_stealth_invis);
2617 
2618     if (parse_flags[Mission::Parse_Object_Flags::SF_Dont_collide_invis])
2619         shipp->flags.set(Ship::Ship_Flags::Dont_collide_invis);
2620 
2621     if (parse_flags[Mission::Parse_Object_Flags::SF_Primitive_sensors])
2622         shipp->flags.set(Ship::Ship_Flags::Primitive_sensors);
2623 
2624     if (parse_flags[Mission::Parse_Object_Flags::SF_No_subspace_drive])
2625         shipp->flags.set(Ship::Ship_Flags::No_subspace_drive);
2626 
2627     if (parse_flags[Mission::Parse_Object_Flags::SF_Nav_carry_status])
2628         shipp->flags.set(Ship::Ship_Flags::Navpoint_carry);
2629 
2630     if (parse_flags[Mission::Parse_Object_Flags::SF_Affected_by_gravity])
2631         shipp->flags.set(Ship::Ship_Flags::Affected_by_gravity);
2632 
2633     if (parse_flags[Mission::Parse_Object_Flags::SF_Toggle_subsystem_scanning])
2634         shipp->flags.set(Ship::Ship_Flags::Toggle_subsystem_scanning);
2635 
2636     if (parse_flags[Mission::Parse_Object_Flags::OF_Targetable_as_bomb])
2637         objp->flags.set(Object::Object_Flags::Targetable_as_bomb);
2638 
2639     if (parse_flags[Mission::Parse_Object_Flags::SF_No_builtin_messages])
2640         shipp->flags.set(Ship::Ship_Flags::No_builtin_messages);
2641 
2642     if (parse_flags[Mission::Parse_Object_Flags::SF_Primaries_locked])
2643         shipp->flags.set(Ship::Ship_Flags::Primaries_locked);
2644 
2645     if (parse_flags[Mission::Parse_Object_Flags::SF_Secondaries_locked])
2646         shipp->flags.set(Ship::Ship_Flags::Secondaries_locked);
2647 
2648     if (parse_flags[Mission::Parse_Object_Flags::SF_Set_class_dynamically])
2649         shipp->flags.set(Ship::Ship_Flags::Set_class_dynamically);
2650 
2651     if (parse_flags[Mission::Parse_Object_Flags::SF_No_death_scream])
2652         shipp->flags.set(Ship::Ship_Flags::No_death_scream);
2653 
2654     if (parse_flags[Mission::Parse_Object_Flags::SF_Always_death_scream])
2655         shipp->flags.set(Ship::Ship_Flags::Always_death_scream);
2656 
2657     if (parse_flags[Mission::Parse_Object_Flags::SF_Nav_needslink])
2658         shipp->flags.set(Ship::Ship_Flags::Navpoint_needslink);
2659 
2660     if (parse_flags[Mission::Parse_Object_Flags::SF_Hide_ship_name])
2661         shipp->flags.set(Ship::Ship_Flags::Hide_ship_name);
2662 
2663     if (parse_flags[Mission::Parse_Object_Flags::SF_Lock_all_turrets_initially])
2664         shipp->flags.set(Ship::Ship_Flags::Lock_all_turrets_initially);
2665 
2666     if (parse_flags[Mission::Parse_Object_Flags::SF_Afterburner_locked])
2667         shipp->flags.set(Ship::Ship_Flags::Afterburner_locked);
2668 
2669     if (parse_flags[Mission::Parse_Object_Flags::OF_Force_shields_on])
2670         shipp->flags.set(Ship::Ship_Flags::Force_shields_on);
2671 
2672     if (parse_flags[Mission::Parse_Object_Flags::OF_Immobile])
2673         objp->flags.set(Object::Object_Flags::Immobile);
2674 
2675     if (parse_flags[Mission::Parse_Object_Flags::SF_No_ets])
2676         shipp->flags.set(Ship::Ship_Flags::No_ets);
2677 
2678     if (parse_flags[Mission::Parse_Object_Flags::SF_Cloaked])
2679         shipp->flags.set(Ship::Ship_Flags::Cloaked);
2680 
2681     if (parse_flags[Mission::Parse_Object_Flags::SF_Ship_locked])
2682         shipp->flags.set(Ship::Ship_Flags::Ship_locked);
2683 
2684     if (parse_flags[Mission::Parse_Object_Flags::SF_Weapons_locked])
2685         shipp->flags.set(Ship::Ship_Flags::Weapons_locked);
2686 
2687     if (parse_flags[Mission::Parse_Object_Flags::SF_Scramble_messages])
2688         shipp->flags.set(Ship::Ship_Flags::Scramble_messages);
2689 
2690     if (parse_flags[Mission::Parse_Object_Flags::OF_No_collide])
2691         objp->flags.remove(Object::Object_Flags::Collides);
2692 
2693     if (parse_flags[Mission::Parse_Object_Flags::SF_No_disabled_self_destruct])
2694         shipp->flags.set(Ship::Ship_Flags::No_disabled_self_destruct);
2695 }
2696 
fix_old_special_explosions(p_object * p_objp,int variable_index)2697 void fix_old_special_explosions(p_object *p_objp, int variable_index)
2698 {
2699 	int i;
2700 
2701 	Assertion(!(p_objp->use_special_explosion), "Mission appears to be using both the new and old method of special explosions for %s. Old method values used", p_objp->name);
2702 
2703 	// check all the variables are valid
2704 	for ( i = variable_index; i < (variable_index + BLOCK_EXP_SIZE); i++ ) {
2705 		if (!( Block_variables[i].type & SEXP_VARIABLE_BLOCK )) {
2706 			Warning (LOCATION, "%s is using the old special explosions method but does not appear to have variables for all the values", p_objp->name);
2707 			return;
2708 		}
2709 	}
2710 
2711 	p_objp->use_special_explosion = true;
2712 
2713 	p_objp->special_exp_damage = atoi(Block_variables[variable_index+DAMAGE].text);
2714 	p_objp->special_exp_blast = atoi(Block_variables[variable_index+BLAST].text);
2715 	p_objp->special_exp_inner = atoi(Block_variables[variable_index+INNER_RAD].text);
2716 	p_objp->special_exp_outer = atoi(Block_variables[variable_index+OUTER_RAD].text);
2717 	p_objp->use_shockwave = (atoi(Block_variables[variable_index+PROPAGATE].text) ? 1:0);
2718 	p_objp->special_exp_shockwave_speed = atoi(Block_variables[variable_index+SHOCK_SPEED].text);
2719 	p_objp->special_exp_deathroll_time = 0;
2720 }
2721 
fix_old_special_hits(p_object * p_objp,int variable_index)2722 void fix_old_special_hits(p_object *p_objp, int variable_index)
2723 {
2724 	int i;
2725 
2726 	Assertion( ((p_objp->special_hitpoints == 0) && (p_objp->special_shield == -1)),"Mission appears to be using both the new and old method of special hitpoints for %s", p_objp->name);
2727 
2728 	// check all the variables are valid
2729 	for ( i = variable_index; i < (variable_index + BLOCK_HIT_SIZE); i++ ) {
2730 		if (!( Block_variables[i].type & SEXP_VARIABLE_BLOCK )) {
2731 			Warning (LOCATION, "%s is using the old special hitpoints method but does not appear to have variables for all the values", p_objp->name);
2732 			return;
2733 		}
2734 	}
2735 
2736 	p_objp->special_hitpoints = atoi(Block_variables[variable_index+HULL_STRENGTH].text);
2737 	p_objp->special_shield = atoi(Block_variables[variable_index+SHIELD_STRENGTH].text);
2738 }
2739 
p_object()2740 p_object::p_object()
2741 	: next(NULL), prev(NULL), dock_list(NULL), created_object(NULL)
2742 {}
2743 
2744 // this will be called when Parse_objects is cleared between missions and upon shutdown
~p_object()2745 p_object::~p_object()
2746 {
2747 	dock_free_dock_list(this);
2748 }
get_display_name()2749 const char* p_object::get_display_name() {
2750 	if (has_display_name()) {
2751 		return display_name.c_str();
2752 	} else {
2753 		return name;
2754 	}
2755 }
has_display_name()2756 bool p_object::has_display_name() {
2757 	return flags[Mission::Parse_Object_Flags::SF_Has_display_name];
2758 }
2759 
2760 extern int parse_warp_params(const WarpParams *inherit_from, WarpDirection direction, const char *info_type_name, const char *sip_name);
2761 
2762 /**
2763  * Mp points at the text of an object, which begins with the "$Name:" field.
2764  * Snags all object information.  Creating the ship now only happens after everything has been parsed.
2765  *
2766  * @param pm Mission
2767  * @param flag is parameter that is used to tell what kind information we are retrieving from the mission.
2768  * if we are just getting player starts, then don't create the objects
2769  * @param p_objp Object
2770  */
parse_object(mission * pm,int,p_object * p_objp)2771 int parse_object(mission *pm, int  /*flag*/, p_object *p_objp)
2772 {
2773 	int	i, j, count, delay;
2774     char name[NAME_LENGTH];
2775 	ship_info *sip;
2776 
2777 	Assert(pm != NULL);
2778 
2779 	// Goober5000
2780 	p_objp->created_object = NULL;
2781 	p_objp->next = NULL;
2782 	p_objp->prev = NULL;
2783 	p_objp->flags.reset();
2784 
2785 	required_string("$Name:");
2786 	stuff_string(p_objp->name, F_NAME, NAME_LENGTH);
2787 	if (mission_parse_get_parse_object(p_objp->name))
2788 		error_display(0, NOX("Redundant ship name: %s\n"), p_objp->name);
2789 
2790 	// if this name has a hash, create a default display name
2791 	if (get_pointer_to_first_hash_symbol(p_objp->name)) {
2792 		p_objp->display_name = p_objp->name;
2793 		end_string_at_first_hash_symbol(p_objp->display_name);
2794 		p_objp->flags.set(Mission::Parse_Object_Flags::SF_Has_display_name);
2795 	}
2796 
2797 	if (optional_string("$Display Name:")) {
2798 		stuff_string(p_objp->display_name, F_NAME);
2799 		p_objp->flags.set(Mission::Parse_Object_Flags::SF_Has_display_name);
2800 	}
2801 
2802 	find_and_stuff("$Class:", &p_objp->ship_class, F_NAME, Ship_class_names, Ship_info.size(), "ship class");
2803 	if (p_objp->ship_class < 0)
2804 	{
2805 		if (Fred_running) {
2806 			Warning(LOCATION, "Ship \"%s\" has an invalid ship type (ships.tbl probably changed).  Making it type 0\n", p_objp->name);
2807 		}
2808 		else {
2809 			mprintf(("MISSIONS: Ship \"%s\" has an invalid ship type (ships.tbl probably changed).  Making it type 0\n", p_objp->name));
2810 		}
2811 
2812 		p_objp->ship_class = 0;
2813 		Num_unknown_ship_classes++;
2814 	}
2815 	sip = &Ship_info[p_objp->ship_class];
2816 
2817 	// Karajorma - See if there are any alternate classes specified for this ship.
2818 	p_objp->alt_classes.clear();
2819 	// The alt class can either be a variable or a ship class name
2820 	char alt_ship_class[TOKEN_LENGTH > NAME_LENGTH ? TOKEN_LENGTH : NAME_LENGTH];
2821 	int is_variable;
2822 
2823 	while (optional_string("$Alt Ship Class:")) {
2824 		alt_class new_alt_class;
2825 
2826 		is_variable = get_string_or_variable(alt_ship_class);
2827 
2828 		if (is_variable) {
2829 			new_alt_class.variable_index = get_index_sexp_variable_name(alt_ship_class);
2830 			if(new_alt_class.variable_index >= 0) {
2831 				new_alt_class.ship_class = ship_info_lookup(Sexp_variables[new_alt_class.variable_index].text);
2832 			}
2833 			else {
2834 				new_alt_class.ship_class = -1;
2835 			}
2836 		}
2837 		else {
2838 			new_alt_class.variable_index = -1;
2839 			new_alt_class.ship_class = ship_info_lookup(alt_ship_class);
2840 		}
2841 
2842 		if (new_alt_class.ship_class < 0 ) {
2843 			if (!Fred_running) {
2844 				Warning(LOCATION, "Ship \"%s\" has an invalid Alternate Ship Class type (ships.tbl probably changed). Skipping this entry", p_objp->name);
2845 				continue;
2846 			}
2847 			else {
2848 				// incorrect initial values for a variable can be fixed in FRED
2849 				if (new_alt_class.variable_index != -1) {
2850 					Warning(LOCATION, "Ship \"%s\" has an invalid Alternate Ship Class type.", p_objp->name);
2851 				}
2852 				// but there is little we can do if someone spelled a ship class incorrectly
2853 				else {
2854 					Warning(LOCATION, "Ship \"%s\" has an invalid Alternate Ship Class type. Skipping this entry", p_objp->name);
2855 					continue;
2856 				}
2857 			}
2858 		}
2859 
2860 		if (optional_string("+Default Class:")) {
2861 			new_alt_class.default_to_this_class = true;
2862 		}
2863 		else {
2864 			new_alt_class.default_to_this_class = false;
2865 		}
2866 
2867 		p_objp->alt_classes.push_back(new_alt_class);
2868 	}
2869 
2870 	// if this is a multiplayer dogfight mission, skip support ships
2871 	if(MULTI_DOGFIGHT && (sip->flags[Ship::Info_Flags::Support]))
2872 		return 0;
2873 
2874 	// optional alternate name type
2875 	p_objp->alt_type_index = -1;
2876 	if(optional_string("$Alt:"))
2877 	{
2878 		// alternate name
2879 		stuff_string(name, F_NAME, NAME_LENGTH);
2880 
2881 		// try and find the alternate name
2882 		p_objp->alt_type_index = mission_parse_lookup_alt(name);
2883 		if(p_objp->alt_type_index < 0)
2884 			WarningEx(LOCATION, "Mission %s\nError looking up alternate ship type name %s!\n", pm->name, name);
2885 		else
2886 			mprintf(("Using alternate ship type name: %s\n", name));
2887 	}
2888 
2889 	// optional callsign
2890 	p_objp->callsign_index = -1;
2891 	if(optional_string("$Callsign:"))
2892 	{
2893 		// alternate callsign
2894 		stuff_string(name, F_NAME, NAME_LENGTH);
2895 
2896 		// try and find the callsign
2897 		p_objp->callsign_index = mission_parse_lookup_callsign(name);
2898 		if(p_objp->callsign_index < 0)
2899 			WarningEx(LOCATION, "Mission %s\nError looking up callsign %s!\n", pm->name, name);
2900 		else
2901 			mprintf(("Using callsign: %s\n", name));
2902 	}
2903 
2904 	// static alias stuff - stupid, but it seems to be necessary
2905 	static const char *temp_team_names[MAX_IFFS];
2906 	for (i = 0; i < Num_iffs; i++)
2907 		temp_team_names[i] = Iff_info[i].iff_name;
2908 
2909 	find_and_stuff("$Team:", &p_objp->team, F_NAME, temp_team_names, Num_iffs, "team name");
2910 
2911 	// save current team for loadout purposes, so that in multi we always respawn
2912 	// from the original loadout slot even if the team changes
2913 	p_objp->loadout_team = p_objp->team;
2914 
2915 	if (optional_string("$Team Color Setting:")) {
2916 		char temp[NAME_LENGTH];
2917 		stuff_string(temp, F_NAME, NAME_LENGTH);
2918 		p_objp->team_color_setting = temp;
2919 
2920 		if (Team_Colors.find(p_objp->team_color_setting) == Team_Colors.end()) {
2921 			mprintf(("Invalid team color specified in mission file for ship %s, resetting to default\n", p_objp->name));
2922 			p_objp->team_color_setting = sip->default_team_name;
2923 		}
2924 	}
2925 
2926 	required_string("$Location:");
2927 	stuff_vec3d(&p_objp->pos);
2928 
2929 	required_string("$Orientation:");
2930 	stuff_matrix(&p_objp->orient);
2931 
2932 	// legacy code, not even used in FS1
2933 	if (optional_string("$IFF:"))
2934 	{
2935 		stuff_string(name, F_NAME, NAME_LENGTH);
2936 	}
2937 
2938 	find_and_stuff("$AI Behavior:",	&p_objp->behavior, F_NAME, Ai_behavior_names, Num_ai_behaviors, "AI behavior");
2939 	p_objp->ai_goals = -1;
2940 
2941 	if (optional_string("+AI Class:"))
2942 	{
2943 		p_objp->ai_class = match_and_stuff(F_NAME, Ai_class_names, Num_ai_classes, "AI class");
2944 
2945 		if (p_objp->ai_class < 0)
2946 		{
2947 			Warning(LOCATION, "AI Class for ship %s does not exist in ai.tbl. Setting to first available class.\n", p_objp->name);
2948 			p_objp->ai_class = 0;
2949 		}
2950 	}
2951 	else
2952 	{
2953 		p_objp->ai_class = sip->ai_class;
2954 	}
2955 
2956 	if (optional_string("$AI Goals:"))
2957 		p_objp->ai_goals = get_sexp_main();
2958 
2959 	if (!required_string_either("$AI Goals:", "$Cargo 1:"))
2960 	{
2961 		required_string("$AI Goals:");
2962 		p_objp->ai_goals = get_sexp_main();
2963 	}
2964 
2965 	p_objp->cargo1 = -1;
2966 	int temp;
2967 	find_and_stuff_or_add("$Cargo 1:", &temp, F_NAME, Cargo_names, &Num_cargo, MAX_CARGO, "cargo");
2968 	p_objp->cargo1 = char(temp);
2969 	if (optional_string("$Cargo 2:"))
2970 	{
2971 		stuff_string(name, F_NAME, NAME_LENGTH);
2972 	}
2973 
2974 	parse_common_object_data(p_objp);  // get initial conditions and subsys status
2975 	count = 0;
2976 	while (required_string_either("$Arrival Location:", "$Status Description:"))	{
2977 		Assert(count < MAX_OBJECT_STATUS);
2978 
2979 		find_and_stuff("$Status Description:", &p_objp->status_type[count], F_NAME, Status_desc_names, Num_status_names, "Status Description");
2980 		find_and_stuff("$Status:", &p_objp->status[count], F_NAME, Status_type_names, Num_status_names, "Status Type");
2981 		find_and_stuff("$Target:", &p_objp->target[count], F_NAME, Status_target_names, Num_status_names, "Target");
2982 		count++;
2983 	}
2984 	p_objp->status_count = count;
2985 
2986 	p_objp->arrival_anchor = -1;
2987 	p_objp->arrival_distance = 0;
2988 	p_objp->arrival_path_mask = -1;	// -1 only until resolved
2989 
2990 	find_and_stuff("$Arrival Location:", &p_objp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
2991 
2992 	if (optional_string("+Arrival Distance:"))
2993 	{
2994 		stuff_int(&p_objp->arrival_distance);
2995 
2996 		// Goober5000
2997 		if ((p_objp->arrival_distance <= 0) && ((p_objp->arrival_location == ARRIVE_NEAR_SHIP) || (p_objp->arrival_location == ARRIVE_IN_FRONT_OF_SHIP)))
2998 		{
2999 			Warning(LOCATION, "Arrival distance for ship %s cannot be %d.  Setting to 1.\n", p_objp->name, p_objp->arrival_distance);
3000 			p_objp->arrival_distance = 1;
3001 		}
3002 	}
3003 
3004 	if (p_objp->arrival_location != ARRIVE_AT_LOCATION)
3005 	{
3006 		required_string("$Arrival Anchor:");
3007 		stuff_string(name, F_NAME, NAME_LENGTH);
3008 		p_objp->arrival_anchor = get_anchor(name);
3009 	}
3010 
3011 	if (optional_string("+Arrival Paths:"))
3012 	{
3013 		// temporarily use mask to point to the restriction index
3014 		p_objp->arrival_path_mask = add_path_restriction();
3015 	}
3016 
3017 	delay = 0;
3018 	if (optional_string("+Arrival Delay:"))
3019 	{
3020 		stuff_int(&delay);
3021 		if (delay < 0)
3022 			Error(LOCATION, "Cannot have arrival delay < 0 (ship %s)", p_objp->name);
3023 	}
3024 
3025 	if (!Fred_running)
3026 		p_objp->arrival_delay = -delay;			// use negative numbers to mean we haven't set up a timer yet
3027 	else
3028 		p_objp->arrival_delay = delay;
3029 
3030 	required_string("$Arrival Cue:");
3031 	p_objp->arrival_cue = get_sexp_main();
3032 
3033 	p_objp->departure_anchor = -1;
3034 	p_objp->departure_path_mask = -1;	// -1 only until resolved
3035 
3036 	find_and_stuff("$Departure Location:", &p_objp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
3037 
3038 	if (p_objp->departure_location != DEPART_AT_LOCATION)
3039 	{
3040 		required_string("$Departure Anchor:");
3041 		stuff_string(name, F_NAME, NAME_LENGTH);
3042 		p_objp->departure_anchor = get_anchor(name);
3043 	}
3044 
3045 	if (optional_string("+Departure Paths:"))
3046 	{
3047 		// temporarily use mask to point to the restriction index
3048 		p_objp->departure_path_mask = add_path_restriction();
3049 	}
3050 
3051 	delay = 0;
3052 	if (optional_string("+Departure Delay:"))
3053 	{
3054 		stuff_int(&delay);
3055 		if (delay < 0)
3056 			Error(LOCATION, "Cannot have departure delay < 0 (ship %s)", p_objp->name);
3057 	}
3058 
3059 	if (!Fred_running)
3060 		p_objp->departure_delay = -delay;
3061 	else
3062 		p_objp->departure_delay = delay;
3063 
3064 	required_string("$Departure Cue:");
3065 	p_objp->departure_cue = get_sexp_main();
3066 
3067 	// look for warp parameters
3068 	p_objp->warpin_params_index = parse_warp_params(&Warp_params[sip->warpin_params_index], WarpDirection::WARP_IN, "Ship", p_objp->name);
3069 	p_objp->warpout_params_index = parse_warp_params(&Warp_params[sip->warpout_params_index], WarpDirection::WARP_OUT, "Ship", p_objp->name);
3070 
3071 	if (optional_string("$Misc Properties:"))
3072 		stuff_string(p_objp->misc, F_NAME, NAME_LENGTH);
3073 
3074 	required_string("$Determination:");
3075 	int dummy;
3076 	stuff_int(&dummy);
3077 
3078     // set flags
3079     if (optional_string("+Flags:"))
3080     {
3081         SCP_vector<SCP_string> unparsed;
3082         parse_string_flag_list(p_objp->flags, Parse_object_flags, num_parse_object_flags, &unparsed);
3083         if (!unparsed.empty()) {
3084             for (size_t k = 0; k < unparsed.size(); ++k) {
3085                 WarningEx(LOCATION, "Unknown flag in parse object flags: %s", unparsed[k].c_str());
3086             }
3087         }
3088     }
3089 
3090     // second set - Goober5000
3091     if (optional_string("+Flags2:"))
3092     {
3093         SCP_vector<SCP_string> unparsed;
3094         parse_string_flag_list(p_objp->flags, Parse_object_flags, num_parse_object_flags, &unparsed);
3095         if (!unparsed.empty()) {
3096             for (size_t k = 0; k < unparsed.size(); ++k) {
3097 				// catch typos or deprecations
3098 				if (!stricmp(unparsed[k].c_str(), "no-collide") || !stricmp(unparsed[k].c_str(), "no_collide")) {
3099 					p_objp->flags.set(Mission::Parse_Object_Flags::OF_No_collide);
3100 				}
3101 				else {
3102 					WarningEx(LOCATION, "Unknown flag in parse object flags: %s", unparsed[k].c_str());
3103 				}
3104             }
3105         }
3106     }
3107 
3108 
3109 	// always store respawn priority, just for ease of implementation
3110 	p_objp->respawn_priority = 0;
3111 	if(optional_string("+Respawn Priority:"))
3112 		stuff_int(&p_objp->respawn_priority);
3113 
3114 	p_objp->escort_priority = 0;
3115     if (optional_string("+Escort Priority:"))
3116     {
3117         Assert(p_objp->flags[Mission::Parse_Object_Flags::SF_Escort]);
3118         stuff_int(&p_objp->escort_priority);
3119     }
3120 
3121     if (p_objp->flags[Mission::Parse_Object_Flags::OF_Player_start])
3122     {
3123         p_objp->flags.set(Mission::Parse_Object_Flags::SF_Cargo_known);	// make cargo known for players
3124         Player_starts++;
3125     }
3126 
3127 	p_objp->use_special_explosion = false;
3128 	p_objp->special_exp_damage = -1;
3129 	p_objp->special_exp_blast = -1;
3130 	p_objp->special_exp_inner = -1;
3131 	p_objp->special_exp_outer = -1;
3132 	p_objp->use_shockwave = false;
3133 	p_objp->special_exp_shockwave_speed = 0;
3134 	p_objp->special_exp_deathroll_time = 0;
3135 
3136 	p_objp->special_hitpoints = 0;
3137 	p_objp->special_shield = -1;
3138 
3139 	if (optional_string("$Special Explosion:")) {
3140 		p_objp->use_special_explosion = true;
3141 		bool period_detected = false;
3142 
3143 		if (required_string("+Special Exp Damage:")) {
3144 			stuff_int(&p_objp->special_exp_damage);
3145 
3146 			if (*Mp == '.') {
3147 				period_detected = true;
3148 				advance_to_eoln(NULL);
3149 			}
3150 		}
3151 
3152 		if (required_string("+Special Exp Blast:")) {
3153 			stuff_int(&p_objp->special_exp_blast);
3154 
3155 			if (*Mp == '.') {
3156 				period_detected = true;
3157 				advance_to_eoln(NULL);
3158 			}
3159 		}
3160 
3161 		if (required_string("+Special Exp Inner Radius:")) {
3162 			stuff_int(&p_objp->special_exp_inner);
3163 
3164 			if (*Mp == '.') {
3165 				period_detected = true;
3166 				advance_to_eoln(NULL);
3167 			}
3168 		}
3169 
3170 		if (required_string("+Special Exp Outer Radius:")) {
3171 			stuff_int(&p_objp->special_exp_outer);
3172 
3173 			if (*Mp == '.') {
3174 				period_detected = true;
3175 				advance_to_eoln(NULL);
3176 			}
3177 		}
3178 
3179 		if (optional_string("+Special Exp Shockwave Speed:")) {
3180 			stuff_int(&p_objp->special_exp_shockwave_speed);
3181 			p_objp->use_shockwave = true;
3182 
3183 			if (*Mp == '.') {
3184 				period_detected = true;
3185 				advance_to_eoln(NULL);
3186 			}
3187 		}
3188 
3189 		if (optional_string("+Special Exp Death Roll Time:")) {
3190 			stuff_int(&p_objp->special_exp_deathroll_time);
3191 		}
3192 
3193 		if (period_detected) {
3194 			nprintf(("Warning", "Special explosion attributes have been returned to integer format\n"));
3195 		}
3196 	}
3197 
3198 	if (optional_string("+Special Hitpoints:")) {
3199 		stuff_int(&p_objp->special_hitpoints);
3200 	}
3201 
3202 	if (optional_string("+Special Shield Points:")) {
3203 		stuff_int(&p_objp->special_shield);
3204 	}
3205 
3206 	if (optional_string("+Special Exp index:")) {
3207 		int variable_index;
3208 		stuff_int(&variable_index);
3209 		fix_old_special_explosions(p_objp, variable_index);
3210 	}
3211 
3212 	if (optional_string("+Special Hitpoint index:")) {
3213 		int variable_index;
3214 		stuff_int(&variable_index);
3215 		fix_old_special_hits(p_objp, variable_index);
3216 	}
3217 
3218 	// set custom shield value
3219 	if (p_objp->special_shield != -1) {
3220 		p_objp->ship_max_shield_strength = (float) p_objp->special_shield;
3221 	}
3222 	else {
3223 		p_objp->ship_max_shield_strength = sip->max_shield_strength;
3224 	}
3225 
3226 	// set custom hitpoint value
3227 	if (p_objp->special_hitpoints > 0) {
3228 		p_objp->ship_max_hull_strength = (float) p_objp->special_hitpoints;
3229 	}
3230 	else {
3231 		p_objp->ship_max_hull_strength = sip->max_hull_strength;
3232 	}
3233 
3234 	Assert(p_objp->ship_max_hull_strength > 0.0f);	// Goober5000: div-0 check (not shield because we might not have one)
3235 	p_objp->max_shield_recharge = sip->max_shield_recharge;
3236 
3237 
3238 	// if the kamikaze flag is set, we should have the next flag
3239 	if (optional_string("+Kamikaze Damage:"))
3240 	{
3241 		int damage;
3242 
3243 		stuff_int(&damage);
3244 		p_objp->kamikaze_damage = damage;
3245 	}
3246 
3247 	p_objp->hotkey = -1;
3248 	if (optional_string("+Hotkey:"))
3249 	{
3250 		stuff_int(&p_objp->hotkey);
3251 		Assert((p_objp->hotkey >= 0) && (p_objp->hotkey < 10));
3252 	}
3253 
3254 	// Goober5000
3255 	p_objp->dock_list = NULL;
3256 	while (optional_string("+Docked With:"))
3257 	{
3258 		char docked_with[NAME_LENGTH];
3259 		char docker_point[NAME_LENGTH];
3260 		char dockee_point[NAME_LENGTH];
3261 
3262 		// grab docking information
3263 		// (whoever designed the original docking system
3264 		// reversed the dockpoints in the mission file)
3265 		stuff_string(docked_with, F_NAME, NAME_LENGTH);
3266 		required_string("$Docker Point:");
3267 		stuff_string(dockee_point, F_NAME, NAME_LENGTH);
3268 		required_string("$Dockee Point:");
3269 		stuff_string(docker_point, F_NAME, NAME_LENGTH);
3270 
3271 		// make sure we don't overflow the limit
3272 		if (Total_initially_docked >= MAX_SHIPS)
3273 		{
3274 			mprintf(("Too many initially docked instances; skipping...\n"));
3275 			continue;
3276 		}
3277 
3278 		// put this information into the Initially_docked array
3279 		strcpy_s(Initially_docked[Total_initially_docked].docker, p_objp->name);
3280 		strcpy_s(Initially_docked[Total_initially_docked].dockee, docked_with);
3281 		strcpy_s(Initially_docked[Total_initially_docked].docker_point, docker_point);
3282 		strcpy_s(Initially_docked[Total_initially_docked].dockee_point, dockee_point);
3283 		Total_initially_docked++;
3284 	}
3285 
3286 	// check the optional parameter for destroying the ship before the mission starts.  If this parameter is
3287 	// here, then we need to destroy the ship N seconds before the mission starts (for debris purposes).
3288 	// store the time value here.  We want to create this object for sure.  Set the arrival cue and arrival
3289 	// delay to bogus values
3290 	p_objp->destroy_before_mission_time = -1;
3291 	if (optional_string("+Destroy At:"))
3292 	{
3293 		stuff_int(&p_objp->destroy_before_mission_time);
3294 		Assert (p_objp->destroy_before_mission_time >= 0);
3295 		p_objp->arrival_cue = Locked_sexp_true;
3296 		p_objp->arrival_delay = timestamp(0);
3297 	}
3298 
3299 	// check for the optional "orders accepted" string which contains the orders from the default
3300     // set that this ship will actually listen to
3301     if (optional_string("+Orders Accepted:"))
3302     {
3303         stuff_int(&p_objp->orders_accepted);
3304         if (p_objp->orders_accepted != -1)
3305             p_objp->flags.set(Mission::Parse_Object_Flags::SF_Use_unique_orders);
3306     }
3307 
3308 	p_objp->group = 0;
3309 	if (optional_string("+Group:"))
3310 		stuff_int(&p_objp->group);
3311 
3312 	bool table_score = false;
3313 	if (optional_string("+Use Table Score:")) {
3314 		table_score = true;
3315 	}
3316 
3317 	if (optional_string("+Score:")) {
3318 		if (!table_score) {
3319 			stuff_int(&p_objp->score);
3320 		}
3321 		// throw away the value the mission file has and use the table value.
3322 		else {
3323 			int temp_score;
3324 			stuff_int(&temp_score);
3325 		}
3326 	}
3327 	else {
3328 		table_score = true;
3329 	}
3330 
3331 	if (table_score) {
3332 		p_objp->score = sip->score;
3333 	}
3334 
3335 	if (optional_string("+Assist Score Percentage:")) {
3336 		stuff_float(&p_objp->assist_score_pct);
3337 		// value must be a percentage
3338 		if (p_objp->assist_score_pct < 0) {
3339 			p_objp->assist_score_pct = 0;
3340 		}
3341 		else if (p_objp->assist_score_pct > 1) {
3342 			p_objp->assist_score_pct = 1;
3343 		}
3344 	}
3345 	else {
3346 		p_objp->assist_score_pct = 0;
3347 	}
3348 
3349 	// parse the persona index if present
3350 	p_objp->persona_index = -1;
3351 	if (optional_string("+Persona Index:"))
3352 		stuff_int(&p_objp->persona_index);
3353 
3354 	// texture replacement - Goober5000
3355 	p_objp->replacement_textures = sip->replacement_textures;	// initialize our set with the ship class set, which may be empty
3356 	if (optional_string("$Texture Replace:") || optional_string("$Duplicate Model Texture Replace:"))
3357 	{
3358 		texture_replace tr;
3359 		char *p;
3360 
3361 		tr.from_table = false;
3362 
3363 		while (optional_string("+old:"))
3364 		{
3365 			strcpy_s(tr.ship_name, p_objp->name);
3366 			tr.new_texture_id = -1;
3367 
3368 			stuff_string(tr.old_texture, F_NAME, MAX_FILENAME_LEN);
3369 			required_string("+new:");
3370 			stuff_string(tr.new_texture, F_NAME, MAX_FILENAME_LEN);
3371 
3372 			// get rid of extensions
3373 			p = strchr(tr.old_texture, '.');
3374 			if (p)
3375 			{
3376 				mprintf(("Extraneous extension found on replacement texture %s!\n", tr.old_texture));
3377 				*p = 0;
3378 			}
3379 			p = strchr(tr.new_texture, '.');
3380 			if (p)
3381 			{
3382 				mprintf(("Extraneous extension found on replacement texture %s!\n", tr.new_texture));
3383 				*p = 0;
3384 			}
3385 
3386 			// add it if we aren't over the limit
3387 			if (p_objp->replacement_textures.size() < MAX_MODEL_TEXTURES)
3388 				p_objp->replacement_textures.push_back(tr);
3389 			else
3390 				mprintf(("Too many replacement textures specified for ship '%s'!\n", p_objp->name));
3391 		}
3392 	}
3393 
3394 	// now load the textures (do this outside the parse loop because we may have ship class replacements too)
3395 	for (SCP_vector<texture_replace>::iterator tr = p_objp->replacement_textures.begin(); tr != p_objp->replacement_textures.end(); ++tr)
3396 	{
3397 		// load the texture
3398 		if (!stricmp(tr->new_texture, "invisible"))
3399 		{
3400 			// invisible is a special case
3401 			tr->new_texture_id = REPLACE_WITH_INVISIBLE;
3402 		}
3403 		else
3404 		{
3405 			// try to load texture or anim as normal
3406 			tr->new_texture_id = bm_load_either(tr->new_texture);
3407 		}
3408 
3409 		// not found?
3410 		if (tr->new_texture_id < 0)
3411 		{
3412 			mprintf(("Could not load replacement texture %s for ship %s\n", tr->new_texture, p_objp->name));
3413 		}
3414 
3415 		// account for FRED
3416 		if (Fred_running)
3417 		{
3418 			Fred_texture_replacements.push_back(*tr);
3419 			Fred_texture_replacements.back().new_texture_id = -1;
3420 		}
3421 	}
3422 
3423 	p_objp->wingnum = -1;					// set the wing number to -1 -- possibly to be set later
3424 	p_objp->pos_in_wing = -1;				// Goober5000
3425 
3426 	for (i=0;i<MAX_IFFS;i++)
3427 	{
3428 		for (j=0;j<MAX_IFFS;j++)
3429 		{
3430 			p_objp->alt_iff_color[i][j] = -1;
3431 		}
3432 	}
3433 
3434 	// for multiplayer, assign a network signature to this parse object.  Doing this here will
3435 	// allow servers to use the signature with clients when creating new ships, instead of having
3436 	// to pass ship names all the time
3437 	if (Game_mode & GM_MULTIPLAYER)
3438 		p_objp->net_signature = multi_assign_network_signature(MULTI_SIG_SHIP);
3439 
3440 	// set the wing_status position to be -1 for all objects.  This will get set to an appropriate
3441 	// value when the wing positions are finally determined.
3442 	p_objp->wing_status_wing_index = -1;
3443 	p_objp->wing_status_wing_pos = -1;
3444 	p_objp->respawn_count = 0;
3445 
3446 	// Goober5000 - preload stuff for certain object flags
3447 	// (done after parsing object, but before creating it)
3448 	if (p_objp->flags[Mission::Parse_Object_Flags::Knossos_warp_in])
3449 		Knossos_warp_ani_used = 1;
3450 
3451 	// this is a valid/legal ship to create
3452 	return 1;
3453 }
3454 
mission_parse_handle_late_arrivals(p_object * p_objp)3455 void mission_parse_handle_late_arrivals(p_object *p_objp)
3456 {
3457 	ship_info *sip = NULL;
3458 	model_subsystem *subsystems = NULL;
3459 
3460 	// only for objects which show up after the start of a mission
3461 	if (p_objp->created_object != NULL)
3462 		return;
3463 
3464 	Assert( p_objp->ship_class >= 0 );
3465 
3466 	sip = &Ship_info[p_objp->ship_class];
3467 
3468 	if (sip->n_subsystems > 0) {
3469 		subsystems = &sip->subsystems[0];
3470 	}
3471 
3472 	// we need the model to process the texture set, so go ahead and load it now
3473 	sip->model_num = model_load(sip->pof_file, sip->n_subsystems, subsystems);
3474 }
3475 
3476 // Goober5000 - I split this because 1) it's clearer; and 2) initially multiple docked ships would have been
3477 // insanely difficult otherwise
3478 //
3479 // Maybe create the object.
3480 // Don't create the new ship blindly.  First, check the sexp for the arrival cue
3481 // to determine when this ship should arrive.  If not right away, stick this ship
3482 // onto the ship arrival list to be looked at later.  Also check to see if it should use the
3483 // wings arrival cue.  The ship may get created later depending on whether or not the wing
3484 // is created.
3485 // Always create ships when FRED is running.
mission_parse_maybe_create_parse_object(p_object * pobjp)3486 void mission_parse_maybe_create_parse_object(p_object *pobjp)
3487 {
3488 	// Bail if it was already created.  This should only happen when we previously
3489 	// created all the objects in a docked group simultaneously.
3490 	if (pobjp->created_object != NULL)
3491 	{
3492 		Assert(object_is_docked(pobjp));
3493 		return;
3494 	}
3495 
3496 	// Goober5000
3497 	// shunt this guy to the arrival list if he meets one of the following conditions:
3498 	// 1) he's docked but not the dock leader
3499 	// 2) this is FS2 (i.e. not FRED2) AND he meets one of the following conditions:
3500 	//    a) he's not cued to arrive yet
3501 	//    b) his arrival delay hasn't elapsed
3502 	//    c) he's reinforcement
3503     if ((object_is_docked(pobjp) && !(pobjp->flags[Mission::Parse_Object_Flags::SF_Dock_leader])) ||
3504         (!Fred_running && (!eval_sexp(pobjp->arrival_cue) ||
3505             !timestamp_elapsed(pobjp->arrival_delay) ||
3506             (pobjp->flags[Mission::Parse_Object_Flags::SF_Reinforcement]))))
3507 	{
3508 		// we can't add ships getting destroyed to the arrival list!!!
3509 		Assert (pobjp->destroy_before_mission_time < 0);
3510 
3511 		// add to arrival list
3512 		list_append(&Ship_arrival_list, pobjp);
3513 
3514 		// we need to deal with replacement textures now, so that texture page-in will work properly
3515 		mission_parse_handle_late_arrivals(pobjp);
3516 	}
3517 	// ingame joiners bail here.
3518 	else if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN))
3519 	{
3520 		return;
3521 	}
3522 	// the ship is present at the beginning of the mission, so we create it
3523 	else
3524 	{
3525 		int	real_objnum = parse_create_object(pobjp);	// this object may later get destroyed depending on wing status!!!!
3526 
3527 		// if the ship is supposed to be destroyed before the mission, then blow up the ship and mark the pieces
3528 		// as last forever.  Only call this stuff when you are blowing up the ship
3529 		if (pobjp->destroy_before_mission_time >= 0)
3530 		{
3531 			object *objp = &Objects[real_objnum];
3532 
3533 			// FreeSpace
3534 			if (!Fred_running)
3535 			{
3536 				shipfx_blow_up_model(objp, 0, 0, &objp->pos);
3537 				objp->flags.set(Object::Object_Flags::Should_be_dead);
3538 
3539 				// Make sure that the ship is marked as destroyed so the AI doesn't freak out later
3540 				ship_add_exited_ship(&Ships[objp->instance], Ship::Exit_Flags::Destroyed);
3541 
3542 				// once the ship is exploded, find the debris pieces belonging to this object, mark them
3543 				// as not to expire, and move them forward in time N seconds
3544 				for (auto &db: Debris)
3545 				{
3546 					if (!(db.flags[Debris_Flags::Used]))		// not used, move onto the next one.
3547 						continue;
3548 					if (db.source_objnum != real_objnum)		// not from this ship, move to next one
3549 						continue;
3550 
3551 					debris_remove_from_hull_list(&db);
3552 					db.flags.set(Debris_Flags::DoNotExpire);   // mark as don't expire
3553 					db.lifeleft = -1.0f;						// be sure that lifeleft == -1.0 so that it really doesn't expire!
3554 
3555 					// now move the debris along its path for N seconds
3556 					objp = &Objects[db.objnum];
3557 					physics_sim(&objp->pos, &objp->orient, &objp->phys_info, (float) pobjp->destroy_before_mission_time);
3558 				}
3559 			}
3560 			// FRED
3561 			else
3562 			{
3563 				// be sure to set the variable in the ships structure for the final death time!!!
3564 				Ships[objp->instance].final_death_time = pobjp->destroy_before_mission_time;
3565 				Ships[objp->instance].flags.set(Ship::Ship_Flags::Kill_before_mission);
3566 			}
3567 		}
3568 	}
3569 }
3570 
parse_common_object_data(p_object * p_objp)3571 void parse_common_object_data(p_object *p_objp)
3572 {
3573 	int i;
3574 
3575 	// Genghis: used later for subsystem checking
3576 	auto sip = &Ship_info[p_objp->ship_class];
3577 
3578 	// set some defaults..
3579 	p_objp->initial_velocity = 0;
3580 	p_objp->initial_hull = 100;
3581 	p_objp->initial_shields = 100;
3582 
3583 	// now change defaults if present
3584 	if (optional_string("+Initial Velocity:")) {
3585 		stuff_int(&p_objp->initial_velocity);
3586 	}
3587 
3588 	if (optional_string("+Initial Hull:"))
3589 		stuff_int(&p_objp->initial_hull);
3590 	if (optional_string("+Initial Shields:"))
3591 		stuff_int(&p_objp->initial_shields);
3592 
3593 	p_objp->subsys_index = Subsys_index;
3594 	p_objp->subsys_count = 0;
3595 	while (optional_string("+Subsystem:")) {
3596 		i = allocate_subsys_status();
3597 
3598 		p_objp->subsys_count++;
3599 		stuff_string(Subsys_status[i].name, F_NAME, NAME_LENGTH);
3600 
3601 		// Genghis: check that the subsystem name makes sense for this ship type
3602 		if (subsystem_stricmp(Subsys_status[i].name, NOX("pilot")))
3603 		{
3604 			int j;
3605 			for (j=0; j < sip->n_subsystems; ++j)
3606 				if (!subsystem_stricmp(sip->subsystems[j].subobj_name, Subsys_status[i].name))
3607 					break;
3608 			//if (j == sip->n_subsystems)
3609 				//Warning(LOCATION, "Ship \"%s\", class \"%s\"\nUnknown subsystem \"%s\" found in mission!", objp->name, sip->name, Subsys_status[i].name);
3610 		}
3611 
3612 		if (optional_string("$Damage:"))
3613 			stuff_float(&Subsys_status[i].percent);
3614 
3615 		Subsys_status[i].subsys_cargo_name = 0;
3616 		if (optional_string("+Cargo Name:")) {
3617 			char cargo_name[NAME_LENGTH];
3618 			stuff_string(cargo_name, F_NAME, NAME_LENGTH);
3619 			int index = string_lookup(cargo_name, Cargo_names, Num_cargo, "cargo", 0);
3620 			if (index == -1) {
3621 				if (Num_cargo < MAX_CARGO) {
3622 					index = Num_cargo;
3623 					strcpy(Cargo_names[Num_cargo++], cargo_name);
3624 				}
3625 				else {
3626 					WarningEx(LOCATION, "Maximum number of cargo names (%d) exceeded, defaulting to Nothing!", MAX_CARGO);
3627 					index = 0;
3628 				}
3629 			}
3630 			Subsys_status[i].subsys_cargo_name = index;
3631 		}
3632 
3633 		if (optional_string("+AI Class:"))
3634 		{
3635 			Subsys_status[i].ai_class = match_and_stuff(F_NAME, Ai_class_names, Num_ai_classes, "AI class");
3636 
3637 			if (Subsys_status[i].ai_class < 0)
3638 			{
3639 				Warning(LOCATION, "AI Class for ship %s and subsystem %s does not exist in ai.tbl. Setting to first available class.\n", p_objp->name, Subsys_status[i].name);
3640 				Subsys_status[i].ai_class = 0;
3641 			}
3642 		}
3643 
3644 		if (optional_string("+Primary Banks:"))
3645 			stuff_int_list(Subsys_status[i].primary_banks, MAX_SHIP_PRIMARY_BANKS, WEAPON_LIST_TYPE);
3646 
3647 		// Goober5000
3648 		if (optional_string("+Pbank Ammo:"))
3649 			stuff_int_list(Subsys_status[i].primary_ammo, MAX_SHIP_PRIMARY_BANKS, RAW_INTEGER_TYPE);
3650 
3651 		if (optional_string("+Secondary Banks:"))
3652 			stuff_int_list(Subsys_status[i].secondary_banks, MAX_SHIP_SECONDARY_BANKS, WEAPON_LIST_TYPE);
3653 
3654 		if (optional_string("+Sbank Ammo:"))
3655 			stuff_int_list(Subsys_status[i].secondary_ammo, MAX_SHIP_SECONDARY_BANKS, RAW_INTEGER_TYPE);
3656 
3657 	}
3658 }
3659 
3660 
3661 /**
3662  * Checks if any ships of a certain ship class are still available in the team loadout
3663  * @return The index of the ship in team_data->ship_list if found or -1 if it isn't
3664  */
get_reassigned_index(team_data * current_team,int ship_class)3665 int get_reassigned_index(team_data *current_team, int ship_class)
3666 {
3667 	// Search through the available ships to see if there is a matching ship class in the loadout
3668 	for (int i=0; i < current_team->num_ship_choices; i++)
3669 	{
3670 		if (ship_class == current_team->ship_list[i])
3671 		{
3672 			if (current_team->ship_count[i] > 0) {
3673 				return i;
3674 			}
3675 			else {
3676 				return -1;
3677 			}
3678 		}
3679 	}
3680 
3681 	return -1;
3682 }
3683 
3684 /**
3685  * Updates the loadout quanities for a ship class.
3686  */
update_loadout_totals(team_data * current_team,int loadout_index)3687 void update_loadout_totals(team_data *current_team, int loadout_index)
3688 {
3689 	// Fix the loadout variables to show that the class has less available if there are still ships available
3690 	if (current_team->ship_count[loadout_index] > 0)
3691 	{
3692 		Assert (current_team->loadout_total > 0);
3693 
3694 		current_team->ship_count[loadout_index]--;
3695 		current_team->loadout_total--;
3696 	}
3697 }
3698 
3699 /**
3700  * Attempts to set the class of this ship based which ship classes still remain unassigned in the ship loadout
3701  * The ship class specified by the mission file itself is tested first. Followed by the list of alt classes.
3702  * If an alt class flagged as default_to_this_class is reached the ship will be assigned to that class.
3703  * If the class can't be assigned because no ships of that class remain the function returns false.
3704  */
is_ship_assignable(p_object * p_objp)3705 bool is_ship_assignable(p_object *p_objp)
3706 {
3707 	int loadout_index = -1;
3708 
3709 	team_data *data_for_team = &Team_data[p_objp->team];
3710 
3711 	// First lets check if the ship specified in the mission file is of an assignable class
3712 	loadout_index = get_reassigned_index(data_for_team, p_objp->ship_class);
3713 	if (loadout_index != -1 )
3714 	{
3715 		Assert (data_for_team->loadout_total > 0);
3716 
3717 		update_loadout_totals(data_for_team, loadout_index);
3718 
3719 		// Since the ship in the mission file matched one available in the loadout we need go no further
3720 		return true;
3721 	}
3722 
3723 	// Now we check the alt_classes (if there are any)
3724 	for (SCP_vector<alt_class>::iterator pac = p_objp->alt_classes.begin(); pac != p_objp->alt_classes.end(); ++pac) {
3725 		// we don't check availability unless we are asked to
3726 		if (pac->default_to_this_class == false) {
3727 			loadout_index = pac->ship_class;
3728 			break;
3729 		}
3730 		else {
3731 			loadout_index = get_reassigned_index(data_for_team, pac->ship_class);
3732 			if (loadout_index != -1 ) {
3733 				update_loadout_totals(data_for_team, loadout_index);
3734 				break;
3735 			}
3736 		}
3737 	}
3738 
3739 	// If we managed to assign a class we'd may need to actually swap to it
3740 	if (loadout_index != -1 ) {
3741 		if (p_objp->ship_class != data_for_team->ship_list[loadout_index])
3742 		{
3743 			swap_parse_object(p_objp, data_for_team->ship_list[loadout_index]);
3744 		}
3745 		return true;
3746 	}
3747 
3748 	return false;
3749 }
3750 
3751 /**
3752  * Checks the list of Parse_objects to see if any of them should be reassigned based on the
3753  * number of ships of that class that were present in the loadout.
3754  */
process_loadout_objects()3755 void process_loadout_objects()
3756 {
3757 	SCP_vector<size_t> reassignments;
3758 
3759 	// Loop through all the Parse_objects looking for ships that should be affected by the loadout code.
3760 	for (size_t i=0; i < Parse_objects.size(); i++)
3761 	{
3762 		p_object *p_objp = &Parse_objects[i];
3763         if (p_objp->flags[Mission::Parse_Object_Flags::SF_Set_class_dynamically])
3764 		{
3765 			if (!(is_ship_assignable(p_objp)))
3766 			{
3767 				// store the ship so we can come back to it later.
3768 				reassignments.push_back(i);
3769 			}
3770 		}
3771 	}
3772 
3773 	// Now we go though the ships we were unable to assign earlier and reassign them on a first come first
3774 	// served basis.
3775 	for (size_t m=0; m < reassignments.size(); m++)
3776 	{
3777 		p_object *p_objp = &Parse_objects[reassignments[m]];
3778 		team_data *current_team = &Team_data[p_objp->team];
3779 		bool loadout_assigned = false;
3780         Assert(p_objp->flags[Mission::Parse_Object_Flags::SF_Set_class_dynamically]);
3781 
3782 		// First thing to check is whether we actually have any ships left to assign
3783 		if (current_team->loadout_total == 0)
3784 		{
3785 			// If there is nothing left to assign we should use the ship in the mission file
3786 			loadout_assigned = true;
3787 		}
3788 		// We do have ships left in the team loadout that we can assign
3789 		else
3790 		{
3791 			// Go through the loadout until we find an unassigned ship
3792 			for (int j=0; j < current_team->num_ship_choices; j++)
3793 			{
3794 				if (current_team->ship_count[j] > 0)
3795 				{
3796 					update_loadout_totals(current_team, j);
3797 					// We will need to assign a new class too (if a p_object the same class was available
3798 					// it should have been assigned by attempt_loadout_assignation_from_defaults()
3799 					Assert (p_objp->ship_class != current_team->ship_list[j]);
3800 					swap_parse_object(p_objp, current_team->ship_list[j]);
3801 
3802 					loadout_assigned = true;
3803 					break ;
3804 				}
3805 			}
3806 		}
3807 
3808 		// We should never reach here with an unassigned loadout
3809 		Assert (loadout_assigned);
3810 	}
3811 }
3812 
3813 extern int Multi_ping_timestamp;
parse_objects(mission * pm,int flag)3814 void parse_objects(mission *pm, int flag)
3815 {
3816 	Assert(pm != NULL);
3817 
3818 	required_string("#Objects");
3819 
3820 	// parse in objects
3821 	Parse_objects.clear();
3822 	while (required_string_either("#Wings", "$Name:"))
3823 	{
3824 		p_object pobj;
3825 
3826 		// parse a single object
3827 		int valid = parse_object(pm, flag, &pobj);
3828 
3829 		// not all objects are always valid or legal
3830 		if (!valid)
3831 			continue;
3832 
3833 		// add it
3834 		Parse_objects.push_back(pobj);
3835 
3836 		// send out a ping if we are multi so that psnet2 doesn't kill us off for a long load
3837 		// NOTE that we can't use the timestamp*() functions here since they won't increment
3838 		//      during this loading process
3839 		if (Game_mode & GM_MULTIPLAYER)
3840 		{
3841 			if ((Multi_ping_timestamp == -1) || (Multi_ping_timestamp <= timer_get_milliseconds()))
3842 			{
3843 				multi_ping_send_all();
3844 				Multi_ping_timestamp = timer_get_milliseconds() + 10000; // timeout is 10 seconds between pings
3845 			}
3846 		}
3847 	}
3848 
3849 	// Goober5000 - I moved the docking stuff to post_process_ships_wings because of interdependencies
3850 	// between ships and wings.  Neither docking stuff nor ship stuff (for ships present at mission start)
3851 	// will be valid until after post_process_ships_wings is run.
3852 
3853 	// Karajorma - Now that we've parsed all the objects we can set the class of those which were flagged
3854 	// to be set based on the number of ships available in the loadout.
3855 	if (!Fred_running)
3856 	{
3857 		process_loadout_objects();
3858 	}
3859 }
3860 
3861 /**
3862  * Replaces a p_object with a new one based on a Ship_info index.
3863  */
swap_parse_object(p_object * p_obj,int new_ship_class)3864 void swap_parse_object(p_object *p_obj, int new_ship_class)
3865 {
3866 	ship_info *new_ship_info = &Ship_info[new_ship_class];
3867 	ship_info *old_ship_info = &Ship_info[p_obj->ship_class];
3868 	int subsys_ind = p_obj->subsys_index;
3869 	subsys_status *ship_subsystems = &Subsys_status[subsys_ind];
3870 
3871 	// Class
3872 	// First things first. Change the class of the p_object
3873 	p_obj->ship_class = new_ship_class;
3874 
3875 	// Hitpoints
3876 	// We need to take into account that the ship might have been assigned special hitpoints so we can't
3877 	// simply swap old for new.
3878 	Assert (p_obj->ship_max_hull_strength > 0);
3879 	Assert (old_ship_info->max_hull_strength > 0);
3880 
3881 	float hp_multiplier = p_obj->ship_max_hull_strength / old_ship_info->max_hull_strength;
3882 	p_obj->ship_max_hull_strength = new_ship_info->max_hull_strength * hp_multiplier;
3883 
3884 	// Shields
3885 	// Again we have to watch out for special hitpoints but this time we can't assume that there will be a
3886 	// shield. So first lets see if there is one.
3887 	if ((p_obj->ship_max_shield_strength != old_ship_info->max_shield_strength) &&
3888 		(p_obj->ship_max_shield_strength > 0) &&
3889 		(new_ship_info->max_shield_strength > 0))
3890 	{
3891 		// This ship is using special hitpoints to alter the shield strength
3892 		float shield_multiplier = p_obj->ship_max_shield_strength / i2fl(old_ship_info->max_shield_strength);
3893 		p_obj->ship_max_shield_strength = new_ship_info->max_shield_strength * shield_multiplier;
3894 	}
3895 	// Not using special hitpoints or a class which has a shield strength of zero
3896 	else
3897 	{
3898 		p_obj->ship_max_shield_strength = new_ship_info->max_shield_strength;
3899 	}
3900 
3901 	// Primary weapons
3902 	// First find out what is the correct number for a ship of this class
3903 	int num_pbanks = new_ship_info->num_primary_banks;
3904 	// Now cycle through the primary banks looking for banks that were added or removed
3905 	for (int i=0; i < MAX_SHIP_PRIMARY_BANKS; i++)
3906 	{
3907 		// If we're dealing with a primary bank that actually should exist on this ship
3908 		if ( i < num_pbanks )
3909 		{
3910 			// We only care if a weapon hasn't been parsed in for this bank
3911 			if (ship_subsystems->primary_banks[i] == -1)
3912 			{
3913 				// Give the ship the default weapon for this bank.
3914 				ship_subsystems->primary_banks[i] = new_ship_info->primary_bank_weapons[i];
3915 			}
3916 		}
3917 		// Any primary banks the ship doesn't have should be set to -1
3918 		else
3919 		{
3920 			ship_subsystems->primary_banks[i] = -1;
3921 		}
3922 	}
3923 
3924 	// Secondary weapons
3925 	// Again we first have to find out how many we should have
3926 	int num_sbanks = new_ship_info->num_secondary_banks;
3927 	// Now cycle through the secondary banks looking for banks that were added or removed
3928 	for (int j=0; j < MAX_SHIP_SECONDARY_BANKS; j++)
3929 	{
3930 		// If we're dealing with a primary bank that actually should exist on this ship
3931 		if ( j < num_sbanks )
3932 		{
3933 			// We only care if a weapon hasn't been parsed in for this bank
3934 			if (ship_subsystems->secondary_banks[j] == -1){
3935 				// Give the ship the default weapon for this bank.
3936 				ship_subsystems->secondary_banks[j] = new_ship_info->secondary_bank_weapons[j];
3937 			}
3938 		}
3939 		// Any secondary banks the ship doesn't have should be set to -1
3940 		else
3941 		{
3942 			ship_subsystems->secondary_banks[j] = -1;
3943 		}
3944 	}
3945 }
3946 
mission_parse_get_parse_object(ushort net_signature)3947 p_object *mission_parse_get_parse_object(ushort net_signature)
3948 {
3949 	SCP_vector<p_object>::iterator ii;
3950 
3951 	// look for original ships
3952 	for (ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
3953 		if (ii->net_signature == net_signature)
3954 			return &(*ii);
3955 
3956 	// boo
3957 	return NULL;
3958 }
3959 
3960 // Goober5000 - also get it by name
mission_parse_get_parse_object(const char * name)3961 p_object *mission_parse_get_parse_object(const char *name)
3962 {
3963 	SCP_vector<p_object>::iterator ii;
3964 
3965 	// look for original ships
3966 	for (ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
3967 		if (!stricmp(ii->name, name))
3968 			return &(*ii);
3969 
3970 	// boo
3971 	return NULL;
3972 }
3973 
find_wing_name(char * name)3974 int find_wing_name(char *name)
3975 {
3976 	int	i;
3977 
3978 	for (i = 0; i < Num_wings; i++)
3979 	{
3980 		if (!stricmp(name, Wings[i].name))
3981 			return i;
3982 	}
3983 
3984 	return -1;
3985 }
3986 
3987 /**
3988 * @brief						Tries to create a wing of ships
3989 * @param[inout]	wingp			Pointer to the wing structure of the wing to be created
3990 * @param[in] num_to_create		Number of ships to create
3991 * @param[in] force				If set to 1, the wing will be created regardless of whether or not the arrival conditions
3992 *								have been met yet.
3993 * @param[in] specific_instance	Set this to create a specific ship from this wing
3994 * @returns						Number of ships created
3995 */
parse_wing_create_ships(wing * wingp,int num_to_create,int force,int specific_instance)3996 int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int specific_instance )
3997 {
3998 	int wingnum, objnum, num_create_save;
3999 	int time_to_arrive;
4000 	int pre_create_count;
4001 	int i, j;
4002 
4003 	// we need to send this in multiplayer
4004 	pre_create_count = wingp->total_arrived_count;
4005 
4006 	// force is used to force creation of the wing -- used for multiplayer
4007 	if ( !force ) {
4008 		// we only want to evaluate the arrival cue of the wing if:
4009 		// 1) single player
4010 		// 2) multiplayer and I am the host of the game
4011 		// can't create any ships if the arrival cue is false or the timestamp has not elapsed.
4012 
4013 		if ( !eval_sexp(wingp->arrival_cue) ) /* || !timestamp_elapsed(wingp->arrival_delay) ) */
4014 			return 0;
4015 
4016 		// once the sexpressions becomes true, then check the arrival delay on the wing.  The first time, the
4017 		// arrival delay will be <= 0 meaning that no timer object has been set yet.  Set up the timestamp
4018 		// which should always give a number >= 0;
4019 		if ( wingp->arrival_delay <= 0 ) {
4020 			wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
4021 			Assert ( wingp->arrival_delay >= 0 );
4022 		}
4023 
4024 		if ( !timestamp_elapsed( wingp->arrival_delay ) )
4025 			return 0;
4026 
4027 		// if wing is coming from docking bay, then be sure that ship we are arriving from actually exists
4028 		// (or will exist).
4029 		if ( wingp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
4030 			int shipnum;
4031 			char *name;
4032 
4033 			Assert( wingp->arrival_anchor >= 0 );
4034 			name = Parse_names[wingp->arrival_anchor];
4035 
4036 			// see if ship is in mission.
4037 			shipnum = ship_name_lookup( name );
4038 			if ( shipnum == -1 ) {
4039 				int num_remaining;
4040 
4041 				// see if ship is yet to arrive.  If so, then return 0 so we can evaluate again later.
4042 				if (mission_check_ship_yet_to_arrive(name))
4043 					return 0;
4044 
4045 				// since this wing cannot arrive from this place, we need to mark the wing as destroyed and
4046 				// set the wing variables appropriately.  Good for directives.
4047 
4048 				// set the gone flag
4049                 wingp->flags.set(Ship::Wing_Flags::Gone);
4050 
4051 				// mark the number of waves and number of ships destroyed equal to the last wave and the number
4052 				// of ships yet to arrive
4053 				num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count);
4054 				wingp->total_arrived_count += num_remaining;
4055 				wingp->current_wave = wingp->num_waves;
4056 
4057 				if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, NULL) ) {
4058 					wingp->total_destroyed += num_remaining;
4059 				} else if ( mission_log_get_time(LOG_SHIP_DEPARTED, name, NULL, NULL) ) {
4060 					wingp->total_departed += num_remaining;
4061 				} else {
4062 					wingp->total_vanished += num_remaining;
4063 				}
4064 
4065 				mission_parse_mark_non_arrival(wingp);	// Goober5000
4066 				return 0;
4067 			}
4068 
4069 			// Goober5000 - check status of fighterbays - if they're destroyed, we can't launch - but we want to reeval later
4070 			if (ship_fighterbays_all_destroyed(&Ships[shipnum]))
4071 				return 0;
4072 		}
4073 
4074 		if ( num_to_create == 0 )
4075 			return 0;
4076 
4077 		// check the wave_delay_timestamp field.  If it is not valid, make it valid (based on wave delay min
4078 		// and max values).  If it is valid, and not elapsed, then return.  If it is valid and elasped, then
4079 		// continue on.
4080 		if ( !timestamp_valid(wingp->wave_delay_timestamp) ) {
4081 
4082 			// if at least one of these is valid, then reset the timestamp.  If they are both zero, we will create the
4083 			// wave
4084 			if ( (wingp->wave_delay_min > 0) || (wingp->wave_delay_max > 0) ) {
4085 				Assert ( wingp->wave_delay_min <= wingp->wave_delay_max );
4086 				time_to_arrive = wingp->wave_delay_min + (int)(frand() * (wingp->wave_delay_max - wingp->wave_delay_min));
4087 
4088 				// MWA -- 5/18/98
4089 				// HACK HACK -- in the presense of Mike Comet and Mitri, I have introduced one of the most
4090 				// serious breaches of coding standards.  I'm to lazy to fix this the correct way.  Insert
4091 				// a delay before the next wave of the wing can arrive to that clients in the game have ample
4092 				// time to kill off any ships in the wing before the next wave arrives.
4093 				if ( Game_mode & GM_MULTIPLAYER ){
4094 					time_to_arrive += 7;
4095 				}
4096 				wingp->wave_delay_timestamp = timestamp(time_to_arrive * 1000);
4097 				return 0;
4098 			}
4099 
4100 			// if we get here, both min and max values are 0;  See comments above for a most serious hack
4101 			time_to_arrive = 0;
4102 			if ( Game_mode & GM_MULTIPLAYER )
4103 				time_to_arrive += 7;
4104 			time_to_arrive *= 1000;
4105 			wingp->wave_delay_timestamp = timestamp(time_to_arrive);
4106 		}
4107 
4108 		// now check to see if the wave_delay_timestamp is elapsed or not
4109 		if ( !timestamp_elapsed(wingp->wave_delay_timestamp) )
4110 			return 0;
4111 	}
4112 
4113 	// finally we can create the wing.
4114 
4115 	num_create_save = num_to_create;
4116 
4117 	wingnum = WING_INDEX(wingp);					// get the wing number
4118 
4119 	// if there are no ships to create, then all ships must be player start ships -- do nothing in this case.
4120 	if ( num_to_create == 0 ){
4121 		return 0;
4122 	}
4123 
4124 	wingp->current_wave++;						// we are creating new ships
4125 	// we need to create num_to_create ships.  Since the arrival cues for ships in a wing
4126 	// are ignored, then *all* ships must be in the Ship_arrival_list.
4127 
4128 	objnum = -1;
4129 
4130 	// Goober5000 - we have to do this via the array because we have no guarantee we'll be able to iterate along the list
4131 	// (since created objects plus anything they're docked to will be removed from it)
4132 	for (SCP_vector<p_object>::iterator ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
4133 	{
4134 		int index;
4135 		ai_info *aip;
4136 		p_object *p_objp = &(*ii);
4137 
4138 		// ensure on arrival list
4139 		if (!parse_object_on_arrival_list(p_objp))
4140 			continue;
4141 
4142 		// compare the wingnums.  When they are equal, we can create the ship.  In the case of
4143 		// wings that have multiple waves, this code implies that we essentially creating clones
4144 		// of the ships that were created in Fred for the wing when more ships for a new wave
4145 		// arrive.  The threshold value of a wing can also make one of the ships in a wing be "cloned"
4146 		// more often than other ships in the wing.  I don't think this matters much.
4147 		if (p_objp->wingnum != wingnum)
4148 			continue;
4149 
4150 		Assert( (p_objp->pos_in_wing >= 0) && (p_objp->pos_in_wing < MAX_SHIPS_PER_WING) );
4151 
4152 		// when ingame joining, we need to create a specific ship out of the list of ships for a
4153 		// wing.  specific_instance is a 0 based integer which specified which ship in the wing
4154 		// to create.  So, only create the ship we actually need to.
4155 		if ((Game_mode & GM_MULTIPLAYER) && (specific_instance > 0))
4156 		{
4157 			specific_instance--;
4158 			continue;
4159 		}
4160 		// when not creating a specific ship, we should skip over any ships that weren't carried along in the red-alert
4161         else if (p_objp->flags[Mission::Parse_Object_Flags::Red_alert_deleted])
4162 		{
4163 			num_to_create--;
4164 			num_create_save--;
4165 			ship_add_ship_type_count(p_objp->ship_class, -1);
4166 			wingp->red_alert_skipped_ships++;
4167 
4168 			// clear the flag so that this parse object can be used for the next wave
4169             p_objp->flags.remove(Mission::Parse_Object_Flags::Red_alert_deleted);
4170 
4171 			// skip over this parse object
4172 			if (num_to_create == 0)
4173 				break;
4174 			else
4175 				continue;
4176 		}
4177 
4178         Assert(!(p_objp->flags[Mission::Parse_Object_Flags::SF_Cannot_arrive]));		// get allender
4179 
4180 		// if we have the maximum number of ships in the wing, we must bail as well
4181 		if (wingp->current_count >= MAX_SHIPS_PER_WING)
4182 		{
4183 			Int3();					// this is bogus -- we should always allow all ships to be created
4184 			num_to_create = 0;
4185 			break;
4186 		}
4187 
4188 		// bash the ship name to be the name of the wing + some number if there is > 1 wave in this wing
4189 		wingp->total_arrived_count++;
4190 		if (wingp->num_waves > 1)
4191 		{
4192 			bool needs_display_name;
4193 			wing_bash_ship_name(p_objp->name, wingp->name, wingp->total_arrived_count + wingp->red_alert_skipped_ships, &needs_display_name);
4194 
4195 			// set up display name if we need to
4196 			// (In the unlikely edge case where the ship already has a display name for some reason, it will be overwritten.
4197 			// This is unavoidable, because if we didn't overwrite display names, all waves would have the display name from the first wave.)
4198 			if (needs_display_name)
4199 			{
4200 				p_objp->display_name = p_objp->name;
4201 				end_string_at_first_hash_symbol(p_objp->display_name);
4202 				p_objp->flags.set(Mission::Parse_Object_Flags::SF_Has_display_name);
4203 			}
4204 
4205 			// subsequent waves of ships will not be in the ship registry, so add them
4206 			if (!ship_registry_get(p_objp->name))
4207 			{
4208 				ship_registry_entry entry(p_objp->name);
4209 				entry.p_objp = p_objp;
4210 
4211 				Ship_registry.push_back(entry);
4212 				Ship_registry_map[p_objp->name] = static_cast<int>(Ship_registry.size() - 1);
4213 			}
4214 		}
4215 
4216 		// also, if multiplayer, set the parse object's net signature to be wing's net signature
4217 		// base + total_arrived_count (before adding 1)
4218 		// Cyborg -- The original ships in the wave have their net_signature set at mission parse
4219 		// so only do this if this is a subsequent wave.
4220 		if (Game_mode & GM_MULTIPLAYER && wingp->num_waves > 1)
4221 		{
4222 			// Cyborg -- Also, then we need to subtract the original wave's number of fighters
4223 			// and also subtract 1 to use the wing's starting signature
4224 			p_objp->net_signature = (ushort) (wingp->net_signature + wingp->total_arrived_count - (wingp->wave_count + 1));
4225 		}
4226 
4227 
4228 		objnum = parse_create_object(p_objp);
4229 		aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
4230 
4231 		// copy any goals from the wing to the newly created ship
4232 		for (index = 0; index < MAX_AI_GOALS; index++)
4233 		{
4234 			if (wingp->ai_goals[index].ai_mode != AI_GOAL_NONE)
4235 				ai_copy_mission_wing_goal(&wingp->ai_goals[index], aip);
4236 		}
4237 
4238 		Ai_info[Ships[Objects[objnum].instance].ai_index].wing = wingnum;
4239 
4240 		if (wingp->flags[Ship::Wing_Flags::No_dynamic])
4241 			aip->ai_flags.set(AI::AI_Flags::No_dynamic);
4242 
4243 		// update housekeeping variables
4244 		// NOTE:  for the initial wing setup we use actual position to get around
4245 		//        object order isses, but ships in all following waves just get
4246 		//        tacked onto the end of the list
4247 		if (wingp->current_wave == 1) {
4248 			wingp->ship_index[p_objp->pos_in_wing] = Objects[objnum].instance;
4249 		} else {
4250 			wingp->ship_index[wingp->current_count] = Objects[objnum].instance;
4251 		}
4252 
4253 		// set up wingman status index
4254 		hud_wingman_status_set_index(wingp, &Ships[Objects[objnum].instance], p_objp);
4255 
4256 		p_objp->wing_status_wing_index = Ships[Objects[objnum].instance].wing_status_wing_index;
4257 		p_objp->wing_status_wing_pos = Ships[Objects[objnum].instance].wing_status_wing_pos;
4258 
4259 		wingp->current_count++;
4260 
4261 		// keep any player ship on the parse object list -- used for respawns
4262 		// 5/8/98 -- MWA -- don't remove ships from the list when you are ingame joining
4263 		if (!(p_objp->flags[Mission::Parse_Object_Flags::OF_Player_start]))
4264 		{
4265 			if ((Game_mode & GM_NORMAL) || !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN))
4266 			{
4267 				// only remove ship if one wave in wing
4268 				if (wingp->num_waves == wingp->current_wave)
4269 				{
4270 					// remove p_objp from the list
4271 					list_remove(&Ship_arrival_list, p_objp);
4272 
4273 					// free up sexp nodes for reuse
4274 					if (p_objp->ai_goals != -1)
4275 						free_sexp2(p_objp->ai_goals);
4276 				}
4277 			}
4278 		}
4279 
4280 		// flag ship with SF_FROM_PLAYER_WING if a member of player starting wings
4281 		if (MULTI_TEAM)
4282 		{
4283 			// different for tvt -- Goober5000
4284 			for (j = 0; j < MAX_TVT_WINGS; j++)
4285 			{
4286 				if (!stricmp(TVT_wing_names[j], wingp->name))
4287 					Ships[Objects[objnum].instance].flags.set(Ship::Ship_Flags::From_player_wing);
4288 			}
4289 		}
4290 		else
4291 		{
4292 			for (j = 0; j < MAX_STARTING_WINGS; j++)
4293 			{
4294 				if (!stricmp(Starting_wing_names[j], wingp->name))
4295 					Ships[Objects[objnum].instance].flags.set(Ship::Ship_Flags::From_player_wing);
4296 			}
4297 		}
4298 
4299 		// keep track of how many ships to create.  Stop when we have done all that we are supposed to do.
4300 		num_to_create--;
4301 		if (num_to_create == 0)
4302 			break;
4303 	}
4304 
4305 	// we should always have enough ships in the list!!!
4306 	Assert (num_to_create == 0);
4307 
4308 	// wing current_count needs to match the end of the ship_index[] list, but there
4309 	// is a very off chance it could have holes in it (especially if it's a red-alert
4310 	// wing that arrives late), so make sure to compact the list
4311 	int length = MAX_SHIPS_PER_WING;
4312 	for (i = 0; i < length; i++)
4313 	{
4314 		if (wingp->ship_index[i] == -1)
4315 		{
4316 			// shift actual values downward
4317 			for (j = i; j < length - 1; j++)
4318 			{
4319 				wingp->ship_index[j] = wingp->ship_index[j+1];
4320 
4321 				// update "special" ship too
4322 				if (wingp->special_ship == j+1)
4323 					wingp->special_ship--;
4324 			}
4325 
4326 			// last value becomes -1
4327 			wingp->ship_index[j] = -1;
4328 			length--;
4329 
4330 			// stay on the current index in case we still have a -1
4331 			i--;
4332 		}
4333 	}
4334 
4335 	// possibly play some event driven music here.  Send a network packet indicating the wing was
4336 	// created.  Only do this stuff if actually in the mission.
4337 	if ( (objnum != -1) && (Game_mode & GM_IN_MISSION) ) {		// if true, we have created at least one new ship.
4338 		int it, ship_num;
4339 
4340 		// see if this wing is a player starting wing, and if so, call the maybe_add_form_goal
4341 		// function to possibly make the wing form on the player
4342 		for (it = 0; it < MAX_STARTING_WINGS; it++ ) {
4343 			if ( Starting_wings[it] == wingnum ){
4344 				break;
4345 			}
4346 		}
4347 		if ( it < MAX_STARTING_WINGS ){
4348 			ai_maybe_add_form_goal( wingp );
4349 		}
4350 
4351 		mission_log_add_entry( LOG_WING_ARRIVED, wingp->name, NULL, wingp->current_wave );
4352 		ship_num = wingp->ship_index[0];
4353 
4354 		if ( !(Ships[ship_num].flags[Ship::Ship_Flags::No_arrival_music]) && !(wingp->flags[Ship::Wing_Flags::No_arrival_music]) ) {
4355 			if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
4356 				Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
4357 				event_music_arrival(Ships[ship_num].team);
4358 			}
4359 		}
4360 
4361 		// possibly change the location where these ships arrive based on the wings arrival location
4362 		mission_set_wing_arrival_location( wingp, num_create_save );
4363 
4364 		// if in multiplayer (and I am the host) and in the mission, send a wing create command to all
4365 		// other players
4366 		if ( MULTIPLAYER_MASTER ){
4367 			send_wing_create_packet( wingp, num_create_save, pre_create_count );
4368 		}
4369 
4370 #ifndef NDEBUG
4371 		// test code to check to be sure that all ships in the wing are ignoring the same types
4372 		// of orders from the leader
4373 		if ( Fred_running ) {
4374 			Assert( wingp->ship_index[wingp->special_ship] != -1 );
4375 			int orders = Ships[wingp->ship_index[0]].orders_accepted;
4376 			for (it = 0; it < wingp->current_count; it++ ) {
4377 				if (it == wingp->special_ship)
4378 					continue;
4379 
4380 				if ( orders != Ships[wingp->ship_index[it]].orders_accepted ) {
4381 					Warning(LOCATION, "ships in wing %s are ignoring different player orders.  Please find Mark A\nto talk to him about this.", wingp->name );
4382 					break;
4383 				}
4384 			}
4385 		}
4386 #endif
4387 
4388 	}
4389 
4390 	wingp->wave_delay_timestamp = timestamp(-1);		// we will need to set this up properly for the next wave
4391 	return num_create_save;
4392 }
4393 
parse_wing(mission * pm)4394 void parse_wing(mission *pm)
4395 {
4396 	int wingnum, i, wing_goals, delay, saved_arrival_delay;
4397 	char name[NAME_LENGTH], ship_names[MAX_SHIPS_PER_WING][NAME_LENGTH];
4398     char wing_flag_strings[PARSEABLE_WING_FLAGS][NAME_LENGTH];
4399 	wing *wingp;
4400 
4401 	Assert(pm != NULL);
4402 	wingp = &Wings[Num_wings];
4403 
4404 	required_string("$Name:");
4405 	stuff_string(wingp->name, F_NAME, NAME_LENGTH);
4406 
4407 	// quickly look through the squadron wing names to see if we have to warn the modder about this mission
4408 	// to avoid them avoid the multi missing wing bug.
4409 	if (pm->game_type & (MISSION_TYPE_MULTI | MISSION_TYPE_MULTI_COOP)) {
4410 		for (int j = 0; j < MAX_SQUADRON_WINGS; j++) {
4411 			if (!strcmp(wingp->name, Squadron_wing_names[j])) {
4412 				Squadron_wing_names_found[j] = true;
4413 			}
4414 		}
4415 	}
4416 
4417 	wingnum = find_wing_name(wingp->name);
4418 	if (wingnum != -1)
4419 		error_display(0, NOX("Redundant wing name: %s\n"), wingp->name);
4420 	wingnum = Num_wings;
4421 
4422 	wingp->total_arrived_count = 0;
4423 	wingp->red_alert_skipped_ships = 0;
4424 	wingp->total_destroyed = 0;
4425 	wingp->total_departed = 0;	// Goober5000
4426 	wingp->total_vanished = 0;	// Goober5000
4427     wingp->flags.reset();
4428 
4429 	// squad logo - Goober5000
4430 	if (optional_string("+Squad Logo:"))
4431 	{
4432 		int flag = -1;
4433 
4434 		stuff_string(wingp->wing_squad_filename, F_NAME, MAX_FILENAME_LEN);
4435 
4436 		// load it only if FRED isn't running
4437 		if (!Fred_running)
4438 		{
4439 			// check all previous wings to see if we already loaded it (we want to save memory)
4440 			for (i = 0; i < Num_wings; i++)
4441 			{
4442 				// do we have a previous texture?
4443 				if (Wings[i].wing_insignia_texture != -1)
4444 				{
4445 					// if we have a match
4446 					if (!stricmp(Wings[i].wing_squad_filename, wingp->wing_squad_filename))
4447 					{
4448 						flag = i;
4449 						break;
4450 					}
4451 				}
4452 			}
4453 
4454 			// if we have loaded it already, just use the old bitmap index
4455 			if (flag != -1)
4456 			{
4457 				wingp->wing_insignia_texture = Wings[flag].wing_insignia_texture;
4458 			}
4459 			else
4460 			{
4461 				wing_load_squad_bitmap(wingp);
4462 			}
4463 		}
4464 	}
4465 
4466 	required_string("$Waves:");
4467 	stuff_int(&wingp->num_waves);
4468 	Assert ( wingp->num_waves >= 1 );		// there must be at least 1 wave
4469 
4470 	wingp->current_wave = 0;
4471 
4472 	required_string("$Wave Threshold:");
4473 	stuff_int(&wingp->threshold);
4474 
4475 	required_string("$Special Ship:");
4476 	stuff_int(&wingp->special_ship);
4477 
4478 	// Use a custom formation if specified
4479 	if (optional_string("+Formation:")) {
4480 		char f[NAME_LENGTH];
4481 		stuff_string(f, F_NAME, NAME_LENGTH);
4482 
4483 		wingp->formation = wing_formation_lookup(f);
4484 		if (wingp->formation < 0) {
4485 			Warning(LOCATION, "Invalid Formation %s.", f);
4486 		}
4487 	}
4488 	else {
4489 		wingp->formation = -1;
4490 	}
4491 
4492 	wingp->arrival_anchor = -1;
4493 	wingp->arrival_distance = 0;
4494 	wingp->arrival_path_mask = -1;	// -1 only until resolved
4495 
4496 	find_and_stuff("$Arrival Location:", &wingp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
4497 
4498 	if ( optional_string("+Arrival Distance:") )
4499 	{
4500 		stuff_int( &wingp->arrival_distance );
4501 
4502 		// Goober5000
4503 		if ((wingp->arrival_distance <= 0) && ((wingp->arrival_location == ARRIVE_NEAR_SHIP) || (wingp->arrival_location == ARRIVE_IN_FRONT_OF_SHIP)))
4504 		{
4505 			Warning(LOCATION, "Arrival distance for wing %s cannot be %d.  Setting to 1.\n", wingp->name, wingp->arrival_distance);
4506 			wingp->arrival_distance = 1;
4507 		}
4508 	}
4509 
4510 	if ( wingp->arrival_location != ARRIVE_AT_LOCATION )
4511 	{
4512 		required_string("$Arrival Anchor:");
4513 		stuff_string(name, F_NAME, NAME_LENGTH);
4514 		wingp->arrival_anchor = get_anchor(name);
4515 	}
4516 
4517 	if (optional_string("+Arrival Paths:"))
4518 	{
4519 		// temporarily use mask to point to the restriction index
4520 		wingp->arrival_path_mask = add_path_restriction();
4521 	}
4522 
4523 	if (optional_string("+Arrival delay:")) {
4524 		stuff_int(&delay);
4525 		if ( delay < 0 )
4526 			Error(LOCATION, "Cannot have arrival delay < 0 on wing %s", wingp->name );
4527 	} else
4528 		delay = 0;
4529 	saved_arrival_delay = delay;
4530 
4531 	if ( !Fred_running ){
4532 		wingp->arrival_delay = -delay;
4533 	} else {
4534 		wingp->arrival_delay = delay;
4535 	}
4536 
4537 	required_string("$Arrival Cue:");
4538 	wingp->arrival_cue = get_sexp_main();
4539 
4540 	wingp->departure_anchor = -1;
4541 	wingp->departure_path_mask = -1;	// -1 only until resolved
4542 
4543 	find_and_stuff("$Departure Location:", &wingp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
4544 
4545 	if ( wingp->departure_location != DEPART_AT_LOCATION )
4546 	{
4547 		required_string("$Departure Anchor:");
4548 		stuff_string( name, F_NAME, NAME_LENGTH );
4549 		wingp->departure_anchor = get_anchor(name);
4550 	}
4551 
4552 	if (optional_string("+Departure Paths:"))
4553 	{
4554 		// temporarily use mask to point to the restriction index
4555 		wingp->departure_path_mask = add_path_restriction();
4556 	}
4557 
4558 	if (optional_string("+Departure delay:")) {
4559 		stuff_int(&delay);
4560 		if ( delay < 0 )
4561 			Error(LOCATION, "Cannot have departure delay < 0 on wing %s", wingp->name );
4562 	} else
4563 		delay = 0;
4564 
4565 	if ( !Fred_running )
4566 		wingp->departure_delay = -delay;		// use negative numbers to mean that delay timer not yet set
4567 	else
4568 		wingp->departure_delay = delay;
4569 
4570 	required_string("$Departure Cue:");
4571 	wingp->departure_cue = get_sexp_main();
4572 
4573 	// stores a list of all names of ships in the wing
4574 	required_string("$Ships:");
4575 	wingp->wave_count = (int)stuff_string_list( ship_names, MAX_SHIPS_PER_WING );
4576 	wingp->current_count = 0;
4577 
4578 	// get the wings goals, if any
4579 	wing_goals = -1;
4580 	if ( optional_string("$AI Goals:") )
4581 		wing_goals = get_sexp_main();
4582 
4583 	wingp->hotkey = -1;
4584 	if (optional_string("+Hotkey:")) {
4585 		stuff_int(&wingp->hotkey);
4586 		Assert((wingp->hotkey >= 0) && (wingp->hotkey < 10));
4587 	}
4588 
4589 	if (optional_string("+Flags:")) {
4590 		auto count = (int)stuff_string_list( wing_flag_strings, PARSEABLE_WING_FLAGS);
4591 
4592         for (i = 0; i < count; i++) {
4593             if (!stricmp(wing_flag_strings[i], NOX("ignore-count")))
4594                 wingp->flags.set(Ship::Wing_Flags::Ignore_count);
4595             else if (!stricmp(wing_flag_strings[i], NOX("reinforcement")))
4596                 wingp->flags.set(Ship::Wing_Flags::Reinforcement);
4597             else if (!stricmp(wing_flag_strings[i], NOX("no-arrival-music")))
4598                 wingp->flags.set(Ship::Wing_Flags::No_arrival_music);
4599             else if (!stricmp(wing_flag_strings[i], NOX("no-arrival-message")))
4600                 wingp->flags.set(Ship::Wing_Flags::No_arrival_message);
4601             else if (!stricmp(wing_flag_strings[i], NOX("no-arrival-warp")))
4602                 wingp->flags.set(Ship::Wing_Flags::No_arrival_warp);
4603             else if (!stricmp(wing_flag_strings[i], NOX("no-departure-warp")))
4604                 wingp->flags.set(Ship::Wing_Flags::No_departure_warp);
4605             else if (!stricmp(wing_flag_strings[i], NOX("no-dynamic")))
4606                 wingp->flags.set(Ship::Wing_Flags::No_dynamic);
4607             else if (!stricmp(wing_flag_strings[i], NOX("nav-carry-status")))
4608                 wingp->flags.set(Ship::Wing_Flags::Nav_carry);
4609             else
4610                 Warning(LOCATION, "unknown wing flag\n%s\n\nSkipping.", wing_flag_strings[i]);
4611 		}
4612 	}
4613 
4614 	// get the wave arrival delay bounds (if present).  Used as lower and upper bounds (in seconds)
4615 	// which determine when new waves of a wing should arrive.
4616 	wingp->wave_delay_min = 0;
4617 	wingp->wave_delay_max = 0;
4618 	if ( optional_string("+Wave Delay Min:") )
4619 		stuff_int( &(wingp->wave_delay_min) );
4620 	if ( optional_string("+Wave Delay Max:") )
4621 		stuff_int( &(wingp->wave_delay_max) );
4622 
4623 	// be sure to set the wave arrival timestamp of this wing to pop right away so that the
4624 	// wing could be created if it needs to be
4625 	wingp->wave_delay_timestamp = timestamp(0);
4626 
4627 	// initialize wing goals
4628 	for (i=0; i<MAX_AI_GOALS; i++) {
4629 		wingp->ai_goals[i].ai_mode = AI_GOAL_NONE;
4630 		wingp->ai_goals[i].signature = -1;
4631 		wingp->ai_goals[i].priority = -1;
4632 		wingp->ai_goals[i].flags.reset();
4633 	}
4634 
4635 	// 7/13/98 -- MWA
4636 	// error checking against the player ship wings (i.e. starting & tvt) to be sure that wave count doesn't exceed one for
4637 	// these wings.
4638 	if ( MULTI_NOT_TEAM ) {
4639 		for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
4640 			if ( !stricmp(Starting_wing_names[i], wingp->name) ) {
4641 				if ( wingp->num_waves > 1 ) {
4642 					// only end the game if we're the server - clients will eventually find out :)
4643 					if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4644 						multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_WAVE_COUNT);
4645 					}
4646 				}
4647 			}
4648 		}
4649 	}
4650 	else if (MULTI_TEAM) {
4651 		for (i = 0; i < MAX_TVT_WINGS; i++ ) {
4652 			if ( !stricmp(TVT_wing_names[i], wingp->name) ) {
4653 				if ( wingp->num_waves > 1 ) {
4654 					// only end the game if we're the server - clients will eventually find out :)
4655 					if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4656 						multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_WAVE_COUNT);
4657 					}
4658 				}
4659 			}
4660 		}
4661 	}
4662 
4663 	// Get the next starting signature for this in this wing.  We want to reserve wave_count * num_waves
4664 	// of signature.  These can be used to construct wings for ingame joiners.
4665 	if ( Game_mode & GM_MULTIPLAYER ) {
4666 		int next_signature;
4667 
4668 		wingp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP );
4669 		// Cyborg -- Subtract one because the original wave already has its signatures set.
4670 		next_signature = wingp->net_signature + (wingp->wave_count * (wingp->num_waves - 1));
4671 		if ( next_signature > SHIP_SIG_MAX )
4672 			Error(LOCATION, "Too many total ships in mission (%d) for network signature assignment", SHIP_SIG_MAX);
4673 		multi_set_network_signature( (ushort)next_signature, MULTI_SIG_SHIP );
4674 	}
4675 
4676 	for (i=0; i<MAX_SHIPS_PER_WING; i++)
4677 		wingp->ship_index[i] = -1;
4678 
4679 	// set up the ai_goals for this wing -- all ships created from this wing will inherit these goals
4680 	// goals for the wing are stored slightly differently than for ships.  We simply store the index
4681 	// into the sexpression array of each goal (max 10).  When a ship in this wing is created, each
4682 	// goal in the wings goal array is given to the ship.
4683 	if ( wing_goals != -1 ) {
4684 		int sexp;
4685 
4686 		// this will assign the goals to the wings as well as to any ships in the wing that have been
4687 		// already created.
4688 		for ( sexp = CDR(wing_goals); sexp != -1; sexp = CDR(sexp) )
4689 			ai_add_wing_goal_sexp(sexp, AIG_TYPE_EVENT_WING, wingp);  // used by Fred
4690 
4691 		free_sexp2(wing_goals);  // free up sexp nodes for reuse, since they aren't needed anymore.
4692 	}
4693 
4694 	// set the wing number for all ships in the wing
4695 	for (i = 0; i < wingp->wave_count; i++ ) {
4696 		char *ship_name = ship_names[i];
4697 		int assigned = 0;
4698 
4699 		// Goober5000 - since the ship/wing creation stuff is reordered to accommodate multiple docking,
4700 		// everything is still only in the parse array at this point (in both FRED and FS2)
4701 
4702 		// find the parse object and assign it the wing number
4703 		for (SCP_vector<p_object>::iterator p_objp = Parse_objects.begin(); p_objp != Parse_objects.end(); ++p_objp) {
4704 			if ( !strcmp(ship_name, p_objp->name) ) {
4705 				// get Allender -- ship appears to be in multiple wings
4706 				Assert (p_objp->wingnum == -1);
4707 
4708 				// assign wingnum
4709 				p_objp->wingnum = wingnum;
4710 				p_objp->pos_in_wing = i;
4711 				assigned++;
4712 
4713 				// Goober5000 - if this is a player start object, there shouldn't be a wing arrival delay (Mantis #2678)
4714 				if ((p_objp->flags[Mission::Parse_Object_Flags::OF_Player_start]) && (saved_arrival_delay != 0)) {
4715 					Warning(LOCATION, "Wing %s specifies an arrival delay of %ds, but it also contains a player.  The arrival delay will be reset to 0.", wingp->name, saved_arrival_delay);
4716 					if (!Fred_running && wingp->arrival_delay > 0) {
4717 						// timestamp has been set, so set it again
4718 						wingp->arrival_delay = timestamp(0);
4719 					} else {
4720 						// no timestamp, or timestamp invalid
4721 						wingp->arrival_delay = 0;
4722 					}
4723 					// prevent message from reappearing
4724 					saved_arrival_delay = 0;
4725 				}
4726 			}
4727 		}
4728 
4729 		// error checking
4730 		if (assigned == 0) {
4731 			Error(LOCATION, "Cannot load mission -- for wing %s, ship %s is not present in #Objects section.\n", wingp->name, ship_name);
4732 		} else if (assigned > 1) {
4733 			Error(LOCATION, "Cannot load mission -- for wing %s, ship %s is specified multiple times in wing.\n", wingp->name, ship_name);
4734 		}
4735 	}
4736 
4737 	// Goober5000 - wing creation stuff moved to post_process_ships_wings
4738 }
4739 
parse_wings(mission * pm)4740 void parse_wings(mission* pm)
4741 {
4742 	// reset this for the missing wing bug.
4743 	for (int i = 0; i < MAX_SQUADRON_WINGS; i++) {
4744 		Squadron_wing_names_found[i] = false;
4745 	}
4746 
4747 	required_string("#Wings");
4748 	while (required_string_either("#Events", "$Name:"))
4749 	{
4750 		Assert(Num_wings < MAX_WINGS);
4751 		parse_wing(pm);
4752 		Num_wings++;
4753 	}
4754 
4755 	bool found = false;
4756 
4757 	static_assert(MAX_TVT_WINGS_PER_TEAM == 1, "Unless you also update the section of code below or redo the loadout code, for TvT, there should be just one player wing, otherwise, wings may start disappearing in game.");
4758 
4759 	// now see if we found the missing wing.  We're looking for a wing that is there after a wing that is not.
4760 	// for now TvT mission do not have enough player wings to be affected by this bug.
4761 	if (pm->game_type & (MISSION_TYPE_MULTI | MISSION_TYPE_MULTI_COOP)) {
4762 		do {
4763 			found = false;
4764 			// we only search up to the MAX_STARTING_WINGS because non-starting wings should not be in starting wing indices (0-2)
4765 			for (int i = 1; i < MAX_STARTING_WINGS; i++) {
4766 				// If there was a wing for this squadron entry, check the last one. If it's empty, we found a mistake, so move the wing names over.
4767 				if (Squadron_wing_names_found[i] && !Squadron_wing_names_found[i - 1]) {
4768 					Warning(LOCATION, "Squadron wings are not in the correct order and may cause wings to disappear in multi.\n\nEither wing %s should exist or the %s entry needs to come before it in the list.\n\nPlease go back and fix the mission.", Squadron_wing_names[i - 1], Squadron_wing_names[i]);
4769 					char temp_chars[NAME_LENGTH];
4770 					strcpy_s(temp_chars, Squadron_wing_names[i - 1]);
4771 					strcpy_s(Squadron_wing_names[i - 1], Squadron_wing_names[i]);
4772 					strcpy_s(Squadron_wing_names[i], temp_chars);
4773 					Squadron_wing_names_found[i] = !Squadron_wing_names_found[i];
4774 					Squadron_wing_names_found[i - 1] = !Squadron_wing_names_found[i - 1];
4775 					found = true;
4776 				}
4777 			}
4778 		} while (found);
4779 	}
4780 }
4781 
4782 // Goober5000
resolve_path_masks(int anchor,int * path_mask)4783 void resolve_path_masks(int anchor, int *path_mask)
4784 {
4785 	path_restriction_t *prp;
4786 
4787 	// if we have no restrictions, do a quick out
4788 	if (*path_mask < 0)
4789 	{
4790 		*path_mask = 0;
4791 		return;
4792 	}
4793 
4794 	// get path restriction info
4795 	prp = &Path_restrictions[*path_mask];
4796 
4797 	// uninitialized; compute the mask from scratch
4798 	if (prp->cached_mask & (1 << MAX_SHIP_BAY_PATHS))
4799 	{
4800 		int j, bay_path, modelnum;
4801 		p_object *parent_pobjp;
4802 
4803 		// get anchor ship
4804 		Assert(!(anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG));
4805 		parent_pobjp = mission_parse_get_parse_object(Parse_names[anchor]);
4806 
4807 		// Load the anchor ship model with subsystems and all; it'll need to be done for this mission anyway
4808 		ship_info *sip = &Ship_info[parent_pobjp->ship_class];
4809 		modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
4810 
4811 		// resolve names to indexes
4812 		*path_mask = 0;
4813 		for	(j = 0; j < prp->num_paths; j++)
4814 		{
4815 			bay_path = model_find_bay_path(modelnum, prp->path_names[j]);
4816 			if (bay_path < 0)
4817 				continue;
4818 
4819 			*path_mask |= (1 << bay_path);
4820 		}
4821 
4822 		// cache the result
4823 		prp->cached_mask = *path_mask;
4824 	}
4825 	// already computed; so reuse it
4826 	else
4827 	{
4828 		*path_mask = prp->cached_mask;
4829 	}
4830 }
4831 
4832 /**
4833  * Resolve arrival/departure path masks
4834  * NB: between parsing and the time this function is run, the path_mask variables store the index of the path info;
4835  * at all other times, they store the masks of the bay paths as expected
4836  */
post_process_path_stuff()4837 void post_process_path_stuff()
4838 {
4839 	int i;
4840 	wing *wingp;
4841 
4842 	// take care of parse objects (ships)
4843 	for (SCP_vector<p_object>::iterator pobjp = Parse_objects.begin(); pobjp != Parse_objects.end(); ++pobjp)
4844 	{
4845 		resolve_path_masks(pobjp->arrival_anchor, &pobjp->arrival_path_mask);
4846 		resolve_path_masks(pobjp->departure_anchor, &pobjp->departure_path_mask);
4847 	}
4848 
4849 	// take care of wings
4850 	for (i = 0; i < Num_wings; i++)
4851 	{
4852 		wingp = &Wings[i];
4853 
4854 		resolve_path_masks(wingp->arrival_anchor, &wingp->arrival_path_mask);
4855 		resolve_path_masks(wingp->departure_anchor, &wingp->departure_path_mask);
4856 	}
4857 }
4858 
4859 // Goober5000
post_process_ships_wings()4860 void post_process_ships_wings()
4861 {
4862 	int i;
4863 
4864 	// Goober5000 - first, resolve the path masks.  Needs to be done first because
4865 	// mission_parse_maybe_create_parse_object relies on it.
4866 	post_process_path_stuff();
4867 
4868 	// Goober5000 - now that we've parsed all the objects, resolve the initially docked references.
4869 	// This must be done before anything that relies on the dock references but can't be done until
4870 	// both ships and wings have been parsed.
4871 	mission_parse_set_up_initial_docks();
4872 
4873 	// Goober5000 - now add all the parse objects to the ship registry.  This must be done before
4874 	// any ships are created from the parse objects.
4875 	for (auto &p_obj : Parse_objects)
4876 	{
4877 		ship_registry_entry entry(p_obj.name);
4878 		entry.p_objp = &p_obj;
4879 
4880 		Ship_registry.push_back(entry);
4881 		Ship_registry_map[p_obj.name] = static_cast<int>(Ship_registry.size() - 1);
4882 	}
4883 
4884 	// Goober5000 - now create all objects that we can.  This must be done before any ship stuff
4885 	// but can't be done until the dock references are resolved.  This was originally done
4886 	// in parse_object().
4887 	for (auto &p_obj : Parse_objects)
4888 	{
4889 		// Evaluate the arrival cue and maybe set up the arrival delay.  This can't be done until the ship registry is populated
4890 		// (because SEXPs now require a complete ship registry) but must be done before the arrival list check inside
4891 		// mission_parse_maybe_create_parse_object.  That check is, in fact, the only reason this is needed.  We don't need to
4892 		// pre-emptively set up the delay for wings because there is no equivalent wing arrival list check.  In any case, the
4893 		// arrival_delay is always validated in mission_did_ship_arrive (for ships) and parse_wing_create_ships (for wings).
4894 		// Addendum: Don't mess with any arrival delays which are strictly positive, meaning they have already been set.
4895 		// (This is the case for ships destroyed before mission.  In the retail codebase, the destroy-before-mission chunk was
4896 		// parsed after the arrival cue and delay were parsed and checked, so it overwrote them.)
4897 		if (!Fred_running && (p_obj.arrival_cue >= 0) && (p_obj.arrival_delay <= 0))
4898 		{
4899 			// eval the arrival cue.  if the cue is true, set up the timestamp for the arrival delay
4900 			if (eval_sexp(p_obj.arrival_cue))
4901 				p_obj.arrival_delay = timestamp(-p_obj.arrival_delay * 1000);
4902 		}
4903 
4904 		// create as usual
4905 		mission_parse_maybe_create_parse_object(&p_obj);
4906 	}
4907 
4908 
4909 	// ----------------- at this point the ships have been created -----------------
4910 	// Now set up the wings.  This must be done after both dock stuff and ship stuff.
4911 
4912 	// error checking for custom wings
4913 	if (strcmp(Starting_wing_names[0], TVT_wing_names[0]) != 0)
4914 	{
4915 		Error(LOCATION, "The first starting wing and the first team-versus-team wing must have the same wing name.\n");
4916 	}
4917 
4918 	// Goober5000 - for FRED, the ships are initialized after the wings, so we must now tell the wings
4919 	// where their ships are
4920 	if (Fred_running)
4921 	{
4922 		// even though the ships are already created, only the parse objects know the wing info
4923 		for (SCP_vector<p_object>::iterator p_objp = Parse_objects.begin(); p_objp != Parse_objects.end(); ++p_objp)
4924 		{
4925 			// link the ship into the wing's array of ship indices (previously done in parse_wing
4926 			// in a rather less backwards way)
4927 			if (p_objp->wingnum >= 0)
4928 			{
4929 				int shipnum = ship_name_lookup(p_objp->name);
4930 
4931 				Assert(shipnum >= 0 && shipnum < MAX_SHIPS);
4932 				Assert(p_objp->pos_in_wing >= 0 && p_objp->pos_in_wing < MAX_SHIPS_PER_WING);
4933 
4934 				Wings[p_objp->wingnum].ship_index[p_objp->pos_in_wing] = shipnum;
4935 			}
4936 		}
4937 	}
4938 	// Goober5000 - for FS2, the wings are initialized first, so all we have to do is create their ships
4939 	else
4940 	{
4941 		for (i = 0; i < Num_wings; i++)
4942 		{
4943 			wing *wingp = &Wings[i];
4944 
4945 			// create the wing if is isn't a reinforcement.
4946 			if (!(wingp->flags[Ship::Wing_Flags::Reinforcement]))
4947 				parse_wing_create_ships(wingp, wingp->wave_count);
4948 		}
4949 	}
4950 }
4951 
4952 // mission events are sexpressions which cause things to happen based on the outcome
4953 // of other events in a mission.  Essentially scripting the different things that can happen
4954 // in a mission
4955 
parse_event(mission *)4956 void parse_event(mission * /*pm*/)
4957 {
4958 	char buf[NAME_LENGTH];
4959 	mission_event *event;
4960 
4961 	event = &Mission_events[Num_mission_events];
4962 	// Need to set this to zero so that we don't accidentally reuse old data.
4963 	event->flags = 0;
4964 
4965 	required_string( "$Formula:" );
4966 	event->formula = get_sexp_main();
4967 
4968 	if (optional_string("+Name:")){
4969 		stuff_string(event->name, F_NAME, NAME_LENGTH);
4970 	} else {
4971 		event->name[0] = 0;
4972 	}
4973 
4974 	if ( optional_string("+Repeat Count:")){
4975 		stuff_int( &(event->repeat_count) );
4976 
4977 		// sanity check on the repeat count variable
4978 		// _argv[-1] - negative repeat count is now legal; means repeat indefinitely.
4979 		if ( event->repeat_count == 0 ){
4980 			Warning(LOCATION, "Repeat count for mission event %s is 0.\nMust be >= 1 or negative!  Setting to 1.", event->name );
4981 			event->repeat_count = 1;
4982 		}
4983 	} else {
4984 		event->repeat_count = 1;
4985 	}
4986 
4987 	if ( optional_string("+Trigger Count:")){
4988 		stuff_int( &(event->trigger_count) );
4989 		event->flags |= MEF_USING_TRIGGER_COUNT;
4990 
4991 		// if we have a trigger count but no repeat count, we want the event to loop until it has triggered enough times
4992 		if (event->repeat_count == 1) {
4993 			event->repeat_count = -1;
4994 		}
4995 
4996 		// sanity check on the trigger count variable
4997 		// negative trigger count is also legal
4998 		if ( event->trigger_count == 0 ){
4999 			Warning(LOCATION, "Trigger count for mission event %s is 0.\nMust be >= 1 or negative!  Setting to 1.", event->name );
5000 			event->trigger_count = 1;
5001 		}
5002 	} else {
5003 		event->trigger_count = 1;
5004 	}
5005 
5006 	event->interval = -1;
5007 	if ( optional_string("+Interval:")){
5008 		stuff_int( &(event->interval) );
5009 	}
5010 
5011 	event->score = 0;
5012 	if ( optional_string("+Score:") ){
5013 		stuff_int(&event->score);
5014 	}
5015 
5016 	event->chain_delay = -1;
5017 	if ( optional_string("+Chained:") ){
5018 		stuff_int(&event->chain_delay);
5019 	}
5020 
5021 	if ( optional_string("+Objective:") ) {
5022 		stuff_string(buf, F_NAME, NAME_LENGTH);
5023 		event->objective_text = vm_strdup(buf);
5024 	} else {
5025 		event->objective_text = NULL;
5026 	}
5027 
5028 	if ( optional_string("+Objective key:") ) {
5029 		stuff_string(buf, F_NAME, NAME_LENGTH);
5030 		event->objective_key_text = vm_strdup(buf);
5031 	} else {
5032 		event->objective_key_text = NULL;
5033 	}
5034 
5035 	event->team = -1;
5036 	if( optional_string("+Team:") ) {
5037 		stuff_int(&event->team);
5038 
5039 		// sanity check
5040 		if (event->team < -1 || event->team >= MAX_TVT_TEAMS) {
5041 			if (Fred_running && !Warned_about_team_out_of_range) {
5042 				Warning(LOCATION, "+Team: value was out of range in the mission file!  This was probably caused by a bug in an older version of FRED.  Using -1 for now.");
5043 				Warned_about_team_out_of_range = true;
5044 			}
5045 			event->team = -1;
5046 		}
5047 	}
5048 
5049 	if (optional_string("+Event Flags:")) {
5050 		parse_string_flag_list(&event->flags, Mission_event_flags, Num_mission_event_flags);
5051 	}
5052 
5053 	if( optional_string("+Event Log Flags:") ) {
5054 		SCP_vector<SCP_string> buffer;
5055 
5056 		stuff_string_list(buffer);
5057 		for (int i = 0; i < (int)buffer.size(); i++) {
5058 			int add_flag = 1;
5059 
5060 			for (int j = 0; j < MAX_MISSION_EVENT_LOG_FLAGS; j++) {
5061 				if (!stricmp(buffer[i].c_str(), Mission_event_log_flags[j])) {
5062 					// bitshift add_flag so that it equals the index of the flag in Mission_event_log_flags[]
5063 					add_flag = add_flag << j;
5064 					event->mission_log_flags |= add_flag;
5065 				}
5066 			}
5067 		}
5068 	}
5069 
5070 	if (optional_string("$Annotations Start")) {
5071 		// annotations are only used in FRED
5072 		if (Fred_running) {
5073 			while (check_for_string("+Comment:") || check_for_string("+Background Color:") || check_for_string("+Path:")) {
5074 				event_annotation ea;
5075 				ea.path.push_back(Num_mission_events);
5076 
5077 				if (optional_string("+Comment:")) {
5078 					stuff_string(ea.comment, F_MULTITEXT);
5079 					lcl_replace_stuff(ea.comment, true);
5080 				}
5081 
5082 				if (optional_string("+Background Color:")) {
5083 					stuff_ubyte(&ea.r);
5084 					if (*Mp == ',')
5085 						Mp++;
5086 					stuff_ubyte(&ea.g);
5087 					if (*Mp == ',')
5088 						Mp++;
5089 					stuff_ubyte(&ea.b);
5090 				}
5091 
5092 				if (optional_string("+Path:")) {
5093 					int num;
5094 					while (true) {
5095 						ignore_gray_space();
5096 						if (stuff_int_optional(&num) != 2) {
5097 							break;
5098 						}
5099 						ea.path.push_back(num);
5100 					}
5101 				}
5102 
5103 				Event_annotations.push_back(std::move(ea));
5104 			}
5105 			required_string("$Annotations End");
5106 		} else {
5107 			skip_to_string("$Annotations End");
5108 		}
5109 	}
5110 }
5111 
parse_events(mission * pm)5112 void parse_events(mission *pm)
5113 {
5114 	required_string("#Events");
5115 
5116 	while (required_string_either( "#Goals", "$Formula:")) {
5117 		Assert( Num_mission_events < MAX_MISSION_EVENTS );
5118 		parse_event(pm);
5119 		Num_mission_events++;
5120 	}
5121 }
5122 
parse_goal(mission * pm)5123 void parse_goal(mission *pm)
5124 {
5125 	int dummy;
5126 
5127 	mission_goal	*goalp;
5128 
5129 	goalp = &Mission_goals[Num_goals++];
5130 
5131 	Assert(Num_goals < MAX_GOALS);
5132 	Assert(pm != NULL);
5133 
5134 	find_and_stuff("$Type:", &goalp->type, F_NAME, Goal_type_names, Num_goal_type_names, "goal type");
5135 
5136 	required_string("+Name:");
5137 	stuff_string(goalp->name, F_NAME, NAME_LENGTH);
5138 
5139 	// backwards compatibility for old Fred missions - all new missions should use $MessageNew
5140 	if(optional_string("$Message:")){
5141 		stuff_string(goalp->message, F_NAME, MAX_GOAL_TEXT);
5142 	} else {
5143 		required_string("$MessageNew:");
5144 		stuff_string(goalp->message, F_MULTITEXT, MAX_GOAL_TEXT);
5145 	}
5146 
5147 	if (optional_string("$Rating:")){
5148 		stuff_int(&dummy);  // not used
5149 	}
5150 
5151 	required_string("$Formula:");
5152 	goalp->formula = get_sexp_main();
5153 
5154 	goalp->flags = 0;
5155 	if ( optional_string("+Invalid:") )
5156 		goalp->type |= INVALID_GOAL;
5157 	if ( optional_string("+Invalid") )
5158 		goalp->type |= INVALID_GOAL;
5159 	if ( optional_string("+No music") )
5160 		goalp->flags |= MGF_NO_MUSIC;
5161 
5162 	goalp->score = 0;
5163 	if ( optional_string("+Score:") ){
5164 		stuff_int(&goalp->score);
5165 	}
5166 
5167 	goalp->team = 0;
5168 	if ( optional_string("+Team:") ){
5169 		stuff_int( &goalp->team );
5170 
5171 		// sanity check
5172 		if (goalp->team < -1 || goalp->team >= Num_iffs) {
5173 			if (Fred_running && !Warned_about_team_out_of_range) {
5174 				Warning(LOCATION, "+Team: value was out of range in the mission file!  This was probably caused by a bug in an older version of FRED.  Using -1 for now.");
5175 				Warned_about_team_out_of_range = true;
5176 			}
5177 			goalp->team = -1;
5178 		}
5179 	}
5180 }
5181 
parse_goals(mission * pm)5182 void parse_goals(mission *pm)
5183 {
5184 	required_string("#Goals");
5185 
5186 	while (required_string_either("#Waypoints", "$Type:")){
5187 		parse_goal(pm);
5188 	}
5189 }
5190 
parse_waypoint_list(mission * pm)5191 void parse_waypoint_list(mission *pm)
5192 {
5193 	Assert(pm != NULL);
5194 
5195 	char name_buf[NAME_LENGTH];
5196 	required_string("$Name:");
5197 	stuff_string(name_buf, F_NAME, NAME_LENGTH);
5198 
5199 	SCP_vector<vec3d> vec_list;
5200 	required_string("$List:");
5201 	stuff_vec3d_list(vec_list);
5202 
5203 	waypoint_add_list(name_buf, vec_list);
5204 }
5205 
parse_waypoints_and_jumpnodes(mission * pm)5206 void parse_waypoints_and_jumpnodes(mission *pm)
5207 {
5208 	vec3d pos;
5209 
5210 	required_string("#Waypoints");
5211 
5212 	char file_name[MAX_FILENAME_LEN] = { 0 };
5213 	char jump_name[NAME_LENGTH] = { 0 };
5214 
5215 	while (optional_string("$Jump Node:")) {
5216 		stuff_vec3d(&pos);
5217 		CJumpNode jnp(&pos);
5218 
5219 		if (optional_string("$Jump Node Name:") || optional_string("+Jump Node Name:")) {
5220 			stuff_string(jump_name, F_NAME, NAME_LENGTH);
5221 			jnp.SetName(jump_name);
5222 		}
5223 
5224 		if(optional_string("+Model File:")){
5225 			stuff_string(file_name, F_NAME, MAX_FILENAME_LEN);
5226 			jnp.SetModel(file_name);
5227 		}
5228 
5229 		if(optional_string("+Alphacolor:")) {
5230 			ubyte r,g,b,a;
5231 			stuff_ubyte(&r);
5232 			stuff_ubyte(&g);
5233 			stuff_ubyte(&b);
5234 			stuff_ubyte(&a);
5235 			jnp.SetAlphaColor(r, g, b, a);
5236 		}
5237 
5238 		if(optional_string("+Hidden:")) {
5239 			int hide;
5240 			stuff_boolean(&hide);
5241 			jnp.SetVisibility(!hide);
5242 		}
5243 
5244 		Jump_nodes.push_back(std::move(jnp));
5245 	}
5246 
5247 	while (required_string_either("#Messages", "$Name:"))
5248 		parse_waypoint_list(pm);
5249 }
5250 
parse_messages(mission * pm,int flags)5251 void parse_messages(mission *pm, int flags)
5252 {
5253 	required_string("#Messages");
5254 
5255 	// command stuff by Goober5000 ---------------------------------------
5256 	strcpy_s(pm->command_sender, DEFAULT_COMMAND);
5257 	if (optional_string("$Command Sender:"))
5258 	{
5259 		char temp[NAME_LENGTH];
5260 		stuff_string(temp, F_NAME, NAME_LENGTH);
5261 
5262 		if (*temp == '#')
5263 			strcpy_s(pm->command_sender, &temp[1]);
5264 		else
5265 			strcpy_s(pm->command_sender, temp);
5266 	}
5267 
5268 	pm->command_persona = Default_command_persona;
5269 	if (optional_string("$Command Persona:"))
5270 	{
5271 		int idx;
5272 		char temp[NAME_LENGTH];
5273 		stuff_string(temp, F_NAME, NAME_LENGTH);
5274 
5275 		idx = message_persona_name_lookup(temp);
5276 		if (idx >= 0)
5277 			pm->command_persona = idx;
5278 		else
5279 			Warning(LOCATION, "Supplied Command Persona is invalid!  Defaulting to %s.", Personas[Default_command_persona].name);
5280 	}
5281 	// end of command stuff ----------------------------------------------
5282 
5283 
5284 	mprintf(("Starting mission message count : %d\n", (int)Message_waves.size()));
5285 
5286 	// the message_parse function can be found in MissionMessage.h.  The format in the
5287 	// mission file takes the same format as the messages in messages,tbl.  Make parsing
5288 	// a whole lot easier!!!
5289 	while ( required_string_either("#Reinforcements", "$Name")){
5290 		message_parse((flags & MPF_IMPORT_FSM) != 0);		// call the message parsing system
5291 	}
5292 
5293 	mprintf(("Ending mission message count : %d\n", (int)Message_waves.size()));
5294 }
5295 
parse_reinforcement(mission * pm)5296 void parse_reinforcement(mission *pm)
5297 {
5298 	reinforcements *ptr;
5299 	p_object *rforce_obj = NULL;
5300 	int instance = -1;
5301 
5302 	Assert(Num_reinforcements < MAX_REINFORCEMENTS);
5303 	Assert(pm != NULL);
5304 	ptr = &Reinforcements[Num_reinforcements];
5305 
5306 	required_string("$Name:");
5307 	stuff_string(ptr->name, F_NAME, NAME_LENGTH);
5308 
5309 	find_and_stuff("$Type:", &ptr->type, F_NAME, Reinforcement_type_names, Num_reinforcement_type_names, "reinforcement type");
5310 
5311 	required_string("$Num times:");
5312 	stuff_int(&ptr->uses);
5313 	ptr->num_uses = 0;
5314 
5315 	// reset the flags to 0
5316 	ptr->flags = 0;
5317 
5318 	if ( optional_string("+Arrival delay:") ){
5319 		stuff_int( &(ptr->arrival_delay) );
5320 	}
5321 
5322 	if ( optional_string("+No Messages:") ){
5323 		stuff_string_list( ptr->no_messages, MAX_REINFORCEMENT_MESSAGES );
5324 	}
5325 
5326 	if ( optional_string("+Yes Messages:") ){
5327 		stuff_string_list( ptr->yes_messages, MAX_REINFORCEMENT_MESSAGES );
5328 	}
5329 
5330 	// sanity check on the names of reinforcements
5331 	rforce_obj = mission_parse_get_parse_object(ptr->name);
5332 
5333 	if (rforce_obj == NULL) {
5334 		if ((instance = wing_name_lookup(ptr->name, 1)) == -1) {
5335 			Warning(LOCATION, "Reinforcement %s not found as ship or wing", ptr->name);
5336 			return;
5337 		}
5338 	} else {
5339 		// Individual ships in wings can't be reinforcements - FUBAR
5340 		if (rforce_obj->wingnum >= 0)
5341 		{
5342 			Warning(LOCATION, "Reinforcement %s is part of a wing - Ignoring reinforcement declaration", ptr->name);
5343 			return;
5344 		}
5345 		else
5346 		{
5347 			instance = rforce_obj->wingnum;
5348 		}
5349 	}
5350 
5351 	// now, if the reinforcement is a wing, then set the number of waves of the wing == number of
5352 	// uses of the reinforcement
5353 	if (instance >= 0) {
5354 		Wings[instance].num_waves = ptr->uses;
5355 	}
5356 
5357 	Num_reinforcements++;
5358 }
5359 
parse_reinforcements(mission * pm)5360 void parse_reinforcements(mission *pm)
5361 {
5362 	Num_reinforcements = 0;
5363 	required_string("#Reinforcements");
5364 
5365 	while (required_string_either("#Background bitmaps", "$Name:"))
5366 		parse_reinforcement(pm);
5367 }
5368 
parse_one_background(background_t * background)5369 void parse_one_background(background_t *background)
5370 {
5371 	// clear here too because this function can be called from more than one place
5372 	background->suns.clear();
5373 	background->bitmaps.clear();
5374 
5375 	// parse suns
5376 	while (optional_string("$Sun:"))
5377 	{
5378 		starfield_list_entry sle;
5379 
5380 		// filename
5381 		stuff_string(sle.filename, F_NAME, MAX_FILENAME_LEN);
5382 
5383 		// angles
5384 		required_string("+Angles:");
5385 		stuff_float(&sle.ang.p);
5386 		stuff_float(&sle.ang.b);
5387 		stuff_float(&sle.ang.h);
5388 
5389 		// scale
5390 		required_string("+Scale:");
5391 		stuff_float(&sle.scale_x);
5392 		sle.scale_y = sle.scale_x;
5393 		sle.div_x = 1;
5394 		sle.div_y = 1;
5395 
5396 		// add it
5397 		background->suns.push_back(sle);
5398 	}
5399 
5400 	// parse starfields
5401 	while (optional_string("$Starbitmap:"))
5402 	{
5403 		starfield_list_entry sle;
5404 
5405 		// filename
5406 		stuff_string(sle.filename, F_NAME, MAX_FILENAME_LEN);
5407 
5408 		// angles
5409 		required_string("+Angles:");
5410 		stuff_float(&sle.ang.p);
5411 		stuff_float(&sle.ang.b);
5412 		stuff_float(&sle.ang.h);
5413 
5414 		// scale
5415 		if (optional_string("+Scale:"))
5416 		{
5417 			stuff_float(&sle.scale_x);
5418 			sle.scale_y = sle.scale_x;
5419 			sle.div_x = 1;
5420 			sle.div_y = 1;
5421 		}
5422 		else
5423 		{
5424 			required_string("+ScaleX:");
5425 			stuff_float(&sle.scale_x);
5426 
5427 			required_string("+ScaleY:");
5428 			stuff_float(&sle.scale_y);
5429 
5430 			required_string("+DivX:");
5431 			stuff_int(&sle.div_x);
5432 
5433 			required_string("+DivY:");
5434 			stuff_int(&sle.div_y);
5435 		}
5436 
5437 		// add it
5438 		background->bitmaps.push_back(sle);
5439 	}
5440 }
5441 
parse_bitmaps(mission * pm)5442 void parse_bitmaps(mission *pm)
5443 {
5444 	char str[MAX_FILENAME_LEN];
5445 	int z;
5446 
5447 	required_string("#Background bitmaps");
5448 
5449 	required_string("$Num stars:");
5450 	stuff_int(&Num_stars);
5451 	if (Num_stars >= MAX_STARS)
5452 		Num_stars = MAX_STARS;
5453 
5454 	required_string("$Ambient light level:");
5455 	stuff_int(&pm->ambient_light_level);
5456 
5457 	if (pm->ambient_light_level == 0)
5458 	{
5459 		pm->ambient_light_level = DEFAULT_AMBIENT_LIGHT_LEVEL;
5460 	}
5461 
5462 	// This should call light_set_ambient() to
5463 	// set the ambient light
5464 
5465 	Nebula_index = -1;
5466 	Mission_palette = 1;
5467 
5468 	// neb2 info
5469 	strcpy_s(Neb2_texture_name, "");
5470 	Neb2_poof_flags = ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5));
5471 	bool nebula = false;
5472 	if (optional_string("+Neb2:")) {
5473 		nebula = true;
5474 		stuff_string(Neb2_texture_name, F_NAME, MAX_FILENAME_LEN);
5475 	} else if (optional_string("+Neb2Color:")) {
5476 		nebula = true;
5477 		int neb_colors[3];
5478 		stuff_int_list(neb_colors, 3, RAW_INTEGER_TYPE);
5479 		Neb2_fog_color[0] = (ubyte)neb_colors[0];
5480 		Neb2_fog_color[1] = (ubyte)neb_colors[1];
5481 		Neb2_fog_color[2] = (ubyte)neb_colors[2];
5482 	}
5483 	if (nebula) {
5484 		required_string("+Neb2Flags:");
5485 		stuff_int(&Neb2_poof_flags);
5486 
5487 		// initialize neb effect. its gross to do this here, but Fred is dumb so I have no choice ... :(
5488 		if(Fred_running && (pm->flags[Mission::Mission_Flags::Fullneb])){
5489 			neb2_post_level_init();
5490 		}
5491 	}
5492 
5493 	if(pm->flags[Mission::Mission_Flags::Fullneb]){
5494 		// no regular nebula stuff
5495 		nebula_close();
5496 	} else {
5497 		if (optional_string("+Nebula:")) {
5498 			stuff_string(str, F_NAME, MAX_FILENAME_LEN);
5499 
5500 			// parse the proper nebula type (full or not)
5501 			for (z=0; z<NUM_NEBULAS; z++){
5502 				if(pm->flags[Mission::Mission_Flags::Fullneb]){
5503 					if (!stricmp(str, Neb2_filenames[z])) {
5504 						Nebula_index = z;
5505 						break;
5506 					}
5507 				} else {
5508 					if (!stricmp(str, Nebula_filenames[z])) {
5509 						Nebula_index = z;
5510 						break;
5511 					}
5512 				}
5513 			}
5514 
5515 			if (z == NUM_NEBULAS)
5516 				WarningEx(LOCATION, "Mission %s\nUnknown nebula %s!", pm->name, str);
5517 
5518 			if (optional_string("+Color:")) {
5519 				stuff_string(str, F_NAME, MAX_FILENAME_LEN);
5520 				for (z=0; z<NUM_NEBULA_COLORS; z++){
5521 					if (!stricmp(str, Nebula_colors[z])) {
5522 						Mission_palette = z;
5523 						break;
5524 					}
5525 				}
5526 			}
5527 
5528 			if (z == NUM_NEBULA_COLORS)
5529 				WarningEx(LOCATION, "Mission %s\nUnknown nebula color %s!", pm->name, str);
5530 
5531 			if (optional_string("+Pitch:")){
5532 				stuff_int(&Nebula_pitch);
5533 			} else {
5534 				Nebula_pitch = 0;
5535 			}
5536 
5537 			if (optional_string("+Bank:")){
5538 				stuff_int(&Nebula_bank);
5539 			} else {
5540 				Nebula_bank = 0;
5541 			}
5542 
5543 			if (optional_string("+Heading:")){
5544 				stuff_int(&Nebula_heading);
5545 			} else {
5546 				Nebula_heading = 0;
5547 			}
5548 		}
5549 
5550 		if (Nebula_index >= 0){
5551 			nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
5552 		} else {
5553 			nebula_close();
5554 		}
5555 	}
5556 
5557 	// Goober5000
5558 	while (optional_string("$Bitmap List:") || check_for_string("$Sun:") || check_for_string("$Starbitmap:"))
5559 	{
5560 		Backgrounds.emplace_back();
5561 		parse_one_background(&Backgrounds.back());
5562 	}
5563 
5564 	// Goober5000
5565 	stars_load_first_valid_background();
5566 
5567 	if (optional_string("$Environment Map:")) {
5568 		stuff_string(pm->envmap_name, F_NAME, MAX_FILENAME_LEN);
5569 	}
5570 
5571 	// bypass spurious stuff from e.g. FS1 missions
5572 	skip_to_start_of_string("#");
5573 }
5574 
parse_asteroid_fields(mission * pm)5575 void parse_asteroid_fields(mission *pm)
5576 {
5577 	int i, count, subtype;
5578 
5579 	Assert(pm != NULL);
5580 	for (i=0; i<MAX_ASTEROID_FIELDS; i++)
5581 		Asteroid_field.num_initial_asteroids = 0;
5582 
5583 	i = 0;
5584 	count = 0;
5585 
5586 	if (!optional_string("#Asteroid Fields"))
5587 		return;
5588 
5589 	while (required_string_either("#", "$density:")) {
5590 		float speed, density;
5591 		int type;
5592 
5593 		Assert(i < 1);
5594 		required_string("$Density:");
5595 		stuff_float(&density);
5596 
5597 		Asteroid_field.num_initial_asteroids = (int) density;
5598 
5599 		Asteroid_field.field_type = FT_ACTIVE;
5600 		if (optional_string("+Field Type:")) {
5601 			stuff_int( &type );
5602 			Asteroid_field.field_type = (field_type_t)type;
5603 		}
5604 
5605 		Asteroid_field.debris_genre = DG_ASTEROID;
5606 		if (optional_string("+Debris Genre:")) {
5607 			stuff_int( &type );
5608 			Asteroid_field.debris_genre = (debris_genre_t)type;
5609 		}
5610 
5611 		Asteroid_field.field_debris_type[0] = -1;
5612 		Asteroid_field.field_debris_type[1] = -1;
5613 		Asteroid_field.field_debris_type[2] = -1;
5614 		if (Asteroid_field.debris_genre == DG_SHIP) {
5615 			if (optional_string("+Field Debris Type:")) {
5616 				stuff_int(&Asteroid_field.field_debris_type[0]);
5617 			}
5618 			if (optional_string("+Field Debris Type:")) {
5619 				stuff_int(&Asteroid_field.field_debris_type[1]);
5620 			}
5621 			if (optional_string("+Field Debris Type:")) {
5622 				stuff_int(&Asteroid_field.field_debris_type[2]);
5623 			}
5624 		} else {
5625 			// debris asteroid
5626 			if (optional_string("+Field Debris Type:")) {
5627 				stuff_int(&subtype);
5628 				Asteroid_field.field_debris_type[subtype] = 1;
5629 				count++;
5630 			}
5631 			if (optional_string("+Field Debris Type:")) {
5632 				stuff_int(&subtype);
5633 				Asteroid_field.field_debris_type[subtype] = 1;
5634 				count++;
5635 			}
5636 			if (optional_string("+Field Debris Type:")) {
5637 				stuff_int(&subtype);
5638 				Asteroid_field.field_debris_type[subtype] = 1;
5639 				count++;
5640 			}
5641 		}
5642 
5643 		bool invalid_asteroids = false;
5644 		for (int& ast_type : Asteroid_field.field_debris_type) {
5645 			if (ast_type >= (int)Asteroid_info.size()) {
5646 				invalid_asteroids = true;
5647 				ast_type = -1;
5648 			}
5649 		}
5650 
5651 		if (invalid_asteroids)
5652 			Warning(LOCATION, "The Asteroid field contains invalid entries!");
5653 
5654 		// backward compatibility
5655 		if ( (Asteroid_field.debris_genre == DG_ASTEROID) && (count == 0) ) {
5656 			Asteroid_field.field_debris_type[0] = 0;
5657 		}
5658 
5659 		required_string("$Average Speed:");
5660 		stuff_float(&speed);
5661 
5662 		vm_vec_rand_vec_quick(&Asteroid_field.vel);
5663 		vm_vec_scale(&Asteroid_field.vel, speed);
5664 
5665 		Asteroid_field.speed = speed;
5666 
5667 		required_string("$Minimum:");
5668 		stuff_vec3d(&Asteroid_field.min_bound);
5669 
5670 		required_string("$Maximum:");
5671 		stuff_vec3d(&Asteroid_field.max_bound);
5672 
5673 		vec3d a_rad;
5674 		vm_vec_sub(&a_rad, &Asteroid_field.max_bound, &Asteroid_field.min_bound);
5675 		vm_vec_scale(&a_rad, 0.5f);
5676 		float b_rad = vm_vec_mag(&a_rad);
5677 
5678 		Asteroid_field.bound_rad = MAX(3000.0f, b_rad);
5679 
5680 		if (optional_string("+Inner Bound:")) {
5681 			Asteroid_field.has_inner_bound = 1;
5682 
5683 			required_string("$Minimum:");
5684 			stuff_vec3d(&Asteroid_field.inner_min_bound);
5685 
5686 			required_string("$Maximum:");
5687 			stuff_vec3d(&Asteroid_field.inner_max_bound);
5688 		} else {
5689 			Asteroid_field.has_inner_bound = 0;
5690 		}
5691 		i++;
5692 	}
5693 }
5694 
parse_variables()5695 void parse_variables()
5696 {
5697 	int i, j, num_variables = 0;
5698 
5699 	if (! optional_string("#Sexp_variables") ) {
5700 		return;
5701 	} else {
5702 		num_variables = stuff_sexp_variable_list();
5703 	}
5704 
5705 	// yeesh - none of this should be done in FRED :)
5706 	// It shouldn't be done for missions in the tecroom either. They should default to whatever FRED set them to
5707 	if ( Fred_running || !(Game_mode & GM_CAMPAIGN_MODE) ) {
5708 		return;
5709 	}
5710 
5711 	// Goober5000 - now set the default value, if it's a variable saved on mission progress
5712 	// loop through the current mission's variables
5713 	for (j = 0; j < num_variables; j++) {
5714 		// check against existing variables
5715 		for (auto& current_pv : Campaign.persistent_variables) {
5716 			// if the active mission has a variable with the same name as a variable saved to the campaign file override its initial value with the previous mission's value
5717 			if ( !stricmp(Sexp_variables[j].variable_name, current_pv.variable_name) ) {
5718 				// if this is an eternal that shares the same name as a non-eternal warn but do nothing
5719 				if (Sexp_variables[j].type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE) {
5720 					error_display(0, "Variable %s is marked eternal but has the same name as another persistent variable. One of these should be renamed to avoid confusion", Sexp_variables[j].text);
5721 				}
5722 				else if (Sexp_variables[j].type  & SEXP_VARIABLE_IS_PERSISTENT) {
5723 					Sexp_variables[j].type = current_pv.type;
5724 					strcpy_s(Sexp_variables[j].text, current_pv.text);
5725 					break;
5726 				} else {
5727 					error_display(0, "Variable %s has the same name as another persistent variable. One of these should be renamed to avoid confusion", Sexp_variables[j].text);
5728 				}
5729 			}
5730 		}
5731 	}
5732 
5733 	// next, see if any eternal variables are set loop through the current mission's variables
5734 	for (j = 0; j < num_variables; j++) {
5735 		// check against existing variables
5736 		for (i = 0; i < (int)Player->variables.size(); i++) {
5737 			// if the active mission has a variable with the same name as a variable saved to the player file override its initial value with the previous mission's value
5738 			if ( !stricmp(Sexp_variables[j].variable_name, Player->variables[i].variable_name) ) {
5739 				if (Sexp_variables[j].type & SEXP_VARIABLE_IS_PERSISTENT) {
5740 					// if the variable in the player file is marked as eternal but the version in the mission file is not, we assume that the player file one is rogue
5741 					// and use the one in the mission file instead.
5742 					if ((Player->variables[i].type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE) && !(Sexp_variables[j].type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE)) {
5743 						break;
5744 					}
5745 					// replace the default values with the ones saved to the player file
5746 					Sexp_variables[j].type = Player->variables[i].type;
5747 					strcpy_s(Sexp_variables[j].text, Player->variables[i].text);
5748 
5749 					/*
5750 					// check that the eternal flag has been set. Players using a player file from before the eternal flag was added may have old player-persistent variables
5751 					// these should be converted to non-eternals
5752 					if (!(Player->variables[i].type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE)) {
5753 						Sexp_variables[j].type &= ~SEXP_VARIABLE_SAVE_TO_PLAYER_FILE;
5754 					}
5755 					*/
5756 
5757 					break;
5758 				} else {
5759 					error_display(0, "Variable %s has the same name as an eternal variable. One of these should be renamed to avoid confusion", Sexp_variables[j].variable_name);
5760 				}
5761 			}
5762 		}
5763 	}
5764 }
5765 
parse_sexp_containers()5766 void parse_sexp_containers()
5767 {
5768 	if (!optional_string("#Sexp_containers")) {
5769 		return;
5770 	}
5771 
5772 	if (optional_string("$Lists")) {
5773 		stuff_sexp_list_containers();
5774 		required_string("$End Lists");
5775 	}
5776 
5777 	if (optional_string("$Maps")) {
5778 		stuff_sexp_map_containers();
5779 		required_string("$End Maps");
5780 	}
5781 }
5782 
parse_mission(mission * pm,int flags)5783 bool parse_mission(mission *pm, int flags)
5784 {
5785 	int saved_warning_count = Global_warning_count;
5786 	int saved_error_count = Global_error_count;
5787 
5788 	int i;
5789 	Warned_about_team_out_of_range = false;
5790 
5791 	waypoint_parse_init();
5792 
5793 	Player_starts = Num_cargo = Num_goals = Num_wings = 0;
5794 	Player_start_shipnum = -1;
5795 	*Player_start_shipname = 0;		// make the string 0 length for checking later
5796 	clear_texture_replacements();
5797 
5798 	// initialize the initially_docked array.
5799 	for ( i = 0; i < MAX_SHIPS; i++ ) {
5800 		Initially_docked[i].docker[0] = '\0';
5801 		Initially_docked[i].dockee[0] = '\0';
5802 		Initially_docked[i].docker_point[0] = '\0';
5803 		Initially_docked[i].dockee_point[0] = '\0';
5804 	}
5805 	Total_initially_docked = 0;
5806 
5807 	list_init(&Ship_arrival_list);	// init list for arrival ships
5808 
5809 	parse_init();
5810 
5811 	Subsys_index = 0;
5812 	Subsys_status_size = 0;
5813 
5814 	if (Subsys_status != NULL) {
5815 		vm_free( Subsys_status );
5816 		Subsys_status = NULL;
5817 	}
5818 
5819 	parse_mission_info(pm);
5820 
5821 	Current_file_checksum = netmisc_calc_checksum(pm,MISSION_CHECKSUM_SIZE);
5822 
5823 	if (flags & MPF_ONLY_MISSION_INFO)
5824 		return true;
5825 
5826 	parse_plot_info(pm);
5827 	parse_variables();
5828 	parse_sexp_containers();
5829 	parse_briefing_info(pm);	// TODO: obsolete code, keeping so we don't obsolete existing mission files
5830 	parse_cutscenes(pm);
5831 	parse_fiction(pm);
5832 	parse_cmd_briefs(pm);
5833 	parse_briefing(pm, flags);
5834 	parse_debriefing_new(pm);
5835 	parse_player_info(pm);
5836 	parse_objects(pm, flags);
5837 	parse_wings(pm);
5838 	parse_events(pm);
5839 	parse_goals(pm);
5840 	parse_waypoints_and_jumpnodes(pm);
5841 	parse_messages(pm, flags);
5842 	parse_reinforcements(pm);
5843 	parse_bitmaps(pm);
5844 	parse_asteroid_fields(pm);
5845 	parse_music(pm, flags);
5846 
5847 	// if we couldn't load some mod data
5848 	if ((Num_unknown_ship_classes > 0) || ( Num_unknown_loadout_classes > 0 )) {
5849 		// if running on standalone server, just print to the log
5850 		if (Game_mode & GM_STANDALONE_SERVER) {
5851 			mprintf(("Warning!  Could not load %d ship classes!\n", Num_unknown_ship_classes));
5852 			return false;
5853 		}
5854 		// don't do this in FRED; we will display a separate popup
5855 		else if (!Fred_running) {
5856 			// build up the prompt...
5857 			SCP_string text;
5858 
5859 			if (Num_unknown_ship_classes > 0) {
5860 				sprintf(text, "Warning!\n\nFreeSpace was unable to find %d ship class%s while loading this mission.  This can happen if you try to play a %s that is incompatible with the current mod.\n\n", Num_unknown_ship_classes, (Num_unknown_ship_classes > 1) ? "es" : "", (Game_mode & GM_CAMPAIGN_MODE) ? "campaign" : "mission");
5861 			}
5862 			else {
5863 				sprintf(text, "Warning!\n\nFreeSpace was unable to find %d weapon class%s while loading this mission.  This can happen if you try to play a %s that is incompatible with the current mod.\n\n", Num_unknown_loadout_classes, (Num_unknown_loadout_classes > 1) ? "es" : "", (Game_mode & GM_CAMPAIGN_MODE) ? "campaign" : "mission");
5864 			}
5865 
5866 			if (Game_mode & GM_CAMPAIGN_MODE) {
5867 				text += "(The current campaign is \"";
5868 				text += Campaign.name;
5869 			} else {
5870 				text += "(The current mission is \"";
5871 				text += pm->name;
5872 			}
5873 
5874 			text += "\", and the current mod is \"";
5875 
5876 			if (Cmdline_mod == NULL || *Cmdline_mod == 0) {
5877 				text += "<retail default> ";
5878 			} else {
5879 				for (char *mod_token = Cmdline_mod; *mod_token != '\0'; mod_token += strlen(mod_token) + 1) {
5880 					text += mod_token;
5881 					text += " ";
5882 				}
5883 			}
5884 
5885 			text.erase(text.length() - 1);	// trim last space
5886 			text += "\".)\n\n  You can continue to load the mission, but it is quite likely that you will encounter a large number of mysterious errors.  It is recommended that you either select a ";
5887 			text += (Game_mode & GM_CAMPAIGN_MODE) ? "campaign" : "mission";
5888 			text += " that is compatible with your current mod, or else exit FreeSpace and select a different mod.\n\n";
5889 
5890 			text += "Do you want to continue to load the mission?";
5891 
5892 			// now display the popup
5893 			int popup_rval = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, text.c_str());
5894 			if (popup_rval == 0) {
5895 				return false;
5896 			}
5897 		}
5898 	}
5899 
5900 	if (!post_process_mission()) {
5901 		return false;
5902 	}
5903 
5904 	if ((saved_warning_count - Global_warning_count) > 10 || (saved_error_count - Global_error_count) > 0) {
5905 		char text[512];
5906 		sprintf(text, "Warning!\n\nThe current mission has generated %d warnings and/or errors during load.  These are usually caused by corrupted ship models or syntax errors in the mission file.  While FreeSpace Open will attempt to compensate for these issues, it cannot guarantee a trouble-free gameplay experience.  Source Code Project staff cannot provide assistance or support for these problems, as they are caused by the mission's data files, not FreeSpace Open's source code.", (saved_warning_count - Global_warning_count) + (saved_error_count - Global_error_count));
5907 		popup(PF_TITLE_BIG | PF_TITLE_RED | PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, text);
5908 	}
5909 
5910 	log_printf(LOGFILE_EVENT_LOG, "Mission %s loaded.\n", pm->name);
5911 
5912 	// success
5913 	return true;
5914 }
5915 
post_process_mission()5916 bool post_process_mission()
5917 {
5918 	int			i;
5919 	int			indices[MAX_SHIPS], objnum;
5920 	ship_weapon	*swp;
5921 	ship_obj *so;
5922 
5923 	// Goober5000 - must be done before all other post processing
5924 	post_process_ships_wings();
5925 
5926 	// the player_start_shipname had better exist at this point!
5927 	Player_start_shipnum = ship_name_lookup( Player_start_shipname );
5928 	Assert( Player_start_shipnum != -1 );
5929 	Player_start_pobject = mission_parse_get_parse_object( Player_start_shipname );
5930 	Assert( Player_start_pobject != NULL );
5931 
5932 	// Assign objnum, shipnum, etc. to the player structure
5933 	objnum = Ships[Player_start_shipnum].objnum;
5934 	Player_obj = &Objects[objnum];
5935 	if (!Fred_running){
5936 		Player->objnum = objnum;
5937 	}
5938 
5939 	Player_obj->flags.set(Object::Object_Flags::Player_ship);			// make this object a player controlled ship.
5940 	Player_ship = &Ships[Player_start_shipnum];
5941 	Player_ai = &Ai_info[Player_ship->ai_index];
5942 
5943 	Player_ai->targeted_subsys = NULL;
5944 	Player_ai->targeted_subsys_parent = -1;
5945 
5946 	// determine if player start has initial velocity and set forward cruise percent to relect this
5947 	// this should check prev_ramp_vel because that is in local coordinates --wookieejedi
5948 	if ( Player_obj->phys_info.prev_ramp_vel.xyz.z > 0.0f )
5949 		Player->ci.forward_cruise_percent = Player_obj->phys_info.prev_ramp_vel.xyz.z / Player_ship->current_max_speed * 100.0f;
5950 
5951 	// set up wing indexes
5952 	for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
5953 		Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1);
5954 	}
5955 
5956 	for (i = 0; i < MAX_SQUADRON_WINGS; i++ ) {
5957 		Squadron_wings[i] = wing_name_lookup(Squadron_wing_names[i], 1);
5958 	}
5959 
5960 	for (i = 0; i < MAX_TVT_WINGS; i++ ) {
5961 		TVT_wings[i] = wing_name_lookup(TVT_wing_names[i], 1);
5962 	}
5963 
5964 	// when TVT, hack starting wings to be team wings
5965 	if(MULTI_TEAM){
5966 		Assert(MAX_TVT_WINGS <= MAX_STARTING_WINGS);
5967 		for (i=0; i<MAX_STARTING_WINGS; i++)
5968 		{
5969 			if (i<MAX_TVT_WINGS)
5970 				Starting_wings[i] = TVT_wings[i];
5971 			else
5972 				Starting_wings[i] = -1;
5973 		}
5974 	}
5975 
5976 	init_ai_system();
5977 
5978 	waypoint_create_game_objects();
5979 
5980 	// Goober5000 - this needs to be called only once after parsing of objects and wings is complete
5981 	// (for individual invalidation, see mission_parse_mark_non_arrival)
5982 	mission_parse_mark_non_arrivals();
5983 
5984 	// deal with setting up arrival location for all ships.  Must do this now after all ships are created
5985 	mission_parse_set_arrival_locations();
5986 
5987 	// clear out information about arriving support ships
5988 	Arriving_support_ship = NULL;
5989 	Num_arriving_repair_targets = 0;
5990 
5991 	// convert all ship name indices to ship indices now that mission has been loaded
5992 	if (Fred_running) {
5993 		for (i=0; i<Num_parse_names; i++) {
5994 			indices[i] = ship_name_lookup(Parse_names[i], 1);
5995 			if (indices[i] < 0)
5996 				Warning(LOCATION, "Ship name \"%s\" referenced, but this ship doesn't exist", Parse_names[i]);
5997 		}
5998 
5999 		for (i=0; i<MAX_SHIPS; i++) {
6000 			if ((Ships[i].objnum >= 0) && (Ships[i].arrival_anchor >= 0) && (Ships[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHOR_FLAG))
6001 				Ships[i].arrival_anchor = indices[Ships[i].arrival_anchor];
6002 
6003 			if ( (Ships[i].objnum >= 0) && (Ships[i].departure_anchor >= 0) )
6004 				Ships[i].departure_anchor = indices[Ships[i].departure_anchor];
6005 		}
6006 
6007 		for (i=0; i<MAX_WINGS; i++) {
6008 			if (Wings[i].wave_count  && (Wings[i].arrival_anchor >= 0) && (Wings[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHOR_FLAG))
6009 				Wings[i].arrival_anchor = indices[Wings[i].arrival_anchor];
6010 
6011 			if (Wings[i].wave_count  && (Wings[i].departure_anchor >= 0) )
6012 				Wings[i].departure_anchor = indices[Wings[i].departure_anchor];
6013 		}
6014 
6015 	}
6016 
6017 	// before doing anything else, we must validate all of the sexpressions that were loaded into the mission.
6018 	// Loop through the Sexp_nodes array and send the top level functions to the check_sexp_syntax parser
6019 
6020 	for (i = 0; i < Num_sexp_nodes; i++) {
6021 		if (is_sexp_top_level(i) && (!Fred_running || (i != Sexp_clipboard))) {
6022 			int result, bad_node, op;
6023 
6024 			op = get_operator_index(i);
6025 			Assert(op != -1);  // need to make sure it is an operator before we treat it like one..
6026 			result = check_sexp_syntax( i, query_operator_return_type(op), 1, &bad_node);
6027 
6028 			// entering this if statement will result in program termination!!!!!
6029 			// print out an error based on the return value from check_sexp_syntax()
6030 			// G5K: now entering this statement simply aborts the mission load
6031 			if ( result ) {
6032 				SCP_string sexp_str;
6033 				SCP_string error_msg;
6034 
6035 				convert_sexp_to_string(sexp_str, i, SEXP_ERROR_CHECK_MODE);
6036 				truncate_message_lines(sexp_str, 30);
6037 				sprintf(error_msg, "%s.\n\nIn sexpression: %s\n(Error appears to be: %s)", sexp_error_message(result), sexp_str.c_str(), Sexp_nodes[bad_node].text);
6038 				Warning(LOCATION, "%s", error_msg.c_str());
6039 
6040 				// syntax errors are recoverable in Fred but not FS
6041 				if (!Fred_running) {
6042 					return false;
6043 				}
6044 			}
6045 		}
6046 	}
6047 
6048 	// multiplayer missions are handled just before mission start
6049 	if (!(Game_mode & GM_MULTIPLAYER) ){
6050 		ai_post_process_mission();
6051 	}
6052 
6053 	// first we need to clear out the counts for this mission
6054 	ship_clear_ship_type_counts();
6055 
6056 	// we must also count all of the ships of particular types.  We count all of the ships that do not have
6057 	// their SF_IGNORE_COUNT flag set.  We don't count ships in wings when the equivalent wing flag is set.
6058 	// in counting ships in wings, we increment the count by the wing's wave count to account for everyone.
6059 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6060 		int num, shipnum;
6061 
6062 		shipnum = Objects[so->objnum].instance;
6063 		// pass over non-ship objects and player ship objects
6064 		if ( Ships[shipnum].objnum == -1 || (Objects[Ships[shipnum].objnum].flags[Object::Object_Flags::Player_ship]) )
6065 			continue;
6066 		if ( Ships[shipnum].flags[Ship::Ship_Flags::Ignore_count] )
6067 			continue;
6068 		if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags[Ship::Wing_Flags::Ignore_count]) )
6069 			continue;
6070 
6071 		num = 1;
6072 
6073 		ship_add_ship_type_count( Ships[shipnum].ship_info_index, num );
6074 	}
6075 
6076 	// now go through the list of ships yet to arrive
6077 	for (p_object *p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
6078 	{
6079 		int num;
6080 
6081 		// go through similar motions as above
6082 		if ( p_objp->flags[Mission::Parse_Object_Flags::SF_Ignore_count] )
6083 			continue;
6084 		if ( (p_objp->wingnum != -1) && (Wings[p_objp->wingnum].flags[Ship::Wing_Flags::Ignore_count]) )
6085 			continue;
6086 
6087 		if ( p_objp->wingnum == -1 )
6088 			num = 1;
6089 		else
6090 			num = Wings[p_objp->wingnum].num_waves - 1;			// subtract one since we already counted the first wave
6091 
6092 		ship_add_ship_type_count( p_objp->ship_class, num );
6093 	}
6094 
6095 	// set player weapons that are selected by default
6096 	// AL 09/17/97: I added this code to select the first primary/secondary weapons,
6097 	// since I noticed the player ship sometimes doesn't get default weapons selected
6098 
6099 	// DB: modified 4/23/98 to take multiplayer into account. Under certain circumstances, multiplayer netplayer ships
6100 	//     had their current_primary_bank and current_secondary_bank set to -1 (from ship_set()) and left there since
6101 	//     Player_ship is not the only one we need to need about.
6102 	for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6103 		ship *shipp = &Ships[Objects[so->objnum].instance];
6104 
6105 		// don't process non player wing ships
6106 		if ( !(shipp->flags[Ship::Ship_Flags::From_player_wing] ) )
6107 			continue;
6108 
6109 		swp = &shipp->weapons;
6110 
6111 		if ( swp->num_primary_banks > 0 ) {
6112 			swp->current_primary_bank = 0;			// currently selected primary bank
6113 		}
6114 
6115 		if ( swp->num_secondary_banks > 0 ) {
6116 			swp->current_secondary_bank = 0;			// currently selected secondary bank
6117 		}
6118 	}
6119 
6120 	ets_init_ship(Player_obj);	// init ETS data for the player
6121 
6122 	// put the timestamp stuff here for now
6123 	Mission_arrival_timestamp = timestamp( ARRIVAL_TIMESTAMP );
6124 	Mission_departure_timestamp = timestamp( DEPARTURE_TIMESTAMP );
6125 	Mission_end_time = -1;
6126 
6127 	Allow_arrival_music_timestamp=timestamp(0);
6128 	Allow_arrival_message_timestamp=timestamp(0);
6129 	Arrival_message_delay_timestamp = timestamp(-1);
6130 
6131 	int idx;
6132 	for(idx=0; idx<2; idx++){
6133 		Allow_arrival_music_timestamp_m[idx]=timestamp(0);
6134 		Allow_arrival_message_timestamp_m[idx]=timestamp(0);
6135 		Arrival_message_delay_timestamp_m[idx] = timestamp(-1);
6136 	}
6137 
6138 	if(Game_mode & GM_MULTIPLAYER){
6139 		multi_respawn_build_points();
6140 	}
6141 
6142 	// maybe reset hotkey defaults when loading new mission
6143 	if ( Last_file_checksum != Current_file_checksum ){
6144 		mission_hotkey_reset_saved();
6145 	}
6146 	Last_file_checksum = Current_file_checksum;
6147 
6148 	// success
6149 	return true;
6150 }
6151 
get_mission_info(const char * filename,mission * mission_p,bool basic)6152 int get_mission_info(const char *filename, mission *mission_p, bool basic)
6153 {
6154 	char real_fname[MAX_FILENAME_LEN];
6155 
6156 	strncpy(real_fname, filename, MAX_FILENAME_LEN-1);
6157 	real_fname[sizeof(real_fname)-1] = '\0';
6158 
6159 	char *p = strrchr(real_fname, '.');
6160 	if (p) *p = 0; // remove any extension
6161 	strcat_s(real_fname, FS_MISSION_FILE_EXT);  // append mission extension
6162 
6163 	int filelength;
6164 
6165 	// if mission_p is NULL, make it point to The_mission
6166 	if ( mission_p == NULL )
6167 		mission_p = &The_mission;
6168 
6169 	CFILE *ftemp = cfopen(real_fname, "rt");
6170 	if (!ftemp) {
6171 		return -1;
6172 	}
6173 
6174 	// 7/9/98 -- MWA -- check for 0 length file.
6175 	filelength = cfilelength(ftemp);
6176 	cfclose(ftemp);
6177 	if (filelength == 0) {
6178 		return -1;
6179 	}
6180 
6181 	try
6182 	{
6183 		read_file_text(real_fname, CF_TYPE_MISSIONS);
6184 		mission_p->Reset();
6185 		parse_init(basic);
6186 		parse_mission_info(mission_p, basic);
6187 	}
6188 	catch (const parse::ParseException& e)
6189 	{
6190 		mprintf(("MISSIONS: Unable to parse '%s'!  Error message = %s.\n", real_fname, e.what()));
6191 		return -1;
6192 	}
6193 
6194 	return 0;
6195 }
6196 
6197 /**
6198  * Initialize the mission parse process.
6199  */
parse_init(bool basic)6200 void parse_init(bool basic)
6201 {
6202 	reset_parse();
6203 
6204 	// if we just want basic info then we don't need some of this initialization
6205 	if (!basic)
6206 	{
6207 		for (int i = 0; i < MAX_CARGO; i++)
6208 			Cargo_names[i] = Cargo_names_buf[i]; // make a pointer array for compatibility
6209 
6210 		ai_clear_goal_target_names();
6211 
6212 		// if we are just wanting basic info then we shouldn't need sexps
6213 		// (prevents memory fragmentation with the now dynamic Sexp_nodes[])
6214 		init_sexp();
6215 	}
6216 }
6217 
6218 // main parse routine for parsing a mission.  The default parameter flags tells us which information
6219 // to get when parsing the mission.  0 means get everything (default).  Other flags just gets us basic
6220 // info such as game type, number of players etc.
parse_main(const char * mission_name,int flags)6221 bool parse_main(const char *mission_name, int flags)
6222 {
6223 	int i;
6224 	bool rval;
6225 
6226 	// reset parse error stuff
6227 	Num_unknown_ship_classes = 0;
6228 	Num_unknown_weapon_classes = 0;
6229 	Num_unknown_loadout_classes = 0;
6230 
6231 	// fill in Ship_class_names array with the names from the ship_info struct;
6232 	Num_parse_names = 0;
6233 	Num_path_restrictions = 0;
6234 	Assert(Ship_info.size() <= MAX_SHIP_CLASSES);
6235 
6236 	i = 0;
6237 	for (auto it = Ship_info.begin(); it != Ship_info.end(); i++, ++it)
6238 		Ship_class_names[i] = it->name;
6239 
6240 	do {
6241 		// don't do this for imports
6242 		if (!(flags & MPF_IMPORT_FSM)) {
6243 			CFILE *ftemp = cfopen(mission_name, "rt", CFILE_NORMAL, CF_TYPE_MISSIONS);
6244 
6245 			// fail situation.
6246 			if (!ftemp) {
6247 				if (!Fred_running)
6248 					Error( LOCATION, "Couldn't open mission '%s'\n", mission_name );
6249 
6250 				Current_file_length = -1;
6251 				Current_file_checksum = 0;
6252 
6253 				rval = false;
6254 				break;
6255 			}
6256 
6257 			Current_file_length = cfilelength(ftemp);
6258 			cfclose(ftemp);
6259 		}
6260 
6261 		try
6262 		{
6263 			// import?
6264 			if (flags & MPF_IMPORT_FSM) {
6265 				read_file_text(mission_name, CF_TYPE_ANY);
6266 				convertFSMtoFS2();
6267 			}
6268 			else {
6269 				read_file_text(mission_name, CF_TYPE_MISSIONS);
6270 			}
6271 
6272 			The_mission.Reset();
6273 			rval = parse_mission(&The_mission, flags);
6274 			display_parse_diagnostics();
6275 		}
6276 		catch (const parse::ParseException& e)
6277 		{
6278 			mprintf(("MISSIONS: Unable to parse '%s'!  Error message = %s.\n", mission_name, e.what()));
6279 			rval = false;
6280 			break;
6281 		}
6282 	} while (0);
6283 
6284 	if (!Fred_running)
6285 		strcpy_s(Mission_filename, mission_name);
6286 
6287 	return rval;
6288 }
6289 
6290 // Note, this is currently only called from game_shutdown()
mission_parse_close()6291 void mission_parse_close()
6292 {
6293 	// free subsystems
6294 	if (Subsys_status != NULL)
6295 	{
6296 		vm_free(Subsys_status);
6297 		Subsys_status = NULL;
6298 	}
6299 
6300 	// the destructor for each p_object will clear its dock list
6301 	Parse_objects.clear();
6302 }
6303 
6304 /**
6305  * Sets the arrival location of the ships in wingp.
6306  *
6307  * @param wingp Pointer to wing
6308  * @param num_to_set The threshold value for wings may have us create more ships in the wing when there are still some remaining
6309  */
mission_set_wing_arrival_location(wing * wingp,int num_to_set)6310 void mission_set_wing_arrival_location( wing *wingp, int num_to_set )
6311 {
6312 	int index;
6313 	int anchor_objnum = -1;
6314 
6315 	// get the starting index into the ship_index array of the first ship whose location we need set.
6316 
6317 	index = wingp->current_count - num_to_set;
6318 	if ( (wingp->arrival_location == ARRIVE_FROM_DOCK_BAY) || (wingp->arrival_location == ARRIVE_AT_LOCATION) ) {
6319 		while ( index < wingp->current_count ) {
6320 			object *objp;
6321 
6322 			objp = &Objects[Ships[wingp->ship_index[index]].objnum];
6323 			anchor_objnum = mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(objp), wingp->arrival_path_mask, NULL, NULL);
6324 
6325 			index++;
6326 		}
6327 	} else {
6328 		object *leader_objp;
6329 		vec3d pos;
6330 		matrix orient;
6331 		int wing_index;
6332 
6333 		// wing is not arriving from a docking bay -- possibly move them based on arriving near
6334 		// or in front of some other ship.
6335 		index = wingp->current_count - num_to_set;
6336 		leader_objp = &Objects[Ships[wingp->ship_index[index]].objnum];
6337 		anchor_objnum = mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(leader_objp), wingp->arrival_path_mask, &pos, &orient);
6338 		if (anchor_objnum != -1) {
6339 			// modify the remaining ships created
6340 			index++;
6341 			wing_index = 1;
6342 			while ( index < wingp->current_count ) {
6343 				object *objp;
6344 
6345 				objp = &Objects[Ships[wingp->ship_index[index]].objnum];
6346 
6347 				// change the position of the next ships in the wing.  Use the cool function in AiCode.cpp which
6348 				// Mike K wrote to give new positions to the wing members.
6349 				get_absolute_wing_pos( &objp->pos, leader_objp, WING_INDEX(wingp), wing_index++, false);
6350 				memcpy( &objp->orient, &orient, sizeof(matrix) );
6351 
6352 				index++;
6353 			}
6354 		}
6355 	}
6356 
6357 	if (Game_mode & GM_IN_MISSION) {
6358 		for ( index = wingp->current_count - num_to_set; index < wingp->current_count; index ++ ) {
6359 			object *objp = &Objects[Ships[wingp->ship_index[index]].objnum];
6360 			object *anchor_objp = (anchor_objnum >= 0) ? &Objects[anchor_objnum] : nullptr;
6361 
6362 			if (Script_system.IsActiveAction(CHA_ONSHIPARRIVE)) {
6363 				Script_system.SetHookObjects(2, "Ship", objp, "Parent", anchor_objp);
6364 				Script_system.RunCondition(CHA_ONSHIPARRIVE, objp);
6365 				Script_system.RemHookVars({"Ship", "Parent"});
6366 			}
6367 
6368 			if (wingp->arrival_location != ARRIVE_FROM_DOCK_BAY) {
6369 				shipfx_warpin_start(objp);
6370 			}
6371 		}
6372 	}
6373 }
6374 
6375 /**
6376  * Called after a mission is parsed to set the arrival locations of all ships in the
6377  * mission to the apprioriate spot.  Mainly needed because ships might be in dock bays to start
6378  * the mission, so their AI mode must be set appropriately.
6379  */
mission_parse_set_arrival_locations()6380 void mission_parse_set_arrival_locations()
6381 {
6382 	int i;
6383 	object *objp;
6384 
6385 	if ( Fred_running )
6386 		return;
6387 
6388 	obj_merge_created_list();
6389 	for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
6390 		ship *shipp;
6391 
6392 		if ( objp->type != OBJ_SHIP )
6393 			continue;
6394 
6395 		shipp = &Ships[objp->instance];
6396 		// if the ship is in a wing -- ignore the info and let the wing info handle it
6397 		if ( shipp->wingnum != -1 )
6398 			continue;
6399 
6400 		// call function to set arrival location for this ship.
6401 		mission_set_arrival_location( shipp->arrival_anchor, shipp->arrival_location, shipp->arrival_distance, OBJ_INDEX(objp), shipp->arrival_path_mask, NULL, NULL);
6402 	}
6403 
6404 	// do the wings
6405 	for ( i = 0; i < Num_wings; i++ ) {
6406 
6407 		// if wing has no ships, then don't process it.
6408 		if ( Wings[i].current_count == 0 )
6409 			continue;
6410 
6411 		mission_set_wing_arrival_location( &Wings[i], Wings[i].current_count );
6412 	}
6413 }
6414 
6415 // Goober5000
sexp_is_locked_false(int node)6416 bool sexp_is_locked_false(int node)
6417 {
6418 	// dunno why these are different, but they are
6419 	if (Fred_running)
6420 		return (node == Locked_sexp_false);
6421 	else
6422 		return (Sexp_nodes[node].value == SEXP_KNOWN_FALSE);
6423 }
6424 
set_cue_to_false(int * cue)6425 void set_cue_to_false(int *cue)
6426 {
6427 	free_sexp2(*cue);
6428 	*cue = Locked_sexp_false;
6429 }
6430 
6431 // function to set the arrival cue of a ship to false
reset_arrival_to_false(p_object * pobjp,bool reset_wing)6432 void reset_arrival_to_false(p_object *pobjp, bool reset_wing)
6433 {
6434 	// falsify the ship cue
6435 	mprintf(("Setting arrival cue of ship %s to false for initial docking purposes.\n", pobjp->name));
6436 	set_cue_to_false(&pobjp->arrival_cue);
6437 
6438 	// falsify the wing cue and all ships in that wing
6439 	if (reset_wing && pobjp->wingnum >= 0)
6440 	{
6441 		wing *wingp = &Wings[pobjp->wingnum];
6442 		mprintf(("Setting arrival cue of wing %s to false for initial docking purposes.\n", wingp->name));
6443 		set_cue_to_false(&wingp->arrival_cue);
6444 
6445 		for (SCP_vector<p_object>::iterator ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
6446 		{
6447 			if ((&(*ii) != pobjp) && (ii->wingnum == pobjp->wingnum))
6448 				reset_arrival_to_false(&(*ii), false);
6449 		}
6450 	}
6451 }
6452 
6453 /**
6454  * In both retail and SCP, the dock "leader" is defined as the only guy in his
6455  * group with a non-false arrival cue.
6456  *
6457  * If we are forcing a leader, that means *none* of the ships in this dock group
6458  * have a non-false arrival cue, so we just need to pick one
6459  */
parse_object_mark_dock_leader_sub(p_object * pobjp,p_dock_function_info * infop,bool force_a_leader)6460 void parse_object_mark_dock_leader_sub(p_object *pobjp, p_dock_function_info *infop, bool force_a_leader)
6461 {
6462 	int cue_to_check;
6463 
6464 	// if this guy is part of a wing, he uses his wing's arrival cue
6465 	if (pobjp->wingnum >= 0)
6466 	{
6467 		cue_to_check = Wings[pobjp->wingnum].arrival_cue;
6468 	}
6469 	// check the object's arrival cue
6470 	else
6471 	{
6472 		cue_to_check = pobjp->arrival_cue;
6473 	}
6474 
6475 	// is he a leader (using the definition above)?
6476 	if (!sexp_is_locked_false(cue_to_check) || force_a_leader)
6477 	{
6478 		p_object *existing_leader;
6479 
6480 		// increment number of leaders found
6481 		infop->maintained_variables.int_value++;
6482 
6483 		// see if we already found a leader
6484 		existing_leader = infop->maintained_variables.objp_value;
6485 		if (existing_leader != NULL)
6486 		{
6487 			// keep existing leader if he has a higher priority than us
6488 			if (ship_class_compare(pobjp->ship_class, existing_leader->ship_class) >= 0)
6489 			{
6490 				// set my arrival cue to false
6491 				reset_arrival_to_false(pobjp, true);
6492 				return;
6493 			}
6494 
6495 			// otherwise, unmark the existing leader and set his arrival cue to false
6496             existing_leader->flags.remove(Mission::Parse_Object_Flags::SF_Dock_leader);
6497 			reset_arrival_to_false(existing_leader, true);
6498 		}
6499 
6500 		// mark and save me as the leader
6501         pobjp->flags.set(Mission::Parse_Object_Flags::SF_Dock_leader);
6502 		infop->maintained_variables.objp_value = pobjp;
6503 	}
6504 }
6505 
parse_object_mark_dock_leader_helper(p_object * pobjp,p_dock_function_info * infop)6506 void parse_object_mark_dock_leader_helper(p_object *pobjp, p_dock_function_info *infop)
6507 {
6508 	parse_object_mark_dock_leader_sub(pobjp, infop, false);
6509 }
6510 
parse_object_choose_arbitrary_dock_leader_helper(p_object * pobjp,p_dock_function_info * infop)6511 void parse_object_choose_arbitrary_dock_leader_helper(p_object *pobjp, p_dock_function_info *infop)
6512 {
6513 	parse_object_mark_dock_leader_sub(pobjp, infop, true);
6514 }
6515 
6516 // Goober5000
parse_object_set_handled_flag_helper(p_object * pobjp,p_dock_function_info *)6517 void parse_object_set_handled_flag_helper(p_object *pobjp, p_dock_function_info * /*infop*/)
6518 {
6519     pobjp->flags.set(Mission::Parse_Object_Flags::Already_handled);
6520 }
6521 
6522 // Goober5000
parse_object_clear_handled_flag_helper(p_object * pobjp,p_dock_function_info *)6523 void parse_object_clear_handled_flag_helper(p_object *pobjp, p_dock_function_info * /*infop*/)
6524 {
6525     pobjp->flags.remove(Mission::Parse_Object_Flags::Already_handled);
6526 }
6527 
6528 // Goober5000
parse_object_clear_all_handled_flags()6529 void parse_object_clear_all_handled_flags()
6530 {
6531 	// clear flag for all ships
6532 	for (SCP_vector<p_object>::iterator ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
6533 	{
6534 		p_object *pobjp = &(*ii);
6535 		p_dock_function_info dfi;
6536 
6537 		// since we're going through all objects, this object may not be docked
6538 		if (!object_is_docked(pobjp))
6539 			continue;
6540 
6541 		// has this object (by extension, this group of docked objects) been cleared already?
6542 		if (!(pobjp->flags[Mission::Parse_Object_Flags::Already_handled]))
6543 			continue;
6544 
6545 		// clear the handled flag for this group
6546 		dock_evaluate_all_docked_objects(pobjp, &dfi, parse_object_clear_handled_flag_helper);
6547 	}
6548 }
6549 
6550 // Goober5000
6551 // This function iterates through the Initially_docked array and builds the dock trees
6552 // for each parse object.  This can only be done after all objects and wings have been parsed.
mission_parse_set_up_initial_docks()6553 void mission_parse_set_up_initial_docks()
6554 {
6555 	int i;
6556 
6557 	// build trees
6558 	for (i = 0; i < Total_initially_docked; i++)
6559 	{
6560 		char *docker_point, *dockee_point;
6561 		p_object *docker, *dockee;
6562 
6563 		// resolve the docker and dockee
6564 		docker = mission_parse_get_parse_object(Initially_docked[i].docker);
6565 		if (docker == NULL)
6566 		{
6567 			Warning(LOCATION, "Could not resolve initially docked object '%s'!", Initially_docked[i].docker);
6568 			continue;
6569 		}
6570 		dockee = mission_parse_get_parse_object(Initially_docked[i].dockee);
6571 		if (dockee == NULL)
6572 		{
6573 			Warning(LOCATION, "Could not resolve docking target '%s' of initially docked object '%s'!", Initially_docked[i].dockee, Initially_docked[i].docker);
6574 			continue;
6575 		}
6576 
6577 		// skip docking if they're already docked
6578 		// (in FSO, we list all initially docked pairs for all ships,
6579 		// so we end up with twice as many docking entries as we need)
6580 		if (dock_check_find_direct_docked_object(docker, dockee))
6581 			continue;
6582 
6583 		// resolve the dockpoints
6584 		docker_point = Initially_docked[i].docker_point;
6585 		dockee_point = Initially_docked[i].dockee_point;
6586 
6587 		// docker point in use?
6588 		if (dock_find_object_at_dockpoint(docker, docker_point) != NULL)
6589 		{
6590 			Warning(LOCATION, "Trying to initially dock '%s' and '%s', but the former's dockpoint is already in use!", Initially_docked[i].docker, Initially_docked[i].dockee);
6591 			continue;
6592 		}
6593 
6594 		// dockee point in use?
6595 		if (dock_find_object_at_dockpoint(dockee, dockee_point) != NULL)
6596 		{
6597 			Warning(LOCATION, "Trying to initially dock '%s' and '%s', but the latter's dockpoint is already in use!", Initially_docked[i].docker, Initially_docked[i].dockee);
6598 			continue;
6599 		}
6600 
6601 		dock_dock_objects(docker, docker_point, dockee, dockee_point);
6602 	}
6603 
6604 	// now resolve the leader of each tree
6605 	for (SCP_vector<p_object>::iterator ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
6606 	{
6607 		p_object *pobjp = &(*ii);
6608 		p_dock_function_info dfi;
6609 
6610 		// since we're going through all objects, this object may not be docked
6611 		if (!object_is_docked(pobjp))
6612 			continue;
6613 
6614 		// has this object (by extension, this group of docked objects) been handled already?
6615 		if (pobjp->flags[Mission::Parse_Object_Flags::Already_handled])
6616 			continue;
6617 
6618 		// find the dock leader(s)
6619 		dock_evaluate_all_docked_objects(pobjp, &dfi, parse_object_mark_dock_leader_helper);
6620 
6621 		// display an error if necessary
6622 		if (dfi.maintained_variables.int_value == 0)
6623 		{
6624 			Warning(LOCATION, "In the docking group containing %s, every ship has an arrival cue set to false.  The group will not appear in-mission!\n", pobjp->name);
6625 
6626 			// for FRED, we must arbitrarily choose a dock leader, otherwise the entire docked group will not be loaded
6627 			if (Fred_running)
6628 				dock_evaluate_all_docked_objects(pobjp, &dfi, parse_object_choose_arbitrary_dock_leader_helper);
6629 		}
6630 		else if (dfi.maintained_variables.int_value > 1)
6631 		{
6632 			Warning(LOCATION, "In the docking group containing %s, there is more than one ship with a non-false arrival cue!  There can only be one such ship.  Setting all arrival cues except %s to false...\n", dfi.maintained_variables.objp_value->name, dfi.maintained_variables.objp_value->name);
6633 		}
6634 
6635 		// clear dfi stuff
6636 		dfi.maintained_variables.int_value = 0;
6637 		dfi.maintained_variables.objp_value = NULL;
6638 
6639 		// set the handled flag for this group
6640 		dock_evaluate_all_docked_objects(pobjp, &dfi, parse_object_set_handled_flag_helper);
6641 	}
6642 
6643 	// now clear the handled flags
6644 	parse_object_clear_all_handled_flags();
6645 }
6646 
6647 /**
6648  * Returns true or false if the given mission support multiplayers
6649  */
mission_parse_is_multi(const char * filename,char * mission_name)6650 int mission_parse_is_multi(const char *filename, char *mission_name)
6651 {
6652 	int game_type;
6653 	int filelength;
6654 	CFILE *ftemp;
6655 
6656 	// new way of getting information.  Open the file, and just get the name and the game_type flags.
6657 	// return the flags if a multiplayer mission
6658 
6659 	ftemp = cfopen(filename, "rt");
6660 	if (!ftemp)
6661 		return 0;
6662 
6663 	// 7/9/98 -- MWA -- check for 0 length file.
6664 	filelength = cfilelength(ftemp);
6665 	cfclose(ftemp);
6666 	if ( filelength == 0 )
6667 		return 0;
6668 
6669 	game_type = 0;
6670 	do {
6671 		try
6672 		{
6673 			read_file_text(filename, CF_TYPE_MISSIONS);
6674 			reset_parse();
6675 
6676 			if (skip_to_string("$Name:") != 1) {
6677 				nprintf(("Network", "Unable to process %s because we couldn't find $Name:", filename));
6678 				break;
6679 			}
6680 			stuff_string(mission_name, F_NAME, NAME_LENGTH);
6681 
6682 			if (skip_to_string("+Game Type Flags:") != 1) {
6683 				nprintf(("Network", "Unable to process %s because we couldn't find +Game Type Flags:\n", filename));
6684 				break;
6685 			}
6686 			stuff_int(&game_type);
6687 		}
6688 		catch (const parse::ParseException& e)
6689 		{
6690 			mprintf(("MISSIONS: Unable to parse '%s'!  Error message = %s.\n", filename, e.what()));
6691 			break;
6692 		}
6693 	} while (0);
6694 
6695 	return (game_type & MISSION_TYPE_MULTI) ? game_type : 0;
6696 }
6697 
6698 /**
6699  * Called to retrieve useful information about a mission.
6700  *
6701  * We will get the name, description, and number of players for a mission. Probably used for multiplayer only?
6702  * The calling function can use the information in The_mission to get the name/description of the mission
6703  * if needed.
6704  */
mission_parse_get_multi_mission_info(const char * filename)6705 int mission_parse_get_multi_mission_info( const char *filename )
6706 {
6707 	if ( get_mission_info(filename, &The_mission) )
6708 		return -1;
6709 
6710 	Assert( The_mission.game_type & MISSION_TYPE_MULTI );		// assume multiplayer only for now?
6711 
6712 	// return the number of parse_players.  later, we might want to include (optionally?) the number
6713 	// of other ships in the main players wing (usually wing 'alpha') for inclusion of number of
6714 	// players allowed.
6715 
6716 	return The_mission.num_players;
6717 }
6718 
6719 /**
6720  * @brief				Returns the parse object on the ship arrival list associated with the given name.
6721  * @param[in] name		The name of the object
6722  * @returns				The parse object, or NULL if no object with the given name is on the arrival list
6723  * @remarks				This function is used to determine whether a ship has arrived. Ships on the arrival list
6724  *						are considered to not be in the game; In order to make respawns work in multiplayer,
6725  *						player ships (those marked with the P_OF_PLAYER_START flag) are never removed from it.
6726  */
mission_parse_get_arrival_ship(const char * name)6727 p_object *mission_parse_get_arrival_ship(const char *name)
6728 {
6729 	p_object *p_objp;
6730 
6731 	if (name == nullptr)
6732 		return nullptr;
6733 
6734 	for (p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
6735 	{
6736 		if (!stricmp(p_objp->name, name))
6737 		{
6738 			return p_objp;	// still on the arrival list
6739 		}
6740 	}
6741 
6742 	return nullptr;
6743 }
6744 
6745 /**
6746  * @brief					Returns the parse object on the ship arrival list associated with the given net signature.
6747  * @param[in] net_signature	The net signature of the object
6748  * @returns					The parse object, or NULL if no object with the given signature is on the arrival list
6749  * @remarks					This function is used to determine whether a ship has arrived. Ships on the arrival list
6750  *							are considered to not be in the game; In order to make respawns work in multiplayer,
6751  *							player ships (those marked with the P_OF_PLAYER_START flag) are never removed from it.
6752  */
mission_parse_get_arrival_ship(ushort net_signature)6753 p_object *mission_parse_get_arrival_ship(ushort net_signature)
6754 {
6755 	p_object *p_objp;
6756 
6757 	for (p_objp = GET_FIRST(&Ship_arrival_list); p_objp !=END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
6758 	{
6759 		if (p_objp->net_signature == net_signature)
6760 		{
6761 			return p_objp;	// still on the arrival list
6762 		}
6763 	}
6764 
6765 	return NULL;
6766 }
6767 
6768 /**
6769  * Because player ships remain on the arrival list (see parse_wing_create_ships), testing for yet-to-arrive by merely
6770  * checking the list will produce false positives for player ships.  So this function also checks whether the object was created.
6771  */
mission_check_ship_yet_to_arrive(const char * name)6772 bool mission_check_ship_yet_to_arrive(const char *name)
6773 {
6774 	p_object *p_objp = mission_parse_get_arrival_ship(name);
6775 	if (p_objp == nullptr)
6776 		return false;
6777 
6778 	if (p_objp->created_object != nullptr)
6779 		return false;
6780 
6781 	return true;
6782 }
6783 
6784 /**
6785  * Sets the arrival location of a parse object according to the arrival location of the object.
6786  * @return objnum of anchor ship if there is one, -1 otherwise.
6787  */
mission_set_arrival_location(int anchor,int location,int dist,int objnum,int path_mask,vec3d * new_pos,matrix * new_orient)6788 int mission_set_arrival_location(int anchor, int location, int dist, int objnum, int path_mask, vec3d *new_pos, matrix *new_orient)
6789 {
6790 	int shipnum, anchor_objnum;
6791 	vec3d anchor_pos, rand_vec, new_fvec;
6792 	matrix orient;
6793 
6794 	if ( location == ARRIVE_AT_LOCATION )
6795 		return -1;
6796 
6797 	Assert(anchor >= 0);
6798 
6799 	// this ship might possibly arrive at another location.  The location is based on the
6800 	// proximity of some ship (and some other special tokens)
6801 	if (anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG)
6802 	{
6803 		bool get_players = (anchor & SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG) > 0;
6804 
6805 		// filter out iff
6806 		int iff_index = anchor;
6807 		iff_index &= ~SPECIAL_ARRIVAL_ANCHOR_FLAG;
6808 		iff_index &= ~SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG;
6809 
6810 		// get ship
6811 		shipnum = ship_get_random_team_ship(iff_get_mask(iff_index), get_players ? SHIP_GET_ONLY_PLAYERS : SHIP_GET_ANY_SHIP);
6812 	}
6813 	// if we didn't find the arrival anchor in the list of special nodes, then do a
6814 	// ship name lookup on the anchor
6815 	else
6816 	{
6817 		shipnum = ship_name_lookup(Parse_names[anchor]);
6818 	}
6819 
6820 	// if we didn't get an object from one of the above functions, then make the object
6821 	// arrive at its placed location
6822 	if (shipnum < 0)
6823 	{
6824 		Assert ( location != ARRIVE_FROM_DOCK_BAY );		// bogus data somewhere!!!  get mwa
6825 		nprintf (("allender", "couldn't find ship for arrival anchor -- using location ship created at"));
6826 		return -1;
6827 	}
6828 
6829 	// take the shipnum and get the position.  once we have positions, we can determine where
6830 	// to make this ship appear
6831 	Assert ( shipnum != -1 );
6832 	anchor_objnum = Ships[shipnum].objnum;
6833 	anchor_pos = Objects[anchor_objnum].pos;
6834 
6835 	// if arriving from docking bay, then set ai mode and call function as per AL's instructions.
6836 	if ( location == ARRIVE_FROM_DOCK_BAY ) {
6837 		vec3d pos, fvec;
6838 
6839 		// if we get an error, just let the ship arrive(?)
6840 		if ( ai_acquire_emerge_path(&Objects[objnum], anchor_objnum, path_mask, &pos, &fvec) == -1 ) {
6841 			// get MWA or AL -- not sure what to do here when we cannot acquire a path
6842 			mprintf(("Unable to acquire arrival path on anchor ship %s\n", Ships[shipnum].ship_name));
6843 			return -1;
6844 		}
6845 		Objects[objnum].pos = pos;
6846 		vm_vector_2_matrix(&Objects[objnum].orient, &fvec, NULL, NULL);
6847 	} else {
6848 
6849 		// AL: ensure dist > 0 (otherwise get errors in vecmat)
6850 		// TODO: maybe set distance to 2x ship radius of ship appearing in front of?
6851 		if ( dist <= 0 )
6852 		{
6853 			// Goober5000 - default to 100
6854 			Warning(LOCATION, "Distance of %d is invalid in mission_set_arrival_location.  Defaulting to 100.\n", dist);
6855 			dist = 100;
6856 		}
6857 
6858 		// get a vector which is the ships arrival position based on the type of arrival
6859 		// this ship should have.  Arriving near a ship we use a random normalized vector
6860 		// scaled by the distance given by the designer.  Arriving in front of a ship means
6861 		// entering the battle in the view cone.
6862 		if ( location == ARRIVE_NEAR_SHIP ) {
6863 			// get a random vector -- use static randvec if in multiplayer
6864 			if ( Game_mode & GM_NORMAL )
6865 				vm_vec_rand_vec_quick(&rand_vec);
6866 			else
6867 				static_randvec( Objects[objnum].net_signature, &rand_vec );
6868 		} else if ( location == ARRIVE_IN_FRONT_OF_SHIP ) {
6869 			vec3d t1, t2, t3;
6870 			int r1, r2;
6871 			float x;
6872 
6873 			// cool function by MK to give a reasonable random vector "in front" of a ship
6874 			// rvec and uvec are the right and up vectors.
6875 			// If these are not available, this would be an expensive method.
6876 			x = cosf(fl_radians(45.0f));
6877 			if ( Game_mode & GM_NORMAL ) {
6878 				r1 = Random::flip_coin() ? -1 : 1;
6879 				r2 = Random::flip_coin() ? -1 : 1;
6880 			} else {
6881 				// in multiplayer, use the static rand functions so that all clients can get the
6882 				// same information.
6883 				r1 = static_rand(Objects[objnum].net_signature) < STATIC_RAND_MAX / 2 ? -1 : 1;
6884 				r2 = static_rand(Objects[objnum].net_signature+1) < STATIC_RAND_MAX / 2 ? -1 : 1;
6885 			}
6886 
6887 			vm_vec_copy_scale(&t1, &(Objects[anchor_objnum].orient.vec.fvec), x);
6888 			vm_vec_copy_scale(&t2, &(Objects[anchor_objnum].orient.vec.rvec), (1.0f - x) * r1);
6889 			vm_vec_copy_scale(&t3, &(Objects[anchor_objnum].orient.vec.uvec), (1.0f - x) * r2);
6890 
6891 			vm_vec_add(&rand_vec, &t1, &t2);
6892 			vm_vec_add2(&rand_vec, &t3);
6893 			vm_vec_normalize(&rand_vec);
6894 		}
6895 
6896 		// add in the radius of the two ships involved.  This will make the ship arrive further than
6897 		// specified, but will appear more accurate since we are pushing the edge of the model to the
6898 		// specified distance.  large objects appears to be a lot closer without the following line because
6899 		// the object centers were at the correct distance, but the model itself was much closer to the
6900 		// target ship.
6901 		dist += (int)Objects[objnum].radius + (int)Objects[anchor_objnum].radius;
6902 		vm_vec_scale_add(&Objects[objnum].pos, &anchor_pos, &rand_vec, (float)dist);
6903 
6904 		// I think that we will always want to orient the ship that is arriving to face towards
6905 		// the ship it is arriving near/in front of.  The effect will be cool!
6906 		//
6907 		// calculate the new fvec of the ship arriving and use only that to get the matrix.  isn't a big
6908 		// deal not getting bank.
6909 		vm_vec_sub(&new_fvec, &anchor_pos, &Objects[objnum].pos );
6910 		vm_vector_2_matrix( &orient, &new_fvec, NULL, NULL );
6911 		Objects[objnum].orient = orient;
6912 	}
6913 
6914 	// set the new_pos parameter since it might be used outside the function (i.e. when dealing with wings).
6915 	if ( new_pos )
6916 		memcpy(new_pos, &Objects[objnum].pos, sizeof(vec3d) );
6917 
6918 	if ( new_orient )
6919 		memcpy( new_orient, &Objects[objnum].orient, sizeof(matrix) );
6920 
6921 	return anchor_objnum;
6922 }
6923 
6924 /**
6925  * Mark a reinforcement as available
6926  */
mission_parse_mark_reinforcement_available(char * name)6927 void mission_parse_mark_reinforcement_available(char *name)
6928 {
6929 	int i;
6930 	reinforcements *rp;
6931 
6932 	for (i = 0; i < Num_reinforcements; i++) {
6933 		rp = &Reinforcements[i];
6934 		if ( !stricmp(rp->name, name) ) {
6935 			if ( !(rp->flags & RF_IS_AVAILABLE) ) {
6936 				rp->flags |= RF_IS_AVAILABLE;
6937 
6938 				// tell all of the clients.
6939 				if ( MULTIPLAYER_MASTER ) {
6940 					send_reinforcement_avail( i );
6941 				}
6942 			}
6943 			return;
6944 		}
6945 	}
6946 
6947 	Assert ( i < Num_reinforcements );
6948 }
6949 
6950 /**
6951  * Takes a parse object and checks the arrival cue, delay and destruction of object it is arriving from then creates the object if necessary.
6952  *
6953  * @return -1 if not created.
6954  * @return objnum of created ship otherwise
6955  */
mission_did_ship_arrive(p_object * objp)6956 int mission_did_ship_arrive(p_object *objp)
6957 {
6958 	int should_arrive;
6959 
6960 	// find out in the arrival cue became true
6961 	should_arrive = eval_sexp(objp->arrival_cue);
6962 
6963 	// we must first check to see if this ship is a reinforcement or not.  If so, then don't
6964 	// process
6965 	if ( objp->flags[Mission::Parse_Object_Flags::SF_Reinforcement] ) {
6966 
6967 		// if this ship did arrive, mark the reinforcement as available, and tell clients if in multiplayer
6968 		// mode
6969 		if ( should_arrive ) {
6970 			mission_parse_mark_reinforcement_available(objp->name);
6971 		}
6972 		return -1;
6973 	}
6974 
6975 	if ( should_arrive ) { 		// has the arrival criteria been met?
6976 		int object_num;
6977 
6978 		// check to see if the delay field <= 0.  if so, then create a timestamp and then maybe
6979 		// create the object
6980 		if ( objp->arrival_delay <= 0 ) {
6981 			objp->arrival_delay = timestamp( -objp->arrival_delay * 1000 );
6982 
6983 			// make sure we have a valid timestamp
6984 			Assert( objp->arrival_delay > 0 );
6985 		}
6986 
6987 		// if the timestamp hasn't elapsed, move onto the next ship.
6988 		if ( !timestamp_elapsed(objp->arrival_delay) )
6989 			return -1;
6990 
6991 		// check to see if this ship is to arrive via a docking bay.  If so, and the ship to arrive from
6992 		// doesn't exist, don't create.
6993 		if ( objp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
6994 			int shipnum;
6995 			char *name;
6996 
6997 			Assert( objp->arrival_anchor >= 0 );
6998 			name = Parse_names[objp->arrival_anchor];
6999 
7000 			// see if ship is in mission.
7001 			shipnum = ship_name_lookup( name );
7002 			if ( shipnum == -1 ) {
7003 				// see if ship is yet to arrive.  If so, then return -1 so we can evaluate again later.
7004 				if (mission_check_ship_yet_to_arrive(name))
7005 					return -1;
7006 
7007 				mission_parse_mark_non_arrival(objp);	// Goober5000
7008 				WarningEx(LOCATION, "Warning: Ship %s cannot arrive from docking bay of destroyed or departed %s.\n", objp->name, name);
7009 				return -1;
7010 			}
7011 
7012 			// Goober5000: aha - also don't create if fighterbay is destroyed
7013 			if (ship_fighterbays_all_destroyed(&Ships[shipnum])) {
7014 				WarningEx(LOCATION, "Warning: Ship %s cannot arrive from destroyed docking bay of %s.\n", objp->name, name);
7015 				return -1;
7016 			}
7017 		}
7018 
7019 		if ( objp->flags[Mission::Parse_Object_Flags::SF_Cannot_arrive] ) {
7020 			WarningEx(LOCATION, "Warning: Ship %s cannot arrive. Ship not created.\n", objp->name);
7021 			return -1;
7022 		}
7023 
7024 		// create the ship
7025 		object_num = parse_create_object(objp);
7026 
7027 		// since this ship is not in a wing, create a SHIP_ARRIVE entry
7028 		//mission_log_add_entry( LOG_SHIP_ARRIVE, objp->name, NULL );
7029 		Assert(object_num >= 0 && object_num < MAX_OBJECTS);
7030 
7031 		// Play the music track for an arrival
7032 		if ( !(Ships[Objects[object_num].instance].flags[Ship::Ship_Flags::No_arrival_music]) )
7033 			if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
7034 				Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
7035 				event_music_arrival(Ships[Objects[object_num].instance].team);
7036 			}
7037 		return object_num;
7038 	}
7039 
7040 	return -1;
7041 }
7042 
7043 // Goober5000
mission_maybe_make_ship_arrive(p_object * p_objp)7044 void mission_maybe_make_ship_arrive(p_object *p_objp)
7045 {
7046 	// try to create ship
7047 	int objnum = mission_did_ship_arrive(p_objp);
7048 	if (objnum < 0)
7049 		return;
7050 
7051 	// remove from arrival list
7052 	if (p_objp == Arriving_support_ship)
7053 		mission_parse_support_arrived(objnum);
7054 	else
7055 		list_remove(&Ship_arrival_list, p_objp);
7056 }
7057 
7058 // Goober5000
mission_parse_mark_non_arrival(p_object * p_objp)7059 void mission_parse_mark_non_arrival(p_object *p_objp)
7060 {
7061 	// mark the flag
7062     p_objp->flags.set(Mission::Parse_Object_Flags::SF_Cannot_arrive);
7063 }
7064 
7065 // Goober5000
mission_parse_mark_non_arrival(wing * wingp)7066 void mission_parse_mark_non_arrival(wing *wingp)
7067 {
7068 	int wingnum = WING_INDEX(wingp);
7069 
7070 	// look through all ships yet to arrive...
7071 	for (p_object *p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
7072 	{
7073 		// ...and mark the ones in this wing
7074         if (p_objp->wingnum == wingnum)
7075             p_objp->flags.set(Mission::Parse_Object_Flags::SF_Cannot_arrive);
7076 	}
7077 }
7078 
7079 /**
7080  * Set a flag on all parse objects on ship arrival list which cannot arrive in the mission
7081  */
mission_parse_mark_non_arrivals()7082 void mission_parse_mark_non_arrivals()
7083 {
7084 	for (p_object *p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp))
7085 	{
7086 		if (p_objp->wingnum != -1)
7087 		{
7088 			if (!object_is_docked(p_objp) && (Sexp_nodes[Wings[p_objp->wingnum].arrival_cue].value == SEXP_KNOWN_FALSE))
7089 				p_objp->flags.set(Mission::Parse_Object_Flags::SF_Cannot_arrive);
7090 		}
7091 		else
7092 		{
7093 			if (Sexp_nodes[p_objp->arrival_cue].value == SEXP_KNOWN_FALSE)
7094 				p_objp->flags.set(Mission::Parse_Object_Flags::SF_Cannot_arrive);
7095 		}
7096 	}
7097 }
7098 
7099 /**
7100  * Deal with support ship arrival. This function can get called from either single or multiplayer.  Needed to that clients
7101  * can know when to abort rearm.
7102  *
7103  * @param objnum is the object number of the arriving support ship
7104  */
mission_parse_support_arrived(int objnum)7105 void mission_parse_support_arrived( int objnum )
7106 {
7107 	int i;
7108 
7109 	// when the support ship arrives, the shipname it is supposed to repair is in the 'misc'
7110 	// field of the parse object.  If the ship still exists, call ai function which actually
7111 	// issues the goal for the repair
7112 	for ( i = 0; i < Num_arriving_repair_targets; i++ ) {
7113 		int shipnum;
7114 
7115 		shipnum = ship_name_lookup( Arriving_repair_targets[i] );
7116 
7117 		if ( shipnum != -1 ) {
7118 			object *requester_objp, *support_objp;
7119 
7120 			support_objp = &Objects[objnum];
7121 			requester_objp = &Objects[Ships[shipnum].objnum];
7122 			ai_add_rearm_goal( requester_objp, support_objp );
7123 		}
7124 	}
7125 
7126 	Ships[Objects[objnum].instance].flags.set(Ship::Ship_Flags::Warped_support);
7127 
7128 	Arriving_support_ship = NULL;
7129 	Num_arriving_repair_targets = 0;
7130 }
7131 
7132 // Goober5000
parse_object_on_arrival_list(p_object * pobjp)7133 int parse_object_on_arrival_list(p_object *pobjp)
7134 {
7135 	return (pobjp->next != NULL) && (pobjp->prev != NULL);
7136 }
7137 
7138 /**
7139  * Check the lists of arriving ships and wings, creating new ships/wings if the arrival criteria have been met
7140  */
mission_eval_arrivals()7141 void mission_eval_arrivals()
7142 {
7143 	int i;
7144 	int rship = -1;
7145 	wing *wingp;
7146 
7147 	// before checking arrivals, check to see if we should play a message concerning arrivals
7148 	// of other wings.  We use the timestamps to delay the arrival message slightly for
7149 	// better effect
7150 	if (timestamp_valid(Arrival_message_delay_timestamp) && timestamp_elapsed(Arrival_message_delay_timestamp) && !MULTI_TEAM)
7151 	{
7152 		int use_terran_cmd;
7153 
7154 		// use terran command 25% of time
7155 		use_terran_cmd = ((frand() - 0.75) > 0.0f)?1:0;
7156 
7157 		rship = ship_get_random_player_wing_ship( SHIP_GET_UNSILENCED );
7158 		if ((rship < 0) || use_terran_cmd)
7159 			message_send_builtin_to_player(MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1);
7160 		else if (rship >= 0)
7161 			message_send_builtin_to_player(MESSAGE_ARRIVE_ENEMY, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1);
7162 
7163 		Arrival_message_delay_timestamp = timestamp(-1);		// make the stamp invalid
7164 	}
7165 
7166 	// check the arrival list
7167 	// Goober5000 - we can't run through the list the usual way because we might
7168 	// remove a bunch of objects and completely screw up the list linkage
7169 	for (SCP_vector<p_object>::iterator ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
7170 	{
7171 		p_object *pobjp = &(*ii);
7172 
7173 		// make sure we're on the arrival list
7174 		if (!parse_object_on_arrival_list(pobjp))
7175 			continue;
7176 
7177 		// if this object has a wing, don't create it -- let code for wings determine if it should be created
7178 		if (pobjp->wingnum >= 0)
7179 			continue;
7180 
7181 		// make it arrive
7182 		mission_maybe_make_ship_arrive(pobjp);
7183 	}
7184 
7185 	// check the support ship arrival list
7186 	if (Arriving_support_ship)
7187 	{
7188 		// make it arrive (support ships are not put on the arrival list)
7189 		mission_maybe_make_ship_arrive(Arriving_support_ship);
7190 	}
7191 
7192 	// we must also check to see if there are waves of a wing that must
7193 	// reappear if all the ships of the current wing have been destroyed or
7194 	// have departed. If this is the case, then create the next wave.
7195 	for (i = 0; i < Num_wings; i++)
7196 	{
7197 		wingp = &Wings[i];
7198 
7199 		// should we process this wing anymore
7200 		if (wingp->flags[Ship::Wing_Flags::Gone])
7201 			continue;
7202 
7203 		// if we have a reinforcement wing, then don't try to create new ships automatically.
7204 		if (wingp->flags[Ship::Wing_Flags::Reinforcement])
7205 		{
7206 			// check to see in the wings arrival cue is true, and if so, then mark the reinforcement
7207 			// as available
7208 			if (eval_sexp(wingp->arrival_cue))
7209 				mission_parse_mark_reinforcement_available(wingp->name);
7210 
7211 			// reinforcement wings skip the rest of the loop
7212 			continue;
7213 		}
7214 
7215 		// don't do evaluations for departing wings
7216 		if (wingp->flags[Ship::Wing_Flags::Departing])
7217 			continue;
7218 
7219 		// must check to see if we are at the last wave.  Code above to determine when a wing is gone only
7220 		// gets run when a ship is destroyed (not every N seconds like it used to).  Do a quick check here.
7221 		if (wingp->current_wave == wingp->num_waves)
7222 			continue;
7223 
7224 		// If the current wave of this wing is 0, then we haven't created the ships in the wing yet.
7225 		// If the threshold of the wing has been reached, then we need to create more ships.
7226 		if ((wingp->current_wave == 0) || (wingp->current_count <= wingp->threshold))
7227 		{
7228 			// Call parse_wing_create_ships to try and create it.  That function will eval the arrival
7229 			// cue of the wing and create the ships if necessary.
7230 			int created = parse_wing_create_ships(wingp, wingp->wave_count);
7231 
7232 			// if we didn't create any ships, nothing more to do for this wing
7233 			if (created <= 0)
7234 				continue;
7235 
7236 			// If this wing was a reinforcement wing, then we need to reset the reinforcement flag for the wing
7237 			// so the user can call in another set if need be.
7238 			if (wingp->flags[Ship::Wing_Flags::Reset_reinforcement])
7239 			{
7240                 wingp->flags.remove(Ship::Wing_Flags::Reset_reinforcement);
7241                 wingp->flags.set(Ship::Wing_Flags::Reinforcement);
7242 			}
7243 
7244 			// probably send a message to the player when this wing arrives.
7245 			// if no message, nothing more to do for this wing
7246 			if (wingp->flags[Ship::Wing_Flags::No_arrival_message])
7247 				continue;
7248 
7249 			// multiplayer team vs. team
7250 			if(MULTI_TEAM)
7251 			{
7252 				// send a hostile wing arrived message
7253 				rship = wingp->ship_index[wingp->special_ship];
7254 
7255 				int multi_team_filter = Ships[rship].team;
7256 
7257 				// there are two timestamps at work here.  One to control how often the player receives
7258 				// messages about incoming hostile waves, and the other to control how long after
7259 				// the wing arrives does the player actually get the message.
7260 				if (timestamp_elapsed(Allow_arrival_message_timestamp_m[multi_team_filter]))
7261 				{
7262 					if (!timestamp_valid(Arrival_message_delay_timestamp_m[multi_team_filter]))
7263 					{
7264 						Arrival_message_delay_timestamp_m[multi_team_filter] = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX);
7265 					}
7266 					Allow_arrival_message_timestamp_m[multi_team_filter] = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION);
7267 
7268 					// send to the proper team
7269 					message_send_builtin_to_player(MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, multi_team_filter);
7270 				}
7271 			}
7272 			// does the player attack this ship?
7273 			else if (iff_x_attacks_y(Player_ship->team, Ships[wingp->ship_index[0]].team))
7274 			{
7275 				// there are two timestamps at work here.  One to control how often the player receives
7276 				// messages about incoming hostile waves, and the other to control how long after
7277 				// the wing arrives does the player actually get the message.
7278 				if (timestamp_elapsed(Allow_arrival_message_timestamp))
7279 				{
7280 					if (!timestamp_valid(Arrival_message_delay_timestamp))
7281 					{
7282 						Arrival_message_delay_timestamp = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX);
7283 					}
7284 					Allow_arrival_message_timestamp = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION);
7285 				}
7286 			}
7287 			// everything else
7288 			else
7289 			{
7290 				rship = ship_get_random_ship_in_wing(i, SHIP_GET_UNSILENCED);
7291 				if (rship >= 0)
7292 				{
7293 					int j;
7294 					SCP_string message_name;
7295 					sprintf(message_name, "%s Arrived", wingp->name);
7296 
7297 					// see if this wing has an arrival message associated with it
7298 					for (j = 0; j < MAX_BUILTIN_MESSAGE_TYPES; j++)
7299 					{
7300 						if (!stricmp(message_name.c_str(), Builtin_messages[j].name))
7301 						{
7302 							message_send_builtin_to_player(j, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1);
7303 							break;
7304 						}
7305 					}
7306 				}
7307 			}
7308 		}
7309 	}
7310 
7311 	Mission_arrival_timestamp = timestamp(ARRIVAL_TIMESTAMP);
7312 }
7313 
7314 
7315 /**
7316  * Called to make object objp depart.  Rewritten and expanded by Goober5000.
7317  */
mission_do_departure(object * objp,bool goal_is_to_warp)7318 int mission_do_departure(object *objp, bool goal_is_to_warp)
7319 {
7320 	Assert(objp->type == OBJ_SHIP);
7321 	int location, anchor, path_mask;
7322 	ship *shipp = &Ships[objp->instance];
7323 	ai_info *aip = &Ai_info[shipp->ai_index];
7324 
7325 	mprintf(("Entered mission_do_departure() for %s\n", shipp->ship_name));
7326 
7327 	// add scripting hook for 'On Depature Started' --wookieejedi
7328 	// hook is placed at the begining of this function to allow the scripter to
7329 	// actually have access to the ship's departure decisions before they are all executed
7330 	OnDepartureStartedHook->run(scripting::hook_param_list(scripting::hook_param("Ship", 'o', objp)));
7331 
7332 	// abort rearm, because if we entered this function we're either going to depart via hyperspace, depart via bay,
7333 	// or revert to our default behavior
7334 	ai_abort_rearm_request(objp);
7335 
7336 	// if our current goal is to warp, then we won't consider departing to a bay, because the goal explicitly says to warp out
7337 	// (this sort of goal can be assigned in FRED, either in the ship's initial orders or as the ai-warp-out goal)
7338 	if (goal_is_to_warp)
7339 	{
7340 		// aha, but not if we were ORDERED to depart, because the comms menu ALSO uses the goal code, and yet the comms menu means any departure method!
7341 		if ((shipp->flags[Ship::Ship_Flags::Departure_ordered]) || ((shipp->wingnum >= 0) && (Wings[shipp->wingnum].flags[Ship::Wing_Flags::Departure_ordered])))
7342 		{
7343 			mprintf(("Looks like we were ordered to depart; initiating the standard departure logic\n"));
7344 		}
7345 		// since our goal is to warp, then if we can warp, jump directly to the warping part
7346 		else if (ship_can_warp_full_check(shipp))
7347 		{
7348 			mprintf(("Our current goal is to warp!  Trying to warp...\n"));
7349 			goto try_to_warp;
7350 		}
7351 		// otherwise, since we can't warp, we'll do the standard bay departure check, etc.
7352 	}
7353 
7354 	// if this ship belongs to a wing, then use the wing departure information
7355 	if (shipp->wingnum >= 0)
7356 	{
7357 		wing *wingp = &Wings[shipp->wingnum];
7358 
7359 		// copy the wing's departure information to the ship
7360 		// (needed because the bay departure code will check the ship's information again later on)
7361 		shipp->departure_location = wingp->departure_location;
7362 		shipp->departure_anchor = wingp->departure_anchor;
7363 		shipp->departure_path_mask = wingp->departure_path_mask;
7364 	}
7365 
7366 	location = shipp->departure_location;
7367 	anchor = shipp->departure_anchor;
7368 	path_mask = shipp->departure_path_mask;
7369 
7370 	// if departing to a docking bay, try to find the anchor ship to depart to.  If not found, then
7371 	// just make it warp out like anything else.
7372 	if (location == DEPART_AT_DOCK_BAY)
7373 	{
7374 		int anchor_shipnum;
7375 		char *name;
7376 
7377 		Assert(anchor >= 0);
7378 		name = Parse_names[anchor];
7379 
7380 		// see if ship is yet to arrive.  If so, then warp.
7381 		if (mission_check_ship_yet_to_arrive(name))
7382 		{
7383 			mprintf(("Anchor ship %s hasn't arrived yet!  Trying to warp...\n", name));
7384 			goto try_to_warp;
7385 		}
7386 
7387 		// see if ship is in mission.  If not, then we can assume it was destroyed or departed since
7388 		// it is not on the arrival list (as shown by above if statement).
7389 		anchor_shipnum = ship_name_lookup(name);
7390 		if (anchor_shipnum < 0)
7391 		{
7392 			mprintf(("Anchor ship %s not found!  Trying to warp...\n", name));
7393 			goto try_to_warp;
7394 		}
7395 
7396 		// see if we can actually depart to the ship
7397 		if (!ship_useful_for_departure(anchor_shipnum, shipp->departure_path_mask))
7398 		{
7399 			mprintf(("Anchor ship %s not suitable for departure (dying, departing, bays destroyed, etc.).  Trying to warp...\n", name));
7400 			goto try_to_warp;
7401 		}
7402 
7403 		// find a path
7404 		if (ai_acquire_depart_path(objp, Ships[anchor_shipnum].objnum, path_mask) >= 0)
7405 		{
7406 			MONITOR_INC(NumShipDepartures,1);
7407 
7408 			mprintf(("Acquired departure path\n"));
7409 			return 1;
7410 		}
7411 	}
7412 
7413 try_to_warp:
7414 
7415 	// make sure we can actually warp
7416 	if (ship_can_warp_full_check(shipp))
7417 	{
7418 		mprintf(("Setting mode to warpout\n"));
7419 
7420 		ai_set_mode_warp_out(objp, aip);
7421 		MONITOR_INC(NumShipDepartures,1);
7422 
7423 		return 1;
7424 	}
7425 	// find something else to do
7426 	else
7427 	{
7428 		// NOTE: this point should no longer be reached in the standard goal code, since the goal or comm order must be achievable
7429 		// for this function to be called.  This point should only be reached if the ship has no subspace drive, AND either has no
7430 		// mothership assigned (or departs to hyperspace) or the mothership was destroyed, AND falls into one of the following cases:
7431 		// 1) The ship's departure cue evaluates to true in the mission
7432 		// 2) A support ship has had its hull fall to 25% when it has no repair targets
7433 		// 3) A fighter or bomber with an IFF that doesn't allow support ships has its warp_out_timestamp elapse (but this seems to not be a possibility anymore)
7434 		// 4) An instructor in a training mission has been fired upon
7435 		mprintf(("Can't warp!  Doing something else instead.\n"));
7436 
7437         shipp->flags.remove(Ship::Ship_Flags::Depart_dockbay);
7438         shipp->flags.remove(Ship::Ship_Flags::Depart_warp);
7439 		ai_do_default_behavior(objp);
7440 
7441 		return 0;
7442 	}
7443 }
7444 
7445 /**
7446  * Put here because mission_eval_arrivals is here.
7447  * @todo Might move these to a better location later -- MWA
7448  */
mission_eval_departures()7449 void mission_eval_departures()
7450 {
7451 	int i, j;
7452 	object *objp;
7453 	wing *wingp;
7454 
7455 	// scan through the active ships an evaluate their departure cues.  For those
7456 	// ships whose time has come, set their departing flag.
7457 
7458 	for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
7459 		if (objp->type == OBJ_SHIP) {
7460 			ship	*shipp;
7461 
7462 			Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
7463 
7464 			shipp = &Ships[objp->instance];
7465 
7466 			// don't process a ship that is already departing or dying or disabled
7467 			// AL 12-30-97: Added SF_CANNOT_WARP to check
7468 			// Goober5000 - fixed so that it WILL eval when SF_CANNOT_WARP if departing to dockbay
7469 			// wookieejedi - fixed so it accounts for break and never warp too
7470 			if ( shipp->is_dying_or_departing() || ( !(ship_can_warp_full_check(shipp)) && (shipp->departure_location != DEPART_AT_DOCK_BAY)) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) {
7471 				continue;
7472 			}
7473 
7474 			// don't process ships that are part of a wing -- handled in seperate case
7475 			if ( shipp->wingnum != -1 )
7476 				continue;
7477 
7478 			// when the departure cue becomes true, set off the departure delay timer.  We store the
7479 			// timer as -seconds in FreeSpace which indicates that the timer has not been set.  If the timer
7480 			// is not set, then turn it into a valid timer and keep evaluating the timer until it is elapsed
7481 			if ( eval_sexp(shipp->departure_cue) ) {
7482 				if ( shipp->departure_delay <= 0 )
7483 					shipp->departure_delay = timestamp(-shipp->departure_delay * 1000 );
7484 				if ( timestamp_elapsed(shipp->departure_delay) )
7485 					mission_do_departure( objp );
7486 			}
7487 		}
7488 	}
7489 
7490 	// now scan through the list of wings and check their departure cues.  For wings with
7491 	// that cue being true, we must update internal variables to indicate that the wing is
7492 	// departed and that no further waves of this wing will appear
7493 
7494 	for ( i = 0; i < Num_wings; i++ ) {
7495 		wingp = &Wings[i];
7496 
7497 		// should we process this wing anymore
7498 		if ( wingp->flags[Ship::Wing_Flags::Departing] )
7499 			continue;
7500 
7501 		// evaluate the sexpression.  If true, mark all the ships in this wing as departing and increment
7502 		// the num departed in the wing structure.  Then add number of remaining waves * ships/wave to
7503 		// departed count to get total count of ships in the wing which departed.  (We are counting ships
7504 		// that have not yet arrived as departed if they never arrive -- this may be bad, but for some reason
7505 		// seems like the right thing to do).
7506 
7507 		if ( eval_sexp(wingp->departure_cue) ) {
7508 			// if we haven't set up the departure timer yet (would be <= 0) setup the timer to pop N seconds
7509 			// later
7510 			if ( wingp->departure_delay <= 0 )
7511 				wingp->departure_delay = timestamp( -wingp->departure_delay * 1000 );
7512 			if ( !timestamp_elapsed(wingp->departure_delay) )
7513 				continue;
7514 
7515             wingp->flags.set(Ship::Wing_Flags::Departing);
7516 			for ( j = 0; j < wingp->current_count; j++ ) {
7517 				ship *shipp;
7518 
7519 				shipp = &Ships[wingp->ship_index[j]];
7520 				if ( (shipp->is_departing()) || (shipp->flags[Ship::Ship_Flags::Dying]) )
7521 					continue;
7522 
7523 				Assert ( shipp->objnum != -1 );
7524 				objp = &Objects[shipp->objnum];
7525 
7526 				mission_do_departure( objp );
7527 				// don't add to wingp->total_departed here -- this is taken care of in ship code.
7528 			}
7529 		}
7530 	}
7531 	Mission_departure_timestamp = timestamp(DEPARTURE_TIMESTAMP);
7532 }
7533 
7534 /**
7535  * Called from high level game loop to do mission evaluation stuff
7536  */
mission_parse_eval_stuff()7537 void mission_parse_eval_stuff()
7538 {
7539 	mission_eval_arrivals();
7540 	mission_eval_departures();
7541 }
7542 
allocate_subsys_status()7543 int allocate_subsys_status()
7544 {
7545 	int i;
7546 	// set primary weapon ammunition here, but does it actually matter? - Goober5000
7547 
7548 	Assert(Subsys_index >= 0);
7549 
7550 	// we allocate in blocks of MIN_SUBSYS_STATUS_SIZE so if we need more then make more
7551 	if ( (Subsys_status == NULL) || (Subsys_index >= (Subsys_status_size - 1)) ) {
7552 		Assert( MIN_SUBSYS_STATUS_SIZE > 0 );
7553 
7554 		Subsys_status_size += MIN_SUBSYS_STATUS_SIZE;
7555 		Subsys_status = (subsys_status*)vm_realloc(Subsys_status, sizeof(subsys_status) * Subsys_status_size );
7556 	}
7557 
7558 	Verify( Subsys_status != NULL );
7559 
7560 	// the memset is redundant to the below assignments
7561 	//memset( &Subsys_status[Subsys_index], 0, sizeof(subsys_status) );
7562 
7563 	Subsys_status[Subsys_index].name[0] = '\0';
7564 
7565 	Subsys_status[Subsys_index].percent = 0.0f;
7566 
7567 	Subsys_status[Subsys_index].primary_banks[0] = SUBSYS_STATUS_NO_CHANGE;
7568 	Subsys_status[Subsys_index].primary_ammo[0] = 100; // *
7569 
7570 	for (i=1; i<MAX_SHIP_PRIMARY_BANKS; i++)
7571 	{
7572 		Subsys_status[Subsys_index].primary_banks[i] = -1;  // none
7573 		Subsys_status[Subsys_index].primary_ammo[i] = 100;	// *
7574 	}
7575 
7576 	Subsys_status[Subsys_index].secondary_banks[0] = SUBSYS_STATUS_NO_CHANGE;
7577 	Subsys_status[Subsys_index].secondary_ammo[0] = 100;
7578 
7579 	for (i=1; i<MAX_SHIP_SECONDARY_BANKS; i++)
7580 	{
7581 		Subsys_status[Subsys_index].secondary_banks[i] = -1;
7582 		Subsys_status[Subsys_index].secondary_ammo[i] = 100;
7583 	}
7584 
7585 	Subsys_status[Subsys_index].ai_class = SUBSYS_STATUS_NO_CHANGE;
7586 
7587 	Subsys_status[Subsys_index].subsys_cargo_name = 0;	// "Nothing"
7588 
7589 	return Subsys_index++;
7590 }
7591 
7592 // Goober5000
insert_subsys_status(p_object * pobjp)7593 int insert_subsys_status(p_object *pobjp)
7594 {
7595 	int i, new_index;
7596 
7597 	// this is not good; we have to allocate another slot, but then bump all the
7598 	// slots upward so that this particular parse object's subsystems are contiguous
7599 	new_index = allocate_subsys_status();
7600 
7601 	// only bump the subsystems if this isn't the very last subsystem
7602 	if (new_index != pobjp->subsys_index + pobjp->subsys_count)
7603 	{
7604 		// copy the new blank entry for future reference
7605 		subsys_status temp_entry;
7606 		memcpy(&temp_entry, &Subsys_status[new_index], sizeof(subsys_status));
7607 
7608 		// shift elements upward
7609 		for (i = Subsys_index - 1; i > (pobjp->subsys_index + pobjp->subsys_count); i--)
7610 		{
7611 			memcpy(&Subsys_status[i], &Subsys_status[i-1], sizeof(subsys_status));
7612 		}
7613 
7614 		// correct the index so that the new subsystem belongs to the proper p_object
7615 		new_index = pobjp->subsys_index + pobjp->subsys_count;
7616 
7617 		// put the blank entry in the p_object
7618 		memcpy(&Subsys_status[new_index], &temp_entry, sizeof(subsys_status));
7619 	}
7620 
7621 	// make the p_object aware of its new subsystem
7622 	pobjp->subsys_count++;
7623 
7624 	// we also have to adjust all the indexes in existing parse objects
7625 	// (each p_object's subsys_index points to subsystem 0 in its list)
7626 	for (SCP_vector<p_object>::iterator ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
7627 	{
7628 		// bump up base index to accommodate inserted subsystem
7629 		if (ii->subsys_index >= new_index)
7630 			ii->subsys_index++;
7631 	}
7632 
7633 	return new_index;
7634 }
7635 
7636 // Goober5000
parse_get_subsys_status(p_object * pobjp,const char * subsys_name)7637 subsys_status *parse_get_subsys_status(p_object *pobjp, const char *subsys_name)
7638 {
7639 	int i;
7640 	subsys_status *sssp;
7641 
7642 	for (i = 0; i < pobjp->subsys_count; i++)
7643 	{
7644 		sssp = &Subsys_status[pobjp->subsys_index + i];
7645 
7646 		if (!subsystem_stricmp(sssp->name, subsys_name))
7647 			return sssp;
7648 	}
7649 
7650 	return NULL;
7651 }
7652 
7653 // find (or add) the name in the list and return an index to it.
get_parse_name_index(const char * name)7654 int get_parse_name_index(const char *name)
7655 {
7656 	int i;
7657 
7658 	for (i=0; i<Num_parse_names; i++)
7659 		if (!stricmp(name, Parse_names[i]))
7660 			return i;
7661 
7662 	Assert(i < MAX_SHIPS + MAX_WINGS);
7663 	Assert(strlen(name) < NAME_LENGTH);
7664 	strcpy_s(Parse_names[i], name);
7665 	return Num_parse_names++;
7666 }
7667 
7668 // Goober5000
add_path_restriction()7669 int add_path_restriction()
7670 {
7671 	int i, j;
7672 
7673 	// parse it
7674 	path_restriction_t temp;
7675 	temp.cached_mask = (1 << MAX_SHIP_BAY_PATHS);	// uninitialized value (too high)
7676 	temp.num_paths = (int)stuff_string_list(temp.path_names, MAX_SHIP_BAY_PATHS);
7677 
7678 	// no restriction?
7679 	if (temp.num_paths == 0)
7680 		return -1;
7681 
7682 	// first, see if it's duplicated anywhere
7683 	for (i = 0; i < Num_path_restrictions; i++)
7684 	{
7685 		// must have same number of allowed paths
7686 		if (temp.num_paths != Path_restrictions[i].num_paths)
7687 			continue;
7688 
7689 		// see if path names match
7690 		for (j = 0; j < temp.num_paths; j++)
7691 		{
7692 			// no match, so skip this
7693 			if (stricmp(temp.path_names[j], Path_restrictions[i].path_names[j]) != 0)
7694 				goto continue_outer_loop;
7695 		}
7696 
7697 		// match!
7698 		return i;
7699 
7700 continue_outer_loop:
7701 		;
7702 	}
7703 
7704 	// no match, so add a new restriction
7705 
7706 	// check limit
7707 	if (Num_path_restrictions >= MAX_PATH_RESTRICTIONS)
7708 	{
7709 		Warning(LOCATION, "Maximum number of path restrictions reached");
7710 		return -1;
7711 	}
7712 
7713 	// add this restriction at the new index
7714 	int index = Num_path_restrictions++;
7715 	Path_restrictions[index] = temp;
7716 
7717 	return index;
7718 }
7719 
7720 /**
7721  * Look for \<any friendly\>, \<any hostile player\>, etc.
7722  */
get_special_anchor(const char * name)7723 int get_special_anchor(const char *name)
7724 {
7725 	char tmp[NAME_LENGTH + 15];
7726 	const char *iff_name;
7727 	int iff_index;
7728 
7729 	if (strnicmp(name, "<any ", 5) != 0)
7730 		return -1;
7731 
7732 	strcpy_s(tmp, name+5);
7733 	iff_name = strtok(tmp, " >");
7734 
7735 	// hack substitute "hostile" for "enemy"
7736 	if (!stricmp(iff_name, "enemy"))
7737 		iff_name = "hostile";
7738 
7739 	iff_index = iff_lookup(iff_name);
7740 	if (iff_index < 0)
7741 		return -1;
7742 
7743 	// restrict to players?
7744 	if (stristr(name+5, "player") != NULL)
7745 		return (iff_index | SPECIAL_ARRIVAL_ANCHOR_FLAG | SPECIAL_ARRIVAL_ANCHOR_PLAYER_FLAG);
7746 	else
7747 		return (iff_index | SPECIAL_ARRIVAL_ANCHOR_FLAG);
7748 }
7749 
get_anchor(const char * name)7750 int get_anchor(const char *name)
7751 {
7752 	int special_anchor = get_special_anchor(name);
7753 
7754 	if (special_anchor >= 0)
7755 		return special_anchor;
7756 
7757 	return get_parse_name_index(name);
7758 }
7759 
7760 /**
7761  * Fixup the goals/ai references for player objects in the mission
7762  */
mission_parse_fixup_players()7763 void mission_parse_fixup_players()
7764 {
7765 	object *objp;
7766 
7767 	for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
7768 		if ( (objp->type == OBJ_SHIP) && (objp->flags[Object::Object_Flags::Player_ship]) ) {
7769 			game_busy( NOX("** fixing up player/ai stuff **") );	// animate the loading screen, doesn't nothing if the screen is not active
7770 			ai_clear_ship_goals( &Ai_info[Ships[objp->instance].ai_index] );
7771 			init_ai_object( OBJ_INDEX(objp) );
7772 		}
7773 	}
7774 }
7775 
7776 // code to warp in a new support ship.  It works by finding the average position of all ships
7777 // in the mission, creating a vector from that position to the player, and scaling out behind the
7778 // player some distance.  Should be sufficient.
7779 
7780 #define WARP_IN_MIN_DISTANCE	1000.0f
7781 #define WARP_IN_TIME_MIN		3000				// warps in min 3 seconds later
7782 #define WARP_IN_TIME_MAX		6000				// warps in max 6 seconds later
7783 
7784 /**
7785  * Adds requester_objp onto the queue of ships for the arriving support ship to service
7786  */
mission_add_to_arriving_support(object * requester_objp)7787 void mission_add_to_arriving_support( object *requester_objp )
7788 {
7789 	int i;
7790 	ship *shipp;
7791 
7792 	Assert ( Arriving_support_ship );
7793 
7794 	if ( Num_arriving_repair_targets == MAX_AI_GOALS ) {
7795 		mprintf(("Reached MAX_AI_GOALS trying to add repair request!\n"));
7796 		return;
7797 	}
7798 
7799 	shipp = &Ships[requester_objp->instance];
7800 	// check for duplicates before adding
7801 	for (i = 0; i < Num_arriving_repair_targets; i++ ) {
7802 		if ( !stricmp(Arriving_repair_targets[i], shipp->ship_name) ){
7803 			break;
7804 		}
7805 	}
7806 	if ( i != Num_arriving_repair_targets ){		// found the ship before reaching the end -- ignore it!
7807 		return;
7808 	}
7809 
7810 	strcpy_s( Arriving_repair_targets[Num_arriving_repair_targets], Ships[requester_objp->instance].ship_name );
7811 	Num_arriving_repair_targets++;
7812 
7813 	if ( MULTIPLAYER_MASTER ){
7814 		multi_maybe_send_repair_info( requester_objp, NULL, REPAIR_INFO_WARP_ADD );
7815 	}
7816 }
7817 
7818 extern int pp_collide_any(vec3d *curpos, vec3d *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag);
7819 
7820 /**
7821  * Set the warp in position for a support ship relative to an object.
7822  * Caller tries several positions, passing vector in x, y, z.
7823  */
get_warp_in_pos(vec3d * pos,object * objp,float x,float y,float z)7824 int get_warp_in_pos(vec3d *pos, object *objp, float x, float y, float z)
7825 {
7826 	float	rand_val;
7827 
7828 	if ( Game_mode & GM_NORMAL )
7829 		rand_val = frand();
7830 	else
7831 		rand_val = static_randf(objp->net_signature);
7832 
7833 	rand_val = 1.0f + (rand_val - 0.5f)*0.2f;
7834 
7835 	*pos = objp->pos;
7836 
7837 	vm_vec_scale_add2( pos, &objp->orient.vec.rvec, x*rand_val*800.0f);
7838 	vm_vec_scale_add2( pos, &objp->orient.vec.uvec, y*rand_val*800.0f);
7839 	vm_vec_scale_add2( pos, &objp->orient.vec.fvec, z*rand_val*800.0f);
7840 
7841 	return pp_collide_any(&objp->pos, pos, objp->radius, objp, NULL, 1);
7842 }
7843 
7844 /**
7845  * Modified by Goober5000 to allow more flexibility in support ships
7846  */
mission_bring_in_support_ship(object * requester_objp)7847 void mission_bring_in_support_ship( object *requester_objp )
7848 {
7849 	vec3d center, warp_in_pos;
7850 	p_object *pobj;
7851 	ship *requester_shipp;
7852 	int i, j, requester_species;
7853 
7854 	Assert ( requester_objp->type == OBJ_SHIP );
7855 	requester_shipp = &Ships[requester_objp->instance];	//	MK, 10/23/97, used to be ->type, bogus, no?
7856 
7857 	// if the support ship is already arriving, add the requester to the list
7858 	if ( Arriving_support_ship ) {
7859 		mission_add_to_arriving_support( requester_objp );
7860 		return;
7861 	}
7862 
7863 	// create a parse object, and put it onto the ship arrival list.  This whole thing kind of stinks.
7864 	// I want to put it into a parse object since it needs to arrive just a little later than
7865 	// this function is called.  I have to make some assumptions in the code about values for the parse
7866 	// object since I'm no longer working with a mission file.  These exceptions will be noted with
7867 	// comments
7868 
7869 	Arriving_support_ship = &Support_ship_pobj;
7870 	pobj = Arriving_support_ship;
7871 
7872 	// get average position of all ships
7873 	obj_get_average_ship_pos( &center );
7874 	vm_vec_sub( &warp_in_pos, &center, &(requester_objp->pos) );
7875 
7876 	//	Choose position to warp in ship.
7877 	//	Temporary, but changed by MK because it used to be exactly behind the player.
7878 	//	This could cause an Assert if the player immediately targeted it (before moving).
7879 	//	Tend to put in front of the player to aid him in flying towards the ship.
7880 
7881 	if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.1f, 1.0f))
7882 		if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.2f, -1.0f))
7883 			if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.2f, -1.0f))
7884 				if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.1f, 1.0f))
7885 					get_warp_in_pos(&warp_in_pos, requester_objp, 0.1f, 1.0f, 0.2f);
7886 
7887 	// position for ship if it warps in
7888 	pobj->pos = warp_in_pos;
7889 
7890 	// tally the ship
7891 	The_mission.support_ships.tally++;
7892 
7893 	// create a name for the ship.  use "Support #".  look for collisions until one isn't found anymore
7894 	i = 1;
7895 	do {
7896 		sprintf(pobj->name, NOX("Support %d"), i);
7897 		if ( (ship_name_lookup(pobj->name) == -1) && (ship_find_exited_ship_by_name(pobj->name) == -1) )
7898 			break;
7899 		i++;
7900 	} while(1);
7901 
7902 	vm_set_identity( &(pobj->orient) );
7903 
7904 	// *sigh*.  Gotta get the ship class.  For now, this will amount to finding a ship in the ship_info
7905 	// array with the same team as the requester of type SIF_SUPPORT.  Might need to be changed, but who knows
7906 
7907 	// Goober5000 - who knew of the SCP release? ;) only determine ship class if not set by SEXP
7908 	pobj->ship_class = The_mission.support_ships.ship_class;
7909 	if (pobj->ship_class < 0)
7910 	{
7911 		requester_species = Ship_info[requester_shipp->ship_info_index].species;
7912 
7913 		// 5/6/98 -- MWA  Don't need to do anything for multiplayer.  I think that we always want to use
7914 		// the species of the caller ship.
7915 
7916 		i = -1;
7917 		// get index of correct species support ship
7918 		for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
7919             if ((it->species == requester_species) && (it->flags[Ship::Info_Flags::Support])) {
7920 				i = (int)std::distance(Ship_info.cbegin(), it);
7921 				break;
7922 			}
7923 		}
7924 
7925 		if ( i != -1 )
7926 			pobj->ship_class = i;
7927 		else
7928 			Int3();				// BOGUS!!!!  gotta figure something out here
7929 	}
7930 
7931 	// set support ship hitpoints
7932 	pobj->ship_max_hull_strength = Ship_info[i].max_hull_strength;
7933 	pobj->ship_max_shield_strength = Ship_info[i].max_shield_strength;
7934 	pobj->max_shield_recharge = Ship_info[i].max_shield_recharge;
7935 
7936 	pobj->team = requester_shipp->team;
7937 
7938 	for (i=0;i<MAX_IFFS;i++)
7939 	{
7940 		for (j=0;j<MAX_IFFS;j++)
7941 		{
7942 			pobj->alt_iff_color[i][j] = -1;
7943 		}
7944 	}
7945 
7946 	pobj->behavior = AIM_NONE;		// ASSUMPTION:  the mission file has the string "None" which maps to AIM_NONE
7947 
7948 	// set the ai_goals to -1.  We will put the requester object shipname in repair target array and then take
7949 	// care of setting up the goal when creating the ship!!!!
7950 	pobj->ai_goals = -1;
7951 	Num_arriving_repair_targets = 0;
7952 	mission_add_to_arriving_support( requester_objp );
7953 
7954 	// need to set ship's cargo to nothing.  scan the cargo_names array looking for the string nothing.
7955 	// add it if not found
7956 	for (i = 0; i < Num_cargo; i++ )
7957 		if ( !stricmp(Cargo_names[i], NOX("nothing")) )
7958 			break;
7959 
7960 	if ( i == Num_cargo ) {
7961 		strcpy(Cargo_names[i], NOX("Nothing"));
7962 		Num_cargo++;
7963 	}
7964 	pobj->cargo1 = char(i);
7965 
7966 	pobj->status_count = 0;
7967 
7968 	// Goober5000 - take some stuff from mission flags
7969 	pobj->arrival_location = The_mission.support_ships.arrival_location;
7970 	pobj->arrival_anchor = The_mission.support_ships.arrival_anchor;
7971 	pobj->departure_location = The_mission.support_ships.departure_location;
7972 	pobj->departure_anchor = The_mission.support_ships.departure_anchor;
7973 
7974 	pobj->arrival_path_mask = 0;
7975 	pobj->departure_path_mask = 0;
7976 
7977 	pobj->arrival_distance = 0;
7978 	pobj->arrival_cue = Locked_sexp_true;
7979 	pobj->arrival_delay = timestamp_rand(WARP_IN_TIME_MIN, WARP_IN_TIME_MAX);
7980 
7981 	pobj->subsys_count = 0;				// number of elements used in subsys_status array
7982 	pobj->initial_velocity = 100;		// start at 100% velocity
7983 	pobj->initial_hull = 100;			// start at 100% hull
7984 	pobj->initial_shields = 100;		// and 100% shields
7985 
7986 	pobj->departure_cue = Locked_sexp_false;
7987 	pobj->departure_delay = 0;
7988 
7989 	pobj->warpin_params_index = -1;
7990 	pobj->warpout_params_index = -1;
7991 
7992 	pobj->wingnum = -1;
7993 
7994     pobj->flags.reset();
7995 
7996     if (Player_obj->flags[Object::Object_Flags::No_shields])  {
7997 		pobj->flags.set(Mission::Parse_Object_Flags::OF_No_shields);	// support ships have no shields when player has not shields
7998 	}
7999 
8000 	pobj->ai_class = Ship_info[pobj->ship_class].ai_class;
8001 	pobj->hotkey = -1;
8002 	pobj->score = 0;
8003 	pobj->assist_score_pct = 0;
8004 
8005 	pobj->dock_list = NULL;
8006 	pobj->created_object = NULL;
8007 	pobj->group = -1;
8008 	pobj->persona_index = -1;
8009 	pobj->net_signature = multi_assign_network_signature(MULTI_SIG_SHIP);
8010 	pobj->wing_status_wing_index = -1;
8011 	pobj->wing_status_wing_pos = -1;
8012 	pobj->respawn_count = 0;
8013 	pobj->alt_type_index = -1;
8014 	pobj->callsign_index = -1;
8015 	pobj->replacement_textures.clear();
8016 }
8017 
8018 /**
8019  * Returns true if a support ship is currently in the process of warping in.
8020  */
mission_is_support_ship_arriving()8021 int mission_is_support_ship_arriving()
8022 {
8023 	if ( Arriving_support_ship )
8024 		return 1;
8025 	else
8026 		return 0;
8027 }
8028 
8029 /**
8030  * Returns true if the given ship is scheduled to be repaired by the arriving support ship
8031  */
mission_is_repair_scheduled(object * objp)8032 int mission_is_repair_scheduled( object *objp )
8033 {
8034 	char *name;
8035 	int i;
8036 
8037 	if ( !Arriving_support_ship )
8038 		return 0;
8039 
8040 	Assert ( objp->type == OBJ_SHIP );
8041 	name = Ships[objp->instance].ship_name;
8042 	for (i = 0; i < Num_arriving_repair_targets; i++ ) {
8043 		if ( !strcmp( name, Arriving_repair_targets[i]) )
8044 			return 1;
8045 	}
8046 
8047 	return 0;
8048 }
8049 
8050 /**
8051  * Removed the given ship from the list of ships that are to get repair by arriving support ship
8052  */
mission_remove_scheduled_repair(object * objp)8053 int mission_remove_scheduled_repair( object *objp )
8054 {
8055 	char *name;
8056 	int i, index;
8057 
8058 	if ( !Arriving_support_ship )
8059 		return 0;
8060 
8061 	// itereate through the target list looking for this ship name.  If not found, we
8062 	// can simply return.
8063 	Assert ( objp->type == OBJ_SHIP );
8064 	name = Ships[objp->instance].ship_name;
8065 	for (index = 0; index < Num_arriving_repair_targets; index++ ) {
8066 		if ( !strcmp( name, Arriving_repair_targets[index]) )
8067 			break;
8068 	}
8069 	if ( index == Num_arriving_repair_targets )
8070 		return 0;
8071 
8072 	// ship is found -- compress the array
8073 	for ( i = index; i < Num_arriving_repair_targets - 1; i++ )
8074 		strcpy_s( Arriving_repair_targets[i], Arriving_repair_targets[i+1] );
8075 
8076 	Num_arriving_repair_targets--;
8077 
8078 	if ( MULTIPLAYER_MASTER )
8079 		multi_maybe_send_repair_info( objp, NULL, REPAIR_INFO_WARP_REMOVE );
8080 
8081 	return 1;
8082 }
8083 
8084 /**
8085  * Alternate name stuff
8086  */
mission_parse_lookup_alt(const char * name)8087 int mission_parse_lookup_alt(const char *name)
8088 {
8089 	int idx;
8090 
8091 	// sanity
8092 	if(name == NULL)
8093 		return -1;
8094 
8095 	// lookup
8096 	for(idx=0; idx<Mission_alt_type_count; idx++)
8097 	{
8098 		if(!strcmp(Mission_alt_types[idx], name))
8099 			return idx;
8100 	}
8101 
8102 	// could not find
8103 	return -1;
8104 }
8105 
8106 static int mission_parse_lookup_alt_index_warn = 1;
mission_parse_lookup_alt_index(int index)8107 const char *mission_parse_lookup_alt_index(int index)
8108 {
8109 	if((index < 0) || (index >= Mission_alt_type_count))
8110 	{
8111 		if (mission_parse_lookup_alt_index_warn)
8112 		{
8113 			Warning(LOCATION, "Ship with invalid alt_name.  Get a programmer");
8114 			mission_parse_lookup_alt_index_warn = 0;
8115 		}
8116 		return "";
8117 	}
8118 
8119 	// get it
8120 	return Mission_alt_types[index];
8121 }
8122 
mission_parse_add_alt(const char * name)8123 int mission_parse_add_alt(const char *name)
8124 {
8125 	// sanity
8126 	if(name == NULL)
8127 		return -1;
8128 
8129 	// maybe add
8130 	if(Mission_alt_type_count < MAX_ALT_TYPE_NAMES)
8131 	{
8132 		// stuff the name
8133 		strcpy_s(Mission_alt_types[Mission_alt_type_count++], name);
8134 
8135 		// done
8136 		return Mission_alt_type_count - 1;
8137 	}
8138 
8139 	return -1;
8140 }
8141 
mission_parse_remove_alt(const char * name)8142 void mission_parse_remove_alt(const char *name)
8143 {
8144 	// sanity
8145 	if(name == NULL)
8146 		return;
8147 
8148 	// maybe remove
8149 	for (int i = 0; i < Mission_alt_type_count; ++i)
8150 	{
8151 		if (!strcmp(Mission_alt_types[i], name))
8152 		{
8153 			// remove this name by overwriting it with the last name
8154 			if (i < Mission_alt_type_count - 1)
8155 				strcpy_s(Mission_alt_types[i], Mission_alt_types[Mission_alt_type_count - 1]);
8156 
8157 			Mission_alt_type_count--;
8158 			break;
8159 		}
8160 	}
8161 }
8162 
mission_parse_reset_alt()8163 void mission_parse_reset_alt()
8164 {
8165 	Mission_alt_type_count = 0;
8166 }
8167 
8168 // For compatibility purposes some mods want alt names to truncate at the hash symbol.  But we can't actually do that at mission load,
8169 // since we have to save them again in FRED.  So this function processes the names just before the mission starts.
8170 // To further complicate things, some mods actually want the hash to be displayed.  So this function uses the double-hash as an escape sequence for a single hash.
mission_process_alt_types()8171 void mission_process_alt_types()
8172 {
8173 	for (int i = 0; i < Mission_alt_type_count; ++i)
8174 	{
8175 		// truncate at a single hash
8176 		end_string_at_first_hash_symbol(Mission_alt_types[i], true);
8177 
8178 		// ## -> #
8179 		consolidate_double_characters(Mission_alt_types[i], '#');
8180 	}
8181 }
8182 
8183 /**
8184  * Callsign stuff
8185  */
mission_parse_lookup_callsign(const char * name)8186 int mission_parse_lookup_callsign(const char *name)
8187 {
8188 	int idx;
8189 
8190 	// sanity
8191 	if(name == NULL)
8192 		return -1;
8193 
8194 	// lookup
8195 	for(idx=0; idx<Mission_callsign_count; idx++)
8196 	{
8197 		if(!strcmp(Mission_callsigns[idx], name))
8198 			return idx;
8199 	}
8200 
8201 	// could not find
8202 	return -1;
8203 }
8204 
8205 static int mission_parse_lookup_callsign_index_warn = 1;
mission_parse_lookup_callsign_index(int index)8206 const char *mission_parse_lookup_callsign_index(int index)
8207 {
8208 	if((index < 0) || (index >= Mission_callsign_count))
8209 	{
8210 		if (mission_parse_lookup_callsign_index_warn)
8211 		{
8212 			Warning(LOCATION, "Ship with invalid callsign.  Get a programmer");
8213 			mission_parse_lookup_callsign_index_warn = 0;
8214 		}
8215 		return "";
8216 	}
8217 
8218 	// get it
8219 	return Mission_callsigns[index];
8220 }
8221 
mission_parse_add_callsign(const char * name)8222 int mission_parse_add_callsign(const char *name)
8223 {
8224 	// sanity
8225 	if(name == NULL)
8226 		return -1;
8227 
8228 	// maybe add
8229 	if(Mission_callsign_count < MAX_CALLSIGNS)
8230 	{
8231 		// stuff the name
8232 		strcpy_s(Mission_callsigns[Mission_callsign_count++], name);
8233 
8234 		// done
8235 		return Mission_callsign_count - 1;
8236 	}
8237 
8238 	return -1;
8239 }
8240 
mission_parse_remove_callsign(const char * name)8241 void mission_parse_remove_callsign(const char *name)
8242 {
8243 	// sanity
8244 	if(name == NULL)
8245 		return;
8246 
8247 	// maybe remove
8248 	for (int i = 0; i < Mission_callsign_count; ++i)
8249 	{
8250 		if (!strcmp(Mission_callsigns[i], name))
8251 		{
8252 			// remove this callsign by overwriting it with the last callsign
8253 			if (i < Mission_callsign_count - 1)
8254 				strcpy_s(Mission_callsigns[i], Mission_callsigns[Mission_callsign_count - 1]);
8255 
8256 			Mission_callsign_count--;
8257 			break;
8258 		}
8259 	}
8260 }
8261 
mission_parse_reset_callsign()8262 void mission_parse_reset_callsign()
8263 {
8264 	Mission_callsign_count = 0;
8265 }
8266 
is_training_mission()8267 int is_training_mission()
8268 {
8269 	return (The_mission.game_type & MISSION_TYPE_TRAINING);
8270 }
8271 
8272 /**
8273  * Go through all the displayed text in one section and fix the section and text delimiters should all be different
8274  */
conv_fix_punctuation_section(char * str,const char * section_start,const char * section_end,const char * text_start,const char * text_end)8275 void conv_fix_punctuation_section(char *str, const char *section_start, const char *section_end, const char *text_start,
8276 								  const char *text_end)
8277 {
8278 	char *s1, *s2, *t1, *t2;
8279 
8280 	s1 = strstr(str, section_start);
8281 	s2 = strstr(s1, section_end);
8282 
8283 	t1 = s1;
8284 
8285 	while (1)
8286 	{
8287 		t1 = strstr(t1+1, text_start);
8288 		if (!t1 || t1 > s2) return;
8289 
8290 		t2 = strstr(t1, text_end);
8291 		if (!t2 || t2 > s2) return;
8292 
8293 		replace_all(t1, "\"", "$quote", PARSE_TEXT_SIZE - (str - Parse_text), (t2 - t1));
8294 	}
8295 }
8296 
8297 // Goober5000
conv_fix_punctuation()8298 void conv_fix_punctuation()
8299 {
8300 	// command briefings
8301 	conv_fix_punctuation_section(Parse_text, "#Command Briefing", "#Briefing", "$Stage Text:", "$end_multi_text");
8302 
8303 	// briefings
8304 	conv_fix_punctuation_section(Parse_text, "#Briefing", "#Debriefing_info", "$multi_text", "$end_multi_text");
8305 
8306 	// debriefings
8307 	conv_fix_punctuation_section(Parse_text, "#Debriefing_info", "#Players", "$Multi text", "$end_multi_text");
8308 
8309 	// messages
8310 	conv_fix_punctuation_section(Parse_text, "#Messages", "#Reinforcements", "$Message:", "\n");
8311 }
8312 
8313 // Goober5000
convertFSMtoFS2()8314 void convertFSMtoFS2()
8315 {
8316 	// fix punctuation
8317 	conv_fix_punctuation();
8318 }
8319 
clear_texture_replacements()8320 void clear_texture_replacements()
8321 {
8322 	Fred_texture_replacements.clear();
8323 }
8324