FS2_Open
Open source remastering of the Freespace 2 engine
missionparse.cpp
Go to the documentation of this file.
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 #ifdef _WIN32
13 #include <windows.h>
14 #endif
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <assert.h>
20 #include <stdarg.h>
21 #include <setjmp.h>
22 
23 
24 #include "ai/aigoals.h"
25 #include "asteroid/asteroid.h"
26 #include "bmpman/bmpman.h"
27 #include "cfile/cfile.h"
28 #include "cmdline/cmdline.h"
29 #include "debris/debris.h"
30 #include "gamesnd/eventmusic.h"
31 #include "globalincs/alphacolors.h"
32 #include "globalincs/linklist.h"
33 #include "hud/hudescort.h"
34 #include "hud/hudets.h"
35 #include "hud/hudwingmanstatus.h"
36 #include "iff_defs/iff_defs.h"
37 #include "io/timer.h"
38 #include "jumpnode/jumpnode.h"
39 #include "lighting/lighting.h"
40 #include "localization/localize.h"
41 #include "math/fvi.h"
42 #include "math/staticrand.h"
45 #include "mission/missiongoals.h"
46 #include "mission/missionhotkey.h"
47 #include "mission/missionlog.h"
48 #include "mission/missionmessage.h"
49 #include "mission/missionparse.h"
52 #include "missionui/redalert.h"
53 #include "mod_table/mod_table.h"
54 #include "nebula/neb.h"
55 #include "nebula/neblightning.h"
56 #include "network/multi.h"
57 #include "network/multi_endgame.h"
58 #include "network/multi_respawn.h"
59 #include "network/multimsgs.h"
60 #include "network/multiutil.h"
61 #include "object/parseobjectdock.h"
62 #include "object/waypoint.h"
63 #include "parse/generic_log.h"
64 #include "parse/parselo.h"
65 #include "parse/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 
77 LOCAL struct {
83 
85 
88 
89 int Mission_palette; // index into Nebula_palette_filenames[] of palette file to use for mission
90 int Nebula_index; // index into Nebula_filenames[] of nebula to use in mission.
92 int Num_cargo = 0;
97 int Player_starts = 1;
100 
104 
108 
109 // alternate ship type names
112 
113 // callsigns
116 
117 #define SHIP_WARP_TIME 5.0f // how many seconds it takes for ship to warp in
118 
119 // the ship arrival list will contain a list of ships that are yet to arrive. This
120 // list could also include ships that are part of wings!
121 p_object Ship_arrival_list; // for linked list of ships to arrive later
122 
123 // all the ships that we parse
125 
126 
127 // list for arriving support ship
132 
133 #define MIN_SUBSYS_STATUS_SIZE 25
137 
139 
141 
142 // variables for player start in single player
146 
147 // name of all ships to use while parsing a mission (since a ship might be referenced by
148 // something before that ship has even been loaded yet)
151 
153 
156 
157 //XSTR:OFF
158 
160  "Nebula01",
161  "Nebula02",
162  "Nebula03"
163 };
164 
166  "Nebfull01",
167  "Nebfull02",
168  "Nebfull03"
169 };
170 
171 // Note: Nebula_colors[] and Nebula_palette_filenames are linked via index numbers
173  "Red",
174  "Blue",
175  "Gold",
176  "Purple",
177  "Maroon",
178  "Green",
179  "Grey blue",
180  "Violet",
181  "Grey Green",
182 };
183 
185  "Chase",
186  "Evade",
187  "Get behind",
188  "Stay Near",
189  "Still",
190  "Guard",
191  "Avoid",
192  "Waypoints",
193  "Dock",
194  "None",
195  "Big Ship",
196  "Path",
197  "Be Rearmed",
198  "Safety",
199  "Evade Weapon",
200  "Strafe",
201  "Play Dead",
202  "Bay Emerge",
203  "Bay Depart",
204  "Sentry Gun",
205  "Warp Out",
206 };
207 
210 
211 char *Ship_class_names[MAX_SHIP_CLASSES]; // to be filled in from Ship_info array
212 
214  "Fighter", "Fighter Wing", "Cargo", "Cargo Wing", "Largeship",
215  "Largeship Wing", "Capital", "Planet", "Asteroid Field", "Waypoint",
216  "Support Ship", "Freighter(no cargo)", "Freighter(has cargo)",
217  "Freighter Wing(no cargo)", "Freighter Wing(has cargo)", "Installation",
218  "Bomber", "Bomber Wing", "Cruiser", "Cruiser Wing", "Unknown", "Unknown Wing",
219  "Player Fighter", "Player Fighter Wing", "Player Bomber", "Player Bomber Wing",
220  "Knossos Device", "Transport Wing", "Corvette", "Gas Miner", "Awacs", "Supercap", "Sentry Gun", "Jump Node", "Transport"
221 };
222 
224  "Shields Critical", "Engines Damaged", "Fully Operational",
225 };
226 
228  "Damaged", "Disabled", "Corroded",
229 };
230 
232  "Weapons", "Engines", "Cable TV",
233 };
234 
235 // definitions for arrival locations for ships/wings
237  "Hyperspace", "Near Ship", "In front of ship", "Docking Bay",
238 };
239 
241  "Hyperspace", "Docking Bay",
242 };
243 
245  "Primary", "Secondary", "Bonus",
246 };
247 
249  "Attack/Protect",
250  "Repair/Rearm",
251 };
252 
254  "Single Player Only",
255  "Multiplayer Only",
256  "Single/Multi Player",
257  "Training mission"
258 };
259 
261  "cargo-known",
262  "ignore-count",
263  "protect-ship",
264  "reinforcement",
265  "no-shields",
266  "escort",
267  "player-start",
268  "no-arrival-music",
269  "no-arrival-warp",
270  "no-departure-warp",
271  "locked",
272  "invulnerable",
273  "hidden-from-sensors",
274  "scannable",
275  "kamikaze",
276  "no-dynamic",
277  "red-alert-carry",
278  "beam-protect-ship",
279  "flak-protect-ship",
280  "laser-protect-ship",
281  "missile-protect-ship",
282  "guardian",
283  "special-warp",
284  "vaporize",
285  "stealth",
286  "friendly-stealth-invisible",
287  "don't-collide-invisible",
288 };
289 
291  "primitive-sensors",
292  "no-subspace-drive",
293  "nav-carry-status",
294  "affected-by-gravity",
295  "toggle-subsystem-scanning",
296  "targetable-as-bomb",
297  "no-builtin-messages",
298  "primaries-locked",
299  "secondaries-locked",
300  "no-death-scream",
301  "always-death-scream",
302  "nav-needslink",
303  "hide-ship-name",
304  "set-class-dynamically",
305  "lock-all-turrets",
306  "afterburners-locked",
307  "force-shields-on",
308  "immobile",
309  "no-ets",
310  "cloaked",
311  "ship-locked",
312  "weapons-locked",
313  "scramble-messages",
314  "no-collide",
315  "no-disabled-self-destruct",
316 };
317 
319  "true",
320  "false",
321  "always true", // disabled
322  "always false",
323  "first repeat",
324  "last repeat",
325  "first trigger",
326  "last trigger",
327  "state change",
328 };
329 
330 
331 //XSTR:ON
332 
334 
337 
339 
340 // definitions for timestamps for eval'ing arrival/departure cues
344 
345 #define ARRIVAL_TIMESTAMP 2000 // every 2 seconds
346 #define DEPARTURE_TIMESTAMP 2200 // every 2.2 seconds -- just to be a little different
347 
348 // calculates a "unique" file signature as a ushort (checksum) and an int (file length)
349 // the amount of The_mission we're going to checksum
350 // WARNING : do NOT call this function on the server - it will overwrite goals, etc
351 #define MISSION_CHECKSUM_SIZE (NAME_LENGTH + NAME_LENGTH + 4 + DATE_TIME_LENGTH + DATE_TIME_LENGTH)
352 
353 // timers used to limit arrival messages and music
354 #define ARRIVAL_MUSIC_MIN_SEPARATION 60000
355 #define ARRIVAL_MESSAGE_MIN_SEPARATION 30000
356 
357 #define ARRIVAL_MESSAGE_DELAY_MIN 2000
358 #define ARRIVAL_MESSAGE_DELAY_MAX 3000
359 
360 static int Allow_arrival_music_timestamp;
361 static int Allow_arrival_message_timestamp;
362 static int Arrival_message_delay_timestamp;
363 
364 // multi TvT
365 static int Allow_arrival_music_timestamp_m[2];
366 static int Allow_arrival_message_timestamp_m[2];
367 static int Arrival_message_delay_timestamp_m[2];
368 
369 extern fix game_get_overall_frametime(); // for texture animation
370 
371 // local prototypes
373 void post_process_mission();
377 int mission_set_arrival_location(int anchor, int location, int distance, int objnum, int path_mask, vec3d *new_pos, matrix *new_orient);
378 int get_anchor(char *name);
381 void mission_set_wing_arrival_location( wing *wingp, int num_to_set );
382 int parse_lookup_alt_name(char *name);
383 void parse_init(bool basic = false);
388 
389 static bool Warned_about_team_out_of_range;
390 
391 // Goober5000
395 
396 // Goober5000 - FRED import
397 void convertFSMtoFS2();
398 
399 
400 MONITOR(NumShipArrivals)
401 MONITOR(NumShipDepartures)
402 
403 
404 // Goober5000
405 void parse_custom_bitmap(const char *expected_string_640, const char *expected_string_1024, char *string_field_640, char *string_field_1024)
406 {
407  int found640 = 0, found1024 = 0;
408  strcpy(string_field_640, "");
409  strcpy(string_field_1024, "");
410 
411  // custom mission loading background, or whatever
412  if (optional_string(expected_string_640))
413  {
414  found640 = 1;
415  stuff_string(string_field_640, F_NAME, MAX_FILENAME_LEN);
416  }
417  if (optional_string(expected_string_1024))
418  {
419  found1024 = 1;
420  stuff_string(string_field_1024, F_NAME, MAX_FILENAME_LEN);
421  }
422 
423  // error testing
424  if (Fred_running && (found640) && !(found1024))
425  {
426  Warning(LOCATION, "Mission: found an entry for %s but not a corresponding entry for %s!", expected_string_640, expected_string_1024);
427  }
428  if (Fred_running && !(found640) && (found1024))
429  {
430  Warning(LOCATION, "Mission: found an entry for %s but not a corresponding entry for %s!", expected_string_1024, expected_string_640);
431  }
432 }
433 
434 void parse_mission_info(mission *pm, bool basic = false)
435 {
436  int i;
437  char game_string[NAME_LENGTH];
438 
439  // Goober5000
440  skip_to_start_of_string("#Mission Info");
441 
442  required_string("#Mission Info");
443 
444  required_string("$Version:");
445  stuff_float(&pm->version);
446  if (pm->version != MISSION_VERSION)
447  mprintf(("Older mission, should update it (%.2f<-->%.2f)\n", pm->version, MISSION_VERSION));
448 
449  required_string("$Name:");
451 
452  required_string("$Author:");
454 
455  required_string("$Created:");
457 
458  required_string("$Modified:");
460 
461  required_string("$Notes:");
463 
464  if (optional_string("$Mission Desc:"))
466  else
467  strcpy_s(pm->mission_desc, NOX("No description\n"));
468 
469  pm->game_type = MISSION_TYPE_SINGLE; // default to single player only
470  if ( optional_string("+Game Type:")) {
471  // HACK HACK HACK -- stuff_string was changed to *not* ignore carriage returns. Since the
472  // old style missions may have carriage returns, deal with it here.
474  stuff_string(game_string, F_NAME, NAME_LENGTH);
475  for ( i = 0; i < OLD_MAX_GAME_TYPES; i++ ) {
476  if ( !stricmp(game_string, Old_game_types[i]) ) {
477 
478  // this block of code is now old mission compatibility code. We specify game
479  // type in a different manner than before.
480  if ( i == OLD_GAME_TYPE_SINGLE_ONLY )
482  else if ( i == OLD_GAME_TYPE_MULTI_ONLY )
484  else if ( i == OLD_GAME_TYPE_SINGLE_MULTI )
486  else if ( i == OLD_GAME_TYPE_TRAINING )
488  else
489  Int3();
490 
491  if ( pm->game_type & MISSION_TYPE_MULTI )
493 
494  break;
495  }
496  }
497  }
498 
499  if ( optional_string("+Game Type Flags:") ) {
500  stuff_int(&pm->game_type);
501  }
502 
503  pm->flags = 0;
504  if (optional_string("+Flags:")){
505  stuff_int(&pm->flags);
506  }
507 
508  // nebula mission stuff
509  Neb2_awacs = -1.0f;
510  if(optional_string("+NebAwacs:")){
512  }
513  if(optional_string("+Storm:")){
515 
516  if (!basic)
518  }
519  Neb2_fog_near_mult = 1.0f;
520  Neb2_fog_far_mult = 1.0f;
521  if(optional_string("+Fog Near Mult:")){
523  }
524  if(optional_string("+Fog Far Mult:")){
526  }
527 
528  // Goober5000 - ship contrail speed threshold
530  if (optional_string("$Contrail Speed Threshold:")){
532  }
533 
534  // get the number of players if in a multiplayer mission
535  pm->num_players = 1;
536  if ( pm->game_type & MISSION_TYPE_MULTI ) {
537  if ( optional_string("+Num Players:") ) {
538  stuff_int( &(pm->num_players) );
539  }
540  }
541 
542  // get the number of respawns
543  pm->num_respawns = 0;
544  if ( pm->game_type & MISSION_TYPE_MULTI ) {
545  if ( optional_string("+Num Respawns:") ){
546  stuff_int( (int*)&(pm->num_respawns) );
547  }
548  }
549 
550  The_mission.max_respawn_delay = -1;
551  if ( pm->game_type & MISSION_TYPE_MULTI ) {
552  if ( optional_string("+Max Respawn Time:") ){
553  stuff_int( &The_mission.max_respawn_delay );
554  }
555  }
556 
557  if ( optional_string("+Red Alert:")) {
558  int temp;
559  stuff_int(&temp);
560 
561  if (temp)
563  else
565  }
567 
568  if ( optional_string("+Scramble:")) {
569  int temp;
570  stuff_int(&temp);
571 
572  if (temp)
574  else
576  }
577 
578  // if we are just requesting basic info then skip everything else. the reason
579  // for this is to make sure that we don't modify things outside of the mission struct
580  // that might not get reset afterwards (like what can happen in the techroom) - taylor
581  //
582  // NOTE: this can be dangerous so be sure that any get_mission_info() call (defaults to basic info) will
583  // only reference data parsed before this point!! (like current FRED2 and game code does)
584  if (basic)
585  return;
586 
587 
588  // set up support ships
590  pm->support_ships.arrival_anchor = -1;
594  pm->support_ships.max_subsys_repair_val = 100.0f; //ASSUMPTION: full repair capabilities
595  pm->support_ships.max_support_ships = -1; // infinite
597  pm->support_ships.ship_class = -1;
598  pm->support_ships.tally = 0;
600 
601  // for each species, store whether support is available
602  for (int species = 0; species < (int)Species_info.size(); species++)
603  {
604  for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
605  {
606  if ((it->flags & SIF_SUPPORT) && (it->species == species))
607  {
608  pm->support_ships.support_available_for_species |= (1 << species);
609  break;
610  }
611  }
612  }
613 
614  if ( optional_string("+Disallow Support:"))
615  {
616  int temp;
617  stuff_int(&temp);
618 
619  pm->support_ships.max_support_ships = (temp > 0) ? 0 : -1;
620  }
621 
622  if ( optional_string("+Hull Repair Ceiling:"))
623  {
624  float temp;
625  stuff_float(&temp);
626 
627  //ONLY set max_hull_repair_val if the value given is valid -C
628  if (temp <= 100.0f && temp >= 0.0f) {
630  }
631  }
632 
633  if ( optional_string("+Subsystem Repair Ceiling:"))
634  {
635  float temp;
636  stuff_float(&temp);
637 
638  //ONLY set max_subsys_repair_val if the value given is valid -C
639  if (temp <= 100.0f && temp >= 0.0f) {
641  }
642  }
643 
644  if (optional_string("+All Teams Attack")){
645  Mission_all_attack = 1;
646  } else {
647  Mission_all_attack = 0;
648  }
649 
650  // Maybe delay the player's entry.
651  if (optional_string("+Player Entry Delay:")) {
652  float temp;
653 
654  stuff_float(&temp);
655  Assert(temp >= 0.0f);
656  Entry_delay_time = fl2f(temp);
657  }
658  else
659  {
660  Entry_delay_time = 0;
661  }
662 
663  if (optional_string("+Viewer pos:")){
664  stuff_vec3d(&Parse_viewer_pos);
665  }
666 
667  if (optional_string("+Viewer orient:")){
668  stuff_matrix(&Parse_viewer_orient);
669  }
670 
671  // possible squadron reassignment
672  strcpy_s(pm->squad_name, "");
673  strcpy_s(pm->squad_filename, "");
674  if(optional_string("+SquadReassignName:")){
676  if(optional_string("+SquadReassignLogo:")){
678  }
679  }
680  // always clear out squad reassignments if not single player
682  strcpy_s(pm->squad_name, "");
683  strcpy_s(pm->squad_filename, "");
684  }
685  // reassign the player
686  else {
687  if(!Fred_running && (Player != NULL) && (pm->squad_name[0] != '\0') && (Game_mode & GM_CAMPAIGN_MODE)){
688  mprintf(("Reassigning player to squadron %s\n", pm->squad_name));
691  }
692  }
693 
694 
695  // wing stuff by Goober5000 ------------------------------------------
696  // the wing name arrays are initialized in ship_level_init
697  if (optional_string("$Starting wing names:"))
698  {
700  }
701 
702  if (optional_string("$Squadron wing names:"))
703  {
705  }
706 
707  if (optional_string("$Team-versus-team wing names:"))
708  {
710  }
711  // end of wing stuff -------------------------------------------------
712 
713 
714  // set up the Num_teams variable accoriding to the game_type variable'
715  Num_teams = 1; // assume 1
716 
717  // multiplayer team v. team games have two teams. If we have three teams, we need to use
718  // a new mission mode!
720  Num_teams = 2;
721  }
722 
723  // Goober5000 - made this into a function since we use much the same technique for the briefing background
724  parse_custom_bitmap("$Load Screen 640:", "$Load Screen 1024:", pm->loading_screen[GR_640], pm->loading_screen[GR_1024]);
725 
726  strcpy_s(pm->skybox_model, "");
727  if (optional_string("$Skybox Model:"))
728  {
730  }
731 
733  if (optional_string("+Skybox Orientation:"))
734  {
736  }
737 
738  if (optional_string("+Skybox Flags:")){
739  pm->skybox_flags = 0;
740  stuff_int(&pm->skybox_flags);
741  }else{
743  }
744 
745  // Goober5000 - AI on a per-mission basis
746  The_mission.ai_profile = &Ai_profiles[Default_ai_profile];
747  if (optional_string("$AI Profile:"))
748  {
749  int index;
750  char temp[NAME_LENGTH];
751 
753  index = ai_profile_lookup(temp);
754 
755  if (index >= 0)
756  The_mission.ai_profile = &Ai_profiles[index];
757  else
758  WarningEx(LOCATION, "Mission: %s\nUnknown AI profile %s!", pm->name, temp );
759  }
760 
761  Assert( The_mission.ai_profile != NULL );
762 
763  // Kazan - player use AI at start?
765  Player_use_ai = 1;
766 
767  pm->sound_environment.id = -1;
768  if (optional_string("$Sound Environment:")) {
769  char preset[65] = { '\0' };
770  stuff_string(preset, F_NAME, sizeof(preset)-1);
771 
772  int preset_id = ds_eax_get_preset_id(preset);
773 
774  if (preset_id >= 0) {
775  sound_env_get(&pm->sound_environment, preset_id);
776  }
777 
778  // NOTE: values will be clamped properly when the effect is actually set
779 
780  if (optional_string("+Volume:")) {
782  }
783 
784  if (optional_string("+Damping:")) {
786  }
787 
788  if (optional_string("+Decay Time:")) {
790  }
791  }
792 }
793 
795 {
796  char temp[NAME_LENGTH];
797  Assert(pm != NULL);
798 
799  // alternate type names begin here
801  if(optional_string("#Alternate Types:")){
802  // read them all in
803  while(!optional_string("#end")){
804  required_string("$Alt:");
806 
807  // maybe store it
808  mission_parse_add_alt(temp);
809  }
810  }
811 
812  // callsigns begin here
814  if(optional_string("#Callsigns:")){
815  // read them all in
816  while(!optional_string("#end")){
817  required_string("$Callsign:");
819 
820  // maybe store it
822  }
823  }
824 
825  Player_starts = 0;
826  required_string("#Players");
827 
828  while (required_string_either("#Objects", "$")){
829  parse_player_info2(pm);
830  }
831 }
832 
834 {
835  char str[NAME_LENGTH];
836  int nt, i, total, list[MAX_SHIP_CLASSES * 4], list2[MAX_WEAPON_TYPES * 4];
837  team_data *ptr;
838 
839  // read in a ship/weapon pool for each team.
840  for ( nt = 0; nt < Num_teams; nt++ ) {
841  int num_choices;
842 
843  ptr = &Team_data[nt];
844  // get the shipname for single player missions
845  // MWA -- make this required later!!!!
846  if ( optional_string("$Starting Shipname:") )
848 
849  required_string("$Ship Choices:");
851 
852  // make sure we have a count which is divisible by four since four values are added for each ship
853  Assert((total%4) == 0);
854 
855  num_choices = 0;
856 
857  // only every 4th entry is actually a ship class.
858  for (i=0; i<total; i += 4) {
859  // in a campaign, see if the player is allowed the ships or not. Remove them from the
860  // pool if they are not allowed
862  if ( !Campaign.ships_allowed[list[i]] )
863  continue;
864  }
865 
866  ptr->ship_list[num_choices] = list[i];
867  // if the list isn't set by a variable leave the variable name empty
868  if (list[i+1] == -1) {
869  strcpy_s(ptr->ship_list_variables[num_choices], "") ;
870  }
871  else {
872  strcpy_s(ptr->ship_list_variables[num_choices],Sexp_variables[list[i+1]].variable_name);
873  }
874  ptr->ship_count[num_choices] = list[i+2];
875  ptr->loadout_total += list[i+2];
876 
877  // if the list isn't set by a variable leave the variable name empty
878  if (list[i+3] == -1) {
879  strcpy_s(ptr->ship_count_variables[num_choices], "");
880  }
881  else {
882  strcpy_s(ptr->ship_count_variables[num_choices], Sexp_variables[list[i+3]].variable_name);
883  }
884  num_choices++;
885  }
886  ptr->num_ship_choices = num_choices;
887 
888  ptr->default_ship = -1;
889  if (optional_string("+Default_ship:")) {
891  ptr->default_ship = ship_info_lookup(str);
892  if (-1 == ptr->default_ship) {
893  WarningEx(LOCATION, "Mission: %s\nUnknown default ship %s! Defaulting to %s.", pm->name, str, Ship_info[ptr->ship_list[0]].name );
894  ptr->default_ship = ptr->ship_list[0]; // default to 1st in list
895  }
896  // see if the player's default ship is an allowable ship (campaign only). If not, then what
897  // do we do? choose the first allowable one?
899  if ( !(Campaign.ships_allowed[ptr->default_ship]) ) {
900  for (i = 0; i < static_cast<int>(Ship_info.size()); i++ ) {
901  if ( Campaign.ships_allowed[i] ) {
902  ptr->default_ship = i;
903  break;
904  }
905  }
906  Assertion( i < static_cast<int>(Ship_info.size()), "Mission: %s: Could not find a valid default ship.\n", pm->name );
907  }
908  }
909  }
910 
911  if (ptr->default_ship == -1) // invalid or not specified, make first in list
912  ptr->default_ship = ptr->ship_list[0];
913 
914  required_string("+Weaponry Pool:");
916 
917  // make sure we have a count which is divisible by four since four values are added for each ship
918  Assert((total%4) == 0);
919  num_choices = 0;
920 
921  for (i = 0; i < total; i += 4) {
922  // in a campaign, see if the player is allowed the weapons or not. Remove them from the
923  // pool if they are not allowed
925  if ( !Campaign.weapons_allowed[list2[i]] ) {
926  continue;
927  }
928  }
929 
930  if ( (list2[i] >= 0) && (list2[i] < MAX_WEAPON_TYPES) ) {
931  // always allow the pool to be added in FRED, it is a verbal warning
932  // to let the mission dev know about the problem
933  if ( (Weapon_info[list2[i]].wi_flags & WIF_PLAYER_ALLOWED) || Fred_running ) {
934  ptr->weaponry_pool[num_choices] = list2[i];
935  ptr->weaponry_count[num_choices] = list2[i+2];
936 
937  // if the list isn't set by a variable leave the variable name empty
938  if (list2[i+1] == -1) {
939  strcpy_s(ptr->weaponry_pool_variable[num_choices], "");
940  }
941  else {
942  strcpy_s(ptr->weaponry_pool_variable[num_choices], Sexp_variables[list2[i+1]].variable_name);
943  }
944 
945  // if the list isn't set by a variable leave the variable name empty
946  if (list2[i+3] == -1) {
947  strcpy_s(ptr->weaponry_amount_variable[num_choices], "");
948  }
949  else {
950  strcpy_s(ptr->weaponry_amount_variable[num_choices], Sexp_variables[list2[i+3]].variable_name);
951  }
952  num_choices++;
953  }
954  else {
955  WarningEx(LOCATION, "Weapon '%s' in weapon pool isn't allowed on player loadout! Ignoring it ...\n", Weapon_info[i].name);
956  }
957  }
958  }
959  ptr->num_weapon_choices = num_choices;
960 
961  memset(ptr->weapon_required, 0, MAX_WEAPON_TYPES * sizeof(bool));
962  if (optional_string("+Required for mission:"))
963  {
964  int num_weapons;
965  int weapon_list_buf[MAX_WEAPON_TYPES];
966  num_weapons = stuff_int_list(weapon_list_buf, MAX_WEAPON_TYPES, WEAPON_LIST_TYPE);
967 
968  for (i = 0; i < num_weapons; i++)
969  ptr->weapon_required[weapon_list_buf[i]] = true;
970  }
971  }
972 
973  if ( nt != Num_teams )
974  Error(LOCATION, "Not enough ship/weapon pools for mission. There are %d teams and only %d pools.", Num_teams, nt);
975 }
976 
978 {
979  pm->cutscenes.clear();
980 
981  if (optional_string("#Cutscenes"))
982  {
983  mission_cutscene scene;
984 
985  while (!optional_string("#end"))
986  {
987  // this list should correspond to the MOVIE_* #defines
988  scene.type = optional_string_one_of(6,
989  "$Fiction Viewer Cutscene:",
990  "$Command Brief Cutscene:",
991  "$Briefing Cutscene:",
992  "$Pre-game Cutscene:",
993  "$Debriefing Cutscene:",
994  "$Campaign End Cutscene:");
995 
996  // no more cutscenes specified?
997  if (scene.type < 0)
998  break;
999 
1000  // get the cutscene file
1002 
1003  // get the sexp if we have one
1004  if (optional_string("+formula:"))
1005  scene.formula = get_sexp_main();
1006  else
1007  scene.formula = Locked_sexp_true;
1008 
1009  // add it
1010  pm->cutscenes.push_back(scene);
1011  }
1012  }
1013 }
1014 
1016 {
1017  if (optional_string("#Plot Info"))
1018  {
1019  char dummy_filespec[FILESPEC_LENGTH];
1020  char dummy_name[NAME_LENGTH];
1021 
1022  required_string("$Tour:");
1023  stuff_string(dummy_name, F_NAME, NAME_LENGTH);
1024 
1025  required_string("$Pre-Briefing Cutscene:");
1026  stuff_string(dummy_filespec, F_FILESPEC, FILESPEC_LENGTH);
1027 
1028  required_string("$Pre-Mission Cutscene:");
1029  stuff_string(dummy_filespec, F_FILESPEC, FILESPEC_LENGTH);
1030 
1031  required_string("$Next Mission Success:");
1032  stuff_string(dummy_name, F_NAME, NAME_LENGTH);
1033 
1034  required_string("$Next Mission Partial:");
1035  stuff_string(dummy_name, F_NAME, NAME_LENGTH);
1036 
1037  required_string("$Next Mission Failure:");
1038  stuff_string(dummy_name, F_NAME, NAME_LENGTH);
1039  }
1040 }
1041 
1043 {
1044  char junk[4096];
1045 
1046  if ( !optional_string("#Briefing Info") )
1047  return;
1048 
1049  required_string("$Briefing Voice 1:");
1050  stuff_string(junk, F_FILESPEC, sizeof(junk));
1051 
1052  required_string("$Briefing Text 1:");
1053  stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1054 
1055  required_string("$Briefing Voice 2:");
1056  stuff_string(junk, F_FILESPEC, sizeof(junk));
1057 
1058  required_string("$Briefing Text 2:");
1059  stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1060 
1061  required_string("$Briefing Voice 3:");
1062  stuff_string(junk, F_FILESPEC, sizeof(junk));
1063 
1064  required_string("$Briefing Text 3:");
1065  stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1066 
1067  required_string("$Debriefing Voice 1:");
1068  stuff_string(junk, F_FILESPEC, sizeof(junk));
1069 
1070  required_string("$Debriefing Text 1:");
1071  stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1072 
1073  required_string("$Debriefing Voice 2:");
1074  stuff_string(junk, F_FILESPEC, sizeof(junk));
1075 
1076  required_string("$Debriefing Text 2:");
1077  stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1078 
1079  required_string("$Debriefing Voice 3:");
1080  stuff_string(junk, F_FILESPEC, sizeof(junk));
1081 
1082  required_string("$Debriefing Text 3:");
1083  stuff_string(junk, F_MULTITEXTOLD, sizeof(junk));
1084 }
1085 
1090 {
1091  int i, index, num;
1092  char *ch;
1093  char temp[NAME_LENGTH];
1094 
1096 
1097  if ( !optional_string("#Music") ) {
1098  return;
1099  }
1100 
1101  required_string("$Event Music:");
1103 
1104  // Goober5000
1105  if (optional_string("$Substitute Event Music:"))
1107 
1108  required_string("$Briefing Music:");
1110 
1111  // Goober5000
1112  if (optional_string("$Substitute Briefing Music:"))
1114 
1115  // old stuff, apparently
1116  if (optional_string("$Debriefing Success Music:"))
1117  {
1120  if ((index >= 0) && ((Spooled_music[index].flags & SMF_VALID) || Fred_running)) {
1122  }
1123  }
1124 
1125  // not old, just added since it makes sense
1126  if (optional_string("$Debriefing Average Music:"))
1127  {
1130  if ((index >= 0) && ((Spooled_music[index].flags & SMF_VALID) || Fred_running)) {
1132  }
1133  }
1134 
1135  // old stuff
1136  if (optional_string("$Debriefing Fail Music:"))
1137  {
1140  if ((index >= 0) && ((Spooled_music[index].flags & SMF_VALID) || Fred_running)) {
1142  }
1143  }
1144 
1145  // new stuff
1146  if (optional_string("$Fiction Viewer Music:"))
1147  {
1150  }
1151 
1152  // Goober5000 - if briefing not specified in import, default to BRIEF1
1153  if (!stricmp(pm->briefing_music_name, "none") && (flags & MPF_IMPORT_FSM))
1154  strcpy_s(pm->briefing_music_name, "BRIEF1");
1155 
1156  // Goober5000 - old way of grabbing substitute music, but here for reverse compatibility
1157  if (optional_string("$Substitute Music:"))
1158  {
1160  Mp++;
1162  }
1163 
1164  // Goober5000 - if the mission is being imported, the substitutes are the specified tracks
1165  // (with FS1 prefixes) and we generate new specified tracks
1166  if (flags & MPF_IMPORT_FSM)
1167  {
1168  // no specified music?
1169  if (!stricmp(pm->event_music_name, "none"))
1170  goto done_event_music;
1171 
1172  // set the FS1 equivalent as the substitute
1175 
1176  // if we have Marauder, it's in FS2 as Deuteronomy, so we're done
1177  if (!stricmp(pm->event_music_name, "7: Marauder") && event_music_get_soundtrack_index("5: Deuteronomy") >= 0)
1178  {
1179  strcpy_s(pm->event_music_name, "5: Deuteronomy");
1180  goto done_event_music;
1181  }
1182 
1183  // search for something with the same track number
1184  strcpy_s(temp, pm->event_music_name);
1185  ch = strchr(temp, ':');
1186  if (ch != NULL)
1187  {
1188  *(ch + 1) = '\0';
1189 
1190  for (i = 0; i < Num_soundtracks; i++)
1191  {
1192  if (!strncmp(temp, Soundtracks[i].name, strlen(temp)))
1193  {
1195  goto done_event_music;
1196  }
1197  }
1198  }
1199 
1200  // last resort: pick a random track out of the 7 FS2 soundtracks
1201  num = (Num_soundtracks < 7) ? Num_soundtracks : 7;
1202  strcpy_s(pm->event_music_name, Soundtracks[rand() % num].name);
1203 
1204 
1205 done_event_music:
1206 
1207 
1208  // no specified music?
1209  if (!stricmp(pm->briefing_music_name, "none"))
1210  goto done_briefing_music;
1211 
1212  // set the FS1 equivalent as the substitute
1215 
1216  // Choco Mousse is the FS1 title soundtrack, so use Aquitaine in FS2
1217  if (!stricmp(pm->briefing_music_name, "Choco Mousse") && event_music_get_spooled_music_index("Aquitaine") >= 0)
1218  {
1219  strcpy_s(pm->briefing_music_name, "Aquitaine");
1220  goto done_briefing_music;
1221  }
1222 
1223  // we might have a match with the same track number
1225  goto done_briefing_music;
1226 
1227  // last resort: pick a random track out of the first 7 FS2 briefings (the regular ones)...
1228  num = (Num_music_files < 7) ? Num_music_files : 7;
1229  strcpy_s(pm->briefing_music_name, Spooled_music[rand() % num].name);
1230 
1231 
1232 done_briefing_music:
1233 
1234  /* NO-OP */ ;
1235  }
1236 
1237 
1238  // set the soundtrack, preferring the substitute in FS2 (not FRED!)
1240  if ((index >= 0) && (Soundtracks[index].flags & EMF_VALID) && !Fred_running)
1241  {
1243  }
1244  else
1245  {
1247  }
1248 
1249  // set the briefing, preferring the substitute in FS2 (not FRED!)
1251  if ((index >= 0) && (Spooled_music[index].flags & SMF_VALID) && !Fred_running)
1252  {
1254  }
1255  else
1256  {
1258  }
1259 }
1260 
1265 {
1267 
1268  if (optional_string("#Fiction Viewer"))
1269  {
1270  bool fiction_viewer_loaded = false;
1271 
1272  while (check_for_string("$File:"))
1273  {
1274  fiction_viewer_stage stage;
1275  memset(&stage, 0, sizeof(fiction_viewer_stage));
1276  stage.formula = Locked_sexp_true;
1277 
1278  required_string("$File:");
1280 
1281  if (optional_string("$Font:"))
1283 
1284  if (optional_string("$Voice:"))
1286 
1287  if (optional_string("$UI:"))
1289 
1290  parse_custom_bitmap("$Background 640:", "$Background 1024:", stage.background[GR_640], stage.background[GR_1024]);
1291 
1292  // get the sexp if we have one
1293  if (optional_string("$Formula:"))
1294  stage.formula = get_sexp_main();
1295 
1296  // now, store this stage
1297  Fiction_viewer_stages.push_back(stage);
1298 
1299  // see if this is the stage we want to display, then display it
1300  if (!Fred_running && !fiction_viewer_loaded && is_sexp_true(stage.formula))
1301  {
1303  fiction_viewer_loaded = true;
1304  }
1305  }
1306  }
1307 }
1308 
1313 {
1314  int stage;
1315 
1317  stage = 0;
1318 
1319  required_string("#Command Briefing");
1320 
1321  // Yarn - use the same code as for mission loading screens
1322  parse_custom_bitmap("$Background 640:", "$Background 1024:", Cur_cmd_brief->background[GR_640], Cur_cmd_brief->background[GR_1024]);
1323 
1324  while (optional_string("$Stage Text:")) {
1325  Assert(stage < CMD_BRIEF_STAGES_MAX);
1327 
1328  required_string("$Ani Filename:");
1330  if (optional_string("+Wave Filename:"))
1332  else
1333  Cur_cmd_brief->stage[stage].wave_filename[0] = 0;
1334 
1335  stage++;
1336  }
1337 
1338  Cur_cmd_brief->num_stages = stage;
1339 }
1340 
1342 {
1343  int i;
1344 
1345  cmd_brief_reset();
1346  // a hack follows because old missions don't have a command briefing
1347  if (required_string_either("#Command Briefing", "#Briefing"))
1348  return;
1349 
1350  for (i=0; i<Num_teams; i++) {
1352  parse_cmd_brief(pm);
1353  }
1354 }
1355 
1362 {
1363  int nt, i, j, stage_num = 0, icon_num = 0;
1364  brief_stage *bs;
1365  brief_icon *bi;
1366  briefing *bp;
1367 
1368  char not_used_text[MAX_ICON_TEXT_LEN];
1369 
1370  brief_reset();
1371 
1372  // MWA -- 2/3/98. we can now have multiple briefing and debriefings in a mission
1373  for ( nt = 0; nt < Num_teams; nt++ ) {
1374  if ( !optional_string("#Briefing") )
1375  break;
1376 
1377  bp = &Briefings[nt];
1378  required_string("$start_briefing");
1379 
1380  // Goober5000 - use the same code as for mission loading screens
1381  parse_custom_bitmap("$briefing_background_640:", "$briefing_background_1024:", bp->background[GR_640], bp->background[GR_1024]);
1382  parse_custom_bitmap("$ship_select_background_640:", "$ship_select_background_1024:", bp->ship_select_background[GR_640], bp->ship_select_background[GR_1024]);
1383  parse_custom_bitmap("$weapon_select_background_640:", "$weapon_select_background_1024:", bp->weapon_select_background[GR_640], bp->weapon_select_background[GR_1024]);
1384 
1385  required_string("$num_stages:");
1386  stuff_int(&bp->num_stages);
1388 
1389  stage_num = 0;
1390  while (required_string_either("$end_briefing", "$start_stage")) {
1391  required_string("$start_stage");
1392  Assert(stage_num < MAX_BRIEF_STAGES);
1393  bs = &bp->stages[stage_num++];
1394  required_string("$multi_text");
1395  stuff_string(bs->text, F_MULTITEXT, NULL);
1396  required_string("$voice:");
1398  required_string("$camera_pos:");
1399  stuff_vec3d(&bs->camera_pos);
1400  required_string("$camera_orient:");
1402  required_string("$camera_time:");
1403  stuff_int(&bs->camera_time);
1404 
1405  if ( optional_string("$num_lines:") ) {
1406  stuff_int(&bs->num_lines);
1407 
1408  if ( Fred_running ) {
1409  Assert(bs->lines!=NULL);
1410  } else {
1411  if ( bs->num_lines > 0 ) {
1412  bs->lines = (brief_line *)vm_malloc(sizeof(brief_line)*bs->num_lines);
1413  Assert(bs->lines!=NULL);
1414  }
1415  }
1416 
1417  for (i=0; i<bs->num_lines; i++) {
1418  required_string("$line_start:");
1419  stuff_int(&bs->lines[i].start_icon);
1420  required_string("$line_end:");
1421  stuff_int(&bs->lines[i].end_icon);
1422  }
1423  }
1424  else {
1425  bs->num_lines = 0;
1426  }
1427 
1428  required_string("$num_icons:");
1429  stuff_int(&bs->num_icons);
1430 
1431  if ( Fred_running ) {
1432  Assert(bs->icons!=NULL);
1433  } else {
1434  if ( bs->num_icons > 0 ) {
1435  bs->icons = (brief_icon *)vm_malloc(sizeof(brief_icon)*bs->num_icons);
1436  Assert(bs->icons!=NULL);
1437  }
1438  }
1439 
1440  if ( optional_string("$flags:") )
1441  stuff_int(&bs->flags);
1442  else
1443  bs->flags = 0;
1444 
1445  if ( optional_string("$formula:") )
1446  bs->formula = get_sexp_main();
1447  else
1448  bs->formula = Locked_sexp_true;
1449 
1450  Assert(bs->num_icons <= MAX_STAGE_ICONS );
1451 
1452  // static alias stuff - stupid, but it seems to be necessary
1453  static char *temp_team_names[MAX_IFFS];
1454  for (i = 0; i < Num_iffs; i++)
1455  temp_team_names[i] = Iff_info[i].iff_name;
1456 
1457  while (required_string_either("$end_stage", "$start_icon"))
1458  {
1459  required_string("$start_icon");
1460  Assert(icon_num < MAX_STAGE_ICONS);
1461  bi = &bs->icons[icon_num++];
1462 
1463  required_string("$type:");
1464  stuff_int(&bi->type);
1465 
1466  // Goober5000 - import
1467  if (flags & MPF_IMPORT_FSM)
1468  {
1469  // someone changed the jump node icon to a Knossos, so change it back
1470  if (bi->type == ICON_KNOSSOS_DEVICE)
1471  bi->type = ICON_JUMP_NODE;
1472 
1473  // change largeship to transport
1474  else if (bi->type == ICON_LARGESHIP)
1475  bi->type = ICON_TRANSPORT;
1476 
1477  // ditto
1478  else if (bi->type == ICON_LARGESHIP_WING)
1479  bi->type = ICON_TRANSPORT_WING;
1480  }
1481 
1482  find_and_stuff("$team:", &bi->team, F_NAME, temp_team_names, Num_iffs, "team name");
1483 
1484  find_and_stuff("$class:", &bi->ship_class, F_NAME, Ship_class_names, Ship_info.size(), "ship class");
1485 
1486  // Goober5000 - import
1487  if (flags & MPF_IMPORT_FSM)
1488  {
1489  // the Faustus is a largeship
1490  if (!strnicmp(Ship_info[bi->ship_class].name, "GTSC Faustus", 12))
1491  {
1492  if (bi->type == ICON_CRUISER)
1493  bi->type = ICON_LARGESHIP;
1494 
1495  else if (bi->type == ICON_CRUISER_WING)
1496  bi->type = ICON_LARGESHIP_WING;
1497  }
1498  // the Demon is a support ship :p
1499  else if (!strnicmp(Ship_info[bi->ship_class].name, "SD Demon", 8))
1500  {
1501  bi->type = ICON_SUPPORT_SHIP;
1502  }
1503  // the Hades is a supercap
1504  else if (!strnicmp(Ship_info[bi->ship_class].name, "GTD Hades", 9))
1505  {
1506  bi->type = ICON_SUPERCAP;
1507  }
1508  }
1509 
1510  required_string("$pos:");
1511  stuff_vec3d(&bi->pos);
1512 
1513  bi->label[0] = 0;
1514  if (optional_string("$label:"))
1516 
1517  if (optional_string("+id:")) {
1518  stuff_int(&bi->id);
1519  if (bi->id >= Cur_brief_id)
1520  Cur_brief_id = bi->id + 1;
1521 
1522  } else {
1523  bi->id = -1;
1524  for (i=0; i<stage_num-1; i++)
1525  for (j=0; j < bp->stages[i].num_icons; j++)
1526  {
1527  if (!stricmp(bp->stages[i].icons[j].label, bi->label))
1528  bi->id = bp->stages[i].icons[j].id;
1529  }
1530 
1531  if (bi->id < 0)
1532  bi->id = Cur_brief_id++;
1533  }
1534 
1535  bi->flags=0;
1536  int val;
1537  required_string("$hlight:");
1538  stuff_int(&val);
1539  if ( val>0 ) {
1540  bi->flags |= BI_HIGHLIGHT;
1541  }
1542 
1543  if (optional_string("$mirror:"))
1544  {
1545  stuff_int(&val);
1546  if ( val>0 ) {
1547  bi->flags |= BI_MIRROR_ICON;
1548  }
1549  }
1550 
1551  if (optional_string("$use wing icon:"))
1552  {
1553  stuff_int(&val);
1554  if ( val>0 ) {
1555  bi->flags |= BI_USE_WING_ICON;
1556  }
1557  }
1558 
1559  required_string("$multi_text");
1560  stuff_string(not_used_text, F_MULTITEXT, MAX_ICON_TEXT_LEN);
1561  required_string("$end_icon");
1562  } // end while
1563  Assert(bs->num_icons == icon_num);
1564  icon_num = 0;
1565  required_string("$end_stage");
1566  } // end while
1567 
1568  Assert(bp->num_stages == stage_num);
1569  required_string("$end_briefing");
1570  }
1571 
1572  if ( nt != Num_teams )
1573  Error(LOCATION, "Not enough briefings in mission file. There are %d teams and only %d briefings.", Num_teams, nt );
1574 }
1575 
1580 {
1581  int stage_num, nt;
1582  debriefing *db;
1583  debrief_stage *dbs;
1584 
1585  debrief_reset();
1586 
1587  // 2/3/98 -- MWA. We can now have multiple briefings and debriefings on a team
1588  for ( nt = 0; nt < Num_teams; nt++ ) {
1589 
1590  if ( !optional_string("#Debriefing_info") )
1591  break;
1592 
1593  stage_num = 0;
1594 
1595  db = &Debriefings[nt];
1596 
1597  // Yarn - use the same code as for mission loading screens
1598  parse_custom_bitmap("$Background 640:", "$Background 1024:", db->background[GR_640], db->background[GR_1024]);
1599 
1600  required_string("$Num stages:");
1601  stuff_int(&db->num_stages);
1603 
1604  while (required_string_either("#", "$Formula")) {
1605  Assert(stage_num < MAX_DEBRIEF_STAGES);
1606  dbs = &db->stages[stage_num++];
1607  required_string("$Formula:");
1608  dbs->formula = get_sexp_main();
1609  required_string("$multi text");
1610  stuff_string(dbs->text, F_MULTITEXT, NULL);
1611  required_string("$Voice:");
1613  required_string("$Recommendation text:");
1615  } // end while
1616 
1617  Assert(db->num_stages == stage_num);
1618  }
1619 
1620  if ( nt != Num_teams )
1621  Error(LOCATION, "Not enough debriefings for mission. There are %d teams and only %d debriefings;\n", Num_teams, nt );
1622 }
1623 
1625 {
1626  object *objp = p_objp->created_object;
1627  ship *shipp = &Ships[objp->instance];
1628  object *knossos_objp = NULL;
1629 
1630  // Assume no valid knossos device
1631  shipp->special_warpin_objnum = -1;
1632 
1633  // find knossos device
1634  for (ship_obj *so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so))
1635  {
1636  object *ship_objp = &Objects[so->objnum];
1637 
1638  if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE)
1639  {
1640  // be close to the right device (allow multiple knossoses)
1641  if ( vm_vec_dist_quick(&ship_objp->pos, &p_objp->pos) < 2.0f*(ship_objp->radius + objp->radius) )
1642  {
1643  knossos_objp = ship_objp;
1644  break;
1645  }
1646  }
1647  }
1648 
1649  if (knossos_objp == NULL)
1650  return;
1651 
1652  // set ship special_warpin_objnum
1653  shipp->special_warpin_objnum = OBJ_INDEX(knossos_objp);
1654 
1655  // position self for warp on plane of device
1656  vec3d new_point;
1657  polymodel *pm = model_get(Ship_info[shipp->ship_info_index].model_num);
1658 
1659  float dist = fvi_ray_plane(&new_point, &knossos_objp->pos, &knossos_objp->orient.vec.fvec, &p_objp->pos, &p_objp->orient.vec.fvec, 0.0f);
1660  float desired_dist = -pm->mins.xyz.z;
1661  vm_vec_scale_add2(&objp->pos, &objp->orient.vec.fvec, (dist - desired_dist));
1662 
1663  // if ship is BIG or HUGE, make it go through the center of the knossos
1664  if (Ship_info[shipp->ship_info_index].flags & SIF_HUGE_SHIP)
1665  {
1666  vec3d offset;
1667  vm_vec_sub(&offset, &knossos_objp->pos, &new_point);
1668  vm_vec_add2(&knossos_objp->pos, &offset);
1669  }
1670 }
1671 
1676 {
1677  int dockpoint, parent_dockpoint;
1678  char *dockpoint_name, *parent_dockpoint_name;
1679  object *objp, *parent_objp;
1680 
1681  // get the actual in-game objects that will be docked
1682  objp = pobjp->created_object;
1683  parent_objp = parent_pobjp->created_object;
1684 
1685  // check valid
1686  if (!objp || !parent_objp)
1687  {
1688  Int3();
1689  return;
1690  }
1691 
1692  // get dockpoint names
1693  dockpoint_name = dock_find_dockpoint_used_by_object(pobjp, parent_pobjp);
1694  parent_dockpoint_name = dock_find_dockpoint_used_by_object(parent_pobjp, pobjp);
1695 
1696  // check valid
1697  if (!dockpoint_name || !parent_dockpoint_name)
1698  {
1699  Int3();
1700  return;
1701  }
1702 
1703  // resolve names to dockpoints
1704  dockpoint = model_find_dock_name_index(Ship_info[Ships[objp->instance].ship_info_index].model_num, dockpoint_name);
1705  parent_dockpoint = model_find_dock_name_index(Ship_info[Ships[parent_objp->instance].ship_info_index].model_num, parent_dockpoint_name);
1706 
1707  // check valid
1708  if ((dockpoint < 0) || (parent_dockpoint < 0))
1709  {
1710  Int3();
1711  return;
1712  }
1713 
1714  // dock them
1715  nprintf(("AI", "Initially docked: %s to parent %s\n", Ships[objp->instance].ship_name, Ships[parent_objp->instance].ship_name));
1716  ai_dock_with_object(objp, dockpoint, parent_objp, parent_dockpoint, AIDO_DOCK_NOW);
1717 }
1718 
1720 
1721 // Goober5000
1723 {
1724  // create the object
1725  parse_create_object_sub(pobjp);
1726 
1727  // remove the object from the arrival list
1728  // (don't remove the leader, because he'll be removed the usual way)
1729  if (pobjp != infop->parameter_variables.objp_value)
1730  {
1731  // If an object is present at the beginning of the mission, it may not have been put on the arrival
1732  // list. This is totally dependent on the order the objects are parsed in the mission file because
1733  // that is the order in which they are created. All the objects in a docked group are created
1734  // simultaneously when and only when the leader is created.
1735 
1736  // if it's on the list, remove it
1737  if (parse_object_on_arrival_list(pobjp))
1738  list_remove(&Ship_arrival_list, pobjp);
1739  }
1740 }
1741 
1747 {
1748  object *objp;
1749 
1750  // if this guy is part of a dock group, create the entire group, starting with the leader
1751  if (object_is_docked(pobjp))
1752  {
1754 
1755  // we should only be calling this for dock leaders, because the dock leader
1756  // governs the creation of his entire group
1757  Assert((pobjp->flags & P_SF_DOCK_LEADER));
1758 
1759  // if the leader will be destroyed before the mission starts, then *only* create the leader;
1760  // don't create the rest of the group (this is what retail did)
1761  if (pobjp->destroy_before_mission_time >= 0)
1762  return parse_create_object_sub(pobjp);
1763 
1764  // store the leader as a parameter
1765  dfi.parameter_variables.objp_value = pobjp;
1766 
1767  // Just as we couldn't create the parse dock trees until we had parsed them all,
1768  // we can't dock the in-game objects until we have created them all. :p
1769 
1770  // create all the objects
1772 
1773  // dock all the objects
1774  dock_dock_docked_objects(pobjp);
1775 
1776  // now clear the handled flags
1778  }
1779  // create normally
1780  else
1781  {
1782  parse_create_object_sub(pobjp);
1783  }
1784 
1785  // get the main object
1786  objp = pobjp->created_object;
1787 
1788  // warp it in (moved from parse_create_object_sub)
1789  if ((Game_mode & GM_IN_MISSION) && (!Fred_running) && (!Game_restoring))
1790  {
1791  if ((Ships[objp->instance].wingnum < 0) && (pobjp->arrival_location != ARRIVE_FROM_DOCK_BAY))
1792  {
1793  shipfx_warpin_start(objp);
1794  }
1795  }
1796 
1797  // return the main object's objnum
1798  return OBJ_INDEX(objp);
1799 }
1800 
1801 void parse_bring_in_docked_wing(p_object *p_objp, int wingnum, int shipnum);
1802 
1808 {
1809  int i, j, k, objnum, shipnum;
1810  int anchor_objnum = -1;
1811  ai_info *aip;
1812  ship_subsys *ptr;
1813  ship *shipp;
1814  ship_info *sip;
1815  subsys_status *sssp;
1816  ship_weapon *wp;
1817 
1818  // texture replacements
1819  polymodel *pm;
1820 
1821  MONITOR_INC(NumShipArrivals, 1);
1822 
1823  // base level creation - need ship name in case of duplicate textures
1824  objnum = ship_create(&p_objp->orient, &p_objp->pos, p_objp->ship_class, p_objp->name);
1825  Assert(objnum != -1);
1826  shipnum = Objects[objnum].instance;
1827 
1828  shipp = &Ships[shipnum];
1829  sip = &Ship_info[shipp->ship_info_index];
1830 
1831  // Goober5000 - make the parse object aware of what it was created as
1832  p_objp->created_object = &Objects[objnum];
1833 
1834  // Goober5000 - if this object is being created because he's docked to something,
1835  // and he's in a wing, then mark the wing as having arrived
1836  if (object_is_docked(p_objp) && !(p_objp->flags & P_SF_DOCK_LEADER) && (p_objp->wingnum >= 0))
1837  {
1838  if (!Fred_running)
1839  parse_bring_in_docked_wing(p_objp, p_objp->wingnum, shipnum);
1840  }
1841 
1842  // if arriving through knossos, adjust objpj->pos to plane of knossos and set flag
1843  // special warp is single player only
1844  if ((p_objp->flags & P_KNOSSOS_WARP_IN) && !(Game_mode & GM_MULTIPLAYER))
1845  {
1846  if (!Fred_running)
1848  }
1849 
1850  shipp->group = p_objp->group;
1851  shipp->team = p_objp->team;
1852  strcpy_s(shipp->ship_name, p_objp->name);
1853  shipp->escort_priority = p_objp->escort_priority;
1855  shipp->special_exp_damage = p_objp->special_exp_damage;
1856  shipp->special_exp_blast = p_objp->special_exp_blast;
1857  shipp->special_exp_inner = p_objp->special_exp_inner;
1858  shipp->special_exp_outer = p_objp->special_exp_outer;
1859  shipp->use_shockwave = p_objp->use_shockwave;
1862 
1863  shipp->special_hitpoints = p_objp->special_hitpoints;
1864  shipp->special_shield = p_objp->special_shield;
1865 
1866  for (i=0;i<MAX_IFFS;i++)
1867  {
1868  for (j=0;j<MAX_IFFS;j++)
1869  {
1870  shipp->ship_iff_color[i][j] = p_objp->alt_iff_color[i][j];
1871  }
1872  }
1873 
1876  shipp->max_shield_recharge = p_objp->max_shield_recharge;
1877 
1878  // Goober5000 - ugh, this is really stupid having to do this here; if the
1879  // ship creation code was better organized this wouldn't be necessary
1880  if (shipp->special_hitpoints > 0)
1881  {
1882  float hull_factor = shipp->ship_max_hull_strength / sip->max_hull_strength;
1883  ship_subsys *ss;
1884 
1885  for (ss = GET_FIRST(&shipp->subsys_list); ss != END_OF_LIST(&shipp->subsys_list) && ss != NULL; ss = GET_NEXT(ss))
1886  {
1887  ss->max_hits *= hull_factor;
1888 
1889  if (Fred_running)
1890  ss->current_hits = 0.0f;
1891  else
1892  ss->current_hits = ss->max_hits;
1893  }
1894 
1896  }
1897 
1898  // Goober5000 - this is also stupid; this is in ship_set but it needs to be done here because of the special hitpoints mod
1899  Objects[objnum].hull_strength = shipp->ship_max_hull_strength;
1900 
1901  shipp->respawn_priority = p_objp->respawn_priority;
1902 
1903  // if this is a multiplayer dogfight game, and its from a player wing, make it team traitor
1904  if (MULTI_DOGFIGHT && (p_objp->wingnum >= 0))
1905  {
1906  for (i = 0; i < MAX_STARTING_WINGS; i++)
1907  {
1908  if (!stricmp(Starting_wing_names[i], Wings[p_objp->wingnum].name))
1909  shipp->team = Iff_traitor;
1910  }
1911  }
1912 
1913  if (!Fred_running)
1914  {
1915  ship_assign_sound(&Ships[shipnum]);
1916  }
1917 
1918  aip = &(Ai_info[shipp->ai_index]);
1919  aip->behavior = p_objp->behavior;
1920  aip->mode = aip->behavior;
1921 
1922  // make sure aim_safety has its submode defined
1923  if (aip->mode == AIM_SAFETY) {
1924  aip->submode = AISS_1;
1925  }
1926 
1927  // alternate stuff
1928  shipp->alt_type_index = p_objp->alt_type_index;
1929  shipp->callsign_index = p_objp->callsign_index;
1930 
1931  aip->ai_class = p_objp->ai_class;
1932  shipp->weapons.ai_class = p_objp->ai_class; // Fred uses this instead of above.
1933  //Fixes a bug where the AI class attributes were not copied if the AI class was set in the mission.
1934  if (The_mission.ai_profile->flags & AIPF_FIX_AI_CLASS_BUG)
1935  ship_set_new_ai_class(shipnum, p_objp->ai_class);
1936 
1937  // must reset the number of ai goals when the object is created
1938  for (i = 0; i < MAX_AI_GOALS; i++)
1939  {
1940  aip->goals[i].ai_mode = AI_GOAL_NONE;
1941  aip->goals[i].signature = -1;
1942  aip->goals[i].priority = -1;
1943  aip->goals[i].flags = 0;
1944  }
1945 
1946  shipp->cargo1 = p_objp->cargo1;
1947 
1948  shipp->arrival_location = p_objp->arrival_location;
1949  shipp->arrival_distance = p_objp->arrival_distance;
1950  shipp->arrival_anchor = p_objp->arrival_anchor;
1951  shipp->arrival_path_mask = p_objp->arrival_path_mask;
1952  shipp->arrival_cue = p_objp->arrival_cue;
1953  shipp->arrival_delay = p_objp->arrival_delay;
1954  shipp->departure_location = p_objp->departure_location;
1955  shipp->departure_anchor = p_objp->departure_anchor;
1956  shipp->departure_path_mask = p_objp->departure_path_mask;
1957  shipp->departure_cue = p_objp->departure_cue;
1958  shipp->departure_delay = p_objp->departure_delay;
1959  shipp->wingnum = p_objp->wingnum;
1960  shipp->hotkey = p_objp->hotkey;
1961  shipp->score = p_objp->score;
1962  shipp->assist_score_pct = p_objp->assist_score_pct;
1963  shipp->persona_index = p_objp->persona_index;
1964  if (Ship_info[shipp->ship_info_index].uses_team_colors && !p_objp->team_color_setting.empty())
1965  shipp->team_name = p_objp->team_color_setting;
1966 
1967  // reset texture animations
1969 
1970  // handle the replacement textures
1971  if (!p_objp->replacement_textures.empty())
1972  {
1973  shipp->ship_replacement_textures = (int *) vm_malloc( MAX_REPLACEMENT_TEXTURES * sizeof(int));
1974 
1975  for (i = 0; i < MAX_REPLACEMENT_TEXTURES; i++)
1976  shipp->ship_replacement_textures[i] = -1;
1977  }
1978 
1979  // now fill them in
1980  for (SCP_vector<texture_replace>::iterator tr = p_objp->replacement_textures.begin(); tr != p_objp->replacement_textures.end(); ++tr)
1981  {
1982  pm = model_get(sip->model_num);
1983 
1984  // look for textures
1985  for (j = 0; j < pm->n_textures; j++)
1986  {
1987  texture_map *tmap = &pm->maps[j];
1988 
1989  int tnum = tmap->FindTexture(tr->old_texture);
1990  if(tnum > -1)
1991  shipp->ship_replacement_textures[j * TM_NUM_TYPES + tnum] = tr->new_texture_id;
1992  }
1993  }
1994 
1995  // Copy across the alt classes (if any) for FRED
1996  if (Fred_running) {
1997  shipp->s_alt_classes = p_objp->alt_classes;
1998  }
1999 
2000  // check the parse object's flags for possible things to set on this newly created ship
2001  resolve_parse_flags(&Objects[objnum], p_objp->flags, p_objp->flags2);
2002 
2003 
2004  // other flag checks
2006 
2007  // forcing the shields on or off depending on flags -- but only if shield strength supports it
2008 
2009  // no strength means we can't have shields, period
2010  if (p_objp->ship_max_shield_strength == 0.0f)
2011  Objects[objnum].flags |= OF_NO_SHIELDS;
2012  // force shields on means we have them regardless of other flags; per r5332 this ranks above the next check
2013  else if (p_objp->flags2 & P2_OF_FORCE_SHIELDS_ON)
2014  Objects[objnum].flags &= ~OF_NO_SHIELDS;
2015  // intrinsic no-shields means we have them off in-game
2016  else if (!Fred_running && (sip->flags2 & SIF2_INTRINSIC_NO_SHIELDS))
2017  Objects[objnum].flags |= OF_NO_SHIELDS;
2018 
2019  // don't set the flag if the mission is ongoing in a multiplayer situation. This will be set by the players in the
2020  // game only before the game or during respawning.
2021  // MWA -- changed the next line to remove the !(Game_mode & GM_MULTIPLAYER). We shouldn't be setting
2022  // this flag in single player mode -- it gets set in post process mission.
2023  if ((p_objp->flags & P_OF_PLAYER_START) && (Fred_running || ((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION))))
2024  Objects[objnum].flags |= OF_PLAYER_SHIP;
2025 
2026  // a couple of ai_info flags. Also, do a reasonable default for the kamikaze damage regardless of
2027  // whether this flag is set or not
2028  if (p_objp->flags & P_AIF_KAMIKAZE)
2029  {
2031  Ai_info[shipp->ai_index].kamikaze_damage = p_objp->kamikaze_damage;
2032  }
2033 
2034  if (p_objp->flags & P_AIF_NO_DYNAMIC)
2036 
2037  if (p_objp->flags & P_SF_RED_ALERT_STORE_STATUS)
2038  {
2039  if (!(Game_mode & GM_MULTIPLAYER)) {
2040  shipp->flags |= SF_RED_ALERT_STORE_STATUS;
2041  }
2042  }
2043 
2044  if (p_objp->flags & P_KNOSSOS_WARP_IN)
2045  {
2046  Objects[objnum].flags |= OF_SPECIAL_WARPIN;
2048  }
2049 
2050  // set the orders that this ship will accept. It will have already been set to default from the
2051  // ship create code, so only set them if the parse object flags say they are unique
2052  if (p_objp->flags & P_SF_USE_UNIQUE_ORDERS)
2053  {
2054  shipp->orders_accepted = p_objp->orders_accepted;
2055 
2056  // MWA 5/15/98 -- Added the following debug code because some orders that ships
2057  // will accept were apparently written out incorrectly with Fred. This Int3() should
2058  // trap these instances.
2059 #ifndef NDEBUG
2060  if (Fred_running)
2061  {
2062  int default_orders, remaining_orders;
2063 
2064  default_orders = ship_get_default_orders_accepted(&Ship_info[shipp->ship_info_index]);
2065  remaining_orders = p_objp->orders_accepted & ~default_orders;
2066  if (remaining_orders)
2067  {
2068  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);
2069  }
2070  }
2071 #endif
2072  }
2073 
2074  if (p_objp->flags & P_SF_DOCK_LEADER)
2075  shipp->flags |= SF_DOCK_LEADER;
2076 
2077  if (p_objp->flags & P_SF_WARP_BROKEN)
2078  shipp->flags |= SF_WARP_BROKEN;
2079 
2080  if (p_objp->flags & P_SF_WARP_NEVER)
2081  shipp->flags |= SF_WARP_NEVER;
2083 
2084 
2085  // if ship is in a wing, and the wing's no_warp_effect flag is set, then set the equivalent
2086  // flag for the ship
2087  if ((shipp->wingnum != -1) && (Wings[shipp->wingnum].flags & WF_NO_ARRIVAL_WARP))
2088  shipp->flags |= SF_NO_ARRIVAL_WARP;
2089 
2090  if ((shipp->wingnum != -1) && (Wings[shipp->wingnum].flags & WF_NO_DEPARTURE_WARP))
2091  shipp->flags |= SF_NO_DEPARTURE_WARP;
2092 
2093  // ditto for Kazan
2094  if ((shipp->wingnum != -1) && (Wings[shipp->wingnum].flags & WF_NAV_CARRY))
2095  shipp->flags2 |= SF2_NAVPOINT_CARRY;
2096 
2097  // if the wing index and wing pos are set for this parse object, set them for the ship. This
2098  // is useful in multiplayer when ships respawn
2100  shipp->wing_status_wing_pos = p_objp->wing_status_wing_pos;
2101 
2102 
2103  // set up the ai_goals for this object -- all ships created here are AI controlled.
2104  if (p_objp->ai_goals != -1)
2105  {
2106  int sexp;
2107 
2108  // set up the ai goals for this object.
2109  for (sexp = CDR(p_objp->ai_goals); sexp != -1; sexp = CDR(sexp))
2111 
2112  // free the sexpression nodes only for non-wing ships. wing code will handle its own case
2113  if (p_objp->wingnum < 0)
2114  free_sexp2(p_objp->ai_goals); // free up sexp nodes for reuse, since they aren't needed anymore.
2115  }
2116 
2117  Assert(sip->model_num != -1);
2118 
2119  // initialize subsystem statii here. The subsystems are given a percentage damaged. So a percent value
2120  // of 20% means that the subsystem is 20% damaged (*not* 20% of max hits). This is opposite the way
2121  // that the initial velocity/hull strength/shields work
2122  i = p_objp->subsys_count;
2123  while (i--)
2124  {
2125  sssp = &Subsys_status[p_objp->subsys_index + i];
2126  if (!stricmp(sssp->name, NOX("Pilot")))
2127  {
2128  wp = &shipp->weapons;
2129  if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
2130  {
2131  for (j=k=0; j<MAX_SHIP_PRIMARY_BANKS; j++)
2132  {
2133  if ((sssp->primary_banks[j] >= 0) || Fred_running)
2134  wp->primary_bank_weapons[k++] = sssp->primary_banks[j];
2135  }
2136 
2137  if (Fred_running)
2139  else
2140  wp->num_primary_banks = k;
2141  }
2142 
2143  if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
2144  {
2145  for (j = k = 0; j < MAX_SHIP_SECONDARY_BANKS; j++)
2146  {
2147  if ((sssp->secondary_banks[j] >= 0) || Fred_running)
2148  wp->secondary_bank_weapons[k++] = sssp->secondary_banks[j];
2149  }
2150 
2151  if (Fred_running)
2153  else
2154  wp->num_secondary_banks = k;
2155  }
2156 
2157  // primary weapons too - Goober5000
2158  for (j = 0; j < wp->num_primary_banks; j++)
2159  {
2160  if (Fred_running)
2161  {
2162  wp->primary_bank_ammo[j] = sssp->primary_ammo[j];
2163  }
2165  {
2167  "Primary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
2168  shipp->ship_name, sssp->name, j, Weapon_info[wp->primary_bank_weapons[j]].name);
2169 
2170  int capacity = fl2i(sssp->primary_ammo[j]/100.0f * sip->primary_bank_ammo_capacity[j] + 0.5f);
2171  wp->primary_bank_ammo[j] = fl2i(capacity / Weapon_info[wp->primary_bank_weapons[j]].cargo_size + 0.5f);
2172  }
2173  }
2174 
2175  for (j = 0; j < wp->num_secondary_banks; j++)
2176  {
2177  if (Fred_running)
2178  {
2179  wp->secondary_bank_ammo[j] = sssp->secondary_ammo[j];
2180  }
2181  else if (wp->secondary_bank_weapons[j] >= 0)
2182  {
2184  "Secondary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
2185  shipp->ship_name, sssp->name, j, Weapon_info[wp->secondary_bank_weapons[j]].name);
2186 
2187  int capacity = fl2i(sssp->secondary_ammo[j]/100.0f * sip->secondary_bank_ammo_capacity[j] + 0.5f);
2188  wp->secondary_bank_ammo[j] = fl2i(capacity / Weapon_info[wp->secondary_bank_weapons[j]].cargo_size + 0.5f);
2189  }
2190  }
2191 
2192  continue;
2193  }
2194 
2195  ptr = GET_FIRST(&shipp->subsys_list);
2196  while (ptr != END_OF_LIST(&shipp->subsys_list))
2197  {
2198  // check the mission flag to possibly free all beam weapons - Goober5000, taken from SEXP.CPP
2199  if (The_mission.flags & MISSION_FLAG_BEAM_FREE_ALL_BY_DEFAULT)
2200  {
2201  // mark all turrets as beam free
2202  if(ptr->system_info->type == SUBSYSTEM_TURRET)
2203  {
2205  ptr->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
2206  }
2207  }
2208 
2210  {
2211  // mark all turrets as locked
2212  if(ptr->system_info->type == SUBSYSTEM_TURRET)
2213  {
2215  }
2216  }
2217 
2218  if (!subsystem_stricmp(ptr->system_info->subobj_name, sssp->name))
2219  {
2220  if (Fred_running)
2221  {
2222  ptr->current_hits = sssp->percent;
2223  ptr->max_hits = 100.0f;
2224  }
2225  else
2226  {
2228 
2229  float new_hits = ptr->max_hits * (100.0f - sssp->percent) / 100.f;
2230  if (!(ptr->flags & SSF_NO_AGGREGATE)) {
2231  shipp->subsys_info[ptr->system_info->type].aggregate_current_hits -= (ptr->max_hits - new_hits);
2232  }
2233 
2234  if ((100.0f - sssp->percent) < 0.5)
2235  {
2236  ptr->current_hits = 0.0f;
2237  ptr->submodel_info_1.blown_off = 1;
2238  }
2239  else
2240  {
2241  ptr->current_hits = new_hits;
2242  }
2243  }
2244 
2245  if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE)
2246  for (j=0; j<MAX_SHIP_PRIMARY_BANKS; j++)
2247  ptr->weapons.primary_bank_weapons[j] = sssp->primary_banks[j];
2248 
2250  for (j=0; j<MAX_SHIP_SECONDARY_BANKS; j++)
2251  ptr->weapons.secondary_bank_weapons[j] = sssp->secondary_banks[j];
2252 
2253  // Goober5000
2254  for (j = 0; j < ptr->weapons.num_primary_banks; j++)
2255  {
2256  if (Fred_running) {
2257  ptr->weapons.primary_bank_ammo[j] = sssp->primary_ammo[j];
2260  "Primary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
2261  shipp->ship_name, sssp->name, j, Weapon_info[ptr->weapons.primary_bank_weapons[j]].name);
2262 
2263  int capacity = fl2i(sssp->primary_ammo[j]/100.0f * ptr->weapons.primary_bank_capacity[j] + 0.5f);
2264  ptr->weapons.primary_bank_ammo[j] = fl2i(capacity / Weapon_info[ptr->weapons.primary_bank_weapons[j]].cargo_size + 0.5f);
2265  }
2266  }
2267 
2268  for (j = 0; j < ptr->weapons.num_secondary_banks; j++)
2269  {
2270  if (Fred_running) {
2271  ptr->weapons.secondary_bank_ammo[j] = sssp->secondary_ammo[j];
2272  } else if (ptr->weapons.secondary_bank_weapons[j] >= 0) {
2274  "Secondary weapon cargo size <= 0. Ship (%s) Subsystem (%s) Bank (%i) Weapon (%s)",
2275  shipp->ship_name, sssp->name, j, Weapon_info[ptr->weapons.secondary_bank_weapons[j]].name);
2276 
2277  int capacity = fl2i(sssp->secondary_ammo[j]/100.0f * ptr->weapons.secondary_bank_capacity[j] + 0.5f);
2279  }
2280  }
2281 
2282  ptr->subsys_cargo_name = sssp->subsys_cargo_name;
2283 
2284  if (sssp->ai_class != SUBSYS_STATUS_NO_CHANGE)
2285  ptr->weapons.ai_class = sssp->ai_class;
2286 
2287  ptr->turret_best_weapon = -1;
2288  ptr->turret_animation_position = MA_POS_NOT_SET; // model animation position is not set
2289  ptr->turret_animation_done_time = 0;
2290  }
2291 
2292  ptr = GET_NEXT(ptr);
2293  }
2294  }
2295 
2296  // initial hull strength, shields, and velocity are all expressed as a percentage of the max value/
2297  // so a initial_hull value of 90% means 90% of max. This way is opposite of how subsystems are dealt
2298  // with
2299  if (Fred_running)
2300  {
2301  Objects[objnum].phys_info.speed = (float) p_objp->initial_velocity;
2302  Objects[objnum].hull_strength = (float) p_objp->initial_hull;
2303  Objects[objnum].shield_quadrant[0] = (float) p_objp->initial_shields;
2304 
2305  }
2306  else
2307  {
2308  int max_allowed_sparks, num_sparks, iLoop;
2309 
2310  Objects[objnum].hull_strength = p_objp->initial_hull * shipp->ship_max_hull_strength / 100.0f;
2311  for (iLoop = 0; iLoop<Objects[objnum].n_quadrants; iLoop++)
2312  {
2313  Objects[objnum].shield_quadrant[iLoop] = (float) (shipp->max_shield_recharge * p_objp->initial_shields * get_max_shield_quad(&Objects[objnum]) / 100.0f);
2314  }
2315 
2316  // initial velocities now do not apply to ships which warp in after mission starts
2317  // WMC - Make it apply for ships with IN_PLACE_ANIM type
2318  // zookeeper - Also make it apply for hyperspace warps
2319  if (!(Game_mode & GM_IN_MISSION) || (sip->warpin_type == WT_IN_PLACE_ANIM || sip->warpin_type == WT_HYPERSPACE))
2320  {
2321  Objects[objnum].phys_info.speed = (float) p_objp->initial_velocity * sip->max_speed / 100.0f;
2322  Objects[objnum].phys_info.vel.xyz.z = Objects[objnum].phys_info.speed;
2323  Objects[objnum].phys_info.prev_ramp_vel = Objects[objnum].phys_info.vel;
2324  Objects[objnum].phys_info.desired_vel = Objects[objnum].phys_info.vel;
2325  }
2326 
2327  // recalculate damage of subsystems
2329 
2330  // create sparks on a ship whose hull is damaged. We will create two sparks for every 20%
2331  // of hull damage done. 100 means no sparks. between 80 and 100 do two sparks. 60 and 80 is
2332  // four, etc.
2333  pm = model_get(sip->model_num);
2334  max_allowed_sparks = get_max_sparks(&Objects[objnum]);
2335  num_sparks = (int)((100.0f - p_objp->initial_hull) / 5.0f);
2336  if (num_sparks > max_allowed_sparks)
2337  num_sparks = max_allowed_sparks;
2338 
2339  for (iLoop = 0; iLoop < num_sparks; iLoop++)
2340  {
2341  vec3d v1, v2;
2342 
2343  // DA 10/20/98 - sparks must be chosen on the hull and not any submodel
2344  if ( Cmdline_old_collision_sys ) {
2345  submodel_get_two_random_points(sip->model_num, pm->detail[0], &v1, &v2);
2346  } else {
2347  submodel_get_two_random_points_better(sip->model_num, pm->detail[0], &v1, &v2);
2348  }
2349  ship_hit_sparks_no_rotate(&Objects[objnum], &v1);
2350  }
2351  }
2352 
2353  // in mission, we add a log entry -- set ship positions for ships not in wings, and then do
2354  // warpin effect
2355  if ((Game_mode & GM_IN_MISSION) && (!Fred_running))
2356  {
2358 
2359  if (!Game_restoring)
2360  {
2361  // if this ship isn't in a wing, determine its arrival location
2362  if (shipp->wingnum == -1)
2363  {
2364  int location;
2365 
2366  // multiplayer clients set the arrival location of ships to be at location since their
2367  // position has already been determined. Don't actually set the variable since we
2368  // don't want the warp effect to show if coming from a dock bay.
2369  location = p_objp->arrival_location;
2370 
2371  if (MULTIPLAYER_CLIENT)
2372  location = ARRIVE_AT_LOCATION;
2373 
2374  anchor_objnum = mission_set_arrival_location(p_objp->arrival_anchor, location, p_objp->arrival_distance, objnum, p_objp->arrival_path_mask, NULL, NULL);
2375 
2376  // Goober5000 - warpin start moved to parse_create_object
2377  }
2378  }
2379 
2380  // possibly add this ship to a hotkey set
2381  // Ships can now have both a ship-hotkey and a wing-hotkey -- FSF
2382  if (shipp->hotkey != -1)
2384  if ((shipp->wingnum != -1) && (Wings[shipp->wingnum].hotkey != -1))
2386 
2387  // possibly add this ship to the hud escort list
2388  if (shipp->flags & SF_ESCORT)
2389  hud_add_remove_ship_escort(objnum, 1);
2390  }
2391 
2392  // for multiplayer games, make a call to the network code to assign the object signature
2393  // of the newly created object. The network host of the netgame will always assign a signature
2394  // to a newly created object. The network signature will get to the clients of the game in
2395  // different manners depending on whether or not an individual ship or a wing was created.
2396  if (Game_mode & GM_MULTIPLAYER)
2397  {
2398  Objects[objnum].net_signature = p_objp->net_signature;
2399 
2400  // Goober5000 - for an initially docked group, only send the packet for the dock leader... this is necessary so that the
2401  // docked hierarchy of objects can be created in the right order on the client side
2402  if (!object_is_docked(p_objp) || (p_objp->flags & P_SF_DOCK_LEADER))
2403  {
2404  if ((Game_mode & GM_IN_MISSION) && MULTIPLAYER_MASTER && (p_objp->wingnum == -1))
2405  send_ship_create_packet(&Objects[objnum], (p_objp == Arriving_support_ship) ? 1 : 0);
2406  }
2407  }
2408 
2409  if (Game_mode & GM_IN_MISSION) {
2410  if (anchor_objnum >= 0)
2411  Script_system.SetHookObjects(2, "Ship", &Objects[objnum], "Parent", &Objects[anchor_objnum]);
2412  else
2413  Script_system.SetHookObjects(2, "Ship", &Objects[objnum], "Parent", NULL);
2414 
2415  Script_system.RunCondition(CHA_ONSHIPARRIVE, 0, NULL, &Objects[objnum]);
2416  Script_system.RemHookVars(2, "Ship", "Parent");
2417  }
2418 
2419  return objnum;
2420 }
2421 
2428 void parse_bring_in_docked_wing(p_object *p_objp, int wingnum, int shipnum)
2429 {
2430  Assert(!Fred_running);
2431  Assert(p_objp != NULL);
2432  Assert(wingnum >= 0);
2433  Assert(shipnum >= 0);
2434  int j, index;
2435  wing *wingp = &Wings[wingnum];
2436 
2437  // link ship and wing together
2438  // (do this first because mission log relies on ship_index)
2439  wingp->ship_index[p_objp->pos_in_wing] = shipnum;
2440  Ships[shipnum].wingnum = wingnum;
2441 
2442  // has this wing arrived at all yet?
2443  if (wingp->current_wave == 0)
2444  {
2445  wingp->current_wave++;
2446  mission_log_add_entry( LOG_WING_ARRIVED, wingp->name, NULL, wingp->current_wave );
2447  wingp->wave_delay_timestamp = timestamp(-1);
2448  }
2449  // how did we get more than one wave here?
2450  else if (wingp->current_wave > 1)
2451  Error(LOCATION, "Wing %s was created from docked ships but somehow has more than one wave!", wingp->name);
2452 
2453  // increment tallies
2454  wingp->total_arrived_count++;
2455  wingp->current_count++;
2456  // make sure we haven't created too many ships
2458 
2459  // at this point the wing has arrived, so handle the stuff for this particular ship
2460 
2461  // set up wingman status index
2462  hud_wingman_status_set_index(wingp, &Ships[shipnum], p_objp);
2463 
2464  // copy to parse object
2466  p_objp->wing_status_wing_pos = Ships[shipnum].wing_status_wing_pos;
2467 
2468  // set flag if necessary
2469  for (j = 0; j < MAX_STARTING_WINGS; j++)
2470  {
2471  if (!stricmp(Starting_wing_names[j], wingp->name))
2472  Ships[shipnum].flags |= SF_FROM_PLAYER_WING;
2473  }
2474 
2475  // handle AI
2476  ai_info *aip = &Ai_info[Ships[shipnum].ai_index];
2477  aip->wing = wingnum;
2478 
2479  if (wingp->flags & WF_NO_DYNAMIC)
2480  aip->ai_flags |= AIF_NO_DYNAMIC;
2481 
2482  // copy any goals from the wing to the newly created ship
2483  for (index = 0; index < MAX_AI_GOALS; index++)
2484  {
2485  if (wingp->ai_goals[index].ai_mode != AI_GOAL_NONE)
2486  ai_copy_mission_wing_goal(&wingp->ai_goals[index], aip);
2487  }
2488 }
2489 
2490 // Goober5000
2491 void resolve_parse_flags(object *objp, int parse_flags, int parse_flags2)
2492 {
2493  Assert(objp != NULL);
2494  ship *shipp = &Ships[objp->instance];
2495 
2496  if (parse_flags & P_SF_CARGO_KNOWN)
2497  shipp->flags |= SF_CARGO_REVEALED;
2498 
2499  if (parse_flags & P_SF_IGNORE_COUNT)
2500  shipp->flags |= SF_IGNORE_COUNT;
2501 
2502  if (parse_flags & P_OF_PROTECTED)
2503  objp->flags |= OF_PROTECTED;
2504 
2505  if (parse_flags & P_SF_REINFORCEMENT)
2506  {
2507  //Individual ships in wings can't be reinforcements - FUBAR
2508  if(shipp->wingnum >= 0)
2509  {
2510  Warning(LOCATION, "Ship %s is a reinforcement unit but is a member of a wing. Ignoring reinforcement flag.", shipp->ship_name);
2511  }
2512  else
2513  {
2514  shipp->flags |= SF_REINFORCEMENT;
2515  }
2516  }
2517 
2518  if ((parse_flags & P_OF_NO_SHIELDS) && (parse_flags2 & P2_OF_FORCE_SHIELDS_ON))
2519  {
2520  Warning(LOCATION, "The parser found a ship with both the \"force-shields-on\" and \"no-shields\" flags; this is inconsistent!");
2521  }
2522  if (parse_flags & P_OF_NO_SHIELDS)
2523  objp->flags |= OF_NO_SHIELDS;
2524 
2525  if (parse_flags & P_SF_ESCORT)
2526  shipp->flags |= SF_ESCORT;
2527 
2528  // P_OF_PLAYER_START is handled in parse_create_object_sub
2529 
2530  if (parse_flags & P_SF_NO_ARRIVAL_MUSIC)
2531  shipp->flags |= SF_NO_ARRIVAL_MUSIC;
2532 
2533  if (parse_flags & P_SF_NO_ARRIVAL_WARP)
2534  shipp->flags |= SF_NO_ARRIVAL_WARP;
2535 
2536  if (parse_flags & P_SF_NO_DEPARTURE_WARP)
2537  shipp->flags |= SF_NO_DEPARTURE_WARP;
2538 
2539  if (parse_flags & P_SF_LOCKED) {
2540  shipp->flags2 |= SF2_SHIP_LOCKED;
2541  shipp->flags2 |= SF2_WEAPONS_LOCKED;
2542  }
2543 
2544  if (parse_flags & P_OF_INVULNERABLE)
2545  objp->flags |= OF_INVULNERABLE;
2546 
2547  if (parse_flags & P_SF_HIDDEN_FROM_SENSORS)
2548  shipp->flags |= SF_HIDDEN_FROM_SENSORS;
2549 
2550  if (parse_flags & P_SF_SCANNABLE)
2551  shipp->flags |= SF_SCANNABLE;
2552 
2553  // P_AIF_KAMIKAZE, P_AIF_NO_DYNAMIC, and P_SF_RED_ALERT_CARRY are handled in parse_create_object_sub
2554 
2555  if (parse_flags & P_OF_BEAM_PROTECTED)
2556  objp->flags |= OF_BEAM_PROTECTED;
2557 
2558  if (parse_flags & P_OF_FLAK_PROTECTED)
2559  objp->flags |= OF_FLAK_PROTECTED;
2560 
2561  if (parse_flags & P_OF_LASER_PROTECTED)
2562  objp->flags |= OF_LASER_PROTECTED;
2563 
2564  if (parse_flags & P_OF_MISSILE_PROTECTED)
2565  objp->flags |= OF_MISSILE_PROTECTED;
2566 
2567  if (parse_flags & P_SF_GUARDIAN)
2569 
2570  if (parse_flags & P_SF_VAPORIZE)
2571  shipp->flags |= SF_VAPORIZE;
2572 
2573  if (parse_flags & P_SF2_STEALTH)
2574  shipp->flags2 |= SF2_STEALTH;
2575 
2576  if (parse_flags & P_SF2_FRIENDLY_STEALTH_INVIS)
2578 
2579  if (parse_flags & P_SF2_DONT_COLLIDE_INVIS)
2580  shipp->flags2 |= SF2_DONT_COLLIDE_INVIS;
2581 
2582  if (parse_flags2 & P2_SF2_PRIMITIVE_SENSORS)
2583  shipp->flags2 |= SF2_PRIMITIVE_SENSORS;
2584 
2585  if (parse_flags2 & P2_SF2_NO_SUBSPACE_DRIVE)
2586  shipp->flags2 |= SF2_NO_SUBSPACE_DRIVE;
2587 
2588  if (parse_flags2 & P2_SF2_NAV_CARRY_STATUS)
2589  shipp->flags2 |= SF2_NAVPOINT_CARRY;
2590 
2591  if (parse_flags2 & P2_SF2_AFFECTED_BY_GRAVITY)
2592  shipp->flags2 |= SF2_AFFECTED_BY_GRAVITY;
2593 
2594  if (parse_flags2 & P2_SF2_TOGGLE_SUBSYSTEM_SCANNING)
2596 
2597  if (parse_flags2 & P2_OF_TARGETABLE_AS_BOMB)
2598  objp->flags |= OF_TARGETABLE_AS_BOMB;
2599 
2600  if (parse_flags2 & P2_SF2_NO_BUILTIN_MESSAGES)
2601  shipp->flags2 |= SF2_NO_BUILTIN_MESSAGES;
2602 
2603  if (parse_flags2 & P2_SF2_PRIMARIES_LOCKED)
2604  shipp->flags2 |= SF2_PRIMARIES_LOCKED;
2605 
2606  if (parse_flags2 & P2_SF2_SECONDARIES_LOCKED)
2607  shipp->flags2 |= SF2_SECONDARIES_LOCKED;
2608 
2609  if (parse_flags2 & P2_SF2_SET_CLASS_DYNAMICALLY)
2611 
2612  if (parse_flags2 & P2_SF2_NO_DEATH_SCREAM)
2613  shipp->flags2 |= SF2_NO_DEATH_SCREAM;
2614 
2615  if (parse_flags2 & P2_SF2_ALWAYS_DEATH_SCREAM)
2616  shipp->flags2 |= SF2_ALWAYS_DEATH_SCREAM;
2617 
2618  if (parse_flags2 & P2_SF2_NAV_NEEDSLINK)
2619  shipp->flags2 |= SF2_NAVPOINT_NEEDSLINK;
2620 
2621  if (parse_flags2 & P2_SF2_HIDE_SHIP_NAME)
2622  shipp->flags2 |= SF2_HIDE_SHIP_NAME;
2623 
2624  if (parse_flags2 & P2_SF2_LOCK_ALL_TURRETS_INITIALLY)
2626 
2627  if (parse_flags2 & P2_SF2_AFTERBURNER_LOCKED)
2628  shipp->flags2 |= SF2_AFTERBURNER_LOCKED;
2629 
2630  if (parse_flags2 & P2_OF_FORCE_SHIELDS_ON)
2631  shipp->flags2 |= SF2_FORCE_SHIELDS_ON;
2632 
2633  if (parse_flags2 & P2_OF_IMMOBILE)
2634  objp->flags |= OF_IMMOBILE;
2635 
2636  if (parse_flags2 & P2_SF2_NO_ETS)
2637  shipp->flags2 |= SF2_NO_ETS;
2638 
2639  if (parse_flags2 & P2_SF2_CLOAKED)
2640  shipp->flags2 |= SF2_CLOAKED;
2641 
2642  if (parse_flags2 & P2_SF2_SHIP_LOCKED)
2643  shipp->flags2 |= SF2_SHIP_LOCKED;
2644 
2645  if (parse_flags2 & P2_SF2_WEAPONS_LOCKED)
2646  shipp->flags2 |= SF2_WEAPONS_LOCKED;
2647 
2648  if (parse_flags2 & P2_SF2_SCRAMBLE_MESSAGES)
2649  shipp->flags2 |= SF2_SCRAMBLE_MESSAGES;
2650 
2651  if (parse_flags2 & P2_SF2_NO_DISABLED_SELF_DESTRUCT)
2653 
2654  // don't remove no-collide if not set in the mission
2655  if (parse_flags2 & P2_OF_NO_COLLIDE)
2656  obj_set_flags(objp, objp->flags & ~OF_COLLIDES);
2657 }
2658 
2659 void fix_old_special_explosions(p_object *p_objp, int variable_index)
2660 {
2661  int i;
2662 
2663  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);
2664 
2665  // check all the variables are valid
2666  for ( i = variable_index; i < (variable_index + BLOCK_EXP_SIZE); i++ ) {
2667  if (!( Block_variables[i].type & SEXP_VARIABLE_BLOCK )) {
2668  Warning (LOCATION, "%s is using the old special explosions method but does not appear to have variables for all the values", p_objp->name);
2669  return;
2670  }
2671  }
2672 
2673  p_objp->use_special_explosion = true;
2674 
2675  p_objp->special_exp_damage = atoi(Block_variables[variable_index+DAMAGE].text);
2676  p_objp->special_exp_blast = atoi(Block_variables[variable_index+BLAST].text);
2677  p_objp->special_exp_inner = atoi(Block_variables[variable_index+INNER_RAD].text);
2678  p_objp->special_exp_outer = atoi(Block_variables[variable_index+OUTER_RAD].text);
2679  p_objp->use_shockwave = (atoi(Block_variables[variable_index+PROPAGATE].text) ? 1:0);
2680  p_objp->special_exp_shockwave_speed = atoi(Block_variables[variable_index+SHOCK_SPEED].text);
2681  p_objp->special_exp_deathroll_time = 0;
2682 }
2683 
2684 void fix_old_special_hits(p_object *p_objp, int variable_index)
2685 {
2686  int i;
2687 
2688  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);
2689 
2690  // check all the variables are valid
2691  for ( i = variable_index; i < (variable_index + BLOCK_HIT_SIZE); i++ ) {
2692  if (!( Block_variables[i].type & SEXP_VARIABLE_BLOCK )) {
2693  Warning (LOCATION, "%s is using the old special hitpoints method but does not appear to have variables for all the values", p_objp->name);
2694  return;
2695  }
2696  }
2697 
2698  p_objp->special_hitpoints = atoi(Block_variables[variable_index+HULL_STRENGTH].text);
2699  p_objp->special_shield = atoi(Block_variables[variable_index+SHIELD_STRENGTH].text);
2700 }
2701 
2703  : next(NULL), prev(NULL), dock_list(NULL), created_object(NULL)
2704 {}
2705 
2706 // this will be called when Parse_objects is cleared between missions and upon shutdown
2708 {
2709  dock_free_dock_list(this);
2710 }
2711 
2721 int parse_object(mission *pm, int flag, p_object *p_objp)
2722 {
2723  int i, j, count, delay;
2724  char name[NAME_LENGTH], flag_strings[MAX_PARSE_OBJECT_FLAGS][NAME_LENGTH];
2725  char flag_strings_2[MAX_PARSE_OBJECT_FLAGS_2][NAME_LENGTH];
2726 
2727  Assert(pm != NULL);
2728 
2729  // Goober5000
2730  p_objp->created_object = NULL;
2731  p_objp->next = NULL;
2732  p_objp->prev = NULL;
2733 
2734  required_string("$Name:");
2735  stuff_string(p_objp->name, F_NAME, NAME_LENGTH);
2736  if (mission_parse_get_parse_object(p_objp->name))
2737  error_display(0, NOX("Redundant ship name: %s\n"), p_objp->name);
2738 
2739 
2740  find_and_stuff("$Class:", &p_objp->ship_class, F_NAME, Ship_class_names, Ship_info.size(), "ship class");
2741  if (p_objp->ship_class < 0)
2742  {
2743  if (Fred_running) {
2744  Warning(LOCATION, "Ship \"%s\" has an invalid ship type (ships.tbl probably changed). Making it type 0\n", p_objp->name);
2745  }
2746  else {
2747  mprintf(("MISSIONS: Ship \"%s\" has an invalid ship type (ships.tbl probably changed). Making it type 0\n", p_objp->name));
2748  }
2749 
2750  p_objp->ship_class = 0;
2752  }
2753 
2754  // Karajorma - See if there are any alternate classes specified for this ship.
2755  p_objp->alt_classes.clear();
2756  // The alt class can either be a variable or a ship class name
2757  char alt_ship_class[TOKEN_LENGTH > NAME_LENGTH ? TOKEN_LENGTH : NAME_LENGTH];
2758  int is_variable;
2759 
2760  while (optional_string("$Alt Ship Class:")) {
2761  alt_class new_alt_class;
2762 
2763  is_variable = get_string_or_variable(alt_ship_class);
2764 
2765  if (is_variable) {
2766  new_alt_class.variable_index = get_index_sexp_variable_name(alt_ship_class);
2767  if(new_alt_class.variable_index >= 0) {
2768  new_alt_class.ship_class = ship_info_lookup(Sexp_variables[new_alt_class.variable_index].text);
2769  }
2770  else {
2771  new_alt_class.ship_class = -1;
2772  }
2773  }
2774  else {
2775  new_alt_class.variable_index = -1;
2776  new_alt_class.ship_class = ship_info_lookup(alt_ship_class);
2777  }
2778 
2779  if (new_alt_class.ship_class < 0 ) {
2780  if (!Fred_running) {
2781  Warning(LOCATION, "Ship \"%s\" has an invalid Alternate Ship Class type (ships.tbl probably changed). Skipping this entry", p_objp->name);
2782  continue;
2783  }
2784  else {
2785  // incorrect initial values for a variable can be fixed in FRED
2786  if (new_alt_class.variable_index != -1) {
2787  Warning(LOCATION, "Ship \"%s\" has an invalid Alternate Ship Class type.", p_objp->name);
2788  }
2789  // but there is little we can do if someone spelled a ship class incorrectly
2790  else {
2791  Warning(LOCATION, "Ship \"%s\" has an invalid Alternate Ship Class type. Skipping this entry", p_objp->name);
2792  continue;
2793  }
2794  }
2795  }
2796 
2797  if (optional_string("+Default Class:")) {
2798  new_alt_class.default_to_this_class = true;
2799  }
2800  else {
2801  new_alt_class.default_to_this_class = false;
2802  }
2803 
2804  p_objp->alt_classes.push_back(new_alt_class);
2805  }
2806 
2807  // if this is a multiplayer dogfight mission, skip support ships
2808  if(MULTI_DOGFIGHT && (Ship_info[p_objp->ship_class].flags & SIF_SUPPORT))
2809  return 0;
2810 
2811  // optional alternate name type
2812  p_objp->alt_type_index = -1;
2813  if(optional_string("$Alt:"))
2814  {
2815  // alternate name
2817 
2818  // try and find the alternate name
2819  p_objp->alt_type_index = mission_parse_lookup_alt(name);
2820  if(p_objp->alt_type_index < 0)
2821  WarningEx(LOCATION, "Mission %s\nError looking up alternate ship type name %s!\n", pm->name, name);
2822  else
2823  mprintf(("Using alternate ship type name: %s\n", name));
2824  }
2825 
2826  // optional callsign
2827  p_objp->callsign_index = -1;
2828  if(optional_string("$Callsign:"))
2829  {
2830  // alternate callsign
2832 
2833  // try and find the callsign
2835  if(p_objp->callsign_index < 0)
2836  WarningEx(LOCATION, "Mission %s\nError looking up callsign %s!\n", pm->name, name);
2837  else
2838  mprintf(("Using callsign: %s\n", name));
2839  }
2840 
2841  // static alias stuff - stupid, but it seems to be necessary
2842  static char *temp_team_names[MAX_IFFS];
2843  for (i = 0; i < Num_iffs; i++)
2844  temp_team_names[i] = Iff_info[i].iff_name;
2845 
2846  find_and_stuff("$Team:", &p_objp->team, F_NAME, temp_team_names, Num_iffs, "team name");
2847 
2848  if (optional_string("$Team Color Setting:")) {
2849  char temp[NAME_LENGTH];
2851  p_objp->team_color_setting = temp;
2852 
2853  if (Team_Colors.find(p_objp->team_color_setting) == Team_Colors.end()) {
2854  mprintf(("Invalid team color specified in mission file for ship %s, resetting to default\n", p_objp->name));
2855  p_objp->team_color_setting = Ship_info[p_objp->ship_class].default_team_name;
2856  }
2857  }
2858 
2859  required_string("$Location:");
2860  stuff_vec3d(&p_objp->pos);
2861 
2862  required_string("$Orientation:");
2863  stuff_matrix(&p_objp->orient);
2864 
2865  // legacy code, not even used in FS1
2866  if (optional_string("$IFF:"))
2867  {
2869  }
2870 
2871  find_and_stuff("$AI Behavior:", &p_objp->behavior, F_NAME, Ai_behavior_names, Num_ai_behaviors, "AI behavior");
2872  p_objp->ai_goals = -1;
2873 
2874  if (optional_string("+AI Class:"))
2875  {
2876  p_objp->ai_class = match_and_stuff(F_NAME, Ai_class_names, Num_ai_classes, "AI class");
2877 
2878  if (p_objp->ai_class < 0)
2879  {
2880  Warning(LOCATION, "AI Class for ship %s does not exist in ai.tbl. Setting to first available class.\n", p_objp->name);
2881  p_objp->ai_class = 0;
2882  }
2883  }
2884  else
2885  {
2886  p_objp->ai_class = Ship_info[p_objp->ship_class].ai_class;
2887  }
2888 
2889  if (optional_string("$AI Goals:"))
2890  p_objp->ai_goals = get_sexp_main();
2891 
2892  if (!required_string_either("$AI Goals:", "$Cargo 1:"))
2893  {
2894  required_string("$AI Goals:");
2895  p_objp->ai_goals = get_sexp_main();
2896  }
2897 
2898  p_objp->cargo1 = -1;
2899  int temp;
2900  find_and_stuff_or_add("$Cargo 1:", &temp, F_NAME, Cargo_names, &Num_cargo, MAX_CARGO, "cargo");
2901  p_objp->cargo1 = char(temp);
2902  if (optional_string("$Cargo 2:"))
2903  {
2905  }
2906 
2907  parse_common_object_data(p_objp); // get initial conditions and subsys status
2908  count = 0;
2909  while (required_string_either("$Arrival Location:", "$Status Description:")) {
2910  Assert(count < MAX_OBJECT_STATUS);
2911 
2912  find_and_stuff("$Status Description:", &p_objp->status_type[count], F_NAME, Status_desc_names, Num_status_names, "Status Description");
2913  find_and_stuff("$Status:", &p_objp->status[count], F_NAME, Status_type_names, Num_status_names, "Status Type");
2914  find_and_stuff("$Target:", &p_objp->target[count], F_NAME, Status_target_names, Num_status_names, "Target");
2915  count++;
2916  }
2917  p_objp->status_count = count;
2918 
2919  p_objp->arrival_anchor = -1;
2920  p_objp->arrival_distance = 0;
2921  p_objp->arrival_path_mask = -1; // -1 only until resolved
2922 
2923  find_and_stuff("$Arrival Location:", &p_objp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
2924 
2925  if (optional_string("+Arrival Distance:"))
2926  {
2927  stuff_int(&p_objp->arrival_distance);
2928 
2929  // Goober5000
2930  if ((p_objp->arrival_distance <= 0) && ((p_objp->arrival_location == ARRIVE_NEAR_SHIP) || (p_objp->arrival_location == ARRIVE_IN_FRONT_OF_SHIP)))
2931  {
2932  Warning(LOCATION, "Arrival distance for ship %s cannot be %d. Setting to 1.\n", p_objp->name, p_objp->arrival_distance);
2933  p_objp->arrival_distance = 1;
2934  }
2935  }
2936 
2937  if (p_objp->arrival_location != ARRIVE_AT_LOCATION)
2938  {
2939  required_string("$Arrival Anchor:");
2941  p_objp->arrival_anchor = get_anchor(name);
2942  }
2943 
2944  if (optional_string("+Arrival Paths:"))
2945  {
2946  // temporarily use mask to point to the restriction index
2948  }
2949 
2950  delay = 0;
2951  if (optional_string("+Arrival Delay:"))
2952  {
2953  stuff_int(&delay);
2954  if (delay < 0)
2955  Error(LOCATION, "Cannot have arrival delay < 0 (ship %s)", p_objp->name);
2956  }
2957 
2958  if (!Fred_running)
2959  p_objp->arrival_delay = -delay; // use negative numbers to mean we haven't set up a timer yet
2960  else
2961  p_objp->arrival_delay = delay;
2962 
2963  required_string("$Arrival Cue:");
2964  p_objp->arrival_cue = get_sexp_main();
2965  if (!Fred_running && (p_objp->arrival_cue >= 0))
2966  {
2967  // eval the arrival cue. if the cue is true, set up the timestamp for the arrival delay
2968  Assert (p_objp->arrival_delay <= 0);
2969 
2970  // don't eval arrival_cues when just looking for player information.
2971  // evaluate to determine if sexp is always false.
2972  if (eval_sexp(p_objp->arrival_cue))
2973  p_objp->arrival_delay = timestamp(-p_objp->arrival_delay * 1000);
2974  }
2975 
2976  p_objp->departure_anchor = -1;
2977  p_objp->departure_path_mask = -1; // -1 only until resolved
2978 
2979  find_and_stuff("$Departure Location:", &p_objp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
2980 
2981  if (p_objp->departure_location != DEPART_AT_LOCATION)
2982  {
2983  required_string("$Departure Anchor:");
2985  p_objp->departure_anchor = get_anchor(name);
2986  }
2987 
2988  if (optional_string("+Departure Paths:"))
2989  {
2990  // temporarily use mask to point to the restriction index
2992  }
2993 
2994  delay = 0;
2995  if (optional_string("+Departure Delay:"))
2996  {
2997  stuff_int(&delay);
2998  if (delay < 0)
2999  Error(LOCATION, "Cannot have departure delay < 0 (ship %s)", p_objp->name);
3000  }
3001 
3002  if (!Fred_running)
3003  p_objp->departure_delay = -delay;
3004  else
3005  p_objp->departure_delay = delay;
3006 
3007  required_string("$Departure Cue:");
3008  p_objp->departure_cue = get_sexp_main();
3009 
3010  if (optional_string("$Misc Properties:"))
3011  stuff_string(p_objp->misc, F_NAME, NAME_LENGTH);
3012 
3013  required_string("$Determination:");
3014  int dummy;
3015  stuff_int(&dummy);
3016 
3017  // set flags
3018  p_objp->flags = 0;
3019  if (optional_string("+Flags:"))
3020  {
3021  count = stuff_string_list(flag_strings, MAX_PARSE_OBJECT_FLAGS);
3022  for (i=0; i<count; i++)
3023  {
3024  for (j=0; j<MAX_PARSE_OBJECT_FLAGS; j++)
3025  {
3026  if (!stricmp(flag_strings[i], Parse_object_flags[j]))
3027  {
3028  p_objp->flags |= (1 << j);
3029  break;
3030  }
3031  }
3032 
3033  if (j == MAX_PARSE_OBJECT_FLAGS)
3034  Warning(LOCATION, "Unknown flag in mission file: %s\n", flag_strings[i]);
3035  }
3036  }
3037 
3038  // second set - Goober5000
3039  p_objp->flags2 = 0;
3040  if (optional_string("+Flags2:"))
3041  {
3042  count = stuff_string_list(flag_strings_2, MAX_PARSE_OBJECT_FLAGS_2);
3043  for (i=0; i<count; i++)
3044  {
3045  for (j=0; j<MAX_PARSE_OBJECT_FLAGS_2; j++)
3046  {
3047  if (!stricmp(flag_strings_2[i], Parse_object_flags_2[j]))
3048  {
3049  p_objp->flags2 |= (1 << j);
3050  break;
3051  }
3052  }
3053 
3054  if (j == MAX_PARSE_OBJECT_FLAGS_2)
3055  Warning(LOCATION, "Unknown flag2 in mission file: %s\n", flag_strings_2[i]);
3056  }
3057  }
3058 
3059 
3060  // always store respawn priority, just for ease of implementation
3061  p_objp->respawn_priority = 0;
3062  if(optional_string("+Respawn Priority:"))
3063  stuff_int(&p_objp->respawn_priority);
3064 
3065  p_objp->escort_priority = 0;
3066  if (optional_string("+Escort Priority:"))
3067  {
3068  Assert(p_objp->flags & P_SF_ESCORT);
3069  stuff_int(&p_objp->escort_priority);
3070  }
3071 
3072  if (p_objp->flags & P_OF_PLAYER_START)
3073  {
3074  p_objp->flags |= P_SF_CARGO_KNOWN; // make cargo known for players
3075  Player_starts++;
3076  }
3077 
3078  p_objp->use_special_explosion = false;
3079  p_objp->special_exp_damage = -1;
3080  p_objp->special_exp_blast = -1;
3081  p_objp->special_exp_inner = -1;
3082  p_objp->special_exp_outer = -1;
3083  p_objp->use_shockwave = false;
3084  p_objp->special_exp_shockwave_speed = 0;
3085  p_objp->special_exp_deathroll_time = 0;
3086 
3087  p_objp->special_hitpoints = 0;
3088  p_objp->special_shield = -1;
3089 
3090  if (optional_string("$Special Explosion:")) {
3091  p_objp->use_special_explosion = true;
3092  bool period_detected = false;
3093 
3094  if (required_string("+Special Exp Damage:")) {
3095  stuff_int(&p_objp->special_exp_damage);
3096 
3097  if (*Mp == '.') {
3098  period_detected = true;
3099  advance_to_eoln(NULL);
3100  }
3101  }
3102 
3103  if (required_string("+Special Exp Blast:")) {
3104  stuff_int(&p_objp->special_exp_blast);
3105 
3106  if (*Mp == '.') {
3107  period_detected = true;
3108  advance_to_eoln(NULL);
3109  }
3110  }
3111 
3112  if (required_string("+Special Exp Inner Radius:")) {
3113  stuff_int(&p_objp->special_exp_inner);
3114 
3115  if (*Mp == '.') {
3116  period_detected = true;
3117  advance_to_eoln(NULL);
3118  }
3119  }
3120 
3121  if (required_string("+Special Exp Outer Radius:")) {
3122  stuff_int(&p_objp->special_exp_outer);
3123 
3124  if (*Mp == '.') {
3125  period_detected = true;
3126  advance_to_eoln(NULL);
3127  }
3128  }
3129 
3130  if (optional_string("+Special Exp Shockwave Speed:")) {
3132  p_objp->use_shockwave = true;
3133 
3134  if (*Mp == '.') {
3135  period_detected = true;
3136  advance_to_eoln(NULL);
3137  }
3138  }
3139 
3140  if (optional_string("+Special Exp Death Roll Time:")) {
3142  }
3143 
3144  if (period_detected) {
3145  nprintf(("Warning", "Special explosion attributes have been returned to integer format"));
3146  }
3147  }
3148 
3149  if (optional_string("+Special Hitpoints:")) {
3150  stuff_int(&p_objp->special_hitpoints);
3151  }
3152 
3153  if (optional_string("+Special Shield Points:")) {
3154  stuff_int(&p_objp->special_shield);
3155  }
3156 
3157  if (optional_string("+Special Exp index:")) {
3158  int variable_index;
3159  stuff_int(&variable_index);
3160  fix_old_special_explosions(p_objp, variable_index);
3161  }
3162 
3163  if (optional_string("+Special Hitpoint index:")) {
3164  int variable_index;
3165  stuff_int(&variable_index);
3166  fix_old_special_hits(p_objp, variable_index);
3167  }
3168 
3169  // set custom shield value
3170  if (p_objp->special_shield != -1) {
3171  p_objp->ship_max_shield_strength = (float) p_objp->special_shield;
3172  }
3173  else {
3174  p_objp->ship_max_shield_strength = Ship_info[p_objp->ship_class].max_shield_strength;
3175  }
3176 
3177  // set custom hitpoint value
3178  if (p_objp->special_hitpoints > 0) {
3179  p_objp->ship_max_hull_strength = (float) p_objp->special_hitpoints;
3180  }
3181  else {
3182  p_objp->ship_max_hull_strength = Ship_info[p_objp->ship_class].max_hull_strength;
3183  }
3184 
3185  Assert(p_objp->ship_max_hull_strength > 0.0f); // Goober5000: div-0 check (not shield because we might not have one)
3186  p_objp->max_shield_recharge = Ship_info[p_objp->ship_class].max_shield_recharge;
3187 
3188 
3189  // if the kamikaze flag is set, we should have the next flag
3190  if (optional_string("+Kamikaze Damage:"))
3191  {
3192  int damage;
3193 
3194  stuff_int(&damage);
3195  p_objp->kamikaze_damage = damage;
3196  }
3197 
3198  p_objp->hotkey = -1;
3199  if (optional_string("+Hotkey:"))
3200  {
3201  stuff_int(&p_objp->hotkey);
3202  Assert((p_objp->hotkey >= 0) && (p_objp->hotkey < 10));
3203  }
3204 
3205  // Goober5000
3206  p_objp->dock_list = NULL;
3207  while (optional_string("+Docked With:"))
3208  {
3209  char docked_with[NAME_LENGTH];
3210  char docker_point[NAME_LENGTH];
3211  char dockee_point[NAME_LENGTH];
3212 
3213  // grab docking information
3214  // (whoever designed the original docking system
3215  // reversed the dockpoints in the mission file)
3216  stuff_string(docked_with, F_NAME, NAME_LENGTH);
3217  required_string("$Docker Point:");
3218  stuff_string(dockee_point, F_NAME, NAME_LENGTH);
3219  required_string("$Dockee Point:");
3220  stuff_string(docker_point, F_NAME, NAME_LENGTH);
3221 
3222  // make sure we don't overflow the limit
3224  {
3225  mprintf(("Too many initially docked instances; skipping...\n"));
3226  continue;
3227  }
3228 
3229  // put this information into the Initially_docked array
3232  strcpy_s(Initially_docked[Total_initially_docked].docker_point, docker_point);
3233  strcpy_s(Initially_docked[Total_initially_docked].dockee_point, dockee_point);
3235  }
3236 
3237  // check the optional parameter for destroying the ship before the mission starts. If this parameter is
3238  // here, then we need to destroy the ship N seconds before the mission starts (for debris purposes).
3239  // store the time value here. We want to create this object for sure. Set the arrival cue and arrival
3240  // delay to bogus values
3241  p_objp->destroy_before_mission_time = -1;
3242  if (optional_string("+Destroy At:"))
3243  {
3245  Assert (p_objp->destroy_before_mission_time >= 0);
3246  p_objp->arrival_cue = Locked_sexp_true;
3247  p_objp->arrival_delay = timestamp(0);
3248  }
3249 
3250  // check for the optional "orders accepted" string which contains the orders from the default
3251  // set that this ship will actually listen to
3252  if (optional_string("+Orders Accepted:"))
3253  {
3254  stuff_int(&p_objp->orders_accepted);
3255  if (p_objp->orders_accepted != -1)
3256  p_objp->flags |= P_SF_USE_UNIQUE_ORDERS;
3257  }
3258 
3259  p_objp->group = 0;
3260  if (optional_string("+Group:"))
3261  stuff_int(&p_objp->group);
3262 
3263  bool table_score = false;
3264  if (optional_string("+Use Table Score:")) {
3265  table_score = true;
3266  }
3267 
3268  if (optional_string("+Score:")) {
3269  if (!table_score) {
3270  stuff_int(&p_objp->score);
3271  }
3272  // throw away the value the mission file has and use the table value.
3273  else {
3274  int temp_score;
3275  stuff_int(&temp_score);
3276  }
3277  }
3278  else {
3279  table_score = true;
3280  }
3281 
3282  if (table_score) {
3283  p_objp->score = Ship_info[p_objp->ship_class].score;
3284  }
3285 
3286  if (optional_string("+Assist Score Percentage:")) {
3287  stuff_float(&p_objp->assist_score_pct);
3288  // value must be a percentage
3289  if (p_objp->assist_score_pct < 0) {
3290  p_objp->assist_score_pct = 0;
3291  }
3292  else if (p_objp->assist_score_pct > 1) {
3293  p_objp->assist_score_pct = 1;
3294  }
3295  }
3296  else {
3297  p_objp->assist_score_pct = 0;
3298  }
3299 
3300  // parse the persona index if present
3301  p_objp->persona_index = -1;
3302  if (optional_string("+Persona Index:"))
3303  stuff_int(&p_objp->persona_index);
3304 
3305  // texture replacement - Goober5000
3306  p_objp->replacement_textures = Ship_info[p_objp->ship_class].replacement_textures; // initialize our set with the ship class set, which may be empty
3307  if (optional_string("$Texture Replace:") || optional_string("$Duplicate Model Texture Replace:"))
3308  {
3309  texture_replace tr;
3310  char *p;
3311 
3312  while (optional_string("+old:"))
3313  {
3314  strcpy_s(tr.ship_name, p_objp->name);
3315  tr.new_texture_id = -1;
3316 
3318  required_string("+new:");
3320 
3321  // get rid of extensions
3322  p = strchr(tr.old_texture, '.');
3323  if (p)
3324  {
3325  mprintf(("Extraneous extension found on replacement texture %s!\n", tr.old_texture));
3326  *p = 0;
3327  }
3328  p = strchr(tr.new_texture, '.');
3329  if (p)
3330  {
3331  mprintf(("Extraneous extension found on replacement texture %s!\n", tr.new_texture));
3332  *p = 0;
3333  }
3334 
3335  // add it if we aren't over the limit
3336  if (p_objp->replacement_textures.size() < MAX_MODEL_TEXTURES)
3337  p_objp->replacement_textures.push_back(tr);
3338  else
3339  mprintf(("Too many replacement textures specified for ship '%s'!\n", p_objp->name));
3340  }
3341  }
3342 
3343  // now load the textures (do this outside the parse loop because we may have ship class replacements too)
3344  for (SCP_vector<texture_replace>::iterator tr = p_objp->replacement_textures.begin(); tr != p_objp->replacement_textures.end(); ++tr)
3345  {
3346  // load the texture
3347  if (!stricmp(tr->new_texture, "invisible"))
3348  {
3349  // invisible is a special case
3350  tr->new_texture_id = REPLACE_WITH_INVISIBLE;
3351  }
3352  else
3353  {
3354  // try to load texture or anim as normal
3355  tr->new_texture_id = bm_load_either(tr->new_texture);
3356  }
3357 
3358  // not found?
3359  if (tr->new_texture_id < 0)
3360  {
3361  mprintf(("Could not load replacement texture %s for ship %s\n", tr->new_texture, p_objp->name));
3362  }
3363 
3364  // account for FRED
3365  if (Fred_running)
3366  {
3367  Fred_texture_replacements.push_back(*tr);
3368  Fred_texture_replacements.back().new_texture_id = -1;
3369  }
3370  }
3371 
3372  p_objp->wingnum = -1; // set the wing number to -1 -- possibly to be set later
3373  p_objp->pos_in_wing = -1; // Goober5000
3374 
3375  for (i=0;i<MAX_IFFS;i++)
3376  {
3377  for (j=0;j<MAX_IFFS;j++)
3378  {
3379  p_objp->alt_iff_color[i][j] = -1;
3380  }
3381  }
3382 
3383  // for multiplayer, assign a network signature to this parse object. Doing this here will
3384  // allow servers to use the signature with clients when creating new ships, instead of having
3385  // to pass ship names all the time
3386  if (Game_mode & GM_MULTIPLAYER)
3388 
3389  // set the wing_status position to be -1 for all objects. This will get set to an appropriate
3390  // value when the wing positions are finally determined.
3391  p_objp->wing_status_wing_index = -1;
3392  p_objp->wing_status_wing_pos = -1;
3393  p_objp->respawn_count = 0;
3394 
3395  // Goober5000 - preload stuff for certain object flags
3396  // (done after parsing object, but before creating it)
3397  if (p_objp->flags & P_KNOSSOS_WARP_IN)
3399 
3400  // this is a valid/legal ship to create
3401  return 1;
3402 }
3403 
3405 {
3406  ship_info *sip = NULL;
3407  model_subsystem *subsystems = NULL;
3408 
3409  // only for objects which show up after the start of a mission
3410  if (p_objp->created_object != NULL)
3411  return;
3412 
3413  Assert( p_objp->ship_class >= 0 );
3414 
3415  sip = &Ship_info[p_objp->ship_class];
3416 
3417  if (sip->n_subsystems > 0) {
3418  subsystems = &sip->subsystems[0];
3419  }
3420 
3421  // we need the model to process the texture set, so go ahead and load it now
3422  sip->model_num = model_load(sip->pof_file, sip->n_subsystems, subsystems);
3423 }
3424 
3425 // Goober5000 - I split this because 1) it's clearer; and 2) initially multiple docked ships would have been
3426 // insanely difficult otherwise
3427 //
3428 // Maybe create the object.
3429 // Don't create the new ship blindly. First, check the sexp for the arrival cue
3430 // to determine when this ship should arrive. If not right away, stick this ship
3431 // onto the ship arrival list to be looked at later. Also check to see if it should use the
3432 // wings arrival cue. The ship may get created later depending on whether or not the wing
3433 // is created.
3434 // Always create ships when FRED is running.
3436 {
3437  // Bail if it was already created. This should only happen when we previously
3438  // created all the objects in a docked group simultaneously.
3439  if (pobjp->created_object != NULL)
3440  {
3441  Assert(object_is_docked(pobjp));
3442  return;
3443  }
3444 
3445  // Goober5000
3446  // shunt this guy to the arrival list if he meets one of the following conditions:
3447  // 1) he's docked but not the dock leader
3448  // 2) this is FS2 (i.e. not FRED2) AND he meets one of the following conditions:
3449  // a) he's not cued to arrive yet
3450  // b) his arrival delay hasn't elapsed
3451  // c) he's reinforcement
3452  if ( (object_is_docked(pobjp) && !(pobjp->flags & P_SF_DOCK_LEADER)) || (!Fred_running && (!eval_sexp(pobjp->arrival_cue) || !timestamp_elapsed(pobjp->arrival_delay) || (pobjp->flags & P_SF_REINFORCEMENT))) )
3453  {
3454  // we can't add ships getting destroyed to the arrival list!!!
3455  Assert (pobjp->destroy_before_mission_time < 0);
3456 
3457  // add to arrival list
3458  list_append(&Ship_arrival_list, pobjp);
3459 
3460  // we need to deal with replacement textures now, so that texture page-in will work properly
3462  }
3463  // ingame joiners bail here.
3465  {
3466  return;
3467  }
3468  // the ship is present at the beginning of the mission, so we create it
3469  else
3470  {
3471  int real_objnum = parse_create_object(pobjp); // this object may later get destroyed depending on wing status!!!!
3472 
3473  // if the ship is supposed to be destroyed before the mission, then blow up the ship and mark the pieces
3474  // as last forever. Only call this stuff when you are blowing up the ship
3475  if (pobjp->destroy_before_mission_time >= 0)
3476  {
3477  object *objp = &Objects[real_objnum];
3478 
3479  // FreeSpace
3480  if (!Fred_running)
3481  {
3482  int i;
3483  shipfx_blow_up_model(objp, Ship_info[Ships[objp->instance].ship_info_index].model_num, 0, 0, &objp->pos);
3484  objp->flags |= OF_SHOULD_BE_DEAD;
3485 
3486  // once the ship is exploded, find the debris pieces belonging to this object, mark them
3487  // as not to expire, and move them forward in time N seconds
3488  for (i = 0; i < MAX_DEBRIS_PIECES; i++)
3489  {
3490  debris *db;
3491 
3492  db = &Debris[i];
3493  if (!(db->flags & DEBRIS_USED)) // not used, move onto the next one.
3494  continue;
3495  if (db->source_objnum != real_objnum) // not from this ship, move to next one
3496  continue;
3497 
3498  debris_clear_expired_flag(db); // mark as don't expire
3499  db->lifeleft = -1.0f; // be sure that lifeleft == -1.0 so that it really doesn't expire!
3500 
3501  // now move the debris along its path for N seconds
3502  objp = &Objects[db->objnum];
3503  physics_sim(&objp->pos, &objp->orient, &objp->phys_info, (float) pobjp->destroy_before_mission_time);
3504  }
3505  }
3506  // FRED
3507  else
3508  {
3509  // be sure to set the variable in the ships structure for the final death time!!!
3512  }
3513  }
3514  }
3515 }
3516 
3518 {
3519  int i;
3520 
3521  // Genghis: used later for subsystem checking
3522  ship_info* sip = &Ship_info[objp->ship_class];
3523 
3524  // set some defaults..
3525  objp->initial_velocity = 0;
3526  objp->initial_hull = 100;
3527  objp->initial_shields = 100;
3528 
3529  // now change defaults if present
3530  if (optional_string("+Initial Velocity:")) {
3531  stuff_int(&objp->initial_velocity);
3532  }
3533 
3534  if (optional_string("+Initial Hull:"))
3535  stuff_int(&objp->initial_hull);
3536  if (optional_string("+Initial Shields:"))
3537  stuff_int(&objp->initial_shields);
3538 
3539  objp->subsys_index = Subsys_index;
3540  objp->subsys_count = 0;
3541  while (optional_string("+Subsystem:")) {
3542  i = allocate_subsys_status();
3543 
3544  objp->subsys_count++;
3545  stuff_string(Subsys_status[i].name, F_NAME, NAME_LENGTH);
3546 
3547  // Genghis: check that the subsystem name makes sense for this ship type
3548  if (subsystem_stricmp(Subsys_status[i].name, NOX("pilot")))
3549  {
3550  int j;
3551  for (j=0; j < sip->n_subsystems; ++j)
3552  if (!subsystem_stricmp(sip->subsystems[j].subobj_name, Subsys_status[i].name))
3553  break;
3554  //if (j == sip->n_subsystems)
3555  //Warning(LOCATION, "Ship \"%s\", class \"%s\"\nUnknown subsystem \"%s\" found in mission!", objp->name, sip->name, Subsys_status[i].name);
3556  }
3557 
3558  if (optional_string("$Damage:"))
3559  stuff_float(&Subsys_status[i].percent);
3560 
3561  Subsys_status[i].subsys_cargo_name = 0;
3562  if (optional_string("+Cargo Name:")) {
3563  char cargo_name[NAME_LENGTH];
3564  stuff_string(cargo_name, F_NAME, NAME_LENGTH);
3565  int index = string_lookup(cargo_name, Cargo_names, Num_cargo, "cargo", 0);
3566  if (index == -1) {
3567  if (Num_cargo < MAX_CARGO) {
3568  index = Num_cargo;
3569  strcpy(Cargo_names[Num_cargo++], cargo_name);
3570  }
3571  else {
3572  WarningEx(LOCATION, "Maximum number of cargo names (%d) exceeded, defaulting to Nothing!", MAX_CARGO);
3573  index = 0;
3574  }
3575  }
3576  Subsys_status[i].subsys_cargo_name = index;
3577  }
3578 
3579  if (optional_string("+AI Class:"))
3580  Subsys_status[i].ai_class = match_and_stuff(F_NAME, Ai_class_names, Num_ai_classes, "AI class");
3581 
3582  if (optional_string("+Primary Banks:"))
3583  stuff_int_list(Subsys_status[i].primary_banks, MAX_SHIP_PRIMARY_BANKS, WEAPON_LIST_TYPE);
3584 
3585  // Goober5000
3586  if (optional_string("+Pbank Ammo:"))
3587  stuff_int_list(Subsys_status[i].primary_ammo, MAX_SHIP_PRIMARY_BANKS, RAW_INTEGER_TYPE);
3588 
3589  if (optional_string("+Secondary Banks:"))
3590  stuff_int_list(Subsys_status[i].secondary_banks, MAX_SHIP_SECONDARY_BANKS, WEAPON_LIST_TYPE);
3591 
3592  if (optional_string("+Sbank Ammo:"))
3593  stuff_int_list(Subsys_status[i].secondary_ammo, MAX_SHIP_SECONDARY_BANKS, RAW_INTEGER_TYPE);
3594 
3595  }
3596 }
3597 
3598 
3603 int get_reassigned_index(team_data *current_team, int ship_class)
3604 {
3605  // Search through the available ships to see if there is a matching ship class in the loadout
3606  for (int i=0; i < current_team->num_ship_choices; i++)
3607  {
3608  if (ship_class == current_team->ship_list[i])
3609  {
3610  if (current_team->ship_count[i] > 0) {
3611  return i;
3612  }
3613  else {
3614  return -1;
3615  }
3616  }
3617  }
3618 
3619  return -1;
3620 }
3621 
3625 void update_loadout_totals(team_data *current_team, int loadout_index)
3626 {
3627  // Fix the loadout variables to show that the class has less available if there are still ships available
3628  if (current_team->ship_count[loadout_index] > 0)
3629  {
3630  Assert (current_team->loadout_total > 0);
3631 
3632  current_team->ship_count[loadout_index]--;
3633  current_team->loadout_total--;
3634  }
3635 }
3636 
3644 {
3645  int loadout_index = -1;
3646 
3647  team_data *data_for_team = &Team_data[p_objp->team];
3648 
3649  // First lets check if the ship specified in the mission file is of an assignable class
3650  loadout_index = get_reassigned_index(data_for_team, p_objp->ship_class);
3651  if (loadout_index != -1 )
3652  {
3653  Assert (data_for_team->loadout_total > 0);
3654 
3655  update_loadout_totals(data_for_team, loadout_index);
3656 
3657  // Since the ship in the mission file matched one available in the loadout we need go no further
3658  return true;
3659  }
3660 
3661  // Now we check the alt_classes (if there are any)
3662  for (SCP_vector<alt_class>::iterator pac = p_objp->alt_classes.begin(); pac != p_objp->alt_classes.end(); ++pac) {
3663  // we don't check availability unless we are asked to
3664  if (pac->default_to_this_class == false) {
3665  loadout_index = pac->ship_class;
3666  break;
3667  }
3668  else {
3669  loadout_index = get_reassigned_index(data_for_team, pac->ship_class);
3670  if (loadout_index != -1 ) {
3671  update_loadout_totals(data_for_team, loadout_index);
3672  break;
3673  }
3674  }
3675  }
3676 
3677  // If we managed to assign a class we'd may need to actually swap to it
3678  if (loadout_index != -1 ) {
3679  if (p_objp->ship_class != data_for_team->ship_list[loadout_index])
3680  {
3681  swap_parse_object(p_objp, data_for_team->ship_list[loadout_index]);
3682  }
3683  return true;
3684  }
3685 
3686  return false;
3687 }
3688 
3694 {
3695  SCP_vector<size_t> reassignments;
3696 
3697  // Loop through all the Parse_objects looking for ships that should be affected by the loadout code.
3698  for (size_t i=0; i < Parse_objects.size(); i++)
3699  {
3700  p_object *p_objp = &Parse_objects[i];
3701  if (p_objp->flags2 & P2_SF2_SET_CLASS_DYNAMICALLY)
3702  {
3703  if (!(is_ship_assignable(p_objp)))
3704  {
3705  // store the ship so we can come back to it later.
3706  reassignments.push_back(i);
3707  }
3708  }
3709  }
3710 
3711  // Now we go though the ships we were unable to assign earlier and reassign them on a first come first
3712  // served basis.
3713  for (size_t m=0; m < reassignments.size(); m++)
3714  {
3715  p_object *p_objp = &Parse_objects[reassignments[m]];
3716  team_data *current_team = &Team_data[p_objp->team];
3717  bool loadout_assigned = false;
3719 
3720  // First thing to check is whether we actually have any ships left to assign
3721  if (current_team->loadout_total == 0)
3722  {
3723  // If there is nothing left to assign we should use the ship in the mission file
3724  loadout_assigned = true;
3725  }
3726  // We do have ships left in the team loadout that we can assign
3727  else
3728  {
3729  // Go through the loadout until we find an unassigned ship
3730  for (int j=0; j < current_team->num_ship_choices; j++)
3731  {
3732  if (current_team->ship_count[j] > 0)
3733  {
3734  update_loadout_totals(current_team, j);
3735  // We will need to assign a new class too (if a p_object the same class was available
3736  // it should have been assigned by attempt_loadout_assignation_from_defaults()
3737  Assert (p_objp->ship_class != current_team->ship_list[j]);
3738  swap_parse_object(p_objp, current_team->ship_list[j]);
3739 
3740  loadout_assigned = true;
3741  break ;
3742  }
3743  }
3744  }
3745 
3746  // We should never reach here with an unassigned loadout
3747  Assert (loadout_assigned);
3748  }
3749 }
3750 
3751 extern int Multi_ping_timestamp;
3752 void parse_objects(mission *pm, int flag)
3753 {
3754  Assert(pm != NULL);
3755 
3756  required_string("#Objects");
3757 
3758  // parse in objects
3759  Parse_objects.clear();
3760  while (required_string_either("#Wings", "$Name:"))
3761  {
3762  p_object pobj;
3763 
3764  // parse a single object
3765  int valid = parse_object(pm, flag, &pobj);
3766 
3767  // not all objects are always valid or legal
3768  if (!valid)
3769  continue;
3770 
3771  // add it
3772  Parse_objects.push_back(pobj);
3773 
3774  // send out a ping if we are multi so that psnet2 doesn't kill us off for a long load
3775  // NOTE that we can't use the timestamp*() functions here since they won't increment
3776  // during this loading process
3777  if (Game_mode & GM_MULTIPLAYER)
3778  {
3779  if ((Multi_ping_timestamp == -1) || (Multi_ping_timestamp <= timer_get_milliseconds()))
3780  {
3782  Multi_ping_timestamp = timer_get_milliseconds() + 10000; // timeout is 10 seconds between pings
3783  }
3784  }
3785  }
3786 
3787  // Goober5000 - I moved the docking stuff to post_process_ships_wings because of interdependencies
3788  // between ships and wings. Neither docking stuff nor ship stuff (for ships present at mission start)
3789  // will be valid until after post_process_ships_wings is run.
3790 
3791  // Karajorma - Now that we've parsed all the objects we can set the class of those which were flagged
3792  // to be set based on the number of ships available in the loadout.
3793  if (!Fred_running)
3794  {
3796  }
3797 }
3798 
3802 void swap_parse_object(p_object *p_obj, int new_ship_class)
3803 {
3804  ship_info *new_ship_info = &Ship_info[new_ship_class];
3805  ship_info *old_ship_info = &Ship_info[p_obj->ship_class];
3806  int subsys_ind = p_obj->subsys_index;
3807  subsys_status *ship_subsystems = &Subsys_status[subsys_ind];
3808 
3809  // Class
3810  // First things first. Change the class of the p_object
3811  p_obj->ship_class = new_ship_class;
3812 
3813  // Hitpoints
3814  // We need to take into account that the ship might have been assigned special hitpoints so we can't
3815  // simply swap old for new.
3816  Assert (p_obj->ship_max_hull_strength > 0);
3817  Assert (old_ship_info->max_hull_strength > 0);
3818 
3819  float hp_multiplier = p_obj->ship_max_hull_strength / old_ship_info->max_hull_strength;
3820  p_obj->ship_max_hull_strength = new_ship_info->max_hull_strength * hp_multiplier;
3821 
3822  // Shields
3823  // Again we have to watch out for special hitpoints but this time we can't assume that there will be a
3824  // shield. So first lets see if there is one.
3825  if ((p_obj->ship_max_shield_strength != old_ship_info->max_shield_strength) &&
3826  (p_obj->ship_max_shield_strength > 0) &&
3827  (new_ship_info->max_shield_strength > 0))
3828  {
3829  // This ship is using special hitpoints to alter the shield strength
3830  float shield_multiplier = p_obj->ship_max_shield_strength / i2fl(old_ship_info->max_shield_strength);
3831  p_obj->ship_max_shield_strength = new_ship_info->max_shield_strength * shield_multiplier;
3832  }
3833  // Not using special hitpoints or a class which has a shield strength of zero
3834  else
3835  {
3836  p_obj->ship_max_shield_strength = new_ship_info->max_shield_strength;
3837  }
3838 
3839  // Primary weapons
3840  // First find out what is the correct number for a ship of this class
3841  int num_pbanks = new_ship_info->num_primary_banks;
3842  // Now cycle through the primary banks looking for banks that were added or removed
3843  for (int i=0; i < MAX_SHIP_PRIMARY_BANKS; i++)
3844  {
3845  // If we're dealing with a primary bank that actually should exist on this ship
3846  if ( i < num_pbanks )
3847  {
3848  // We only care if a weapon hasn't been parsed in for this bank
3849  if (ship_subsystems->primary_banks[i] == -1)
3850  {
3851  // Give the ship the default weapon for this bank.
3852  ship_subsystems->primary_banks[i] = new_ship_info->primary_bank_weapons[i];
3853  }
3854  }
3855  // Any primary banks the ship doesn't have should be set to -1
3856  else
3857  {
3858  ship_subsystems->primary_banks[i] = -1;
3859  }
3860  }
3861 
3862  // Secondary weapons
3863  // Again we first have to find out how many we should have
3864  int num_sbanks = new_ship_info->num_secondary_banks;
3865  // Now cycle through the secondary banks looking for banks that were added or removed
3866  for (int j=0; j < MAX_SHIP_SECONDARY_BANKS; j++)
3867  {
3868  // If we're dealing with a primary bank that actually should exist on this ship
3869  if ( j < num_sbanks )
3870  {
3871  // We only care if a weapon hasn't been parsed in for this bank
3872  if (ship_subsystems->secondary_banks[j] == -1){
3873  // Give the ship the default weapon for this bank.
3874  ship_subsystems->secondary_banks[j] = new_ship_info->secondary_bank_weapons[j];
3875  }
3876  }
3877  // Any secondary banks the ship doesn't have should be set to -1
3878  else
3879  {
3880  ship_subsystems->secondary_banks[j] = -1;
3881  }
3882  }
3883 }
3884 
3886 {
3888 
3889  // look for original ships
3890  for (ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
3891  if (ii->net_signature == net_signature)
3892  return &(*ii);
3893 
3894  // boo
3895  return NULL;
3896 }
3897 
3898 // Goober5000 - also get it by name
3900 {
3902 
3903  // look for original ships
3904  for (ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
3905  if (!stricmp(ii->name, name))
3906  return &(*ii);
3907 
3908  // boo
3909  return NULL;
3910 }
3911 
3913 {
3914  int i;
3915 
3916  for (i = 0; i < Num_wings; i++)
3917  {
3918  if (!stricmp(name, Wings[i].name))
3919  return i;
3920  }
3921 
3922  return -1;
3923 }
3924 
3934 int parse_wing_create_ships( wing *wingp, int num_to_create, int force, int specific_instance )
3935 {
3936  int wingnum, objnum, num_create_save;
3937  int time_to_arrive;
3938  int pre_create_count;
3939  int i, j;
3940 
3941  // we need to send this in multiplayer
3942  pre_create_count = wingp->total_arrived_count;
3943 
3944  // force is used to force creation of the wing -- used for multiplayer
3945  if ( !force ) {
3946  // we only want to evaluate the arrival cue of the wing if:
3947  // 1) single player
3948  // 2) multiplayer and I am the host of the game
3949  // can't create any ships if the arrival cue is false or the timestamp has not elapsed.
3950 
3951  if ( !eval_sexp(wingp->arrival_cue) ) /* || !timestamp_elapsed(wingp->arrival_delay) ) */
3952  return 0;
3953 
3954  // once the sexpressions becomes true, then check the arrival delay on the wing. The first time, the
3955  // arrival delay will be <= 0 meaning that no timer object has been set yet. Set up the timestamp
3956  // which should always give a number >= 0;
3957  if ( wingp->arrival_delay <= 0 ) {
3958  wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
3959  Assert ( wingp->arrival_delay >= 0 );
3960  }
3961 
3962  if ( !timestamp_elapsed( wingp->arrival_delay ) )
3963  return 0;
3964 
3965  // if wing is coming from docking bay, then be sure that ship we are arriving from actually exists
3966  // (or will exist).
3967  if ( wingp->arrival_location == ARRIVE_FROM_DOCK_BAY ) {
3968  int shipnum;
3969  char *name;
3970 
3971  Assert( wingp->arrival_anchor >= 0 );
3972  name = Parse_names[wingp->arrival_anchor];
3973 
3974  // see if ship is in mission.
3975  shipnum = ship_name_lookup( name );
3976  if ( shipnum == -1 ) {
3977  int num_remaining;
3978 
3979  // see if ship is yet to arrive. If so, then return 0 so we can evaluate again later.
3981  return 0;
3982 
3983  // since this wing cannot arrive from this place, we need to mark the wing as destroyed and
3984  // set the wing variables appropriately. Good for directives.
3985 
3986  // set the gone flag
3987  wingp->flags |= WF_WING_GONE;
3988 
3989  // mark the number of waves and number of ships destroyed equal to the last wave and the number
3990  // of ships yet to arrive
3991  num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count);
3992  wingp->total_arrived_count += num_remaining;
3993  wingp->current_wave = wingp->num_waves;
3994 
3995  if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) || mission_log_get_time(LOG_SELF_DESTRUCTED, name, NULL, NULL) ) {
3996  wingp->total_destroyed += num_remaining;
3997  } else if ( mission_log_get_time(LOG_SHIP_DEPARTED, name, NULL, NULL) ) {
3998  wingp->total_departed += num_remaining;
3999  } else {
4000  wingp->total_vanished += num_remaining;
4001  }
4002 
4003  mission_parse_mark_non_arrival(wingp); // Goober5000
4004  return 0;
4005  }
4006 
4007  // Goober5000 - check status of fighterbays - if they're destroyed, we can't launch - but we want to reeval later
4008  if (ship_fighterbays_all_destroyed(&Ships[shipnum]))
4009  return 0;
4010  }
4011 
4012  if ( num_to_create == 0 )
4013  return 0;
4014 
4015  // check the wave_delay_timestamp field. If it is not valid, make it valid (based on wave delay min
4016  // and max values). If it is valid, and not elapsed, then return. If it is valid and elasped, then
4017  // continue on.
4018  if ( !timestamp_valid(wingp->wave_delay_timestamp) ) {
4019 
4020  // if at least one of these is valid, then reset the timestamp. If they are both zero, we will create the
4021  // wave
4022  if ( (wingp->wave_delay_min > 0) || (wingp->wave_delay_max > 0) ) {
4023  Assert ( wingp->wave_delay_min <= wingp->wave_delay_max );
4024  time_to_arrive = wingp->wave_delay_min + (int)(frand() * (wingp->wave_delay_max - wingp->wave_delay_min));
4025 
4026  // MWA -- 5/18/98
4027  // HACK HACK -- in the presense of Mike Comet and Mitri, I have introduced one of the most
4028  // serious breaches of coding standards. I'm to lazy to fix this the correct way. Insert
4029  // a delay before the next wave of the wing can arrive to that clients in the game have ample
4030  // time to kill off any ships in the wing before the next wave arrives.
4031  if ( Game_mode & GM_MULTIPLAYER ){
4032  time_to_arrive += 7;
4033  }
4034  wingp->wave_delay_timestamp = timestamp(time_to_arrive * 1000);
4035  return 0;
4036  }
4037 
4038  // if we get here, both min and max values are 0; See comments above for a most serious hack
4039  time_to_arrive = 0;
4040  if ( Game_mode & GM_MULTIPLAYER )
4041  time_to_arrive += 7;
4042  time_to_arrive *= 1000;
4043  wingp->wave_delay_timestamp = timestamp(time_to_arrive);
4044  }
4045 
4046  // now check to see if the wave_delay_timestamp is elapsed or not
4047  if ( !timestamp_elapsed(wingp->wave_delay_timestamp) )
4048  return 0;
4049  }
4050 
4051  // finally we can create the wing.
4052 
4053  num_create_save = num_to_create;
4054 
4055  wingnum = WING_INDEX(wingp); // get the wing number
4056 
4057  // if there are no ships to create, then all ships must be player start ships -- do nothing in this case.
4058  if ( num_to_create == 0 ){
4059  return 0;
4060  }
4061 
4062  wingp->current_wave++; // we are creating new ships
4063  // we need to create num_to_create ships. Since the arrival cues for ships in a wing
4064  // are ignored, then *all* ships must be in the Ship_arrival_list.
4065 
4066  objnum = -1;
4067 
4068  // Goober5000 - we have to do this via the array because we have no guarantee we'll be able to iterate along the list
4069  // (since created objects plus anything they're docked to will be removed from it)
4070  for (SCP_vector<p_object>::iterator ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
4071  {
4072  int index;
4073  ai_info *aip;
4074  p_object *p_objp = &(*ii);
4075 
4076  // ensure on arrival list
4077  if (!parse_object_on_arrival_list(p_objp))
4078  continue;
4079 
4080  // compare the wingnums. When they are equal, we can create the ship. In the case of
4081  // wings that have multiple waves, this code implies that we essentially creating clones
4082  // of the ships that were created in Fred for the wing when more ships for a new wave
4083  // arrive. The threshold value of a wing can also make one of the ships in a wing be "cloned"
4084  // more often than other ships in the wing. I don't think this matters much.
4085  if (p_objp->wingnum != wingnum)
4086  continue;
4087 
4088  Assert( (p_objp->pos_in_wing >= 0) && (p_objp->pos_in_wing < MAX_SHIPS_PER_WING) );
4089 
4090  // when ingame joining, we need to create a specific ship out of the list of ships for a
4091  // wing. specific_instance is a 0 based integer which specified which ship in the wing
4092  // to create. So, only create the ship we actually need to.
4093  if ((Game_mode & GM_MULTIPLAYER) && (specific_instance > 0))
4094  {
4095  specific_instance--;
4096  continue;
4097  }
4098  // when not creating a specific ship, we should skip over any ships that weren't carried along in the red-alert
4099  else if (p_objp->flags2 & P2_RED_ALERT_DELETED)
4100  {
4101  num_to_create--;
4102  num_create_save--;
4103  ship_add_ship_type_count(p_objp->ship_class, -1);
4104  wingp->red_alert_skipped_ships++;
4105 
4106  // clear the flag so that this parse object can be used for the next wave
4107  p_objp->flags2 &= ~P2_RED_ALERT_DELETED;
4108 
4109  // skip over this parse object
4110  if (num_to_create == 0)
4111  break;
4112  else
4113  continue;
4114  }
4115 
4116  Assert (!(p_objp->flags & P_SF_CANNOT_ARRIVE)); // get allender
4117 
4118  // if we have the maximum number of ships in the wing, we must bail as well
4119  if (wingp->current_count >= MAX_SHIPS_PER_WING)
4120  {
4121  Int3(); // this is bogus -- we should always allow all ships to be created
4122  num_to_create = 0;
4123  break;
4124  }
4125 
4126  // bash the ship name to be the name of the wing + some number if there is > 1 wave in this wing
4127  wingp->total_arrived_count++;
4128  if (wingp->num_waves > 1)
4129  wing_bash_ship_name(p_objp->name, wingp->name, wingp->total_arrived_count + wingp->red_alert_skipped_ships);
4130 
4131  // also, if multiplayer, set the parse object's net signature to be wing's net signature
4132  // base + total_arrived_count (before adding 1)
4133  if (Game_mode & GM_MULTIPLAYER)
4134  {
4135  p_objp->net_signature = (ushort) (wingp->net_signature + wingp->total_arrived_count);
4136  }
4137 
4138 
4139  objnum = parse_create_object(p_objp);
4140  aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
4141 
4142  // copy any goals from the wing to the newly created ship
4143  for (index = 0; index < MAX_AI_GOALS; index++)
4144  {
4145  if (wingp->ai_goals[index].ai_mode != AI_GOAL_NONE)
4146  ai_copy_mission_wing_goal(&wingp->ai_goals[index], aip);
4147  }
4148 
4149  Ai_info[Ships[Objects[objnum].instance].ai_index].wing = wingnum;
4150 
4151  if (wingp->flags & WF_NO_DYNAMIC)
4152  aip->ai_flags |= AIF_NO_DYNAMIC;
4153 
4154  // update housekeeping variables
4155  // NOTE: for the initial wing setup we use actual position to get around
4156  // object order isses, but ships in all following waves just get
4157  // tacked onto the end of the list
4158  if (wingp->current_wave == 1) {
4159  wingp->ship_index[p_objp->pos_in_wing] = Objects[objnum].instance;
4160  } else {
4161  wingp->ship_index[wingp->current_count] = Objects[objnum].instance;
4162  }
4163 
4164  // set up wingman status index
4165  hud_wingman_status_set_index(wingp, &Ships[Objects[objnum].instance], p_objp);
4166 
4167  p_objp->wing_status_wing_index = Ships[Objects[objnum].instance].wing_status_wing_index;
4168  p_objp->wing_status_wing_pos = Ships[Objects[objnum].instance].wing_status_wing_pos;
4169 
4170  wingp->current_count++;
4171 
4172  // keep any player ship on the parse object list -- used for respawns
4173  // 5/8/98 -- MWA -- don't remove ships from the list when you are ingame joining
4174  if (!(p_objp->flags & P_OF_PLAYER_START))
4175  {
4177  {
4178  // only remove ship if one wave in wing
4179  if (wingp->num_waves == wingp->current_wave)
4180  {
4181  // remove p_objp from the list
4182  list_remove(&Ship_arrival_list, p_objp);
4183 
4184  // free up sexp nodes for reuse
4185  if (p_objp->ai_goals != -1)
4186  free_sexp2(p_objp->ai_goals);
4187  }
4188  }
4189  }
4190 
4191  // flag ship with SF_FROM_PLAYER_WING if a member of player starting wings
4192  if (MULTI_TEAM)
4193  {
4194  // different for tvt -- Goober5000
4195  for (j = 0; j < MAX_TVT_WINGS; j++)
4196  {
4197  if (!stricmp(TVT_wing_names[j], wingp->name))
4198  Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING;
4199  }
4200  }
4201  else
4202  {
4203  for (j = 0; j < MAX_STARTING_WINGS; j++)
4204  {
4205  if (!stricmp(Starting_wing_names[j], wingp->name))
4206  Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING;
4207  }
4208  }
4209 
4210  // keep track of how many ships to create. Stop when we have done all that we are supposed to do.
4211  num_to_create--;
4212  if (num_to_create == 0)
4213  break;
4214  }
4215 
4216  // we should always have enough ships in the list!!!
4217  Assert (num_to_create == 0);
4218 
4219  // wing current_count needs to match the end of the ship_index[] list, but there
4220  // is a very off chance it could have holes in it (especially if it's a red-alert
4221  // wing that arrives late), so make sure to compact the list
4222  int length = MAX_SHIPS_PER_WING;
4223  for (i = 0; i < length; i++)
4224  {
4225  if (wingp->ship_index[i] == -1)
4226  {
4227  // shift actual values downward
4228  for (j = i; j < length - 1; j++)
4229  {
4230  wingp->ship_index[j] = wingp->ship_index[j+1];
4231 
4232  // update "special" ship too
4233  if (wingp->special_ship == j+1)
4234  wingp->special_ship--;
4235  }
4236 
4237  // last value becomes -1
4238  wingp->ship_index[j] = -1;
4239  length--;
4240 
4241  // stay on the current index in case we still have a -1
4242  i--;
4243  }
4244  }
4245 
4246  // possibly play some event driven music here. Send a network packet indicating the wing was
4247  // created. Only do this stuff if actually in the mission.
4248  if ( (objnum != -1) && (Game_mode & GM_IN_MISSION) ) { // if true, we have created at least one new ship.
4249  int it, ship_num;
4250 
4251  // see if this wing is a player starting wing, and if so, call the maybe_add_form_goal
4252  // function to possibly make the wing form on the player
4253  for (it = 0; it < MAX_STARTING_WINGS; it++ ) {
4254  if ( Starting_wings[it] == wingnum ){
4255  break;
4256  }
4257  }
4258  if ( it < MAX_STARTING_WINGS ){
4259  ai_maybe_add_form_goal( wingp );
4260  }
4261 
4262  mission_log_add_entry( LOG_WING_ARRIVED, wingp->name, NULL, wingp->current_wave );
4263  ship_num = wingp->ship_index[0];
4264 
4265  if ( !(Ships[ship_num].flags & SF_NO_ARRIVAL_MUSIC) && !(wingp->flags & WF_NO_ARRIVAL_MUSIC) ) {
4266  if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) {
4267  Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION);
4268  event_music_arrival(Ships[ship_num].team);
4269  }
4270  }
4271 
4272  // possibly change the location where these ships arrive based on the wings arrival location
4273  mission_set_wing_arrival_location( wingp, num_create_save );
4274 
4275  // if in multiplayer (and I am the host) and in the mission, send a wing create command to all
4276  // other players
4277  if ( MULTIPLAYER_MASTER ){
4278  send_wing_create_packet( wingp, num_create_save, pre_create_count );
4279  }
4280 
4281 #ifndef NDEBUG
4282  // test code to check to be sure that all ships in the wing are ignoring the same types
4283  // of orders from the leader
4284  if ( Fred_running ) {
4285  Assert( wingp->ship_index[wingp->special_ship] != -1 );
4286  int orders = Ships[wingp->ship_index[0]].orders_accepted;
4287  for (it = 0; it < wingp->current_count; it++ ) {
4288  if (it == wingp->special_ship)
4289  continue;
4290 
4291  if ( orders != Ships[wingp->ship_index[it]].orders_accepted ) {
4292  Warning(LOCATION, "ships in wing %s are ignoring different player orders. Please find Mark A\nto talk to him about this.", wingp->name );
4293  break;
4294  }
4295  }
4296  }
4297 #endif
4298 
4299  }
4300 
4301  wingp->wave_delay_timestamp = timestamp(-1); // we will need to set this up properly for the next wave
4302  return num_create_save;
4303 }
4304 
4306 {
4307  int wingnum, i, wing_goals, delay, saved_arrival_delay;
4308  char name[NAME_LENGTH], ship_names[MAX_SHIPS_PER_WING][NAME_LENGTH];
4309  char wing_flag_strings[MAX_WING_FLAGS][NAME_LENGTH];
4310  wing *wingp;
4311 
4312  Assert(pm != NULL);
4313  wingp = &Wings[Num_wings];
4314 
4315  required_string("$Name:");
4316  stuff_string(wingp->name, F_NAME, NAME_LENGTH);
4317  wingnum = find_wing_name(wingp->name);
4318  if (wingnum != -1)
4319  error_display(0, NOX("Redundant wing name: %s\n"), wingp->name);
4320  wingnum = Num_wings;
4321 
4322  wingp->total_arrived_count = 0;
4323  wingp->red_alert_skipped_ships = 0;
4324  wingp->total_destroyed = 0;
4325  wingp->total_departed = 0; // Goober5000
4326  wingp->total_vanished = 0; // Goober5000
4327  wingp->flags = 0;
4328 
4329  // squad logo - Goober5000
4330  if (optional_string("+Squad Logo:"))
4331  {
4332  int flag = -1;
4333 
4335 
4336  // load it only if FRED isn't running
4337  if (!Fred_running)
4338  {
4339  // check all previous wings to see if we already loaded it (we want to save memory)
4340  for (i = 0; i < Num_wings; i++)
4341  {
4342  // do we have a previous texture?
4343  if (Wings[i].wing_insignia_texture != -1)
4344  {
4345  // if we have a match
4346  if (!stricmp(Wings[i].wing_squad_filename, wingp->wing_squad_filename))
4347  {
4348  flag = i;
4349  break;
4350  }
4351  }
4352  }
4353 
4354  // if we have loaded it already, just use the old bitmap index
4355  if (flag != -1)
4356  {
4358  }
4359  else
4360  {
4361  wing_load_squad_bitmap(wingp);
4362  }
4363  }
4364  }
4365 
4366  required_string("$Waves:");
4367  stuff_int(&wingp->num_waves);
4368  Assert ( wingp->num_waves >= 1 ); // there must be at least 1 wave
4369 
4370  wingp->current_wave = 0;
4371 
4372  required_string("$Wave Threshold:");
4373  stuff_int(&wingp->threshold);
4374 
4375  required_string("$Special Ship:");
4376  stuff_int(&wingp->special_ship);
4377 
4378  wingp->arrival_anchor = -1;
4379  wingp->arrival_distance = 0;
4380  wingp->arrival_path_mask = -1; // -1 only until resolved
4381 
4382  find_and_stuff("$Arrival Location:", &wingp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location");
4383 
4384  if ( optional_string("+Arrival Distance:") )
4385  {
4386  stuff_int( &wingp->arrival_distance );
4387 
4388  // Goober5000
4389  if ((wingp->arrival_distance <= 0) && ((wingp->arrival_location == ARRIVE_NEAR_SHIP) || (wingp->arrival_location == ARRIVE_IN_FRONT_OF_SHIP)))
4390  {
4391  Warning(LOCATION, "Arrival distance for wing %s cannot be %d. Setting to 1.\n", wingp->name, wingp->arrival_distance);
4392  wingp->arrival_distance = 1;
4393  }
4394  }
4395 
4396  if ( wingp->arrival_location != ARRIVE_AT_LOCATION )
4397  {
4398  required_string("$Arrival Anchor:");
4400  wingp->arrival_anchor = get_anchor(name);
4401  }
4402 
4403  if (optional_string("+Arrival Paths:"))
4404  {
4405  // temporarily use mask to point to the restriction index
4407  }
4408 
4409  if (optional_string("+Arrival delay:")) {
4410  stuff_int(&delay);
4411  if ( delay < 0 )
4412  Error(LOCATION, "Cannot have arrival delay < 0 on wing %s", wingp->name );
4413  } else
4414  delay = 0;
4415  saved_arrival_delay = delay;
4416 
4417  if ( !Fred_running ){
4418  wingp->arrival_delay = -delay;
4419  } else {
4420  wingp->arrival_delay = delay;
4421  }
4422 
4423  required_string("$Arrival Cue:");
4424  wingp->arrival_cue = get_sexp_main();
4425  if ( !Fred_running && (wingp->arrival_cue >= 0) ) {
4426  if ( eval_sexp(wingp->arrival_cue) ) // evaluate to determine if sexp is always false.
4427  wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 );
4428  }
4429 
4430 
4431  wingp->departure_anchor = -1;
4432  wingp->departure_path_mask = -1; // -1 only until resolved
4433 
4434  find_and_stuff("$Departure Location:", &wingp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location");
4435 
4436  if ( wingp->departure_location != DEPART_AT_LOCATION )
4437  {
4438  required_string("$Departure Anchor:");
4439  stuff_string( name, F_NAME, NAME_LENGTH );
4440  wingp->departure_anchor = get_anchor(name);
4441  }
4442 
4443  if (optional_string("+Departure Paths:"))
4444  {
4445  // temporarily use mask to point to the restriction index
4447  }
4448 
4449  if (optional_string("+Departure delay:")) {
4450  stuff_int(&delay);
4451  if ( delay < 0 )
4452  Error(LOCATION, "Cannot have departure delay < 0 on wing %s", wingp->name );
4453  } else
4454  delay = 0;
4455 
4456  if ( !Fred_running )
4457  wingp->departure_delay = -delay; // use negative numbers to mean that delay timer not yet set
4458  else
4459  wingp->departure_delay = delay;
4460 
4461  required_string("$Departure Cue:");
4462  wingp->departure_cue = get_sexp_main();
4463 
4464  // stores a list of all names of ships in the wing
4465  required_string("$Ships:");
4466  wingp->wave_count = stuff_string_list( ship_names, MAX_SHIPS_PER_WING );
4467  wingp->current_count = 0;
4468 
4469  // get the wings goals, if any
4470  wing_goals = -1;
4471  if ( optional_string("$AI Goals:") )
4472  wing_goals = get_sexp_main();
4473 
4474  wingp->hotkey = -1;
4475  if (optional_string("+Hotkey:")) {
4476  stuff_int(&wingp->hotkey);
4477  Assert((wingp->hotkey >= 0) && (wingp->hotkey < 10));
4478  }
4479 
4480  if (optional_string("+Flags:")) {
4481  int count;
4482 
4483  count = stuff_string_list( wing_flag_strings, MAX_WING_FLAGS );
4484  for (i = 0; i < count; i++ ) {
4485  if ( !stricmp( wing_flag_strings[i], NOX("ignore-count")) )
4486  wingp->flags |= WF_IGNORE_COUNT;
4487  else if ( !stricmp( wing_flag_strings[i], NOX("reinforcement")) )
4488  wingp->flags |= WF_REINFORCEMENT;
4489  else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-music")) )
4490  wingp->flags |= WF_NO_ARRIVAL_MUSIC;
4491  else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-message")) )
4492  wingp->flags |= WF_NO_ARRIVAL_MESSAGE;
4493  else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-warp")) )
4494  wingp->flags |= WF_NO_ARRIVAL_WARP;
4495  else if ( !stricmp( wing_flag_strings[i], NOX("no-departure-warp")) )
4496  wingp->flags |= WF_NO_DEPARTURE_WARP;
4497  else if ( !stricmp( wing_flag_strings[i], NOX("no-dynamic")) )
4498  wingp->flags |= WF_NO_DYNAMIC;
4499  else if ( !stricmp( wing_flag_strings[i], NOX("nav-carry-status")) )
4500  wingp->flags |= WF_NAV_CARRY;
4501  else
4502  Warning(LOCATION, "unknown wing flag\n%s\n\nSkipping.", wing_flag_strings[i]);
4503  }
4504  }
4505 
4506  // get the wave arrival delay bounds (if present). Used as lower and upper bounds (in seconds)
4507  // which determine when new waves of a wing should arrive.
4508  wingp->wave_delay_min = 0;
4509  wingp->wave_delay_max = 0;
4510  if ( optional_string("+Wave Delay Min:") )
4511  stuff_int( &(wingp->wave_delay_min) );
4512  if ( optional_string("+Wave Delay Max:") )
4513  stuff_int( &(wingp->wave_delay_max) );
4514 
4515  // be sure to set the wave arrival timestamp of this wing to pop right away so that the
4516  // wing could be created if it needs to be
4517  wingp->wave_delay_timestamp = timestamp(0);
4518 
4519  // initialize wing goals
4520  for (i=0; i<MAX_AI_GOALS; i++) {
4521  wingp->ai_goals[i].ai_mode = AI_GOAL_NONE;
4522  wingp->ai_goals[i].signature = -1;
4523  wingp->ai_goals[i].priority = -1;
4524  wingp->ai_goals[i].flags = 0;
4525  }
4526 
4527  // 7/13/98 -- MWA
4528  // error checking against the player ship wings (i.e. starting & tvt) to be sure that wave count doesn't exceed one for
4529  // these wings.
4530  if ( MULTI_NOT_TEAM ) {
4531  for (i = 0; i < MAX_STARTING_WINGS; i++ ) {
4532  if ( !stricmp(Starting_wing_names[i], wingp->name) ) {
4533  if ( wingp->num_waves > 1 ) {
4534  // only end the game if we're the server - clients will eventually find out :)
4537  }
4538  }
4539  }
4540  }
4541  }
4542  else if (MULTI_TEAM) {
4543  for (i = 0; i < MAX_TVT_WINGS; i++ ) {
4544  if ( !stricmp(TVT_wing_names[i], wingp->name) ) {
4545  if ( wingp->num_waves > 1 ) {
4546  // only end the game if we're the server - clients will eventually find out :)
4549  }
4550  }
4551  }
4552  }
4553  }
4554 
4555  // Get the next starting signature for this in this wing. We want to reserve wave_count * num_waves
4556  // of signature. These can be used to construct wings for ingame joiners.
4557  if ( Game_mode & GM_MULTIPLAYER ) {
4558  int next_signature;
4559 
4561  next_signature = wingp->net_signature + (wingp->wave_count * wingp->num_waves);
4562  if ( next_signature > SHIP_SIG_MAX )
4563  Error(LOCATION, "Too many total ships in mission (%d) for network signature assignment", SHIP_SIG_MAX);
4565  }
4566 
4567  for (i=0; i<MAX_SHIPS_PER_WING; i++)
4568  wingp->ship_index[i] = -1;
4569 
4570  // set up the ai_goals for this wing -- all ships created from this wing will inherit these goals
4571  // goals for the wing are stored slightly differently than for ships. We simply store the index
4572  // into the sexpression array of each goal (max 10). When a ship in this wing is created, each
4573  // goal in the wings goal array is given to the ship.
4574  if ( wing_goals != -1 ) {
4575  int sexp;
4576 
4577  // this will assign the goals to the wings as well as to any ships in the wing that have been
4578  // already created.
4579  for ( sexp = CDR(wing_goals); sexp != -1; sexp = CDR(sexp) )
4580  ai_add_wing_goal_sexp(sexp, AIG_TYPE_EVENT_WING, wingnum); // used by Fred
4581 
4582  free_sexp2(wing_goals); // free up sexp nodes for reuse, since they aren't needed anymore.
4583  }
4584 
4585  // set the wing number for all ships in the wing
4586  for (i = 0; i < wingp->wave_count; i++ ) {
4587  char *ship_name = ship_names[i];
4588  int assigned = 0;
4589 
4590  // Goober5000 - since the ship/wing creation stuff is reordered to accommodate multiple docking,
4591  // everything is still only in the parse array at this point (in both FRED and FS2)
4592 
4593  // find the parse object and assign it the wing number
4594  for (SCP_vector<p_object>::iterator p_objp = Parse_objects.begin(); p_objp != Parse_objects.end(); ++p_objp) {
4595  if ( !strcmp(ship_name, p_objp->name) ) {
4596  // get Allender -- ship appears to be in multiple wings
4597  Assert (p_objp->wingnum == -1);
4598 
4599  // assign wingnum
4600  p_objp->wingnum = wingnum;
4601  p_objp->pos_in_wing = i;
4602  assigned++;
4603 
4604  // Goober5000 - if this is a player start object, there shouldn't be a wing arrival delay (Mantis #2678)
4605  if ((p_objp->flags & P_OF_PLAYER_START) && (saved_arrival_delay != 0)) {
4606  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);
4607  if (!Fred_running && wingp->arrival_delay > 0) {
4608  // timestamp has been set, so set it again
4609  wingp->arrival_delay = timestamp(0);
4610  } else {
4611  // no timestamp, or timestamp invalid
4612  wingp->arrival_delay = 0;
4613  }
4614  // prevent message from reappearing
4615  saved_arrival_delay = 0;
4616  }
4617  }
4618  }
4619 
4620  // error checking
4621  if (assigned == 0) {
4622  Error(LOCATION, "Cannot load mission -- for wing %s, ship %s is not present in #Objects section.\n", wingp->name, ship_name);
4623  } else if (assigned > 1) {
4624  Error(LOCATION, "Cannot load mission -- for wing %s, ship %s is specified multiple times in wing.\n", wingp->name, ship_name);
4625  }
4626  }
4627 
4628  // Goober5000 - wing creation stuff moved to post_process_ships_wings
4629 }
4630 
4632 {
4633  required_string("#Wings");
4634  while (required_string_either("#Events", "$Name:"))
4635  {
4637  parse_wing(pm);
4638  Num_wings++;
4639  }
4640 }
4641 
4642 // Goober5000
4643 void resolve_path_masks(int anchor, int *path_mask)
4644 {
4645  path_restriction_t *prp;
4646 
4647  // if we have no restrictions, do a quick out
4648  if (*path_mask < 0)
4649  {
4650  *path_mask = 0;
4651  return;
4652  }
4653 
4654  // get path restriction info
4655  prp = &Path_restrictions[*path_mask];
4656 
4657  // uninitialized; compute the mask from scratch
4658  if (prp->cached_mask & (1 << MAX_SHIP_BAY_PATHS))
4659  {
4660  int j, bay_path, modelnum;
4661  p_object *parent_pobjp;
4662 
4663  // get anchor ship
4664  Assert(!(anchor & SPECIAL_ARRIVAL_ANCHOR_FLAG));
4665  parent_pobjp = mission_parse_get_parse_object(Parse_names[anchor]);
4666 
4667  // Load the anchor ship model with subsystems and all; it'll need to be done for this mission anyway
4668  ship_info *sip = &Ship_info[parent_pobjp->ship_class];
4669  modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
4670 
4671  // resolve names to indexes
4672  *path_mask = 0;
4673  for (j = 0; j < prp->num_paths; j++)
4674  {
4675  bay_path = model_find_bay_path(modelnum, prp->path_names[j]);
4676  if (bay_path < 0)
4677  continue;
4678 
4679  *path_mask |= (1 << bay_path);
4680  }
4681 
4682  // cache the result
4683  prp->cached_mask = *path_mask;
4684  }
4685  // already computed; so reuse it
4686  else
4687  {
4688  *path_mask = prp->cached_mask;
4689  }
4690 }
4691 
4698 {
4699  int i;
4700  wing *wingp;
4701 
4702  // take care of parse objects (ships)
4703  for (SCP_vector<p_object>::iterator pobjp = Parse_objects.begin(); pobjp != Parse_objects.end(); ++pobjp)
4704  {
4705  resolve_path_masks(pobjp->arrival_anchor, &pobjp->arrival_path_mask);
4706  resolve_path_masks(pobjp->departure_anchor, &pobjp->departure_path_mask);
4707  }
4708 
4709  // take care of wings
4710  for (i = 0; i < Num_wings; i++)
4711  {
4712  wingp = &Wings[i];
4713 
4716  }
4717 }
4718 
4719 // Goober5000
4721 {
4722  int i;
4723 
4724  // Goober5000 - first, resolve the path masks. Needs to be done first because
4725  // mission_parse_maybe_create_parse_object relies on it.
4727 
4728  // Goober5000 - now that we've parsed all the objects, resolve the initially docked references.
4729  // This must be done before anything that relies on the dock references but can't be done until
4730  // both ships and wings have been parsed.
4732 
4733  // Goober5000 - now create all objects that we can. This must be done before any ship stuff
4734  // but can't be done until the dock references are resolved. This was originally done
4735  // in parse_object().
4736  for (SCP_vector<p_object>::iterator ii = Parse_objects.begin(); ii != Parse_objects.end(); ++ii)
4737  {
4739  }
4740 
4741 
4742  // ----------------- at this point the ships have been created -----------------
4743  // Now set up the wings. This must be done after both dock stuff and ship stuff.
4744 
4745  // error checking for custom wings
4746  if (strcmp(Starting_wing_names[0], TVT_wing_names[0]))
4747  {
4748  Error(LOCATION, "The first starting wing and the first team-versus-team wing must have the same wing name.\n");
4749  }
4750 
4751  // Goober5000 - for FRED, the ships are initialized after the wings, so we must now tell the wings
4752  // where their ships are
4753  if (Fred_running)
4754  {
4755  // even though the ships are already created, only the parse objects know the wing info
4756  for (SCP_vector<p_object>::iterator p_objp = Parse_objects.begin(); p_objp != Parse_objects.end(); ++p_objp)
4757  {
4758  // link the ship into the wing's array of ship indices (previously done in parse_wing
4759  // in a rather less backwards way)
4760  if (p_objp->wingnum >= 0)
4761  {
4762  int shipnum = ship_name_lookup(p_objp->name);
4763 
4764  Assert(shipnum >= 0 && shipnum < MAX_SHIPS);
4765  Assert(p_objp->pos_in_wing >= 0 && p_objp->pos_in_wing < MAX_SHIPS_PER_WING);
4766 
4767  Wings[p_objp->wingnum].ship_index[p_objp->pos_in_wing] = shipnum;
4768  }
4769  }
4770  }
4771  // Goober5000 - for FS2, the wings are initialized first, so all we have to do is create their ships
4772  else
4773  {
4774  for (i = 0; i < Num_wings; i++)
4775  {
4776  wing *wingp = &Wings[i];
4777 
4778  // create the wing if is isn't a reinforcement.
4779  if (!(wingp->flags & WF_REINFORCEMENT))
4780  parse_wing_create_ships(wingp, wingp->wave_count);
4781  }
4782  }
4783 }
4784 
4785 // mission events are sexpressions which cause things to happen based on the outcome
4786 // of other events in a mission. Essentially scripting the different things that can happen
4787 // in a mission
4788 
4790 {
4791  char buf[NAME_LENGTH];
4793 
4795  event->chain_delay = -1;
4796 
4797  required_string( "$Formula:" );
4798  event->formula = get_sexp_main();
4799 
4800  if (optional_string("+Name:")){
4801  stuff_string(event->name, F_NAME, NAME_LENGTH);
4802  } else {
4803  event->name[0] = 0;
4804  }
4805 
4806  if ( optional_string("+Repeat Count:")){
4807  stuff_int( &(event->repeat_count) );
4808  } else {
4809  event->repeat_count = 1;
4810  }
4811 
4812  if ( optional_string("+Trigger Count:")){
4813  stuff_int( &(event->trigger_count) );
4814  // if we have a trigger count but no repeat count, we want the event to loop until it has triggered enough times
4815  if (event->repeat_count == 1) {
4816  event->repeat_count = -1;
4817  }
4818  event->flags |= MEF_USING_TRIGGER_COUNT;
4819  }
4820  else {
4821  event->trigger_count = 1;
4822  }
4823 
4824  event->interval = -1;
4825  if ( optional_string("+Interval:")){
4826  stuff_int( &(event->interval) );
4827  }
4828 
4829  event->score = 0;
4830  if ( optional_string("+Score:") ){
4831  stuff_int(&event->score);
4832  }
4833 
4834  if ( optional_string("+Chained:") ){
4835  stuff_int(&event->chain_delay);
4836  }
4837 
4838  if ( optional_string("+Objective:") ) {
4840  event->objective_text = vm_strdup(buf);
4841  } else {
4842  event->objective_text = NULL;
4843  }
4844 
4845  if ( optional_string("+Objective key:") ) {
4847  event->objective_key_text = vm_strdup(buf);
4848  } else {
4849  event->objective_key_text = NULL;
4850  }
4851 
4852  event->team = -1;
4853  if( optional_string("+Team:") ) {
4854  stuff_int(&event->team);
4855 
4856  // sanity check
4857  if (event->team < -1 || event->team >= MAX_TVT_TEAMS) {
4858  if (Fred_running && !Warned_about_team_out_of_range) {
4859  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.");
4860  Warned_about_team_out_of_range = true;
4861  }
4862  event->team = -1;
4863  }
4864  }
4865 
4866  if( optional_string("+Event Log Flags:") ) {
4868 
4869  stuff_string_list(buffer);
4870  for (int i = 0; i < (int)buffer.size(); i++) {
4871  int add_flag = 1;
4872 
4873  for (int j = 0; j < MAX_MISSION_EVENT_LOG_FLAGS; j++) {
4874  if (!stricmp(buffer[i].c_str(), Mission_event_log_flags[j])) {
4875  // bitshift add_flag so that it equals the index of the flag in Mission_event_log_flags[]
4876  add_flag = add_flag << j;
4877  event->mission_log_flags |= add_flag;
4878  }
4879  }
4880  }
4881  }
4882 
4883  event->timestamp = timestamp(-1);
4884 
4885  // sanity check on the repeat count variable
4886  // _argv[-1] - negative repeat count is now legal; means repeat indefinitely.
4887  if ( event->repeat_count == 0 ){
4888  Error (LOCATION, "Repeat count for mission event %s is 0.\nMust be >= 1 or negative!", event->name );
4889  }
4890 }
4891 
4893 {
4894  required_string("#Events");
4895 
4896  while (required_string_either( "#Goals", "$Formula:")) {
4898  parse_event(pm);
4900  }
4901 }
4902 
4904 {
4905  int dummy;
4906 
4907  mission_goal *goalp;
4908 
4909  goalp = &Mission_goals[Num_goals++];
4910 
4912  Assert(pm != NULL);
4913 
4914  find_and_stuff("$Type:", &goalp->type, F_NAME, Goal_type_names, Num_goal_type_names, "goal type");
4915 
4916  required_string("+Name:");
4917  stuff_string(goalp->name, F_NAME, NAME_LENGTH);
4918 
4919  // backwards compatibility for old Fred missions - all new missions should use $MessageNew
4920  if(optional_string("$Message:")){
4922  } else {
4923  required_string("$MessageNew:");
4925  }
4926 
4927  if (optional_string("$Rating:")){
4928  stuff_int(&dummy); // not used
4929  }
4930 
4931  required_string("$Formula:");
4932  goalp->formula = get_sexp_main();
4933 
4934  goalp->flags = 0;
4935  if ( optional_string("+Invalid:") )
4936  goalp->type |= INVALID_GOAL;
4937  if ( optional_string("+Invalid") )
4938  goalp->type |= INVALID_GOAL;
4939  if ( optional_string("+No music") )
4940  goalp->flags |= MGF_NO_MUSIC;
4941 
4942  goalp->score = 0;
4943  if ( optional_string("+Score:") ){
4944  stuff_int(&goalp->score);
4945  }
4946 
4947  goalp->team = 0;
4948  if ( optional_string("+Team:") ){
4949  stuff_int( &goalp->team );
4950 
4951  // sanity check
4952  if (goalp->team < -1 || goalp->team >= Num_iffs) {
4953  if (Fred_running && !Warned_about_team_out_of_range) {
4954  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.");
4955  Warned_about_team_out_of_range = true;
4956  }
4957  goalp->team = -1;
4958  }
4959  }
4960 }
4961 
4963 {
4964  required_string("#Goals");
4965 
4966  while (required_string_either("#Waypoints", "$Type:")){
4967  parse_goal(pm);
4968  }
4969 }
4970 
4972 {
4973  Assert(pm != NULL);
4974 
4975  char name_buf[NAME_LENGTH];
4976  required_string("$Name:");
4977  stuff_string(name_buf, F_NAME, NAME_LENGTH);
4978 
4979  SCP_vector<vec3d> vec_list;
4980  required_string("$List:");
4981  stuff_vec3d_list(vec_list);
4982 
4983  waypoint_add_list(name_buf, vec_list);
4984 }
4985 
4987 {
4988  vec3d pos;
4989 
4990  required_string("#Waypoints");
4991 
4992  char file_name[MAX_FILENAME_LEN] = { 0 };
4993  char jump_name[NAME_LENGTH] = { 0 };
4994 
4995  while (optional_string("$Jump Node:")) {
4996  stuff_vec3d(&pos);
4997  CJumpNode jnp(&pos);
4998 
4999  if (optional_string("$Jump Node Name:") || optional_string("+Jump Node Name:")) {
5000  st