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(¢er_pos, &actual_local_center, &objp->orient);
1704 vm_vec_add2(¢er_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, ¢er_pos, &objp->orient.vec.fvec, 0.0f);
1717 vm_vec_scale_add(&new_center_pos, ¢er_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, ¢er_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( ¢er );
7874 vm_vec_sub( &warp_in_pos, ¢er, &(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